diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000000..be6c1fa878 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-coverage": { + "version": "17.13.1", + "commands": [ + "dotnet-coverage" + ], + "rollForward": false + } + } +} diff --git a/.editorconfig b/.editorconfig index e323c80086..742a013007 100644 --- a/.editorconfig +++ b/.editorconfig @@ -66,6 +66,9 @@ tab_width = 4 # Do not set end_of_line property, this is causing issues with Linux, # see https://github.com/dotnet/roslyn/issues/55526 +# MSTESTOBS: Type or member is obsolete +dotnet_diagnostic.MSTESTOBS.severity = none + #### .NET Coding Conventions #### ## Organize usings @@ -211,6 +214,7 @@ dotnet_diagnostic.CA1827.severity = warning # CA1827: Do not use Cou dotnet_diagnostic.CA1836.severity = warning # CA1836: Prefer IsEmpty over Count dotnet_diagnostic.CA1840.severity = warning # CA1840: Use 'Environment.CurrentManagedThreadId' dotnet_diagnostic.CA1852.severity = warning # CA1852: Seal internal types +dotnet_code_quality.CA1852.ignore_internalsvisibleto = true dotnet_diagnostic.CA1854.severity = warning # CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method # Disabled as it's making the code complex to deal with when multi targeting dotnet_diagnostic.CA1863.severity = none # CA1863: Use 'CompositeFormat' @@ -245,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/.github/CODEOWNERS b/.github/CODEOWNERS index 2874056945..804b456c01 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners -src/Package/MSTest.Sdk @MarcoRossignoli -src/Platform @MarcoRossignoli @Evangelink +src/Package/MSTest.Sdk @MarcoRossignoli @Evangelink +src/Platform @MarcoRossignoli @Youssef1313 @Evangelink diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 5375827be7..88ed4a361d 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a bug report to help us improve MSTest +about: Create a bug report to help us improve Microsoft.Testing.Platform and MSTest labels: [bug, need-triage] --- diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index 53121833f4..aa865b4bf0 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -1,6 +1,6 @@ --- name: Feature request -about: Suggest a new feature/idea for MSTest +about: Suggest a new feature/idea for Microsoft.Testing.Platform or MSTest labels: [feature-request, need-triage] --- diff --git a/.github/workflows/backport-base.yml b/.github/workflows/backport-base.yml new file mode 100644 index 0000000000..8518144320 --- /dev/null +++ b/.github/workflows/backport-base.yml @@ -0,0 +1,265 @@ +on: + workflow_call: + inputs: + pr_title_template: + description: 'The template used for the PR title. Special placeholder tokens that will be replaced with a value: %target_branch%, %source_pr_title%, %source_pr_number%, %cc_users%.' + required: false + type: string + default: '[%target_branch%] %source_pr_title%' + pr_description_template: + description: 'The template used for the PR description. Special placeholder tokens that will be replaced with a value: %target_branch%, %source_pr_title%, %source_pr_number%, %cc_users%.' + required: false + type: string + default: | + Backport of #%source_pr_number% to %target_branch% + + /cc %cc_users% + repository_owners: + description: 'A comma-separated list of repository owners where the workflow will run. Defaults to "dotnet,microsoft".' + required: false + type: string + default: 'dotnet,microsoft' + +jobs: + cleanup: + if: ${{ contains(format('{0},', inputs.repository_owners), format('{0},', github.repository_owner)) && github.event_name == 'schedule' }} + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: Cleanup workflow runs + uses: actions/github-script@v7 + with: + script: | + const repo_owner = context.payload.repository.owner.login; + const repo_name = context.payload.repository.name; + + // look up workflow from current run + const currentWorkflowRun = await github.rest.actions.getWorkflowRun({ + owner: repo_owner, + repo: repo_name, + run_id: context.runId + }); + + // get runs which are 'completed' (other candidate values of status field are e.g. 'queued' and 'in_progress') + for await (const response of github.paginate.iterator( + github.rest.actions.listWorkflowRuns, { + owner: repo_owner, + repo: repo_name, + workflow_id: currentWorkflowRun.data.workflow_id, + status: 'completed' + } + )) { + // delete each run + for (const run of response.data) { + console.log(`Deleting workflow run ${run.id}`); + await github.rest.actions.deleteWorkflowRun({ + owner: repo_owner, + repo: repo_name, + run_id: run.id + }); + } + } + + run_backport: + if: ${{ contains(format('{0},', inputs.repository_owners), format('{0},', github.repository_owner)) && github.event.issue.pull_request != '' && contains(github.event.comment.body, '/backport to') }} + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write + steps: + - name: Extract backport target branch + uses: actions/github-script@v7 + id: target-branch-extractor + with: + result-encoding: string + script: | + if (context.eventName !== "issue_comment") throw "Error: This action only works on issue_comment events."; + + // extract the target branch name from the trigger phrase containing these characters: a-z, A-Z, digits, forward slash, dot, hyphen, underscore + const regex = /^\/backport to ([a-zA-Z\d\/\.\-\_]+)/; + target_branch = regex.exec(context.payload.comment.body); + if (target_branch == null) throw "Error: No backport branch found in the trigger phrase."; + + return target_branch[1]; + - name: Unlock comments if PR is locked + uses: actions/github-script@v7 + if: ${{ github.event.issue.locked == true }} + with: + script: | + console.log(`Unlocking locked PR #${context.issue.number}.`); + await github.rest.issues.unlock({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + - name: Post backport started comment to pull request + uses: actions/github-script@v7 + with: + script: | + const target_branch = '${{ steps.target-branch-extractor.outputs.result }}'; + const backport_start_body = `Started backporting to ${target_branch}: https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: backport_start_body + }); + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run backport + uses: actions/github-script@v7 + env: + BACKPORT_PR_TITLE_TEMPLATE: ${{ inputs.pr_title_template }} + BACKPORT_PR_DESCRIPTION_TEMPLATE: ${{ inputs.pr_description_template }} + with: + script: | + const target_branch = '${{ steps.target-branch-extractor.outputs.result }}'; + const repo_owner = context.payload.repository.owner.login; + const repo_name = context.payload.repository.name; + const pr_number = context.payload.issue.number; + const comment_user = context.payload.comment.user.login; + + try { + // verify the comment user is a repo collaborator + try { + await github.rest.repos.checkCollaborator({ + owner: repo_owner, + repo: repo_name, + username: comment_user + }); + console.log(`Verified ${comment_user} is a repo collaborator.`); + } catch (error) { + console.log(error); + throw new Error(`Error: @${comment_user} is not a repo collaborator, backporting is not allowed. If you're a collaborator please make sure your ${repo_owner} team membership visibility is set to Public on https://github.com/orgs/${repo_owner}/people?query=${comment_user}`); + } + + try { await exec.exec(`git ls-remote --exit-code --heads origin ${target_branch}`) } catch { throw new Error(`Error: The specified backport target branch ${target_branch} wasn't found in the repo.`); } + console.log(`Backport target branch: ${target_branch}`); + + console.log("Applying backport patch"); + + await exec.exec(`git checkout ${target_branch}`); + await exec.exec(`git clean -xdff`); + + // configure git + await exec.exec(`git config user.name "github-actions"`); + await exec.exec(`git config user.email "github-actions@github.com"`); + + // create temporary backport branch + const temp_branch = `backport/pr-${pr_number}-to-${target_branch}`; + await exec.exec(`git checkout -b ${temp_branch}`); + + // skip opening PR if the branch already exists on the origin remote since that means it was opened + // by an earlier backport and force pushing to the branch updates the existing PR + let should_open_pull_request = true; + try { + await exec.exec(`git ls-remote --exit-code --heads origin ${temp_branch}`); + should_open_pull_request = false; + } catch { } + + // download and apply patch + await exec.exec(`curl -sSL "${context.payload.issue.pull_request.patch_url}" --output changes.patch`); + + // const git_am_command = "git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch"; + // let git_am_output = `$ ${git_am_command}\n\n`; + // let git_am_failed = false; + // try { + // await exec.exec(git_am_command, [], { + // listeners: { + // stdout: function stdout(data) { git_am_output += data; }, + // stderr: function stderr(data) { git_am_output += data; } + // } + // }); + // } catch (error) { + // git_am_output += error; + // git_am_failed = true; + // } + + // if (git_am_failed) { + // const git_am_failed_body = `@${context.payload.comment.user.login} backporting to ${target_branch} failed, the patch most likely resulted in conflicts:\n\n\`\`\`shell\n${git_am_output}\n\`\`\`\n\nPlease backport manually!`; + // await github.rest.issues.createComment({ + // owner: repo_owner, + // repo: repo_name, + // issue_number: pr_number, + // body: git_am_failed_body + // }); + // core.setFailed("Error: git am failed, most likely due to a merge conflict."); + // return; + // } + // else { + // // push the temp branch to the repository + // await exec.exec(`git push --force --set-upstream origin HEAD:${temp_branch}`); + // } + + // if (!should_open_pull_request) { + // console.log("Backport temp branch already exists, skipping opening a PR."); + // return; + // } + + // prepare the GitHub PR details + + // get users to cc (append PR author if different from user who issued the backport command) + // let cc_users = `@${comment_user}`; + // if (comment_user != context.payload.issue.user.login) cc_users += ` @${context.payload.issue.user.login}`; + + // replace the special placeholder tokens with values + // const { BACKPORT_PR_TITLE_TEMPLATE, BACKPORT_PR_DESCRIPTION_TEMPLATE } = process.env + + // const backport_pr_title = BACKPORT_PR_TITLE_TEMPLATE + // .replace(/%target_branch%/g, target_branch) + // .replace(/%source_pr_title%/g, context.payload.issue.title) + // .replace(/%source_pr_number%/g, context.payload.issue.number) + // .replace(/%cc_users%/g, cc_users); + + // const backport_pr_description = BACKPORT_PR_DESCRIPTION_TEMPLATE + // .replace(/%target_branch%/g, target_branch) + // .replace(/%source_pr_title%/g, context.payload.issue.title) + // .replace(/%source_pr_number%/g, context.payload.issue.number) + // .replace(/%cc_users%/g, cc_users); + + // open the GitHub PR + // await github.rest.pulls.create({ + // owner: repo_owner, + // repo: repo_name, + // title: backport_pr_title, + // body: backport_pr_description, + // head: temp_branch, + // base: target_branch + // }); + + // console.log("Successfully opened the GitHub PR."); + } catch (error) { + + core.setFailed(error); + + // post failure to GitHub comment + const unknown_error_body = `@${comment_user} an error occurred while backporting to ${target_branch}, please check the run log for details!\n\n${error.message}`; + await github.rest.issues.createComment({ + owner: repo_owner, + repo: repo_name, + issue_number: pr_number, + body: unknown_error_body + }); + } + + - uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7 + with: + token: ${{ secrets.BACKPORT_MACHINE_USER_PAT }} + push-to-fork: youssef-backport-bot/testfx + + - name: Re-lock PR comments + uses: actions/github-script@v7 + if: ${{ github.event.issue.locked == true && (success() || failure()) }} + with: + script: | + console.log(`Locking previously locked PR #${context.issue.number} again.`); + await github.rest.issues.lock({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + lock_reason: "resolved" + }); \ No newline at end of file diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000000..f0a13576ad --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,23 @@ +name: Backport PR to branch +on: + issue_comment: + types: [created] + schedule: + # once a day at 13:00 UTC to cleanup old runs + - cron: '0 13 * * *' + +permissions: + contents: write + issues: write + pull-requests: write + actions: write + +jobs: + backport: + if: ${{ contains(github.event.comment.body, '/backport to') || github.event_name == 'schedule' }} + uses: microsoft/testfx/.github/workflows/backport-base.yml@main + with: + pr_description_template: | + Backport of #%source_pr_number% to %target_branch% + + /cc %cc_users% diff --git a/Directory.Build.props b/Directory.Build.props index 291e3b4f05..6e927fe5da 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -25,13 +25,11 @@ - net462 uap10.0.16299 net6.0-windows10.0.18362.0 - net8.0 - netcoreapp3.1;net6.0;net7.0;net8.0 - net6.0;net7.0;net8.0 + net6.0;net7.0;net8.0;net9.0 + netcoreapp3.1;net6.0;net7.0;net8.0;net9.0 @@ -67,4 +65,21 @@ 0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7 + + + + + + + + + + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index d50ae7fda7..0aae14f8fc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,17 +6,17 @@ 8.2.2 17.11.4 - 3.11.0-beta1.24574.2 3.11.0 4.10.0 - $(MicrosoftCodeAnalysisAnalyzersVersion) + 3.11.0-beta1.24629.2 $(MicrosoftCodeAnalysisPublicApiAnalyzersVersion) 6.2.14 17.12.0 1.49.0 - 17.12.0-beta.24429.1 + 17.12.0 + 1.5.0-preview.24577.4 4.3.1 4.3.1 @@ -24,6 +24,7 @@ 1.1.3-beta1.24423.1 + 3.8.0-preview.25057.8 @@ -36,23 +37,21 @@ - + - - + - @@ -64,23 +63,24 @@ - - + + + + - + - - + - + - latest-recommended + preview-recommended true - + + - diff --git a/eng/Build.props b/eng/Build.props index 57aac3a459..e04a3d62ad 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -4,38 +4,47 @@ all - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/eng/CodeCoverage.proj b/eng/CodeCoverage.proj index 76ae926fba..c336517d96 100644 --- a/eng/CodeCoverage.proj +++ b/eng/CodeCoverage.proj @@ -8,45 +8,51 @@ - <_CodecovPath>$(PkgCodecov)\tools\Codecov.exe - <_ReportGeneratorPath>$(PkgReportGenerator)\tools\net47\ReportGenerator.exe + <_LocalDotNetPath>$(DotNetRoot)\dotnet.exe + $(ArtifactsCoverageDir)full\ + $(ArtifactsCoverageDir)unit\ + $(ArtifactsCoverageDir)integration\ + Cobertura.xml <_CoverageReports Include="$(ArtifactsTestResultsDir)\*.coverage" /> + <_CoverageReports Include="@(_CoverageReports->'"%(Identity)"', ' ')" /> <_UnitCoverageReports Include="@(_CoverageReports)" Condition="$([System.String]::Copy('%(Identity)').Contains('.UnitTests_'))" /> + <_UnitCoverageReports Include="@(_UnitCoverageReports->'"%(Identity)"', ' ')" /> <_IntegrationCoverageReports Include="@(_CoverageReports)" Condition="$([System.String]::Copy('%(Identity)').Contains('.IntegrationTests_'))" /> + <_IntegrationCoverageReports Include="@(_IntegrationCoverageReports->'"%(Identity)"', ' ')" /> - - + + - - + + - - + + - <_CodecovFullArgs Include="-f;$(ArtifactsCoverageDir)full\Cobertura.xml" /> - <_CodecovUnitArgs Include="-f;$(ArtifactsCoverageDir)unit\Cobertura.xml" /> - <_CodecovIntegrationArgs Include="-f;$(ArtifactsCoverageDir)integration\Cobertura.xml" /> + <_CodecovFullArgs Include="-f;$(MergedFullCoverageDirectory)$(CoberturaFileName)" /> + <_CodecovUnitArgs Include="-f;$(MergedUnitCoverageDirectory)$(CoberturaFileName)" /> + <_CodecovIntegrationArgs Include="-f;$(MergedIntegrationCoverageDirectory)$(CoberturaFileName)" /> <_CodecovArgs Include="--required" /> diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props new file mode 100644 index 0000000000..f4d2580ef8 --- /dev/null +++ b/eng/DotNetBuild.props @@ -0,0 +1,10 @@ + + + + + + testfx + true + + + diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml new file mode 100644 index 0000000000..1b37a10db6 --- /dev/null +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index afaac5d206..d550616ca3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,29 +1,49 @@ - + https://github.com/dotnet/arcade - 7d955f9f470465e144c76d47fd2596a0e4c02a21 + 61b8f746424762d2e3173ebfaab19346224d591c - + https://github.com/dotnet/arcade - 7d955f9f470465e144c76d47fd2596a0e4c02a21 + 61b8f746424762d2e3173ebfaab19346224d591c - + https://github.com/dotnet/arcade - 7d955f9f470465e144c76d47fd2596a0e4c02a21 + 61b8f746424762d2e3173ebfaab19346224d591c - + https://dev.azure.com/devdiv/DevDiv/_git/vs-code-coverage - 2d88381a218863f6b20537637b2bcfb0bc93686d + 0bfbc955e0dc1826412fdeaa4587106f038ec8de - + https://github.com/microsoft/testanywhere - 222b1daf1d58cd34402736d9a896975b79171b6d + d898ece7f4a764f772bf2ec89ac78a960edb3114 - - https://github.com/microsoft/testanywhere - aa2fcc8616d988b234bc1d218465b20c56d0b82f + + + https://github.com/dotnet/diagnostics + 8c505ca6921b5f7e9b8acc234cc8f15035537ee4 + + + + + https://github.com/dotnet/source-build-externals + 4df883d781a4290873b3b968afc0ff0df7132507 + + + + + https://github.com/dotnet/source-build-reference-packages + 80f1e84b2077a7208943db050067d86c94ace837 + + + + + https://github.com/dotnet/arcade + 61b8f746424762d2e3173ebfaab19346224d591c + diff --git a/eng/Versions.props b/eng/Versions.props index 6c4dd96500..15569ec0e6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,16 +1,15 @@ - 3.7.0 + 3.8.0 - 1.5.0 + 1.6.0 preview - 10.0.0-beta.24572.3 - 17.13.0-preview.24572.7 + 10.0.0-beta.24606.6 + 17.14.0-preview.25058.3 - 1.5.0-preview.24573.1 - 1.0.0-alpha.24473.2 + 1.0.0-alpha.25058.1 diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml index 4da05afe05..f9ba1625c2 100644 --- a/eng/common/core-templates/steps/source-build.yml +++ b/eng/common/core-templates/steps/source-build.yml @@ -78,7 +78,7 @@ steps: portableBuildArgs= if [ '${{ parameters.platform.portableBuild }}' != '' ]; then - portableBuildArgs='/p:PortabelBuild=${{ parameters.platform.portableBuild }}' + portableBuildArgs='/p:PortableBuild=${{ parameters.platform.portableBuild }}' fi ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 20ae8c2868..096bfe51f1 100644 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -73,8 +73,8 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="13.3-RELEASE" -__FreeBSDPkg="1.17.0" +__FreeBSDBase="13.4-RELEASE" +__FreeBSDPkg="1.21.3" __FreeBSDABI="13" __FreeBSDPackages="libunwind" __FreeBSDPackages+=" icu" @@ -371,7 +371,7 @@ while :; do ;; freebsd14) __CodeName=freebsd - __FreeBSDBase="14.0-RELEASE" + __FreeBSDBase="14.2-RELEASE" __FreeBSDABI="14" __SkipUnmount=1 ;; @@ -574,7 +574,7 @@ elif [[ "$__CodeName" == "freebsd" ]]; then curl -SL "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version fi echo "ABI = \"FreeBSD:${__FreeBSDABI}:${__FreeBSDMachineArch}\"; FINGERPRINTS = \"${__RootfsDir}/usr/share/keys\"; REPOS_DIR = [\"${__RootfsDir}/etc/pkg\"]; REPO_AUTOUPDATE = NO; RUN_SCRIPTS = NO;" > "${__RootfsDir}"/usr/local/etc/pkg.conf - echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"${__RootfsDir}/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf + echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf mkdir -p "$__RootfsDir"/tmp # get and build package manager if [[ "$__hasWget" == 1 ]]; then diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh index dc396a9556..3eef7409f7 100644 --- a/eng/common/native/install-dependencies.sh +++ b/eng/common/native/install-dependencies.sh @@ -44,15 +44,11 @@ case "$os" in export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 # Skip brew update for now, see https://github.com/actions/setup-python/issues/577 # brew update --preinstall - - # Temporarily uninstall pkg-config@0.29.2 to work around https://github.com/actions/runner-images/issues/10984 - brew uninstall --ignore-dependencies --force pkg-config@0.29.2 - brew bundle --no-upgrade --no-lock --file=- < - - - - - - - - - - - - - - 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/eng/verify-nupkgs.ps1 b/eng/verify-nupkgs.ps1 index d119680355..a8c3ba4d07 100644 --- a/eng/verify-nupkgs.ps1 +++ b/eng/verify-nupkgs.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] Param( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $configuration ) @@ -19,12 +19,13 @@ function Unzip { function Confirm-NugetPackages { Write-Verbose "Starting Confirm-NugetPackages." $expectedNumOfFiles = @{ - "MSTest.Sdk" = 15; - "MSTest.Internal.TestFx.Documentation" = 10; - "MSTest.TestFramework" = 130; - "MSTest.TestAdapter" = 76; - "MSTest" = 6; - "MSTest.Analyzers" = 10; + "MSTest.Sdk" = 15 + "MSTest.Internal.TestFx.Documentation" = 10 + "MSTest.TestFramework" = 148 + "MSTest.TestAdapter" = 75 + "MSTest" = 6 + "MSTest.Analyzers" = 50 + "Microsoft.Testing.Platform.DotNetTestClient" = 7 } $packageDirectory = Resolve-Path "$PSScriptRoot/../artifacts/packages/$configuration" @@ -79,7 +80,8 @@ function Confirm-NugetPackages { if ($errors) { Write-Error "Validation of NuGet packages failed with $($errors.Count) errors:`n$($errors -join "`n")" - } else { + } + else { Write-Host "Successfully validated content of NuGet packages" } } diff --git a/global.json b/global.json index 77e444f5e2..6534c78b79 100644 --- a/global.json +++ b/global.json @@ -1,18 +1,19 @@ { "tools": { - "dotnet": "9.0.100", + "dotnet": "10.0.100-alpha.1.24573.1", "runtimes": { "dotnet": [ "3.1.32", "6.0.35", "7.0.20", - "8.0.10" + "8.0.11", + "9.0.0" ], "dotnet/x86": [ - "8.0.10" + "9.0.0" ], "aspnetcore": [ - "8.0.10" + "9.0.0" ] }, "vs": { @@ -20,12 +21,12 @@ } }, "sdk": { - "version": "9.0.100", + "version": "10.0.100-alpha.1.24573.1", "allowPrerelease": true, "rollForward": "latestFeature" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.24572.3", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.24606.6", "MSBuild.Sdk.Extras": "3.0.44" } } diff --git a/samples/FxExtensibility/AssertEx.cs b/samples/FxExtensibility/AssertEx.cs index 595d001974..6e9abb7eb6 100644 --- a/samples/FxExtensibility/AssertEx.cs +++ b/samples/FxExtensibility/AssertEx.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace MSTest.Extensibility.Samples; diff --git a/samples/FxExtensibility/AssertIs.cs b/samples/FxExtensibility/AssertIs.cs index 91d3c662a7..eff6cfed75 100644 --- a/samples/FxExtensibility/AssertIs.cs +++ b/samples/FxExtensibility/AssertIs.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace MSTest.Extensibility.Samples; diff --git a/samples/FxExtensibility/Properties/AssemblyInfo.cs b/samples/FxExtensibility/Properties/AssemblyInfo.cs index d3592d292b..3c43a8702b 100644 --- a/samples/FxExtensibility/Properties/AssemblyInfo.cs +++ b/samples/FxExtensibility/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. diff --git a/samples/Playground/DebuggerUtility.cs b/samples/Playground/DebuggerUtility.cs index 6b58a2a58d..babb647424 100644 --- a/samples/Playground/DebuggerUtility.cs +++ b/samples/Playground/DebuggerUtility.cs @@ -1,15 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#if NETCOREAPP #pragma warning disable CA1837 // Use 'Environment.ProcessId' #pragma warning disable CA1416 // Validate platform compatibility -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; namespace Microsoft.Testing.TestInfrastructure; @@ -26,7 +21,7 @@ private static bool AttachVSToProcess(int? pid, int? vsPid, bool enableLog = fal { if (pid == null) { - Trace($"FAIL: Pid is null.", enabled: enableLog); + Trace("FAIL: Pid is null.", enabled: enableLog); return false; } @@ -42,7 +37,7 @@ private static bool AttachVSToProcess(int? pid, int? vsPid, bool enableLog = fal return true; } - Trace($"Parent VS not found, finding the first VS that started.", enabled: enableLog); + Trace("Parent VS not found, finding the first VS that started.", enabled: enableLog); Process? firstVsProcess = GetFirstVsProcess(); if (firstVsProcess != null) @@ -127,21 +122,21 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) Marshal.ThrowExceptionForHR(r); if (bindCtx == null) { - Trace($"BindCtx is null. Cannot attach VS.", enabled: enableLog); + Trace("BindCtx is null. Cannot attach VS.", enabled: enableLog); return false; } bindCtx.GetRunningObjectTable(out runningObjectTable); if (runningObjectTable == null) { - Trace($"RunningObjectTable is null. Cannot attach VS.", enabled: enableLog); + Trace("RunningObjectTable is null. Cannot attach VS.", enabled: enableLog); return false; } runningObjectTable.EnumRunning(out enumMoniker); if (enumMoniker == null) { - Trace($"EnumMoniker is null. Cannot attach VS.", enabled: enableLog); + Trace("EnumMoniker is null. Cannot attach VS.", enabled: enableLog); return false; } @@ -356,3 +351,5 @@ private static extern int NtQueryInformationProcess( [DllImport("ole32.dll")] private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc); } + +#endif diff --git a/samples/Playground/Playground.csproj b/samples/Playground/Playground.csproj index 6e3e66fa49..f83ced76c8 100644 --- a/samples/Playground/Playground.csproj +++ b/samples/Playground/Playground.csproj @@ -2,12 +2,16 @@ Exe - net8.0 + net9.0 enable false $(NoWarn);NETSDK1023 + + + + diff --git a/samples/Playground/Program.cs b/samples/Playground/Program.cs index c285308345..35fee852f9 100644 --- a/samples/Playground/Program.cs +++ b/samples/Playground/Program.cs @@ -1,20 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Extensions.TestHostControllers; using Microsoft.Testing.Platform.Messages; +#if NETCOREAPP using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; -using Microsoft.Testing.Platform.Services; +using MSTest.Acceptance.IntegrationTests.Messages.V100; +#endif using Microsoft.Testing.Platform.TestHost; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MSTest.Acceptance.IntegrationTests.Messages.V100; - namespace Playground; public class Program @@ -26,8 +24,11 @@ public static async Task Main(string[] args) if (Environment.GetEnvironmentVariable("TESTSERVERMODE") != "1") { +#if NETCOREAPP // To attach to the children Microsoft.Testing.TestInfrastructure.DebuggerUtility.AttachCurrentProcessToParentVSProcess(); +#endif + ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); // Test MSTest @@ -37,7 +38,7 @@ public static async Task Main(string[] args) // testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, _) => new DummyAdapter()); // Custom test host controller extension - testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(s => new OutOfProc(s.GetMessageBus())); + // testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(s => new OutOfProc(s.GetMessageBus())); // Enable Trx // testApplicationBuilder.AddTrxReportProvider(); @@ -49,8 +50,11 @@ public static async Task Main(string[] args) } else { +#if NETFRAMEWORK + throw new NotSupportedException("Server mode is not supported on .NET Framework"); +#else Environment.SetEnvironmentVariable("TESTSERVERMODE", "0"); - using TestingPlatformClient client = await TestingPlatformClientFactory.StartAsServerAndConnectAsync(Environment.ProcessPath!, enableDiagnostic: true); + using TestingPlatformClient client = await TestingPlatformClientFactory.StartAsServerAndConnectToTheClientAsync(Environment.ProcessPath!); await client.InitializeAsync(); List testNodeUpdates = new(); @@ -61,12 +65,13 @@ public static async Task Main(string[] args) }); await discoveryResponse.WaitCompletionAsync(); - ResponseListener runRequest = await client.RunTestsAsync(Guid.NewGuid(), testNodeUpdates.Select(x => x.Node).ToArray(), node => Task.CompletedTask); + ResponseListener runRequest = await client.RunTestsAsync(Guid.NewGuid(), testNodeUpdates.Select(x => x.Node).ToArray(), _ => Task.CompletedTask); await runRequest.WaitCompletionAsync(); await client.ExitAsync(); return 0; +#endif } } } diff --git a/samples/Playground/ServerMode/LogsCollector.cs b/samples/Playground/ServerMode/LogsCollector.cs index 037e12b643..8ac87d7e0a 100644 --- a/samples/Playground/ServerMode/LogsCollector.cs +++ b/samples/Playground/ServerMode/LogsCollector.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using static Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100.TestingPlatformClient; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/samples/Playground/ServerMode/TelemetryCollector.cs b/samples/Playground/ServerMode/TelemetryCollector.cs index ac621b10fd..7b48806dc5 100644 --- a/samples/Playground/ServerMode/TelemetryCollector.cs +++ b/samples/Playground/ServerMode/TelemetryCollector.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; namespace MSTest.Acceptance.IntegrationTests.Messages.V100; diff --git a/samples/Playground/ServerMode/TestNodeUpdateCollector.cs b/samples/Playground/ServerMode/TestNodeUpdateCollector.cs index 5943cf25fa..067a4f4410 100644 --- a/samples/Playground/ServerMode/TestNodeUpdateCollector.cs +++ b/samples/Playground/ServerMode/TestNodeUpdateCollector.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; namespace MSTest.Acceptance.IntegrationTests.Messages.V100; diff --git a/samples/Playground/ServerMode/TestingPlatformClientFactory.cs b/samples/Playground/ServerMode/TestingPlatformClientFactory.cs index a5f432d990..44491c2798 100644 --- a/samples/Playground/ServerMode/TestingPlatformClientFactory.cs +++ b/samples/Playground/ServerMode/TestingPlatformClientFactory.cs @@ -1,13 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics; -using System.Globalization; using System.Net; using System.Net.Sockets; -using System.Text; -using System.Text.RegularExpressions; using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; @@ -51,7 +46,7 @@ public static async Task StartAsServerAndConnectToTheClie { OnStandardOutput = (_, output) => builder.AppendLine(CultureInfo.InvariantCulture, $"OnStandardOutput:\n{output}"), OnErrorOutput = (_, output) => builder.AppendLine(CultureInfo.InvariantCulture, $"OnErrorOutput:\n{output}"), - OnExit = (processHandle, exitCode) => builder.AppendLine(CultureInfo.InvariantCulture, $"OnExit: exit code '{exitCode}'"), + OnExit = (_, exitCode) => builder.AppendLine(CultureInfo.InvariantCulture, $"OnExit: exit code '{exitCode}'"), Arguments = $"--server --client-host localhost --client-port {((IPEndPoint)tcpListener.LocalEndpoint).Port}", // Arguments = $"--server --client-host localhost --client-port {((IPEndPoint)tcpListener.LocalEndpoint).Port} --diagnostic --diagnostic-verbosity trace", @@ -73,89 +68,6 @@ public static async Task StartAsServerAndConnectToTheClie return new TestingPlatformClient(new(tcpClient.GetStream()), tcpClient, processHandler); } - - public static async Task StartAsServerAndConnectAsync(string testApp, bool enableDiagnostic = false) - { - var environmentVariables = new Dictionary(DefaultEnvironmentVariables); - foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) - { - // Skip all unwanted environment variables. - string? key = entry.Key.ToString(); - if (WellKnownEnvironmentVariables.ToSkipEnvironmentVariables.Contains(key, StringComparer.OrdinalIgnoreCase)) - { - continue; - } - - environmentVariables[key!] = entry.Value!.ToString()!; - } - - // We expect to not fail for unhandled exception in server mode for IDE needs. - environmentVariables.Add("TESTINGPLATFORM_EXIT_PROCESS_ON_UNHANDLED_EXCEPTION", "0"); - - // To attach to the server on startup - // environmentVariables.Add(EnvironmentVariableConstants.TESTINGPLATFORM_LAUNCH_ATTACH_DEBUGGER, "1"); - TaskCompletionSource portFound = new(); - ProcessConfiguration processConfig = new(testApp) - { - OnStandardOutput = - (_, output) => - { - Match m = ParsePort().Match(output); - if (m.Success && int.TryParse(m.Groups[1].Value, out int port)) - { - portFound.SetResult(port); - } - - // Do not remove pls - // NotepadWindow.WriteLine($"[OnStandardOutput] {output}"); - }, - - // Do not remove pls - // OnErrorOutput = (_, output) => NotepadWindow.WriteLine($"[OnErrorOutput] {output}"), - OnErrorOutput = (_, output) => - { - if (!portFound.Task.IsCompleted) - { - try - { - portFound.SetException(new InvalidOperationException(output)); - } - catch (InvalidOperationException) - { - // possible race - } - } - }, - OnExit = (processHandle, exitCode) => - { - if (exitCode != 0) - { - if (portFound.Task.Exception is null && !portFound.Task.IsCompleted) - { - portFound.SetException(new InvalidOperationException($"Port not found during parsing and process exited with code '{exitCode}'")); - } - } - }, - - // OnExit = (_, exitCode) => NotepadWindow.WriteLine($"[OnExit] Process exit code '{exitCode}'"), - Arguments = "--server --diagnostic --diagnostic-verbosity trace", - // Arguments = "--server", - EnvironmentVariables = environmentVariables, - }; - - IProcessHandle processHandler = ProcessFactory.Start(processConfig, cleanDefaultEnvironmentVariableIfCustomAreProvided: false); - await portFound.Task; - - var tcpClient = new TcpClient(); - using CancellationTokenSource cancellationTokenSource = new(TimeSpan.FromSeconds(90)); -#pragma warning disable VSTHRD103 // Call async methods when in an async method - await tcpClient.ConnectAsync(new IPEndPoint(IPAddress.Loopback, portFound.Task.Result), cancellationTokenSource.Token); -#pragma warning restore VSTHRD103 // Call async methods when in an async method - return new TestingPlatformClient(new(tcpClient.GetStream()), tcpClient, processHandler, enableDiagnostic); - } - - [GeneratedRegex(@"Starting server. Listening on port '(\d+)'")] - private static partial Regex ParsePort(); } public sealed class ProcessConfiguration @@ -257,7 +169,7 @@ public static IProcessHandle Start(ProcessConfiguration config, bool cleanDefaul if (config.OnExit != null) { - process.Exited += (s, e) => config.OnExit.Invoke(processHandle, process.ExitCode); + process.Exited += (_, _) => config.OnExit.Invoke(processHandle, process.ExitCode); } if (config.OnStandardOutput != null) diff --git a/samples/Playground/ServerMode/v1.0.0/ClientInfo.cs b/samples/Playground/ServerMode/v1.0.0/ClientInfo.cs index a4548d891f..5acc6821ed 100644 --- a/samples/Playground/ServerMode/v1.0.0/ClientInfo.cs +++ b/samples/Playground/ServerMode/v1.0.0/ClientInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.Json; - using Newtonsoft.Json; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/samples/Playground/ServerMode/v1.0.0/InitializeRequest.cs b/samples/Playground/ServerMode/v1.0.0/InitializeRequest.cs index abf5adb281..f15e94ab37 100644 --- a/samples/Playground/ServerMode/v1.0.0/InitializeRequest.cs +++ b/samples/Playground/ServerMode/v1.0.0/InitializeRequest.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.Json; - using Newtonsoft.Json; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/samples/Playground/ServerMode/v1.0.0/RpcListener.cs b/samples/Playground/ServerMode/v1.0.0/RpcListener.cs index 6bed732b68..e4eca6facb 100644 --- a/samples/Playground/ServerMode/v1.0.0/RpcListener.cs +++ b/samples/Playground/ServerMode/v1.0.0/RpcListener.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; internal sealed class ConsoleRpcListener : TraceListener diff --git a/samples/Playground/ServerMode/v1.0.0/ServerInfo.cs b/samples/Playground/ServerMode/v1.0.0/ServerInfo.cs index ccab026462..e2b0e4a49f 100644 --- a/samples/Playground/ServerMode/v1.0.0/ServerInfo.cs +++ b/samples/Playground/ServerMode/v1.0.0/ServerInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.Json; - using Newtonsoft.Json; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs b/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs index 57a585299b..0d37b7f3d5 100644 --- a/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs +++ b/samples/Playground/ServerMode/v1.0.0/TestingPlatformClient.cs @@ -1,10 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics; using System.Net.Sockets; -using System.Text; using MSTest.Acceptance.IntegrationTests.Messages.V100; diff --git a/samples/Playground/Tests.cs b/samples/Playground/Tests.cs index eb480d750f..ffd007f386 100644 --- a/samples/Playground/Tests.cs +++ b/samples/Playground/Tests.cs @@ -15,13 +15,16 @@ public class TestClass public TestContext TestContext { get; set; } [TestMethod] - public void Test() => TestContext.AddResultFile(@"c:\hello2"); + [DynamicData(nameof(Data))] + public void Test3(int a, int b) + => Assert.AreNotEqual(a, b); - [TestMethod] - public void Test2() => Assert.AreEqual(1, 0, "few"); - - [TestMethod] - public void Test3() + public static IEnumerable<(int A, int B)> Data { + get + { + yield return (1, 2); + yield return (3, 4); + } } } diff --git a/samples/public/Directory.Build.props b/samples/public/Directory.Build.props index a5c07027eb..3dff110b22 100644 --- a/samples/public/Directory.Build.props +++ b/samples/public/Directory.Build.props @@ -1,13 +1,13 @@ - 8.1.0 - 17.12.6 - 3.6.2 + 8.2.2 + 17.13.1 + 3.6.4 1.0.0-alpha.24530.4 - 1.45.1 - 1.4.2 - 17.11.1 + 1.49.0 + 1.4.3 + 17.12.0 diff --git a/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain.sln b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain.sln new file mode 100644 index 0000000000..392762a899 --- /dev/null +++ b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35507.96 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSTestProjectWithExplicitMain", "MSTestProjectWithExplicitMain\MSTestProjectWithExplicitMain.csproj", "{3F00FD7B-D9E6-42C6-8607-3186BFBD4A0F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3F00FD7B-D9E6-42C6-8607-3186BFBD4A0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F00FD7B-D9E6-42C6-8607-3186BFBD4A0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F00FD7B-D9E6-42C6-8607-3186BFBD4A0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F00FD7B-D9E6-42C6-8607-3186BFBD4A0F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {68CE81DD-68F6-49B0-87AF-DC6223F45845} + EndGlobalSection +EndGlobal diff --git a/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain.csproj b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain.csproj new file mode 100644 index 0000000000..83058077b3 --- /dev/null +++ b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain.csproj @@ -0,0 +1,31 @@ + + + + net9.0 + latest + enable + enable + true + Exe + true + + true + + false + + + + + + + + + + + + + + diff --git a/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/MSTestSettings.cs b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/MSTestSettings.cs new file mode 100644 index 0000000000..553a44aa64 --- /dev/null +++ b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/MSTestSettings.cs @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/Program.cs b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/Program.cs new file mode 100644 index 0000000000..d0f56c90bd --- /dev/null +++ b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/Program.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; + +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Builder; + +// Create the test application builder +ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +// Register the testing framework +testApplicationBuilder.AddMSTest(() => new[] { Assembly.GetExecutingAssembly() }); + +// Register Code Coverage extension +testApplicationBuilder.AddCodeCoverageProvider(); + +// Register TRX report extension +testApplicationBuilder.AddTrxReportProvider(); + +// Register Telemetry extension +testApplicationBuilder.AddAppInsightsTelemetryProvider(); + +// Alternatively, instead of registering everything manually, I could rely on the MSBuild hooks and use +// testApplicationBuilder.AddSelfRegisteredExtensions(args); +// This is what is called by the generated entry point + +// In addition to be using each extension helper method, we can directly register extensions to each of +// the extensibility area. For now, the following 3 are exposed: +// testApplicationBuilder.CommandLine +// testApplicationBuilder.TestHost +// testApplicationBuilder.TestHostControllers +// but the goal is to also expose all these areas: https://github.com/microsoft/testfx/blob/main/src/Platform/Microsoft.Testing.Platform/Builder/TestApplicationBuilder.cs#L57-L69 + +// NOTE that registering an extension is not enough and each extension has some activation criteria, +// most of the time the presence of a command line option but it could be anything (including nothing). + +// Build the test app +using ITestApplication testApplication = await testApplicationBuilder.BuildAsync(); + +// Run the test app +return await testApplication.RunAsync(); diff --git a/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/Test1.cs b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/Test1.cs new file mode 100644 index 0000000000..21f9186303 --- /dev/null +++ b/samples/public/mstest-runner/MSTestProjectWithExplicitMain/MSTestProjectWithExplicitMain/Test1.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTestProjectWithExplicitMain; + +[TestClass] +public sealed class Test1 +{ + [TestMethod] + public void TestMethod1() + { + } +} diff --git a/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt b/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt index 91178cbc64..31a6272126 100644 --- a/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt +++ b/src/Adapter/MSTest.TestAdapter/BannedSymbols.txt @@ -1 +1,2 @@ M:Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers.ReflectHelper.#ctor; This is allowed only for tests. +N:Microsoft.Testing.Platform diff --git a/src/Adapter/MSTest.TestAdapter/Constants.cs b/src/Adapter/MSTest.TestAdapter/Constants.cs index 057ebb6bfa..569d49159b 100644 --- a/src/Adapter/MSTest.TestAdapter/Constants.cs +++ b/src/Adapter/MSTest.TestAdapter/Constants.cs @@ -10,6 +10,8 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// internal static class Constants { + internal const string PublicTypeObsoleteMessage = "We will remove or hide this type starting with v4. If you are using this type, reach out to our team on https://github.com/microsoft/testfx."; + /// /// The 3rd level entry (class) name in the hierarchy array. /// @@ -121,6 +123,8 @@ internal static class Constants internal static readonly TestProperty TestDynamicDataProperty = TestProperty.Register("MSTest.DynamicData", "DynamicData", typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase)); internal static readonly TestProperty TestIdGenerationStrategyProperty = TestProperty.Register("MSTest.TestIdGenerationStrategy", "TestIdGenerationStrategy", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase)); + + internal static readonly TestProperty TestDataSourceIgnoreMessageProperty = TestProperty.Register("MSTest.TestDataSourceIgnoreMessageProperty", "TestDataSourceIgnoreMessageProperty", typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase)); #endregion #region Private Constants diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs index 711bda3b0e..74654b6bca 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumerator.cs @@ -1,12 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; using System.Runtime.Serialization; using System.Security; -using System.Text; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -20,6 +16,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; /// /// Enumerates through all types in the assembly in search of valid test methods. /// +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class AssemblyEnumerator : MarshalByRefObject { /// @@ -49,11 +46,6 @@ public AssemblyEnumerator(MSTestSettings settings) => // This would just be resetting the settings to itself in non desktop workflows. MSTestSettings.PopulateSettings(settings); - /// - /// Gets or sets the run settings to use for current discovery session. - /// - public string? RunSettingsXml { get; set; } - /// /// Returns object to be used for controlling lifetime, null means infinite lifetime. /// @@ -70,19 +62,22 @@ public AssemblyEnumerator(MSTestSettings settings) => /// Enumerates through all types in the assembly in search of valid test methods. /// /// The assembly file name. + /// The xml specifying runsettings. /// Contains warnings if any, that need to be passed back to the caller. /// A collection of Test Elements. - internal ICollection EnumerateAssembly(string assemblyFileName, out ICollection warnings) + internal ICollection EnumerateAssembly( + string assemblyFileName, + [StringSyntax(StringSyntaxAttribute.Xml, nameof(runSettingsXml))] string? runSettingsXml, + List warnings) { DebugEx.Assert(!StringEx.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name."); - var warningMessages = new List(); var tests = new List(); // Contains list of assembly/class names for which we have already added fixture tests. var fixturesTests = new HashSet(); Assembly assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(assemblyFileName, isReflectionOnly: false); - IReadOnlyList types = GetTypes(assembly, assemblyFileName, warningMessages); + Type[] types = GetTypes(assembly, assemblyFileName, warnings); bool discoverInternals = ReflectHelper.GetDiscoverInternalsAttribute(assembly) != null; TestIdGenerationStrategy testIdGenerationStrategy = ReflectHelper.GetTestIdGenerationStrategy(assembly); @@ -91,15 +86,28 @@ internal ICollection EnumerateAssembly(string assemblyFileName, DataRowAttribute.TestIdGenerationStrategy = testIdGenerationStrategy; DynamicDataAttribute.TestIdGenerationStrategy = testIdGenerationStrategy; - TestDataSourceDiscoveryOption testDataSourceDiscovery = ReflectHelper.GetTestDataSourceDiscoveryOption(assembly) + TestDataSourceUnfoldingStrategy dataSourcesUnfoldingStrategy = ReflectHelper.GetTestDataSourceOptions(assembly)?.UnfoldingStrategy switch + { + // When strategy is auto we want to unfold + TestDataSourceUnfoldingStrategy.Auto => TestDataSourceUnfoldingStrategy.Unfold, + // When strategy is set, let's use it + { } value => value, + // When the attribute is not set, let's look at the legacy attribute +#pragma warning disable CS0612 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete - - // When using legacy strategy, there is no point in trying to "read" data during discovery - // as the ID generator will ignore it. - ?? (testIdGenerationStrategy == TestIdGenerationStrategy.Legacy - ? TestDataSourceDiscoveryOption.DuringExecution - : TestDataSourceDiscoveryOption.DuringDiscovery); + null => (ReflectHelper.GetTestDataSourceDiscoveryOption(assembly), testIdGenerationStrategy) switch +#pragma warning restore CS0612 // Type or member is obsolete + { + (TestDataSourceDiscoveryOption.DuringExecution, _) => TestDataSourceUnfoldingStrategy.Fold, + // When using legacy strategy, there is no point in trying to "read" data during discovery + // as the ID generator will ignore it. + (null, TestIdGenerationStrategy.Legacy) => TestDataSourceUnfoldingStrategy.Fold, #pragma warning restore CS0618 // Type or member is obsolete + _ => TestDataSourceUnfoldingStrategy.Unfold, + }, + }; + + Dictionary? testRunParametersFromRunSettings = RunSettingsUtilities.GetTestRunParameters(runSettingsXml); foreach (Type type in types) { if (type == null) @@ -107,12 +115,11 @@ internal ICollection EnumerateAssembly(string assemblyFileName, continue; } - List testsInType = DiscoverTestsInType(assemblyFileName, RunSettingsXml, type, warningMessages, discoverInternals, - testDataSourceDiscovery, testIdGenerationStrategy, fixturesTests); + List testsInType = DiscoverTestsInType(assemblyFileName, testRunParametersFromRunSettings, type, warnings, discoverInternals, + dataSourcesUnfoldingStrategy, testIdGenerationStrategy, fixturesTests); tests.AddRange(testsInType); } - warnings = warningMessages; return tests; } @@ -192,29 +199,28 @@ internal static string GetLoadExceptionDetails(ReflectionTypeLoadException ex) /// The reflected assembly name. /// True to discover test classes which are declared internal in /// addition to test classes which are declared public. - /// to use when generating tests. /// to use when generating TestId. /// a TypeEnumerator instance. - internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, TestDataSourceDiscoveryOption discoveryOption, TestIdGenerationStrategy testIdGenerationStrategy) + internal virtual TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, TestIdGenerationStrategy testIdGenerationStrategy) { var typeValidator = new TypeValidator(ReflectHelper, discoverInternals); var testMethodValidator = new TestMethodValidator(ReflectHelper, discoverInternals); - return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator, discoveryOption, testIdGenerationStrategy); + return new TypeEnumerator(type, assemblyFileName, ReflectHelper, typeValidator, testMethodValidator, testIdGenerationStrategy); } private List DiscoverTestsInType( string assemblyFileName, - [StringSyntax(StringSyntaxAttribute.Xml, nameof(runSettingsXml))] string? runSettingsXml, + Dictionary? testRunParametersFromRunSettings, Type type, List warningMessages, bool discoverInternals, - TestDataSourceDiscoveryOption discoveryOption, + TestDataSourceUnfoldingStrategy dataSourcesUnfoldingStrategy, TestIdGenerationStrategy testIdGenerationStrategy, HashSet fixturesTests) { IDictionary tempSourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(assemblyFileName); - tempSourceLevelParameters = RunSettingsUtilities.GetTestRunParameters(runSettingsXml)?.ConcatWithOverwrites(tempSourceLevelParameters) + tempSourceLevelParameters = testRunParametersFromRunSettings?.ConcatWithOverwrites(tempSourceLevelParameters) ?? tempSourceLevelParameters ?? new Dictionary(); var sourceLevelParameters = tempSourceLevelParameters.ToDictionary(x => x.Key, x => (object?)x.Value); @@ -225,25 +231,22 @@ private List DiscoverTestsInType( try { typeFullName = type.FullName; - TypeEnumerator testTypeEnumerator = GetTypeEnumerator(type, assemblyFileName, discoverInternals, discoveryOption, testIdGenerationStrategy); - ICollection? unitTestCases = testTypeEnumerator.Enumerate(out ICollection warningsFromTypeEnumerator); - warningMessages.AddRange(warningsFromTypeEnumerator); + TypeEnumerator testTypeEnumerator = GetTypeEnumerator(type, assemblyFileName, discoverInternals, testIdGenerationStrategy); + List? unitTestCases = testTypeEnumerator.Enumerate(warningMessages); if (unitTestCases != null) { foreach (UnitTestElement test in unitTestCases) { - if (discoveryOption == TestDataSourceDiscoveryOption.DuringDiscovery) + if (_typeCache.GetTestMethodInfoForDiscovery(test.TestMethod) is { } testMethodInfo) { - Lazy testMethodInfo = GetTestMethodInfo(sourceLevelParameters, test); - // Add fixture tests like AssemblyInitialize, AssemblyCleanup, ClassInitialize, ClassCleanup. - if (MSTestSettings.CurrentSettings.ConsiderFixturesAsSpecialTests && testMethodInfo.Value is not null) + if (MSTestSettings.CurrentSettings.ConsiderFixturesAsSpecialTests) { - AddFixtureTests(testMethodInfo.Value, tests, fixturesTests); + AddFixtureTests(testMethodInfo, tests, fixturesTests); } - if (DynamicDataAttached(test, testMethodInfo, tests)) + if (TryUnfoldITestDataSources(test, testMethodInfo, dataSourcesUnfoldingStrategy, tests)) { continue; } @@ -265,51 +268,10 @@ private List DiscoverTestsInType( return tests; } - private Lazy GetTestMethodInfo(IDictionary sourceLevelParameters, UnitTestElement test) => - new(() => - { - // NOTE: From this place we don't have any path that would let the user write a message on the TestContext and we don't do - // anything with what would be printed anyway so we can simply use a simple StringWriter. - using var writer = new StringWriter(); - TestMethod testMethod = test.TestMethod; - MSTestAdapter.PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, sourceLevelParameters); - return _typeCache.GetTestMethodInfo(testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces); - }); - - private static bool DynamicDataAttached(UnitTestElement test, Lazy testMethodInfo, List tests) - { - // It should always be `true`, but if any part of the chain is obsolete; it might not contain those. - // Since we depend on those properties, if they don't exist, we bail out early. - if (!test.TestMethod.HasManagedMethodAndTypeProperties) - { - return false; - } - - DynamicDataType originalDataType = test.TestMethod.DataType; - - // PERF: For perf we started setting DataType in TypeEnumerator, so when it is None we will not reach this line. - // But if we do run this code, we still reset it to None, because the code that determines if this is data drive test expects the value to be None - // and only sets it when needed. - // - // If you remove this line and acceptance tests still pass you are okay. - test.TestMethod.DataType = DynamicDataType.None; - - // The data source tests that we can process currently are those using attributes that - // implement ITestDataSource (i.e, DataRow and DynamicData attributes). - // However, for DataSourceAttribute, we currently don't have anyway to process it during discovery. - // (Note: this method is only called under discoveryOption == TestDataSourceDiscoveryOption.DuringDiscovery) - // So we want to return false from this method for non ITestDataSource (whether it's None or DataSourceAttribute). Otherwise, the test - // will be completely skipped which is wrong behavior. - return originalDataType == DynamicDataType.ITestDataSource && - testMethodInfo.Value != null && - TryProcessITestDataSourceTests(test, testMethodInfo.Value, tests); - } - private static void AddFixtureTests(TestMethodInfo testMethodInfo, List tests, HashSet fixtureTests) { string assemblyName = testMethodInfo.Parent.Parent.Assembly.GetName().Name!; string assemblyLocation = testMethodInfo.Parent.Parent.Assembly.Location; - string className = testMethodInfo.Parent.ClassType.Name; string classFullName = testMethodInfo.Parent.ClassType.FullName!; // Check if fixtures for this assembly has already been added. @@ -320,13 +282,13 @@ private static void AddFixtureTests(TestMethodInfo testMethodInfo, List tests) + private static bool TryUnfoldITestDataSources(UnitTestElement test, TestMethodInfo testMethodInfo, TestDataSourceUnfoldingStrategy dataSourcesUnfoldingStrategy, List tests) { + // It should always be `true`, but if any part of the chain is obsolete; it might not contain those. + // Since we depend on those properties, if they don't exist, we bail out early. + if (!test.TestMethod.HasManagedMethodAndTypeProperties) + { + return false; + } + // We don't have a special method to filter attributes that are not derived from Attribute, so we take all // attributes and filter them. We don't have to care if there is one, because this method is only entered when // there is at least one (we determine this in TypeEnumerator.GetTestFromMethod. IEnumerable testDataSources = ReflectHelper.Instance.GetDerivedAttributes(testMethodInfo.MethodInfo, inherit: false).OfType(); + // We need to use a temporary list to avoid adding tests to the main list if we fail to expand any data source. + List tempListOfTests = new(); + try { - return ProcessITestDataSourceTests(test, new(testMethodInfo.MethodInfo, test.DisplayName), testDataSources, tests); + bool isDataDriven = false; + foreach (ITestDataSource dataSource in testDataSources) + { + isDataDriven = true; + if (!TryUnfoldITestDataSource(dataSource, dataSourcesUnfoldingStrategy, test, new(testMethodInfo.MethodInfo, test.DisplayName), tempListOfTests)) + { + // TODO: Improve multi-source design! + // Ideally we would want to consider each data source separately but when one source cannot be expanded, + // we will run all sources from the given method so we need to bail-out "globally". + return false; + } + } + + if (tempListOfTests.Count > 0) + { + tests.AddRange(tempListOfTests); + } + + return isDataDriven; } catch (Exception ex) { string message = string.Format(CultureInfo.CurrentCulture, Resource.CannotEnumerateIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, ex); PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo($"DynamicDataEnumerator: {message}"); + + if (tempListOfTests.Count > 0) + { + tests.AddRange(tempListOfTests); + } + return false; } } - private static bool ProcessITestDataSourceTests(UnitTestElement test, ReflectionTestMethodInfo methodInfo, IEnumerable testDataSources, - List tests) + private static bool TryUnfoldITestDataSource(ITestDataSource dataSource, TestDataSourceUnfoldingStrategy dataSourcesUnfoldingStrategy, UnitTestElement test, ReflectionTestMethodInfo methodInfo, List tests) { - foreach (ITestDataSource dataSource in testDataSources) + var unfoldingCapability = dataSource as ITestDataSourceUnfoldingCapability; + + // If the global strategy is to fold and local has no strategy or uses Auto then return false + if (dataSourcesUnfoldingStrategy == TestDataSourceUnfoldingStrategy.Fold + && (unfoldingCapability is null || unfoldingCapability.UnfoldingStrategy == TestDataSourceUnfoldingStrategy.Auto)) { - IEnumerable? data; + return false; + } - // This code is to discover tests. To run the tests code is in TestMethodRunner.ExecuteDataSourceBasedTests. - // Any change made here should be reflected in TestMethodRunner.ExecuteDataSourceBasedTests as well. - data = dataSource.GetData(methodInfo); + // If the data source specifies the unfolding strategy as fold then return false + if (unfoldingCapability?.UnfoldingStrategy == TestDataSourceUnfoldingStrategy.Fold) + { + return false; + } + + // Otherwise, unfold the data source and verify it can be serialized. + IEnumerable? data; + + // This code is to discover tests. To run the tests code is in TestMethodRunner.ExecuteDataSourceBasedTests. + // Any change made here should be reflected in TestMethodRunner.ExecuteDataSourceBasedTests as well. + data = dataSource.GetData(methodInfo); + string? testDataSourceIgnoreMessage = (dataSource as ITestDataSourceIgnoreCapability)?.IgnoreMessage; - if (!data.Any()) + if (!data.Any()) + { + if (!MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) { - if (!MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) - { - throw dataSource.GetExceptionForEmptyDataSource(methodInfo); - } + throw dataSource.GetExceptionForEmptyDataSource(methodInfo); + } - UnitTestElement discoveredTest = test.Clone(); - // Make the test not data driven, because it had no data. - discoveredTest.TestMethod.DataType = DynamicDataType.None; - discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, null) ?? discoveredTest.DisplayName; + UnitTestElement discoveredTest = test.Clone(); + // Make the test not data driven, because it had no data. + discoveredTest.TestMethod.DataType = DynamicDataType.None; + discoveredTest.TestMethod.TestDataSourceIgnoreMessage = testDataSourceIgnoreMessage; + discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, null) ?? discoveredTest.DisplayName; - tests.Add(discoveredTest); + tests.Add(discoveredTest); - continue; - } + return true; + } - var testDisplayNameFirstSeen = new Dictionary(); - var discoveredTests = new List(); - int index = 0; + var testDisplayNameFirstSeen = new Dictionary(); + var discoveredTests = new List(); + int index = 0; - foreach (object?[] d in data) - { - UnitTestElement discoveredTest = test.Clone(); - discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, d) ?? discoveredTest.DisplayName; + foreach (object?[] d in data) + { + UnitTestElement discoveredTest = test.Clone(); + discoveredTest.DisplayName = dataSource.GetDisplayName(methodInfo, d) ?? discoveredTest.DisplayName; - // If strategy is DisplayName and we have a duplicate test name don't expand the test, bail out. + // If strategy is DisplayName and we have a duplicate test name don't expand the test, bail out. #pragma warning disable CS0618 // Type or member is obsolete - if (test.TestMethod.TestIdGenerationStrategy == TestIdGenerationStrategy.DisplayName - && testDisplayNameFirstSeen.TryGetValue(discoveredTest.DisplayName!, out int firstIndexSeen)) - { - string warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_DuplicateDisplayName, firstIndexSeen, index, discoveredTest.DisplayName); - warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning); - PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumerator: {warning}"); + if (test.TestMethod.TestIdGenerationStrategy == TestIdGenerationStrategy.DisplayName + && testDisplayNameFirstSeen.TryGetValue(discoveredTest.DisplayName!, out int firstIndexSeen)) + { + string warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_DuplicateDisplayName, firstIndexSeen, index, discoveredTest.DisplayName); + warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning); + PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumerator: {warning}"); - // Duplicated display name so bail out. Caller will handle adding the original test. - return false; - } + // Duplicated display name so bail out. Caller will handle adding the original test. + return false; + } #pragma warning restore CS0618 // Type or member is obsolete - try - { - discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(d); - discoveredTest.TestMethod.DataType = DynamicDataType.ITestDataSource; - } - catch (SerializationException ex) - { - string warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_CannotSerialize, index, discoveredTest.DisplayName); - warning += Environment.NewLine; - warning += ex.ToString(); - warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning); - PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumerator: {warning}"); - - // Serialization failed for the type, bail out. Caller will handle adding the original test. - return false; - } - - discoveredTests.Add(discoveredTest); - testDisplayNameFirstSeen[discoveredTest.DisplayName!] = index++; + try + { + discoveredTest.TestMethod.SerializedData = DataSerializationHelper.Serialize(d); + discoveredTest.TestMethod.TestDataSourceIgnoreMessage = testDataSourceIgnoreMessage; + discoveredTest.TestMethod.DataType = DynamicDataType.ITestDataSource; + } + catch (SerializationException ex) + { + string warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute_CannotSerialize, index, discoveredTest.DisplayName); + warning += Environment.NewLine; + warning += ex.ToString(); + warning = string.Format(CultureInfo.CurrentCulture, Resource.CannotExpandIDataSourceAttribute, test.TestMethod.ManagedTypeName, test.TestMethod.ManagedMethodName, warning); + PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning($"DynamicDataEnumerator: {warning}"); + + // Serialization failed for the type, bail out. Caller will handle adding the original test. + return false; } - tests.AddRange(discoveredTests); + discoveredTests.Add(discoveredTest); + testDisplayNameFirstSeen[discoveredTest.DisplayName!] = index++; } + tests.AddRange(discoveredTests); + return true; } } diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs index 357a0eb456..53ec74c28f 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/AssemblyEnumeratorWrapper.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,7 +10,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; /// /// Enumerates through an assembly to get a set of test methods. /// -internal class AssemblyEnumeratorWrapper +internal sealed class AssemblyEnumeratorWrapper { /// /// Assembly name for UTF. @@ -27,7 +24,7 @@ internal class AssemblyEnumeratorWrapper /// The run Settings. /// Contains warnings if any, that need to be passed back to the caller. /// A collection of test elements. - internal ICollection? GetTests(string? assemblyFileName, IRunSettings? runSettings, out ICollection warnings) + internal ICollection? GetTests(string? assemblyFileName, IRunSettings? runSettings, out List warnings) { warnings = new List(); @@ -52,7 +49,7 @@ internal class AssemblyEnumeratorWrapper } // Load the assembly in isolation if required. - return GetTestsInIsolation(fullFilePath, runSettings, out warnings); + return GetTestsInIsolation(fullFilePath, runSettings, warnings); } catch (FileNotFoundException ex) { @@ -98,7 +95,7 @@ internal class AssemblyEnumeratorWrapper } } - private static ICollection GetTestsInIsolation(string fullFilePath, IRunSettings? runSettings, out ICollection warnings) + private static ICollection GetTestsInIsolation(string fullFilePath, IRunSettings? runSettings, List warnings) { using MSTestAdapter.PlatformServices.Interface.ITestSourceHost isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings, frameworkHandle: null); @@ -107,15 +104,16 @@ private static ICollection GetTestsInIsolation(string fullFileP // This might not be supported if an older version of Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices // assembly is already loaded into the App Domain. + string? xml = null; try { - assemblyEnumerator.RunSettingsXml = runSettings?.SettingsXml; + xml = runSettings?.SettingsXml; } catch { PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(Resource.OlderTFMVersionFound); } - return assemblyEnumerator.EnumerateAssembly(fullFilePath, out warnings); + return assemblyEnumerator.EnumerateAssembly(fullFilePath, xml, warnings); } } diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs index 861d108ab4..2669c119ad 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/TestMethodValidator.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,6 +10,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; /// /// Determines if a method is a valid test method. /// +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class TestMethodValidator { private readonly ReflectHelper _reflectHelper; @@ -60,22 +58,12 @@ internal virtual bool IsValidTestMethod(MethodInfo testMethodInfo, Type type, IC return false; } - // Generic method Definitions are not valid. - if (testMethodInfo.IsGenericMethodDefinition) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, testMethodInfo.DeclaringType!.FullName, testMethodInfo.Name); - warnings.Add(message); - return false; - } - bool isAccessible = testMethodInfo.IsPublic || (_discoverInternals && testMethodInfo.IsAssembly); // Todo: Decide whether parameter count matters. - // The isGenericMethod check below id to verify that there are no closed generic methods slipping through. - // Closed generic methods being GenericMethod and open being GenericMethod. bool isValidTestMethod = isAccessible && - testMethodInfo is { IsAbstract: false, IsStatic: false, IsGenericMethod: false } && + testMethodInfo is { IsAbstract: false, IsStatic: false } && testMethodInfo.IsValidReturnType(); if (!isValidTestMethod) diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs index 2cfc50d46b..cbb50767f7 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/TypeEnumerator.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.ObjectModel; -using System.Globalization; -using System.Reflection; - using Microsoft.TestPlatform.AdapterUtilities; using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; @@ -17,6 +13,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; /// /// Enumerates through the type looking for Valid Test Methods to execute. /// +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class TypeEnumerator { private readonly Type _type; @@ -24,7 +21,6 @@ internal class TypeEnumerator private readonly TypeValidator _typeValidator; private readonly TestMethodValidator _testMethodValidator; private readonly TestIdGenerationStrategy _testIdGenerationStrategy; - private readonly TestDataSourceDiscoveryOption _discoveryOption; private readonly ReflectHelper _reflectHelper; /// @@ -36,7 +32,7 @@ internal class TypeEnumerator /// The validator for test classes. /// The validator for test methods. /// to use when generating TestId. - internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator, TestDataSourceDiscoveryOption discoveryOption, TestIdGenerationStrategy testIdGenerationStrategy) + internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator, TestIdGenerationStrategy testIdGenerationStrategy) { _type = type; _assemblyFilePath = assemblyFilePath; @@ -44,7 +40,6 @@ internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflec _typeValidator = typeValidator; _testMethodValidator = testMethodValidator; _testIdGenerationStrategy = testIdGenerationStrategy; - _discoveryOption = discoveryOption; } /// @@ -52,10 +47,8 @@ internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflec /// /// Contains warnings if any, that need to be passed back to the caller. /// list of test cases. - internal virtual ICollection? Enumerate(out ICollection warnings) + internal virtual List? Enumerate(List warnings) { - warnings = new Collection(); - if (!_typeValidator.IsValidTestClass(_type, warnings)) { return null; @@ -70,11 +63,11 @@ internal TypeEnumerator(Type type, string assemblyFilePath, ReflectHelper reflec /// /// Contains warnings if any, that need to be passed back to the caller. /// List of Valid Tests. - internal Collection GetTests(ICollection warnings) + internal List GetTests(List warnings) { bool foundDuplicateTests = false; var foundTests = new HashSet(); - var tests = new Collection(); + var tests = new List(); // Test class is already valid. Verify methods. // PERF: GetRuntimeMethods is used here to get all methods, including non-public, and static methods. @@ -117,12 +110,11 @@ internal Collection GetTests(ICollection warnings) currentType = currentType.BaseType; } - return new Collection( - tests.GroupBy( - t => t.TestMethod.Name, - (_, elements) => - elements.OrderBy(t => inheritanceDepths[t.TestMethod.DeclaringClassFullName ?? t.TestMethod.FullClassName]).First()) - .ToList()); + return tests.GroupBy( + t => t.TestMethod.Name, + (_, elements) => + elements.OrderBy(t => inheritanceDepths[t.TestMethod.DeclaringClassFullName ?? t.TestMethod.FullClassName]).First()) + .ToList(); } /// @@ -156,22 +148,6 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT method.DeclaringType.Assembly); } - // PERF: When discovery option is set to DuringDiscovery, we will expand data on tests to one test case - // per data item. This will happen in AssemblyEnumerator. But AssemblyEnumerator does not have direct access to - // the method info or method attributes, so it would create a TestMethodInfo to see if the test is data driven. - // Creating TestMethodInfo is expensive and should be done only for a test that we know is data driven. - // - // So to optimize this we check if we have some data source attribute. Because here we have access to all attributes - // and we store that info in DataType. AssemblyEnumerator will pick this up and will get the real test data in the expensive way - // or it will skip over getting the data cheaply, when DataType = DynamicDataType.None. - // - // This needs to be done only when DuringDiscovery is set, because otherwise we would populate the DataType, but we would not populate - // and execution would not try to re-populate the data, because DataType is already set to data driven, so it would just throw error about empty data. - if (_discoveryOption == TestDataSourceDiscoveryOption.DuringDiscovery) - { - testMethod.DataType = GetDynamicDataType(method); - } - var testElement = new UnitTestElement(testMethod) { // Get compiler generated type name for async test method (either void returning or task returning). @@ -179,7 +155,6 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT TestCategory = _reflectHelper.GetTestCategories(method, _type), DoNotParallelize = _reflectHelper.IsDoNotParallelizeSet(method, _type), Priority = _reflectHelper.GetPriority(method), - Ignored = _reflectHelper.IsNonDerivedAttributeDefined(method, inherit: false), DeploymentItems = PlatformServiceProvider.Instance.TestDeployment.GetDeploymentItems(method, _type, warnings), }; @@ -199,49 +174,45 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT testElement.Traits = traits.ToArray(); - if (_reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true) is CssIterationAttribute cssIteration) - { - testElement.CssIteration = cssIteration.CssIteration; - } + Attribute[] attributes = _reflectHelper.GetCustomAttributesCached(method, inherit: true); + TestMethodAttribute? testMethodAttribute = null; - if (_reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true) is CssProjectStructureAttribute cssProjectStructure) + // Backward looping for backcompat. This used to be calls to _reflectHelper.GetFirstDerivedAttributeOrDefault + // So, to make sure the first attribute always wins, we loop from end to start. + for (int i = attributes.Length - 1; i >= 0; i--) { - testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure; - } - - if (_reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true) is DescriptionAttribute descriptionAttribute) - { - testElement.Description = descriptionAttribute.Description; + if (attributes[i] is TestMethodAttribute tma) + { + testMethodAttribute = tma; + } + else if (attributes[i] is CssIterationAttribute cssIteration) + { + testElement.CssIteration = cssIteration.CssIteration; + } + else if (attributes[i] is CssProjectStructureAttribute cssProjectStructure) + { + testElement.CssProjectStructure = cssProjectStructure.CssProjectStructure; + } + else if (attributes[i] is DescriptionAttribute descriptionAttribute) + { + testElement.Description = descriptionAttribute.Description; + } } - WorkItemAttribute[] workItemAttributes = _reflectHelper.GetDerivedAttributes(method, inherit: true).ToArray(); - if (workItemAttributes.Length != 0) + IEnumerable workItemAttributes = attributes.OfType(); + if (workItemAttributes.Any()) { testElement.WorkItemIds = workItemAttributes.Select(x => x.Id.ToString(CultureInfo.InvariantCulture)).ToArray(); } + // In production, we always have a TestMethod attribute because GetTestFromMethod is called under IsValidTestMethod + // In unit tests, we may not have the test to have TestMethodAttribute. + // TODO: Adjust all unit tests to properly have the attribute and uncomment the assert. + // DebugEx.Assert(testMethodAttribute is not null, "Expected to find a 'TestMethod' attribute."); + // get DisplayName from TestMethodAttribute (or any inherited attribute) - TestMethodAttribute? testMethodAttribute = _reflectHelper.GetFirstDerivedAttributeOrDefault(method, inherit: true); testElement.DisplayName = testMethodAttribute?.DisplayName ?? method.Name; return testElement; } - - private DynamicDataType GetDynamicDataType(MethodInfo method) - { - foreach (Attribute attribute in _reflectHelper.GetDerivedAttributes(method, inherit: true)) - { - if (AttributeComparer.IsDerived(attribute)) - { - return DynamicDataType.ITestDataSource; - } - - if (AttributeComparer.IsDerived(attribute)) - { - return DynamicDataType.DataSourceAttribute; - } - } - - return DynamicDataType.None; - } } diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs b/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs index f9f45eea1b..9e8e1c98d6 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/TypeValidator.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -12,6 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; /// /// Determines whether a type is a valid test class for this adapter. /// +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class TypeValidator { // Setting this to a string representation instead of a typeof(TestContext).FullName @@ -47,7 +45,7 @@ internal TypeValidator(ReflectHelper reflectHelper, bool discoverInternals) /// The reflected type. /// Contains warnings if any, that need to be passed back to the caller. /// Return true if it is a valid test class. - internal virtual bool IsValidTestClass(Type type, ICollection warnings) + internal virtual bool IsValidTestClass(Type type, List warnings) { // PERF: We are doing caching reflection here, meaning we will cache every class info in the // assembly, this is because when we discover and run we will repeatedly inspect all the types in the assembly, and this @@ -104,7 +102,7 @@ internal static bool HasCorrectTestContextSignature(Type type) { DebugEx.Assert(type != null, "HasCorrectTestContextSignature type is null"); - IEnumerable propertyInfoEnumerable = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredProperties(type); + PropertyInfo[] propertyInfoEnumerable = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredProperties(type); var propertyInfo = new List(); foreach (PropertyInfo pinfo in propertyInfoEnumerable) diff --git a/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs b/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs index e9ff1ccdc1..b3b92c1548 100644 --- a/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs +++ b/src/Adapter/MSTest.TestAdapter/Discovery/UnitTestDiscoverer.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -12,6 +10,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class UnitTestDiscoverer { private readonly AssemblyEnumeratorWrapper _assemblyEnumeratorWrapper; @@ -56,7 +55,7 @@ internal virtual void DiscoverTestsInSource( ITestCaseDiscoverySink discoverySink, IDiscoveryContext? discoveryContext) { - ICollection? testElements = _assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out ICollection? warnings); + ICollection? testElements = _assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out List warnings); bool treatDiscoveryWarningsAsErrors = MSTestSettings.CurrentSettings.TreatDiscoveryWarningsAsErrors; diff --git a/src/Adapter/MSTest.TestAdapter/DynamicDataOperations.cs b/src/Adapter/MSTest.TestAdapter/DynamicDataOperations.cs index a666067292..954b9b435e 100644 --- a/src/Adapter/MSTest.TestAdapter/DynamicDataOperations.cs +++ b/src/Adapter/MSTest.TestAdapter/DynamicDataOperations.cs @@ -1,16 +1,8 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.VisualStudio.TestTools.UnitTesting; -#if NET471_OR_GREATER || NETCOREAPP -using System.Collections; -using System.Runtime.CompilerServices; -#endif -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; internal class DynamicDataOperations : IDynamicDataOperations @@ -25,36 +17,35 @@ public IEnumerable GetData(Type? _dynamicDataDeclaringType, DynamicDat switch (_dynamicDataSourceType) { - case DynamicDataSourceType.Property: - PropertyInfo property = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredProperty(_dynamicDataDeclaringType, _dynamicDataSourceName) - ?? throw new ArgumentNullException($"{DynamicDataSourceType.Property} {_dynamicDataSourceName}"); - if (property.GetGetMethod(true) is not { IsStatic: true }) + case DynamicDataSourceType.AutoDetect: +#pragma warning disable IDE0045 // Convert to conditional expression - it becomes less readable. + if (GetPropertyConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) is { } dynamicDataPropertyInfo) + { + obj = GetDataFromProperty(dynamicDataPropertyInfo); + } + else if (GetMethodConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) is { } dynamicDataMethodInfo) + { + obj = GetDataFromMethod(dynamicDataMethodInfo); + } + else { - throw new NotSupportedException( - string.Format( - CultureInfo.InvariantCulture, - FrameworkMessages.DynamicDataInvalidPropertyLayout, - property.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{property.Name}" : property.Name)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resource.DynamicDataSourceShouldExistAndBeValid, _dynamicDataSourceName, _dynamicDataDeclaringType.FullName)); } +#pragma warning restore IDE0045 // Convert to conditional expression - obj = property.GetValue(null, null); + break; + case DynamicDataSourceType.Property: + PropertyInfo property = GetPropertyConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) + ?? throw new ArgumentNullException($"{DynamicDataSourceType.Property} {_dynamicDataSourceName}"); + + obj = GetDataFromProperty(property); break; case DynamicDataSourceType.Method: - MethodInfo method = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredMethod(_dynamicDataDeclaringType, _dynamicDataSourceName) + MethodInfo method = GetMethodConsideringInheritance(_dynamicDataDeclaringType, _dynamicDataSourceName) ?? throw new ArgumentNullException($"{DynamicDataSourceType.Method} {_dynamicDataSourceName}"); - if (!method.IsStatic - || method.ContainsGenericParameters - || method.GetParameters().Length > 0) - { - throw new NotSupportedException( - string.Format( - CultureInfo.InvariantCulture, - FrameworkMessages.DynamicDataInvalidPropertyLayout, - method.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{method.Name}" : method.Name)); - } - obj = method.Invoke(null, null); + obj = GetDataFromMethod(method); break; } @@ -82,6 +73,38 @@ public IEnumerable GetData(Type? _dynamicDataDeclaringType, DynamicDat return data; } + private static object? GetDataFromMethod(MethodInfo method) + { + if (!method.IsStatic + || method.ContainsGenericParameters + || method.GetParameters().Length > 0) + { + throw new NotSupportedException( + string.Format( + CultureInfo.InvariantCulture, + FrameworkMessages.DynamicDataInvalidPropertyLayout, + method.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{method.Name}" : method.Name)); + } + + // Note: the method is static and takes no parameters. + return method.Invoke(null, null); + } + + private static object? GetDataFromProperty(PropertyInfo property) + { + if (property.GetGetMethod(true) is not { IsStatic: true }) + { + throw new NotSupportedException( + string.Format( + CultureInfo.InvariantCulture, + FrameworkMessages.DynamicDataInvalidPropertyLayout, + property.DeclaringType?.FullName is { } typeFullName ? $"{typeFullName}.{property.Name}" : property.Name)); + } + + // Note: the property getter is static. + return property.GetValue(null, null); + } + /// public string? GetDisplayName(string? DynamicDataDisplayName, Type? DynamicDataDisplayNameDeclaringType, MethodInfo methodInfo, object?[]? data) { @@ -129,34 +152,139 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume return true; } -#if NETCOREAPP || NET471_OR_GREATER - if (dataSource is IEnumerable enumerable) + if (dataSource is IEnumerable enumerable and not string) { List objects = new(); foreach (object? entry in enumerable) { - if (entry is not ITuple tuple - || (objects.Count > 0 && objects[^1].Length != tuple.Length)) + if (entry is null) { data = null; return false; } - object[] array = new object[tuple.Length]; - for (int i = 0; i < tuple.Length; i++) + if (!TryHandleTupleDataSource(entry, objects)) { - array[i] = tuple[i]!; + objects.Add(new[] { entry }); } - - objects.Add(array); } data = objects; return true; } -#endif data = null; return false; } + + private static bool TryHandleTupleDataSource(object data, List objects) + { +#if NET471_OR_GREATER || NETCOREAPP + if (data is ITuple tuple + && (objects.Count == 0 || objects[^1].Length == tuple.Length)) + { + object[] array = new object[tuple.Length]; + for (int i = 0; i < tuple.Length; i++) + { + array[i] = tuple[i]!; + } + + objects.Add(array); + return true; + } +#else + Type type = data.GetType(); + if (IsTupleOrValueTuple(data.GetType(), out int tupleSize) + && (objects.Count == 0 || objects[objects.Count - 1].Length == tupleSize)) + { + object[] array = new object[tupleSize]; + for (int i = 0; i < tupleSize; i++) + { + array[i] = type.GetField($"Item{i + 1}")?.GetValue(data)!; + } + + objects.Add(array); + return true; + } +#endif + + return false; + } + +#if !NET471_OR_GREATER && !NETCOREAPP + private static bool IsTupleOrValueTuple(Type type, out int tupleSize) + { + tupleSize = 0; + if (!type.IsGenericType) + { + return false; + } + + Type genericTypeDefinition = type.GetGenericTypeDefinition(); + + if (genericTypeDefinition == typeof(Tuple<>) || + genericTypeDefinition == typeof(Tuple<,>) || + genericTypeDefinition == typeof(Tuple<,,>) || + genericTypeDefinition == typeof(Tuple<,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,,,>) || + genericTypeDefinition == typeof(Tuple<,,,,,,,>)) + { + tupleSize = type.GetGenericArguments().Length; + return true; + } + + if (genericTypeDefinition == typeof(ValueTuple<>) || + genericTypeDefinition == typeof(ValueTuple<,>) || + genericTypeDefinition == typeof(ValueTuple<,,>) || + genericTypeDefinition == typeof(ValueTuple<,,,>) || + genericTypeDefinition == typeof(ValueTuple<,,,,>) || + genericTypeDefinition == typeof(ValueTuple<,,,,,>) || + genericTypeDefinition == typeof(ValueTuple<,,,,,,>) || + genericTypeDefinition == typeof(ValueTuple<,,,,,,,>)) + { + tupleSize = type.GetGenericArguments().Length; + return true; + } + + return false; + } +#endif + + private static PropertyInfo? GetPropertyConsideringInheritance(Type type, string propertyName) + { + // NOTE: Don't use GetRuntimeProperty. It considers inheritance only for instance properties. + Type? currentType = type; + while (currentType is not null) + { + PropertyInfo? property = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredProperty(currentType, propertyName); + if (property is not null) + { + return property; + } + + currentType = currentType.BaseType; + } + + return null; + } + + private static MethodInfo? GetMethodConsideringInheritance(Type type, string methodName) + { + // NOTE: Don't use GetRuntimeMethod. It considers inheritance only for instance methods. + Type? currentType = type; + while (currentType is not null) + { + MethodInfo? method = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredMethod(currentType, methodName); + if (method is not null) + { + return method; + } + + currentType = currentType.BaseType; + } + + return null; + } } diff --git a/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs b/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs index 8f6d5d75f2..86151717b6 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/ClassCleanupManager.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; @@ -62,4 +62,21 @@ public void MarkTestComplete(TestMethodInfo testMethodInfo, TestMethod testMetho ShouldRunEndOfAssemblyCleanup = _remainingTestsByClass.IsEmpty; } } + + internal static void ForceCleanup(TypeCache typeCache, IDictionary sourceLevelParameters, IMessageLogger logger) + { + using var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture, "context"); + TestContext testContext = new TestContextImplementation(null, writer, sourceLevelParameters, logger); + IEnumerable classInfoCache = typeCache.ClassInfoListWithExecutableCleanupMethods; + foreach (TestClassInfo classInfo in classInfoCache) + { + classInfo.ExecuteClassCleanup(testContext); + } + + IEnumerable assemblyInfoCache = typeCache.AssemblyInfoListWithExecutableCleanupMethods; + foreach (TestAssemblyInfo assemblyInfo in assemblyInfoCache) + { + assemblyInfo.ExecuteAssemblyCleanup(testContext); + } + } } diff --git a/src/Adapter/MSTest.TestAdapter/Execution/ExceptionHelper.cs b/src/Adapter/MSTest.TestAdapter/Execution/ExceptionHelper.cs index 1bf1e69189..0c36b4e56f 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/ExceptionHelper.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/ExceptionHelper.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -77,12 +73,14 @@ internal static class ExceptionHelper bool first = true; while (stackTraces.Count != 0) { - result.AppendFormat( - CultureInfo.CurrentCulture, - "{0} {1}{2}", - first ? string.Empty : (Resource.UTA_EndOfInnerExceptionTrace + Environment.NewLine), - stackTraces.Pop(), - Environment.NewLine); + if (!first) + { + result.AppendLine(Resource.UTA_EndOfInnerExceptionTrace); + } + + result.Append(' '); + result.AppendLine(stackTraces.Pop()); + first = false; } @@ -138,7 +136,7 @@ internal static string TrimStackTrace(string stackTrace) /// /// The aggregated exception message that considers inner exceptions. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] internal static string GetFormattedExceptionMessage(this Exception ex) { DebugEx.Assert(ex != null, "exception should not be null."); diff --git a/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs b/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs index 584d98446a..7e3dc95d39 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/LogMessageListener.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; @@ -14,9 +11,16 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// Listens for log messages and Debug.WriteLine /// Note that this class is not thread-safe and thus should only be used when unit tests are being run serially. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class LogMessageListener : IDisposable { - private static readonly object TraceLock = new(); + private static readonly Lock TraceLock = new(); private static int s_listenerCount; private static ThreadSafeStringWriter? s_redirectedDebugTrace; diff --git a/src/Adapter/MSTest.TestAdapter/Execution/RunCleanupResult.cs b/src/Adapter/MSTest.TestAdapter/Execution/RunCleanupResult.cs index edf7534eff..11e1dedcea 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/RunCleanupResult.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/RunCleanupResult.cs @@ -7,7 +7,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// Result of the run cleanup operation. /// [Serializable] -internal class RunCleanupResult +internal sealed class RunCleanupResult { /// /// Gets or sets the standard out of the cleanup methods. diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs index 4cdc59959c..8da4e5c3de 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblyInfo.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; @@ -18,19 +14,23 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// Defines TestAssembly Info object. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestAssemblyInfo { - private readonly object _assemblyInfoExecuteSyncObject; + private readonly Lock _assemblyInfoExecuteSyncObject = new(); /// /// Initializes a new instance of the class. /// /// Sets the this class is representing. internal TestAssemblyInfo(Assembly assembly) - { - _assemblyInfoExecuteSyncObject = new object(); - Assembly = assembly; - } + => Assembly = assembly; /// /// Gets AssemblyInitialize method for the assembly. @@ -260,7 +260,7 @@ public void RunAssemblyInitialize(TestContext testContext) /// It is a replacement for RunAssemblyCleanup but as we are in a bug-fix version, we do not want to touch /// public API and so we introduced this method. /// - internal void ExecuteAssemblyCleanup() + internal void ExecuteAssemblyCleanup(TestContext testContext) { if (AssemblyCleanupMethod == null) { @@ -272,8 +272,18 @@ internal void ExecuteAssemblyCleanup() try { AssemblyCleanupException = FixtureMethodRunner.RunWithTimeoutAndCancellation( - () => AssemblyCleanupMethod.InvokeAsSynchronousTask(null), - new CancellationTokenSource(), + () => + { + if (AssemblyCleanupMethod.GetParameters().Length == 0) + { + AssemblyCleanupMethod.InvokeAsSynchronousTask(null); + } + else + { + AssemblyCleanupMethod.InvokeAsSynchronousTask(null, testContext); + } + }, + testContext.CancellationTokenSource, AssemblyCleanupMethodTimeoutMilliseconds, AssemblyCleanupMethod, new AssemblyExecutionContextScope(isCleanup: true), diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblySettingsProvider.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblySettingsProvider.cs index 043ba32aa7..4fb24e115c 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblySettingsProvider.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestAssemblySettingsProvider.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; using System.Security; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -10,7 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; -internal class TestAssemblySettingsProvider : MarshalByRefObject +internal sealed class TestAssemblySettingsProvider : MarshalByRefObject { /// /// Returns object to be used for controlling lifetime, null means infinite lifetime. diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestCaseDiscoverySink.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestCaseDiscoverySink.cs index 1ba6f9adbe..20376c53dc 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestCaseDiscoverySink.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestCaseDiscoverySink.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -11,17 +9,12 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// The test case discovery sink. /// -internal class TestCaseDiscoverySink : ITestCaseDiscoverySink +internal sealed class TestCaseDiscoverySink : ITestCaseDiscoverySink { - /// - /// Initializes a new instance of the class. - /// - public TestCaseDiscoverySink() => Tests = new Collection(); - /// /// Gets the tests. /// - public ICollection Tests { get; } + public ICollection Tests { get; } = new List(); /// /// Sends the test case. diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs index e5de71475b..b3c6510e62 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestClassInfo.cs @@ -1,11 +1,6 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.InteropServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; @@ -20,30 +15,42 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// Defines the TestClassInfo object. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestClassInfo { - private readonly object _testClassExecuteSyncObject = new(); + /// + /// Test context property name. + /// + private const string TestContextPropertyName = "TestContext"; + + private readonly Lock _testClassExecuteSyncObject = new(); + + private UnitTestResult? _classInitializeResult; /// /// Initializes a new instance of the class. /// /// Underlying test class type. /// Constructor for the test class. - /// Reference to the property in test class. /// Test class attribute. /// Parent assembly info. internal TestClassInfo( Type type, ConstructorInfo constructor, bool isParameterlessConstructor, - PropertyInfo? testContextProperty, TestClassAttribute classAttribute, TestAssemblyInfo parent) { ClassType = type; Constructor = constructor; IsParameterlessConstructor = isParameterlessConstructor; - TestContextProperty = testContextProperty; + TestContextProperty = ResolveTestContext(type); Parent = parent; ClassAttribute = classAttribute; } @@ -247,6 +254,7 @@ public void RunClassInitialize(TestContext testContext) // If no class initialize and no base class initialize, return if (ClassInitializeMethod is null && BaseClassInitMethods.Count == 0) { + IsClassInitializeExecuted = true; return; } @@ -338,22 +346,62 @@ public void RunClassInitialize(TestContext testContext) throw testFailedException; } + private UnitTestResult? TryGetClonedCachedClassInitializeResult() + { + // Historically, we were not caching class initialize result, and were always going through the logic in GetResultOrRunClassInitialize. + // When caching is introduced, we found out that using the cached instance can change the behavior in some cases. For example, + // if you have Console.WriteLine in class initialize, those will be present on the UnitTestResult. + // Before caching was introduced, these logs will be only in the first class initialize result (attached to the first test run in class) + // By re-using the cached instance, it's now part of all tests. + // To preserve the original behavior, we clone the cached instance so we keep only the information we are sure should be reused. + if (_classInitializeResult is null) + { + return null; + } + + if (_classInitializeResult.ErrorStackTrace is not null && _classInitializeResult.ErrorMessage is not null) + { + return new( + new TestFailedException( + _classInitializeResult.Outcome, + _classInitializeResult.ErrorMessage, + new StackTraceInformation(_classInitializeResult.ErrorStackTrace, _classInitializeResult.ErrorFilePath, _classInitializeResult.ErrorLineNumber, _classInitializeResult.ErrorColumnNumber))); + } + + // We are expecting this to be hit for the case of "Passed". + // It's unlikely to be hit otherwise (GetResultOrRunClassInitialize appears to only create either Passed results or a result from exception), but + // we can still create UnitTestResult from outcome and error message (which is expected to be null for Passed). + return new(_classInitializeResult.Outcome, _classInitializeResult.ErrorMessage); + } + internal UnitTestResult GetResultOrRunClassInitialize(ITestContext testContext, string initializationLogs, string initializationErrorLogs, string initializationTrace, string initializationTestContextMessages) { + UnitTestResult? clonedInitializeResult = TryGetClonedCachedClassInitializeResult(); + + // Optimization: If we already ran before and know the result, return it. + if (clonedInitializeResult is not null) + { + DebugEx.Assert(IsClassInitializeExecuted, "Class initialize result should be available if and only if class initialize was executed"); + return clonedInitializeResult; + } + + DebugEx.Assert(!IsClassInitializeExecuted, "If class initialize was executed, we should have been in the previous if were we have a result available."); + + // For optimization purposes, return right away if there is nothing to execute. + // For STA, this avoids starting a thread when we know it will do nothing. + // But we still return early even not STA. + if (ClassInitializeMethod is null && BaseClassInitMethods.Count == 0) + { + IsClassInitializeExecuted = true; + return _classInitializeResult = new(ObjectModelUnitTestOutcome.Passed, null); + } + bool isSTATestClass = AttributeComparer.IsDerived(ClassAttribute); bool isWindowsOS = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (isSTATestClass && isWindowsOS && Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) { - // For optimization purposes, we duplicate some of the logic of RunClassInitialize here so we don't need to start - // a thread for nothing. - if ((ClassInitializeMethod is null && BaseClassInitMethods.Count == 0) - || IsClassInitializeExecuted) - { - return DoRun(); - } - UnitTestResult result = new(ObjectModelUnitTestOutcome.Error, "MSTest STATestClass ClassInitialize didn't complete"); Thread entryPointThread = new(() => result = DoRun()) { @@ -423,6 +471,7 @@ UnitTestResult DoRun() result.TestContextMessages = initializationTestContextMessages; } + _classInitializeResult = result; return result; } } @@ -483,12 +532,12 @@ UnitTestResult DoRun() try { classCleanupMethod = ClassCleanupMethod; - ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod, BaseClassCleanupMethods.Count) : null; + ClassCleanupException = classCleanupMethod is not null ? InvokeCleanupMethod(classCleanupMethod, BaseClassCleanupMethods.Count, null!) : null; var baseClassCleanupQueue = new Queue(BaseClassCleanupMethods); while (baseClassCleanupQueue.Count > 0 && ClassCleanupException is null) { classCleanupMethod = baseClassCleanupQueue.Dequeue(); - ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, baseClassCleanupQueue.Count); + ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, baseClassCleanupQueue.Count, null!); } IsClassCleanupExecuted = ClassCleanupException is null; @@ -540,7 +589,7 @@ UnitTestResult DoRun() /// This is a replacement for RunClassCleanup but as we are on a bug fix version, we do not want to change /// the public API, hence this method. /// - internal void ExecuteClassCleanup() + internal void ExecuteClassCleanup(TestContext testContext) { if ((ClassCleanupMethod is null && BaseClassCleanupMethods.Count == 0) || IsClassCleanupExecuted) @@ -553,8 +602,10 @@ internal void ExecuteClassCleanup() lock (_testClassExecuteSyncObject) { if (IsClassCleanupExecuted - // If there is a ClassInitialize method and it has not been executed, then we should not execute ClassCleanup - || (!IsClassInitializeExecuted && ClassInitializeMethod is not null)) + // If ClassInitialize method has not been executed, then we should not execute ClassCleanup + // Note that if there is no ClassInitialze method at all, we will still set + // IsClassInitializeExecuted to true in RunClassInitialize + || !IsClassInitializeExecuted) { return; } @@ -563,9 +614,10 @@ internal void ExecuteClassCleanup() { if (classCleanupMethod is not null) { - if (!ReflectHelper.Instance.IsNonDerivedAttributeDefined(classCleanupMethod.DeclaringType!, false)) + if (ClassAttribute.IgnoreMessage is null && + !ReflectHelper.Instance.IsNonDerivedAttributeDefined(classCleanupMethod.DeclaringType!, false)) { - ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, remainingCleanupCount: BaseClassCleanupMethods.Count); + ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, remainingCleanupCount: BaseClassCleanupMethods.Count, testContext); } } @@ -574,9 +626,10 @@ internal void ExecuteClassCleanup() for (int i = 0; i < BaseClassCleanupMethods.Count; i++) { classCleanupMethod = BaseClassCleanupMethods[i]; - if (!ReflectHelper.Instance.IsNonDerivedAttributeDefined(classCleanupMethod.DeclaringType!, false)) + if (ClassAttribute.IgnoreMessage is null && + !ReflectHelper.Instance.IsNonDerivedAttributeDefined(classCleanupMethod.DeclaringType!, false)) { - ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, remainingCleanupCount: BaseClassCleanupMethods.Count - 1 - i); + ClassCleanupException = InvokeCleanupMethod(classCleanupMethod, remainingCleanupCount: BaseClassCleanupMethods.Count - 1 - i, testContext); if (ClassCleanupException is not null) { break; @@ -697,7 +750,7 @@ void DoRun() using LogMessageListener logListener = new(MSTestSettings.CurrentSettings.CaptureDebugTraces); try { - ExecuteClassCleanup(); + ExecuteClassCleanup(testContext.Context); } finally { @@ -735,7 +788,7 @@ void DoRun() } } - private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, int remainingCleanupCount) + private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, int remainingCleanupCount, TestContext testContext) { TimeoutInfo? timeout = null; if (ClassCleanupMethodTimeoutMilliseconds.TryGetValue(methodInfo, out TimeoutInfo localTimeout)) @@ -744,12 +797,54 @@ void DoRun() } return FixtureMethodRunner.RunWithTimeoutAndCancellation( - () => methodInfo.InvokeAsSynchronousTask(null), - new CancellationTokenSource(), + () => + { + if (methodInfo.GetParameters().Length == 0) + { + methodInfo.InvokeAsSynchronousTask(null); + } + else + { + methodInfo.InvokeAsSynchronousTask(null, testContext); + } + }, + testContext.CancellationTokenSource, timeout, methodInfo, new ClassExecutionContextScope(ClassType, remainingCleanupCount), Resource.ClassCleanupWasCancelled, Resource.ClassCleanupTimedOut); } + + /// + /// Resolves the test context property. + /// + /// The class Type. + /// The for TestContext property. Null if not defined. + private static PropertyInfo? ResolveTestContext(Type classType) + { + try + { + PropertyInfo? testContextProperty = PlatformServiceProvider.Instance.ReflectionOperations.GetRuntimeProperty(classType, TestContextPropertyName, includeNonPublic: false); + if (testContextProperty == null) + { + // that's okay may be the property was not defined + return null; + } + + // check if testContextProperty is of correct type + if (!string.Equals(testContextProperty.PropertyType.FullName, typeof(TestContext).FullName, StringComparison.Ordinal)) + { + string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestContextTypeMismatchLoadError, classType.FullName); + throw new TypeInspectionException(errorMessage); + } + + return testContextProperty; + } + catch (AmbiguousMatchException ex) + { + string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestContextLoadError, classType.FullName, ex.Message); + throw new TypeInspectionException(errorMessage); + } + } } diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs index 928465b7f7..a238267e72 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestExecutionManager.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Runtime.InteropServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; @@ -19,8 +14,26 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// Class responsible for execution of tests at assembly level and sending tests via framework handle. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestExecutionManager { + private sealed class RemotingMessageLogger : MarshalByRefObject, IMessageLogger + { + private readonly IMessageLogger _realMessageLogger; + + public RemotingMessageLogger(IMessageLogger messageLogger) + => _realMessageLogger = messageLogger; + + public void SendMessage(TestMessageLevel testMessageLevel, string message) + => _realMessageLogger.SendMessage(testMessageLevel, message); + } + /// /// Dictionary for test run parameters. /// @@ -300,7 +313,7 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC [MSTestSettings.CurrentSettings, unitTestElements, (int)sourceSettings.ClassCleanupLifecycle])!; // Ensures that the cancellation token gets through AppDomain boundary. - _testRunCancellationToken?.Register(testRunner.Cancel); + _testRunCancellationToken?.Register(static state => ((UnitTestRunner)state!).Cancel(), testRunner); if (MSTestSettings.CurrentSettings.ParallelizationWorkers.HasValue) { @@ -394,6 +407,11 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext? runC ExecuteTestsWithTestRunner(testsToRun, frameworkHandle, source, sourceLevelParameters, testRunner); } + if (PlatformServiceProvider.Instance.IsGracefulStopRequested) + { + testRunner.ForceCleanup(sourceLevelParameters!, new RemotingMessageLogger(frameworkHandle)); + } + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed tests belonging to source {0}", source); } @@ -411,9 +429,15 @@ private void ExecuteTestsWithTestRunner( ? tests.OrderBy(t => t.GetManagedType()).ThenBy(t => t.GetManagedMethod()) : tests; + var remotingMessageLogger = new RemotingMessageLogger(testExecutionRecorder); + foreach (TestCase currentTest in orderedTests) { _testRunCancellationToken?.ThrowIfCancellationRequested(); + if (PlatformServiceProvider.Instance.IsGracefulStopRequested) + { + break; + } // If it is a fixture test, add it to the list of fixture tests and do not execute it. // It is executed by test itself. @@ -435,7 +459,10 @@ private void ExecuteTestsWithTestRunner( // Run single test passing test context properties to it. IDictionary tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest); Dictionary testContextProperties = GetTestContextProperties(tcmProperties, sourceLevelParameters); - UnitTestResult[] unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties); + + // testRunner could be in a different AppDomain. We cannot pass the testExecutionRecorder directly. + // Instead, we pass a proxy (remoting object) that is marshallable by ref. + UnitTestResult[] unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties, remotingMessageLogger); PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed test {0}", unitTestElement.TestMethod.Name); @@ -481,18 +508,20 @@ private void ExecuteTestsWithTestRunner( IDictionary tcmProperties, IDictionary sourceLevelParameters) { - var testContextProperties = new Dictionary(); + // This dictionary will have *at least* 8 entries. Those are the sourceLevelParameters + // which were originally calculated from TestDeployment.GetDeploymentInformation. + var testContextProperties = new Dictionary(capacity: 8); // Add tcm properties. - foreach (KeyValuePair propertyPair in tcmProperties) + foreach ((TestProperty key, object? value) in tcmProperties) { - testContextProperties[propertyPair.Key.Id] = propertyPair.Value; + testContextProperties[key.Id] = value; } // Add source level parameters. - foreach (KeyValuePair propertyPair in sourceLevelParameters) + foreach ((string key, object value) in sourceLevelParameters) { - testContextProperties[propertyPair.Key] = propertyPair.Value; + testContextProperties[key] = value; } return testContextProperties; diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs index 5457c046d8..3b61f1aae0 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodInfo.cs @@ -1,16 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Text; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; using Microsoft.VisualStudio.TestTools.UnitTesting; using ObjectModelUnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; @@ -21,6 +16,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// Defines the TestMethod Info object. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestMethodInfo : ITestMethod { /// @@ -43,6 +45,7 @@ internal TestMethodInfo( TestMethod = testMethod; Parent = parent; TestMethodOptions = testMethodOptions; + ExpectedException = ResolveExpectedException(); } /// @@ -87,6 +90,8 @@ internal TestMethodInfo( /// internal TestMethodOptions TestMethodOptions { get; } + internal ExpectedExceptionBaseAttribute? ExpectedException { get; set; /*set for testing only*/ } + public Attribute[]? GetAllAttributes(bool inherit) => ReflectHelper.Instance.GetDerivedAttributes(TestMethod, inherit).ToArray(); public TAttributeType[] GetAttributes(bool inherit) @@ -112,7 +117,7 @@ public virtual TestResult Invoke(object?[]? arguments) watch.Start(); try { - result = IsTimeoutSet ? ExecuteInternalWithTimeout(arguments) : ExecuteInternal(arguments); + result = IsTimeoutSet ? ExecuteInternalWithTimeout(arguments) : ExecuteInternal(arguments, null); } finally { @@ -210,13 +215,58 @@ public virtual TestResult Invoke(object?[]? arguments) return newParameters; } + /// + /// Resolves the expected exception attribute. The function will try to + /// get all the expected exception attributes defined for a testMethod. + /// + /// + /// The expected exception attribute found for this test. Null if not found. + /// + private ExpectedExceptionBaseAttribute? ResolveExpectedException() + { + IEnumerable expectedExceptions; + + try + { + expectedExceptions = ReflectHelper.Instance.GetDerivedAttributes(TestMethod, inherit: true); + } + catch (Exception ex) + { + // If construction of the attribute throws an exception, indicate that there was an + // error when trying to run the test + string errorMessage = string.Format( + CultureInfo.CurrentCulture, + Resource.UTA_ExpectedExceptionAttributeConstructionException, + Parent.ClassType.FullName, + TestMethod.Name, + ex.GetFormattedExceptionMessage()); + throw new TypeInspectionException(errorMessage); + } + + // Verify that there is only one attribute (multiple attributes derived from + // ExpectedExceptionBaseAttribute are not allowed on a test method) + // This is needed EVEN IF the attribute doesn't allow multiple. + // See https://github.com/microsoft/testfx/issues/4331 + if (expectedExceptions.Count() > 1) + { + string errorMessage = string.Format( + CultureInfo.CurrentCulture, + Resource.UTA_MultipleExpectedExceptionsOnTestMethod, + Parent.ClassType.FullName, + TestMethod.Name); + throw new TypeInspectionException(errorMessage); + } + + return expectedExceptions.FirstOrDefault(); + } + /// /// Execute test without timeout. /// /// Arguments to be passed to the method. /// The result of the execution. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - private TestResult ExecuteInternal(object?[]? arguments) + private TestResult ExecuteInternal(object?[]? arguments, CancellationTokenSource? timeoutTokenSource) { DebugEx.Assert(TestMethod != null, "UnitTestExecuter.DefaultTestMethodInvoke: testMethod = null."); @@ -238,7 +288,7 @@ private TestResult ExecuteInternal(object?[]? arguments) // For any failure after this point, we must run TestCleanup _isTestContextSet = true; - if (RunTestInitializeMethod(_classInstance, result)) + if (RunTestInitializeMethod(_classInstance, result, timeoutTokenSource)) { hasTestInitializePassed = true; if (IsTimeoutSet) @@ -266,12 +316,21 @@ private TestResult ExecuteInternal(object?[]? arguments) // Expected Exception was thrown, so Pass the test result.Outcome = UTF.UnitTestOutcome.Passed; } - else if (realException is OperationCanceledException oce && oce.CancellationToken == TestMethodOptions.TestContext?.Context.CancellationTokenSource.Token) + else if (realException.IsOperationCanceledExceptionFromToken(TestMethodOptions.TestContext!.Context.CancellationTokenSource.Token)) { result.Outcome = UTF.UnitTestOutcome.Timeout; result.TestFailureException = new TestFailedException( ObjectModelUnitTestOutcome.Timeout, - string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Cancelled, TestMethodName)); + timeoutTokenSource?.Token.IsCancellationRequested == true + ? string.Format( + CultureInfo.InvariantCulture, + Resource.Execution_Test_Timeout, + TestMethodName, + TestMethodOptions.TimeoutInfo.Timeout) + : string.Format( + CultureInfo.InvariantCulture, + Resource.Execution_Test_Cancelled, + TestMethodName)); } else { @@ -292,11 +351,11 @@ private TestResult ExecuteInternal(object?[]? arguments) // if the user specified that the test was going to throw an exception, and // it did not, we should fail the test // We only perform this check if the test initialize passes and the test method is actually run. - if (hasTestInitializePassed && !isExceptionThrown && TestMethodOptions.ExpectedException != null) + if (hasTestInitializePassed && !isExceptionThrown && ExpectedException is { } expectedException) { result.TestFailureException = new TestFailedException( ObjectModelUnitTestOutcome.Failed, - TestMethodOptions.ExpectedException.NoExceptionMessage); + expectedException.NoExceptionMessage); result.Outcome = UTF.UnitTestOutcome.Failed; } } @@ -320,7 +379,7 @@ private TestResult ExecuteInternal(object?[]? arguments) // Pulling it out so extension writers can abort custom cleanups if need be. Having this in a finally block // does not allow a thread abort exception to be raised within the block but throws one after finally is executed // crashing the process. This was blocking writing an extension for Dynamic Timeout in VSO. - RunTestCleanupMethod(result); + RunTestCleanupMethod(result, timeoutTokenSource); return testRunnerException != null ? throw testRunnerException : result; } @@ -331,7 +390,7 @@ private bool IsExpectedException(Exception ex, TestResult result) // if the user specified an expected exception, we need to check if this // exception was thrown. If it was thrown, we should pass the test. In // case a different exception was thrown, the test is seen as failure - if (TestMethodOptions.ExpectedException == null) + if (ExpectedException == null) { return false; } @@ -341,7 +400,7 @@ private bool IsExpectedException(Exception ex, TestResult result) { // If the expected exception attribute's Verify method returns, then it // considers this exception as expected, so the test passed - TestMethodOptions.ExpectedException.Verify(ex); + ExpectedException.Verify(ex); return true; } catch (Exception verifyEx) @@ -465,7 +524,7 @@ private static TestFailedException HandleMethodException(Exception ex, Exception /// /// Instance of TestResult. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - private void RunTestCleanupMethod(TestResult result) + private void RunTestCleanupMethod(TestResult result, CancellationTokenSource? timeoutTokenSource) { DebugEx.Assert(result != null, "result != null"); @@ -481,16 +540,25 @@ private void RunTestCleanupMethod(TestResult result) { try { + // Reset the cancellation token source to avoid cancellation of cleanup methods because of the init or test method cancellation. + TestMethodOptions.TestContext!.Context.CancellationTokenSource = new CancellationTokenSource(); + + // If we are running with a method timeout, we need to cancel the cleanup when the overall timeout expires. If it already expired, nothing to do. + if (timeoutTokenSource is { IsCancellationRequested: false }) + { + timeoutTokenSource?.Token.Register(TestMethodOptions.TestContext.Context.CancellationTokenSource.Cancel); + } + // Test cleanups are called in the order of discovery // Current TestClass -> Parent -> Grandparent testCleanupException = testCleanupMethod is not null - ? InvokeCleanupMethod(testCleanupMethod, _classInstance, Parent.BaseTestCleanupMethodsQueue.Count) + ? InvokeCleanupMethod(testCleanupMethod, _classInstance, Parent.BaseTestCleanupMethodsQueue.Count, timeoutTokenSource) : null; var baseTestCleanupQueue = new Queue(Parent.BaseTestCleanupMethodsQueue); while (baseTestCleanupQueue.Count > 0 && testCleanupException is null) { testCleanupMethod = baseTestCleanupQueue.Dequeue(); - testCleanupException = InvokeCleanupMethod(testCleanupMethod, _classInstance, baseTestCleanupQueue.Count); + testCleanupException = InvokeCleanupMethod(testCleanupMethod, _classInstance, baseTestCleanupQueue.Count, timeoutTokenSource); } } finally @@ -514,9 +582,9 @@ private void RunTestCleanupMethod(TestResult result) } // If the exception is already a `TestFailedException` we throw it as-is - if (testCleanupException is TestFailedException) + if (testCleanupException is TestFailedException tfe) { - result.Outcome = UTF.UnitTestOutcome.Failed; + result.Outcome = tfe.Outcome.ToAdapterOutcome(); result.TestFailureException = testCleanupException; return; } @@ -592,7 +660,7 @@ private void RunTestCleanupMethod(TestResult result) /// Instance of TestResult. /// True if the TestInitialize method(s) did not throw an exception. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - private bool RunTestInitializeMethod(object classInstance, TestResult result) + private bool RunTestInitializeMethod(object classInstance, TestResult result, CancellationTokenSource? timeoutTokenSource) { DebugEx.Assert(classInstance != null, "classInstance != null"); DebugEx.Assert(result != null, "result != null"); @@ -608,7 +676,9 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) while (baseTestInitializeStack.Count > 0) { testInitializeMethod = baseTestInitializeStack.Pop(); - testInitializeException = testInitializeMethod is not null ? InvokeInitializeMethod(testInitializeMethod, classInstance) : null; + testInitializeException = testInitializeMethod is not null + ? InvokeInitializeMethod(testInitializeMethod, classInstance, timeoutTokenSource) + : null; if (testInitializeException is not null) { break; @@ -618,7 +688,9 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) if (testInitializeException == null) { testInitializeMethod = Parent.TestInitializeMethod; - testInitializeException = testInitializeMethod is not null ? InvokeInitializeMethod(testInitializeMethod, classInstance) : null; + testInitializeException = testInitializeMethod is not null + ? InvokeInitializeMethod(testInitializeMethod, classInstance, timeoutTokenSource) + : null; } } catch (Exception ex) @@ -633,9 +705,9 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) } // If the exception is already a `TestFailedException` we throw it as-is - if (testInitializeException is TestFailedException) + if (testInitializeException is TestFailedException tfe) { - result.Outcome = UTF.UnitTestOutcome.Failed; + result.Outcome = tfe.Outcome.ToAdapterOutcome(); result.TestFailureException = testInitializeException; return false; } @@ -645,7 +717,7 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) // Prefix the exception message with the exception type name as prefix when exception is not assert exception. string exceptionMessage = realException is UnitTestAssertException ? realException.TryGetMessage() - : ExceptionHelper.GetFormattedExceptionMessage(realException); + : realException.GetFormattedExceptionMessage(); string errorMessage = string.Format( CultureInfo.CurrentCulture, Resource.UTA_InitMethodThrows, @@ -654,7 +726,9 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) exceptionMessage); StackTraceInformation? stackTrace = realException.GetStackTraceInformation(); - result.Outcome = realException is AssertInconclusiveException ? UTF.UnitTestOutcome.Inconclusive : UTF.UnitTestOutcome.Failed; + result.Outcome = realException is AssertInconclusiveException + ? UTF.UnitTestOutcome.Inconclusive + : UTF.UnitTestOutcome.Failed; result.TestFailureException = new TestFailedException( result.Outcome.ToUnitTestOutcome(), errorMessage, @@ -664,7 +738,7 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) return false; } - private TestFailedException? InvokeInitializeMethod(MethodInfo methodInfo, object classInstance) + private TestFailedException? InvokeInitializeMethod(MethodInfo methodInfo, object classInstance, CancellationTokenSource? timeoutTokenSource) { TimeoutInfo? timeout = null; if (Parent.TestInitializeMethodTimeoutMilliseconds.TryGetValue(methodInfo, out TimeoutInfo localTimeout)) @@ -679,10 +753,13 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) methodInfo, new InstanceExecutionContextScope(classInstance, Parent.ClassType), Resource.TestInitializeWasCancelled, - Resource.TestInitializeTimedOut); + Resource.TestInitializeTimedOut, + timeoutTokenSource is null + ? null + : (timeoutTokenSource, TestMethodOptions.TimeoutInfo.Timeout)); } - private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, object classInstance, int remainingCleanupCount) + private TestFailedException? InvokeCleanupMethod(MethodInfo methodInfo, object classInstance, int remainingCleanupCount, CancellationTokenSource? timeoutTokenSource) { TimeoutInfo? timeout = null; if (Parent.TestCleanupMethodTimeoutMilliseconds.TryGetValue(methodInfo, out TimeoutInfo localTimeout)) @@ -697,7 +774,10 @@ private bool RunTestInitializeMethod(object classInstance, TestResult result) methodInfo, new InstanceExecutionContextScope(classInstance, Parent.ClassType, remainingCleanupCount), Resource.TestCleanupWasCancelled, - Resource.TestCleanupTimedOut); + Resource.TestCleanupTimedOut, + timeoutTokenSource is null + ? null + : (timeoutTokenSource, TestMethodOptions.TimeoutInfo.Timeout)); } /// @@ -781,18 +861,27 @@ private bool SetTestContext(object classInstance, TestResult result) // In most cases, exception will be TargetInvocationException with real exception wrapped // in the InnerException; or user code throws an exception. // It also seems that in rare cases the ex can be null. - Exception actualException = ex.InnerException ?? ex; - string exceptionMessage = actualException.GetFormattedExceptionMessage(); - StackTraceInformation? stackTraceInfo = actualException.GetStackTraceInformation(); + Exception realException = ex.GetRealException(); - string errorMessage = string.Format( - CultureInfo.CurrentCulture, - Resource.UTA_InstanceCreationError, - TestClassName, - exceptionMessage); + if (realException.IsOperationCanceledExceptionFromToken(TestMethodOptions.TestContext!.Context.CancellationTokenSource.Token)) + { + result.Outcome = UTF.UnitTestOutcome.Timeout; + result.TestFailureException = new TestFailedException(ObjectModelUnitTestOutcome.Timeout, string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName, TestMethodOptions.TimeoutInfo.Timeout)); + } + else + { + string exceptionMessage = realException.GetFormattedExceptionMessage(); + StackTraceInformation? stackTraceInfo = realException.GetStackTraceInformation(); - result.Outcome = UTF.UnitTestOutcome.Failed; - result.TestFailureException = new TestFailedException(ObjectModelUnitTestOutcome.Failed, errorMessage, stackTraceInfo); + string errorMessage = string.Format( + CultureInfo.CurrentCulture, + Resource.UTA_InstanceCreationError, + TestClassName, + exceptionMessage); + + result.Outcome = UTF.UnitTestOutcome.Failed; + result.TestFailureException = new TestFailedException(ObjectModelUnitTestOutcome.Failed, errorMessage, stackTraceInfo); + } } return classInstance; @@ -822,13 +911,13 @@ private TestResult ExecuteInternalWithTimeout(object?[]? arguments) Outcome = UTF.UnitTestOutcome.Timeout, TestFailureException = new TestFailedException( ObjectModelUnitTestOutcome.Timeout, - string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName)), + string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName, TestMethodOptions.TimeoutInfo.Timeout)), }; } try { - return ExecuteInternal(arguments); + return ExecuteInternal(arguments, timeoutTokenSource); } catch (OperationCanceledException) { @@ -840,7 +929,7 @@ private TestResult ExecuteInternalWithTimeout(object?[]? arguments) TestFailureException = new TestFailedException( ObjectModelUnitTestOutcome.Timeout, timeoutTokenSource.Token.IsCancellationRequested - ? string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName) + ? string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName, TestMethodOptions.TimeoutInfo.Timeout) : string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Cancelled, TestMethodName)), }; } @@ -848,26 +937,14 @@ private TestResult ExecuteInternalWithTimeout(object?[]? arguments) finally { timeoutTokenSource?.Dispose(); + timeoutTokenSource = null; } } TestResult? result = null; Exception? failure = null; - void ExecuteAsyncAction() - { - try - { - result = ExecuteInternal(arguments); - } - catch (Exception ex) - { - failure = ex; - } - } - - CancellationToken cancelToken = TestMethodOptions.TestContext!.Context.CancellationTokenSource.Token; - if (PlatformServiceProvider.Instance.ThreadOperations.Execute(ExecuteAsyncAction, TestMethodOptions.TimeoutInfo.Timeout, cancelToken)) + if (PlatformServiceProvider.Instance.ThreadOperations.Execute(ExecuteAsyncAction, TestMethodOptions.TimeoutInfo.Timeout, TestMethodOptions.TestContext!.Context.CancellationTokenSource.Token)) { if (failure != null) { @@ -878,12 +955,12 @@ void ExecuteAsyncAction() // It's possible that some failures happened and that the cleanup wasn't executed, so we need to run it here. // The method already checks if the cleanup was already executed. - RunTestCleanupMethod(result); + RunTestCleanupMethod(result, null); return result; } // Timed out or canceled - string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName); + string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, TestMethodName, TestMethodOptions.TimeoutInfo.Timeout); if (TestMethodOptions.TestContext.Context.CancellationTokenSource.IsCancellationRequested) { errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Cancelled, TestMethodName); @@ -898,7 +975,20 @@ void ExecuteAsyncAction() // We don't know when the cancellation happened so it's possible that the cleanup wasn't executed, so we need to run it here. // The method already checks if the cleanup was already executed. - RunTestCleanupMethod(timeoutResult); + RunTestCleanupMethod(timeoutResult, null); return timeoutResult; + + // Local functions + void ExecuteAsyncAction() + { + try + { + result = ExecuteInternal(arguments, null); + } + catch (Exception ex) + { + failure = ex; + } + } } } diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs index 69c590cdd9..6a401c345a 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestMethodRunner.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; -using System.Runtime.InteropServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -22,7 +18,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// This class is responsible to running tests and converting framework TestResults to adapter TestResults. /// -internal class TestMethodRunner +internal sealed class TestMethodRunner { /// /// Test context which needs to be passed to the various methods of the test. @@ -69,7 +65,7 @@ public TestMethodRunner(TestMethodInfo testMethodInfo, TestMethod testMethod, IT internal UnitTestResult[] Execute(string initializationLogs, string initializationErrorLogs, string initializationTrace, string initializationTestContextMessages) { bool isSTATestClass = AttributeComparer.IsDerived(_testMethodInfo.Parent.ClassAttribute); - bool isSTATestMethod = _testMethodInfo.TestMethodOptions.Executor is not null && AttributeComparer.IsDerived(_testMethodInfo.TestMethodOptions.Executor); + bool isSTATestMethod = AttributeComparer.IsDerived(_testMethodInfo.TestMethodOptions.Executor); bool isSTARequested = isSTATestClass || isSTATestMethod; bool isWindowsOS = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (isSTARequested && isWindowsOS && Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) @@ -165,43 +161,46 @@ internal UnitTestResult[] RunTestMethod() DebugEx.Assert(_testMethodInfo.TestMethod != null, "Test method should not be null."); List results = []; + if (_testMethodInfo.TestMethodOptions.Executor == null) + { + throw ApplicationStateGuard.Unreachable(); + } + bool isDataDriven = false; var parentStopwatch = Stopwatch.StartNew(); - - if (_testMethodInfo.TestMethodOptions.Executor != null) + if (_test.DataType == DynamicDataType.ITestDataSource) { - if (_test.DataType == DynamicDataType.ITestDataSource) + if (_test.TestDataSourceIgnoreMessage is not null) { - object?[]? data = DataSerializationHelper.Deserialize(_test.SerializedData); - TestResult[] testResults = ExecuteTestWithDataSource(null, data); - results.AddRange(testResults); + return [new(UnitTestOutcome.Ignored, _test.TestDataSourceIgnoreMessage)]; } - else if (ExecuteDataSourceBasedTests(results)) - { - isDataDriven = true; - } - else - { - _testContext.SetDisplayName(_test.DisplayName); - TestResult[] testResults = ExecuteTest(_testMethodInfo); - foreach (TestResult testResult in testResults) - { - if (StringEx.IsNullOrWhiteSpace(testResult.DisplayName)) - { - testResult.DisplayName = _test.DisplayName; - } - } - - results.AddRange(testResults); - } + object?[]? data = DataSerializationHelper.Deserialize(_test.SerializedData); + TestResult[] testResults = ExecuteTestWithDataSource(null, data); + results.AddRange(testResults); + } + else if (TryExecuteDataSourceBasedTests(results)) + { + isDataDriven = true; + } + else if (TryExecuteFoldedDataDrivenTests(results)) + { + isDataDriven = true; } else { - PlatformServiceProvider.Instance.AdapterTraceLogger.LogError( - "Not able to get executor for method {0}.{1}", - _testMethodInfo.TestClassName, - _testMethodInfo.TestMethodName); + _testContext.SetDisplayName(_test.DisplayName); + TestResult[] testResults = ExecuteTest(_testMethodInfo); + + foreach (TestResult testResult in testResults) + { + if (StringEx.IsNullOrWhiteSpace(testResult.DisplayName)) + { + testResult.DisplayName = _test.DisplayName; + } + } + + results.AddRange(testResults); } // Get aggregate outcome. @@ -214,6 +213,7 @@ internal UnitTestResult[] RunTestMethod() // In legacy scenario #pragma warning disable CS0618 // Type or member is obsolete if (_test.TestIdGenerationStrategy == TestIdGenerationStrategy.Legacy) +#pragma warning restore CS0618 // Type or member is obsolete { parentStopwatch.Stop(); var parentResult = new TestResult @@ -225,7 +225,6 @@ internal UnitTestResult[] RunTestMethod() results = UpdateResultsWithParentInfo(results, parentResult); } -#pragma warning restore CS0618 // Type or member is obsolete else { results = UpdateResultsWithParentInfo(results); @@ -247,108 +246,122 @@ internal UnitTestResult[] RunTestMethod() return results.ToUnitTestResults(); } - private bool ExecuteDataSourceBasedTests(List results) + private bool TryExecuteDataSourceBasedTests(List results) { - bool isDataDriven = false; - DataSourceAttribute[] dataSourceAttribute = _testMethodInfo.GetAttributes(false); if (dataSourceAttribute is { Length: 1 }) { - isDataDriven = true; - Stopwatch watch = new(); - watch.Start(); + ExecuteTestFromDataSourceAttribute(results); + return true; + } - try + return false; + } + + private bool TryExecuteFoldedDataDrivenTests(List results) + { + IEnumerable? testDataSources = _testMethodInfo.GetAttributes(false)?.OfType(); + if (testDataSources?.Any() != true) + { + return false; + } + + foreach (UTF.ITestDataSource testDataSource in testDataSources) + { + if (testDataSource is ITestDataSourceIgnoreCapability { IgnoreMessage: { } ignoreMessage }) { - IEnumerable? dataRows = PlatformServiceProvider.Instance.TestDataSource.GetData(_testMethodInfo, _testContext); + results.Add(new() + { + Outcome = UTF.UnitTestOutcome.Ignored, + IgnoreReason = ignoreMessage, + }); + continue; + } + + IEnumerable? dataSource; + + // This code is to execute tests. To discover the tests code is in AssemblyEnumerator.ProcessTestDataSourceTests. + // Any change made here should be reflected in AssemblyEnumerator.ProcessTestDataSourceTests as well. + dataSource = testDataSource.GetData(_testMethodInfo.MethodInfo); - if (dataRows == null) + if (!dataSource.Any()) + { + if (!MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) { - var inconclusiveResult = new TestResult - { - Outcome = UTF.UnitTestOutcome.Inconclusive, - Duration = watch.Elapsed, - }; - results.Add(inconclusiveResult); + throw testDataSource.GetExceptionForEmptyDataSource(_testMethodInfo.MethodInfo); } - else + + var inconclusiveResult = new TestResult { - try - { - int rowIndex = 0; - - foreach (object dataRow in dataRows) - { - TestResult[] testResults = ExecuteTestWithDataRow(dataRow, rowIndex++); - results.AddRange(testResults); - } - } - finally - { - _testContext.SetDataConnection(null); - _testContext.SetDataRow(null); - } + Outcome = UTF.UnitTestOutcome.Inconclusive, + }; + results.Add(inconclusiveResult); + continue; + } + + foreach (object?[] data in dataSource) + { + try + { + TestResult[] testResults = ExecuteTestWithDataSource(testDataSource, data); + + results.AddRange(testResults); + } + finally + { + _testMethodInfo.SetArguments(null); } } - catch (Exception ex) + } + + return true; + } + + private void ExecuteTestFromDataSourceAttribute(List results) + { + Stopwatch watch = new(); + watch.Start(); + + try + { + IEnumerable? dataRows = PlatformServiceProvider.Instance.TestDataSource.GetData(_testMethodInfo, _testContext); + if (dataRows == null) { - var failedResult = new TestResult + var inconclusiveResult = new TestResult { - Outcome = UTF.UnitTestOutcome.Error, - TestFailureException = ex, + Outcome = UTF.UnitTestOutcome.Inconclusive, Duration = watch.Elapsed, }; - results.Add(failedResult); + results.Add(inconclusiveResult); + return; } - } - else - { - IEnumerable? testDataSources = _testMethodInfo.GetAttributes(false)?.OfType(); - if (testDataSources != null) + try { - foreach (UTF.ITestDataSource testDataSource in testDataSources) + int rowIndex = 0; + + foreach (object dataRow in dataRows) { - isDataDriven = true; - IEnumerable? dataSource; - - // This code is to execute tests. To discover the tests code is in AssemblyEnumerator.ProcessTestDataSourceTests. - // Any change made here should be reflected in AssemblyEnumerator.ProcessTestDataSourceTests as well. - dataSource = testDataSource.GetData(_testMethodInfo.MethodInfo); - - if (!dataSource.Any()) - { - if (!MSTestSettings.CurrentSettings.ConsiderEmptyDataSourceAsInconclusive) - { - throw testDataSource.GetExceptionForEmptyDataSource(_testMethodInfo.MethodInfo); - } - - var inconclusiveResult = new TestResult - { - Outcome = UTF.UnitTestOutcome.Inconclusive, - }; - results.Add(inconclusiveResult); - continue; - } - - foreach (object?[] data in dataSource) - { - try - { - TestResult[] testResults = ExecuteTestWithDataSource(testDataSource, data); - - results.AddRange(testResults); - } - finally - { - _testMethodInfo.SetArguments(null); - } - } + TestResult[] testResults = ExecuteTestWithDataRow(dataRow, rowIndex++); + results.AddRange(testResults); } } + finally + { + _testContext.SetDataConnection(null); + _testContext.SetDataRow(null); + } + } + catch (Exception ex) + { + var failedResult = new TestResult + { + Outcome = UTF.UnitTestOutcome.Error, + TestFailureException = ex, + Duration = watch.Elapsed, + }; + results.Add(failedResult); } - - return isDataDriven; } private TestResult[] ExecuteTestWithDataSource(UTF.ITestDataSource? testDataSource, object?[]? data) @@ -413,7 +426,7 @@ private TestResult[] ExecuteTest(TestMethodInfo testMethodInfo) { try { - return _testMethodInfo.TestMethodOptions.Executor!.Execute(testMethodInfo); + return _testMethodInfo.TestMethodOptions.Executor.Execute(testMethodInfo); } catch (Exception ex) { @@ -447,7 +460,7 @@ private static UTF.UnitTestOutcome GetAggregateOutcome(List results) UTF.UnitTestOutcome aggregateOutcome = results[0].Outcome; foreach (TestResult result in results) { - aggregateOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(aggregateOutcome, result.Outcome); + aggregateOutcome = aggregateOutcome.GetMoreImportantOutcome(result.Outcome); } return aggregateOutcome; diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs b/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs index 14ca69684a..8ebb11d736 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TestRunCancellationToken.cs @@ -1,20 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// /// Cancellation token supporting cancellation of a test run. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestRunCancellationToken { /// /// Callbacks to be invoked when canceled. /// Needs to be a concurrent collection, see https://github.com/microsoft/testfx/issues/3953. /// - private readonly ConcurrentBag _registeredCallbacks = new(); + private readonly ConcurrentBag<(Action, object?)> _registeredCallbacks = new(); public TestRunCancellationToken() : this(CancellationToken.None) @@ -38,9 +43,9 @@ private set if (!previousValue && value) { - foreach (Action callBack in _registeredCallbacks) + foreach ((Action callBack, object? state) in _registeredCallbacks) { - callBack.Invoke(); + callBack.Invoke(state); } } } @@ -55,7 +60,9 @@ private set /// Registers a callback method to be invoked when canceled. /// /// Callback delegate for handling cancellation. - public void Register(Action callback) => _registeredCallbacks.Add(callback); + public void Register(Action callback) => _registeredCallbacks.Add((_ => callback(), null)); + + internal void Register(Action callback, object? state) => _registeredCallbacks.Add((callback, state)); /// /// Unregister the callback method. diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs b/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs index eb6124cf9d..2b13c8384d 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/TypeCache.cs @@ -1,12 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; using System.Security; -using System.Text; using Microsoft.TestPlatform.AdapterUtilities.ManagedNameUtilities; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; @@ -21,13 +16,8 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// Defines type cache which reflects upon a type and cache its test artifacts. /// -internal class TypeCache : MarshalByRefObject +internal sealed class TypeCache : MarshalByRefObject { - /// - /// Test context property name. - /// - private const string TestContextPropertyName = "TestContext"; - /// /// Predefined test Attribute names. /// @@ -89,9 +79,6 @@ public IEnumerable AssemblyInfoListWithExecutableCleanupMethod /// /// Get the test method info corresponding to the parameter test Element. /// - /// The test Method. - /// The test Context. - /// Indicates whether the test method should capture debug traces. /// The . public TestMethodInfo? GetTestMethodInfo(TestMethod testMethod, ITestContext testContext, bool captureDebugTraces) { @@ -109,7 +96,29 @@ public IEnumerable AssemblyInfoListWithExecutableCleanupMethod } // Get the testMethod - return ResolveTestMethod(testMethod, testClassInfo, testContext, captureDebugTraces); + return ResolveTestMethodInfo(testMethod, testClassInfo, testContext, captureDebugTraces); + } + + /// + /// Get the test method info corresponding to the parameter test Element. + /// + /// The . + public TestMethodInfo? GetTestMethodInfoForDiscovery(TestMethod testMethod) + { + Guard.NotNull(testMethod); + + // Get the classInfo (This may throw as GetType calls assembly.GetType(..,true);) + TestClassInfo? testClassInfo = GetClassInfo(testMethod); + + if (testClassInfo == null) + { + // This means the class containing the test method could not be found. + // Return null so we return a not found result. + return null; + } + + // Get the testMethod + return ResolveTestMethodInfoForDiscovery(testMethod, testClassInfo); } /// @@ -186,11 +195,7 @@ private static bool TryGetUnescapedManagedTypeName(TestMethod testMethod, [NotNu continue; } -#if NETCOREAPP || WINDOWS_UWP if (hierarchyPart.StartsWith('\'') && hierarchyPart.EndsWith('\'')) -#else - if (hierarchyPart.StartsWith("'", StringComparison.Ordinal) && hierarchyPart.EndsWith("'", StringComparison.Ordinal)) -#endif { unescapedTypeNameBuilder.Append(hierarchyPart, 1, hierarchyPart.Length - 2); } @@ -259,7 +264,7 @@ private static bool TryGetUnescapedManagedTypeName(TestMethod testMethod, [NotNu /// The . private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) { - IEnumerable constructors = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredConstructors(classType); + ConstructorInfo[] constructors = PlatformServiceProvider.Instance.ReflectionOperations.GetDeclaredConstructors(classType); (ConstructorInfo CtorInfo, bool IsParameterless)? selectedConstructor = null; foreach (ConstructorInfo ctor in constructors) @@ -297,13 +302,11 @@ private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) ConstructorInfo constructor = selectedConstructor.Value.CtorInfo; bool isParameterLessConstructor = selectedConstructor.Value.IsParameterless; - PropertyInfo? testContextProperty = ResolveTestContext(classType); - TestAssemblyInfo assemblyInfo = GetAssemblyInfo(classType); TestClassAttribute? testClassAttribute = ReflectHelper.Instance.GetFirstDerivedAttributeOrDefault(classType, inherit: false); DebugEx.Assert(testClassAttribute is not null, "testClassAttribute is null"); - var classInfo = new TestClassInfo(classType, constructor, isParameterLessConstructor, testContextProperty, testClassAttribute, assemblyInfo); + var classInfo = new TestClassInfo(classType, constructor, isParameterLessConstructor, testClassAttribute, assemblyInfo); // List holding the instance of the initialize/cleanup methods // to be passed into the tuples' queue when updating the class info. @@ -347,41 +350,29 @@ private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod) return classInfo; } - /// - /// Resolves the test context property. - /// - /// The class Type. - /// The for TestContext property. Null if not defined. - private static PropertyInfo? ResolveTestContext(Type classType) + #endregion + + #region AssemblyInfo creation and cache logic. + + private TimeoutInfo? TryGetTimeoutInfo(MethodInfo methodInfo, FixtureKind fixtureKind) { - try + TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); + if (timeoutAttribute != null) { - PropertyInfo? testContextProperty = PlatformServiceProvider.Instance.ReflectionOperations.GetRuntimeProperty(classType, TestContextPropertyName); - if (testContextProperty == null) - { - // that's okay may be the property was not defined - return null; - } - - // check if testContextProperty is of correct type - if (!string.Equals(testContextProperty.PropertyType.FullName, typeof(TestContext).FullName, StringComparison.Ordinal)) + if (!timeoutAttribute.HasCorrectTimeout) { - string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestContextTypeMismatchLoadError, classType.FullName); - throw new TypeInspectionException(errorMessage); + string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); + throw new TypeInspectionException(message); } - return testContextProperty; - } - catch (AmbiguousMatchException ex) - { - string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestContextLoadError, classType.FullName, ex.Message); - throw new TypeInspectionException(errorMessage); + return TimeoutInfo.FromTimeoutAttribute(timeoutAttribute); } - } - #endregion - - #region AssemblyInfo creation and cache logic. + var globalTimeout = TimeoutInfo.FromFixtureSettings(fixtureKind); + return globalTimeout.Timeout > 0 + ? globalTimeout + : null; + } /// /// Get the assembly info for the parameter type. @@ -399,19 +390,14 @@ private TestAssemblyInfo GetAssemblyInfo(Type type) assemblyInfo = new TestAssemblyInfo(assembly); - IReadOnlyList types = AssemblyEnumerator.GetTypes(assembly, assembly.FullName!, null); + Type[] types = AssemblyEnumerator.GetTypes(assembly, assembly.FullName!, null); foreach (Type t in types) { - if (t == null) - { - continue; - } - try { // Only examine classes which are TestClass or derives from TestClass attribute - if (!_reflectionHelper.IsDerivedAttributeDefined(t, inherit: true)) + if (!_reflectionHelper.IsDerivedAttributeDefined(t, inherit: false)) { continue; } @@ -433,41 +419,12 @@ private TestAssemblyInfo GetAssemblyInfo(Type type) if (IsAssemblyOrClassInitializeMethod(methodInfo)) { assemblyInfo.AssemblyInitializeMethod = methodInfo; - - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); - if (timeoutAttribute != null) - { - if (!timeoutAttribute.HasCorrectTimeout) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); - throw new TypeInspectionException(message); - } - - assemblyInfo.AssemblyInitializeMethodTimeoutMilliseconds = TimeoutInfo.FromTimeoutAttribute(timeoutAttribute); - } - else if (MSTestSettings.CurrentSettings.AssemblyInitializeTimeout > 0) - { - assemblyInfo.AssemblyInitializeMethodTimeoutMilliseconds = TimeoutInfo.FromFixtureSettings(FixtureKind.AssemblyInitialize); - } + assemblyInfo.AssemblyInitializeMethodTimeoutMilliseconds = TryGetTimeoutInfo(methodInfo, FixtureKind.AssemblyInitialize); } else if (IsAssemblyOrClassCleanupMethod(methodInfo)) { assemblyInfo.AssemblyCleanupMethod = methodInfo; - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); - if (timeoutAttribute != null) - { - if (!timeoutAttribute.HasCorrectTimeout) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); - throw new TypeInspectionException(message); - } - - assemblyInfo.AssemblyCleanupMethodTimeoutMilliseconds = TimeoutInfo.FromTimeoutAttribute(timeoutAttribute); - } - else if (MSTestSettings.CurrentSettings.AssemblyCleanupTimeout > 0) - { - assemblyInfo.AssemblyCleanupMethodTimeoutMilliseconds = TimeoutInfo.FromFixtureSettings(FixtureKind.AssemblyCleanup); - } + assemblyInfo.AssemblyCleanupMethodTimeoutMilliseconds = TryGetTimeoutInfo(methodInfo, FixtureKind.AssemblyCleanup); } } } @@ -594,20 +551,9 @@ private void UpdateInfoIfClassInitializeOrCleanupMethod( if (isInitializeMethod) { - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); - if (timeoutAttribute != null) - { - if (!timeoutAttribute.HasCorrectTimeout) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); - throw new TypeInspectionException(message); - } - - classInfo.ClassInitializeMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromTimeoutAttribute(timeoutAttribute)); - } - else if (MSTestSettings.CurrentSettings.ClassInitializeTimeout > 0) + if (TryGetTimeoutInfo(methodInfo, FixtureKind.ClassInitialize) is { } timeoutInfo) { - classInfo.ClassInitializeMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromFixtureSettings(FixtureKind.ClassInitialize)); + classInfo.ClassInitializeMethodTimeoutMilliseconds.Add(methodInfo, timeoutInfo); } if (isBase) @@ -627,20 +573,9 @@ private void UpdateInfoIfClassInitializeOrCleanupMethod( if (isCleanupMethod) { - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); - if (timeoutAttribute != null) - { - if (!timeoutAttribute.HasCorrectTimeout) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); - throw new TypeInspectionException(message); - } - - classInfo.ClassCleanupMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromTimeoutAttribute(timeoutAttribute)); - } - else if (MSTestSettings.CurrentSettings.ClassCleanupTimeout > 0) + if (TryGetTimeoutInfo(methodInfo, FixtureKind.ClassCleanup) is { } timeoutInfo) { - classInfo.ClassCleanupMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromFixtureSettings(FixtureKind.ClassCleanup)); + classInfo.ClassCleanupMethodTimeoutMilliseconds.Add(methodInfo, timeoutInfo); } if (isBase) @@ -693,20 +628,9 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( if (hasTestInitialize) { - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); - if (timeoutAttribute != null) + if (TryGetTimeoutInfo(methodInfo, FixtureKind.TestInitialize) is { } timeoutInfo) { - if (!timeoutAttribute.HasCorrectTimeout) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); - throw new TypeInspectionException(message); - } - - classInfo.TestInitializeMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromTimeoutAttribute(timeoutAttribute)); - } - else if (MSTestSettings.CurrentSettings.TestInitializeTimeout > 0) - { - classInfo.TestInitializeMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromFixtureSettings(FixtureKind.TestInitialize)); + classInfo.TestInitializeMethodTimeoutMilliseconds.Add(methodInfo, timeoutInfo); } if (!isBase) @@ -724,20 +648,9 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( if (hasTestCleanup) { - TimeoutAttribute? timeoutAttribute = _reflectionHelper.GetFirstNonDerivedAttributeOrDefault(methodInfo, inherit: false); - if (timeoutAttribute != null) + if (TryGetTimeoutInfo(methodInfo, FixtureKind.TestCleanup) is { } timeoutInfo) { - if (!timeoutAttribute.HasCorrectTimeout) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, methodInfo.DeclaringType!.FullName, methodInfo.Name); - throw new TypeInspectionException(message); - } - - classInfo.TestCleanupMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromTimeoutAttribute(timeoutAttribute)); - } - else if (MSTestSettings.CurrentSettings.TestCleanupTimeout > 0) - { - classInfo.TestCleanupMethodTimeoutMilliseconds.Add(methodInfo, TimeoutInfo.FromFixtureSettings(FixtureKind.TestCleanup)); + classInfo.TestCleanupMethodTimeoutMilliseconds.Add(methodInfo, timeoutInfo); } if (!isBase) @@ -762,24 +675,18 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( /// cannot be found, or a function is found that returns non-void, the result is /// set to error. /// - /// The test Method. - /// The test Class Info. - /// The test Context. - /// Indicates whether the test method should capture debug traces. /// /// The TestMethodInfo for the given test method. Null if the test method could not be found. /// - private TestMethodInfo? ResolveTestMethod(TestMethod testMethod, TestClassInfo testClassInfo, ITestContext testContext, bool captureDebugTraces) + private TestMethodInfo ResolveTestMethodInfo(TestMethod testMethod, TestClassInfo testClassInfo, ITestContext testContext, bool captureDebugTraces) { DebugEx.Assert(testMethod != null, "testMethod is Null"); DebugEx.Assert(testClassInfo != null, "testClassInfo is Null"); MethodInfo methodInfo = GetMethodInfoForTestMethod(testMethod, testClassInfo); - ExpectedExceptionBaseAttribute? expectedExceptionAttribute = _reflectionHelper.ResolveExpectedExceptionHelper(methodInfo, testMethod); TimeoutInfo timeout = GetTestTimeout(methodInfo, testMethod); - - var testMethodOptions = new TestMethodOptions(timeout, expectedExceptionAttribute, testContext, captureDebugTraces, GetTestMethodAttribute(methodInfo, testClassInfo)); + var testMethodOptions = new TestMethodOptions(timeout, testContext, captureDebugTraces, GetTestMethodAttribute(methodInfo, testClassInfo)); var testMethodInfo = new TestMethodInfo(methodInfo, testClassInfo, testMethodOptions); SetCustomProperties(testMethodInfo, testContext); @@ -787,20 +694,29 @@ private void UpdateInfoIfTestInitializeOrCleanupMethod( return testMethodInfo; } + private TestMethodInfo ResolveTestMethodInfoForDiscovery(TestMethod testMethod, TestClassInfo testClassInfo) + { + MethodInfo methodInfo = GetMethodInfoForTestMethod(testMethod, testClassInfo); + + // Let's build a fake options type as it won't be used. + return new TestMethodInfo(methodInfo, testClassInfo, new(TimeoutInfo.FromTimeout(-1), null, false, null!)); + } + /// /// Provides the Test Method Extension Attribute of the TestClass. /// /// The method info. /// The test class info. /// Test Method Attribute. - private TestMethodAttribute? GetTestMethodAttribute(MethodInfo methodInfo, TestClassInfo testClassInfo) + private TestMethodAttribute GetTestMethodAttribute(MethodInfo methodInfo, TestClassInfo testClassInfo) { - // Get the derived TestMethod attribute from reflection - TestMethodAttribute? testMethodAttribute = _reflectionHelper.GetFirstDerivedAttributeOrDefault(methodInfo, inherit: false); + // Get the derived TestMethod attribute from reflection. + // It should be non-null as it was already validated by IsValidTestMethod. + TestMethodAttribute testMethodAttribute = _reflectionHelper.GetFirstDerivedAttributeOrDefault(methodInfo, inherit: false)!; // Get the derived TestMethod attribute from Extended TestClass Attribute // If the extended TestClass Attribute doesn't have extended TestMethod attribute then base class returns back the original testMethod Attribute - testMethodAttribute = testClassInfo.ClassAttribute.GetTestMethodAttribute(testMethodAttribute!) ?? testMethodAttribute; + testMethodAttribute = testClassInfo.ClassAttribute.GetTestMethodAttribute(testMethodAttribute) ?? testMethodAttribute; return testMethodAttribute; } @@ -851,7 +767,8 @@ private MethodInfo GetMethodInfoForTestMethod(TestMethod testMethod, TestClassIn else if (methodBase != null) { Type[] parameters = methodBase.GetParameters().Select(i => i.ParameterType).ToArray(); - testMethodInfo = PlatformServiceProvider.Instance.ReflectionOperations.GetRuntimeMethod(methodBase.DeclaringType!, methodBase.Name, parameters); + // TODO: Should we pass true for includeNonPublic? + testMethodInfo = PlatformServiceProvider.Instance.ReflectionOperations.GetRuntimeMethod(methodBase.DeclaringType!, methodBase.Name, parameters, includeNonPublic: false); } return testMethodInfo is null @@ -911,15 +828,20 @@ private TimeoutInfo GetTestTimeout(MethodInfo methodInfo, TestMethod testMethod) /// /// The test Method Info. /// The test Context. - private static void SetCustomProperties(TestMethodInfo testMethodInfo, ITestContext testContext) + private void SetCustomProperties(TestMethodInfo testMethodInfo, ITestContext testContext) { DebugEx.Assert(testMethodInfo != null, "testMethodInfo is Null"); DebugEx.Assert(testMethodInfo.TestMethod != null, "testMethodInfo.TestMethod is Null"); - object[] attributes = testMethodInfo.TestMethod.GetCustomAttributes(typeof(TestPropertyAttribute), false); + IEnumerable attributes = _reflectionHelper.GetDerivedAttributes(testMethodInfo.TestMethod, inherit: true); DebugEx.Assert(attributes != null, "attributes is null"); - foreach (TestPropertyAttribute attribute in attributes.Cast()) + if (testMethodInfo.TestMethod.DeclaringType is { } testClass) + { + attributes = attributes.Concat(_reflectionHelper.GetDerivedAttributes(testClass, inherit: true)); + } + + foreach (TestPropertyAttribute attribute in attributes) { if (!ValidateAndAssignTestProperty(testMethodInfo, testContext, attribute.Name, attribute.Value)) { diff --git a/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs b/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs index fb0f910321..611e238ec5 100644 --- a/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs +++ b/src/Adapter/MSTest.TestAdapter/Execution/UnitTestRunner.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Security; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; @@ -11,6 +8,7 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; @@ -21,7 +19,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; /// /// The runner that runs a single unit test. Also manages the assembly and class cleanup methods at the end of the run. /// -internal class UnitTestRunner : MarshalByRefObject +internal sealed class UnitTestRunner : MarshalByRefObject { private readonly ConcurrentDictionary _fixtureTests = new(); private readonly TypeCache _typeCache; @@ -130,7 +128,7 @@ internal FixtureTestResult GetFixtureTestResult(TestMethod testMethod, string fi /// The test Method. /// The test context properties. /// The . - internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary testContextProperties) + internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary testContextProperties, IMessageLogger messageLogger) { Guard.NotNull(testMethod); @@ -138,13 +136,12 @@ internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary(testContextProperties); - ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties); - testContext.SetOutcome(UTF.UnitTestOutcome.InProgress); + ITestContext testContextForTestExecution = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties, messageLogger, UTF.UnitTestOutcome.InProgress); // Get the testMethod TestMethodInfo? testMethodInfo = _typeCache.GetTestMethodInfo( testMethod, - testContext, + testContextForTestExecution, MSTestSettings.CurrentSettings.CaptureDebugTraces); UnitTestResult[] result; @@ -160,7 +157,9 @@ internal UnitTestResult[] RunSingleTest(TestMethod testMethod, IDictionary classInfoCache = typeCache.ClassInfoListWithExecutableCleanupMethods; foreach (TestClassInfo classInfo in classInfoCache) { - classInfo.ExecuteClassCleanup(); + classInfo.ExecuteClassCleanup(testContext.Context); } IEnumerable assemblyInfoCache = typeCache.AssemblyInfoListWithExecutableCleanupMethods; foreach (TestAssemblyInfo assemblyInfo in assemblyInfoCache) { - assemblyInfo.ExecuteAssemblyCleanup(); + assemblyInfo.ExecuteAssemblyCleanup(testContext.Context); } } finally @@ -340,31 +347,35 @@ private bool IsTestMethodRunnable( } } - string? ignoreMessage = null; - bool isIgnoreAttributeOnClass = - _reflectHelper.IsNonDerivedAttributeDefined(testMethodInfo.Parent.ClassType, false); - bool isIgnoreAttributeOnMethod = - _reflectHelper.IsNonDerivedAttributeDefined(testMethodInfo.TestMethod, false); - - if (isIgnoreAttributeOnClass) + // TODO: Executor should never be null. Is it incorrectly annotated? + string? ignoreMessage = testMethodInfo.Parent.ClassAttribute.IgnoreMessage ?? testMethodInfo.TestMethodOptions.Executor?.IgnoreMessage; + if (ignoreMessage is not null) { - ignoreMessage = _reflectHelper.GetIgnoreMessage(testMethodInfo.Parent.ClassType); + notRunnableResult = [new UnitTestResult(UnitTestOutcome.Ignored, ignoreMessage)]; + return false; } - if (StringEx.IsNullOrEmpty(ignoreMessage) && isIgnoreAttributeOnMethod) + IgnoreAttribute? ignoreAttributeOnClass = + _reflectHelper.GetFirstNonDerivedAttributeOrDefault(testMethodInfo.Parent.ClassType, inherit: false); + ignoreMessage = ignoreAttributeOnClass?.IgnoreMessage; + + IgnoreAttribute? ignoreAttributeOnMethod = + _reflectHelper.GetFirstNonDerivedAttributeOrDefault(testMethodInfo.TestMethod, inherit: false); + + if (StringEx.IsNullOrEmpty(ignoreMessage) && ignoreAttributeOnMethod is not null) { - ignoreMessage = _reflectHelper.GetIgnoreMessage(testMethodInfo.TestMethod); + ignoreMessage = ignoreAttributeOnMethod.IgnoreMessage; } - if (isIgnoreAttributeOnClass || isIgnoreAttributeOnMethod) + if (ignoreAttributeOnClass is not null || ignoreAttributeOnMethod is not null) { - { - notRunnableResult = [new UnitTestResult(UnitTestOutcome.Ignored, ignoreMessage)]; - return false; - } + notRunnableResult = [new UnitTestResult(UnitTestOutcome.Ignored, ignoreMessage)]; + return false; } notRunnableResult = null; return true; } + + internal void ForceCleanup(IDictionary sourceLevelParameters, IMessageLogger logger) => ClassCleanupManager.ForceCleanup(_typeCache, sourceLevelParameters, logger); } diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs index 67d14e8b73..648fbc41db 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/ExceptionExtensions.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -41,7 +37,7 @@ internal static Exception GetRealException(this Exception exception) /// /// An object. /// Exception message. - internal static string TryGetMessage(this Exception exception) + internal static string TryGetMessage(this Exception? exception) { if (exception == null) { diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs index 8263640283..d220cd9470 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/MethodInfoExtensions.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; @@ -42,10 +39,16 @@ internal static bool HasCorrectClassOrAssemblyCleanupSignature(this MethodInfo m return method is { IsStatic: true, IsPublic: true } && - (method.GetParameters().Length == 0) && + HasCorrectClassOrAssemblyCleanupParameters(method) && method.IsValidReturnType(); } + private static bool HasCorrectClassOrAssemblyCleanupParameters(MethodInfo method) + { + ParameterInfo[] parameters = method.GetParameters(); + return parameters.Length == 0 || (parameters.Length == 1 && parameters[0].ParameterType == typeof(TestContext)); + } + /// /// Verifies that the test Initialize/cleanup has the correct signature. /// @@ -74,7 +77,7 @@ internal static bool HasCorrectTestMethodSignature(this MethodInfo method, bool DebugEx.Assert(method != null, "method should not be null."); return - method is { IsAbstract: false, IsStatic: false, IsGenericMethod: false } && + method is { IsAbstract: false, IsStatic: false } && (method.IsPublic || (discoverInternals && method.IsAssembly)) && (method.GetParameters().Length == 0 || ignoreParameterLength) && method.IsValidReturnType(); // Match return type Task for async methods only. Else return type void. @@ -160,6 +163,11 @@ internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object? try { + if (methodInfo.IsGenericMethod) + { + methodInfo = ConstructGenericMethod(methodInfo, arguments); + } + invokeResult = methodInfo.Invoke(classInstance, arguments); } catch (Exception ex) when (ex is TargetParameterCountException or ArgumentException) @@ -188,4 +196,93 @@ internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object? valueTask.GetAwaiter().GetResult(); } } + + // Scenarios to test: + // + // [DataRow(null, "Hello")] + // [DataRow("Hello", null)] + // public void TestMethod(T t1, T t2) { } + // + // [DataRow(0, "Hello")] + // public void TestMethod(T2 p0, T1, p1) { } + private static MethodInfo ConstructGenericMethod(MethodInfo methodInfo, object?[]? arguments) + { + DebugEx.Assert(methodInfo.IsGenericMethod, "ConstructGenericMethod should only be called for a generic method."); + + if (arguments is null) + { + // An example where this could happen is: + // [TestMethod] + // public void MyTestMethod() { } + throw new TestFailedException(ObjectModel.UnitTestOutcome.Error, string.Format(CultureInfo.InvariantCulture, Resource.GenericParameterCantBeInferredBecauseNoArguments, methodInfo.Name)); + } + + Type[] genericDefinitions = methodInfo.GetGenericArguments(); + var map = new (Type GenericDefinition, Type? Substitution)[genericDefinitions.Length]; + for (int i = 0; i < map.Length; i++) + { + map[i] = (genericDefinitions[i], null); + } + + ParameterInfo[] parameters = methodInfo.GetParameters(); + for (int i = 0; i < parameters.Length; i++) + { + Type parameterType = parameters[i].ParameterType; + if (!parameterType.IsGenericMethodParameter() || arguments[i] is null) + { + continue; + } + + Type substitution = arguments[i]!/*Very strange nullability warning*/.GetType(); + int mapIndexForParameter = GetMapIndexForParameterType(parameterType, map); + Type? existingSubstitution = map[mapIndexForParameter].Substitution; + + if (existingSubstitution is null || substitution.IsAssignableFrom(existingSubstitution)) + { + map[mapIndexForParameter] = (parameterType, substitution); + } + else if (existingSubstitution.IsAssignableFrom(substitution)) + { + // Do nothing. We already have a good existing substitution. + } + else + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resource.GenericParameterConflict, parameterType.Name, existingSubstitution, substitution)); + } + } + + for (int i = 0; i < map.Length; i++) + { + // TODO: Better to throw? or tolerate and transform to typeof(object)? + // This is reachable in the following case for example: + // [DataRow(null)] + // public void TestMethod(T t) { } + Type substitution = map[i].Substitution ?? throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resource.GenericParameterCantBeInferred, map[i].GenericDefinition.Name)); + genericDefinitions[i] = substitution; + } + + try + { + return methodInfo.MakeGenericMethod(genericDefinitions); + } + catch (Exception e) + { + // The caller catches ArgumentExceptions and will lose the original exception details. + // We transform the exception to TestFailedException here to preserve its details. + throw new TestFailedException(ObjectModel.UnitTestOutcome.Error, e.TryGetMessage(), e.TryGetStackTraceInformation(), e); + } + } + + private static int GetMapIndexForParameterType(Type parameterType, (Type GenericDefinition, Type? Substitution)[] map) + { + for (int i = 0; i < map.Length; i++) + { + if (parameterType == map[i].GenericDefinition) + { + return i; + } + } + + throw ApplicationStateGuard.Unreachable(); + } } diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs index c0ed82d9cb..fafe4e6346 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs @@ -86,6 +86,7 @@ internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string testMethod.DataType = dataType; testMethod.SerializedData = data; + testMethod.TestDataSourceIgnoreMessage = testCase.GetPropertyValue(Constants.TestDataSourceIgnoreMessageProperty) as string; } if (testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) is string declaringClassName && declaringClassName != testClassName) diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs index be7f3963cd..d7f47e8b4c 100644 --- a/src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs @@ -7,6 +7,11 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif public static class TestResultExtensions { /// @@ -31,8 +36,16 @@ internal static UnitTestResult[] ToUnitTestResults(this IReadOnlyCollection @@ -24,9 +31,24 @@ public static UnitTestOutcome ToUnitTestOutcome(this UTF.UnitTestOutcome framewo UTF.UnitTestOutcome.Timeout => UnitTestOutcome.Timeout, UTF.UnitTestOutcome.NotRunnable => UnitTestOutcome.NotRunnable, UTF.UnitTestOutcome.NotFound => UnitTestOutcome.NotFound, + UTF.UnitTestOutcome.Ignored => UnitTestOutcome.Ignored, _ => UnitTestOutcome.Error, }; + internal static UTF.UnitTestOutcome ToAdapterOutcome(this UnitTestOutcome outcome) + => outcome switch + { + UnitTestOutcome.Failed => UTF.UnitTestOutcome.Failed, + UnitTestOutcome.Inconclusive => UTF.UnitTestOutcome.Inconclusive, + UnitTestOutcome.InProgress => UTF.UnitTestOutcome.InProgress, + UnitTestOutcome.Passed => UTF.UnitTestOutcome.Passed, + UnitTestOutcome.Timeout => UTF.UnitTestOutcome.Timeout, + UnitTestOutcome.NotRunnable => UTF.UnitTestOutcome.NotRunnable, + UnitTestOutcome.NotFound => UTF.UnitTestOutcome.NotFound, + UnitTestOutcome.Ignored => UTF.UnitTestOutcome.Ignored, + _ => UTF.UnitTestOutcome.Error, + }; + /// /// Returns more important outcome of two. /// diff --git a/src/Adapter/MSTest.TestAdapter/Friends.cs b/src/Adapter/MSTest.TestAdapter/Friends.cs index 989e6a0b7a..bdfd4316bd 100644 --- a/src/Adapter/MSTest.TestAdapter/Friends.cs +++ b/src/Adapter/MSTest.TestAdapter/Friends.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // Friend assemblies -using System.Runtime.CompilerServices; - [assembly: InternalsVisibleTo(assemblyName: "Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo(assemblyName: "DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: InternalsVisibleTo(assemblyName: "MSTest.VstestConsoleWrapper.IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs b/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs index 37623af61b..c82f46d996 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/AttributeComparer.cs @@ -10,24 +10,5 @@ public static bool IsNonDerived(Attribute attribute) => // So, this should NOT be refactored to 'attribute is TAttribute'. attribute.GetType() == typeof(TAttribute); - public static bool IsDerived(Attribute attribute) - { - Type attributeType = attribute.GetType(); - - // IsSubclassOf returns false when the types are equal. - if (attributeType == typeof(TAttribute)) - { - return true; - } - - // IsAssignableFrom also does this internally, but later falls to check generic - // and we don't need that. - if (!typeof(TAttribute).IsInterface) - { - // This returns false when TAttribute is interface (like ITestDataSource). - return attributeType.IsSubclassOf(typeof(TAttribute)); - } - - return typeof(TAttribute).IsAssignableFrom(attributeType); - } + public static bool IsDerived(Attribute attribute) => attribute is TAttribute; } diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/DataSerializationHelper.cs b/src/Adapter/MSTest.TestAdapter/Helpers/DataSerializationHelper.cs index 0b9e1bc782..efafabaa74 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/DataSerializationHelper.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/DataSerializationHelper.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; using System.Runtime.Serialization.Json; -using System.Text; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -14,7 +12,7 @@ internal static class DataSerializationHelper { UseSimpleDictionaryFormat = true, EmitTypeInformation = System.Runtime.Serialization.EmitTypeInformation.Always, - DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("O", System.Globalization.CultureInfo.InvariantCulture), + DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("O", CultureInfo.InvariantCulture), }; /// diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs b/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs index 949291af0c..792366d479 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/EnvironmentWrapper.cs @@ -3,7 +3,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; -internal class EnvironmentWrapper : IEnvironment +internal sealed class EnvironmentWrapper : IEnvironment { public string MachineName => Environment.MachineName; } diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/FixtureKind.cs b/src/Adapter/MSTest.TestAdapter/Helpers/FixtureKind.cs index c472b72d4b..7258055724 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/FixtureKind.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/FixtureKind.cs @@ -3,7 +3,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Internal and self-explanatory")] +[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Internal and self-explanatory")] internal enum FixtureKind { AssemblyInitialize, diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs b/src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs index f4c60e6e4b..79dd00a0aa 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/FixtureMethodRunner.cs @@ -1,14 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; -using System.Runtime.InteropServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; using UnitTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; @@ -18,8 +15,18 @@ internal static class FixtureMethodRunner { internal static TestFailedException? RunWithTimeoutAndCancellation( Action action, CancellationTokenSource cancellationTokenSource, TimeoutInfo? timeoutInfo, MethodInfo methodInfo, - IExecutionContextScope executionContextScope, string methodCanceledMessageFormat, string methodTimedOutMessageFormat) + IExecutionContextScope executionContextScope, string methodCanceledMessageFormat, string methodTimedOutMessageFormat, + // When a test method is marked with [Timeout], this timeout is applied from ctor to destructor, so we need to take + // that into account when processing the OCE of the action. + (CancellationTokenSource TokenSource, int Timeout)? testTimeoutInfo = default) { + if (cancellationTokenSource.Token.IsCancellationRequested) + { + return new( + UnitTestOutcome.Timeout, + string.Format(CultureInfo.InvariantCulture, methodCanceledMessageFormat, methodInfo.DeclaringType!.FullName, methodInfo.Name)); + } + // If no timeout is specified, we can run the action directly. This avoids any overhead of creating a task/thread and // ensures that the execution context is preserved (as we run the action on the current thread). if (timeoutInfo is null) @@ -31,13 +38,22 @@ internal static class FixtureMethodRunner } catch (Exception ex) { - Exception realException = ex.GetRealException(); - - if (realException is OperationCanceledException oce && oce.CancellationToken == cancellationTokenSource.Token) + if (ex.GetRealException().IsOperationCanceledExceptionFromToken(cancellationTokenSource.Token)) { return new( UnitTestOutcome.Timeout, - string.Format(CultureInfo.InvariantCulture, methodCanceledMessageFormat, methodInfo.DeclaringType!.FullName, methodInfo.Name)); + testTimeoutInfo?.TokenSource.Token.IsCancellationRequested == true + ? string.Format( + CultureInfo.InvariantCulture, + methodTimedOutMessageFormat, + methodInfo.DeclaringType!.FullName, + methodInfo.Name, + testTimeoutInfo.Value.Timeout) + : string.Format( + CultureInfo.InvariantCulture, + methodCanceledMessageFormat, + methodInfo.DeclaringType!.FullName, + methodInfo.Name)); } throw; @@ -79,7 +95,7 @@ internal static class FixtureMethodRunner action(); return null; } - catch (OperationCanceledException) + catch (Exception ex) when (ex.IsOperationCanceledExceptionFromToken(cancellationTokenSource.Token)) { // Ideally we would like to check that the token of the exception matches cancellationTokenSource but TestContext // instances are not well defined so we have to handle the exception entirely. @@ -152,9 +168,7 @@ internal static class FixtureMethodRunner methodInfo.Name, timeout)); } - catch (Exception ex) when - (ex is OperationCanceledException - || (ex is AggregateException aggregateEx && aggregateEx.InnerExceptions.OfType().Any())) + catch (Exception ex) when (ex.IsOperationCanceledExceptionFromToken(cancellationTokenSource.Token)) { return new( UnitTestOutcome.Timeout, @@ -172,7 +186,7 @@ internal static class FixtureMethodRunner } } - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [SupportedOSPlatform("windows")] private static TestFailedException? RunWithTimeoutAndCancellationWithSTAThread( Action action, CancellationTokenSource cancellationTokenSource, int timeout, MethodInfo methodInfo, IExecutionContextScope executionContextScope, string methodCanceledMessageFormat, string methodTimedOutMessageFormat) @@ -216,12 +230,7 @@ internal static class FixtureMethodRunner methodInfo.Name, timeout)); } - catch (Exception ex) when - - // This exception occurs when the cancellation happens before the task is actually started. - ((ex is TaskCanceledException tce && tce.CancellationToken == cancellationTokenSource.Token) - || (ex is OperationCanceledException oce && oce.CancellationToken == cancellationTokenSource.Token) - || (ex is AggregateException aggregateEx && aggregateEx.InnerExceptions.OfType().Any())) + catch (Exception ex) when (ex.IsOperationCanceledExceptionFromToken(cancellationTokenSource.Token)) { return new( UnitTestOutcome.Timeout, diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/MSTestDiscovererHelpers.cs b/src/Adapter/MSTest.TestAdapter/Helpers/MSTestDiscovererHelpers.cs index 7d4d8ee7b8..3b76323e58 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/MSTestDiscovererHelpers.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/MSTestDiscovererHelpers.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs b/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs index 7bbf28454d..7dcd86d56d 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs @@ -1,18 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Globalization; -using System.Reflection; using System.Security; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for mocking")] internal class ReflectHelper : MarshalByRefObject { #pragma warning disable RS0030 // Do not use banned APIs @@ -34,6 +31,9 @@ internal class ReflectHelper : MarshalByRefObject /// /// Checks to see if a member or type is decorated with the given attribute. The type is checked exactly. If attribute is derived (inherits from) a class, e.g. [MyTestClass] from [TestClass] it won't match if you look for [TestClass]. The inherit parameter does not impact this checking. /// + /// + /// Note that because derived attribute types are not considered, should be sealed. + /// /// Attribute to search for by fully qualified name. /// Member/Type to test. /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. @@ -57,28 +57,6 @@ public virtual bool IsNonDerivedAttributeDefined(MemberInfo memberIn return false; } - /// - /// Checks to see if a member or type is decorated with the given attribute. The type is checked exactly. If attribute is derived (inherits from) a class, e.g. [MyTestClass] from [TestClass] it won't match if you look for [TestClass]. The inherit parameter does not impact this checking. - /// - /// Attribute to search for by fully qualified name. - /// Type to test. - /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. - /// True if the attribute of the specified type is defined on this member or a class. - public virtual bool IsNonDerivedAttributeDefined(Type type, bool inherit) - where TAttribute : Attribute - => IsNonDerivedAttributeDefined((MemberInfo)type, inherit); - - /// - /// Checks to see if a member or type is decorated with the given attribute, or an attribute that derives from it. e.g. [MyTestClass] from [TestClass] will match if you look for [TestClass]. The inherit parameter does not impact this checking. - /// - /// Attribute to search for. - /// Type to test. - /// Inspect inheritance chain of the member or class. E.g. if parent class has this attribute defined. - /// True if the attribute of the specified type is defined on this member or a class. - public virtual bool IsDerivedAttributeDefined(Type type, bool inherit) - where TAttribute : Attribute - => IsDerivedAttributeDefined((MemberInfo)type, inherit); - /// /// Checks to see if a member or type is decorated with the given attribute, or an attribute that derives from it. e.g. [MyTestClass] from [TestClass] will match if you look for [TestClass]. The inherit parameter does not impact this checking. /// @@ -108,41 +86,6 @@ public virtual bool IsDerivedAttributeDefined(Type type, bool inheri return false; } - /// - /// Resolves the expected exception attribute. The function will try to - /// get all the expected exception attributes defined for a testMethod. - /// - /// The MethodInfo instance. - /// The test method. - /// - /// The expected exception attribute found for this test. Null if not found. - /// - public virtual ExpectedExceptionBaseAttribute? ResolveExpectedExceptionHelper(MethodInfo methodInfo, TestMethod testMethod) - { - DebugEx.Assert(methodInfo != null, "MethodInfo should be non-null"); - - // Get the expected exception attribute - ExpectedExceptionBaseAttribute? expectedException; - try - { - expectedException = GetFirstDerivedAttributeOrDefault(methodInfo, inherit: true); - } - catch (Exception ex) - { - // If construction of the attribute throws an exception, indicate that there was an - // error when trying to run the test - string errorMessage = string.Format( - CultureInfo.CurrentCulture, - Resource.UTA_ExpectedExceptionAttributeConstructionException, - testMethod.FullClassName, - testMethod.Name, - ex.GetFormattedExceptionMessage()); - throw new TypeInspectionException(errorMessage); - } - - return expectedException ?? null; - } - /// /// Returns object to be used for controlling lifetime, null means infinite lifetime. /// @@ -156,7 +99,7 @@ public virtual bool IsDerivedAttributeDefined(Type type, bool inheri public override object InitializeLifetimeService() => null!; /// - /// Gets first attribute that matches the type (but is not derived from it). Use this together with attribute that does not allow multiple. + /// Gets first attribute that matches the type (but is not derived from it). Use this together with attribute that is both sealed and does not allow multiple. /// In such case there cannot be more attributes, and this will avoid the cost of /// checking for more than one attribute. /// @@ -164,7 +107,6 @@ public virtual bool IsDerivedAttributeDefined(Type type, bool inheri /// The type, assembly or method. /// If we should inspect parents of this type. /// The attribute that is found or null. - /// Throws when multiple attributes are found (the attribute must allow multiple). public TAttribute? GetFirstNonDerivedAttributeOrDefault(ICustomAttributeProvider attributeProvider, bool inherit) where TAttribute : Attribute { @@ -281,6 +223,16 @@ internal static TestIdGenerationStrategy GetTestIdGenerationStrategy(Assembly as .OfType() .FirstOrDefault()?.DiscoveryOption; + /// + /// Gets TestDataSourceOptions assembly level attribute. + /// + /// The test assembly. + /// The TestDataSourceOptionsAttribute if set. Null otherwise. + internal static TestDataSourceOptionsAttribute? GetTestDataSourceOptions(Assembly assembly) + => PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(assembly, typeof(TestDataSourceOptionsAttribute)) + .OfType() + .FirstOrDefault(); + /// /// Get the parallelization behavior for a test method. /// @@ -342,15 +294,6 @@ internal static bool IsDoNotParallelizeSet(Assembly assembly) internal virtual int? GetPriority(MemberInfo priorityAttributeProvider) => GetFirstDerivedAttributeOrDefault(priorityAttributeProvider, inherit: true)?.Priority; - /// - /// Priority if any set for test method. Will return priority if attribute is applied to TestMethod - /// else null. - /// - /// The member to inspect. - /// Priority value if defined. Null otherwise. - internal virtual string? GetIgnoreMessage(MemberInfo ignoreAttributeProvider) => - GetFirstDerivedAttributeOrDefault(ignoreAttributeProvider, inherit: true)?.IgnoreMessage; - /// /// Gets the class cleanup lifecycle for the class, if set. /// @@ -386,6 +329,11 @@ internal virtual IEnumerable GetTestPropertiesAsTraits(MemberInfo testPro { IEnumerable testPropertyAttributes = GetDerivedAttributes(testPropertyProvider, inherit: true); + if (testPropertyProvider.DeclaringType is { } testClass) + { + testPropertyAttributes = testPropertyAttributes.Concat(GetDerivedAttributes(testClass, inherit: true)); + } + foreach (TestPropertyAttribute testProperty in testPropertyAttributes) { var testPropertyPair = new Trait(testProperty.Name, testProperty.Value); @@ -435,7 +383,7 @@ internal virtual IEnumerable GetTestPropertiesAsTraits(MemberInfo testPro /// The member to inspect. /// Look at inheritance chain. /// attributes defined. - private Attribute[] GetCustomAttributesCached(ICustomAttributeProvider attributeProvider, bool inherit) + internal Attribute[] GetCustomAttributesCached(ICustomAttributeProvider attributeProvider, bool inherit) { // If the information is cached, then use it otherwise populate the cache using // the reflection APIs. diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs b/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs index 5f7e649824..2503c9f3bb 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/RunSettingsUtilities.cs @@ -1,17 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Xml; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; -internal class RunSettingsUtilities +internal static class RunSettingsUtilities { /// /// Gets the settings to be used while creating XmlReader for runsettings. diff --git a/src/Adapter/MSTest.TestAdapter/Helpers/TestRunParameters.cs b/src/Adapter/MSTest.TestAdapter/Helpers/TestRunParameters.cs index 6703b44ce1..9a324be172 100644 --- a/src/Adapter/MSTest.TestAdapter/Helpers/TestRunParameters.cs +++ b/src/Adapter/MSTest.TestAdapter/Helpers/TestRunParameters.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Xml; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; diff --git a/src/Adapter/MSTest.TestAdapter/IPlatformServiceProvider.cs b/src/Adapter/MSTest.TestAdapter/IPlatformServiceProvider.cs index 8bf09bab56..8923a5c1c8 100644 --- a/src/Adapter/MSTest.TestAdapter/IPlatformServiceProvider.cs +++ b/src/Adapter/MSTest.TestAdapter/IPlatformServiceProvider.cs @@ -3,6 +3,9 @@ using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; @@ -56,6 +59,8 @@ internal interface IPlatformServiceProvider /// TestRunCancellationToken? TestRunCancellationToken { get; set; } + bool IsGracefulStopRequested { get; set; } + /// /// Creates an instance to the platform service for a test source host. /// @@ -121,5 +126,5 @@ ITestSourceHost CreateTestSourceHost( /// /// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant. /// - ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties); + ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome); } diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.Linux.nuspec b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.NonWindows.nuspec similarity index 76% rename from src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.Linux.nuspec rename to src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.NonWindows.nuspec index 6bd0c6adb2..62d93b1975 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.Linux.nuspec +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.NonWindows.nuspec @@ -24,6 +24,10 @@ + + + + PACKAGE.md @@ -32,44 +36,46 @@ - - + - - + - - + - - + - - + + + + + + + + diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj index fb25147250..e195bbe50d 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ true MSTest.TestAdapter.nuspec - MSTest.TestAdapter.Linux.nuspec + MSTest.TestAdapter.NonWindows.nuspec $(OutputPath) MSTest.TestAdapter MSTest TestFramework TestAdapter VisualStudio Unittest MSTestV2 Microsoft @@ -48,12 +48,17 @@ - + + + Analyzer + false + - + @@ -77,11 +82,11 @@ - - + + @@ -97,7 +102,7 @@ PreserveNewest - + PreserveNewest @@ -143,6 +148,7 @@ + diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec index dcb1207828..48850bcbb4 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec +++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec @@ -32,6 +32,10 @@ + + + + PACKAGE.md @@ -40,8 +44,7 @@ - - + @@ -49,23 +52,20 @@ - - - + - - + @@ -74,27 +74,41 @@ - - + - - + + + + + + + + - - + + + diff --git a/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs b/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs index 6aa0c4c10f..c371cd755c 100644 --- a/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs +++ b/src/Adapter/MSTest.TestAdapter/MSTestSettings.cs @@ -1,14 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Xml; -using System.Xml.Linq; - -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +#if !WINDOWS_UWP using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +#endif using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; @@ -20,6 +17,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// Adapter Settings for the run. /// [Serializable] +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class MSTestSettings { /// @@ -256,7 +260,9 @@ public static void PopulateSettings(MSTestSettings settings) [Obsolete("this function will be removed in v4.0.0")] public static void PopulateSettings(IDiscoveryContext? context) => PopulateSettings(context, null, null); - private static bool IsRunSettingsFileHasMSTestSettings(string? runSettingsXml) => IsRunSettingsFileHasSettingName(runSettingsXml, SettingsName) || IsRunSettingsFileHasSettingName(runSettingsXml, SettingsNameAlias); +#if !WINDOWS_UWP + private static bool IsRunSettingsFileHasMSTestSettings(string? runSettingsXml) + => IsRunSettingsFileHasSettingName(runSettingsXml, SettingsName) || IsRunSettingsFileHasSettingName(runSettingsXml, SettingsNameAlias); private static bool IsRunSettingsFileHasSettingName(string? runSettingsXml, string SettingName) { @@ -281,6 +287,7 @@ private static bool IsRunSettingsFileHasSettingName(string? runSettingsXml, stri return !reader.EOF; } +#endif /// /// Populate adapter settings from the context. @@ -291,16 +298,25 @@ private static bool IsRunSettingsFileHasSettingName(string? runSettingsXml, stri /// internal static void PopulateSettings(IDiscoveryContext? context, IMessageLogger? logger, IConfiguration? configuration) { - if (configuration?["mstest"] != null && context?.RunSettings != null && IsRunSettingsFileHasMSTestSettings(context.RunSettings.SettingsXml)) +#if !WINDOWS_UWP + if (configuration?["mstest"] != null + && context?.RunSettings != null + && IsRunSettingsFileHasMSTestSettings(context.RunSettings.SettingsXml)) { throw new InvalidOperationException(Resource.DuplicateConfigurationError); } +#endif // This will contain default adapter settings var settings = new MSTestSettings(); var runConfigurationSettings = RunConfigurationSettings.PopulateSettings(context); - if (!StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml) && configuration?["mstest"] is null) +#if !WINDOWS_UWP + if (!StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml) + && configuration?["mstest"] is null) +#else + if (!StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml)) +#endif { MSTestSettings? aliasSettings = GetSettings(context.RunSettings.SettingsXml, SettingsNameAlias, logger); @@ -318,11 +334,13 @@ internal static void PopulateSettings(IDiscoveryContext? context, IMessageLogger SetGlobalSettings(context.RunSettings.SettingsXml, settings, logger); } +#if !WINDOWS_UWP else if (configuration?["mstest"] is not null) { RunConfigurationSettings.SetRunConfigurationSettingsFromConfig(configuration, runConfigurationSettings); SetSettingsFromConfig(configuration, logger, settings); } +#endif CurrentSettings = settings; RunConfigurationSettings = runConfigurationSettings; @@ -480,11 +498,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge CultureInfo.CurrentCulture, Resource.InvalidClassCleanupLifecycleValue, value, -#if NET - string.Join(", ", Enum.GetNames()))); -#else string.Join(", ", EnumPolyfill.GetNames()))); -#endif break; } @@ -811,11 +825,7 @@ private static void SetParallelSettings(XmlReader reader, MSTestSettings setting CultureInfo.CurrentCulture, Resource.InvalidParallelScopeValue, value, -#if NET - string.Join(", ", Enum.GetNames()))); -#else string.Join(", ", EnumPolyfill.GetNames()))); -#endif break; } @@ -842,11 +852,7 @@ private static void SetParallelSettings(XmlReader reader, MSTestSettings setting private static bool TryParseEnum(string value, out T result) where T : struct, Enum => Enum.TryParse(value, true, out result) -#if NET6_0_OR_GREATER - && Enum.IsDefined(result); -#else - && Enum.IsDefined(typeof(T), result); -#endif + && EnumPolyfill.IsDefined(result); private static void SetGlobalSettings( [StringSyntax(StringSyntaxAttribute.Xml, nameof(runsettingsXml))] string runsettingsXml, @@ -870,6 +876,7 @@ private static void SetGlobalSettings( } } +#if !WINDOWS_UWP private static void ParseBooleanSetting(IConfiguration configuration, string key, IMessageLogger? logger, Action setSetting) { if (configuration[$"mstest:{key}"] is not string value) @@ -967,22 +974,16 @@ internal static void SetSettingsFromConfig(IConfiguration configuration, IMessag if (configuration["mstest:classCleanupLifecycle"] is string classCleanupLifecycle) { - if (TryParseEnum(classCleanupLifecycle, out ClassCleanupBehavior lifecycle)) - { - settings.ClassCleanupLifecycle = lifecycle; - } - else + if (!TryParseEnum(classCleanupLifecycle, out ClassCleanupBehavior lifecycle)) { throw new AdapterSettingsException(string.Format( CultureInfo.CurrentCulture, Resource.InvalidClassCleanupLifecycleValue, classCleanupLifecycle, -#if NET - string.Join(", ", Enum.GetNames()))); -#else string.Join(", ", EnumPolyfill.GetNames()))); -#endif } + + settings.ClassCleanupLifecycle = lifecycle; } if (configuration["mstest:parallelism:workers"] is string workers) @@ -1005,25 +1006,20 @@ internal static void SetSettingsFromConfig(IConfiguration configuration, IMessag if (configuration["mstest:parallelism:scope"] is string value) { value = value.Equals("class", StringComparison.OrdinalIgnoreCase) ? "ClassLevel" - : value.Equals("methood", StringComparison.OrdinalIgnoreCase) ? "MethodLevel" : value; - if (TryParseEnum(value, out ExecutionScope scope)) - { - settings.ParallelizationScope = scope; - } - else + : value.Equals("method", StringComparison.OrdinalIgnoreCase) ? "MethodLevel" : value; + if (!TryParseEnum(value, out ExecutionScope scope)) { throw new AdapterSettingsException(string.Format( CultureInfo.CurrentCulture, Resource.InvalidParallelScopeValue, value, -#if NET - string.Join(", ", Enum.GetNames()))); -#else string.Join(", ", EnumPolyfill.GetNames()))); -#endif } + + settings.ParallelizationScope = scope; } MSTestSettingsProvider.Load(configuration); } +#endif } diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/AdapterSettingsException.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/AdapterSettingsException.cs index 774406f04d..1942c45c2a 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/AdapterSettingsException.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/AdapterSettingsException.cs @@ -3,7 +3,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; -internal class AdapterSettingsException : Exception +internal sealed class AdapterSettingsException : Exception { internal AdapterSettingsException(string? message) : base(message) diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/StackTraceInformation.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/StackTraceInformation.cs index 4d68fef812..43923ec246 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/StackTraceInformation.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/StackTraceInformation.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; [Serializable] -internal class StackTraceInformation +internal sealed class StackTraceInformation { public StackTraceInformation(string stackTrace) : this(stackTrace, null, 0, 0) diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs index f27c2e85af..5e9a68270d 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestAssemblySettings.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; [Serializable] -internal class TestAssemblySettings +internal sealed class TestAssemblySettings { public TestAssemblySettings() => Workers = -1; diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestFailedException.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestFailedException.cs index 2e7373532c..1b11693dcc 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestFailedException.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestFailedException.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// Internal class to indicate Test Execution failure. /// [Serializable] -internal class TestFailedException : Exception +internal sealed class TestFailedException : Exception { public TestFailedException(UnitTestOutcome outcome, string errorMessage) : this(outcome, errorMessage, null, null) diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs index 9da709697a..5b94d2bfa5 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethod.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using Microsoft.TestPlatform.AdapterUtilities; @@ -16,6 +15,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// /// TestMethod contains information about a unit test method that needs to be executed. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif [Serializable] public sealed class TestMethod : ITestMethod { @@ -137,6 +143,8 @@ public string? DeclaringClassFullName /// internal string?[]? SerializedData { get; set; } + internal string? TestDataSourceIgnoreMessage { get; set; } + /// /// Gets or sets the test group set during discovery. /// diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethodOptions.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethodOptions.cs index c2ccd4e3c4..8b1804ce83 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethodOptions.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/TestMethodOptions.cs @@ -10,4 +10,4 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// /// A facade service for options passed to a test method. /// -internal record TestMethodOptions(TimeoutInfo TimeoutInfo, ExpectedExceptionBaseAttribute? ExpectedException, ITestContext? TestContext, bool CaptureDebugTraces, TestMethodAttribute? Executor); +internal sealed record TestMethodOptions(TimeoutInfo TimeoutInfo, ITestContext? TestContext, bool CaptureDebugTraces, TestMethodAttribute Executor); diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/TypeInspectionException.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/TypeInspectionException.cs index aa269a088b..0e13ffe8e9 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/TypeInspectionException.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/TypeInspectionException.cs @@ -7,7 +7,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// Internal class to indicate type inspection failure. /// [Serializable] -internal class TypeInspectionException : Exception +internal sealed class TypeInspectionException : Exception { public TypeInspectionException() : base() diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs index 646d2b010a..1701b39c82 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestElement.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.TestPlatform.AdapterUtilities; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -16,7 +13,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// [Serializable] [DebuggerDisplay("{GetDisplayName()} ({TestMethod.ManagedTypeName})")] -internal class UnitTestElement +internal sealed class UnitTestElement { /// /// Initializes a new instance of the class. @@ -36,11 +33,6 @@ public UnitTestElement(TestMethod testMethod) /// public TestMethod TestMethod { get; private set; } - /// - /// Gets or sets a value indicating whether the unit test should be ignored at run-time. - /// - public bool Ignored { get; set; } - /// /// Gets or sets a value indicating whether it is a async test. /// @@ -170,7 +162,7 @@ internal TestCase ToTestCase() testCase.SetPropertyValue(Constants.PriorityProperty, Priority.Value); } - if (Traits != null) + if (Traits is { Length: > 0 }) { testCase.Traits.AddRange(Traits); } @@ -214,6 +206,7 @@ internal TestCase ToTestCase() testCase.SetPropertyValue(Constants.TestDynamicDataTypeProperty, (int)TestMethod.DataType); testCase.SetPropertyValue(Constants.TestDynamicDataProperty, data); + testCase.SetPropertyValue(Constants.TestDataSourceIgnoreMessageProperty, TestMethod.TestDataSourceIgnoreMessage); } SetTestCaseId(testCase, testFullName); diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs index ec561946dd..8aff354865 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestOutcome.cs @@ -6,6 +6,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; /// /// Outcome of a test. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public enum UnitTestOutcome : int { /// diff --git a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs index 42113cd838..73b33d4468 100644 --- a/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs +++ b/src/Adapter/MSTest.TestAdapter/ObjectModel/UnitTestResult.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -14,6 +11,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; [Serializable] [DebuggerDisplay("{DisplayName} ({Outcome})")] +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class UnitTestResult { /// diff --git a/src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs b/src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs index 8a91c6f5a6..1ad6cc37b1 100644 --- a/src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs +++ b/src/Adapter/MSTest.TestAdapter/PlatformServiceProvider.cs @@ -1,22 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - #if !WINDOWS_UWP using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration; #endif using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// /// The main service provider class that exposes all the platform services available. /// -internal class PlatformServiceProvider : IPlatformServiceProvider +internal sealed class PlatformServiceProvider : IPlatformServiceProvider { + private static readonly Action CancelDelegate = static state => ((TestContextImplementation)state!).Context.CancellationTokenSource.Cancel(); + /// /// Initializes a new instance of the class - a singleton. /// @@ -24,11 +27,11 @@ private PlatformServiceProvider() => #if !WINDOWS_UWP // Set the provider that is used by DynamicDataAttribute when generating data, to allow substituting functionality // in TestFramework without having to put all the stuff in that library. - TestTools.UnitTesting.DynamicDataProvider.Instance = SourceGeneratorToggle.UseSourceGenerator + UTF.DynamicDataProvider.Instance = SourceGeneratorToggle.UseSourceGenerator ? new SourceGeneratedDynamicDataOperations() : new DynamicDataOperations(); #else - TestTools.UnitTesting.DynamicDataProvider.Instance = new DynamicDataOperations(); + UTF.DynamicDataProvider.Instance = new DynamicDataOperations(); #endif /// @@ -138,6 +141,8 @@ public IReflectionOperations2 ReflectionOperations /// public TestRunCancellationToken? TestRunCancellationToken { get; set; } + public bool IsGracefulStopRequested { get; set; } + /// /// Gets or sets the instance for the platform service. /// @@ -220,10 +225,11 @@ public ITestSourceHost CreateTestSourceHost( /// /// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant. /// - public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties) + public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome) { - var testContextImplementation = new TestContextImplementation(testMethod, writer, properties); - TestRunCancellationToken?.Register(testContextImplementation.Context.CancellationTokenSource.Cancel); + var testContextImplementation = new TestContextImplementation(testMethod, writer, properties, messageLogger); + TestRunCancellationToken?.Register(CancelDelegate, testContextImplementation); + testContextImplementation.SetOutcome(outcome); return testContextImplementation; } } diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt index 0d05196024..9de3e651d7 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TimeoutWhenNotSet = 0 -> int const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsName = "MSTest" -> string! const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsNameAlias = "MSTestV2" -> string! @@ -132,6 +132,45 @@ Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestResul Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.CollectSourceInformation.get -> bool Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.RunConfigurationSettings() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.ReflectionMetadataHook +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Assembly.get -> System.Reflection.Assembly! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Assembly.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyAttributes.get -> object![]! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyAttributes.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyName.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyName.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo.Invoker.get -> System.Func! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo.MyConstructorInfo() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo.Parameters.get -> System.Type![]! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.SourceGeneratedReflectionDataProvider() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeAttributes.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeAttributes.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructors.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructors.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructorsInvoker.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructorsInvoker.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.FileName.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.FileName.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.MethodLocations.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.MethodLocations.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.TypeLocation() -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodAttributes.get -> System.Collections.Generic.Dictionary!>! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodAttributes.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodLocations.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodLocations.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethods.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethods.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeProperties.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeProperties.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypePropertiesByName.get -> System.Collections.Generic.Dictionary!>! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypePropertiesByName.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Types.get -> System.Type![]! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Types.init -> void +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypesByName.get -> System.Collections.Generic.Dictionary! +Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypesByName.init -> void Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Cancel() -> void Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.TestRunCancellationToken.Canceled.get -> bool @@ -148,6 +187,7 @@ static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.Pop static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> void static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.RunConfigurationSettings.get -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings.PopulateSettings(Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IDiscoveryContext? context) -> Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.RunConfigurationSettings! +static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.ReflectionMetadataHook.SetMetadata(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider! metadata) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.TestApplicationBuilderExtensions.AddMSTest(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, System.Func!>! getTestAssemblies) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! arguments) -> void virtual Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.Invoke(object?[]? arguments) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult! diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt index d2691b0382..7dc5c58110 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/PublicAPI.Unshipped.txt @@ -1,41 +1 @@ -#nullable enable -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.ReflectionMetadataHook -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Assembly.get -> System.Reflection.Assembly! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Assembly.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyAttributes.get -> object![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyAttributes.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.AssemblyName.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo.Invoker.get -> System.Func! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo.MyConstructorInfo() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.MyConstructorInfo.Parameters.get -> System.Type![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.SourceGeneratedReflectionDataProvider() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeAttributes.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeAttributes.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructors.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructors.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructorsInvoker.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeConstructorsInvoker.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.FileName.get -> string! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.FileName.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.MethodLocations.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.MethodLocations.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeLocation.TypeLocation() -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodAttributes.get -> System.Collections.Generic.Dictionary!>! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodAttributes.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodLocations.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethodLocations.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethods.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeMethods.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeProperties.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypeProperties.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypePropertiesByName.get -> System.Collections.Generic.Dictionary!>! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypePropertiesByName.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Types.get -> System.Type![]! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.Types.init -> void -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypesByName.get -> System.Collections.Generic.Dictionary! -Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider.TypesByName.init -> void -static Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.ReflectionMetadataHook.SetMetadata(Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration.SourceGeneratedReflectionDataProvider! metadata) -> void +#nullable enable diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt index 5e6042d103..9e97765492 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo.TimeoutWhenNotSet = 0 -> int const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsName = "MSTest" -> string! const Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.MSTestSettings.SettingsNameAlias = "MSTestV2" -> string! diff --git a/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt b/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt index ab058de62d..7dc5c58110 100644 --- a/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTest.TestAdapter/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable +#nullable enable diff --git a/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs b/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs index a9c8d5711a..d459e481e5 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs +++ b/src/Adapter/MSTest.TestAdapter/Resources/Resource.Designer.cs @@ -243,11 +243,11 @@ internal static string DuplicateConfigurationError { } /// - /// Looks up a localized string similar to '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>'. + /// Looks up a localized string similar to The dynamic data source '{0}' in type '{1}' should exist and be a property or a method.. /// - internal static string DynamicDataShouldBeValidMessageFormat_MemberType { + internal static string DynamicDataSourceShouldExistAndBeValid { get { - return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_MemberType", resourceCulture); + return ResourceManager.GetString("DynamicDataSourceShouldExistAndBeValid", resourceCulture); } } @@ -279,7 +279,7 @@ internal static string ExceptionsThrown { } /// - /// Looks up a localized string similar to Test '{0}' execution has been aborted.. + /// Looks up a localized string similar to Test '{0}' was canceled. /// internal static string Execution_Test_Cancelled { get { @@ -288,7 +288,7 @@ internal static string Execution_Test_Cancelled { } /// - /// Looks up a localized string similar to Test '{0}' exceeded execution timeout period.. + /// Looks up a localized string similar to Test '{0}' timed out after {1}ms. /// internal static string Execution_Test_Timeout { get { @@ -297,20 +297,38 @@ internal static string Execution_Test_Timeout { } /// - /// Looks up a localized string similar to Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data.. + /// Looks up a localized string similar to Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1}. /// - internal static string FailedFetchAttributeCache { + internal static string FailedToGetCustomAttribute { get { - return ResourceManager.GetString("FailedFetchAttributeCache", resourceCulture); + return ResourceManager.GetString("FailedToGetCustomAttribute", resourceCulture); } } /// - /// Looks up a localized string similar to Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1}. + /// Looks up a localized string similar to The type of the generic parameter '{0}' could not be inferred.. /// - internal static string FailedToGetCustomAttribute { + internal static string GenericParameterCantBeInferred { get { - return ResourceManager.GetString("FailedToGetCustomAttribute", resourceCulture); + return ResourceManager.GetString("GenericParameterCantBeInferred", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred.. + /// + internal static string GenericParameterCantBeInferredBecauseNoArguments { + get { + return ResourceManager.GetString("GenericParameterCantBeInferredBecauseNoArguments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'.. + /// + internal static string GenericParameterConflict { + get { + return ResourceManager.GetString("GenericParameterConflict", resourceCulture); } } @@ -386,15 +404,6 @@ internal static string OlderTFMVersionFound { } } - /// - /// Looks up a localized string similar to An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8.. - /// - internal static string OlderTFMVersionFoundClassCleanup { - get { - return ResourceManager.GetString("OlderTFMVersionFoundClassCleanup", resourceCulture); - } - } - /// /// Looks up a localized string similar to Running tests in any of the provided sources is not supported for the selected platform. /// @@ -603,15 +612,6 @@ internal static string UTA_EndOfInnerExceptionTrace { } } - /// - /// Looks up a localized string similar to UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature. - /// - internal static string UTA_ErrorGenericTestMethod { - get { - return ResourceManager.GetString("UTA_ErrorGenericTestMethod", resourceCulture); - } - } - /// /// Looks up a localized string similar to UTA007: Method {1} defined in class {0} does not have correct signature. Test method marked with the [TestMethod] attribute must be non-static, public, return-type as void and should not take any parameter. Example: public void Test.Class1.Test(). Additionally, if you are using async-await in test method then return-type must be 'Task' or 'ValueTask'. Example: public async Task Test.Class1.Test2(). /// diff --git a/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx b/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx index 6cdf766482..fba272ebdf 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx +++ b/src/Adapter/MSTest.TestAdapter/Resources/Resource.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Test '{0}' exceeded execution timeout period. + Test '{0}' timed out after {1}ms Running tests in any of the provided sources is not supported for the selected platform @@ -175,9 +175,6 @@ Unable to load types from the test source '{0}'. Some or all of the tests in this source may not be discovered. Error: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - File does not exist: {0} @@ -311,7 +308,7 @@ Error: {1} Cannot run test method '{0}.{1}': Method has parameters, but does not define any test source. Use '[DataRow]', '[DynamicData]', or a custom 'ITestDataSource' data source to provide test data. - Test '{0}' execution has been aborted. + Test '{0}' was canceled Exception occurred while enumerating IDataSource attribute on "{0}.{1}": {2} @@ -328,9 +325,6 @@ Error: {1} Exceptions thrown: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} {0}: Attribute full type name. @@ -342,9 +336,6 @@ Error: {1} The called code threw an exception that was caught, but the exception value was null - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} {0}: TypeName with namespace, @@ -411,10 +402,19 @@ but received {4} argument(s), with types '{5}'. Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files. - + + The type of the generic parameter '{0}' could not be inferred. + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + + \ No newline at end of file diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf index 4ffa3138ca..0e1defb645 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.cs.xlf @@ -61,15 +61,30 @@ byl vÅ¡ak pÅ™ijat tento poÄet argumentů: {4} s typy {5}. Byly zjiÅ¡tÄ›ny soubory .runsettings i .testconfig.json. Vyberte prosím jenom jeden z tÄ›chto souborů konfigurace testu. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - Odkazovaný Älen [DynamicData] {0}.{1} by mÄ›l vracet IEnumerable<object[]>, IEnumerable<Tuple> nebo IEnumerable<ValueTuple>. + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + Dynamický zdroj dat '{0}' v typu '{1}' by mÄ›l existovat a být vlastností nebo metodou. - Test '{0}' exceeded execution timeout period. - Test {0} pÅ™ekroÄil Äasový limit spuÅ¡tÄ›ní. - + Test '{0}' timed out after {1}ms + ÄŒasový limit '{0}' testu vyprÅ¡el po {1}ms. + + + + The type of the generic parameter '{0}' could not be inferred. + Typ obecného parametru '{0}' nelze odvodit. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Obecná testovací metoda '{0}' nemá argumenty, takže obecný parametr nelze odvodit. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Byly nalezeny dva konfliktní typy pro obecný parametr '{0}'. Konfliktní typy jsou '{1}' a '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Chyba: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: Obecná metoda nemůže být testovací metodou. {0}.{1} má neplatný podpis. - - File does not exist: {0} Neexistující soubor: {0} @@ -408,9 +418,9 @@ Chyba: {1} - Test '{0}' execution has been aborted. - SpouÅ¡tÄ›ní testu {0} se pÅ™eruÅ¡ilo. - + Test '{0}' was canceled + Testovací '{0}' se zruÅ¡ila. + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Chyba: {1} Vyvolané výjimky: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - NepodaÅ™ilo se získat mezipaměť atributů. Ignoruje se dÄ›diÄnost atributů a spadání do „type defines Attribute model“, abychom získali urÄitá data. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} Získání vlastních atributů pro typ {0} vyvolalo výjimku (bude ignorovat a používat způsob reflexe): {1} @@ -456,11 +461,6 @@ Chyba: {1} Volaný kód vyvolal výjimku, která byla zachycena, ale její hodnota byla null. - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - V sestavení je naÄtena starší verze balíÄku MSTestV2. Metody ÄiÅ¡tÄ›ní testů se nemusí spustit oÄekávaným způsobem. UjistÄ›te se prosím, že vÅ¡echny vaÅ¡e testovací projekty odkazují na balíÄky MSTest novÄ›jší než verze 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} DoÅ¡lo k výjimce pÅ™i rozbalování řádků IDataSource z atributu na „{0}.{1}“: {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf index 43c7aec3be..4f563a2f9d 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.de.xlf @@ -61,15 +61,30 @@ aber empfing {4} Argument(e) mit den Typen „{5}“. Es wurden sowohl die Dateien „.runsettings“ als auch „.testconfig.json“ erkannt. Wählen Sie nur eine dieser Testkonfigurationsdateien aus. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - "[DynamicData]"-Element "{0}.{1}" muss "IEnumerable"<"object[]>", "IEnumerable<Tuple>" oder "IEnumerable<ValueTuple>" zurückgeben. + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + Die dynamische Datenquelle '{0}' im Typ '{1}' muss vorhanden sein und eine Eigenschaft oder Methode sein. - Test '{0}' exceeded execution timeout period. - Der Test "{0}" hat das Ausführungstimeout überschritten. - + Test '{0}' timed out after {1}ms + Timeout bei test '{0}' nach {1}ms. + + + + The type of the generic parameter '{0}' could not be inferred. + Der Typ des generischen Parameters '{0}' konnte nicht abgeleitet werden. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Die generische Testmethode '{0}' hat keine Argumente, daher kann der generische Parameter nicht abgeleitet werden. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Es wurden zwei in Konflikt stehende Typen für den generischen Parameter '{0}' gefunden. Die in Konflikt stehenden Typen sind '{1}' und '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Fehler: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: Eine generische Methode kann keine Testmethode sein. '{0}.{1}' weist eine ungültige Signatur auf. - - File does not exist: {0} Die Datei ist nicht vorhanden: {0} @@ -408,9 +418,9 @@ Fehler: {1} - Test '{0}' execution has been aborted. - Die Ausführung des Tests "{0}" wurde abgebrochen. - + Test '{0}' was canceled + Test '{0}' wurde abgebrochen. + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Fehler: {1} Ausgelöste Ausnahmen: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Fehler beim Abrufen des Attributcaches. Die Attributvererbung wird ignoriert und fällt in "type defines Attribute model", sodass einige Daten vorhanden sind. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} Beim Abrufen von benutzerdefinierten Attributen für den Typ {0} wurde Ausnahme ausgelöst (wird ignoriert und die Reflektionsart verwendet): {1} @@ -456,11 +461,6 @@ Fehler: {1} Der aufgerufene Code hat eine Ausnahme ausgelöst, die abgefangen wurde, aber der Ausnahmewert war NULL. - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Eine ältere Version des MSTestV2-Pakets wird in die Assembly geladen. Testbereinigungsmethoden werden möglicherweise nicht wie erwartet ausgeführt. Stellen Sie sicher, dass alle Testprojekte auf MSTest-Pakete verweisen, die neuer als Version 2.2.8 sind. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} Ausnahme beim Erweitern von IDataSource-Zeilen aus dem Attribut auf "{0}.{1}": {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf index 31d9b13040..a7665e8fe1 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.es.xlf @@ -61,15 +61,30 @@ pero recibió {4} argumento(s), con los tipos "{5}". Se han detectado los archivos ".runsettings" y ".testconfig.json". Seleccione solo uno de estos archivos de configuración de prueba. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - El miembro ''{0}.{1}'' de '[DynamicData]' al que se hace referencia debe devolver ''IEnumerable<object[]>', 'IEnumerable<Tuple>'' o ''IEnumerable<ValueTuple>'' + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + El origen de datos dinámico '{0}' en el tipo '{1}' debe existir y ser una propiedad o un método. - Test '{0}' exceeded execution timeout period. - La prueba '{0}' superó el tiempo de espera de ejecución. - + Test '{0}' timed out after {1}ms + Se agotó el tiempo de espera de la '{0}' de pruebas después de {1}ms + + + + The type of the generic parameter '{0}' could not be inferred. + No se pudo inferir el tipo del parámetro genérico '{0}'. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + El método de prueba genérico '{0}' no tiene argumentos, por lo que no se puede inferir el parámetro genérico. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Se encontraron dos tipos en conflicto para el parámetro genérico '{0}'. Los tipos en conflicto son '{1}' y '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Error: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: Un método genérico no puede ser un método de prueba. {0}.{1} tiene una firma no válida - - File does not exist: {0} El archivo no existe: {0} @@ -408,9 +418,9 @@ Error: {1} - Test '{0}' execution has been aborted. - Se anuló la ejecución de la prueba "{0}". - + Test '{0}' was canceled + Se canceló la '{0}' de pruebas + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Error: {1} Excepciones devueltas: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - No se pudo obtener la memoria caché de atributos. Se omitirá la herencia del atributo y se entrará en "type define Attribute model", de modo que tengamos algunos datos. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} Al obtener atributos personalizados para el tipo {0} se produjo una excepción (se omitirá y se usará la forma de reflexión): {1} @@ -456,11 +461,6 @@ Error: {1} El código llamado produjo una excepción que se detectó, pero el valor de la excepción era null - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Hay una versión anterior del paquete MSTestV2 cargada en el ensamblado. Es posible que los métodos de limpieza de pruebas no se ejecuten según lo esperado. Asegúrese de que todos los proyectos de prueba hacen referencia a paquetes MSTest más recientes que la versión 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} Se produjo una excepción al expandir las filas de IDataSource del atributo en "{0}.{1}": {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf index d6ea39b5be..908df3801d 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.fr.xlf @@ -61,15 +61,30 @@ mais a reçu {4} argument(s), avec les types « {5} ». Les fichiers « .runsettings » et « .testconfig.json » ont été détectés. Veuillez sélectionner un seul de ces fichiers de configuration de test. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - Le membre référencé « [DynamicData] '{0}.{1}' doit renvoyer « IEnumerable<object[]> », « IEnumerable<Tuple> » ou « IEnumerable<ValueTuple> » + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + La source de données dynamique '{0}' dans le type '{1}' doit exister et être une propriété ou une méthode. - Test '{0}' exceeded execution timeout period. - Le test '{0}' a dépassé le délai d'attente de l'exécution. - + Test '{0}' timed out after {1}ms + Délai de '{0}' de test dépassé après {1}ms + + + + The type of the generic parameter '{0}' could not be inferred. + Impossible de déduire le type du paramètre générique '{0}'. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + La méthode de test générique '{0}' n’a pas d’arguments. Le paramètre générique ne peut donc pas être déduit. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Deux types en conflit ont été trouvés pour le paramètre générique '{0}'. Les types en conflit sont '{1}' et '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Erreur : {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015 : une méthode générique ne peut pas être une méthode de test. {0}.{1} a une signature non valide - - File does not exist: {0} Fichier inexistant : {0} @@ -408,9 +418,9 @@ Erreur : {1} - Test '{0}' execution has been aborted. - L'exécution du test '{0}' a été abandonnée. - + Test '{0}' was canceled + Le test '{0}' a été annulé + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Erreur : {1} Exceptions levées/s : This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Échec de l’obtention du cache d’attributs. Héritage d’attribut ignoré et retour à 'type defines Attribute model' pour que nous ayons des données. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} L’obtention d’attributs personnalisés pour le type {0} a levé une exception (ignorera et utilisera la méthode de réflexion) : {1} @@ -456,11 +461,6 @@ Erreur : {1} Le code appelé a levé une exception qui a été interceptée, mais la valeur de l’exception était nul - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Une version antérieure du package MSTestV2 est chargée dans l’assembly. Les méthodes de nettoyage de test risquent de ne pas s’exécuter comme prévu. Vérifiez que tous vos projets de test référencent des packages MSTest plus récent que la version 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} Une exception s’est produite lors du développement des lignes IDataSource à partir de l’attribut sur « {0}.{1} » : {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf index f46eb424a9..4bbe3658f6 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.it.xlf @@ -61,15 +61,30 @@ ma ha ricevuto {4} argomenti, con tipi "{5}". Sono stati rilevati sia i file '.runsettings' sia '.testconfig.json'. Selezionare solo uno di questi file di configurazione di test. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - '[DynamicData]' membro di riferimento '{0}.{1}' deve restituire 'IEnumerable<object[]>', 'IEnumerable<Tuple>' o 'IEnumerable<ValueTuple>' + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + L'origine dati dinamica '{0}' nel tipo '{1}' deve esistere ed essere una proprietà o un metodo. - Test '{0}' exceeded execution timeout period. - È stato superato il periodo di timeout per l'esecuzione del test '{0}'. - + Test '{0}' timed out after {1}ms + Timeout del '{0}' di test dopo {1}ms + + + + The type of the generic parameter '{0}' could not be inferred. + Impossibile dedurre il tipo del parametro generico '{0}'. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Il metodo di test generico '{0}' non contiene argomenti, di conseguenza non è possibile dedurre il parametro generico. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Sono stati trovati due tipi in conflitto per il parametro generico '{0}'. I tipi in conflitto sono '{1}' e '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Errore: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: un metodo generico non può essere un metodo di test. La firma di {0}.{1} non è valida - - File does not exist: {0} Il file {0} non esiste @@ -408,9 +418,9 @@ Errore: {1} - Test '{0}' execution has been aborted. - L'esecuzione del test '{0}' è stata interrotta. - + Test '{0}' was canceled + Il '{0}' di test è stato annullato + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Errore: {1} Eccezioni generate: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Non è stato possibile ottenere la cache degli attributi. L'ereditarietà dell'attributo verrà ignorata e verrà considerato 'il tipo definisce il modello di attributo', in modo che siano disponibili alcuni dati. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} Il recupero degli attributi personalizzati per il tipo {0} ha generato un'eccezione (verrà ignorata e verrà usata la modalità reflection): {1} @@ -456,11 +461,6 @@ Errore: {1} Il codice chiamato ha generato un'eccezione che è stata rilevata, ma il valore dell'eccezione è Null - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Nell'assembly è caricata una versione precedente del pacchetto MSTestV2. I metodi di pulizia dei test potrebbero non essere eseguiti come previsto. Assicurarsi che tutti i progetti di test facciano riferimento a pacchetti MSTest più recenti della versione 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} Si è verificata un'eccezione durante l'espansione delle righe IDataSource dall'attributo in "{0}.{1}": {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf index fcf792a564..f33a668f43 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ja.xlf @@ -62,15 +62,30 @@ but received {4} argument(s), with types '{5}'. '.runsettings' ファイル㨠'.testconfig.json' ファイルã®ä¸¡æ–¹ãŒæ¤œå‡ºã•れã¾ã—ãŸã€‚ã“れらã®ãƒ†ã‚¹ãƒˆæ§‹æˆãƒ•ァイルã®ã„ãšã‚Œã‹ 1 ã¤ã ã‘ã‚’é¸æŠžã—ã¦ãã ã•ã„。 - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - '[DynamicData]' ã®å‚ç…§ã•れるメンãƒãƒ¼ '{0}.{1}' ã¯ã€'IEnumerable<object[]>'ã€'IEnumerable<Tuple>`ã€'IEnumerable<ValueTuple>' ã®ã„ãšã‚Œã‹ã‚’è¿”ã™å¿…è¦ãŒã‚りã¾ã™ + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + åž‹ '{1}' ã®å‹•的データ ソース '{0}' ã¯ã€ãƒ—ロパティã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚ - Test '{0}' exceeded execution timeout period. - テスト '{0}' ã¯å®Ÿè¡Œã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚’è¶…ãˆã¾ã—ãŸã€‚ - + Test '{0}' timed out after {1}ms + テスト '{0}' ㌠{1}ミリ秒後ã«ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã—ã¾ã—㟠+ + + + The type of the generic parameter '{0}' could not be inferred. + ジェãƒãƒªãƒƒã‚¯ パラメーター '{0}' ã®åž‹ã‚’推論ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + '{0}' ジェãƒãƒªãƒƒã‚¯ テスト メソッドã«å¼•æ•°ãŒãªã„ãŸã‚ã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯ パラメーターを推論ã§ãã¾ã›ã‚“。 + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + ジェãƒãƒªãƒƒã‚¯ パラメーター '{0}' ã« 2 ã¤ã®ç«¶åˆã™ã‚‹åž‹ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚ç«¶åˆã™ã‚‹åž‹ã¯ '{1}' ã§ '{2}'。 + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -204,11 +219,6 @@ Error: {1} エラー: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: ジェãƒãƒªãƒƒã‚¯ メソッドãŒãƒ†ã‚¹ãƒˆ メソッドã«ãªã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。{0}.{1} ã®ã‚·ã‚°ãƒãƒãƒ£ã¯ç„¡åйã§ã™ - - File does not exist: {0} ファイルãŒå­˜åœ¨ã—ã¾ã›ã‚“: {0} @@ -409,9 +419,9 @@ Error: {1} - Test '{0}' execution has been aborted. - テスト '{0}' ã®å®Ÿè¡ŒãŒä¸­æ­¢ã•れã¾ã—ãŸã€‚ - + Test '{0}' was canceled + テスト '{0}' ãŒå–り消ã•れã¾ã—㟠+ Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -436,11 +446,6 @@ Error: {1} スローã•れãŸä¾‹å¤–: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - 属性キャッシュをå–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚属性ã®ç¶™æ‰¿ã‚’無視ã™ã‚‹ã¨ã€'type defines Attribute model' ã«åˆ†é¡žã•れるãŸã‚ã€ã„ãã¤ã‹ã®ãƒ‡ãƒ¼ã‚¿ãŒã‚るよã†ã«ãªã‚Šã¾ã™ã€‚ - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} åž‹ {0} ã®ã‚«ã‚¹ã‚¿ãƒ å±žæ€§ã‚’å–得中ã«ä¾‹å¤–ãŒã‚¹ãƒ­ãƒ¼ã•れã¾ã—㟠(無視ã—ã¦ãƒªãƒ•ãƒ¬ã‚¯ã‚·ãƒ§ãƒ³ã®æ–¹æ³•を使用ã—ã¾ã™): {1} @@ -457,11 +462,6 @@ Error: {1} 呼ã³å‡ºã•れãŸã‚³ãƒ¼ãƒ‰ã¯ã‚­ãƒ£ãƒƒãƒã•れãŸä¾‹å¤–をスローã—ã¾ã—ãŸãŒã€ä¾‹å¤–値㌠null ã§ã—㟠- - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - å¤ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã® MSTestV2 パッケージãŒã‚¢ã‚»ãƒ³ãƒ–リã«èª­ã¿è¾¼ã¾ã‚Œã¦ã„ã¾ã™ã€‚テスト クリーンアップ ãƒ¡ã‚½ãƒƒãƒ‰ãŒæ­£ã—ã実行ã•れãªã„å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚ã™ã¹ã¦ã®ãƒ†ã‚¹ãƒˆ プロジェクトãŒã€ãƒãƒ¼ã‚¸ãƒ§ãƒ³ 2.2.8 より新ã—ã„ MSTest パッケージをå‚ç…§ã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。 - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} "{0}.{1}" ã®å±žæ€§ã‹ã‚‰ IDataSource 行を展開中ã«ä¾‹å¤–ãŒç™ºç”Ÿã—ã¾ã—ãŸ: {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf index 47f6282d7a..83f037d230 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ko.xlf @@ -61,15 +61,30 @@ but received {4} argument(s), with types '{5}'. '.runsettings' ë° '.testconfig.json' 파ì¼ì´ ëª¨ë‘ ê²€ìƒ‰ë˜ì—ˆìŠµë‹ˆë‹¤. ì´ëŸ¬í•œ 테스트 구성 íŒŒì¼ ì¤‘ 하나만 ì„ íƒí•˜ì„¸ìš”. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - '[DynamicData]'ì´(ê°€) '{0} 멤버를 참조했습니다.{1}'ì€(는) 'IEnumerable<object[]>', 'IEnumerable<Tuple>' ë˜ëŠ” 'IEnumerable<ValueTuple>'ì„ ë°˜í™˜í•´ì•¼ 합니다. + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + '{1}' 형ì‹ì˜ ë™ì  ë°ì´í„° ì›ë³¸ '{0}' 존재하며 ì†ì„± ë˜ëŠ” 메서드여야 합니다. - Test '{0}' exceeded execution timeout period. - '{0}' 테스트가 실행 시간 ì œí•œì„ ì´ˆê³¼í–ˆìŠµë‹ˆë‹¤. - + Test '{0}' timed out after {1}ms + 테스트 '{0}' {1}밀리초 í›„ì— ì‹œê°„ 초과ë˜ì—ˆìŠµë‹ˆë‹¤. + + + + The type of the generic parameter '{0}' could not be inferred. + 제네릭 매개 변수 '{0}' 형ì‹ì„ 유추할 수 없습니다. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + 제네릭 테스트 메서드 '{0}' ì¸ìˆ˜ê°€ 없으므로 제네릭 매개 변수를 유추할 수 없습니다. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 제네릭 매개 변수 '{0}' ì¶©ëŒí•˜ëŠ” ë‘ ê°€ì§€ 형ì‹ì„ 찾았습니다. ì¶©ëŒí•˜ëŠ” 형ì‹ì€ '{1}' '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} 오류: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: 제네릭 메서드는 테스트 ë©”ì„œë“œì¼ ìˆ˜ 없습니다. {0}.{1}ì— ìž˜ëª»ëœ ì„œëª…ì´ ìžˆìŠµë‹ˆë‹¤. - - File does not exist: {0} 파ì¼ì´ 없습니다. {0} @@ -408,9 +418,9 @@ Error: {1} - Test '{0}' execution has been aborted. - 테스트 '{0}' ì‹¤í–‰ì´ ì¤‘ë‹¨ë˜ì—ˆìŠµë‹ˆë‹¤. - + Test '{0}' was canceled + 테스트 '{0}' 취소ë˜ì—ˆìŠµë‹ˆë‹¤. + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Error: {1} 예외 ë°œìƒ: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - 특성 ìºì‹œë¥¼ 가져오지 못했습니다. ì¼ë¶€ ë°ì´í„°ê°€ 있ë„ë¡ íŠ¹ì„± ìƒì†ì„ 무시하고 'type defines Attribute model'로 바꿉니다. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} {0} 형ì‹ì— 대한 ì‚¬ìš©ìž ì§€ì • íŠ¹ì„±ì„ ê°€ì ¸ì˜¤ëŠ” ë° ì˜ˆì™¸ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤(무시하고 리플렉션 방법 사용). {1} @@ -456,11 +461,6 @@ Error: {1} í˜¸ì¶œëœ ì½”ë“œì—서 확ì¸ëœ 예외가 ë°œìƒí–ˆì§€ë§Œ 예외 ê°’ì´ null입니다. - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - ì´ì „ 버전 MSTestV2 패키지가 ì–´ì…ˆë¸”ë¦¬ì— ë¡œë“œë˜ì–´ 테스트 정리 메서드가 예ìƒëŒ€ë¡œ 실행ë˜ì§€ ì•Šì„ ìˆ˜ 있습니다. 모든 테스트 프로ì íŠ¸ê°€ 2.2.8 ì´í›„ 버전 MSTest 패키지를 참조하는지 확ì¸í•˜ì„¸ìš”. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} "{0}.{1}"ì˜ íŠ¹ì„±ì—서 IDataSource í–‰ì„ í™•ìž¥í•˜ëŠ” ë™ì•ˆ 예외가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf index 8acb03e878..4889dce291 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pl.xlf @@ -61,15 +61,30 @@ ale liczba odebranych argumentów to {4} z typami „{5}â€. Wykryto zarówno pliki „.runsettingsâ€, jak i „.testconfig.jsonâ€. Wybierz tylko jeden z tych plików konfiguracji testu. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - PrzywoÅ‚any element czÅ‚onkowski „{0}.{1}†„[DynamicData]†powinien zwrócić „IEnumerable<object[]>â€, „IEnumerable<Tuple>†lub „IEnumerable<ValueTuple>†+ + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + Dynamiczne źródÅ‚o danych '{0}' w typie '{1}' powinno istnieć i być wÅ‚aÅ›ciwoÅ›ciÄ… lub metodÄ…. - Test '{0}' exceeded execution timeout period. - Test „{0}†przekroczyÅ‚ okres limitu czasu na wykonanie. - + Test '{0}' timed out after {1}ms + UpÅ‚ynÄ…Å‚ limit czasu '{0}' testu po {1}ms + + + + The type of the generic parameter '{0}' could not be inferred. + Nie można wywnioskować typu '{0}' parametru ogólnego. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Ogólna metoda testowa '{0}' nie ma argumentów, wiÄ™c nie można wywnioskować parametru ogólnego. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Znaleziono dwa typy powodujÄ…ce konflikt dla parametru ogólnego '{0}'. Typy powodujÄ…ce konflikty sÄ… '{1}' i '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Błąd: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: Metoda ogólna nie może być metodÄ… testowÄ…. {0}{1} ma nieprawidÅ‚owÄ… sygnaturÄ™ - - File does not exist: {0} Plik nie istnieje: {0} @@ -408,9 +418,9 @@ Błąd: {1} - Test '{0}' execution has been aborted. - Wykonanie testu „{0}†zostaÅ‚o przerwane. - + Test '{0}' was canceled + Anulowano '{0}' testowe + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Błąd: {1} ZgÅ‚oszone wyjÄ…tki: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Nie można pobrać pamiÄ™ci podrÄ™cznej atrybutów. Ignorowanie dziedziczenia atrybutów i wpadniÄ™cie do elementu „typ definiuje model atrybutuâ€, dziÄ™ki czemu mamy pewne dane. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} Pobieranie atrybutów niestandardowych dla typu {0} zgÅ‚osiÅ‚o wyjÄ…tek (zignoruje i użyje sposobu odbicia): {1} @@ -456,11 +461,6 @@ Błąd: {1} WywoÅ‚any kod zgÅ‚osiÅ‚ wyjÄ…tek, który zostaÅ‚ przechwycony, ale wartość wyjÄ…tku miaÅ‚a wartość null - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Starsza wersja pakietu MSTestV2 jest zaÅ‚adowana do zestawu. Metody czyszczenia testów mogÄ… nie dziaÅ‚ać zgodnie z oczekiwaniami. Upewnij siÄ™, że wszystkie projekty testowe odwoÅ‚ujÄ… siÄ™ do pakietów MSTest nowszych niż wersja 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} WystÄ…piÅ‚ wyjÄ…tek podczas rozwijania wierszy IDataSource z atrybutu w przestrzeni nazw „{0}.{1}â€: {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf index 50f9d4c703..25daeea3dc 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.pt-BR.xlf @@ -61,15 +61,30 @@ mas {4} argumentos recebidos, com tipos '{5}'. Ambos os arquivos '.runsettings' e '.testconfig.json' foram detectados. Selecione apenas um desses arquivos de configuração de teste. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - O membro referenciado "{0}.{1}" de "[DynamicData]" deve retornar "IEnumerable<object[]>", "IEnumerable<Tuple>" ou "IEnumerable<ValueTuple>" + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + A fonte de dados dinâmica '{0}' no tipo '{1}' deve existir e ser uma propriedade ou um método. - Test '{0}' exceeded execution timeout period. - Teste '{0}' ultrapassou o período de tempo limite de execução. - + Test '{0}' timed out after {1}ms + Tempo '{0}' tempo limite do teste após {1}ms + + + + The type of the generic parameter '{0}' could not be inferred. + Não foi possível inferir o tipo '{0}' parâmetro genérico. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + O método de teste genérico '{0}' não tem argumentos, portanto, o parâmetro genérico não pode ser inferido. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Foram encontrados dois tipos conflitativos para parâmetro genérico '{0}'. Os tipos conflitativos são '{1}' e '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Erro: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: um método genérico não pode ser um método de teste. {0}.{1} tem assinatura inválida - - File does not exist: {0} O arquivo não existe: {0} @@ -408,9 +418,9 @@ Erro: {1} - Test '{0}' execution has been aborted. - A execução do teste '{0}' foi anulada. - + Test '{0}' was canceled + O '{0}' teste foi cancelado + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Erro: {1} Exceções lançadas: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Falha ao obter cache de atributos. Ignorando a herança de atributos e caindo em 'tipo define modelo de atributo', para que tenhamos alguns dados. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} A obtenção de atributos personalizados para o tipo {0} gerou uma exceção (irá ignorar e usar o modo de reflexão): {1} @@ -456,11 +461,6 @@ Erro: {1} O código chamado lançou uma exceção que foi capturada, mas o valor da exceção era nulo - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Uma versão mais antiga do pacote MSTestV2 é carregada no assembly, os métodos de limpeza de teste podem não ser executados conforme o esperado. Certifique-se de que todos os seus projetos de teste façam referência a pacotes MSTest mais recentes que a versão 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} Ocorreu uma exceção ao expandir as linhas IDataSource do atributo em "{0}.{1}": {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf index 21c0cf85e8..ce2e4f5ef3 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.ru.xlf @@ -61,15 +61,30 @@ but received {4} argument(s), with types '{5}'. Обнаружены файлы ".runsettings" и ".testconfig.json". Выберите только один из Ñтих файлов теÑтовой конфигурации. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - Указанный Ñлемент "[DynamicData]" "{0}.{1}" должен возвращать "IEnumerable<object[]>", "IEnumerable<Tuple>" или "IEnumerable<ValueTuple>" + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + ДинамичеÑкий иÑточник '{0}' в типе '{1}' должен ÑущеÑтвовать и быть ÑвойÑтвом или методом. - Test '{0}' exceeded execution timeout period. - Превышено Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚ÐµÑта "{0}". - + Test '{0}' timed out after {1}ms + Ð’Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ '{0}' иÑтекло через {1}Ð¼Ñ + + + + The type of the generic parameter '{0}' could not be inferred. + Ðе удалоÑÑŒ определить тип универÑального '{0}' параметра. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + УниверÑальный метод '{0}' не имеет аргументов, поÑтому невозможно вывеÑти универÑальный параметр. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Обнаружены два конфликтующих типа Ð´Ð»Ñ ÑƒÐ½Ð¸Ð²ÐµÑ€Ñального параметра '{0}'. Конфликтуют типы '{1}' и '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Ошибка: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: универÑальный метод не может быть методом теÑта. {0}.{1} имеет недопуÑтимую Ñигнатуру - - File does not exist: {0} Файл не ÑущеÑтвует: {0} @@ -408,9 +418,9 @@ Error: {1} - Test '{0}' execution has been aborted. - Выполнение теÑта "{0}" было прервано. - + Test '{0}' was canceled + Проверка '{0}' отменена + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Error: {1} Выданные иÑключениÑ: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Ðе удалоÑÑŒ получить кÑш атрибутов. ÐаÑледование атрибутов игнорируетÑÑ Ð¸ выполнÑетÑÑ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð´ к Ñхеме "тип определÑет модель атрибута", чтобы получить некоторые данные. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} При получении наÑтраиваемых атрибутов Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° {0} возникло иÑключение (оно будет проигнорировано и будет иÑпользовано отражение): {1} @@ -456,11 +461,6 @@ Error: {1} Вызванный код вызвал иÑключение, которое было перехвачено, но значение иÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð¾ равно NULL - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Ð’ Ñборку загружена ÑÑ‚Ð°Ñ€Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð° MSTestV2. Методы очиÑтки теÑтов могут выполнÑтьÑÑ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾. УбедитеÑÑŒ, что вÑе теÑтовые проекты ÑÑылаютÑÑ Ð½Ð° пакеты MSTest Ñ Ð²ÐµÑ€Ñией выше 2.2.8. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} Возникло иÑключение при развертывании Ñтрок IDataSource из атрибута "{0}.{1}": {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf index d7ab817338..255dbab729 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.tr.xlf @@ -61,15 +61,30 @@ ancak, '{5}' türüyle {4} argüman aldı. Hem '.runsettings' hem de '.testconfig.json' dosyaları algılandı. Lütfen bu test yapılandırma dosyalarından yalnızca birini seçin. - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - '[DynamicData]' baÅŸvurulan üyesi '{0}.{1}' ÅŸu deÄŸerleri döndürmelidir: 'IEnumerable<object[]>', 'IEnumerable<Tuple>` veya 'IEnumerable<ValueTuple>' + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + Dinamik veri kaynağı '{0}' türdeki '{1}' bir özellik veya yöntem olmalıdır. - Test '{0}' exceeded execution timeout period. - '{0}' testi yürütme zaman aşımı süresini aÅŸtı. - + Test '{0}' timed out after {1}ms + Test '{0}' ms sonra zaman aşımına {1}oldu + + + + The type of the generic parameter '{0}' could not be inferred. + Genel parametre türü '{0}' çıkarsanamadı. + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + Genel test '{0}' bağımsız deÄŸiÅŸkene sahip olmadığından genel parametre çıkarsanamıyor. + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Genel parametre türü için iki çakışan tür '{0}'. Çakışan türler '{1}' '{2}'. + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} Hata: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: Genel metot bir test metodu olamaz. {0}.{1} geçersiz imzaya sahip - - File does not exist: {0} Dosya yok: {0} @@ -408,9 +418,9 @@ Hata: {1} - Test '{0}' execution has been aborted. - '{0}' testinin yürütülmesi iptal edildi. - + Test '{0}' was canceled + Test '{0}' iptal edildi + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Hata: {1} OluÅŸturulan özel durumlar: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - Öznitelik önbelleÄŸi alınamadı. Öznitelik devralmayı yok saymak ve 'tür, Öznitelik modelini tanımlar' içine düşmek, böylece bazı verilerimiz olur. - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} {0} tipi için özel niteliklerin alınması özel durum oluÅŸturdu (yok sayar ve yansıma yolunu kullanır): {1} @@ -456,11 +461,6 @@ Hata: {1} ÇaÄŸrılan kod, yakalanan bir özel durum yarattı, ancak özel durum deÄŸeri boÅŸtu - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - Montaja MSTestV2 paketinin daha eski bir sürümü yüklendi, test temizleme yöntemleri beklendiÄŸi gibi çalışmayabilir. Lütfen tüm test projelerinizin 2.2.8 sürümünden daha yeni olan MSTest paketlerine baÅŸvurduÄŸundan emin olun. - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} "{0}. {1}" üzerindeki öznitelikten IDataSource satırları geniÅŸletilirken özel durum oluÅŸtu: {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf index 8ee17e2acf..c8617c0588 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hans.xlf @@ -61,15 +61,30 @@ but received {4} argument(s), with types '{5}'. 检测到 ".runsettings" å’Œ ".testconfig.json" 文件。请仅选择其中一个测试é…置文件。 - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - “[DynamicData]â€å¼•用的æˆå‘˜â€œ{0}.{1}â€åº”返回“IEnumerable<object[]>â€ã€â€œIEnumerable<Tuple>â€æˆ–“IEnumerable<ValueTuple>†+ + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + 类型 '{1}' ä¸­çš„åŠ¨æ€æ•°æ®æº '{0}' 应存在,并且应为属性或方法。 - Test '{0}' exceeded execution timeout period. - 测试“{0}â€çš„æ‰§è¡Œè¶…时。 - + Test '{0}' timed out after {1}ms + 测试 '{0}' 在 {1}毫秒åŽè¶…æ—¶ + + + + The type of the generic parameter '{0}' could not be inferred. + 无法推断 '{0}' æ³›åž‹å‚æ•°çš„类型。 + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + 泛型测试方法 '{0}' æ²¡æœ‰å‚æ•°ï¼Œå› æ­¤æ— æ³•æŽ¨æ–­æ³›åž‹å‚æ•°ã€‚ + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + å‘çŽ°æ³›åž‹å‚æ•° '{0}' 的两个冲çªç±»åž‹ã€‚冲çªç±»åž‹ '{1}' å’Œ '{2}'。 + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} 错误: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: 泛型方法ä¸å¯ä¸ºæµ‹è¯•方法。{0}.{1} 具有无效签å - - File does not exist: {0} 文件ä¸å­˜åœ¨: {0} @@ -408,9 +418,9 @@ Error: {1} - Test '{0}' execution has been aborted. - 已中止测试“{0}â€çš„æ‰§è¡Œã€‚ - + Test '{0}' was canceled + 测试 '{0}' 已喿¶ˆ + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Error: {1} 引å‘的异常: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - 无法获å–属性缓存。忽略属性继承并进入“type 定义属性模型â€ï¼Œä»¥ä¾¿æˆ‘们拥有一些数æ®ã€‚ - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} 获å–类型 {0} 自定义属性引å‘异常(将忽略并使用åå°„æ–¹å¼): {1} @@ -456,11 +461,6 @@ Error: {1} 调用的代ç å¼•å‘了æ•获的异常,但异常值为 null - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - 较旧版本的 MSTestV2 包已加载到程åºé›†ä¸­ï¼Œæµ‹è¯•æ¸…ç†æ–¹æ³•å¯èƒ½ä¸ä¼šæŒ‰é¢„期è¿è¡Œã€‚è¯·ç¡®ä¿æ‰€æœ‰æµ‹è¯•项目都引用更高版本 2.2.8 çš„ MSTest 包。 - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} 从“{0}.{1}â€ä¸Šçš„属性扩展 IDataSource 行时出现异常: {2} diff --git a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf index 661dc53fc6..6943b4e708 100644 --- a/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf +++ b/src/Adapter/MSTest.TestAdapter/Resources/xlf/Resource.zh-Hant.xlf @@ -61,15 +61,30 @@ but received {4} argument(s), with types '{5}'. 嵿¸¬åˆ° '.runsettings' å’Œ '.testconfig.json' 檔案。請åªé¸å–其中一個測試設定檔。 - - '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - '[DynamicData]' åƒè€ƒæˆå“¡ '{0}.{1}' 應傳回 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' + + The dynamic data source '{0}' in type '{1}' should exist and be a property or a method. + 類型 '{1}' ä¸­çš„å‹•æ…‹æ•¸æ“šæº '{0}' 應該存在,而且必須是屬性或方法。 - Test '{0}' exceeded execution timeout period. - 測試 '{0}' è¶…éŽåŸ·è¡Œé€¾æ™‚期é™ã€‚ - + Test '{0}' timed out after {1}ms + 測試 '{0}' 在 {1}毫秒後逾時 + + + + The type of the generic parameter '{0}' could not be inferred. + ç„¡æ³•æŽ¨æ–·æ³›åž‹åƒæ•¸ '{0}' 的類型。 + + + + The generic test method '{0}' doesn't have arguments, so the generic parameter cannot be inferred. + 泛型測試方法 '{0}' æ²’æœ‰è‡ªè®Šæ•¸ï¼Œå› æ­¤ç„¡æ³•æŽ¨æ–·æ³›åž‹åƒæ•¸ã€‚ + + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 發ç¾å…©å€‹è¡çªçš„æ³›åž‹åƒæ•¸é¡žåž‹ '{0}'。è¡çªçš„類型 '{1}' 且 '{2}'。 + Invalid value '{0}' for runsettings entry '{1}', setting will be ignored. @@ -203,11 +218,6 @@ Error: {1} 錯誤: {1} - - UTA015: A generic method cannot be a test method. {0}.{1} has invalid signature - UTA015: 泛型方法ä¸å¯ç‚ºæ¸¬è©¦æ–¹æ³•。{0}.{1} 具有無效的簽章 - - File does not exist: {0} 檔案ä¸å­˜åœ¨: {0} @@ -408,9 +418,9 @@ Error: {1} - Test '{0}' execution has been aborted. - 測試 '{0}' 執行已中止。 - + Test '{0}' was canceled + 已喿¶ˆæ¸¬è©¦ '{0}' + Invalid value '{0}' specified for 'ClassCleanupLifecycle'. Supported scopes are {1}. @@ -435,11 +445,6 @@ Error: {1} æ“²å›žçš„ä¾‹å¤–ç‹€æ³æ•¸: This is usually precedes by TestAssembly_AssemblyDiscoveryFailure message, and preceded by list of exceptions thrown in a test discovery session. - - Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data. - 無法å–得屬性快å–。略éŽå±¬æ€§ç¹¼æ‰¿ä¸¦è½åœ¨ 'type defines Attribute model' 中,因此我們有一些資料。 - - Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1} å–得類型 {0} 擲回例外狀æ³çš„自訂屬性 (將會略éŽä¸¦ä½¿ç”¨å映方å¼): {1} @@ -456,11 +461,6 @@ Error: {1} 被呼å«çš„程å¼ç¢¼æ“²å›žæ””截到的例外狀æ³ï¼Œä½†ä¾‹å¤–ç‹€æ³å€¼ç‚º Null - - An older version of MSTestV2 package is loaded in assembly, test cleanup methods might not run as expected. Please make sure all your test projects references MSTest packages newer then version 2.2.8. - 元件中已載入舊版的 MSTestV2 å¥—ä»¶ï¼Œæ¸¬è©¦æ¸…ç†æ–¹æ³•å¯èƒ½ç„¡æ³•如頿œŸæ–¹å¼åŸ·è¡Œã€‚請確定您的所有測試專案都åƒè€ƒäº†æ–°ç‰ˆçš„ MSTest 套件 2.2.8 版。 - - Exception occurred while expanding IDataSource rows from attribute on "{0}.{1}": {2} 從「{0}.{1}ã€ä¸Šçš„屬性展開 IDataSource 資料列時發生例外狀æ³: {2} diff --git a/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs b/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs index ff48f1945e..2f9a8da62f 100644 --- a/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs +++ b/src/Adapter/MSTest.TestAdapter/RunConfigurationSettings.cs @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Xml; +#if !WINDOWS_UWP +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +#endif -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; @@ -13,6 +12,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class RunConfigurationSettings { /// @@ -159,6 +165,7 @@ private static RunConfigurationSettings ToSettings(XmlReader reader) return settings; } +#if !WINDOWS_UWP internal static RunConfigurationSettings SetRunConfigurationSettingsFromConfig(IConfiguration configuration, RunConfigurationSettings settings) { // Expected format of the json is: - @@ -188,4 +195,5 @@ internal static RunConfigurationSettings SetRunConfigurationSettingsFromConfig(I return settings; } +#endif } diff --git a/src/Adapter/MSTest.TestAdapter/SourceGeneratedDynamicDataOperations.cs b/src/Adapter/MSTest.TestAdapter/SourceGeneratedDynamicDataOperations.cs index 96f1d4470e..9ea21161d0 100644 --- a/src/Adapter/MSTest.TestAdapter/SourceGeneratedDynamicDataOperations.cs +++ b/src/Adapter/MSTest.TestAdapter/SourceGeneratedDynamicDataOperations.cs @@ -3,6 +3,4 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; -internal class SourceGeneratedDynamicDataOperations : DynamicDataOperations -{ -} +internal sealed class SourceGeneratedDynamicDataOperations : DynamicDataOperations; diff --git a/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedFileOperations.cs b/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedFileOperations.cs index 537cd989f4..c8b8ac98f3 100644 --- a/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedFileOperations.cs +++ b/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedFileOperations.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; @@ -14,11 +12,10 @@ internal sealed class SourceGeneratedFileOperations : IFileOperations // Not great, but the inner class does some complicated stuff on checking if files exist, better would be to extract the functionality to a class that provides it to both these implementations. private readonly FileOperations _fileOperationsInner = new(skipSourceGeneratorCheck: true); -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public SourceGeneratedReflectionDataProvider ReflectionDataProvider { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + // null is allowed here because the ReflectionDataProvider is set by the source generator. + public SourceGeneratedReflectionDataProvider ReflectionDataProvider { get; set; } = null!; - public object? CreateNavigationSession(string source) => + public object CreateNavigationSession(string source) => // locations are in static metadata, nothing to do here. // But don't return null so the consumer thinks we are doing something. new(); @@ -28,18 +25,18 @@ public void DisposeNavigationSession(object? navigationSession) // locations are in static metadata, nothing to do here. } - public bool DoesFileExist(string assemblyFileName) => ((IFileOperations)_fileOperationsInner).DoesFileExist(assemblyFileName); + public bool DoesFileExist(string assemblyFileName) => _fileOperationsInner.DoesFileExist(assemblyFileName); - public string GetFullFilePath(string assemblyFileName) => ((IFileOperations)_fileOperationsInner).GetFullFilePath(assemblyFileName); + public string GetFullFilePath(string assemblyFileName) => _fileOperationsInner.GetFullFilePath(assemblyFileName); public void GetNavigationData(object navigationSession, string className, string methodName, out int minLineNumber, out string? fileName) - => ReflectionDataProvider!.GetNavigationData(className, methodName, out minLineNumber, out fileName); + => ReflectionDataProvider.GetNavigationData(className, methodName, out minLineNumber, out fileName); - public string? GetAssemblyPath(Assembly assembly) + public string GetAssemblyPath(Assembly assembly) => throw new NotSupportedException("Only tests within the same assembly are allowed in source gen mode"); public Assembly LoadAssembly(string assemblyName, bool isReflectionOnly) => isReflectionOnly ? throw new InvalidOperationException("Reflection only mode is not allowed") - : ReflectionDataProvider!.GetAssembly(assemblyName); + : ReflectionDataProvider.GetAssembly(assemblyName); } #endif diff --git a/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionDataProvider.cs b/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionDataProvider.cs index 925eeba044..b29bb954be 100644 --- a/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionDataProvider.cs +++ b/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionDataProvider.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Reflection; - namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.SourceGeneration; /// diff --git a/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionOperations.cs b/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionOperations.cs index c69b9bbdc4..d66a4db69a 100644 --- a/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionOperations.cs +++ b/src/Adapter/MSTest.TestAdapter/SourceGeneration/SourceGeneratedReflectionOperations.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Diagnostics.CodeAnalysis; -using System.Reflection; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; @@ -50,35 +48,46 @@ public object[] GetCustomAttributes(Assembly assembly, Type /* the attribute typ return attributes.ToArray(); } - public IEnumerable GetDeclaredConstructors(Type classType) + public ConstructorInfo[] GetDeclaredConstructors(Type classType) => ReflectionDataProvider.TypeConstructors[classType]; public MethodInfo? GetDeclaredMethod(Type dynamicDataDeclaringType, string dynamicDataSourceName) => GetDeclaredMethods(dynamicDataDeclaringType).FirstOrDefault(m => m.Name == dynamicDataSourceName); - public IEnumerable GetDeclaredMethods(Type classType) + public MethodInfo[] GetDeclaredMethods(Type classType) => ReflectionDataProvider.TypeMethods[classType]; - public IEnumerable GetDeclaredProperties(Type type) + public PropertyInfo[] GetDeclaredProperties(Type type) => ReflectionDataProvider.TypeProperties[type]; public PropertyInfo? GetDeclaredProperty(Type type, string propertyName) - => GetRuntimeProperty(type, propertyName); + => GetRuntimeProperty(type, propertyName, includeNonPublic: true); public Type[] GetDefinedTypes(Assembly assembly) => ReflectionDataProvider.Types; - public IEnumerable GetRuntimeMethods(Type type) + public MethodInfo[] GetRuntimeMethods(Type type) => ReflectionDataProvider.TypeMethods[type]; - public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters) => throw new NotImplementedException(); + public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters, bool includeNonPublic) + { + IEnumerable runtimeMethods = GetRuntimeMethods(declaringType) + .Where( + m => m.Name == methodName && + m.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameters) && + (includeNonPublic || m.IsPublic)); + return runtimeMethods.SingleOrDefault(); + } - public PropertyInfo? GetRuntimeProperty(Type classType, string propertyName) + public PropertyInfo? GetRuntimeProperty(Type classType, string propertyName, bool includeNonPublic) { Dictionary type = ReflectionDataProvider.TypePropertiesByName[classType]; // We as asking for TestContext here, it may not be there. - return type.TryGetValue(propertyName, out PropertyInfo? propertyInfo) ? propertyInfo : null; + PropertyInfo? property = type.TryGetValue(propertyName, out PropertyInfo? propertyInfo) ? propertyInfo : null; + return !includeNonPublic && (property?.GetMethod?.IsPublic == true || property?.SetMethod?.IsPublic == true) + ? null + : property; } public Type? GetType(string typeName) diff --git a/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs index 1076988370..a713f7acc8 100644 --- a/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs +++ b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -10,7 +8,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; -internal class TestMethodFilter +internal sealed class TestMethodFilter { /// /// Supported properties for filtering. diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedConfiguration.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedConfiguration.cs new file mode 100644 index 0000000000..2deb1e6aa3 --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/BridgedConfiguration.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !WINDOWS_UWP +using Microsoft.Testing.Platform.Configurations; + +using PlatformServicesConfiguration = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.IConfiguration; + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] +internal sealed class BridgedConfiguration : PlatformServicesConfiguration +{ + private readonly IConfiguration _configuration; + + public BridgedConfiguration(IConfiguration configuration) + => _configuration = configuration; + + public string? this[string key] => _configuration[key]; +} +#endif diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs index af5c0c0e4b..3ebc32ee6d 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBannerCapability.cs @@ -2,15 +2,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Runtime.InteropServices; -using System.Text; - using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.Services; namespace Microsoft.VisualStudio.TestTools.UnitTesting; #pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] internal sealed class MSTestBannerCapability : IBannerMessageOwnerCapability { private readonly IPlatformInformation _platformInformation; @@ -31,7 +30,7 @@ internal sealed class MSTestBannerCapability : IBannerMessageOwnerCapability } #if NETCOREAPP - if (System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeCompiled) + if (RuntimeFeature.IsDynamicCodeCompiled) #endif { bannerMessage.Append(" ["); diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs index 5160e34c44..b8c584587b 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestBridgedTestFramework.cs @@ -2,29 +2,24 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Diagnostics; -using System.Reflection; - using Microsoft.Testing.Extensions.VSTestBridge; using Microsoft.Testing.Extensions.VSTestBridge.Requests; using Microsoft.Testing.Platform.Capabilities.TestFramework; -using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Messages; using Microsoft.Testing.Platform.Services; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; namespace Microsoft.VisualStudio.TestTools.UnitTesting; +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] internal sealed class MSTestBridgedTestFramework : SynchronizedSingleSessionVSTestBridgedTestFramework { - private readonly IConfiguration? _configration; + private readonly BridgedConfiguration? _configuration; public MSTestBridgedTestFramework(MSTestExtension mstestExtension, Func> getTestAssemblies, IServiceProvider serviceProvider, ITestFrameworkCapabilities capabilities) : base(mstestExtension, getTestAssemblies, serviceProvider, capabilities) - { - _configration = serviceProvider.GetConfiguration(); - } + => _configuration = new(serviceProvider.GetConfiguration()); /// protected override Task SynchronizedDiscoverTestsAsync(VSTestDiscoverTestExecutionRequest request, IMessageBus messageBus, @@ -36,7 +31,7 @@ protected override Task SynchronizedDiscoverTestsAsync(VSTestDiscoverTestExecuti Debugger.Launch(); } - MSTestDiscoverer.DiscoverTests(request.AssemblyPaths, request.DiscoveryContext, request.MessageLogger, request.DiscoverySink, _configration); + MSTestDiscoverer.DiscoverTests(request.AssemblyPaths, request.DiscoveryContext, request.MessageLogger, request.DiscoverySink, _configuration); return Task.CompletedTask; } @@ -54,11 +49,11 @@ protected override Task SynchronizedRunTestsAsync(VSTestRunTestExecutionRequest if (request.VSTestFilter.TestCases is { } testCases) { - testExecutor.RunTests(testCases, request.RunContext, request.FrameworkHandle, _configration); + testExecutor.RunTests(testCases, request.RunContext, request.FrameworkHandle, _configuration); } else { - testExecutor.RunTests(request.AssemblyPaths, request.RunContext, request.FrameworkHandle, _configration); + testExecutor.RunTests(request.AssemblyPaths, request.RunContext, request.FrameworkHandle, _configuration); } return Task.CompletedTask; diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestExtension.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestExtension.cs index d39139e914..b331865f1a 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestExtension.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestExtension.cs @@ -2,12 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Reflection; - using Microsoft.Testing.Platform.Extensions; namespace Microsoft.VisualStudio.TestTools.UnitTesting; +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] internal sealed class MSTestExtension : IExtension { public string Uid { get; } = GetExtensionUid(); diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs new file mode 100644 index 0000000000..ea80488cb6 --- /dev/null +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestGracefulStopTestExecutionCapability.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !WINDOWS_UWP +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +#pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] +internal sealed class MSTestGracefulStopTestExecutionCapability : IGracefulStopTestExecutionCapability +#pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +{ + private MSTestGracefulStopTestExecutionCapability() + { + } + + public static MSTestGracefulStopTestExecutionCapability Instance { get; } = new(); + + public Task StopTestExecutionAsync(CancellationToken cancellationToken) + { + PlatformServiceProvider.Instance.IsGracefulStopRequested = true; + return Task.CompletedTask; + } +} +#endif diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs index 2a86adaf09..f16999eb17 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestApplicationBuilderExtensions.cs @@ -2,16 +2,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Reflection; - using Microsoft.Testing.Extensions.VSTestBridge.Capabilities; using Microsoft.Testing.Extensions.VSTestBridge.Helpers; using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Services; namespace Microsoft.VisualStudio.TestTools.UnitTesting; +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] public static class TestApplicationBuilderExtensions { public static void AddMSTest(this ITestApplicationBuilder testApplicationBuilder, Func> getTestAssemblies) @@ -20,12 +20,16 @@ public static void AddMSTest(this ITestApplicationBuilder testApplicationBuilder testApplicationBuilder.AddRunSettingsService(extension); testApplicationBuilder.AddTestCaseFilterService(extension); testApplicationBuilder.AddTestRunParametersService(extension); +#pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + testApplicationBuilder.AddMaximumFailedTestsService(extension); +#pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. testApplicationBuilder.AddRunSettingsEnvironmentVariableProvider(extension); testApplicationBuilder.RegisterTestFramework( serviceProvider => new TestFrameworkCapabilities( new VSTestBridgeExtensionBaseCapabilities(), #pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - new MSTestBannerCapability(serviceProvider.GetRequiredService())), + new MSTestBannerCapability(serviceProvider.GetRequiredService()), + MSTestGracefulStopTestExecutionCapability.Instance), #pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. (capabilities, serviceProvider) => new MSTestBridgedTestFramework(extension, getTestAssemblies, serviceProvider, capabilities)); } diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs index befda48370..380adc63c3 100644 --- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs +++ b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/TestingPlatformBuilderHook.cs @@ -2,15 +2,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Reflection; - using Microsoft.Testing.Platform.Builder; namespace Microsoft.VisualStudio.TestTools.UnitTesting; +[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "We can use MTP from this folder")] public static class TestingPlatformBuilderHook { #pragma warning disable IDE0060 // Remove unused parameter - public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] arguments) => testApplicationBuilder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); + public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] arguments) + => testApplicationBuilder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); } #endif diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs index 47a01bf4f6..04eefda012 100644 --- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs +++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Configurations; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -16,6 +16,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; [FileExtension(".appx")] [FileExtension(".dll")] [FileExtension(".exe")] +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class MSTestDiscoverer : ITestDiscoverer { /// @@ -26,8 +33,8 @@ public class MSTestDiscoverer : ITestDiscoverer /// Logger used to log messages. /// Used to send testcases and discovery related events back to Discoverer manager. [System.Security.SecurityCritical] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Discovery context can be null.")] - public void DiscoverTests(IEnumerable sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink) => MSTestDiscoverer.DiscoverTests(sources, discoveryContext, logger, discoverySink, null); + [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Discovery context can be null.")] + public void DiscoverTests(IEnumerable sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink) => DiscoverTests(sources, discoveryContext, logger, discoverySink, null); internal static void DiscoverTests(IEnumerable sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink, IConfiguration? configuration) { diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs index fcad29af3a..236659f902 100644 --- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs +++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -15,6 +13,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; /// Contains the execution logic for this adapter. /// [ExtensionUri(Constants.ExecutorUriString)] +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class MSTestExecutor : ITestExecutor { private readonly CancellationToken _cancellationToken; @@ -44,9 +49,11 @@ internal MSTestExecutor(CancellationToken cancellationToken) /// public TestExecutionManager TestExecutionManager { get; protected set; } - public void RunTests(IEnumerable? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle) => RunTests(tests, runContext, frameworkHandle, null); + public void RunTests(IEnumerable? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle) + => RunTests(tests, runContext, frameworkHandle, null); - public void RunTests(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle) => RunTests(sources, runContext, frameworkHandle, null); + public void RunTests(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle) + => RunTests(sources, runContext, frameworkHandle, null); internal void RunTests(IEnumerable? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle, IConfiguration? configuration) { diff --git a/src/Adapter/MSTest.TestAdapter/build/net/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/build/common/MSTest.TestAdapter.targets similarity index 100% rename from src/Adapter/MSTest.TestAdapter/build/net/MSTest.TestAdapter.targets rename to src/Adapter/MSTest.TestAdapter/build/common/MSTest.TestAdapter.targets diff --git a/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets deleted file mode 100644 index 64fb79bf30..0000000000 --- a/src/Adapter/MSTest.TestAdapter/build/netfx-netcore-netstandard/MSTest.TestAdapter.targets +++ /dev/null @@ -1,88 +0,0 @@ - - - - - $(EnableMSTestRunner) - $(EnableMSTestRunner) - false - true - - - - - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - PreserveNewest - False - - - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - PreserveNewest - False - - - Microsoft.TestPlatform.AdapterUtilities.dll - PreserveNewest - False - - - - - - - - - - - - - - - $(MSBuildThisFileDirectory)Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - - - - - - - - - - - - - - - - - - - - - - - %(CurrentUICultureHierarchy.Identity) - - - - - - - %(MSTestV2Files.UICulture)\%(FileName).dll - PreserveNewest - %(FullPath) - False - - - - - diff --git a/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.props b/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.props index 365706e987..259e47a969 100644 --- a/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.props +++ b/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.props @@ -1,30 +1,7 @@ - - - Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll - PreserveNewest - False - - - Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll - PreserveNewest - False - - - Microsoft.TestPlatform.AdapterUtilities.dll - PreserveNewest - False - - - - - - - + + true + diff --git a/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets b/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets index c33b561416..e8d37601f3 100644 --- a/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets +++ b/src/Adapter/MSTest.TestAdapter/build/uwp/MSTest.TestAdapter.targets @@ -1,13 +1,31 @@ - - true - - $(EnableMSTestRunner) - $(EnableMSTestRunner) - false - true - + + + Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.dll + PreserveNewest + False + + + Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.dll + PreserveNewest + False + + + Microsoft.TestPlatform.AdapterUtilities.dll + PreserveNewest + False + + + + + + + @@ -31,10 +31,14 @@ - + + Analyzer + false + + diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Properties/AssemblyInfo.cs b/src/Adapter/MSTestAdapter.PlatformServices/Properties/AssemblyInfo.cs index e1f84be7e3..d7f4830787 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Properties/AssemblyInfo.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Properties/AssemblyInfo.cs @@ -1,15 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if NETFRAMEWORK || WIN_UI -using System.Runtime.CompilerServices; -#endif -using System.Runtime.InteropServices; - -#if WIN_UI -using System.Runtime.Versioning; -#endif - [assembly: ComVisible(false)] #if WIN_UI diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt index 0c08630cca..5acee6d2bc 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AdapterTraceLogger Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AdapterTraceLogger.AdapterTraceLogger() -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AdapterTraceLogger.LogError(string! format, params object?[]! args) -> void @@ -40,7 +40,10 @@ Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITe Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.GetResultFiles() -> System.Collections.Generic.IList? Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetDataConnection(object? dbConnection) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetDataRow(object? dataRow) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetDisplayName(string? displayName) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetException(System.Exception? exception) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetOutcome(Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome outcome) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetTestData(object?[]? data) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.TryGetPropertyValue(string! propertyName, out object? propertyValue) -> bool Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestDataSource Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestDataSource.GetData(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod! testMethodInfo, Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext! testContext) -> System.Collections.Generic.IEnumerable? @@ -93,7 +96,10 @@ Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextIm Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.GetResultFiles() -> System.Collections.Generic.IList? Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetDataConnection(object? dbConnection) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetDataRow(object? dataRow) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetDisplayName(string? displayName) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetException(System.Exception? exception) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetOutcome(Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome outcome) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetTestData(object?[]? data) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestContextImplementation(Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod! testMethod, System.IO.StringWriter! stringWriter, System.Collections.Generic.IDictionary! properties) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TryGetPropertyValue(string! propertyName, out object? propertyValue) -> bool Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestDataSource diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt index 04a7900759..e2d86d5ce1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/PublicAPI.Unshipped.txt @@ -1,7 +1,5 @@ -#nullable enable -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetDisplayName(string? displayName) -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetException(System.Exception? exception) -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.SetTestData(object?[]? data) -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetDisplayName(string? displayName) -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetException(System.Exception? exception) -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.SetTestData(object?[]? data) -> void +#nullable enable +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestContext.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel messageLevel, string! message) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestContextImplementation(Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod? testMethod, System.IO.StringWriter! stringWriter, System.Collections.Generic.IDictionary! properties) -> void +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel messageLevel, string! message) -> void +*REMOVED*Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestContextImplementation(Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod! testMethod, System.IO.StringWriter! stringWriter, System.Collections.Generic.IDictionary! properties) -> void diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net462/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net462/PublicAPI.Shipped.txt index 7bb31399b1..163eb68518 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net462/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net462/PublicAPI.Shipped.txt @@ -82,4 +82,4 @@ virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Assem virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AssemblyResolver.ReflectionOnlyLoadAssemblyFrom(string! path) -> System.Reflection.Assembly! virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AssemblyResolver.SearchAssembly(System.Collections.Generic.List! searchDirectorypaths, string! name, bool isReflectionOnly) -> System.Reflection.Assembly? virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool -virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! \ No newline at end of file +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt index 0e08282b9a..2dd69c993e 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt @@ -33,4 +33,4 @@ System.SerializableAttribute (forwarded, contained in System.Runtime) System.SerializableAttribute.SerializableAttribute() -> void (forwarded, contained in System.Runtime) virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! -virtual System.MarshalByRefObject.InitializeLifetimeService() -> object! (forwarded, contained in System.Runtime) \ No newline at end of file +virtual System.MarshalByRefObject.InitializeLifetimeService() -> object! (forwarded, contained in System.Runtime) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0/PublicAPI.Shipped.txt index b5ff64abc0..f4783138ee 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net6.0/PublicAPI.Shipped.txt @@ -30,4 +30,4 @@ static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTest static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Reset() -> void static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Settings.get -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool -virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! \ No newline at end of file +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net7.0/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net7.0/PublicAPI.Shipped.txt index b5ff64abc0..f4783138ee 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net7.0/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net7.0/PublicAPI.Shipped.txt @@ -30,4 +30,4 @@ static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTest static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Reset() -> void static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Settings.get -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool -virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! \ No newline at end of file +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net8.0/PublicAPI.Shipped.txt index b5ff64abc0..f4783138ee 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -30,4 +30,4 @@ static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTest static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Reset() -> void static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Settings.get -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool -virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! \ No newline at end of file +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net9.0/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net9.0/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..f4783138ee --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net9.0/PublicAPI.Shipped.txt @@ -0,0 +1,33 @@ +#nullable enable +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories.InDirectory.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories.InMachineNameDirectory.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories.OutDirectory.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories.RootDeploymentDirectory.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories.RootDeploymentDirectory.set -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment.TestRunDirectories.TestRunDirectories(string! rootDirectory) -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DeleteDeploymentDirectoryAfterTestRunIsComplete.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DeploymentEnabled.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DeployTestSourceDependencies.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.GetDirectoryListWithRecursiveProperty(string! baseDirectory) -> System.Collections.Generic.List! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.MSTestAdapterSettings() -> void +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.SearchDirectories.get -> System.Collections.Generic.List! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.RecursiveDirectoryPath +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.RecursiveDirectoryPath.DirectoryPath.get -> string! +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.RecursiveDirectoryPath.IncludeSubDirectories.get -> bool +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.RecursiveDirectoryPath.RecursiveDirectoryPath(string! dirPath, bool includeSubDirectories) -> void +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.RecursiveDirectoryPath.InitializeLifetimeService() -> object! +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.DeploymentDirectory.get -> string? +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.FullyQualifiedTestClassName.get -> string! +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.ResultsDirectory.get -> string? +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestName.get -> string! +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestResultsDirectory.get -> string? +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestRunDirectory.get -> string? +override Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestContextImplementation.TestRunResultsDirectory.get -> string? +static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.IsAppDomainCreationDisabled(string? settingsXml) -> bool +static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ToSettings(System.Xml.XmlReader! reader) -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! +static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Reset() -> void +static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Settings.get -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net9.0/PublicAPI.Unshipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net9.0/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/net9.0/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt index b5ff64abc0..f4783138ee 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt @@ -30,4 +30,4 @@ static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTest static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Reset() -> void static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Settings.get -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool -virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! \ No newline at end of file +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt index b5ff64abc0..f4783138ee 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -30,4 +30,4 @@ static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTest static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Reset() -> void static Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestSettingsProvider.Settings.get -> Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings! virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.DoesDirectoryExist(string! path) -> bool -virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! \ No newline at end of file +virtual Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.MSTestAdapterSettings.ExpandEnvironmentVariables(string! path) -> string! diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt index 0f2adb28da..24625c1ccf 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ #nullable enable Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TraceListenerManager.Close(Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITraceListener! traceListener) -> void Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TraceListenerWrapper.Close() -> void -Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TraceListenerWrapper.Dispose() -> void \ No newline at end of file +Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TraceListenerWrapper.Dispose() -> void diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt +++ b/src/Adapter/MSTestAdapter.PlatformServices/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs b/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs index 3162c23030..e2dbebb62f 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/RecursiveDirectoryPath.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Diagnostics.CodeAnalysis; using System.Security; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -20,6 +19,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// For each directory we need to have two info 1) path 2) includeSubDirectories. /// [Serializable] +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed. Suppression is ok here.")] public class RecursiveDirectoryPath : MarshalByRefObject { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs index dd44632e3a..715db4e02b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/DiaSessionOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; internal static class DiaSessionOperations diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs index c38800d1c0..9ba8f8c5d1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ExecutionContextService.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; internal static class ExecutionContextService diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs index 82064d7c5b..e28a2f5009 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/FileOperations.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Reflection; - #if WIN_UI using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AppContainer; #endif @@ -18,6 +15,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// The file operations. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class FileOperations : IFileOperations { private readonly ConcurrentDictionary _assemblyCache = new(); diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs index 9ceb4b58d9..f8af181afd 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs @@ -2,16 +2,21 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WINDOWS_UWP -using System.Globalization; -using System.Xml; -using Microsoft.Testing.Platform.Configurations; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class MSTestAdapterSettings { /// @@ -253,7 +258,7 @@ public List GetDirectoryListWithRecursiveProperty(string } else { - warningMessage = $"The Directory: {path}, has following problem: {"This is not an absolute path. A base directory should be provided for this to be used as a relative path."}"; + warningMessage = $"The Directory: {path}, has following problem: This is not an absolute path. A base directory should be provided for this to be used as a relative path."; if (EqtTrace.IsWarningEnabled) { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MessageLevel.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/MessageLevel.cs new file mode 100644 index 0000000000..455c39aaf8 --- /dev/null +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/MessageLevel.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + +internal static class MessageLevelExtensions +{ + public static TestMessageLevel ToTestMessageLevel(this MessageLevel messageLevel) + => messageLevel switch + { + MessageLevel.Informational => TestMessageLevel.Informational, + MessageLevel.Warning => TestMessageLevel.Warning, + MessageLevel.Error => TestMessageLevel.Error, + _ => throw new ArgumentOutOfRangeException(nameof(messageLevel)), + }; +} diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs index 4dfbe0b35f..f72e120267 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; #if NETFRAMEWORK using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; @@ -14,6 +11,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// This service is responsible for platform specific reflection operations. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class ReflectionOperations : IReflectionOperations { /// @@ -23,11 +27,20 @@ public class ReflectionOperations : IReflectionOperations /// True to inspect the ancestors of element; otherwise, false. /// The list of attributes on the member. Empty list if none found. [return: NotNullIfNotNull(nameof(memberInfo))] - public object[]? GetCustomAttributes(MemberInfo memberInfo, bool inherit) => + public object[]? GetCustomAttributes(MemberInfo memberInfo, bool inherit) #if NETFRAMEWORK - ReflectionUtility.GetCustomAttributes(memberInfo, inherit).ToArray(); + => ReflectionUtility.GetCustomAttributes(memberInfo, inherit).ToArray(); #else - memberInfo.GetCustomAttributes(inherit); + { + object[] attributes = memberInfo.GetCustomAttributes(typeof(Attribute), inherit); + + // Ensures that when the return of this method is used here: + // https://github.com/microsoft/testfx/blob/e101a9d48773cc935c7b536d25d378d9a3211fee/src/Adapter/MSTest.TestAdapter/Helpers/ReflectHelper.cs#L461 + // then we are already Attribute[] to avoid LINQ Cast and extra array allocation. + // This assert is solely for performance. Nothing "functional" will go wrong if the assert failed. + Debug.Assert(attributes is Attribute[], $"Expected Attribute[], found '{attributes.GetType()}'."); + return attributes; + } #endif /// @@ -55,6 +68,6 @@ public object[] GetCustomAttributes(Assembly assembly, Type type) => #if NETFRAMEWORK ReflectionUtility.GetCustomAttributes(assembly, type).ToArray(); #else - assembly.GetCustomAttributes(type).ToArray(); + assembly.GetCustomAttributes(type, inherit: true); #endif } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations2.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations2.cs index 8f74a3e352..98d475b99b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations2.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ReflectionOperations2.cs @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; -internal class ReflectionOperations2 : ReflectionOperations, IReflectionOperations2 +internal sealed class ReflectionOperations2 : ReflectionOperations, IReflectionOperations2 { + private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + private const BindingFlags Everything = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + public ReflectionOperations2() { #if NET8_0_OR_GREATER - if (!System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported) + if (!RuntimeFeature.IsDynamicCodeSupported) { throw new NotSupportedException("ReflectionOperations2 are not allowed when dynamic code is not supported, use NativeReflectionOperations instead"); } @@ -23,32 +24,36 @@ public ReflectionOperations2() #pragma warning disable IL2026 // Members attributed with RequiresUnreferencedCode may break when trimming #pragma warning disable IL2067 // 'target parameter' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to 'target method'. #pragma warning disable IL2057 // Unrecognized value passed to the typeName parameter of 'System.Type.GetType(String)' - public IEnumerable GetDeclaredConstructors(Type classType) - => classType.GetTypeInfo().DeclaredConstructors; + public ConstructorInfo[] GetDeclaredConstructors(Type classType) + => classType.GetConstructors(DeclaredOnlyLookup); public MethodInfo? GetDeclaredMethod(Type type, string methodName) - => type.GetTypeInfo().GetDeclaredMethod(methodName); + => type.GetMethod(methodName, DeclaredOnlyLookup); - public IEnumerable GetDeclaredMethods(Type classType) - => classType.GetTypeInfo().DeclaredMethods; + public MethodInfo[] GetDeclaredMethods(Type classType) + => classType.GetMethods(DeclaredOnlyLookup); - public IEnumerable GetDeclaredProperties(Type type) - => type.GetTypeInfo().DeclaredProperties; + public PropertyInfo[] GetDeclaredProperties(Type type) + => type.GetProperties(DeclaredOnlyLookup); public PropertyInfo? GetDeclaredProperty(Type type, string propertyName) - => type.GetTypeInfo().GetDeclaredProperty(propertyName); + => type.GetProperty(propertyName, DeclaredOnlyLookup); public Type[] GetDefinedTypes(Assembly assembly) - => assembly.DefinedTypes.ToArray(); + => assembly.GetTypes(); - public IEnumerable GetRuntimeMethods(Type type) - => type.GetRuntimeMethods(); + public MethodInfo[] GetRuntimeMethods(Type type) + => type.GetMethods(Everything); - public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters) - => declaringType.GetRuntimeMethod(methodName, parameters); + public MethodInfo? GetRuntimeMethod(Type declaringType, string methodName, Type[] parameters, bool includeNonPublic) + => includeNonPublic + ? declaringType.GetMethod(methodName, Everything, null, parameters, null) + : declaringType.GetMethod(methodName, parameters); - public PropertyInfo? GetRuntimeProperty(Type classType, string testContextPropertyName) - => classType.GetProperty(testContextPropertyName); + public PropertyInfo? GetRuntimeProperty(Type classType, string testContextPropertyName, bool includeNonPublic) + => includeNonPublic + ? classType.GetProperty(testContextPropertyName, Everything) + : classType.GetProperty(testContextPropertyName); public Type? GetType(string typeName) => Type.GetType(typeName); diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs index 2846da17e6..709ca664ec 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/SettingsProvider.cs @@ -1,12 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !WINDOWS_UWP -using System.Diagnostics.CodeAnalysis; -#endif -using System.Xml; - -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -14,6 +8,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// Class to read settings from the runsettings xml for the desktop. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class MSTestSettingsProvider : ISettingsProvider { #if !WINDOWS_UWP diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs index b67fa921c0..4dc75ae796 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs @@ -1,14 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; #if NETFRAMEWORK using System.Data; using System.Data.Common; #endif -using System.Globalization; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using ITestMethod = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod; @@ -20,6 +19,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// The virtual string properties of the TestContext are retrieved from the property dictionary /// like GetProperty<string>("TestName") or GetProperty<string>("FullyQualifiedTestClassName"). /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestContextImplementation : TestContext, ITestContext { /// @@ -33,15 +39,11 @@ public class TestContextImplementation : TestContext, ITestContext private readonly StringWriter _stringWriter; private readonly ThreadSafeStringWriter? _threadSafeStringWriter; - /// - /// Test Method. - /// - private readonly ITestMethod _testMethod; - /// /// Properties. /// private readonly Dictionary _properties; + private readonly IMessageLogger? _messageLogger; /// /// Specifies whether the writer is disposed or not. @@ -71,27 +73,40 @@ public class TestContextImplementation : TestContext, ITestContext /// The test method. /// The writer where diagnostic messages are written to. /// Properties/configuration passed in. - public TestContextImplementation(ITestMethod testMethod, StringWriter stringWriter, IDictionary properties) + /// The message logger to use. + internal TestContextImplementation(ITestMethod? testMethod, StringWriter stringWriter, IDictionary properties, IMessageLogger messageLogger) + : this(testMethod, stringWriter, properties) + => _messageLogger = messageLogger; + + /// + /// Initializes a new instance of the class. + /// + /// The test method. + /// The writer where diagnostic messages are written to. + /// Properties/configuration passed in. + public TestContextImplementation(ITestMethod? testMethod, StringWriter stringWriter, IDictionary properties) { - DebugEx.Assert(testMethod != null, "TestMethod is not null"); + // testMethod can be null when running ForceCleanup (done when reaching --maximum-failed-tests. DebugEx.Assert(properties != null, "properties is not null"); #if NETFRAMEWORK DebugEx.Assert(stringWriter != null, "StringWriter is not null"); #endif - _testMethod = testMethod; _stringWriter = stringWriter; // Cannot get this type in constructor directly, because all signatures for all platforms need to be the same. _threadSafeStringWriter = stringWriter as ThreadSafeStringWriter; - _properties = new Dictionary(properties) - { - [FullyQualifiedTestClassNameLabel] = _testMethod.FullClassName, - [ManagedTypeLabel] = _testMethod.ManagedTypeName, - [ManagedMethodLabel] = _testMethod.ManagedMethodName, - [TestNameLabel] = _testMethod.Name, - }; + _properties = testMethod is null + ? new Dictionary(properties) + : new Dictionary(properties) + { + [FullyQualifiedTestClassNameLabel] = testMethod.FullClassName, + [ManagedTypeLabel] = testMethod.ManagedTypeName, + [ManagedMethodLabel] = testMethod.ManagedMethodName, + [TestNameLabel] = testMethod.Name, + }; + _testResultFiles = []; } @@ -350,5 +365,7 @@ public void ClearDiagnosticMessages() public void SetDisplayName(string? displayName) => TestDisplayName = displayName; + public override void DisplayMessage(MessageLevel messageLevel, string message) + => _messageLogger?.SendMessage(messageLevel.ToTestMessageLevel(), message); #endregion } diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs index 04b6218b9e..232dc8d19b 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDataSource.cs @@ -4,17 +4,14 @@ #if NETFRAMEWORK using System.Configuration; using System.Data; -using System.Diagnostics; -using System.Globalization; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Data; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; -using Microsoft.VisualStudio.TestTools.UnitTesting; #endif using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; +using Microsoft.VisualStudio.TestTools.UnitTesting; using ITestDataSource = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestDataSource; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -26,12 +23,19 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// the tests since it can only be found at the test output directory. DO NOT call into this platform service outside of the appdomain context if you do not want to hit /// a ReflectionTypeLoadException. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestDataSource : ITestDataSource { #if NETFRAMEWORK - public IEnumerable? GetData(UTF.ITestMethod testMethodInfo, ITestContext testContext) + public IEnumerable? GetData(ITestMethod testMethodInfo, ITestContext testContext) #else - IEnumerable? ITestDataSource.GetData(UTF.ITestMethod testMethodInfo, ITestContext testContext) + IEnumerable? ITestDataSource.GetData(ITestMethod testMethodInfo, ITestContext testContext) #endif { #if NETFRAMEWORK @@ -42,12 +46,12 @@ public class TestDataSource : ITestDataSource Path.GetDirectoryName(new Uri(testMethodInfo.MethodInfo.Module.Assembly.CodeBase).LocalPath), ]; - List dataRowResults = []; + List dataRowResults = []; // Connect to data source. TestDataConnectionFactory factory = new(); - GetConnectionProperties(testMethodInfo.GetAttributes(false)[0], out string providerNameInvariant, out string? connectionString, out string? tableName, out UTF.DataAccessMethod dataAccessMethod); + GetConnectionProperties(testMethodInfo.GetAttributes(false)[0], out string providerNameInvariant, out string? connectionString, out string? tableName, out DataAccessMethod dataAccessMethod); try { @@ -96,7 +100,7 @@ public class TestDataSource : ITestDataSource /// The data access method. /// Number of permutations. /// Permutations. - private static IEnumerable GetPermutation(UTF.DataAccessMethod dataAccessMethod, int length) + private static IEnumerable GetPermutation(DataAccessMethod dataAccessMethod, int length) { switch (dataAccessMethod) { @@ -120,8 +124,8 @@ private static IEnumerable GetPermutation(UTF.DataAccessMethod dataAccessMe /// The connection string. /// The table name. /// The data access method. - private static void GetConnectionProperties(UTF.DataSourceAttribute dataSourceAttribute, out string providerNameInvariant, - out string? connectionString, out string? tableName, out UTF.DataAccessMethod dataAccessMethod) + private static void GetConnectionProperties(DataSourceAttribute dataSourceAttribute, out string providerNameInvariant, + out string? connectionString, out string? tableName, out DataAccessMethod dataAccessMethod) { if (StringEx.IsNullOrEmpty(dataSourceAttribute.DataSourceSettingName)) { @@ -132,7 +136,7 @@ private static void GetConnectionProperties(UTF.DataSourceAttribute dataSourceAt return; } - UTF.DataSourceElement element = TestConfiguration.ConfigurationSection.DataSources[dataSourceAttribute.DataSourceSettingName] + DataSourceElement element = TestConfiguration.ConfigurationSection.DataSources[dataSourceAttribute.DataSourceSettingName] #pragma warning disable CA2201 // Do not raise reserved exception types ?? throw new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_DataSourceConfigurationSectionMissing, dataSourceAttribute.DataSourceSettingName)); #pragma warning restore CA2201 // Do not raise reserved exception types diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs index 74c88e6d38..257f02d895 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - #if !WINDOWS_UWP using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; #endif @@ -25,6 +22,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// The test deployment. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestDeployment : ITestDeployment { #if !WINDOWS_UWP @@ -179,7 +183,7 @@ group test by test.Source into testGroup #if !WINDOWS_UWP internal static IDictionary GetDeploymentInformation(string source) { - var properties = new Dictionary(); + var properties = new Dictionary(capacity: 8); string applicationBaseDirectory = string.Empty; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs index 9261cdb897..e24f91db0a 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSource.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - #if WIN_UI using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AppContainer; #endif @@ -17,6 +15,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// This platform service is responsible for any data or operations to validate /// the test sources provided to the adapter. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestSource : ITestSource { #if WINDOWS_UWP || WIN_UI diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs index 482c4f9b94..441b3c7d5d 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; #if NETFRAMEWORK using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; @@ -23,6 +21,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// A host that loads the test source. This can be in isolation for desktop using an AppDomain or just loading the source in the current context. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TestSourceHost : ITestSourceHost { #if !WINDOWS_UWP diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs index bea00ebe35..52ee8d5205 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadOperations.cs @@ -1,10 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !NETFRAMEWORK -using System.Runtime.InteropServices; -#endif - +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -12,6 +9,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// This service is responsible for any Async operations specific to a platform. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class ThreadOperations : IThreadOperations { /// @@ -40,11 +44,7 @@ private static bool ExecuteWithThreadPool(Action action, int timeout, Cancellati // False means execution timed out. return executionTask.Wait(timeout, cancellationToken); } - catch (Exception ex) when - ((ex is OperationCanceledException oce && oce.CancellationToken == cancellationToken) - - // This exception occurs when the cancellation happens before the task is actually started. - || (ex is TaskCanceledException tce && tce.CancellationToken == cancellationToken)) + catch (Exception ex) when (ex.IsOperationCanceledExceptionFromToken(cancellationToken)) { // Task execution canceled. return false; @@ -52,7 +52,7 @@ private static bool ExecuteWithThreadPool(Action action, int timeout, Cancellati } #endif - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [SupportedOSPlatform("windows")] private static bool ExecuteWithCustomThread(Action action, int timeout, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) @@ -78,11 +78,7 @@ private static bool ExecuteWithCustomThread(Action action, int timeout, Cancella // If the execution thread completes before the timeout, the task will return true, otherwise false. return executionTask.Result; } - catch (Exception ex) when - ((ex is OperationCanceledException oce && oce.CancellationToken == cancellationToken) - - // This exception occurs when the cancellation happens before the task is actually started. - || (ex is TaskCanceledException tce && tce.CancellationToken == cancellationToken)) + catch (Exception ex) when (ex.IsOperationCanceledExceptionFromToken(cancellationToken)) { // Task execution canceled. return false; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs index d898382960..2994696ebd 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/ThreadSafeStringWriter.cs @@ -1,13 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// AsyncContext aware, thread safe string writer that allows output writes from different threads to end up in the same async local context. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class ThreadSafeStringWriter : StringWriter { #if DEBUG @@ -18,7 +23,7 @@ public class ThreadSafeStringWriter : StringWriter // This static lock guards access to the state and getting values from dictionary. There can be multiple different instances of ThreadSafeStringWriter // accessing the state at the same time, and we need to give them the correct state for their async context. Non-concurrent dictionary is used to store the // state because we need to lock around it anyway, to ensure that the State is populated, but not overwritten by every new instance of ThreadSafeStringWriter. - private static readonly object StaticLockObject = new(); + private static readonly Lock StaticLockObject = new(); private readonly string _outputType; /// @@ -172,10 +177,10 @@ private ThreadSafeStringBuilder GetOrAddStringBuilder() /// /// This StringBuilder puts locks around all the methods to avoid conflicts when writing or reading from multiple threads. /// - private class ThreadSafeStringBuilder + private sealed class ThreadSafeStringBuilder { private readonly StringBuilder _stringBuilder = new(); - private readonly object _instanceLockObject = new(); + private readonly Lock _instanceLockObject = new(); public void Append(string? value) { diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs index 36ec699eed..667fa713c0 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListener.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !WINDOWS_UWP && !WIN_UI -using System.Diagnostics; -#endif - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -16,6 +12,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// The virtual operations of the TraceListener are implemented here /// like Close(), Dispose() etc. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TraceListenerWrapper : #if !WINDOWS_UWP && !WIN_UI TextWriterTraceListener, diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs index 9aff10dc67..0eec6af8f1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceListenerManager.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !WINDOWS_UWP && !WIN_UI -using System.Diagnostics; -#endif - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -13,6 +9,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// Internal implementation of TraceListenerManager exposed to the user. /// Responsible for performing Add(), Remove(), Close(), Dispose() operations on traceListener object. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class TraceListenerManager : ITraceListenerManager { #if !WINDOWS_UWP && !WIN_UI diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs index 73e691f00c..60234199e6 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TraceLogger.cs @@ -9,6 +9,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// A service to log any trace messages from the adapter that would be shown in *.TpTrace files. /// +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public class AdapterTraceLogger : IAdapterTraceLogger { /// diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs index 0d74da2d04..a9f194d52a 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs @@ -3,10 +3,6 @@ #if NETFRAMEWORK -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -45,7 +41,7 @@ internal static void SetAppDomainFrameworkVersionBasedOnTestSource(AppDomainSetu { if (GetTargetFrameworkVersionFromVersionString(frameworkVersionString).CompareTo(Version45) > 0) { - PropertyInfo pInfo = typeof(AppDomainSetup).GetProperty(Constants.TargetFrameworkName); + PropertyInfo? pInfo = typeof(AppDomainSetup).GetProperty(Constants.TargetFrameworkName); pInfo?.SetValue(setup, frameworkVersionString, null); } } @@ -257,7 +253,7 @@ internal static Version GetTargetFrameworkVersionFromVersionString(string versio appDomainCultureHelper?.SetUICulture(uiCulture); } - private class AppDomainCultureHelper : MarshalByRefObject + private sealed class AppDomainCultureHelper : MarshalByRefObject { #pragma warning disable CA1822 // Mark members as static - Should not be static for our need public void SetUICulture(CultureInfo uiCulture) => CultureInfo.DefaultThreadCurrentUICulture = uiCulture; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainWrapper.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainWrapper.cs index ce5057c5b6..1fd4e53f75 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainWrapper.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainWrapper.cs @@ -10,11 +10,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// Abstraction over the AppDomain APIs. /// -internal class AppDomainWrapper : IAppDomain +internal sealed class AppDomainWrapper : IAppDomain { - public AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info) => AppDomain.CreateDomain(friendlyName, securityInfo, info); + public AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info) + => AppDomain.CreateDomain(friendlyName, securityInfo, info); - public void Unload(AppDomain appDomain) => AppDomain.Unload(appDomain); + public void Unload(AppDomain appDomain) + => AppDomain.Unload(appDomain); } #endif diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ApplicationStateGuard.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ApplicationStateGuard.cs index 1028c8e7da..6e6c1f2bd7 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ApplicationStateGuard.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ApplicationStateGuard.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter; internal static class ApplicationStateGuard diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs index 8c6511384d..18ddbf8cd5 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AssemblyUtility.cs @@ -4,10 +4,6 @@ #if !WINDOWS_UWP #if NETFRAMEWORK -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -19,6 +15,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Uti /// /// Utility for assembly specific functionality. /// +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class AssemblyUtility #if NETFRAMEWORK : IAssemblyUtility diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs index a787c71b3e..6cf7c7fade 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs @@ -3,11 +3,6 @@ #if !WINDOWS_UWP -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -17,7 +12,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Uti /// /// The deployment utility. /// -internal class DeploymentItemUtility +internal sealed class DeploymentItemUtility { // REVIEW: it would be better if this was a ReflectionHelper, because helper is able to cache. But we don't have reflection helper here, because this is platform services dll. private readonly ReflectionUtility _reflectionUtility; @@ -79,7 +74,7 @@ internal IList GetClassLevelDeploymentItems(Type type, ICollecti /// The warning message if it is an invalid deployment item. /// Returns true if it is a valid deployment item. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Internal method.")] - internal static bool IsValidDeploymentItem([NotNullWhen(true)] string? sourcePath, [NotNullWhen(true)] string? relativeOutputDirectory, out string warning) + internal static bool IsValidDeploymentItem([NotNullWhen(true)] string? sourcePath, [NotNullWhen(true)] string? relativeOutputDirectory, [NotNullWhen(false)] out string? warning) { if (StringEx.IsNullOrEmpty(sourcePath)) { @@ -105,7 +100,7 @@ internal static bool IsValidDeploymentItem([NotNullWhen(true)] string? sourcePat return false; } - warning = string.Empty; + warning = null; return true; } @@ -264,9 +259,9 @@ private static List GetDeploymentItems(IEnumerable deploymentIte IList result = new List(); - foreach (KeyValuePair deploymentItemData in deploymentItemsData) + foreach ((string? key, string? value) in deploymentItemsData) { - AddDeploymentItem(result, new DeploymentItem(deploymentItemData.Key, deploymentItemData.Value)); + AddDeploymentItem(result, new DeploymentItem(key, value)); } return result; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs index 73fdc4278e..af43490bde 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtility.cs @@ -3,10 +3,6 @@ #if !WINDOWS_UWP -#if NETFRAMEWORK || NETSTANDARD || NETCOREAPP3_1 -using System.Diagnostics; -#endif -using System.Globalization; #if NETFRAMEWORK using System.Security; #endif @@ -20,7 +16,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; -internal class DeploymentUtility : DeploymentUtilityBase +internal sealed class DeploymentUtility : DeploymentUtilityBase { public DeploymentUtility() : base() @@ -103,8 +99,8 @@ protected override void AddDependenciesOfDeploymentItem(string deploymentItemFil } #if NETFRAMEWORK - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - protected void ProcessNewStorage(string testSource, IList deploymentItems, IList warnings) + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] + public void ProcessNewStorage(string testSource, IList deploymentItems, IList warnings) { // Add deployment items and process .config files only for storages we have not processed before. if (!DeploymentItemUtility.IsValidDeploymentItem(testSource, string.Empty, out string? errorMessage)) @@ -136,7 +132,7 @@ protected void ProcessNewStorage(string testSource, IList deploy } } - protected IEnumerable GetSatellites(IEnumerable deploymentItems, string testSource, IList warnings) + public IEnumerable GetSatellites(IEnumerable deploymentItems, string testSource, IList warnings) { List satellites = []; foreach (DeploymentItem item in deploymentItems) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs index 0fbcabc78e..a63aa0656a 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs @@ -3,9 +3,6 @@ #if !WINDOWS_UWP -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs index 905e3e09ef..f97005e844 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/FileUtility.cs @@ -3,14 +3,13 @@ #if !WINDOWS_UWP -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for mocking")] internal class FileUtility { private readonly AssemblyUtility _assemblyUtility; @@ -84,7 +83,7 @@ public virtual string GetNextIterationDirectoryName(string parentDirectoryName, /// Returns empty string on error when specified to continue the run on error, /// throw on error when specified to abort the run on error. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] public virtual string CopyFileOverwrite(string source, string destination, out string? warning) { DebugEx.Assert(!StringEx.IsNullOrEmpty(source), "source should not be null."); @@ -222,7 +221,7 @@ public static string TryConvertPathToRelative(string path, string rootDir) /// them. /// /// The root directory to clear. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] public virtual void DeleteDirectories(string filePath) { Guard.NotNullOrWhiteSpace(filePath); @@ -253,7 +252,7 @@ public virtual void DeleteDirectories(string filePath) /// /// path to symbols file. /// Pdb file name or null if non-existent. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] private static string? GetSymbolsFileName(string? path) { if (StringEx.IsNullOrEmpty(path) || path.IndexOfAny(Path.GetInvalidPathChars()) != -1) diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/IAssemblyUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/IAssemblyUtility.cs index e180d9eada..1f923fb408 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/IAssemblyUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/IAssemblyUtility.cs @@ -3,8 +3,6 @@ #if NETFRAMEWORK -using System.Reflection; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; internal interface IAssemblyUtility diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/RandomIntPermutation.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/RandomIntPermutation.cs index 668da63a8a..a18ca30068 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/RandomIntPermutation.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/RandomIntPermutation.cs @@ -2,16 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETFRAMEWORK - -using System.Collections; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// Permutation of integers from 0 to (numberOfObjects - 1), in random order and in the end all values are returned. /// Used to get random permutation for data row access in data driven test. /// -internal class RandomIntPermutation : IEnumerable +internal sealed class RandomIntPermutation : IEnumerable { private readonly int[] _objects; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs index 5ebf1ee8d2..b9fe555b32 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/ReflectionUtility.cs @@ -3,16 +3,12 @@ #if !WINDOWS_UWP -#if NETFRAMEWORK -using System.Collections; -#endif -using System.Reflection; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; /// /// Utility for reflection API's. /// +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")] internal class ReflectionUtility { /// diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SequentialIntPermutation.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SequentialIntPermutation.cs index c77c8fe83d..179aeb21f1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SequentialIntPermutation.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SequentialIntPermutation.cs @@ -3,15 +3,13 @@ #if NETFRAMEWORK -using System.Collections; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; /// /// Permutation of integers from 0 to (numberOfObjects - 1) returned by increment of 1. /// Used to get sequential permutation for data row access in data driven test. /// -internal class SequentialIntPermutation : IEnumerable +internal sealed class SequentialIntPermutation : IEnumerable { private readonly int _numberOfObjects; diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs index 1775e61d6c..87f29d19a1 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/VSInstallationUtilities.cs @@ -3,13 +3,17 @@ #if NETFRAMEWORK -using System.Diagnostics; -using System.Runtime.InteropServices; - using static System.String; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; +#if RELEASE +#if NET6_0_OR_GREATER +[Obsolete(Constants.PublicTypeObsoleteMessage, DiagnosticId = "MSTESTOBS")] +#else +[Obsolete(Constants.PublicTypeObsoleteMessage)] +#endif +#endif public static class VSInstallationUtilities { /// @@ -33,7 +37,7 @@ public static class VSInstallationUtilities /// Gets the visual studio installation path on the local machine. /// /// VS install path. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Need to ignore failures to read the registry settings")] + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Need to ignore failures to read the registry settings")] public static string? VSInstallPath { get diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs index afbef47053..6c23bce1db 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/XmlUtilities.cs @@ -3,15 +3,11 @@ #if NETFRAMEWORK -using System.Globalization; -using System.Reflection; -using System.Text; -using System.Xml; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for mocking")] internal class XmlUtilities { private const string XmlNamespace = "urn:schemas-microsoft-com:asm.v1"; diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs new file mode 100644 index 0000000000..60e8614c80 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldAvoidConditionalAccessFixer.cs @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssertionArgsShouldAvoidConditionalAccessFixer))] +[Shared] +public sealed class AssertionArgsShouldAvoidConditionalAccessFixer : CodeFixProvider +{ + /// + /// The scenario that is complicating this code fix is if we have multiple diagnostics that are doing conditional access + /// on the same expression. In that case, we need to ensure that we don't add multiple Assert.IsNotNull calls. + /// The first idea was to iterate through the existing statements, and if we found Assert.IsNotNull with + /// the relevant expression, we don't add it again. However, this approach works for iterative codefix application + /// only, and doesn't work with the BatchFixAllProvider. The BatchFixAllProvider works by applying individual fixes + /// completely in isolation, then merging the text changes. + /// This means, every invocation of the code action will not see that Assert.IsNotNull was added by another. + /// So, we provide our own FixAllProvider. + /// This FixAllProvider will reuse the same DocumentEditor across all the code actions. + /// + private sealed class CustomFixAll : DocumentBasedFixAllProvider + { + protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics) + { + SyntaxNode root = await document.GetRequiredSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false); + DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken); + Document currentDocument = document; + foreach (Diagnostic diagnostic in diagnostics) + { + SyntaxNode assertInvocation = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + // We need to track the assert invocation so that the individual 'SingleFixCodeAction's can get the up-to-date node + // from the most recent tree. + // Having the most recent node is important for IsNullAssertAlreadyPresent to work properly. + // We get the most recent node via editor.GetChangedRoot().GetCurrentNode(...) + editor.TrackNode(assertInvocation); + } + + foreach (Diagnostic diagnostic in diagnostics) + { + SyntaxNode conditionalAccess = root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true); + SyntaxNode assertInvocation = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (conditionalAccess is not ConditionalAccessExpressionSyntax conditionalAccessExpressionSyntax || + assertInvocation is not InvocationExpressionSyntax invocationExpressionSyntax) + { + continue; + } + + var codeAction = new SingleFixCodeAction(currentDocument, conditionalAccessExpressionSyntax, invocationExpressionSyntax); + currentDocument = codeAction.ApplyFix(editor); + } + + return editor.GetChangedDocument(); + } + } + + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AssertionArgsShouldAvoidConditionalAccessRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => new CustomFixAll(); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + + SyntaxNode conditionalAccess = root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true); + SyntaxNode assertInvocation = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (conditionalAccess is not ConditionalAccessExpressionSyntax conditionalAccessExpressionSyntax || + assertInvocation is not InvocationExpressionSyntax invocationExpressionSyntax) + { + return; + } + + context.RegisterCodeFix( + new SingleFixCodeAction(context.Document, conditionalAccessExpressionSyntax, invocationExpressionSyntax), + diagnostic); + } + + private sealed class SingleFixCodeAction : CodeAction + { + private readonly Document _document; + private readonly ConditionalAccessExpressionSyntax _conditionalAccessExpressionSyntax; + private readonly InvocationExpressionSyntax _invocationExpressionSyntax; + + public SingleFixCodeAction(Document document, ConditionalAccessExpressionSyntax conditionalAccessExpressionSyntax, InvocationExpressionSyntax invocationExpressionSyntax) + { + _document = document; + _conditionalAccessExpressionSyntax = conditionalAccessExpressionSyntax; + _invocationExpressionSyntax = invocationExpressionSyntax; + } + + public override string Title { get; } = CodeFixResources.AssertionArgsShouldAvoidConditionalAccessFix; + + public override string? EquivalenceKey => nameof(AssertionArgsShouldAvoidConditionalAccessFixer); + + protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false); + return ApplyFix(editor); + } + + internal Document ApplyFix(DocumentEditor editor) + { + ExpressionSyntax expressionCheckedForNull = _conditionalAccessExpressionSyntax.Expression; + bool isNullAssertAlreadyPresent = IsNullAssertAlreadyPresent(expressionCheckedForNull, editor.GetChangedRoot().GetCurrentNode(_invocationExpressionSyntax) ?? _invocationExpressionSyntax); + + // Easier than correctly reconstructing the syntax node manually, but not ideal. + ExpressionSyntax parsedExpression = SyntaxFactory.ParseExpression($"{expressionCheckedForNull.ToFullString()}{_conditionalAccessExpressionSyntax.WhenNotNull}"); + parsedExpression = parsedExpression.WithTriviaFrom(_conditionalAccessExpressionSyntax); + + editor.ReplaceNode(_conditionalAccessExpressionSyntax, parsedExpression); + + if (!isNullAssertAlreadyPresent) + { + ExpressionStatementSyntax assertIsNotNull = SyntaxFactory.ExpressionStatement( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("Assert"), + SyntaxFactory.IdentifierName("IsNotNull"))) + .WithArgumentList( + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument(expressionCheckedForNull))))); + if (_invocationExpressionSyntax.Parent is ExpressionStatementSyntax expressionStatement) + { + editor.InsertBefore(expressionStatement, assertIsNotNull); + } + else if (_invocationExpressionSyntax.Parent is ArrowExpressionClauseSyntax arrowExpressionClauseSyntax) + { + // The following types are where ArrowExpressionClause can appear. + // BaseMethodDeclarationSyntax: ConstructorDeclarationSyntax, ConversionOperatorDeclarationSyntax, DestructorDeclarationSyntax, MethodDeclarationSyntax, OperatorDeclarationSyntax + // AccessorDeclarationSyntax, IndexerDeclarationSyntax, PropertyDeclarationSyntax, LocalFunctionStatementSyntax + // + // PropertyDeclarationSyntax and IndexerDeclarationSyntax don't make sense so we won't handle it. + if (arrowExpressionClauseSyntax.Parent is BaseMethodDeclarationSyntax parentBaseMethod) + { + editor.ReplaceNode( + parentBaseMethod, + (node, _) => + { + var parentBaseMethod = (BaseMethodDeclarationSyntax)node; + return parentBaseMethod + .WithExpressionBody(null) + .WithSemicolonToken(default) + .WithBody(SyntaxFactory.Block( + assertIsNotNull, + SyntaxFactory.ExpressionStatement(parentBaseMethod.ExpressionBody!.Expression))); + }); + } + else if (arrowExpressionClauseSyntax.Parent is AccessorDeclarationSyntax parentAccessor) + { + editor.ReplaceNode( + parentAccessor, + (node, _) => + { + var parentAccessor = (AccessorDeclarationSyntax)node; + return parentAccessor + .WithExpressionBody(null) + .WithSemicolonToken(default) + .WithBody(SyntaxFactory.Block( + assertIsNotNull, + SyntaxFactory.ExpressionStatement(parentAccessor.ExpressionBody!.Expression))); + }); + } + else if (arrowExpressionClauseSyntax.Parent is LocalFunctionStatementSyntax parentLocalFunction) + { + editor.ReplaceNode( + parentLocalFunction, + (node, _) => + { + var parentLocalFunction = (LocalFunctionStatementSyntax)node; + return parentLocalFunction + .WithExpressionBody(null) + .WithSemicolonToken(default) + .WithBody(SyntaxFactory.Block( + assertIsNotNull, + SyntaxFactory.ExpressionStatement(parentLocalFunction.ExpressionBody!.Expression))); + }); + } + } + } + + return editor.GetChangedDocument(); + } + } + + private static bool IsNullAssertAlreadyPresent(SyntaxNode expressionCheckedForNull, InvocationExpressionSyntax invocationExpressionSyntax) + { + if (invocationExpressionSyntax.Parent?.Parent is not BlockSyntax blockSyntax) + { + return false; + } + + foreach (StatementSyntax statement in blockSyntax.Statements) + { + if (statement is not ExpressionStatementSyntax expressionStatement) + { + continue; + } + + // We expect Assert.IsNull to be present before the invocation expression in question. + if (expressionStatement.Expression == invocationExpressionSyntax) + { + return false; + } + + if (expressionStatement.Expression is InvocationExpressionSyntax invocation) + { + SimpleNameSyntax? methodName = + invocation.Expression as IdentifierNameSyntax ?? (invocation.Expression as MemberAccessExpressionSyntax)?.Name; + if ((methodName?.Identifier.Value as string) == "IsNotNull" && + invocation.ArgumentList.Arguments.Count > 0 && + invocation.ArgumentList.Arguments[0].Expression.IsEquivalentTo(expressionCheckedForNull)) + { + return true; + } + } + } + + return false; + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs index 6b0ae32313..a949ea4dfd 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AssertionArgsShouldBePassedInCorrectOrderFixer.cs @@ -56,8 +56,8 @@ private static Task SwapArgumentsAsync(Document document, SyntaxNode r SeparatedSyntaxList arguments = invocationExpr.ArgumentList.Arguments; - ArgumentSyntax expectedArg = arguments.FirstOrDefault(IsExpectedArgument); - ArgumentSyntax actualArg = arguments.FirstOrDefault(IsActualArgument); + ArgumentSyntax? expectedArg = arguments.FirstOrDefault(IsExpectedArgument); + ArgumentSyntax? actualArg = arguments.FirstOrDefault(IsActualArgument); // Handle positional arguments if named arguments are not found if (expectedArg == null || actualArg == null) diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidAssertAreSameWithValueTypesFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidAssertAreSameWithValueTypesFixer.cs new file mode 100644 index 0000000000..81f5f78d5c --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidAssertAreSameWithValueTypesFixer.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AvoidAssertAreSameWithValueTypesFixer))] +[Shared] +public sealed class AvoidAssertAreSameWithValueTypesFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.AvoidAssertAreSameWithValueTypesRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + + string? replacement = diagnostic.Properties[AvoidAssertAreSameWithValueTypesAnalyzer.ReplacemenyKey] + ?? throw ApplicationStateGuard.Unreachable(); + + SyntaxNode diagnosticNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (diagnosticNode is not InvocationExpressionSyntax invocation) + { + Debug.Fail($"Is this an interesting scenario where IInvocationOperation for Assert call isn't associated with InvocationExpressionSyntax? SyntaxNode type: '{diagnosticNode.GetType()}', Text: '{diagnosticNode.GetText()}'"); + return; + } + + SyntaxNode methodNameIdentifier = invocation.Expression; + if (methodNameIdentifier is MemberAccessExpressionSyntax memberAccess) + { + methodNameIdentifier = memberAccess.Name; + } + + if (methodNameIdentifier is not SimpleNameSyntax simpleNameSyntax) + { + Debug.Fail($"Is this an interesting scenario where we are unable to retrieve SimpleNameSyntax corresponding to the assert method? SyntaxNode type: '{methodNameIdentifier}', Text: '{methodNameIdentifier.GetText()}'."); + return; + } + + context.RegisterCodeFix( + CodeAction.Create( + title: string.Format(CultureInfo.InvariantCulture, CodeFixResources.AvoidAssertAreSameWithValueTypesFix, replacement), + ct => FixMethodNameAsync(context.Document, simpleNameSyntax, replacement, ct), + equivalenceKey: nameof(AvoidAssertAreSameWithValueTypesFixer)), + diagnostic); + } + + private static async Task FixMethodNameAsync(Document document, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName, CancellationToken cancellationToken) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + editor.ReplaceNode(simpleNameSyntax, simpleNameSyntax.WithIdentifier(SyntaxFactory.Identifier(properAssertMethodName))); + return editor.GetChangedDocument(); + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExpectedExceptionAttributeFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExpectedExceptionAttributeFixer.cs index cf08f96300..ee68f392bb 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExpectedExceptionAttributeFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/AvoidExpectedExceptionAttributeFixer.cs @@ -42,10 +42,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) return; } - if (diagnostic.Properties.ContainsKey(DiagnosticDescriptorHelper.CannotFixPropertyKey)) - { - return; - } + bool allowDerivedTypes = diagnostic.Properties.ContainsKey(AvoidExpectedExceptionAttributeAnalyzer.AllowDerivedTypesKey); // Find the method declaration identified by the diagnostic. MethodDeclarationSyntax methodDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType().First(); @@ -84,35 +81,105 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix( CodeAction.Create( title: CodeFixResources.UseAssertThrowsExceptionOnLastStatementFix, - createChangedDocument: c => WrapLastStatementWithAssertThrowsExceptionAsync(context.Document, methodDeclaration, attributeSyntax, exceptionTypeSymbol, c), + createChangedDocument: c => WrapLastStatementWithAssertThrowsExceptionAsync(context.Document, methodDeclaration, attributeSyntax, exceptionTypeSymbol, allowDerivedTypes, c), equivalenceKey: nameof(AvoidExpectedExceptionAttributeFixer)), diagnostic); } + private static (SyntaxNode ExpressionOrStatement, SyntaxNode NodeToReplace)? TryGetExpressionOfInterestAndNodeToFromBlockSyntax(BlockSyntax? block) + { + if (block is null) + { + return null; + } + + for (int i = block.Statements.Count - 1; i >= 0; i--) + { + StatementSyntax statement = block.Statements[i]; + + if (statement is LockStatementSyntax lockStatement) + { + if (lockStatement.Statement is BlockSyntax lockBlock) + { + if (TryGetExpressionOfInterestAndNodeToFromBlockSyntax(lockBlock) is { } resultFromLock) + { + return resultFromLock; + } + + continue; + } + + statement = lockStatement.Statement; + } + + if (statement is LocalFunctionStatementSyntax or EmptyStatementSyntax) + { + continue; + } + else if (statement is BlockSyntax nestedBlock) + { + if (TryGetExpressionOfInterestAndNodeToFromBlockSyntax(nestedBlock) is { } expressionFromNestedBlock) + { + return expressionFromNestedBlock; + } + + // The BlockSyntax doesn't have any meaningful statements/expressions. + // Ignore it. + continue; + } + else if (statement is ExpressionStatementSyntax expressionStatement) + { + return (expressionStatement.Expression, statement); + } + else if (statement is LocalDeclarationStatementSyntax localDeclarationStatementSyntax && + localDeclarationStatementSyntax.Declaration.Variables.Count == 1 && + localDeclarationStatementSyntax.Declaration.Variables[0].Initializer is { } initializer) + { + return (initializer.Value, statement); + } + + return (statement, statement); + } + + return null; + } + + private static (SyntaxNode ExpressionOrStatement, SyntaxNode NodeToReplace)? TryGetExpressionOfInterestAndNodeToFromExpressionBody(MethodDeclarationSyntax method) + => method.ExpressionBody is null ? null : (method.ExpressionBody.Expression, method.ExpressionBody.Expression); + private static async Task WrapLastStatementWithAssertThrowsExceptionAsync( Document document, MethodDeclarationSyntax methodDeclaration, SyntaxNode attributeSyntax, ITypeSymbol exceptionTypeSymbol, + bool allowDerivedTypes, CancellationToken cancellationToken) { DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); editor.RemoveNode(attributeSyntax); - SyntaxNode? oldStatement = (SyntaxNode?)methodDeclaration.Body?.Statements.LastOrDefault() ?? methodDeclaration.ExpressionBody?.Expression; - if (oldStatement is null) + (SyntaxNode ExpressionOrStatement, SyntaxNode NodeToReplace)? expressionAndNodeToReplace = TryGetExpressionOfInterestAndNodeToFromBlockSyntax(methodDeclaration.Body) + ?? TryGetExpressionOfInterestAndNodeToFromExpressionBody(methodDeclaration); + + if (expressionAndNodeToReplace is null) { return editor.GetChangedDocument(); } - SyntaxNode newLambdaExpression = oldStatement switch + SyntaxGenerator generator = editor.Generator; + SyntaxNode expressionToUseInLambda = expressionAndNodeToReplace.Value.ExpressionOrStatement; + + expressionToUseInLambda = expressionToUseInLambda switch { - ExpressionStatementSyntax oldLambdaExpression => oldLambdaExpression.Expression, - _ => oldStatement, + ThrowStatementSyntax { Expression: not null } throwStatement => generator.ThrowExpression(throwStatement.Expression), + // This is the case when the last statement of the method body is a loop for example (e.g, for, foreach, while, do while). + // It can also happen for using statement, or switch statement. + // In that case, we need to wrap in a block syntax (i.e, curly braces) + StatementSyntax expressionToUseAsStatement => SyntaxFactory.Block(expressionToUseAsStatement), + _ => expressionToUseInLambda.WithoutTrivia(), }; - SyntaxGenerator generator = editor.Generator; - newLambdaExpression = generator.VoidReturningLambdaExpression(newLambdaExpression); + SyntaxNode newLambdaExpression = generator.VoidReturningLambdaExpression(expressionToUseInLambda); bool containsAsyncCode = newLambdaExpression.DescendantNodesAndSelf().Any(n => n is AwaitExpressionSyntax); if (containsAsyncCode) @@ -123,7 +190,14 @@ private static async Task WrapLastStatementWithAssertThrowsExceptionAs SyntaxNode newStatement = generator.InvocationExpression( generator.MemberAccessExpression( generator.IdentifierName("Assert"), - generator.GenericName(containsAsyncCode ? "ThrowsExceptionAsync" : "ThrowsException", [exceptionTypeSymbol])), + generator.GenericName( + (containsAsyncCode, allowDerivedTypes) switch + { + (false, false) => "ThrowsExactly", + (false, true) => "Throws", + (true, false) => "ThrowsExactlyAsync", + (true, true) => "ThrowsAsync", + }, [exceptionTypeSymbol])), newLambdaExpression); if (containsAsyncCode) @@ -137,7 +211,7 @@ private static async Task WrapLastStatementWithAssertThrowsExceptionAs newStatement = generator.ExpressionStatement(newStatement); } - editor.ReplaceNode(oldStatement, newStatement); + editor.ReplaceNode(expressionAndNodeToReplace.Value.NodeToReplace, newStatement.WithTriviaFrom(expressionAndNodeToReplace.Value.NodeToReplace)); return editor.GetChangedDocument(); } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs index 442d0dc282..a32c10e0bf 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs @@ -78,6 +78,24 @@ internal static string AssemblyInitializeShouldBeValidCodeFix { } } + /// + /// Looks up a localized string similar to Move conditional access in assertion to separate 'Assert.IsNotNull' check. + /// + internal static string AssertionArgsShouldAvoidConditionalAccessFix { + get { + return ResourceManager.GetString("AssertionArgsShouldAvoidConditionalAccessFix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use '{0}'. + /// + internal static string AvoidAssertAreSameWithValueTypesFix { + get { + return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesFix", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change method accessibility to 'private'. /// @@ -203,5 +221,23 @@ internal static string UseAttributeOnTestMethodFix { return ResourceManager.GetString("UseAttributeOnTestMethodFix", resourceCulture); } } + + /// + /// Looks up a localized string similar to Use '{0}'. + /// + internal static string UseNewerAssertThrows { + get { + return ResourceManager.GetString("UseNewerAssertThrows", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use '{0}'. + /// + internal static string UseProperAssertMethodsFix { + get { + return ResourceManager.GetString("UseProperAssertMethodsFix", resourceCulture); + } + } } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx index 6e97d9e1a8..50c67bba6c 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx @@ -165,4 +165,16 @@ Use 'Assert.ThrowsException<T>' instead of '[ExpectedException]' attribute + + Use '{0}' + + + Use '{0}' + + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + + + Use '{0}' + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferAssertFailOverAlwaysFalseConditionsFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferAssertFailOverAlwaysFalseConditionsFixer.cs index 56966e24e2..7b5379e0c9 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferAssertFailOverAlwaysFalseConditionsFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferAssertFailOverAlwaysFalseConditionsFixer.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Text; @@ -44,22 +45,28 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix( CodeAction.Create( CodeFixResources.ReplaceWithFailAssertionFix, - ct => SwapArgumentsAsync(context.Document, invocationExpr, ct), + ct => UseAssertFailAsync(context.Document, invocationExpr, diagnostic.AdditionalLocations, ct), nameof(PreferAssertFailOverAlwaysFalseConditionsFixer)), context.Diagnostics); } } - private static async Task SwapArgumentsAsync(Document document, InvocationExpressionSyntax invocationExpr, CancellationToken cancellationToken) + private static async Task UseAssertFailAsync(Document document, InvocationExpressionSyntax invocationExpr, IReadOnlyList additionalLocations, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); SyntaxGenerator generator = editor.Generator; - SyntaxNode newInvocationExpr = generator.InvocationExpression( + var newInvocationExpr = (InvocationExpressionSyntax)generator.InvocationExpression( generator.MemberAccessExpression(generator.IdentifierName("Assert"), "Fail")); + if (additionalLocations.Count >= 1) + { + IEnumerable arguments = additionalLocations.Select(location => (ArgumentSyntax)invocationExpr.FindNode(location.SourceSpan)); + newInvocationExpr = newInvocationExpr.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); + } + editor.ReplaceNode(invocationExpr, newInvocationExpr); return editor.GetChangedDocument(); diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs index 7b02f31dc8..edf1f99822 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferConstructorOverTestInitializeFixer.cs @@ -43,7 +43,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } // Find the method declaration identified by the diagnostic. - MethodDeclarationSyntax methodDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType().FirstOrDefault(); + MethodDeclarationSyntax? methodDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType().FirstOrDefault(); if (methodDeclaration == null) { return; @@ -64,7 +64,7 @@ private static async Task ReplaceTestInitializeWithConstructorAsync(Do // Find the class containing the method if (testInitializeMethod.Parent is ClassDeclarationSyntax containingClass) { - ConstructorDeclarationSyntax existingConstructor = containingClass.Members + ConstructorDeclarationSyntax? existingConstructor = containingClass.Members .OfType() .FirstOrDefault(); diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs index 1c844f6f61..730be4dc58 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferDisposeOverTestCleanupFixer.cs @@ -44,7 +44,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } // Find the TestCleanup method declaration identified by the diagnostic. - MethodDeclarationSyntax testCleanupMethod = syntaxToken.Parent.AncestorsAndSelf().OfType().FirstOrDefault(); + MethodDeclarationSyntax? testCleanupMethod = syntaxToken.Parent.AncestorsAndSelf().OfType().FirstOrDefault(); if (testCleanupMethod == null || !IsTestCleanupMethodValid(testCleanupMethod) || testCleanupMethod.Parent is not TypeDeclarationSyntax containingType) diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs index 6f8b9a2923..c48bdbe928 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/PreferTestInitializeOverConstructorFixer.cs @@ -43,7 +43,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) } // Find the constructor declaration identified by the diagnostic. - ConstructorDeclarationSyntax constructorDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType().FirstOrDefault(); + ConstructorDeclarationSyntax? constructorDeclaration = syntaxToken.Parent.AncestorsAndSelf().OfType().FirstOrDefault(); if (constructorDeclaration == null) { return; diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseNewerAssertThrowsFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseNewerAssertThrowsFixer.cs new file mode 100644 index 0000000000..587c039756 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseNewerAssertThrowsFixer.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Simplification; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseNewerAssertThrowsFixer))] +[Shared] +public sealed class UseNewerAssertThrowsFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.UseNewerAssertThrowsRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + + SyntaxNode diagnosticNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (diagnosticNode is not InvocationExpressionSyntax invocation) + { + Debug.Fail($"Is this an interesting scenario where IInvocationOperation for Assert call isn't associated with InvocationExpressionSyntax? SyntaxNode type: '{diagnosticNode.GetType()}', Text: '{diagnosticNode.GetText()}'"); + return; + } + + SyntaxNode methodNameIdentifier = invocation.Expression; + if (methodNameIdentifier is MemberAccessExpressionSyntax memberAccess) + { + methodNameIdentifier = memberAccess.Name; + } + + if (methodNameIdentifier is not GenericNameSyntax genericNameSyntax) + { + Debug.Fail($"Is this an interesting scenario where we are unable to retrieve GenericNameSyntax corresponding to the assert method? SyntaxNode type: '{methodNameIdentifier}', Text: '{methodNameIdentifier.GetText()}'."); + return; + } + + string updatedMethodName = genericNameSyntax.Identifier.Text switch + { + "ThrowsException" => "ThrowsExactly", + "ThrowsExceptionAsync" => "ThrowsExactlyAsync", + // The analyzer should report a diagnostic only for ThrowsException and ThrowsExceptionAsync + _ => throw ApplicationStateGuard.Unreachable(), + }; + + context.RegisterCodeFix( + CodeAction.Create( + title: string.Format(CultureInfo.InvariantCulture, CodeFixResources.UseNewerAssertThrows, updatedMethodName), + ct => Task.FromResult(context.Document.WithSyntaxRoot(UpdateMethodName(new SyntaxEditor(root, context.Document.Project.Solution.Workspace), invocation, genericNameSyntax, updatedMethodName, diagnostic.AdditionalLocations))), + equivalenceKey: nameof(UseProperAssertMethodsFixer)), + diagnostic); + } + + private static SyntaxNode UpdateMethodName(SyntaxEditor editor, InvocationExpressionSyntax invocation, GenericNameSyntax genericNameSyntax, string updatedMethodName, IReadOnlyList additionalLocations) + { + editor.ReplaceNode(genericNameSyntax, genericNameSyntax.WithIdentifier(SyntaxFactory.Identifier(updatedMethodName).WithTriviaFrom(genericNameSyntax.Identifier))); + + // The object[] parameter to format the message is named parameters in the old ThrowsException[Async] methods, but is named messageArgs in the new ThrowsExactly[Async] methods. + if (invocation.ArgumentList.Arguments.FirstOrDefault(arg => arg.NameColon is { Name.Identifier.Text: "parameters" }) is { } arg) + { + editor.ReplaceNode(arg.NameColon!.Name, arg.NameColon!.Name.WithIdentifier(SyntaxFactory.Identifier("messageArgs").WithTriviaFrom(arg.NameColon.Name.Identifier))); + } + + if (additionalLocations.Count != 1) + { + return editor.GetChangedRoot(); + } + + // The existing ThrowsException call is using the Func overload. The new ThrowsExactly method does not have this overload, so we need to adjust. + // This is a best effort handling. + SyntaxNode actionArgument = editor.OriginalRoot.FindNode(additionalLocations[0].SourceSpan, getInnermostNodeForTie: true); + + if (actionArgument is ParenthesizedLambdaExpressionSyntax lambdaSyntax) + { + if (lambdaSyntax.ExpressionBody is not null) + { + editor.ReplaceNode( + lambdaSyntax.ExpressionBody, + AssignToDiscard(lambdaSyntax.ExpressionBody)); + } + else if (lambdaSyntax.Block is not null) + { + // This is more complex. We need to iterate through all descendants of type ReturnStatementSyntax, and split it into two statements. + // The first statement will be an assignment expression to a discard, and the second statement will be 'return;'. + // We may even need to add extra braces in case the return statement (for example) is originally inside an if statement without braces. + // For example: + // if (condition) + // return Whatever; + // should be converted to: + // if (condition) + // { + // _ = Whatever; + // return; + // } + // Keep in mind: When descending into descendant nodes, we shouldn't descend into potential other lambda expressions or local functions. + IEnumerable returnStatements = lambdaSyntax.Block.DescendantNodes(descendIntoChildren: node => node is not (LocalFunctionStatementSyntax or AnonymousFunctionExpressionSyntax)).OfType(); + foreach (ReturnStatementSyntax returnStatement in returnStatements) + { + if (returnStatement.Expression is not { } returnExpression) + { + // This should be an error in user code. + continue; + } + + ExpressionStatementSyntax returnReplacement = SyntaxFactory.ExpressionStatement(AssignToDiscard(returnStatement.Expression)); + + if (returnStatement.Parent is BlockSyntax blockSyntax) + { + editor.InsertAfter(returnStatement, SyntaxFactory.ReturnStatement()); + editor.ReplaceNode(returnStatement, returnReplacement); + } + else + { + editor.ReplaceNode( + returnStatement, + SyntaxFactory.Block( + returnReplacement, + SyntaxFactory.ReturnStatement())); + } + } + } + } + else if (actionArgument is ExpressionSyntax expressionSyntax) + { + editor.ReplaceNode( + expressionSyntax, + SyntaxFactory.ParenthesizedLambdaExpression( + SyntaxFactory.ParameterList(), + block: null, + expressionBody: AssignToDiscard(SyntaxFactory.InvocationExpression(SyntaxFactory.ParenthesizedExpression(expressionSyntax).WithAdditionalAnnotations(Simplifier.Annotation))))); + } + + return editor.GetChangedRoot(); + } + + private static AssignmentExpressionSyntax AssignToDiscard(ExpressionSyntax expression) + => SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName("_"), expression); +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs new file mode 100644 index 0000000000..d8dfaf8a42 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseProperAssertMethodsFixer.cs @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseProperAssertMethodsFixer))] +[Shared] +public sealed class UseProperAssertMethodsFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } + = ImmutableArray.Create(DiagnosticIds.UseProperAssertMethodsRuleId); + + public override FixAllProvider GetFixAllProvider() + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + Diagnostic diagnostic = context.Diagnostics[0]; + string? mode = diagnostic.Properties[UseProperAssertMethodsAnalyzer.CodeFixModeKey]; + string? properAssertMethodName = diagnostic.Properties[UseProperAssertMethodsAnalyzer.ProperAssertMethodNameKey]; + if (mode is null || properAssertMethodName is null) + { + Debug.Fail($"Both '{nameof(mode)}' and '{properAssertMethodName}' are expected to be non-null."); + return; + } + + SyntaxNode diagnosticNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (diagnosticNode is not InvocationExpressionSyntax invocation) + { + Debug.Fail($"Is this an interesting scenario where IInvocationOperation for Assert call isn't associated with InvocationExpressionSyntax? SyntaxNode type: '{diagnosticNode.GetType()}', Text: '{diagnosticNode.GetText()}'"); + return; + } + + SyntaxNode methodNameIdentifier = invocation.Expression; + if (methodNameIdentifier is MemberAccessExpressionSyntax memberAccess) + { + methodNameIdentifier = memberAccess.Name; + } + + if (methodNameIdentifier is not SimpleNameSyntax simpleNameSyntax) + { + Debug.Fail($"Is this an interesting scenario where we are unable to retrieve SimpleNameSyntax corresponding to the assert method? SyntaxNode type: '{methodNameIdentifier}', Text: '{methodNameIdentifier.GetText()}'."); + return; + } + + Func>? createChangedDocument = null; + switch (mode) + { + case UseProperAssertMethodsAnalyzer.CodeFixModeSimple: + createChangedDocument = ct => FixAssertMethodForSimpleModeAsync(context.Document, diagnostic.AdditionalLocations[0], diagnostic.AdditionalLocations[1], root, simpleNameSyntax, properAssertMethodName, ct); + break; + case UseProperAssertMethodsAnalyzer.CodeFixModeAddArgument: + createChangedDocument = ct => FixAssertMethodForAddArgumentModeAsync(context.Document, diagnostic.AdditionalLocations[0], diagnostic.AdditionalLocations[1], diagnostic.AdditionalLocations[2], root, simpleNameSyntax, properAssertMethodName, ct); + break; + case UseProperAssertMethodsAnalyzer.CodeFixModeRemoveArgument: + createChangedDocument = ct => FixAssertMethodForRemoveArgumentModeAsync(context.Document, diagnostic.AdditionalLocations, root, simpleNameSyntax, properAssertMethodName, diagnostic.Properties.ContainsKey(UseProperAssertMethodsAnalyzer.NeedsNullableBooleanCastKey), ct); + break; + default: + break; + } + + if (createChangedDocument is not null) + { + context.RegisterCodeFix( + CodeAction.Create( + title: string.Format(CultureInfo.InvariantCulture, CodeFixResources.UseProperAssertMethodsFix, properAssertMethodName), + createChangedDocument, + equivalenceKey: nameof(UseProperAssertMethodsFixer)), + diagnostic); + } + } + + private static async Task FixAssertMethodForSimpleModeAsync(Document document, Location conditionLocationToBeReplaced, Location replacementExpressionLocation, SyntaxNode root, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName, CancellationToken cancellationToken) + { + // This doesn't properly handle cases like Assert.IsTrue(message: "My message", condition: x == null) + // The proper handling of this may be Assert.IsNull(message: "My message", value: x) + // Or: Assert.IsNull(x, "My message") + // For now this is not handled. + if (root.FindNode(conditionLocationToBeReplaced.SourceSpan) is not ArgumentSyntax conditionNodeToBeReplaced) + { + return document; + } + + if (root.FindNode(replacementExpressionLocation.SourceSpan) is not ExpressionSyntax replacementExpressionNode) + { + return document; + } + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); + editor.ReplaceNode(conditionNodeToBeReplaced, SyntaxFactory.Argument(replacementExpressionNode).WithAdditionalAnnotations(Formatter.Annotation)); + + return editor.GetChangedDocument(); + } + + private static async Task FixAssertMethodForAddArgumentModeAsync(Document document, Location conditionLocation, Location expectedLocation, Location actualLocation, SyntaxNode root, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName, CancellationToken cancellationToken) + { + // This doesn't properly handle cases like Assert.IsTrue(message: "My message", condition: x == y) + // The proper handling of this may be Assert.AreEqual(message: "My message", expected: x, actual: y) + // Or: Assert.AreEqual(x, y, "My message") + // For now this is not handled. + if (root.FindNode(conditionLocation.SourceSpan) is not ArgumentSyntax conditionNode) + { + return document; + } + + if (conditionNode.Parent is not ArgumentListSyntax argumentList) + { + return document; + } + + if (root.FindNode(expectedLocation.SourceSpan) is not ExpressionSyntax expectedNode) + { + return document; + } + + if (root.FindNode(actualLocation.SourceSpan) is not ExpressionSyntax actualNode) + { + return document; + } + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); + + ArgumentListSyntax newArgumentList = argumentList; + newArgumentList = newArgumentList.ReplaceNode(conditionNode, SyntaxFactory.Argument(expectedNode).WithAdditionalAnnotations(Formatter.Annotation)); + int insertionIndex = argumentList.Arguments.IndexOf(conditionNode) + 1; + newArgumentList = newArgumentList.WithArguments(newArgumentList.Arguments.Insert(insertionIndex, SyntaxFactory.Argument(actualNode).WithAdditionalAnnotations(Formatter.Annotation))); + + editor.ReplaceNode(argumentList, newArgumentList); + + return editor.GetChangedDocument(); + } + + private static async Task FixAssertMethodForRemoveArgumentModeAsync( + Document document, + IReadOnlyList additionalLocations, + SyntaxNode root, + SimpleNameSyntax simpleNameSyntax, + string properAssertMethodName, + bool needsNullableBoolCast, + CancellationToken cancellationToken) + { + // This doesn't properly handle cases like Assert.AreEqual(message: "My message", expected: true, actual: x) + // The proper handling of this may be Assert.IsTrue(message: "My message", condition: x) + // Or: Assert.IsTrue(x, "My message") + // For now this is not handled. + if (root.FindNode(additionalLocations[0].SourceSpan) is not ArgumentSyntax expectedArgumentToRemove) + { + return document; + } + + if (expectedArgumentToRemove.Parent is not ArgumentListSyntax argumentList) + { + return document; + } + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + FixInvocationMethodName(editor, simpleNameSyntax, properAssertMethodName); + + int argumentIndexToRemove = argumentList.Arguments.IndexOf(expectedArgumentToRemove); + ArgumentListSyntax newArgumentList; + if (additionalLocations.Count > 1 && needsNullableBoolCast && + root.FindNode(additionalLocations[1].SourceSpan) is ArgumentSyntax actualArgument) + { + Compilation compilation = editor.SemanticModel.Compilation; + var castExpression = (CastExpressionSyntax)editor.Generator.CastExpression( + compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(compilation.GetSpecialType(SpecialType.System_Boolean)), + actualArgument.Expression); + newArgumentList = argumentList.WithArguments( + argumentList.Arguments.Replace(actualArgument, SyntaxFactory.Argument(castExpression)).RemoveAt(argumentIndexToRemove)); + } + else + { + newArgumentList = argumentList.WithArguments(argumentList.Arguments.RemoveAt(argumentIndexToRemove)); + } + + editor.ReplaceNode(argumentList, newArgumentList); + + return editor.GetChangedDocument(); + } + + private static void FixInvocationMethodName(DocumentEditor editor, SimpleNameSyntax simpleNameSyntax, string properAssertMethodName) + // NOTE: Switching Assert.IsTrue(x == y) to Assert.AreEqual(x, y) MAY produce an overload resolution error. + // For example, Assert.AreEqual("string", true) will fail the inference for generic argument. + // This is not very common and is currently not handled properly. + // If needed, we can adjust the codefix to account for that case and + // produce a GenericNameSyntax (e.g, AreEqual) instead of IdentifierNameSyntax (e.g, AreEqual). + => editor.ReplaceNode(simpleNameSyntax, SyntaxFactory.IdentifierName(properAssertMethodName)); +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf index db5674fdf1..e756f5cdc7 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf @@ -7,6 +7,16 @@ PÅ™idat [TestMethod] + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + PÅ™esunout podmínÄ›ný přístup v kontrolním výrazu, aby se oddÄ›lila kontrola Assert.IsNotNull + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' ZmÄ›nit přístupnost metody na private @@ -82,6 +92,16 @@ PÅ™idat [TestMethod] + + Use '{0}' + Použít {0} + + + + Use '{0}' + Použít {0} + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf index 50a6abafdd..255d46d530 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf @@ -7,6 +7,16 @@ „[TestMethod]“ hinzufügen + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + Bedingten Zugriff in Assertion auf separate "Assert.IsNotNull"-Überprüfung verschieben + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Methodenzugriff auf „privat“ ändern @@ -82,6 +92,16 @@ „[TestMethod]“ hinzufügen + + Use '{0}' + „{0}“ verwenden + + + + Use '{0}' + "{0}" verwenden + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf index 12e4190d26..c3b124a711 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf @@ -7,6 +7,16 @@ Agregar '[TestMethod]' + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + Mover el acceso condicional en la aserción para separar la comprobación 'Assert.IsNotNull' + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Cambiar la accesibilidad del método a "private" @@ -82,6 +92,16 @@ Agregar '[TestMethod]' + + Use '{0}' + Usar "{0}" + + + + Use '{0}' + Usar "{0}" + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf index 8889521f4a..a7d4398834 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf @@ -7,6 +7,16 @@ Ajouter « [TestMethod] » + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + Déplacer l’accès conditionnel dans l’assertion pour séparer les case activée 'Assert.IsNotNull' + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Remplacer l’accessibilité de la méthode par « privé » @@ -82,6 +92,16 @@ Ajouter « [TestMethod] » + + Use '{0}' + Utiliser « {0} » + + + + Use '{0}' + Utiliser « {0} » + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf index 478c90b34b..80c5591b67 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf @@ -7,6 +7,16 @@ Aggiungi '[TestMethod]' + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + Sposta l'accesso condizionale nell'asserzione per separare il controllo 'Assert.IsNotNull' + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Modifica l'accessibilità del metodo in 'privato' @@ -82,6 +92,16 @@ Aggiungi '[TestMethod]' + + Use '{0}' + Usare ‘{0}’ + + + + Use '{0}' + Usa '{0}' + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf index 21dd08acd7..25ffc7b11d 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf @@ -7,6 +7,16 @@ '[TestMethod]' ã®è¿½åŠ  + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + ã‚¢ã‚µãƒ¼ã‚·ãƒ§ãƒ³å†…ã®æ¡ä»¶ä»˜ãアクセスを個別㮠'Assert.IsNotNull' ãƒã‚§ãƒƒã‚¯ã«ç§»å‹•ã—ã¾ã™ + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' メソッドã®ã‚¢ã‚¯ã‚»ã‚·ãƒ“リティを 'private' ã«å¤‰æ›´ã™ã‚‹ @@ -82,6 +92,16 @@ '[TestMethod]' ã®è¿½åŠ  + + Use '{0}' + '{0}' を使用ã™ã‚‹ + + + + Use '{0}' + '{0}' を使用ã—ã¾ã™ + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf index 06ce6bd200..e61c0984c3 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf @@ -7,6 +7,16 @@ '[TestMethod]' 추가 + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + ì–´ì„¤ì…˜ì˜ ì¡°ê±´ë¶€ 액세스를 'Assert.IsNotNull' 검사 구분하ë„ë¡ ì´ë™í•©ë‹ˆë‹¤. + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' 메서드 접근성 '비공개'로 변경하기 @@ -82,6 +92,16 @@ '[TestMethod]' 추가 + + Use '{0}' + '{0}' 사용 + + + + Use '{0}' + '{0}' 사용 + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf index 0debfd4fc0..74fe8fa536 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf @@ -7,6 +7,16 @@ Dodaj „[TestMethod]†+ + Move conditional access in assertion to separate 'Assert.IsNotNull' check + PrzenieÅ› dostÄ™p warunkowy w asercji do oddzielnego sprawdzenia "Assert.IsNotNull" + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' ZmieÅ„ dostÄ™pność metody na „private†(prywatna) @@ -82,6 +92,16 @@ Dodaj „[TestMethod]†+ + Use '{0}' + Użyj „{0}†+ + + + Use '{0}' + Użyj „{0}†+ + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf index a4cf86f456..36baaed6b6 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf @@ -7,6 +7,16 @@ Adicionar ''[TestMethod]" + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + Mover o acesso condicional na asserção para separar o 'Assert.IsNotNull' marcar + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Alterar a acessibilidade do método para 'privado' @@ -82,6 +92,16 @@ Adicionar ''[TestMethod]" + + Use '{0}' + Usar "{0}" + + + + Use '{0}' + Usar '{0}' + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf index b53cdeb2f9..39003005ba 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf @@ -7,6 +7,16 @@ Добавить "[TestMethod]" + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + ПеремеÑтить уÑловный доÑтуп в утверждении, чтобы разделить "Assert.IsNotNull" проверка + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Изменить доÑтупноÑть метода на "private" @@ -82,6 +92,16 @@ Добавить "[TestMethod]" + + Use '{0}' + ИÑпользовать "{0}" + + + + Use '{0}' + ИÑпользовать "{0}" + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf index d770bdb04f..755034dcf3 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf @@ -7,6 +7,16 @@ '[TestMethod]' ekle + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + Onaylamadaki koÅŸullu eriÅŸimi 'Assert.IsNotNull' denetimine ayırmaya taşı + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' Yöntem eriÅŸilebilirliÄŸini ‘özel’ olarak deÄŸiÅŸtir @@ -82,6 +92,16 @@ '[TestMethod]' ekle + + Use '{0}' + '{0}' kullan + + + + Use '{0}' + '{0}' kullan + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf index 6049a3b42f..1a99753471 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf @@ -7,6 +7,16 @@ 添加“[TestMethod]†+ + Move conditional access in assertion to separate 'Assert.IsNotNull' check + 将断言中的æ¡ä»¶è®¿é—®ç§»åŠ¨åˆ°åˆ†éš” “Assert.IsNotNull†检查 + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' 将方法å¯è®¿é—®æ€§æ›´æ”¹ä¸ºâ€œprivate†@@ -82,6 +92,16 @@ 添加“[TestMethod]†+ + Use '{0}' + 使用“{0}†+ + + + Use '{0}' + 使用“{0}†+ + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf index baf429ed2c..1cba0d3629 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf @@ -7,6 +7,16 @@ 新增 '[TestMethod]' + + Move conditional access in assertion to separate 'Assert.IsNotNull' check + 將判斷æç¤ºä¸­çš„æ¢ä»¶å¼å­˜å–移動到個別的 『Assert.IsNotNull〠檢查 + + + + Use '{0}' + Use '{0}' + + Change method accessibility to 'private' 將方法å”助工具變更為 'private' @@ -82,6 +92,16 @@ 新增 '[TestMethod]' + + Use '{0}' + 使用 '{0}' + + + + Use '{0}' + 使用 '{0}' + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj b/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj index 1055a3ecab..21b9ba6b27 100644 --- a/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj +++ b/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj @@ -36,6 +36,12 @@ + + + + + + diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md index 5ce49d10bb..b794b7364a 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md @@ -1,4 +1,12 @@ -## Release 3.6.0 +## Release 3.7.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +MSTEST0037 | `Usage` | Info | UseProperAssertMethodsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0037) + +## Release 3.6.0 ### New Rules diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index 1f7da0db71..f8b045e159 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -1,8 +1,8 @@ -; Unshipped analyzer release +; Unshipped analyzer release ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md - ### New Rules Rule ID | Category | Severity | Notes --------|----------|----------|------- -MSTEST0037 | `Usage` | Info | UseProperAssertMethodsAnalyzer +MSTEST0038 | Usage | Warning | AvoidAssertAreSameWithValueTypesAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0038) +MSTEST0039 | Usage | Info | UseNewerAssertThrowsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0039) diff --git a/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs index df919b1b36..39a385b64c 100644 --- a/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssemblyCleanupShouldBeValidAnalyzer.cs @@ -41,24 +41,32 @@ public override void Initialize(AnalysisContext context) { INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + INamedTypeSymbol? testContextSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, assemblyCleanupAttributeSymbol, testClassAttributeSymbol, taskSymbol, valueTaskSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, assemblyCleanupAttributeSymbol, testClassAttributeSymbol, taskSymbol, valueTaskSymbol, testContextSymbol, canDiscoverInternals), SymbolKind.Method); } }); } - private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol assemblyCleanupAttributeSymbol, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol? taskSymbol, INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals) + private static void AnalyzeSymbol( + SymbolAnalysisContext context, + INamedTypeSymbol assemblyCleanupAttributeSymbol, + INamedTypeSymbol testClassAttributeSymbol, + INamedTypeSymbol? taskSymbol, + INamedTypeSymbol? valueTaskSymbol, + INamedTypeSymbol? testContextSymbol, + bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (!methodSymbol.IsAssemblyCleanupMethod(assemblyCleanupAttributeSymbol)) + if (!methodSymbol.HasAttribute(assemblyCleanupAttributeSymbol)) { return; } - if (!methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, allowGenericType: false, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out bool isFixable)) + if (!methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, allowGenericType: false, FixtureParameterMode.OptionalTestContext, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out bool isFixable)) { context.ReportDiagnostic(isFixable ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) diff --git a/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs index b547b9b71a..79268d60c0 100644 --- a/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssemblyInitializeShouldBeValidAnalyzer.cs @@ -55,9 +55,9 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo { var methodSymbol = (IMethodSymbol)context.Symbol; - if (methodSymbol.IsAssemblyInitializeMethod(assemblyInitializeAttributeSymbol) + if (methodSymbol.HasAttribute(assemblyInitializeAttributeSymbol) && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, - allowGenericType: false, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out bool isFixable)) + allowGenericType: false, FixtureParameterMode.MustHaveTestContext, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: false, out bool isFixable)) { context.ReportDiagnostic(isFixable ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) diff --git a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs index 6cfe536e07..784f174305 100644 --- a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldAvoidConditionalAccessAnalyzer.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Operations; using MSTest.Analyzers.Helpers; +using MSTest.Analyzers.RoslynAnalyzerHelpers; namespace MSTest.Analyzers; @@ -19,37 +20,38 @@ namespace MSTest.Analyzers; [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class AssertionArgsShouldAvoidConditionalAccessAnalyzer : DiagnosticAnalyzer { - private static readonly ImmutableArray AssertSupportedMethodNames = ImmutableArray.Create([ - "IsTrue", - "IsFalse", - "AreEqual", - "AreNotEqual", - "AreSame", - "AreNotSame" + private static readonly ImmutableArray<(string MethodName, int ArgumentCountToCheck)> AssertSupportedMethodNames = ImmutableArray.Create([ + ("IsTrue", 1), + ("IsFalse", 1), + ("AreEqual", 2), + ("AreNotEqual", 2), + ("AreSame", 2), + ("AreNotSame", 2) ]); - private static readonly ImmutableArray CollectionAssertSupportedMethodNames = ImmutableArray.Create([ - "IsTrue", - "IsFalse", - "AreEqual", - "AreNotEqual", - "AreEquivalent", - "AreNotEquivalent", - "Contains", - "DoesNotContain", - "AllItemsAreNotNull", - "AllItemsAreUnique", - "IsSubsetOf", - "IsNotSubsetOf", - "AllItemsAreInstancesOfType" + private static readonly ImmutableArray<(string MethodName, int ArgumentCountToCheck)> CollectionAssertSupportedMethodNames = ImmutableArray.Create([ + ("AreEqual", 2), + ("AreNotEqual", 2), + ("AreEquivalent", 2), + ("AreNotEquivalent", 2), + // TODO: Is it really bad to have Assert.Contains(myCollection, expression_with_conditional_access)? A codefix seems like may not always yield the correct result for this case. + // TODO: Maybe we should check one argument only (the collection itself) + // Same applies to DoesNotContain + ("Contains", 2), + ("DoesNotContain", 2), + ("AllItemsAreNotNull", 1), + ("AllItemsAreUnique", 1), + ("IsSubsetOf", 2), + ("IsNotSubsetOf", 2), + ("AllItemsAreInstancesOfType", 2) ]); - private static readonly ImmutableArray StringAssertSupportedMethodNames = ImmutableArray.Create([ - "Contains", - "StartsWith", - "EndsWith", - "Matches", - "DoesNotMatch" + private static readonly ImmutableArray<(string MethodName, int ArgumentCountToCheck)> StringAssertSupportedMethodNames = ImmutableArray.Create([ + ("Contains", 2), + ("StartsWith", 2), + ("EndsWith", 2), + ("Matches", 2), + ("DoesNotMatch", 2) ]); private static readonly LocalizableResourceString Title = new(nameof(Resources.AssertionArgsShouldAvoidConditionalAccessTitle), Resources.ResourceManager, typeof(Resources)); @@ -91,50 +93,61 @@ public override void Initialize(AnalysisContext context) }); } - private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, ImmutableArray supportedMethodNames) + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, ImmutableArray<(string MethodName, int ArgumentCountToCheck)> supportedMethodNames) { var invocationOperation = (IInvocationOperation)context.Operation; // This is not an invocation of the expected assertion methods. - if (!supportedMethodNames.Contains(invocationOperation.TargetMethod.Name) + (_, int argumentCountToCheck) = supportedMethodNames.FirstOrDefault(x => x.MethodName == invocationOperation.TargetMethod.Name); + if (argumentCountToCheck == 0 || !SymbolEqualityComparer.Default.Equals(assertSymbol, invocationOperation.TargetMethod.ContainingType) - || !HasAnyConditionalAccessOperationChild(invocationOperation)) + || !HasAnyConditionalAccessOperationChild(invocationOperation, argumentCountToCheck, out Location? additionalLocation)) { return; } - context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule, additionalLocations: ImmutableArray.Create(additionalLocation), properties: null)); } - private static bool HasAnyConditionalAccessOperationChild(IInvocationOperation invocationOperation) + private static bool HasAnyConditionalAccessOperationChild(IInvocationOperation invocationOperation, int argumentCountToCheck, [NotNullWhen(true)] out Location? additionalLocation) { foreach (IArgumentOperation argument in invocationOperation.Arguments) { + if (argument.Parameter is null || argument.Parameter.Ordinal >= argumentCountToCheck) + { + continue; + } + + // Check for conversion operations with conditional access => (s?.Length). + IOperation value = argument.Value.WalkDownConversion(); + // Check for conditional access // a?.b // a?.b?.c // a.b?.c - if (argument.Value is IConditionalAccessOperation { Kind: OperationKind.ConditionalAccess }) + if (value.Kind == OperationKind.ConditionalAccess) { + additionalLocation = value.Syntax.GetLocation(); return true; } // Check for binary operations with conditional access => s?.Length > 1. - if (argument.Value is IBinaryOperation binaryOperation) + if (value is IBinaryOperation binaryOperation) { - if (binaryOperation.LeftOperand.Kind == OperationKind.ConditionalAccess || binaryOperation.RightOperand.Kind == OperationKind.ConditionalAccess) + if (binaryOperation.LeftOperand.Kind == OperationKind.ConditionalAccess) { + additionalLocation = binaryOperation.LeftOperand.Syntax.GetLocation(); + return true; + } + else if (binaryOperation.RightOperand.Kind == OperationKind.ConditionalAccess) + { + additionalLocation = binaryOperation.RightOperand.Syntax.GetLocation(); return true; } - } - - // Check for conversion operations with conditional access => (s?.Length). - if (argument.Value is IConversionOperation { Operand.Kind: OperationKind.ConditionalAccess }) - { - return true; } } + additionalLocation = null; return false; } } diff --git a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs index a675d0fc33..9d7e1f6aee 100644 --- a/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AssertionArgsShouldBePassedInCorrectOrderAnalyzer.cs @@ -57,6 +57,9 @@ public override void Initialize(AnalysisContext context) }); } + private static bool IsConstant(IArgumentOperation argumentOperation) + => argumentOperation.Value.ConstantValue.HasValue; + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) { var invocationOperation = (IInvocationOperation)context.Operation; @@ -69,9 +72,10 @@ private static void AnalyzeOperation(OperationAnalysisContext context, INamedTyp return; } - // If the actual value is a constant or a literal, then the arguments are in the wrong order. - if (actualArgument.Value.Kind == OperationKind.Literal - || actualArgument.Value.ConstantValue.HasValue) + // If the actual value is a constant or a literal and expected is not, then the arguments are in the wrong order. + // Note that we don't report if both are literals or constants, as there is no real fix for this. + // If both are literals or constants, the assert will always pass or always fail. + if (IsConstant(actualArgument) && !IsConstant(expectedArgument)) { context.ReportDiagnostic(invocationOperation.CreateDiagnostic(Rule)); return; diff --git a/src/Analyzers/MSTest.Analyzers/AvoidAssertAreSameWithValueTypesAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidAssertAreSameWithValueTypesAnalyzer.cs new file mode 100644 index 0000000000..afa199bd7a --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AvoidAssertAreSameWithValueTypesAnalyzer.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; +using MSTest.Analyzers.RoslynAnalyzerHelpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0025: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AvoidAssertAreSameWithValueTypesAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.AvoidAssertAreSameWithValueTypesTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AvoidAssertAreSameWithValueTypesMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.AvoidAssertAreSameWithValueTypesDescription), Resources.ResourceManager, typeof(Resources)); + + internal const string ReplacemenyKey = nameof(ReplacemenyKey); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AvoidAssertAreSameWithValueTypesRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + Compilation compilation = context.Compilation; + INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); + if (assertSymbol is not null) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol), OperationKind.Invocation); + } + }); + } + + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) + { + var operation = (IInvocationOperation)context.Operation; + IMethodSymbol targetMethod = operation.TargetMethod; + if ((targetMethod.Name != "AreSame" && targetMethod.Name != "AreNotSame") || + !assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default)) + { + return; + } + + IArgumentOperation? argExpected = operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Ordinal == 0); + IArgumentOperation? argActual = operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Ordinal == 1); + if (argExpected is null || argActual is null) + { + return; + } + + ITypeSymbol? expectedType = argExpected.Value.WalkDownConversion().Type; + ITypeSymbol? actualType = argActual.Value.WalkDownConversion().Type; + + if (expectedType?.IsValueType == true || + actualType?.IsValueType == true) + { + string suggestedReplacement = targetMethod.Name == "AreSame" ? "AreEqual" : "AreNotEqual"; + ImmutableDictionary properties = ImmutableDictionary.Empty + .Add(ReplacemenyKey, suggestedReplacement); + context.ReportDiagnostic(operation.CreateDiagnostic(Rule, properties, targetMethod.Name, suggestedReplacement)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs index 5762ba451c..c0f62dd22d 100644 --- a/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/AvoidExpectedExceptionAttributeAnalyzer.cs @@ -22,6 +22,8 @@ public sealed class AvoidExpectedExceptionAttributeAnalyzer : DiagnosticAnalyzer private static readonly LocalizableResourceString Description = new(nameof(Resources.AvoidExpectedExceptionAttributeDescription), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AvoidExpectedExceptionAttributeMessageFormat), Resources.ResourceManager, typeof(Resources)); + internal const string AllowDerivedTypesKey = nameof(AllowDerivedTypesKey); + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( DiagnosticIds.AvoidExpectedExceptionAttributeRuleId, Title, @@ -59,7 +61,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo // Assert.ThrowsException checks the exact Exception type. So, we cannot offer a fix to ThrowsException if the user sets AllowDerivedTypes to true. context.ReportDiagnostic( allowsDerivedTypes - ? methodSymbol.CreateDiagnostic(Rule, properties: DiagnosticDescriptorHelper.CannotFixProperties) + ? methodSymbol.CreateDiagnostic(Rule, properties: ImmutableDictionary.Empty.Add(AllowDerivedTypesKey, null)) : methodSymbol.CreateDiagnostic(Rule)); } } diff --git a/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs index 46fd2292b6..0e693f68dc 100644 --- a/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/ClassCleanupShouldBeValidAnalyzer.cs @@ -42,22 +42,30 @@ public override void Initialize(AnalysisContext context) INamedTypeSymbol? taskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); INamedTypeSymbol? inheritanceBehaviorSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingInheritanceBehavior); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + INamedTypeSymbol? testContextSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestContext); bool canDiscoverInternals = context.Compilation.CanDiscoverInternals(); context.RegisterSymbolAction( - context => AnalyzeSymbol(context, classCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, inheritanceBehaviorSymbol, testClassAttributeSymbol, canDiscoverInternals), + context => AnalyzeSymbol(context, classCleanupAttributeSymbol, taskSymbol, valueTaskSymbol, inheritanceBehaviorSymbol, testClassAttributeSymbol, testContextSymbol, canDiscoverInternals), SymbolKind.Method); } }); } - private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol classCleanupAttributeSymbol, INamedTypeSymbol? taskSymbol, - INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol? inheritanceBehaviorSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) + private static void AnalyzeSymbol( + SymbolAnalysisContext context, + INamedTypeSymbol classCleanupAttributeSymbol, + INamedTypeSymbol? taskSymbol, + INamedTypeSymbol? valueTaskSymbol, + INamedTypeSymbol? inheritanceBehaviorSymbol, + INamedTypeSymbol testClassAttributeSymbol, + INamedTypeSymbol? testContextSymbol, + bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; bool isInheritanceModeSet = methodSymbol.IsInheritanceModeSet(inheritanceBehaviorSymbol, classCleanupAttributeSymbol); - if (methodSymbol.IsClassInitializeMethod(classCleanupAttributeSymbol) + if (methodSymbol.HasAttribute(classCleanupAttributeSymbol) && (!methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, - allowGenericType: isInheritanceModeSet, testContextSymbol: null, + allowGenericType: isInheritanceModeSet, FixtureParameterMode.OptionalTestContext, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable) || (!isInheritanceModeSet && methodSymbol.ContainingType.IsAbstract) || (isInheritanceModeSet && methodSymbol.ContainingType.IsSealed))) diff --git a/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs index 1b25669209..88181fb2f3 100644 --- a/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/ClassInitializeShouldBeValidAnalyzer.cs @@ -59,9 +59,9 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo { var methodSymbol = (IMethodSymbol)context.Symbol; bool isInheritanceModeSet = methodSymbol.IsInheritanceModeSet(inheritanceBehaviorSymbol, classInitializeAttributeSymbol); - if (methodSymbol.IsClassInitializeMethod(classInitializeAttributeSymbol) + if (methodSymbol.HasAttribute(classInitializeAttributeSymbol) && ((!methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: true, - allowGenericType: isInheritanceModeSet, testContextSymbol, + allowGenericType: isInheritanceModeSet, FixtureParameterMode.MustHaveTestContext, testContextSymbol, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) || (!isInheritanceModeSet && methodSymbol.ContainingType.IsAbstract) || (isInheritanceModeSet && methodSymbol.ContainingType.IsSealed))) diff --git a/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs index 3c63f35202..5c537e169f 100644 --- a/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs @@ -37,8 +37,19 @@ public sealed class DataRowShouldBeValidAnalyzer : DiagnosticAnalyzer internal static readonly DiagnosticDescriptor ArgumentTypeMismatchRule = DataRowOnTestMethodRule .WithMessage(new(nameof(Resources.DataRowShouldBeValidMessageFormat_ArgumentTypeMismatch), Resources.ResourceManager, typeof(Resources))); + internal static readonly DiagnosticDescriptor GenericTypeArgumentNotResolvedRule = DataRowOnTestMethodRule + .WithMessage(new(nameof(Resources.DataRowShouldBeValidMessageFormat_GenericTypeArgumentNotResolved), Resources.ResourceManager, typeof(Resources))); + + internal static readonly DiagnosticDescriptor GenericTypeArgumentConflictingTypesRule = DataRowOnTestMethodRule + .WithMessage(new(nameof(Resources.DataRowShouldBeValidMessageFormat_GenericTypeArgumentConflictingTypes), Resources.ResourceManager, typeof(Resources))); + public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(DataRowOnTestMethodRule); + = ImmutableArray.Create( + DataRowOnTestMethodRule, + ArgumentCountMismatchRule, + ArgumentTypeMismatchRule, + GenericTypeArgumentNotResolvedRule, + GenericTypeArgumentConflictingTypesRule); public override void Initialize(AnalysisContext context) { @@ -168,6 +179,8 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat return; } + AnalyzeGenericMethod(context, dataRowSyntax, methodSymbol, constructorArguments); + // Check constructor argument types match method parameter types. List<(int ConstructorArgumentIndex, int MethodParameterIndex)> typeMismatchIndices = new(); for (int currentArgumentIndex = 0; currentArgumentIndex < constructorArguments.Length; currentArgumentIndex++) @@ -179,7 +192,13 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat } ITypeSymbol? argumentType = constructorArguments[currentArgumentIndex].Type; - ITypeSymbol paramType = GetParameterType(methodSymbol, currentArgumentIndex, constructorArguments.Length); + ITypeSymbol paramType = GetParameterType(methodSymbol.Parameters, currentArgumentIndex, constructorArguments.Length); + if (paramType.TypeKind == TypeKind.TypeParameter) + { + // That means the actual type cannot be determined. We should have issued a separate + // diagnostic for that in GetParameterTypeSubstitutions call above. + continue; + } if (argumentType is not null && !argumentType.IsAssignableTo(paramType, context.Compilation)) { @@ -196,11 +215,84 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat } } - private static ITypeSymbol GetParameterType(IMethodSymbol methodSymbol, int currentArgumentIndex, int argumentsCount) + private static void AnalyzeGenericMethod( + SymbolAnalysisContext context, + SyntaxNode dataRowSyntax, + IMethodSymbol methodSymbol, + ImmutableArray constructorArguments) + { + if (!methodSymbol.IsGenericMethod) + { + return; + } + + var parameterTypesSubstitutions = new Dictionary(SymbolEqualityComparer.Default); + foreach (IParameterSymbol parameter in methodSymbol.Parameters) + { + ITypeSymbol parameterType = parameter.Type; + if (parameterType.Kind != SymbolKind.TypeParameter) + { + continue; + } + + TypedConstant constructorArgument = constructorArguments[parameter.Ordinal]; + if (constructorArgument.Type is null) + { + // That's an error scenario. The compiler will be complaining about something already. + continue; + } + + // This happens for [DataRow(null)] which ends up being resolved + // to DataRow(string?[]? stringArrayData) constructor. + // It also happens with [DataRow((object[]?)null)] which resolves + // to the params object[] constructor + // In this case, the argument is simply "null". + if (constructorArgument.Kind == TypedConstantKind.Array && constructorArgument.IsNull) + { + continue; + } + + object? argumentValue = constructorArgument.Value; + if (argumentValue is null) + { + continue; + } + + if (parameterTypesSubstitutions.TryGetValue(parameterType, out (ITypeSymbol Symbol, Type SystemType) existingType)) + { + if (argumentValue.GetType().IsAssignableTo(existingType.SystemType)) + { + continue; + } + else if (existingType.SystemType.IsAssignableTo(argumentValue.GetType())) + { + parameterTypesSubstitutions[parameterType] = (parameterType, argumentValue.GetType()); + } + else + { + context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic(GenericTypeArgumentConflictingTypesRule, parameterType.Name, existingType.Symbol.Name, constructorArgument.Type.Name)); + } + } + else + { + parameterTypesSubstitutions.Add(parameterType, (constructorArgument.Type, argumentValue.GetType())); + } + } + + foreach (ITypeParameterSymbol typeParameter in methodSymbol.TypeParameters) + { + if (!parameterTypesSubstitutions.ContainsKey(typeParameter)) + { + context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic(GenericTypeArgumentNotResolvedRule, typeParameter.Name)); + } + } + } + + private static ITypeSymbol GetParameterType(ImmutableArray parameters, int currentArgumentIndex, int argumentsCount) { - if (currentArgumentIndex >= methodSymbol.Parameters.Length - 1) + if (currentArgumentIndex >= parameters.Length - 1) { - IParameterSymbol lastParameter = methodSymbol.Parameters[^1]; + IParameterSymbol lastParameter = parameters[^1]; // When last parameter is params, we want to check that the extra arguments match the type of the array if (lastParameter.IsParams) @@ -209,13 +301,13 @@ private static ITypeSymbol GetParameterType(IMethodSymbol methodSymbol, int curr } // When only parameter is array and we have more than one argument, we want to check the array type - if (argumentsCount > 1 && methodSymbol.Parameters.Length == 1 && lastParameter.Type.Kind == SymbolKind.ArrayType) + if (argumentsCount > 1 && parameters.Length == 1 && lastParameter.Type.Kind == SymbolKind.ArrayType) { return ((IArrayTypeSymbol)lastParameter.Type).ElementType; } } - return methodSymbol.Parameters[currentArgumentIndex].Type; + return parameters[currentArgumentIndex].Type; } private static bool IsArgumentCountMismatch(int constructorArgumentsLength, ImmutableArray methodParameters) diff --git a/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs index c92572ebd9..d43862282d 100644 --- a/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/DynamicDataShouldBeValidAnalyzer.cs @@ -20,6 +20,7 @@ public sealed class DynamicDataShouldBeValidAnalyzer : DiagnosticAnalyzer { private const int DynamicDataSourceTypeProperty = 0; private const int DynamicDataSourceTypeMethod = 1; + private const int DynamicDataSourceTypeAutoDetect = 2; private static readonly LocalizableResourceString Title = new(nameof(Resources.DynamicDataShouldBeValidTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString Description = new(nameof(Resources.DynamicDataShouldBeValidDescription), Resources.ResourceManager, typeof(Resources)); @@ -46,6 +47,9 @@ public sealed class DynamicDataShouldBeValidAnalyzer : DiagnosticAnalyzer internal static readonly DiagnosticDescriptor SourceTypeMethodRule = NotTestMethodRule .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_SourceTypeMethod), Resources.ResourceManager, typeof(Resources))); + internal static readonly DiagnosticDescriptor SourceTypeNotPropertyOrMethodRule = NotTestMethodRule + .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyOrMethod), Resources.ResourceManager, typeof(Resources))); + internal static readonly DiagnosticDescriptor MemberMethodRule = NotTestMethodRule .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_MemberMethod), Resources.ResourceManager, typeof(Resources))); @@ -58,8 +62,17 @@ public sealed class DynamicDataShouldBeValidAnalyzer : DiagnosticAnalyzer internal static readonly DiagnosticDescriptor DisplayMethodSignatureRule = NotTestMethodRule .WithMessage(new(nameof(Resources.DynamicDataShouldBeValidMessageFormat_DisplayMethodSignature), Resources.ResourceManager, typeof(Resources))); - public override ImmutableArray SupportedDiagnostics { get; } - = ImmutableArray.Create(NotTestMethodRule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + NotTestMethodRule, + MemberNotFoundRule, + FoundTooManyMembersRule, + SourceTypePropertyRule, + SourceTypeMethodRule, + SourceTypeNotPropertyOrMethodRule, + MemberMethodRule, + MemberTypeRule, + DataMemberSignatureRule, + DisplayMethodSignatureRule); public override void Initialize(AnalysisContext context) { @@ -72,12 +85,11 @@ public override void Initialize(AnalysisContext context) && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDynamicDataAttribute, out INamedTypeSymbol? dynamicDataAttributeSymbol) && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDynamicDataSourceType, out INamedTypeSymbol? dynamicDataSourceTypeSymbol) && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIEnumerable1, out INamedTypeSymbol? ienumerableTypeSymbol) - && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesITuple, out INamedTypeSymbol? itupleTypeSymbol) && context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReflectionMethodInfo, out INamedTypeSymbol? methodInfoTypeSymbol)) { context.RegisterSymbolAction( context => AnalyzeSymbol(context, testMethodAttributeSymbol, dynamicDataAttributeSymbol, dynamicDataSourceTypeSymbol, - ienumerableTypeSymbol, itupleTypeSymbol, methodInfoTypeSymbol), + ienumerableTypeSymbol, methodInfoTypeSymbol), SymbolKind.Method); } }); @@ -85,12 +97,12 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testMethodAttributeSymbol, INamedTypeSymbol dynamicDataAttributeSymbol, INamedTypeSymbol dynamicDataSourceTypeSymbol, INamedTypeSymbol ienumerableTypeSymbol, - INamedTypeSymbol itupleTypeSymbol, INamedTypeSymbol methodInfoTypeSymbol) + INamedTypeSymbol methodInfoTypeSymbol) { var methodSymbol = (IMethodSymbol)context.Symbol; bool isTestMethod = false; - List dynamicDataAttributes = new(); + bool hasDynamicDataAttribute = false; foreach (AttributeData methodAttribute in methodSymbol.GetAttributes()) { // Current method should be a test method or should inherit from the TestMethod attribute. @@ -102,49 +114,77 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo if (SymbolEqualityComparer.Default.Equals(methodAttribute.AttributeClass, dynamicDataAttributeSymbol)) { - dynamicDataAttributes.Add(methodAttribute); + hasDynamicDataAttribute = true; + AnalyzeAttribute(context, methodAttribute, methodSymbol, dynamicDataSourceTypeSymbol, ienumerableTypeSymbol, methodInfoTypeSymbol); } } // Check if attribute is set on a test method. - if (!isTestMethod) - { - if (dynamicDataAttributes.Count > 0) - { - context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotTestMethodRule)); - } - - return; - } - - // Check each data row attribute. - foreach (AttributeData attribute in dynamicDataAttributes) + if (!isTestMethod && hasDynamicDataAttribute) { - AnalyzeAttribute(context, attribute, methodSymbol, dynamicDataSourceTypeSymbol, ienumerableTypeSymbol, itupleTypeSymbol, - methodInfoTypeSymbol); + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(NotTestMethodRule)); } } private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeData attributeData, IMethodSymbol methodSymbol, - INamedTypeSymbol dynamicDataSourceTypeSymbol, INamedTypeSymbol ienumerableTypeSymbol, INamedTypeSymbol itupleTypeSymbol, - INamedTypeSymbol methodInfoTypeSymbol) + INamedTypeSymbol dynamicDataSourceTypeSymbol, INamedTypeSymbol ienumerableTypeSymbol, INamedTypeSymbol methodInfoTypeSymbol) { if (attributeData.ApplicationSyntaxReference?.GetSyntax() is not { } attributeSyntax) { return; } - AnalyzeDataSource(context, attributeData, attributeSyntax, methodSymbol, dynamicDataSourceTypeSymbol, ienumerableTypeSymbol, - itupleTypeSymbol); + AnalyzeDataSource(context, attributeData, attributeSyntax, methodSymbol, dynamicDataSourceTypeSymbol, ienumerableTypeSymbol); AnalyzeDisplayNameSource(context, attributeData, attributeSyntax, methodSymbol, methodInfoTypeSymbol); } + private static (ISymbol? Member, bool AreTooMany) TryGetMember(INamedTypeSymbol declaringType, string memberName) + { + INamedTypeSymbol? currentType = declaringType; + while (currentType is not null) + { + (ISymbol? Member, bool AreTooMany) result = TryGetMemberCore(currentType, memberName); + if (result.Member is not null || result.AreTooMany) + { + return result; + } + + // Only continue to look at base types if the member is not found on the current type and we are not hit by "too many methods" rule. + currentType = currentType.BaseType; + } + + return (null, false); + + static (ISymbol? Member, bool AreTooMany) TryGetMemberCore(INamedTypeSymbol declaringType, string memberName) + { + // If we cannot find the member on the given type, report a diagnostic. + if (declaringType.GetMembers(memberName) is { Length: 0 } potentialMembers) + { + return (null, false); + } + + ISymbol? potentialProperty = potentialMembers.FirstOrDefault(m => m.Kind == SymbolKind.Property); + if (potentialProperty is not null) + { + return (potentialProperty, false); + } + + IEnumerable candidateMethods = potentialMembers.Where(m => m.Kind == SymbolKind.Method); + if (candidateMethods.Count() > 1) + { + // If there are multiple methods with the same name, report a diagnostic. This is not a supported scenario. + return (null, true); + } + + return (candidateMethods.FirstOrDefault() ?? potentialMembers[0], false); + } + } + private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeData attributeData, SyntaxNode attributeSyntax, - IMethodSymbol methodSymbol, INamedTypeSymbol dynamicDataSourceTypeSymbol, INamedTypeSymbol ienumerableTypeSymbol, - INamedTypeSymbol itupleTypeSymbol) + IMethodSymbol methodSymbol, INamedTypeSymbol dynamicDataSourceTypeSymbol, INamedTypeSymbol ienumerableTypeSymbol) { string? memberName = null; - int dataSourceType = 0; + int dataSourceType = DynamicDataSourceTypeAutoDetect; INamedTypeSymbol declaringType = methodSymbol.ContainingType; foreach (TypedConstant argument in attributeData.ConstructorArguments) { @@ -175,34 +215,46 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa return; } - // If we cannot find the member on the given type, report a diagnostic. - if (declaringType.GetMembers(memberName) is { Length: 0 } potentialMembers) - { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotFoundRule, declaringType.Name, memberName)); - return; - } + (ISymbol? member, bool areTooMany) = TryGetMember(declaringType, memberName); - // If there are multiple members with the same name, report a diagnostic. This is not a supported scenario. - if (potentialMembers.Length > 1) + if (areTooMany) { + // If there are multiple methods with the same name and all are parameterless, report a diagnostic. This is not a supported scenario. + // Note: This is likely to happen only when they differ in arity (for example, one is non-generic and the other is generic). context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(FoundTooManyMembersRule, declaringType.Name, memberName)); return; } - ISymbol member = potentialMembers[0]; - - // If the member is a property and the data source type is not set to property, report a diagnostic. - if (member.Kind == SymbolKind.Property && dataSourceType is not DynamicDataSourceTypeProperty) + if (member is null) { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypePropertyRule, declaringType.Name, memberName)); + // If we cannot find the member on the given type, report a diagnostic. + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotFoundRule, declaringType.Name, memberName)); return; } - // If the member is a method and the data source type is not set to method, report a diagnostic. - if (member.Kind == SymbolKind.Method && dataSourceType is not DynamicDataSourceTypeMethod) + switch (member.Kind) { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypeMethodRule, declaringType.Name, memberName)); - return; + case SymbolKind.Property: + // If the member is a property and the data source type is not set to property or auto detect, report a diagnostic. + if (dataSourceType is not (DynamicDataSourceTypeProperty or DynamicDataSourceTypeAutoDetect)) + { + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypePropertyRule, declaringType.Name, memberName)); + return; + } + + break; + case SymbolKind.Method: + // If the member is a method and the data source type is not set to method or auto detect, report a diagnostic. + if (dataSourceType is not (DynamicDataSourceTypeMethod or DynamicDataSourceTypeAutoDetect)) + { + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypeMethodRule, declaringType.Name, memberName)); + return; + } + + break; + default: + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(SourceTypeNotPropertyOrMethodRule, declaringType.Name, memberName)); + return; } if (!member.IsStatic) @@ -221,28 +273,12 @@ private static void AnalyzeDataSource(SymbolAnalysisContext context, AttributeDa // Validate member return type. ITypeSymbol? memberTypeSymbol = member.GetMemberType(); - if (memberTypeSymbol is INamedTypeSymbol memberNamedType) + if (memberTypeSymbol is INamedTypeSymbol memberNamedType + && (!SymbolEqualityComparer.Default.Equals(memberNamedType.ConstructedFrom, ienumerableTypeSymbol) + || memberNamedType.TypeArguments.Length != 1)) { - if (!SymbolEqualityComparer.Default.Equals(memberNamedType.ConstructedFrom, ienumerableTypeSymbol) - || memberNamedType.TypeArguments.Length != 1) - { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); - return; - } - - ITypeSymbol collectionBoundType = memberNamedType.TypeArguments[0]; - if (!collectionBoundType.Inherits(itupleTypeSymbol) - && collectionBoundType is not IArrayTypeSymbol) - { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); - } - } - else if (memberTypeSymbol is IArrayTypeSymbol arrayType) - { - if (arrayType.ElementType is not IArrayTypeSymbol) - { - context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); - } + context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberTypeRule, declaringType.Name, memberName)); + return; } } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/ApplicationStateGuard.cs b/src/Analyzers/MSTest.Analyzers/Helpers/ApplicationStateGuard.cs index 39acf92967..bb088f14cb 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/ApplicationStateGuard.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/ApplicationStateGuard.cs @@ -1,12 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.CompilerServices; - namespace MSTest.Analyzers.Helpers; internal static class ApplicationStateGuard { - public static InvalidOperationException Unreachable([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0) + public static UnreachableException Unreachable([CallerFilePath] string? path = null, [CallerLineNumber] int line = 0) => new($"This program location is thought to be unreachable. File='{path}' Line={line}"); } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 79c269bbe8..60450d9c51 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -42,4 +42,6 @@ internal static class DiagnosticIds public const string UseDeploymentItemWithTestMethodOrTestClassRuleId = "MSTEST0035"; public const string DoNotUseShadowingRuleId = "MSTEST0036"; public const string UseProperAssertMethodsRuleId = "MSTEST0037"; + public const string AvoidAssertAreSameWithValueTypesRuleId = "MSTEST0038"; + public const string UseNewerAssertThrowsRuleId = "MSTEST0039"; } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/FixtureParameterMode.cs b/src/Analyzers/MSTest.Analyzers/Helpers/FixtureParameterMode.cs new file mode 100644 index 0000000000..e7ff36eade --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/FixtureParameterMode.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers.Helpers; + +internal enum FixtureParameterMode +{ + /// + /// Indicates that there must not be a TestContext parameter. + /// + MustNotHaveTestContext, + + /// + /// Indicates that a TestContext parameter is mandatory. + /// + MustHaveTestContext, + + /// + /// Indicates that a TestContext parameter is optional. + /// + OptionalTestContext, +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs b/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs index 4f79378743..d4846ee54e 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/FixtureUtils.cs @@ -12,26 +12,9 @@ namespace MSTest.Analyzers.Helpers; internal static class FixtureUtils { - public static bool IsAssemblyInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol assemblyInitializeAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyInitializeAttributeSymbol)); - - public static bool IsAssemblyCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol assemblyCleanupAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, assemblyCleanupAttributeSymbol)); - - public static bool IsClassInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol classInitializeAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classInitializeAttributeSymbol)); - - public static bool IsClassCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol classCleanupAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupAttributeSymbol)); - - public static bool IsTestInitializeMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol testInitializeAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testInitializeAttributeSymbol)); - - public static bool IsTestCleanupMethod(this IMethodSymbol methodSymbol, INamedTypeSymbol testCleanupAttributeSymbol) - => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, testCleanupAttributeSymbol)); - public static bool HasValidFixtureMethodSignature(this IMethodSymbol methodSymbol, INamedTypeSymbol? taskSymbol, INamedTypeSymbol? valueTaskSymbol, bool canDiscoverInternals, bool shouldBeStatic, bool allowGenericType, + FixtureParameterMode fixtureParameterMode, INamedTypeSymbol? testContextSymbol, INamedTypeSymbol testClassAttributeSymbol, bool fixtureAllowInheritedTestClass, out bool isFixable) { isFixable = false; @@ -60,7 +43,7 @@ public static bool HasValidFixtureMethodSignature(this IMethodSymbol methodSymbo return !methodSymbol.IsGenericMethod && methodSymbol.IsStatic == shouldBeStatic && !methodSymbol.IsAbstract - && HasCorrectParameters(methodSymbol, testContextSymbol) + && HasCorrectParameters(methodSymbol, fixtureParameterMode, testContextSymbol) && methodSymbol.IsPublicAndHasCorrectResultantVisibility(canDiscoverInternals) && HasValidReturnType(methodSymbol, taskSymbol, valueTaskSymbol); } @@ -125,10 +108,22 @@ public static bool IsInheritanceModeSet(this IMethodSymbol methodSymbol, INamedT return false; } - private static bool HasCorrectParameters(IMethodSymbol methodSymbol, INamedTypeSymbol? testContextSymbol) - => testContextSymbol is null - ? methodSymbol.Parameters.Length == 0 - : methodSymbol.Parameters.Length == 1 && SymbolEqualityComparer.Default.Equals(methodSymbol.Parameters[0].Type, testContextSymbol); + private static bool HasCorrectParameters(IMethodSymbol methodSymbol, FixtureParameterMode fixtureParameterMode, INamedTypeSymbol? testContextSymbol) + { + return fixtureParameterMode switch + { + FixtureParameterMode.MustNotHaveTestContext => DoesNotHaveTestContext(methodSymbol), + FixtureParameterMode.MustHaveTestContext => HasTestContext(methodSymbol, testContextSymbol), + FixtureParameterMode.OptionalTestContext => DoesNotHaveTestContext(methodSymbol) || HasTestContext(methodSymbol, testContextSymbol), + _ => throw ApplicationStateGuard.Unreachable(), + }; + + static bool DoesNotHaveTestContext(IMethodSymbol methodSymbol) + => methodSymbol.Parameters.Length == 0; + + static bool HasTestContext(IMethodSymbol methodSymbol, INamedTypeSymbol? testContextSymbol) + => testContextSymbol is not null && methodSymbol.Parameters.Length == 1 && SymbolEqualityComparer.Default.Equals(methodSymbol.Parameters[0].Type, testContextSymbol); + } private static bool HasValidReturnType(IMethodSymbol methodSymbol, INamedTypeSymbol? taskSymbol, INamedTypeSymbol? valueTaskSymbol) => methodSymbol is { ReturnsVoid: true, IsAsync: false } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs index 6d58c445e5..4ae6b7faff 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/IMethodSymbolExtensions.cs @@ -9,6 +9,9 @@ namespace MSTest.Analyzers.Helpers; internal static class IMethodSymbolExtensions { + public static bool HasAttribute(this IMethodSymbol methodSymbol, INamedTypeSymbol attribute) + => methodSymbol.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attribute)); + public static bool IsPublicAndHasCorrectResultantVisibility(this IMethodSymbol methodSymbol, bool canDiscoverInternals) { // Even when we allow discovering internals, MSTest engine only supports the method being declared as public. diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs index 86bd502f22..900066e322 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs @@ -42,11 +42,10 @@ internal static class WellKnownTypeNames public const string System = "System"; public const string SystemCollectionsGenericIEnumerable1 = "System.Collections.Generic.IEnumerable`1"; public const string SystemDescriptionAttribute = "System.ComponentModel.DescriptionAttribute"; + public const string SystemFunc1 = "System.Func`1"; public const string SystemIAsyncDisposable = "System.IAsyncDisposable"; public const string SystemIDisposable = "System.IDisposable"; - public const string SystemNullable = "System.Nullable`1"; public const string SystemReflectionMethodInfo = "System.Reflection.MethodInfo"; - public const string SystemRuntimeCompilerServicesITuple = "System.Runtime.CompilerServices.ITuple"; public const string SystemThreadingTasksTask = "System.Threading.Tasks.Task"; public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; public const string SystemThreadingTasksValueTask = "System.Threading.Tasks.ValueTask"; diff --git a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj index b7ded067c4..217d171183 100644 --- a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj +++ b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj @@ -4,7 +4,6 @@ netstandard2.0 false true - Latest *$(MSBuildProjectFile)* diff --git a/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs b/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs index edaaf60a89..e895647dbc 100644 --- a/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs +++ b/src/Analyzers/MSTest.Analyzers/NonNullableReferenceNotInitializedSuppressor.cs @@ -15,7 +15,9 @@ namespace MSTest.Analyzers; /// /// MSTEST0028: . /// -[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +#pragma warning disable RS1004 // Recommend adding language support to diagnostic analyzer - This suppressor is not valid for VB +[DiagnosticAnalyzer(LanguageNames.CSharp)] +#pragma warning restore RS1004 // Recommend adding language support to diagnostic analyzer public sealed class NonNullableReferenceNotInitializedSuppressor : DiagnosticSuppressor { // CS8618: Non-nullable variable must contain a non-null value when exiting constructor. Consider declaring it as nullable. diff --git a/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs index ec11b318ed..921849d077 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferAssertFailOverAlwaysFalseConditionsAnalyzer.cs @@ -32,6 +32,8 @@ private enum EqualityStatus private const string ActualParameterName = "actual"; private const string ConditionParameterName = "condition"; private const string ValueParameterName = "value"; + private const string MessageParameterName = "message"; + private const string ParametersParameterName = "parameters"; private static readonly LocalizableResourceString Title = new(nameof(Resources.PreferAssertFailOverAlwaysFalseConditionsTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.PreferAssertFailOverAlwaysFalseConditionsMessageFormat), Resources.ResourceManager, typeof(Resources)); @@ -57,26 +59,57 @@ public override void Initialize(AnalysisContext context) { Compilation compilation = context.Compilation; INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); - INamedTypeSymbol? nullableSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNullable); if (assertSymbol is not null) { - context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, nullableSymbol), OperationKind.Invocation); + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol), OperationKind.Invocation); } }); } - private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, INamedTypeSymbol? nullableSymbol) + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) { var operation = (IInvocationOperation)context.Operation; if (assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default) && - IsAlwaysFalse(operation, nullableSymbol)) + IsAlwaysFalse(operation)) { - context.ReportDiagnostic(operation.CreateDiagnostic(Rule, operation.TargetMethod.Name)); + context.ReportDiagnostic(operation.CreateDiagnostic(Rule, GetAdditionalLocations(operation), properties: null, operation.TargetMethod.Name)); } } - private static bool IsAlwaysFalse(IInvocationOperation operation, INamedTypeSymbol? nullableSymbol) + private static ImmutableArray GetAdditionalLocations(IInvocationOperation operation) + { + IArgumentOperation? messageArg = operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Name == MessageParameterName); + if (messageArg is null) + { + return ImmutableArray.Empty; + } + + IArgumentOperation? parametersArg = operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Name == ParametersParameterName); + if (parametersArg is null) + { + return ImmutableArray.Create(messageArg.Syntax.GetLocation()); + } + + if (parametersArg.ArgumentKind == ArgumentKind.ParamArray) + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + builder.Add(messageArg.Syntax.GetLocation()); + if (parametersArg.Value is IArrayCreationOperation { Initializer.ElementValues: { } elements }) + { + foreach (IOperation element in elements) + { + builder.Add(element.Syntax.GetLocation()); + } + } + + return builder.ToImmutable(); + } + + return ImmutableArray.Create(messageArg.Syntax.GetLocation(), parametersArg.Syntax.GetLocation()); + } + + private static bool IsAlwaysFalse(IInvocationOperation operation) => operation.TargetMethod.Name switch { "IsTrue" => GetConditionArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: false } }, @@ -84,16 +117,16 @@ private static bool IsAlwaysFalse(IInvocationOperation operation, INamedTypeSymb "AreEqual" => GetEqualityStatus(operation, ExpectedParameterName) == EqualityStatus.NotEqual, "AreNotEqual" => GetEqualityStatus(operation, NotExpectedParameterName) == EqualityStatus.Equal, "IsNotNull" => GetValueArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: null } }, - "IsNull" => GetValueArgument(operation) is { } valueArgumentOperation && IsNotNullableType(valueArgumentOperation, nullableSymbol), + "IsNull" => GetValueArgument(operation) is { } valueArgumentOperation && IsNotNullableType(valueArgumentOperation), _ => false, }; - private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation, INamedTypeSymbol? nullableSymbol) + private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation) { ITypeSymbol? valueArgType = valueArgumentOperation.Value.GetReferencedMemberOrLocalOrParameter().GetReferencedMemberOrLocalOrParameter(); return valueArgType is not null && valueArgType.NullableAnnotation == NullableAnnotation.NotAnnotated - && !SymbolEqualityComparer.IncludeNullability.Equals(valueArgType.OriginalDefinition, nullableSymbol); + && valueArgType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T; } private static IArgumentOperation? GetArgumentWithName(IInvocationOperation operation, string name) diff --git a/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs index 61d03d2294..5895772f5d 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs @@ -51,7 +51,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo { var methodSymbol = (IMethodSymbol)context.Symbol; - if (methodSymbol.IsTestInitializeMethod(testInitAttributeSymbol) && methodSymbol.ReturnsVoid) + if (methodSymbol.HasAttribute(testInitAttributeSymbol) && methodSymbol.ReturnsVoid) { context.ReportDiagnostic(methodSymbol.CreateDiagnostic(Rule)); } diff --git a/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs index 404d4cf7ca..fa126178e5 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferDisposeOverTestCleanupAnalyzer.cs @@ -53,7 +53,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo { var methodSymbol = (IMethodSymbol)context.Symbol; - if (methodSymbol.IsTestCleanupMethod(testCleanupAttributeSymbol)) + if (methodSymbol.HasAttribute(testCleanupAttributeSymbol)) { // We want to report only if the TestCleanup method returns void or if IAsyncDisposable is available. if (iasyncDisposableSymbol is not null diff --git a/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs index 1e22fec61a..b63e66970d 100644 --- a/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/PreferTestCleanupOverDisposeAnalyzer.cs @@ -40,22 +40,28 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable, out INamedTypeSymbol? idisposableSymbol)) + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable, out INamedTypeSymbol? idisposableSymbol) && + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)) { INamedTypeSymbol? iasyncDisposableSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIAsyncDisposable); INamedTypeSymbol? valueTaskSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); - context.RegisterSymbolAction(context => AnalyzeSymbol(context, idisposableSymbol, iasyncDisposableSymbol, valueTaskSymbol), SymbolKind.Method); + context.RegisterSymbolAction(context => AnalyzeSymbol(context, testClassAttributeSymbol, idisposableSymbol, iasyncDisposableSymbol, valueTaskSymbol), SymbolKind.Method); } }); } - private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol idisposableSymbol, INamedTypeSymbol? iasyncDisposableSymbol, + private static void AnalyzeSymbol( + SymbolAnalysisContext context, + INamedTypeSymbol testClassAttributeSymbol, + INamedTypeSymbol idisposableSymbol, + INamedTypeSymbol? iasyncDisposableSymbol, INamedTypeSymbol? valueTaskSymbol) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (methodSymbol.IsAsyncDisposeImplementation(iasyncDisposableSymbol, valueTaskSymbol) - || methodSymbol.IsDisposeImplementation(idisposableSymbol)) + if (methodSymbol.ContainingType.GetAttributes().Any(x => x.AttributeClass.Inherits(testClassAttributeSymbol)) && + (methodSymbol.IsAsyncDisposeImplementation(iasyncDisposableSymbol, valueTaskSymbol) + || methodSymbol.IsDisposeImplementation(idisposableSymbol))) { context.ReportDiagnostic(methodSymbol.CreateDiagnostic(Rule)); } diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt index 681365b958..ff8980d5af 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable Analyzer.Utilities.WellKnownTypeProvider Analyzer.Utilities.WellKnownTypeProvider.Compilation.get -> Microsoft.CodeAnalysis.Compilation! Analyzer.Utilities.WellKnownTypeProvider.GetOrCreateTypeByMetadataName(string! fullTypeName) -> Microsoft.CodeAnalysis.INamedTypeSymbol? diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt index ab058de62d..3f880fefe8 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ -#nullable enable +#nullable enable +MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer +MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.AvoidAssertAreSameWithValueTypesAnalyzer() -> void +override MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs index f41b3eb2a8..96683c893f 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs @@ -68,12 +68,10 @@ internal Resources() { ///-it should not be 'async void' ///-it should not be a special method (finalizer, operator...). ///-it should not be generic - ///-it should not take any parameter + ///-it should either not take any parameter, or take a single parameter of type 'TestContext' ///-return type should be 'void', 'Task' or 'ValueTask' /// - ///The type declaring these methods should also respect the following rules: - ///-The type should be a class - ///-The class should [rest of string was truncated]";. + ///The type declaring these methods should also respect the followi [rest of string was truncated]";. /// internal static string AssemblyCleanupShouldBeValidDescription { get { @@ -183,7 +181,34 @@ internal static string AssertionArgsShouldBePassedInCorrectOrderTitle { } /// - /// Looks up a localized string similar to Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption.. + /// Looks up a localized string similar to Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass.. + /// + internal static string AvoidAssertAreSameWithValueTypesDescription { + get { + return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use '{0}' instead of '{1}' when comparing value types. + /// + internal static string AvoidAssertAreSameWithValueTypesMessageFormat { + get { + return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types. + /// + internal static string AvoidAssertAreSameWithValueTypesTitle { + get { + return ResourceManager.GetString("AvoidAssertAreSameWithValueTypesTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception.. /// internal static string AvoidExpectedExceptionAttributeDescription { get { @@ -192,7 +217,7 @@ internal static string AvoidExpectedExceptionAttributeDescription { } /// - /// Looks up a localized string similar to Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]'. + /// Looks up a localized string similar to Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]'. /// internal static string AvoidExpectedExceptionAttributeMessageFormat { get { @@ -217,9 +242,9 @@ internal static string AvoidExpectedExceptionAttributeTitle { ///-it should not be 'async void' ///-it should not be a special method (finalizer, operator...). ///-it should not be generic - ///-it should not take any parameter + ///-it should either not take any parameter, or take a single parameter of type 'TestContext' ///-return type should be 'void', 'Task' or 'ValueTask' - ///-'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be spec [rest of string was truncated]";. + ///-'InheritanceBehavior.B [rest of string was truncated]";. /// internal static string ClassCleanupShouldBeValidDescription { get { @@ -311,6 +336,24 @@ internal static string DataRowShouldBeValidMessageFormat_ArgumentTypeMismatch { } } + /// + /// Looks up a localized string similar to Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'.. + /// + internal static string DataRowShouldBeValidMessageFormat_GenericTypeArgumentConflictingTypes { + get { + return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_GenericTypeArgumentConflictingTypes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type of the generic parameter '{0}' could not be inferred.. + /// + internal static string DataRowShouldBeValidMessageFormat_GenericTypeArgumentNotResolved { + get { + return ResourceManager.GetString("DataRowShouldBeValidMessageFormat_GenericTypeArgumentNotResolved", resourceCulture); + } + } + /// /// Looks up a localized string similar to DataRow should only be set on a test method. /// @@ -486,7 +529,7 @@ internal static string DynamicDataShouldBeValidMessageFormat_OnTestMethod { } /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method'. + /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended). /// internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeMethod { get { @@ -495,7 +538,16 @@ internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeMethod { } /// - /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property'. + /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported.. + /// + internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyOrMethod { + get { + return ResourceManager.GetString("DynamicDataShouldBeValidMessageFormat_SourceTypeNotPropertyOrMethod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended). /// internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeProperty { get { @@ -504,7 +556,7 @@ internal static string DynamicDataShouldBeValidMessageFormat_SourceTypeProperty } /// - /// Looks up a localized string similar to '[DynamicDta]' member '{0}.{1}' is found more than once. + /// Looks up a localized string similar to '[DynamicData]' member '{0}.{1}' is found more than once. /// internal static string DynamicDataShouldBeValidMessageFormat_TooManyMembers { get { @@ -852,11 +904,11 @@ internal static string TestInitializeShouldBeValidTitle { /// Looks up a localized string similar to Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: ///- it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) ///- it should not be 'static' - ///- it should not be generic + ///- it should may be generic as long as type parameters can be inferred and argument types are compatible ///- it should not be 'abstract' ///- return type should be 'void', 'Task' or 'ValueTask' ///- it should not be 'async void' - ///- it should not be a special method (finalizer, operator...).. + ///- it should not be a special me [rest of string was truncated]";. /// internal static string TestMethodShouldBeValidDescription { get { @@ -1017,6 +1069,24 @@ internal static string UseDeploymentItemWithTestMethodOrTestClassTitle { } } + /// + /// Looks up a localized string similar to Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException'. + /// + internal static string UseNewerAssertThrowsMessageFormat { + get { + return ResourceManager.GetString("UseNewerAssertThrowsMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use newer methods to assert exceptions. + /// + internal static string UseNewerAssertThrowsTitle { + get { + return ResourceManager.GetString("UseNewerAssertThrowsTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'.. /// diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index c85ca03484..470293fe98 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -125,7 +125,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -181,10 +181,10 @@ The type declaring these methods should also respect the following rules: Assertion arguments should be passed in the correct order - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' Avoid '[ExpectedException]' @@ -197,7 +197,7 @@ The type declaring these methods should also respect the following rules: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -399,7 +399,7 @@ The type declaring these methods should also respect the following rules: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -490,10 +490,10 @@ The type declaring these methods should also respect the following rules: '[DynamicData]' member '{0}.{1}' cannot be found - '[DynamicDta]' member '{0}.{1}' is found more than once + '[DynamicData]' member '{0}.{1}' is found more than once - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' member '{0}.{1}' should be a method @@ -502,7 +502,7 @@ The type declaring these methods should also respect the following rules: '[DynamicData]' referenced member '{0}.{1}' should return 'IEnumerable<object[]>', 'IEnumerable<Tuple>` or 'IEnumerable<ValueTuple>' - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) '[DynamicData]' display name method '{0}.{1}' signature is invalid @@ -534,4 +534,28 @@ The type declaring these methods should also respect the following rules: Use 'Assert.{0}' instead of 'Assert.{1}' - \ No newline at end of file + + The type of the generic parameter '{0}' could not be inferred. + + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + + + Use newer methods to assert exceptions + + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + + + Use '{0}' instead of '{1}' when comparing value types + + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + + diff --git a/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs index 662877724d..1fe7925b0b 100644 --- a/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/ReviewAlwaysTrueAssertConditionAnalyzer.cs @@ -57,25 +57,24 @@ public override void Initialize(AnalysisContext context) { Compilation compilation = context.Compilation; INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); - INamedTypeSymbol? nullableSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNullable); if (assertSymbol is not null) { - context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol, nullableSymbol), OperationKind.Invocation); + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol), OperationKind.Invocation); } }); } - private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol, INamedTypeSymbol? nullableSymbol) + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) { var operation = (IInvocationOperation)context.Operation; if (assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default) && - IsAlwaysTrue(operation, nullableSymbol)) + IsAlwaysTrue(operation)) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); } } - private static bool IsAlwaysTrue(IInvocationOperation operation, INamedTypeSymbol? nullableSymbol) + private static bool IsAlwaysTrue(IInvocationOperation operation) => operation.TargetMethod.Name switch { "IsTrue" => GetConditionArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: true } }, @@ -83,16 +82,16 @@ private static bool IsAlwaysTrue(IInvocationOperation operation, INamedTypeSymbo "AreEqual" => GetEqualityStatus(operation, ExpectedParameterName) == EqualityStatus.Equal, "AreNotEqual" => GetEqualityStatus(operation, NotExpectedParameterName) == EqualityStatus.NotEqual, "IsNull" => GetValueArgument(operation) is { Value.ConstantValue: { HasValue: true, Value: null } }, - "IsNotNull" => GetValueArgument(operation) is { } valueArgumentOperation && IsNotNullableType(valueArgumentOperation, nullableSymbol), + "IsNotNull" => GetValueArgument(operation) is { } valueArgumentOperation && IsNotNullableType(valueArgumentOperation), _ => false, }; - private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation, INamedTypeSymbol? nullableSymbol) + private static bool IsNotNullableType(IArgumentOperation valueArgumentOperation) { ITypeSymbol? valueArgType = valueArgumentOperation.Value.GetReferencedMemberOrLocalOrParameter().GetReferencedMemberOrLocalOrParameter(); return valueArgType is not null && valueArgType.NullableAnnotation == NullableAnnotation.NotAnnotated - && !SymbolEqualityComparer.IncludeNullability.Equals(valueArgType.OriginalDefinition, nullableSymbol); + && valueArgType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T; } private static IArgumentOperation? GetArgumentWithName(IInvocationOperation operation, string name) diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs index 81c3fb5952..c066d9e519 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. -using System.Collections; using System.Collections.Immutable; -using System.Diagnostics; #pragma warning disable CA1000 // Do not declare static members on generic types @@ -324,9 +322,9 @@ internal Dictionary> ToDictionary(Func keySelector var dictionary = new Dictionary>(accumulator.Count, comparer); // freeze - foreach (KeyValuePair> pair in accumulator) + foreach ((K? key, ArrayBuilder? value) in accumulator) { - dictionary.Add(pair.Key, pair.Value.ToImmutableAndFree()); + dictionary.Add(key, value.ToImmutableAndFree()); } return dictionary; diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs index d3fbaf64d1..87250620aa 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs @@ -7,7 +7,7 @@ namespace Analyzer.Utilities; /// Acts as a good alternative to /// when the cached value has a cyclic reference to the key preventing early garbage collection of entries. /// -internal class BoundedCacheWithFactory +internal sealed class BoundedCacheWithFactory where TKey : class { // Bounded weak reference cache. diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/CompilationExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/CompilationExtensions.cs index 283a392d83..e740b609c9 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/CompilationExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/CompilationExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.CodeAnalysis; namespace Analyzer.Utilities.Extensions; diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs index a83e870acc..afb5ae2ab1 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/DiagnosticExtensions.cs @@ -1,14 +1,13 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Collections.Immutable; -using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Analyzer.Utilities.Extensions; -internal static class FixtureUtils +internal static class DiagnosticExtensions { public static Diagnostic CreateDiagnostic( this SyntaxNode node, diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IMethodSymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IMethodSymbolExtensions.cs index 949e91704d..617e8e255c 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IMethodSymbolExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IMethodSymbolExtensions.cs @@ -2,8 +2,6 @@ #nullable disable warnings -using System.Diagnostics.CodeAnalysis; - using Microsoft.CodeAnalysis; using MSTest.Analyzers.Helpers; @@ -70,20 +68,24 @@ public static bool IsDisposeImplementation([NotNullWhen(returnValue: true)] this /// public static bool IsAsyncDisposeImplementation([NotNullWhen(returnValue: true)] this IMethodSymbol? method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? iAsyncDisposable, [NotNullWhen(returnValue: true)] INamedTypeSymbol? valueTaskType) { - if (method == null) + while (true) { - return false; - } + if (method == null) + { + return false; + } - if (method.IsOverride) - { - return method.OverriddenMethod.IsAsyncDisposeImplementation(iAsyncDisposable, valueTaskType); - } + if (method.IsOverride) + { + method = method.OverriddenMethod; + continue; + } - // Identify the implementor of IAsyncDisposable.Dispose in the given method's containing type and check - // if it is the given method. - return SymbolEqualityComparer.Default.Equals(method.ReturnType, valueTaskType) && - method.Parameters.IsEmpty && - method.IsImplementationOfInterfaceMethod(null, iAsyncDisposable, "DisposeAsync"); + // Identify the implementor of IAsyncDisposable.Dispose in the given method's containing type and check + // if it is the given method. + return SymbolEqualityComparer.Default.Equals(method.ReturnType, valueTaskType) && + method.Parameters.IsEmpty && + method.IsImplementationOfInterfaceMethod(null, iAsyncDisposable, "DisposeAsync"); + } } } diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs index 1584ad6d0f..b4f48fb09f 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/IOperationExtensions.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Text; - using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs index 86ee2ec619..a76b4c0dc4 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ITypeSymbolExtensions.cs @@ -2,8 +2,6 @@ #nullable disable warnings -using System.Diagnostics.CodeAnalysis; - using Microsoft.CodeAnalysis; namespace Analyzer.Utilities.Extensions; @@ -87,4 +85,10 @@ public static bool DerivesFrom([NotNullWhen(returnValue: true)] this ITypeSymbol return false; } + + public static bool IsNullableValueType([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol != null && typeSymbol.IsValueType && typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T; + + public static bool IsNullableOfBoolean([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol.IsNullableValueType() && ((INamedTypeSymbol)typeSymbol).TypeArguments[0].SpecialType == SpecialType.System_Boolean; } diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs index 6a3a7e88aa..772b399e98 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs @@ -10,8 +10,6 @@ // #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug. // #endif -using System.Diagnostics; - #if DETECT_LEAKS using System.Runtime.CompilerServices; diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs index 2f753bdefb..0fe718cff9 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs @@ -4,7 +4,6 @@ #pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved (not all builds have all types) using System; -using System.Diagnostics; namespace Roslyn.Utilities { diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs index 8adfb81f78..dfa3781100 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. -using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Diagnostics; #pragma warning disable CA1000 // Do not declare static members on generic types diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs index 8a70ae87aa..948329696f 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs @@ -1,8 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - namespace Analyzer.Utilities; internal static class RoslynDebug diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs index c0d16fbee5..f470a13e05 100644 --- a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. -using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using Analyzer.Utilities.Extensions; using Analyzer.Utilities.PooledObjects; @@ -171,14 +168,15 @@ private bool TryGetOrCreateTypeByMetadataNameSlow( } /// - /// Determines if is a with its type + /// Determines if is a with its type /// argument satisfying . /// - /// Type potentially representing a . + /// Type potentially representing a . /// Predicate to check the 's type argument. - /// True if is a with its + /// True if is a with its /// type argument satisfying , false otherwise. - internal bool IsTaskOfType([NotNullWhen(returnValue: true)] ITypeSymbol? typeSymbol, Func typeArgumentPredicate) => typeSymbol != null + internal bool IsTaskOfType([NotNullWhen(returnValue: true)] ITypeSymbol? typeSymbol, Func typeArgumentPredicate) + => typeSymbol != null && typeSymbol.OriginalDefinition != null && SymbolEqualityComparer.Default.Equals(typeSymbol.OriginalDefinition, GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1)) diff --git a/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs index 14fa48d1b5..e6f4ea8199 100644 --- a/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestCleanupShouldBeValidAnalyzer.cs @@ -53,9 +53,9 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (methodSymbol.IsTestCleanupMethod(testCleanupAttributeSymbol) + if (methodSymbol.HasAttribute(testCleanupAttributeSymbol) && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: false, - allowGenericType: true, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) + allowGenericType: true, FixtureParameterMode.MustNotHaveTestContext, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) { context.ReportDiagnostic(isFixable ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) diff --git a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs index fa3b7aed4b..7ed69424de 100644 --- a/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestContextShouldBeValidAnalyzer.cs @@ -37,6 +37,35 @@ public sealed class TestContextShouldBeValidAnalyzer : DiagnosticAnalyzer public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(TestContextShouldBeValidRule); + private static IFieldSymbol? TryGetReturnedField(ImmutableArray operations) + { + foreach (IOperation operation in operations) + { + if (TryGetReturnedField(operation) is { } returnedMember) + { + return returnedMember; + } + } + + return null; + } + + private static IFieldSymbol? TryGetReturnedField(IOperation operation) + { + if (operation is IBlockOperation blockOperation) + { + return TryGetReturnedField(blockOperation.Operations); + } + + if (operation is IReturnOperation { ReturnedValue: IFieldReferenceOperation { Field: { } returnedField } }) + { + return returnedField; + } + + // We can't figure out exactly the field returned by this property. + return null; + } + private static bool AssignsParameterToMember(IParameterSymbol parameter, ISymbol member, ImmutableArray operations) { foreach (IOperation operation in operations) @@ -69,6 +98,46 @@ assignmentOperation.Value is IParameterReferenceOperation parameterReference && SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, parameter); } + private static void CollectTestContextFieldsAssignedInConstructor( + IParameterSymbol testContextParameter, + ImmutableArray operations, + ConcurrentBag fieldsAssignedInConstructor) + { + foreach (IOperation operation in operations) + { + CollectTestContextFieldsAssignedInConstructor(testContextParameter, operation, fieldsAssignedInConstructor); + } + } + + private static void CollectTestContextFieldsAssignedInConstructor( + IParameterSymbol testContextParameter, + IOperation operation, + ConcurrentBag fieldsAssignedInConstructor) + { + if (operation is IBlockOperation blockOperation) + { + CollectTestContextFieldsAssignedInConstructor(testContextParameter, blockOperation.Operations, fieldsAssignedInConstructor); + } + else if (operation is IExpressionStatementOperation expressionStatementOperation) + { + operation = expressionStatementOperation.Operation; + } + + if (operation is ISimpleAssignmentOperation assignmentOperation && + assignmentOperation.Target is IMemberReferenceOperation { Member: IFieldSymbol { } candidateField } && + assignmentOperation.Value is IParameterReferenceOperation parameterReference && + SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, testContextParameter)) + { + fieldsAssignedInConstructor.Add(candidateField); + } + } + + private static IParameterSymbol? TryGetTestContextParameterIfValidConstructor(ISymbol candidate, INamedTypeSymbol testContextSymbol) + => candidate is IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [{ } parameter] } && + SymbolEqualityComparer.Default.Equals(parameter.Type, testContextSymbol) + ? parameter + : null; + public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); @@ -90,6 +159,8 @@ public override void Initialize(AnalysisContext context) var namedType = (INamedTypeSymbol)context.Symbol; foreach (ISymbol member in namedType.GetMembers()) { + IFieldSymbol? fieldReturnedByProperty = null; + ConcurrentBag? fieldsAssignedInConstructor = null; switch (member.Kind) { case SymbolKind.Property: @@ -113,6 +184,20 @@ public override void Initialize(AnalysisContext context) { return; } + + fieldsAssignedInConstructor = new(); + + context.RegisterOperationBlockAction(context => + { + if (context.OwningSymbol.Equals(propertySymbol.GetMethod, SymbolEqualityComparer.Default)) + { + fieldReturnedByProperty = TryGetReturnedField(context.OperationBlocks); + } + else if (TryGetTestContextParameterIfValidConstructor(context.OwningSymbol, testContextSymbol) is { } parameter) + { + CollectTestContextFieldsAssignedInConstructor(parameter, context.OperationBlocks, fieldsAssignedInConstructor); + } + }); } else if (member is IFieldSymbol fieldSymbol) { @@ -145,14 +230,12 @@ public override void Initialize(AnalysisContext context) context.RegisterOperationBlockAction( context => { - if (context.OwningSymbol is not IMethodSymbol { MethodKind: MethodKind.Constructor } constructor || - constructor.Parameters.Length != 1 || - !SymbolEqualityComparer.Default.Equals(constructor.Parameters[0].Type, testContextSymbol)) + if (TryGetTestContextParameterIfValidConstructor(context.OwningSymbol, testContextSymbol) is not { } parameter) { return; } - if (AssignsParameterToMember(constructor.Parameters[0], member, context.OperationBlocks)) + if (AssignsParameterToMember(parameter, member, context.OperationBlocks)) { isAssigned = true; } @@ -161,6 +244,12 @@ public override void Initialize(AnalysisContext context) context.RegisterSymbolEndAction( context => { + if (!isAssigned) + { + isAssigned = fieldReturnedByProperty is not null && + fieldsAssignedInConstructor?.Contains(fieldReturnedByProperty, SymbolEqualityComparer.Default) == true; + } + if (!isAssigned) { context.ReportDiagnostic(member.CreateDiagnostic(TestContextShouldBeValidRule)); diff --git a/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs index 8a48f29d91..559d7e39b4 100644 --- a/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestInitializeShouldBeValidAnalyzer.cs @@ -53,9 +53,9 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo INamedTypeSymbol? valueTaskSymbol, INamedTypeSymbol testClassAttributeSymbol, bool canDiscoverInternals) { var methodSymbol = (IMethodSymbol)context.Symbol; - if (methodSymbol.IsTestInitializeMethod(testInitializeAttributeSymbol) + if (methodSymbol.HasAttribute(testInitializeAttributeSymbol) && !methodSymbol.HasValidFixtureMethodSignature(taskSymbol, valueTaskSymbol, canDiscoverInternals, shouldBeStatic: false, - allowGenericType: true, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) + allowGenericType: true, FixtureParameterMode.MustNotHaveTestContext, testContextSymbol: null, testClassAttributeSymbol, fixtureAllowInheritedTestClass: true, out bool isFixable)) { context.ReportDiagnostic(isFixable ? methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name) diff --git a/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs index b99c64fe1b..7cc5fd1691 100644 --- a/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/TestMethodShouldBeValidAnalyzer.cs @@ -69,7 +69,19 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo return; } - if (methodSymbol.IsGenericMethod || methodSymbol.IsStatic || methodSymbol.IsAbstract || methodSymbol is { ReturnsVoid: true, IsAsync: true } + if (methodSymbol.IsGenericMethod) + { + foreach (ITypeParameterSymbol typeParameter in methodSymbol.TypeParameters) + { + // If none of the parameters match the type parameter, then that generic type can't be inferred. + if (!methodSymbol.Parameters.Any(p => typeParameter.Equals(p.Type, SymbolEqualityComparer.Default))) + { + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(ValidTestMethodSignatureRule, methodSymbol.Name)); + } + } + } + + if (methodSymbol.IsStatic || methodSymbol.IsAbstract || methodSymbol is { ReturnsVoid: true, IsAsync: true } || (!methodSymbol.ReturnsVoid && (taskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, taskSymbol)) && (valueTaskSymbol is null || !SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, valueTaskSymbol)))) diff --git a/src/Analyzers/MSTest.Analyzers/UseDeploymentItemWithTestMethodOrTestClassAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseDeploymentItemWithTestMethodOrTestClassAnalyzer.cs index 6df26db2a1..09e939690e 100644 --- a/src/Analyzers/MSTest.Analyzers/UseDeploymentItemWithTestMethodOrTestClassAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseDeploymentItemWithTestMethodOrTestClassAnalyzer.cs @@ -52,6 +52,15 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testMethodAttributeSymbol, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol deploymentItemAttributeSymbol) { + if (context.Symbol is INamedTypeSymbol { IsAbstract: true }) + { + // As [DeploymentItem] attribute is inherited, it's okay to be present on an abstract class that is not a test class. + // See https://github.com/microsoft/testfx/issues/2683 for information. + // For now, we do the IsAbstract check specifically for classes and not methods. + // If we got a convincing feedback around a false positive for the attribute on an abstract method, we can adjust the check. + return; + } + bool hasDeploymentItemAttribute = false; bool isTestMethodOrTestClass = false; foreach (AttributeData attribute in context.Symbol.GetAttributes()) diff --git a/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs new file mode 100644 index 0000000000..0ccf036c59 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UseNewerAssertThrowsAnalyzer.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0039: Use newer 'Assert.Throws' methods. +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +internal sealed class UseNewerAssertThrowsAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.UseNewerAssertThrowsTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseNewerAssertThrowsMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.UseNewerAssertThrowsRuleId, + Title, + MessageFormat, + null, + Category.Usage, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert, out INamedTypeSymbol? assertTypeSymbol)) + { + return; + } + + INamedTypeSymbol? funcType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemFunc1); + context.RegisterOperationAction(context => AnalyzeInvocationOperation(context, assertTypeSymbol, funcType), OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocationOperation(OperationAnalysisContext context, INamedTypeSymbol assertTypeSymbol, INamedTypeSymbol? funcType) + { + var operation = (IInvocationOperation)context.Operation; + IMethodSymbol targetMethod = operation.TargetMethod; + if (!SymbolEqualityComparer.Default.Equals(targetMethod.ContainingType, assertTypeSymbol) || + targetMethod.Name is not ("ThrowsException" or "ThrowsExceptionAsync")) + { + return; + } + + ImmutableArray additionalLocations = ImmutableArray.Empty; + + // The old synchronous ThrowsException method has an overload that takes a Func as the action. + // The new synchronous ThrowsExactly method does not have this overload, and only Action overload is available. + // Hence, the codefix should be aware of that to adjust accordingly. + // For example, 'Assert.ThrowsException(() => 5)' should be fixed to Assert.ThrowsExactly(() => _ = 5). + // Also, Assert.ThrowsException usage could be a long body with some return statements, which would be invalid for ThrowsExactly as there is only Action overload. + // The codefix should adjust any "return whatever;" statements to "_ = whatever;" followed by "return;" + // The codefix will know that it needs to adjust something if there is an additional location, which will be pointing to the action argument. + if (!targetMethod.Name.EndsWith("Async", StringComparison.Ordinal) && + targetMethod.Parameters[0].Type.OriginalDefinition.Equals(funcType, SymbolEqualityComparer.Default) && + operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Ordinal == 0)?.Syntax.GetLocation() is { } additionalLocation) + { + additionalLocations = ImmutableArray.Create(additionalLocation); + } + + context.ReportDiagnostic(operation.CreateDiagnostic(Rule, additionalLocations, properties: null)); + } +} diff --git a/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs index b3295b921e..f9bccc3686 100644 --- a/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs @@ -44,6 +44,13 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeCompilation(CompilationAnalysisContext context) { + bool hasTestAdapter = context.Compilation.ReferencedAssemblyNames.Any(asm => asm.Name == "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter"); + if (!hasTestAdapter) + { + // We shouldn't produce a diagnostic if only the test framework is referenced, but not the adapter. + return; + } + INamedTypeSymbol? parallelizeAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute); INamedTypeSymbol? doNotParallelizeAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDoNotParallelizeAttribute); diff --git a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs index 223400bc52..dac84bcaaa 100644 --- a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs @@ -2,9 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Immutable; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; @@ -53,6 +52,66 @@ private enum EqualityCheckStatus NotEquals, } + internal const string ProperAssertMethodNameKey = nameof(ProperAssertMethodNameKey); + + /// + /// Only the presence of this key in properties bag indicates that a cast is needed. + /// The value of the key is always null. + /// + internal const string NeedsNullableBooleanCastKey = nameof(NeedsNullableBooleanCastKey); + + /// + /// Key in the properties bag that has value one of CodeFixModeSimple, CodeFixModeAddArgument, or CodeFixModeRemoveArgument. + /// + internal const string CodeFixModeKey = nameof(CodeFixModeKey); + + /// + /// This mode means the codefix operation is as follows: + /// + /// Find the right assert method name from the properties bag using . + /// Replace the identifier syntax for the invocation with the right assert method name. The identifier syntax is calculated by the codefix. + /// Replace the syntax node from the first additional locations with the node from second additional locations. + /// + /// Example: For Assert.IsTrue(x == null), it will become Assert.IsNull(x). + /// The value for ProperAssertMethodNameKey is "IsNull". + /// The first additional location will point to the "x == null" node. + /// The second additional location will point to the "x" node. + /// + internal const string CodeFixModeSimple = nameof(CodeFixModeSimple); + + /// + /// This mode means the codefix operation is as follows: + /// + /// Find the right assert method name from the properties bag using . + /// Replace the identifier syntax for the invocation with the right assert method name. The identifier syntax is calculated by the codefix. + /// Replace the syntax node from the first additional locations with the node from second additional locations. + /// Add new argument which is identical to the node from third additional locations. + /// + /// Example: For Assert.IsTrue(x == y), it will become Assert.AreEqual(y, x). + /// The value for ProperAssertMethodNameKey is "AreEqual". + /// The first additional location will point to the "x == y" node. + /// The second additional location will point to the "y" node. + /// The third additional location will point to the "x" node. + /// + internal const string CodeFixModeAddArgument = nameof(CodeFixModeAddArgument); + + /// + /// This mode means the codefix operation is as follows: + /// + /// Find the right assert method name from the properties bag using . + /// Replace the identifier syntax for the invocation with the right assert method name. The identifier syntax is calculated by the codefix. + /// Remove the argument which the second additional locations points to. + /// + /// Example: For Assert.AreEqual(false, x), it will become Assert.IsFalse(x). + /// The value for ProperAssertMethodNameKey is "IsFalse". + /// The first additional location will point to the "false" node. + /// The second additional location will point to the "x" node, in case a cast is needed. + /// + /// + /// If is present, then the produced code will be Assert.IsFalse((bool?)x);. + /// + internal const string CodeFixModeRemoveArgument = nameof(CodeFixModeRemoveArgument); + private static readonly LocalizableResourceString Title = new(nameof(Resources.UseProperAssertMethodsTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseProperAssertMethodsMessageFormat), Resources.ResourceManager, typeof(Resources)); @@ -118,31 +177,69 @@ private static void AnalyzeInvocationOperation(OperationAnalysisContext context, } } - private static bool IsIsNullPattern(IOperation operation) - => operation is IIsPatternOperation { Pattern: IConstantPatternOperation { Value: { } constantPatternValue } } && - constantPatternValue.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }; + private static bool IsIsNullPattern(IOperation operation, [NotNullWhen(true)] out SyntaxNode? expressionUnderTest) + { + if (operation is IIsPatternOperation { Pattern: IConstantPatternOperation { Value: { } constantPatternValue } } isPatternOperation && + constantPatternValue.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }) + { + expressionUnderTest = isPatternOperation.Value.Syntax; + return true; + } + + expressionUnderTest = null; + return false; + } + + private static bool IsIsNotNullPattern(IOperation operation, [NotNullWhen(true)] out SyntaxNode? expressionUnderTest) + { + if (operation is IIsPatternOperation { Pattern: INegatedPatternOperation { Pattern: IConstantPatternOperation { Value: { } constantPatternValue } } } isPatternOperation && + constantPatternValue.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }) + { + expressionUnderTest = isPatternOperation.Value.Syntax; + return true; + } - private static bool IsIsNotNullPattern(IOperation operation) - => operation is IIsPatternOperation { Pattern: INegatedPatternOperation { Pattern: IConstantPatternOperation { Value: { } constantPatternValue } } } && - constantPatternValue.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }; + expressionUnderTest = null; + return false; + } // TODO: Recognize 'null == something' (i.e, when null is the left operand) - private static bool IsEqualsNullBinaryOperator(IOperation operation) - => operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals, RightOperand: { } rightOperand } && - rightOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }; + private static bool IsEqualsNullBinaryOperator(IOperation operation, [NotNullWhen(true)] out SyntaxNode? expressionUnderTest) + { + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals, RightOperand: { } rightOperand } binaryOperation && + binaryOperation.OperatorMethod is not { MethodKind: MethodKind.UserDefinedOperator } && + rightOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }) + { + expressionUnderTest = binaryOperation.LeftOperand.Syntax; + return true; + } + + expressionUnderTest = null; + return false; + } // TODO: Recognize 'null != something' (i.e, when null is the left operand) - private static bool IsNotEqualsNullBinaryOperator(IOperation operation) - => operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.NotEquals, RightOperand: { } rightOperand } && - rightOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }; + private static bool IsNotEqualsNullBinaryOperator(IOperation operation, [NotNullWhen(true)] out SyntaxNode? expressionUnderTest) + { + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.NotEquals, RightOperand: { } rightOperand } binaryOperation && + binaryOperation.OperatorMethod is not { MethodKind: MethodKind.UserDefinedOperator } && + rightOperand.WalkDownConversion() is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }) + { + expressionUnderTest = binaryOperation.LeftOperand.Syntax; + return true; + } + + expressionUnderTest = null; + return false; + } - private static NullCheckStatus RecognizeNullCheck(IOperation operation) + private static NullCheckStatus RecognizeNullCheck(IOperation operation, /*We cannot express this is not null when NullCheckStatis is not Unknown*/ out SyntaxNode? expressionUnderTest) { - if (IsIsNullPattern(operation) || IsEqualsNullBinaryOperator(operation)) + if (IsIsNullPattern(operation, out expressionUnderTest) || IsEqualsNullBinaryOperator(operation, out expressionUnderTest)) { return NullCheckStatus.IsNull; } - else if (IsIsNotNullPattern(operation) || IsNotEqualsNullBinaryOperator(operation)) + else if (IsIsNotNullPattern(operation, out expressionUnderTest) || IsNotEqualsNullBinaryOperator(operation, out expressionUnderTest)) { return NullCheckStatus.IsNotNull; } @@ -150,52 +247,96 @@ private static NullCheckStatus RecognizeNullCheck(IOperation operation) return NullCheckStatus.Unknown; } - private static EqualityCheckStatus RecognizeEqualityCheck(IOperation operation) + private static EqualityCheckStatus RecognizeEqualityCheck(IOperation operation, out SyntaxNode? toBecomeExpected, out SyntaxNode? toBecomeActual) { - if (operation is IIsPatternOperation { Pattern: IConstantPatternOperation } or - IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals }) + if (operation is IIsPatternOperation { Pattern: IConstantPatternOperation constantPattern1 } isPattern1) { + toBecomeExpected = constantPattern1.Syntax; + toBecomeActual = isPattern1.Value.Syntax; return EqualityCheckStatus.Equals; } - else if (operation is IIsPatternOperation { Pattern: INegatedPatternOperation { Pattern: IConstantPatternOperation } } or - IBinaryOperation { OperatorKind: BinaryOperatorKind.NotEquals }) + else if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.Equals } binaryOperation1 && + binaryOperation1.OperatorMethod is not { MethodKind: MethodKind.UserDefinedOperator }) + { + // This is quite arbitrary. We can do extra checks to see which one (if any) looks like a "constant" and make it the expected. + toBecomeExpected = binaryOperation1.RightOperand.Syntax; + toBecomeActual = binaryOperation1.LeftOperand.Syntax; + return EqualityCheckStatus.Equals; + } + else if (operation is IIsPatternOperation { Pattern: INegatedPatternOperation { Pattern: IConstantPatternOperation constantPattern2 } } isPattern2) + { + toBecomeExpected = constantPattern2.Syntax; + toBecomeActual = isPattern2.Value.Syntax; + return EqualityCheckStatus.NotEquals; + } + else if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.NotEquals } binaryOperation2 && + binaryOperation2.OperatorMethod is not { MethodKind: MethodKind.UserDefinedOperator }) { + // This is quite arbitrary. We can do extra checks to see which one (if any) looks like a "constant" and make it the expected. + toBecomeExpected = binaryOperation2.RightOperand.Syntax; + toBecomeActual = binaryOperation2.LeftOperand.Syntax; return EqualityCheckStatus.NotEquals; } + toBecomeExpected = null; + toBecomeActual = null; return EqualityCheckStatus.Unknown; } private static void AnalyzeIsTrueOrIsFalseInvocation(OperationAnalysisContext context, IOperation conditionArgument, bool isTrueInvocation) { - NullCheckStatus nullCheckStatus = RecognizeNullCheck(conditionArgument); + RoslynDebug.Assert(context.Operation is IInvocationOperation, "Expected IInvocationOperation."); + + NullCheckStatus nullCheckStatus = RecognizeNullCheck(conditionArgument, out SyntaxNode? expressionUnderTest); if (nullCheckStatus != NullCheckStatus.Unknown) { - Debug.Assert(nullCheckStatus is NullCheckStatus.IsNull or NullCheckStatus.IsNotNull, "Unexpected NullCheckStatus value."); + RoslynDebug.Assert(expressionUnderTest is not null, $"Unexpected null for '{nameof(expressionUnderTest)}'."); + RoslynDebug.Assert(nullCheckStatus is NullCheckStatus.IsNull or NullCheckStatus.IsNotNull, "Unexpected NullCheckStatus value."); bool shouldUseIsNull = isTrueInvocation ? nullCheckStatus == NullCheckStatus.IsNull : nullCheckStatus == NullCheckStatus.IsNotNull; + // Here, the codefix will want to switch something like Assert.IsTrue(x == null) with Assert.IsNull(x) + // This is the "simple" mode. + // The message is: Use 'Assert.{0}' instead of 'Assert.{1}'. + string properAssertMethod = shouldUseIsNull ? "IsNull" : "IsNotNull"; + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeSimple); context.ReportDiagnostic(context.Operation.CreateDiagnostic( Rule, - shouldUseIsNull ? "IsNull" : "IsNotNull", + additionalLocations: ImmutableArray.Create(conditionArgument.Syntax.GetLocation(), expressionUnderTest.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, isTrueInvocation ? "IsTrue" : "IsFalse")); return; } - EqualityCheckStatus equalityCheckStatus = RecognizeEqualityCheck(conditionArgument); + EqualityCheckStatus equalityCheckStatus = RecognizeEqualityCheck(conditionArgument, out SyntaxNode? toBecomeExpected, out SyntaxNode? toBecomeActual); if (equalityCheckStatus != EqualityCheckStatus.Unknown) { - Debug.Assert(equalityCheckStatus is EqualityCheckStatus.Equals or EqualityCheckStatus.NotEquals, "Unexpected EqualityCheckStatus value."); + RoslynDebug.Assert(toBecomeExpected is not null, $"Unexpected null for '{nameof(toBecomeExpected)}'."); + RoslynDebug.Assert(toBecomeActual is not null, $"Unexpected null for '{nameof(toBecomeActual)}'."); + RoslynDebug.Assert(equalityCheckStatus is EqualityCheckStatus.Equals or EqualityCheckStatus.NotEquals, "Unexpected EqualityCheckStatus value."); bool shouldUseAreEqual = isTrueInvocation ? equalityCheckStatus == EqualityCheckStatus.Equals : equalityCheckStatus == EqualityCheckStatus.NotEquals; + // Here, the codefix will want to switch something like Assert.IsTrue(x == y) with Assert.AreEqual(x, y) + // This is the "add argument" mode. + // The message is: Use 'Assert.{0}' instead of 'Assert.{1}'. + string properAssertMethod = shouldUseAreEqual ? "AreEqual" : "AreNotEqual"; + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeAddArgument); context.ReportDiagnostic(context.Operation.CreateDiagnostic( Rule, - shouldUseAreEqual ? "AreEqual" : "AreNotEqual", + additionalLocations: ImmutableArray.Create(conditionArgument.Syntax.GetLocation(), toBecomeExpected.GetLocation(), toBecomeActual.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, isTrueInvocation ? "IsTrue" : "IsFalse")); return; } @@ -209,20 +350,50 @@ private static void AnalyzeAreEqualOrAreNotEqualInvocation(OperationAnalysisCont { bool shouldUseIsTrue = expectedLiteralBoolean; + // Here, the codefix will want to switch something like Assert.AreEqual(true, x) with Assert.IsTrue(x) + // This is the "remove argument" mode. + // The message is: Use 'Assert.{0}' instead of 'Assert.{1}'. + string properAssertMethod = shouldUseIsTrue ? "IsTrue" : "IsFalse"; + + bool codeFixShouldAddCast = TryGetSecondArgumentValue((IInvocationOperation)context.Operation, out IOperation? actualArgumentValue) && + actualArgumentValue.Type is { } actualType && + actualType.SpecialType != SpecialType.System_Boolean && + !actualType.IsNullableOfBoolean(); + + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeRemoveArgument); + + if (codeFixShouldAddCast) + { + properties.Add(NeedsNullableBooleanCastKey, null); + } + context.ReportDiagnostic(context.Operation.CreateDiagnostic( Rule, - shouldUseIsTrue ? "IsTrue" : "IsFalse", + additionalLocations: ImmutableArray.Create(expectedArgument.Syntax.GetLocation(), actualArgumentValue?.Syntax.GetLocation() ?? Location.None), + properties: properties.ToImmutable(), + properAssertMethod, isAreEqualInvocation ? "AreEqual" : "AreNotEqual")); } else if (expectedArgument is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } }) { bool shouldUseIsNull = isAreEqualInvocation; + // Here, the codefix will want to switch something like Assert.AreEqual(null, x) with Assert.IsNull(x) + // This is the "remove argument" mode. + // The message is: Use 'Assert.{0}' instead of 'Assert.{1}'. + string properAssertMethod = shouldUseIsNull ? "IsNull" : "IsNotNull"; + ImmutableDictionary.Builder properties = ImmutableDictionary.CreateBuilder(); + properties.Add(ProperAssertMethodNameKey, properAssertMethod); + properties.Add(CodeFixModeKey, CodeFixModeRemoveArgument); context.ReportDiagnostic(context.Operation.CreateDiagnostic( Rule, - shouldUseIsNull ? "IsNull" : "IsNotNull", + additionalLocations: ImmutableArray.Create(expectedArgument.Syntax.GetLocation()), + properties: properties.ToImmutable(), + properAssertMethod, isAreEqualInvocation ? "AreEqual" : "AreNotEqual")); } } @@ -230,6 +401,9 @@ private static void AnalyzeAreEqualOrAreNotEqualInvocation(OperationAnalysisCont private static bool TryGetFirstArgumentValue(IInvocationOperation operation, [NotNullWhen(true)] out IOperation? argumentValue) => TryGetArgumentValueForParameterOrdinal(operation, 0, out argumentValue); + private static bool TryGetSecondArgumentValue(IInvocationOperation operation, [NotNullWhen(true)] out IOperation? argumentValue) + => TryGetArgumentValueForParameterOrdinal(operation, 1, out argumentValue); + private static bool TryGetArgumentValueForParameterOrdinal(IInvocationOperation operation, int ordinal, [NotNullWhen(true)] out IOperation? argumentValue) { argumentValue = operation.Arguments.FirstOrDefault(arg => arg.Parameter?.Ordinal == ordinal)?.Value?.WalkDownConversion(); diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index 5b591476a2..7e0d3ff59b 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -26,7 +26,7 @@ The type declaring these methods should also respect the following rules: – Nesmí být async void. – Nesmí být speciální metodou (finalizaÄní metoda, operátor...). – Nesmí být obecné. -– Nesmí pÅ™ijímat žádný parametr. +– Nesmí pÅ™ijímat žádný parametr, nebo musí pÅ™ijímat jediný parametr typu TestContext. – Návratový typ musí být void, Task nebo ValueTask. Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: @@ -117,14 +117,29 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: Argumenty kontrolního výrazu musí být pÅ™edány ve správném poÅ™adí + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + PÅ™i porovnávání typů hodnot použijte Assert.AreEqual/Assert.AreNotEqual místo Assert.AreSame/Assert.AreNotSame. PÅ™edání typu hodnoty do assert.AreSame/Assert.AreNotSame bude zabaleno (vytváření nového objektu). Protože Assert.AreSame/Assert.AreNotSame provádí porovnání podle odkazu, Assert.AreSame selže, když dojde k zabalení, a Assert.AreNotSame vždy probÄ›hne. + + + + Use '{0}' instead of '{1}' when comparing value types + PÅ™i porovnávání typů hodnot použít '{0}' místo '{1}' + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + Nepoužívejte Assert.AreSame ani Assert.AreNotSame s typy hodnot. + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Preferujte Assert.ThrowsException nebo Assert.ThrowsExceptionAsync pÅ™ed [ExpectedException], protože zajišťuje, že oÄekávanou výjimku vyvolá pouze oÄekávané volání. Rozhraní API assert také poskytují vÄ›tší flexibilitu a umožňují vyhodnocovat další vlastnosti výjimky. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Preferujte Assert.ThrowsExactly nebo Assert.ThrowsExactlyAsync pÅ™ed [ExpectedException], protože zajišťuje, že oÄekávanou výjimku vyvolá pouze oÄekávané volání. Rozhraní API pro vyhodnocení také poskytují vÄ›tší flexibilitu a umožňují vyhodnocovat další vlastnosti výjimky. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Preferovat Assert.ThrowsException/ThrowsExceptionAsync pÅ™ed [ExpectedException] + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Preferovat Assert.ThrowsExactly/ThrowsExactlyAsync pÅ™ed [ExpectedException] @@ -140,7 +155,7 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -158,7 +173,7 @@ The type declaring these methods should also respect the following rules: – Nesmí být async void. – Nesmí být speciální metodou (finalizaÄní metoda, operátor...). – Nesmí být obecné. -– Nesmí pÅ™ijímat žádný parametr. +– Nesmí pÅ™ijímat žádný parametr, nebo musí pÅ™ijímat jediný parametr typu TestContext. – Návratový typ musí být void, Task nebo ValueTask. – V případÄ› třídy abstract by mÄ›l být zadán parametr atributu InheritanceBehavior.BeforeEachDerivedClass. – V případÄ› třídy sealed by nemÄ›l být zadán parametr atributu InheritanceBehavior.BeforeEachDerivedClass. @@ -251,6 +266,16 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: Typ argumentu DataRow by mÄ›l odpovídat typu parametru metody. V indexech dochází k neshodÄ›: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Byly nalezeny dva konfliktní typy pro obecný parametr '{0}'. Konfliktní typy jsou '{1}' a '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Typ obecného parametru '{0}' nelze odvodit. + + DataRow should only be set on a test method Objekt DataRow by mÄ›l být nastaven pouze pro testovací metodu @@ -353,18 +378,23 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - ÄŒlen [DynamicData] {0}.{1} je metoda, takže byste mÄ›li nastavit DynamicDataSourceType.Method. + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' Älen {0}.{1} je metoda, takže byste mÄ›li použít DynamicDataSourceType.AutoDetect nebo DynamicDataSourceType.Method (automatické zjišťování je výchozí, pokud není explicitnÄ› zadáno, a doporuÄuje se) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' Älen {0}.{1} není vlastnost ani metoda. Jsou podporovány pouze vlastnosti a metody. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - ÄŒlen [DynamicData] {0}.{1} je vlastnost, takže byste mÄ›li nastavit DynamicDataSourceType.Property. + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' Älen {0}.{1} je vlastnost, takže byste mÄ›li použít DynamicDataSourceType.AutoDetect nebo DynamicDataSourceType.Property (automatické zjišťování je výchozí, pokud není explicitnÄ› zadáno, a doporuÄuje se) - '[DynamicDta]' member '{0}.{1}' is found more than once - ÄŒlen [DynamicDta] {0}.{1} byl nalezen více než jednou. + '[DynamicData]' member '{0}.{1}' is found more than once + ÄŒlen [DynamicData] {0}.{1} byl nalezen více než jednou. @@ -629,7 +659,7 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -637,7 +667,7 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: Testovací metody (metody oznaÄené atributem [TestMethod]) by mÄ›ly respektovat následující rozložení, které MSTest považuje za platné: – musí být public (nebo internal, pokud je nastaven atribut [assembly: DiscoverInternals]), – musí být static, -– nesmí být obecné, +– můžou být obecné, pokud lze odvodit parametry typu a typy argumentů jsou kompatibilní – nesmí být abstract, – návratový typ by mÄ›l být void, Task nebo ValueTask, – nesmí být async void, @@ -709,6 +739,16 @@ Typ deklarující tyto metody by mÄ›l také respektovat následující pravidla: [DeploymentItem] se dá zadat jenom pro testovací třídu nebo testovací metodu. + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Místo Assert.ThrowsException použijte Assert.ThrowsExactly. + + + + Use newer methods to assert exceptions + Pro vyhodnocení výjimek použijte novÄ›jší metody. + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. Ve výchozím nastavení spouÅ¡tí MSTest testy v rámci stejného sestavení sekvenÄnÄ›, což může vést k závažným omezením výkonu. DoporuÄuje se povolit atribut sestavení [Parallelize] k paralelnímu spouÅ¡tÄ›ní testů nebo explicitnÄ› použít atribut [DoNotParallelize] na úrovni sestavení, pokud je známo, že sestavení není paralelizovatelné. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index 2ece3c50f7..9f9b3db605 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -20,20 +20,20 @@ The type declaring these methods should also respect the following rules: -The class should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. Methoden, die mit „[AssemblyCleanup]“ gekennzeichnet sind, müssen dem folgenden Layout folgen, um gültig zu sein: -– kann nicht für eine generische Klasse deklariert werden. +– kann nicht für eine generische Klasse deklariert werden – muss „public“ sein – muss „static“ sein – darf nicht „async void“ sein – darf keine spezielle Methode sein (Finalizer, Operator...) – darf nicht „generic“ sein -– darf keinen Parameter annehmen +– sollte entweder keinen Parameter oder einen einzelnen Parameter vom Typ „TestContext“ verwenden – der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachten: -– Der Typ sollte eine Klasse sein. +– Der Typ sollte eine Klasse sein – Die Klasse muss „public“ oder „internal“ sein (wenn das Testprojekt das Attribut „[DiscoverInternals]“ verwendet) -– Die Klasse darf nicht „static“ sein. -– Die Klasse muss mit „[TestClass]“ (oder einem abgeleiteten Attribut) markiert werden. +– Die Klasse darf nicht „static“ sein +– Die Klasse muss mit „[TestClass]“ (oder einem abgeleiteten Attribut) markiert werden - die Klasse darf nicht generisch sein. @@ -117,14 +117,29 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Assertionsargumente sollten in der richtigen Reihenfolge übergeben werden. + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + Verwenden Sie beim Vergleichen von Werttypen "Assert.AreEqual"/"Assert.AreNotEqual" anstelle von "Assert.AreSame"/"Assert.AreNotSame". Das Übergeben eines Werttyps an "Assert.AreSame"/"Assert.AreNotSame" wird geschachtelt (ein neues Objekt wird erstellt). Da "Assert.AreSame"/"Assert.AreNotSame" den Vergleich als Verweis führt, tritt bei "Assert.AreSame" beim Boxing ein Fehler auf, und "Assert.AreNotSame" wird immer übergeben. + + + + Use '{0}' instead of '{1}' when comparing value types + Beim Vergleichen von Werttypen '{0}' anstelle von '{1}' verwenden + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + Verwenden Sie "Assert.AreSame" oder "Assert.AreNotSame" nicht mit Werttypen. + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - „Assert.ThrowsException“ oder „Assert.ThrowsExceptionAsync“ gegenüber „[ExpectedException]“ bevorzugen, da dadurch sichergestellt wird, dass nur der erwartete Aufruf die erwartete Ausnahme auslöst. Die Assert-APIs bieten außerdem mehr Flexibilität und ermöglichen es Ihnen, zusätzliche Eigenschaften der Ausführung zu bestätigen. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + „Assert.ThrowsExactly“ oder „Assert.ThrowsExactlyAsync“ gegenüber „[ExpectedException]“ bevorzugen, da dadurch sichergestellt wird, dass nur der erwartete Aufruf die erwartete Ausnahme auslöst. Die Assert-APIs bieten außerdem mehr Flexibilität und ermöglichen es Ihnen, zusätzliche Eigenschaften der Ausführung zu bestätigen. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - „Assert.ThrowsException/ThrowsExceptionAsync“ gegenüber „[ExpectedException]“ bevorzugen + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + „Assert.ThrowsExactly/ThrowsExactlyAsync“ gegenüber „[ExpectedException]“ bevorzugen @@ -140,7 +155,7 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -152,23 +167,22 @@ The type declaring these methods should also respect the following rules: -If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. Methoden, die mit „[ClassCleanup]“ gekennzeichnet sind, müssen dem folgenden Layout folgen, um gültig zu sein: -– kann nicht für eine generische Klasse deklariert werden, ohne dass der Modus „InheritanceBehavior“ auf - festgelegt ist. +– kann nicht für eine generische Klasse deklariert werden, ohne dass der Modus „InheritanceBehavior“ festgelegt ist – muss „public“ sein – muss „static“ sein – darf nicht „async void“ sein – darf keine spezielle Methode sein (Finalizer, Operator...) – darf nicht „generic“ sein -– darf keinen Parameter annehmen +– sollte entweder keinen Parameter oder einen einzelnen Parameter vom Typ „TestContext“ verwenden – der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein – der Attributparameter „InheritanceBehavior.BeforeEachDerivedClass“ sollte angegeben werden, wenn die Klasse „abstract“ ist -– der Attributparameter „InheritanceBehavior.BeforeEachDerivedClass“ sollte angegeben werden, wenn die Klasse „sealed“ ist. +– der Attributparameter „InheritanceBehavior.BeforeEachDerivedClass“ sollte angegeben werden, wenn die Klasse „sealed“ ist Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachten: -– Der Typ sollte eine Klasse sein. +– Der Typ sollte eine Klasse sein – Die Klasse muss „public“ oder „internal“ sein (wenn das Testprojekt das Attribut „[DiscoverInternals]“ verwendet) -– Die Klasse darf nicht „static“ sein. -– Wenn die Klasse „sealed“ ist, sollte sie mit „[TestClass]“ (oder einem abgeleiteten Attribut) markiert werden. +– Die Klasse darf nicht „static“ sein +– Wenn die Klasse „sealed“ ist, sollte sie mit „[TestClass]“ (oder einem abgeleiteten Attribut) markiert werden - die Klasse darf nicht generisch sein. @@ -253,6 +267,16 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Der DataRow-Argumenttyp muss mit dem Methodenparametertyp übereinstimmen. Nichtübereinstimmungen treten bei Indizes auf: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Es wurden zwei in Konflikt stehende Typen für den generischen Parameter '{0}' gefunden. Die in Konflikt stehenden Typen sind '{1}' und '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Der Typ des generischen Parameters '{0}' konnte nicht abgeleitet werden. + + DataRow should only be set on a test method DataRow darf nur für eine Testmethode festgelegt werden. @@ -355,18 +379,23 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - "[DynamicData]"-Element "{0}.{1}" ist eine Methode, daher sollten Sie "DynamicDataSourceType.Method" festlegen. + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' Member "{0}.{1}" ist eine Methode, daher sollten Sie "DynamicDataSourceType.AutoDetect" oder "DynamicDataSourceType.Method" verwenden (die automatische Erkennung ist der Standardwert, wenn sie nicht explizit angegeben wird und empfohlen wird). + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' Member "{0}.{1}" ist weder eine Eigenschaft noch eine Methode. Nur Eigenschaften und Methoden werden unterstützt. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - "[DynamicData]"-Element "{0}.{1}" ist eine Eigenschaft, daher sollten Sie "DynamicDataSourceType.Property" festlegen. + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' Member "{0}.{1}" ist eine Eigenschaft, daher sollten Sie "DynamicDataSourceType.AutoDetect" oder "DynamicDataSourceType.Property" verwenden (die automatische Erkennung ist der Standardwert, wenn sie nicht explizit angegeben wird und empfohlen wird). - '[DynamicDta]' member '{0}.{1}' is found more than once - "[DynamicDta]"-Element "{0}.{1}" wurde mehrmals gefunden. + '[DynamicData]' member '{0}.{1}' is found more than once + „[DynamicData]“-Mitglied „{0}.{1}“ wurde mehrmals gefunden. @@ -631,7 +660,7 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -639,7 +668,7 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Testmethoden, Methoden, die mit dem Attribut „[TestMethod]“ gekennzeichnet sind, sollten das folgende Layout berücksichtigen, um von MSTest als gültig angesehen zu werden: – es darf „public“ (oder „internal“ sein, wenn das Attribut „[assembly: DiscoverInternals]“ festgelegt ist) – es darf nicht „static“ sein -– es darf nicht generisch sein +– es kann generisch sein, solange Typparameter abgeleitet werden können und Argumenttypen kompatibel sind – es darf nicht „abstract“ sein – der Rückgabetyp muss „void“, „Task“ oder „ValueTask“ sein – es darf nicht „async void“ sein @@ -711,6 +740,16 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte „[DeploymentItem]“ kann nur für die Testklasse oder Testmethode angegeben werden. + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Verwenden Sie "Assert.ThrowsExactly" anstelle von "Assert.ThrowsException". + + + + Use newer methods to assert exceptions + Neuere Methoden zum Bestätigen von Ausnahmen verwenden + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. Standardmäßig führt MSTest Tests in derselben Assembly sequentiell aus, was zu erheblichen Leistungseinschränkungen führen kann. Es wird empfohlen, das Assembly-Attribut "[Parallelize]" zu aktivieren, um Tests parallel auszuführen, oder, wenn bekannt ist, dass die Assembly nicht parallelisierbar ist, explizit das Assembly-Attribut "[DoNotParallelize]" zu verwenden. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index c88d16d554..1f9b00a97f 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -23,16 +23,16 @@ The type declaring these methods should also respect the following rules: -no se puede declarar en una clase genérica - debería ser 'público' - debería estar 'estático' -- no debería ser 'async void' +-no debe ser 'async void' -no debe ser un método especial (finalizador, operador...). -no debe ser genérico --no debe tomar ningún parámetro +-no debe tomar ningún parámetro o tomar un único parámetro de tipo 'TestContext' - El tipo de valor devuelto debe ser 'void', 'Task' o 'ValueTask' El tipo que declara estos métodos también debe respetar las reglas siguientes: -El tipo debe ser una clase -La clase debe ser 'public' o 'internal' (si el proyecto de prueba usa el atributo '[DiscoverInternals]') --La clase no debe ser 'statico' +-La clase no debe ser 'static' -La clase debe marcarse con '[TestClass]' (o un atributo derivado) -la clase no debe ser genérica. @@ -117,14 +117,29 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Los argumentos de aserción deben pasarse en el orden correcto + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + Use 'Assert.AreEqual'/'Assert.AreNotEqual' en lugar de 'Assert.AreSame'/'Assert.AreNotSame' al comparar tipos de valor. Al pasar un tipo de valor a 'Assert.AreSame'/'Assert.AreNotSame', se aplicará la conversión boxing (creando un nuevo objeto). Dado que 'Assert.AreSame'/'Assert.AreNotSame' realiza la comparación por referencia, 'Assert.AreSame' producirá un error cuando se produzca una conversión boxing y 'Assert.AreNotSame' pasará siempre. + + + + Use '{0}' instead of '{1}' when comparing value types + Usar '{0}' en lugar de '{1}' al comparar tipos de valor + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + No use "Assert.AreSame" o "Assert.AreNotSame" con tipos de valor + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Preferir "Assert.ThrowsException" o "Assert.ThrowsExceptionAsync" en lugar de '[ExpectedException]' ya que garantiza que solo la llamada esperada inicia la excepción esperada. Las API de aserción también proporcionan más flexibilidad y le permiten declarar propiedades adicionales de la ejecución. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Preferir "Assert.ThrowsExactly" o "Assert.ThrowsExactlyAsync" en lugar de "[ExpectedException]", ya que garantiza que solo la llamada esperada inicie la excepción esperada. Las API de aserción también proporcionan más flexibilidad y permiten declarar propiedades adicionales de la ejecución. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Preferir "Assert.ThrowsException/ThrowsExceptionAsync" en lugar de "[ExpectedException]" + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Preferir "Assert.ThrowsExactly/ThrowsExactlyAsync" en lugar de "[ExpectedException]" @@ -140,7 +155,7 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -155,13 +170,13 @@ The type declaring these methods should also respect the following rules: -no se puede declarar en una clase genérica sin el modo 'InheritanceBehavior' establecido - debería ser 'público' - debería estar 'estático' -- no debería ser 'async void' +-no debe ser 'async void' -no debe ser un método especial (finalizador, operador...). -no debe ser genérico --no debe tomar ningún parámetro +-no debe tomar ningún parámetro o tomar un único parámetro de tipo 'TestContext' - El tipo de valor devuelto debe ser 'void', 'Task' o 'ValueTask' Se debe especificar el parámetro de atributo -'InheritanceBehavior.BeforeEachDerivedClass' si la clase es 'abstract' -- No se debe especificar el parámetro del atributo 'InheritanceBehavior.BeforeEachDerivedClass' si la clase es 'sellada' +No se debe especificar el parámetro de atributo -'InheritanceBehavior.BeforeEachDerivedClass' si la clase es 'sealed' El tipo que declara estos métodos también debe respetar las reglas siguientes: -El tipo debe ser una clase @@ -251,6 +266,16 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: El tipo de argumento DataRow debe coincidir con el tipo de parámetro de método. Se producen errores de coincidencia en los índices: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Se encontraron dos tipos en conflicto para el parámetro genérico '{0}'. Los tipos en conflicto son '{1}' y '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + No se pudo inferir el tipo del parámetro genérico '{0}'. + + DataRow should only be set on a test method DataRow solo debe establecerse en un método de prueba @@ -353,18 +378,23 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - El miembro de '{0}.{1}' de '[DynamicData]' es un método, por lo que debe establecert 'DynamicDataSourceType.Method' + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' miembro '{0}.{1}' es un método, por lo que debe usar 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Method' (la detección automática es el valor predeterminado cuando no se especifica explícitamente y se recomienda) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' miembro '{0}.{1}' no es una propiedad ni un método. Solo se admiten propiedades y métodos. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - El miembro de '{0}.{1}' de '[DynamicData]' es una propiedad, por lo que debe establecer 'DynamicDataSourceType.Property' + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' miembro '{0}.{1}' es una propiedad, por lo que debe usar 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Property' (la detección automática es el valor predeterminado cuando no se especifica explícitamente y se recomienda) - '[DynamicDta]' member '{0}.{1}' is found more than once - El miembro '{0}.{1}' de '[DynamicData]' se encuentra más de una vez + '[DynamicData]' member '{0}.{1}' is found more than once + '[DynamicData]' miembro '{0}.{1}' se encuentra más de una vez @@ -629,7 +659,7 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -637,8 +667,8 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Los métodos de prueba, los métodos marcados con el atributo '[TestMethod]', deben respetar el siguiente diseño para que MSTest lo considere válido: - debe ser 'public' (o 'internal' si se establece el atributo '[assembly: DiscoverInternals]') - no debe ser 'static' -- no debe ser genérico -: no debe ser 'abstract' +- puede ser genérico siempre y cuando se puedan inferir parámetros de tipo y los tipos de argumento sean compatibles +- no debe ser 'abstract' - el tipo de valor devuelto debe ser 'void', 'Task' o 'ValueTask' - no debe ser 'async void' - no debe ser un método especial (finalizador, operador...). @@ -709,6 +739,16 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: '[DeploymentItem]' solo se puede especificar en la clase de prueba o el método de prueba + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Usar "Assert.ThrowsExactly" en lugar de "Assert.ThrowsException" + + + + Use newer methods to assert exceptions + Usar métodos más recientes para declarar excepciones + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. De forma predeterminada, MSTest ejecuta pruebas en el mismo ensamblado secuencialmente, lo que puede provocar limitaciones de rendimiento graves. Se recomienda habilitar el atributo de ensamblado ''[Parallelize]'' o, si se sabe que el ensamblado no se puede paralelizar, usar explícitamente el atributo de nivel de ensamblado ''[DoNotParallelize]''. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index ceb154ccab..e89cfa1e60 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -26,14 +26,14 @@ The type declaring these methods should also respect the following rules: – il ne doit pas être « async void » – Il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). – il ne doit pas être générique -– il ne doit accepter aucun paramètre +- il ne doit pas prendre de paramètre ou prendre un seul paramètre de type 'TestContext' - le type de retour doit être « vide », « Task » ou « ValueTask » Le type déclarant ces méthodes doit également respecter les règles suivantes : Le type doit être une classe -La classe doit être « public » ou « internal » (si le projet de test utilise l’attribut ’[DiscoverInternals]’) -La classe ne doit pas être» ’static » --La classe doit être marquée par « [TestClass] » (ou un attribut dérivé). +-La classe doit être marquée par « [TestClass] » (ou un attribut dérivé) -la classe ne doit pas être générique. @@ -117,14 +117,29 @@ Le type doit être une classe Les arguments d’assertion doivent être passés dans l’ordre approprié + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + Utilisez 'Assert.AreEqual'/'Assert.AreNotEqual' à la place de 'Assert.AreSame'/'Assert.AreNotSame' lors de la comparaison des types valeur. Le passage d’un type valeur à 'Assert.AreSame'/'Assert.AreNotSame' est encadré (création d’un objet). Dans la mesure où 'Assert.AreSame'/'Assert.AreNotSame' effectue la comparaison par référence, 'Assert.AreSame' échoue quand le boxing se produit et 'Assert.AreNotSame' passe toujours. + + + + Use '{0}' instead of '{1}' when comparing value types + Utiliser '{0}' au lieu de '{1}' lors de la comparaison de types valeur + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + N’utilisez pas 'Assert.AreSame' ou 'Assert.AreNotSame' avec des types valeur + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Préférez « Assert.ThrowsException » ou « Assert.ThrowsExceptionAsync » à « [ExpectedException] », car cela assure que seul l’appel attendu lève l’exception attendue. Les API d’assertion offrent également plus de flexibilité et vous permettent de déclarer des propriétés supplémentaires de l’exception. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Préférez « Assert.ThrowsExactly » ou « Assert.ThrowsExactlyAsync » à « [ExpectedException] », car il garantit que seul l’appel attendu lève l’exception attendue. Les API d’assertion offrent également plus de flexibilité et vous permettent de déclarer des propriétés supplémentaires de l’exception. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Préférer « Assert.ThrowsException/ThrowsExceptionAsync » à « [ExpectedException] » + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Préférez « Assert.ThrowsExactly/ThrowsExactlyAsync » à « [ExpectedException] » @@ -140,7 +155,7 @@ Le type doit être une classe -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -152,13 +167,13 @@ The type declaring these methods should also respect the following rules: -If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. Les méthodes marquées par « [ClassCleanup] » doivent respecter le schéma suivant pour être valides : --il ne peut pas être déclarée dans une classe générique si le mode ’InheritanceBehavior’ n’est pas activé. +-il ne peut pas être déclarée dans une classe générique si le mode ’InheritanceBehavior’ n’est pas activé – il doit être « public » – il doit être « static » – il ne doit pas être « async void » – Il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). – il ne doit pas être générique -– il ne doit accepter aucun paramètre +- il ne doit pas prendre de paramètre ou prendre un seul paramètre de type 'TestContext' - le type de retour doit être « vide », « Task » ou « ValueTask » - « InheritanceBehavior.BeforeEachDerivedClass » doit être spécifié si la classe est « abstract » -Le paramètre d’attribut « InheritanceBehavior.BeforeEachDerivedClass » ne doit pas être spécifié si la classe est « scellée » @@ -167,7 +182,7 @@ Le type déclarant ces méthodes doit également respecter les règles suivantes Le type doit être une classe -La classe doit être « public » ou « internal » (si le projet de test utilise l’attribut ’[DiscoverInternals]’) -La classe ne doit pas être « static » --Si la classe est « sealed », elle doit être marquée avec « [TestClass] » (ou un attribut dérivé). +-Si la classe est « sealed », elle doit être marquée avec « [TestClass] » (ou un attribut dérivé) -la classe ne doit pas être générique. @@ -251,6 +266,16 @@ Le type doit être une classe Le type d’argument DataRow doit correspondre au type de paramètre de la méthode. Des incompatibilités se produisent aux index :{0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Deux types en conflit ont été trouvés pour le paramètre générique '{0}'. Les types en conflit sont '{1}' et '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Impossible de déduire le type du paramètre générique '{0}'. + + DataRow should only be set on a test method DataRow ne doit être défini que sur une méthode de test @@ -353,18 +378,23 @@ Le type doit être une classe - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - Membre '[DynamicData]' '{0}.{1}' est une méthode, vous devez donc définir 'DynamicDataSourceType.Method' + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' membre ' {0}.{1}' est une méthode, vous devez donc utiliser 'DynamicDataSourceType.AutoDetect' ou 'DynamicDataSourceType.Method' (la détection automatique est la valeur par défaut quand elle n’est pas spécifiée explicitement et est recommandée) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' membre '{0}.{1}' n’est ni une propriété ni une méthode. Seules les propriétés et les méthodes sont prises en charge. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - Membre '[DynamicData]' '{0}.{1}' est une propriété, vous devez donc définir 'DynamicDataSourceType.Property' + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' membre « {0}.{1} » est une propriété, vous devez donc utiliser « DynamicDataSourceType.AutoDetect » ou « DynamicDataSourceType.Property » (la détection automatique est la valeur par défaut lorsqu’elle n’est pas spécifiée explicitement et est recommandée) - '[DynamicDta]' member '{0}.{1}' is found more than once - Membre « [DynamicDta] »{0}.{1}' est trouvé plusieurs fois + '[DynamicData]' member '{0}.{1}' is found more than once + '[DynamicData]' membre ' {0}.{1}' est trouvé plusieurs fois @@ -629,19 +659,19 @@ Le type doit être une classe Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' - it should not be a special method (finalizer, operator...). - Les méthodes de test, qui sont des méthodes marquées avec l’attribut « [TestMethod] », doivent respecter la disposition suivante pour être considérées comme valides par MSTest : -– il doit être « public » (ou « interne » si l’attribut « [assembly : DiscoverInternals] » est défini) -– le texte ne doit pas être « statique » -– il ne doit pas être générique + Les méthodes de test, c’est-à-dire les méthodes marquées de l’attribut « [TestMethod] », doivent respecter la disposition suivante pour être considérées comme valides par MSTest : +– elle doit être « publique » (ou « interne » si l’attribut « [assembly : DiscoverInternals] » est défini) +– elle ne doit pas être « statique » +– elle peut être générique tant que les paramètres de type peuvent être déduits et que les types d’arguments sont compatibles – il ne doit pas être « abstract » -- doit être « void » ou renvoyer « Task » ou « ValueTask » -– il ne doit pas être « async void » -- il ne doit pas s’agir d’une méthode spéciale (finaliseur, opérateur...). +– le type de retour doit être « void », « Task » ou « ValueTask » +– elle ne doit pas être « async void » +– elle ne doit pas être une méthode spéciale (finaliseur, opérateur...). @@ -709,6 +739,16 @@ Le type doit être une classe « [DeploymentItem] » ne peut être spécifié que sur une classe de test ou une méthode de test + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Utiliser 'Assert.ThrowsExactly' à la place de 'Assert.ThrowsException' + + + + Use newer methods to assert exceptions + Utiliser des méthodes plus nouvelles pour déclarer des exceptions + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. Par défaut, MSTest exécute des tests dans la même assembly de façon séquentielle, ce qui peut entraîner de graves limitations de performances. Il est recommandé d’activer l’attribut d’assemblée « [Parallelize] » pour exécuter des tests en parallèle ou, si l’assemblée est connu pour ne pas être parallélisable, d’utiliser explicitement l’attribut de niveau assemblée « [DoNotParallelize] ». diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index d6b5460f74..717fc30523 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -19,21 +19,21 @@ The type declaring these methods should also respect the following rules: -The class shouldn't be 'static' -The class should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. - I metodi contrassegnati con ‘[AssemblyCleanup]’ devono seguire il layout seguente per essere validi: + I metodi contrassegnati con ‘[AssemblyCleanup]' devono seguire il layout seguente per essere validi: -Non può essere dichiarato in una classe generica - Deve essere 'public' - Deve essere 'static' - Non deve essere 'async void' - Non deve essere un metodo speciale (finalizzatore, operatore...). - Non deve essere generico -- Non deve accettare alcun parametro +- Non deve accettare alcun parametro o accettare un singolo parametro di tipo 'TestContext' - il tipo restituito deve essere 'void', 'Task' o 'ValueTask' Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: -Il tipo deve essere una classe -La classe deve essere 'public' o 'internal' (se il progetto di test usa l'attributo '[DiscoverInternals]') -La classe non deve essere 'static' --La classe deve essere contrassegnata con '[TestClass]' (o un attributo derivato). +-La classe deve essere contrassegnata con '[TestClass]' (o un attributo derivato) -La classe non deve essere generica. @@ -117,14 +117,29 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Gli argomenti dell'asserzione devono essere passati nell'ordine corretto + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + Usare 'Assert.AreEqual'/'Assert.AreNotEqual' invece di 'Assert.AreSame'/'Assert.AreNotSame' durante il confronto dei tipi valore. Se si passa un tipo valore a 'Assert.AreSame'/'Assert.AreNotSame', verrà eseguito il boxing (creazione di un nuovo oggetto). Poiché 'Assert.AreSame'/'Assert.AreNotSame' esegue il confronto per riferimento, 'Assert.AreSame' non riuscirà quando si esegue la boxing e 'Assert.AreNotSame' passerà sempre. + + + + Use '{0}' instead of '{1}' when comparing value types + Usa '{0}' invece di '{1}' durante il confronto dei tipi di valore + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + Non usare 'Assert.AreSame' o 'Assert.AreNotSame' con i tipi di valore + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Preferisci 'Assert.ThrowsException' o 'Assert.ThrowsExceptionAsync' a '[ExpectedException]', perché garantisce che solo la chiamata prevista lanci l'eccezione prevista. Le API di asserzione forniscono anche una maggiore flessibilità e consentono l’asserzione delle proprietà aggiuntive dell'eccezione. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Preferire 'Assert.ThrowsExactly' o 'Assert.ThrowsExactlyAsync' a '[ExpectedException]' perché assicura che solo la chiamata prevista generi l'eccezione prevista. Le API di asserzione offrono anche maggiore flessibilità e consentono di dichiarare proprietà aggiuntive dell'eccezione. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Preferisci 'Assert.ThrowsException/ThrowsExceptionAsync' a '[ExpectedException]'. + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Preferire 'Assert.ThrowsExactly/ThrowsExactlyAsync' a '[ExpectedException]' @@ -140,7 +155,7 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -151,14 +166,14 @@ The type declaring these methods should also respect the following rules: -The class shouldn't be 'static' -If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. - I metodi contrassegnati con ‘[ClassCleanup]’ devono seguire il layout seguente per essere validi: + I metodi contrassegnati con ‘[ClassCleanup]' devono seguire il layout seguente per essere validi: -Non può essere dichiarato in una classe generica se la modalità 'InheritanceBehavior' non è impostata - Deve essere 'public' - Deve essere 'static' - Non deve essere 'async void' - Non deve essere un metodo speciale (finalizzatore, operatore...). - Non deve essere generico -- Non deve accettare alcun parametro +- Non deve accettare alcun parametro o accettare un singolo parametro di tipo 'TestContext' - il tipo restituito deve essere 'void', 'Task' o 'ValueTask' - È necessario specificare il parametro dell'attributo 'InheritanceBehavior.BeforeEachDerivedClass' se la classe è 'abstract' - Non è necessario specificare il parametro dell'attributo 'InheritanceBehavior.BeforeEachDerivedClass' se la classe è 'sealed' @@ -251,6 +266,16 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Il tipo di argomento di DataRow deve corrispondere al tipo di parametro del metodo. Errori di corrispondenza in corrispondenza degli indici: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Sono stati trovati due tipi in conflitto per il parametro generico '{0}'. I tipi in conflitto sono '{1}' e '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Impossibile dedurre il tipo del parametro generico '{0}'. + + DataRow should only be set on a test method DataRow deve essere impostato solo su un metodo di test @@ -353,17 +378,22 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - Il membro '[DynamicData]' '{0}.{1}' è un metodo, quindi impostare 'DynamicDataSourceType.Method' + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' membro '{0}.{1}' è un metodo, quindi è consigliabile usare 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Method'. Il rilevamento automatico è l'impostazione predefinita quando non è specificata in modo esplicito ed è consigliabile + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' membro '{0}.{1}' non è una proprietà né un metodo. Sono supportati solo metodi e proprietà. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - Il membro '[DynamicData]' '{0}.{1}' è una proprietà, quindi impostare 'DynamicDataSourceType.Property' + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' membro '{0}.{1}' è una proprietà, quindi è consigliabile usare 'DynamicDataSourceType.AutoDetect' o 'DynamicDataSourceType.Property'. Il rilevamento automatico è l'impostazione predefinita quando non è specificata in modo esplicito ed è consigliata - '[DynamicDta]' member '{0}.{1}' is found more than once + '[DynamicData]' member '{0}.{1}' is found more than once Il membro '[DynamicData]' '{0}.{1}' è stato trovato più di una volta @@ -629,19 +659,19 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' - it should not be a special method (finalizer, operator...). - I metodi di test, metodi contrassegnati con l'attributo '[TestMethod]', dovrebbero rispettare il layout seguente per essere considerati validi da MSTest: -- dovrebbe essere 'pubblico' (o 'interno' se l'attributo '[assembly: DiscoverInternals]' è impostato) -- non dovrebbe essere 'statico' -- non dovrebbe essere generico -- non dovrebbe essere 'astratto' -- il tipo restituito deve essere 'void', 'Task' o 'ValueTask' -- non dovrebbe essere 'asincrono nullo' -- non dovrebbe essere un metodo speciale (finalizzatore, operatore...). + I metodi di test, metodi contrassegnati con l'attributo '[TestMethod]', devono rispettare il layout seguente per essere considerati validi da MSTest: +- devono essere “public†(o “internal†se l'attributo “[assembly: DiscoverInternals]†è impostato) +- non devono essere “static†+- possono essere generici purché i parametri di tipo possano essere dedotti e i tipi di argomento siano compatibili +- non devono essere “abstract†+- il tipo restituito deve essere “voidâ€, “Task†o “ValueTask†+- non devono essere “async void†+- non devono essere un metodo speciale (finalizzatore, operatore...). @@ -709,6 +739,16 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: '[DeploymentItem]' può essere specificato solo per la classe di test o il metodo di test + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Usare 'Assert.ThrowsExactly' invece di 'Assert.ThrowsException' + + + + Use newer methods to assert exceptions + Usa metodi più recenti per dichiarare le eccezioni + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. Per impostazione predefinita, MSTest esegue i test in sequenza nello stesso assemby, il che può causare gravi limitazioni delle prestazioni. È consigliabile abilitare l'attributo di assembly '[Parallelize]' per eseguire i test in parallelo, o, se l'assembly non è parallelizzabile, usare in modo esplicito l'attributo a livello di assembly '[DoNotParallelize]'. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index c1d6a50590..d975c377ec 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -26,7 +26,7 @@ The type declaring these methods should also respect the following rules: - 'async void' ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ - 特殊ãªãƒ¡ã‚½ãƒƒãƒ‰ (ãƒ•ã‚¡ã‚¤ãƒŠãƒ©ã‚¤ã‚¶ãƒ¼ã€æ¼”ç®—å­...) ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 - ジェãƒãƒªãƒƒã‚¯ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ -- パラメーターをå—ã‘å–ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ +- パラメーターをå—ã‘å–らãªã„ã‹ã€'TestContext' 型㮠1 ã¤ã®ãƒ‘ラメーターをå—ã‘å–ã‚‹å¿…è¦ãŒã‚りã¾ã™ - 戻り値ã®åž‹ãŒ 'void'ã€'Task'ã€ã¾ãŸã¯ 'ValueTask' ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ ã“れらã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’宣言ã™ã‚‹åž‹ã‚‚ã€æ¬¡ã®è¦å‰‡ã«å¾“ã†å¿…è¦ãŒã‚りã¾ã™: @@ -117,14 +117,29 @@ The type declaring these methods should also respect the following rules: ã‚¢ã‚µãƒ¼ã‚·ãƒ§ãƒ³å¼•æ•°ã¯æ­£ã—ã„é †åºã§æ¸¡ã™å¿…è¦ãŒã‚りã¾ã™ + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + 値ã®åž‹ã‚’比較ã™ã‚‹ã¨ãã¯ã€'Assert.AreSame'/'Assert.AreNotSame' ã®ä»£ã‚り㫠'Assert.AreEqual'/'Assert.AreNotEqual' を使用ã—ã¦ãã ã•ã„。値ã®åž‹ã‚’ 'Assert.AreSame'/'Assert.AreNotSame' ã«æ¸¡ã™ã¨ãƒœãƒƒã‚¯ã‚¹åŒ–ã•れã¾ã™ (æ–°ã—ã„オブジェクトãŒä½œæˆã•れã¾ã™)。'Assert.AreSame'/'Assert.AreNotSame' ã¯å‚ç…§ã«ã‚ˆã‚‹æ¯”較を行ã†ãŸã‚ã€ãƒœãƒƒã‚¯ã‚¹åŒ–ãŒç™ºç”Ÿã™ã‚‹ã¨ 'Assert.AreSame' ã¯å¤±æ•—ã—ã€'Assert.AreNotSame' ã¯å¸¸ã«æ¸¡ã•れã¾ã™ã€‚ + + + + Use '{0}' instead of '{1}' when comparing value types + 値ã®åž‹ã‚’比較ã™ã‚‹ã¨ãã« '{1}' ã®ä»£ã‚り㫠'{0}' を使用ã™ã‚‹ + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + 値型㫠'Assert.AreSame' ã¾ãŸã¯ 'Assert.AreNotSame' を使用ã—ãªã„ã§ãã ã•ã„ + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - '[ExpectedException]' よりも 'Assert.ThrowsException' ã¾ãŸã¯ 'Assert.ThrowsExceptionAsync' を優先ã—ã¾ã™ã€‚ã“れã¯ã€äºˆæœŸã•れãŸå‘¼ã³å‡ºã—ã®ã¿ãŒäºˆæœŸã•れãŸä¾‹å¤–をスローã™ã‚‹ã‚ˆã†ã«ã™ã‚‹ãŸã‚ã§ã™ã€‚アサート API も柔軟性ãŒé«˜ãã€ä¾‹å¤–ã®è¿½åŠ ãƒ—ãƒ­ãƒ‘ãƒ†ã‚£ã‚’ã‚¢ã‚µãƒ¼ãƒˆã§ãã¾ã™ã€‚ + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + '[ExpectedException]' よりも 'Assert.ThrowsExactly' ã¾ãŸã¯ 'Assert.ThrowsExactlyAsync' を優先ã—ã¾ã™ã€‚ã“れã¯ã€äºˆæœŸã•れãŸå‘¼ã³å‡ºã—ã®ã¿ãŒäºˆæœŸã•れãŸä¾‹å¤–をスローã™ã‚‹ã‚ˆã†ã«ã™ã‚‹ãŸã‚ã§ã™ã€‚アサート API ã‚‚ã•ã‚‰ã«æŸ”軟性ãŒé«˜ãã€ã“れã«ã‚ˆã‚Šä¾‹å¤–ã®è¿½åŠ ãƒ—ãƒ­ãƒ‘ãƒ†ã‚£ã‚’ã‚¢ã‚µãƒ¼ãƒˆã§ãã¾ã™ã€‚ - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - '[ExpectedException]' よりも 'Assert.ThrowsException/ThrowsExceptionAsync' を優先ã—ã¾ã™ + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + '[ExpectedException]' よりも 'Assert.ThrowsExactly/ThrowsExactlyAsync' を優先ã™ã‚‹ @@ -140,7 +155,7 @@ The type declaring these methods should also respect the following rules: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -158,10 +173,10 @@ The type declaring these methods should also respect the following rules: - 'async void' ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ - 特殊ãªãƒ¡ã‚½ãƒƒãƒ‰ (ãƒ•ã‚¡ã‚¤ãƒŠãƒ©ã‚¤ã‚¶ãƒ¼ã€æ¼”ç®—å­...) ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。 - ジェãƒãƒªãƒƒã‚¯ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ -- パラメーターをå—ã‘å–ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ +- パラメーターをå—ã‘å–らãªã„ã‹ã€'TestContext' 型㮠1 ã¤ã®ãƒ‘ラメーターをå—ã‘å–ã‚‹å¿…è¦ãŒã‚りã¾ã™ - 戻り値ã®åž‹ãŒ 'void'ã€'Task'ã€ã¾ãŸã¯ 'ValueTask' ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ -- クラス㌠'abstract' ã§ã‚ã‚‹å ´åˆã¯ 'InheritanceBehavior.BeforeEachDerivedClass' 属性パラメーターーを指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ -- クラス㌠'sealed' ã§ã‚ã‚‹å ´åˆã¯ 'InheritanceBehavior.BeforeEachDerivedClass' 属性パラメーターーを指定ã—ãªã„å¿…è¦ãŒã‚りã¾ã™ +- クラス㌠'abstract' ã§ã‚ã‚‹å ´åˆã¯ 'InheritanceBehavior.BeforeEachDerivedClass' 属性パラメーターを指定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ +- クラス㌠'sealed' ã§ã‚ã‚‹å ´åˆã¯ 'InheritanceBehavior.BeforeEachDerivedClass' 属性パラメーターを指定ã—ãªã„å¿…è¦ãŒã‚りã¾ã™ ã“れらã®ãƒ¡ã‚½ãƒƒãƒ‰ã‚’宣言ã™ã‚‹åž‹ã‚‚ã€æ¬¡ã®è¦å‰‡ã«å¾“ã†å¿…è¦ãŒã‚りã¾ã™: - åž‹ã¯ã‚¯ãƒ©ã‚¹ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ @@ -251,6 +266,16 @@ The type declaring these methods should also respect the following rules: DataRow 引数ã®åž‹ã¯ã€ãƒ¡ã‚½ãƒƒãƒ‰ パラメーターã®åž‹ã¨ä¸€è‡´ã•ã›ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚インデックスã§ä¸ä¸€è‡´ãŒç™ºç”Ÿã—ã¾ã—ãŸ: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + ジェãƒãƒªãƒƒã‚¯ パラメーター '{0}' ã« 2 ã¤ã®ç«¶åˆã™ã‚‹åž‹ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚ç«¶åˆã™ã‚‹åž‹ã¯ '{1}' ã§ '{2}'。 + + + + The type of the generic parameter '{0}' could not be inferred. + ジェãƒãƒªãƒƒã‚¯ パラメーター '{0}' ã®åž‹ã‚’推論ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ + + DataRow should only be set on a test method DataRow ã¯ãƒ†ã‚¹ãƒˆ メソッドã«ã®ã¿è¨­å®šã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ @@ -353,18 +378,23 @@ The type declaring these methods should also respect the following rules: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - '[DynamicData]' メンãƒãƒ¼ '{0}.{1}' ã¯ãƒ¡ã‚½ãƒƒãƒ‰ã§ã‚ã‚‹ãŸã‚ã€'DynamicDataSourceType.Method' を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' メンãƒãƒ¼ '{0}.{1}' ã¯ãƒ¡ã‚½ãƒƒãƒ‰ã§ã‚ã‚‹ãŸã‚ã€'DynamicDataSourceType.AutoDetect' ã¾ãŸã¯ 'DynamicDataSourceType.Method' を使用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ (è‡ªå‹•æ¤œå‡ºã¯æ˜Žç¤ºçš„ã«æŒ‡å®šã•れã¦ã„ãªã„å ´åˆã®æ—¢å®šå€¤ã§ã‚ã‚Šã€æŽ¨å¥¨ã•れã¾ã™) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + メンãƒãƒ¼ '{0}.{1}' '[DynamicData]' プロパティã§ã‚‚メソッドã§ã‚‚ã‚りã¾ã›ã‚“。プロパティã¨ãƒ¡ã‚½ãƒƒãƒ‰ã®ã¿ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã™ã€‚ - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - '[DynamicData]' メンãƒãƒ¼ '{0}.{1}' ã¯ãƒ—ロパティã§ã‚ã‚‹ãŸã‚ã€'DynamicDataSourceType.Property' を設定ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' メンãƒãƒ¼ '{0}.{1}' ã¯ãƒ—ロパティã§ã‚ã‚‹ãŸã‚ã€'DynamicDataSourceType.AutoDetect' ã¾ãŸã¯ 'DynamicDataSourceType.Property' を使用ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ (è‡ªå‹•æ¤œå‡ºã¯æ˜Žç¤ºçš„ã«æŒ‡å®šã•れã¦ã„ãªã„å ´åˆã®æ—¢å®šå€¤ã§ã‚ã‚Šã€æŽ¨å¥¨ã•れã¾ã™) - '[DynamicDta]' member '{0}.{1}' is found more than once - '[DynamicDta]' メンãƒãƒ¼ '{0}.{1}' ãŒè¤‡æ•°å›žè¦‹ã¤ã‹ã‚Šã¾ã—㟠+ '[DynamicData]' member '{0}.{1}' is found more than once + '[DynamicData]' メンãƒãƒ¼ '{0}.{1}' ãŒè¤‡æ•°å›žè¦‹ã¤ã‹ã‚Šã¾ã—㟠@@ -629,7 +659,7 @@ The type declaring these methods should also respect the following rules: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -637,7 +667,7 @@ The type declaring these methods should also respect the following rules: '[TestMethod]' 属性ã§ãƒžãƒ¼ã‚¯ã•れãŸãƒ†ã‚¹ãƒˆ メソッドã¯ã€MSTest ã«ã‚ˆã£ã¦æœ‰åйã¨è¦‹ãªã•れるよã†ã«ã€æ¬¡ã®ãƒ¬ã‚¤ã‚¢ã‚¦ãƒˆã‚’考慮ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™: - 'public' ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ ('[assembly: DiscoverInternals]' 属性ãŒè¨­å®šã•れã¦ã„ã‚‹å ´åˆã¯ 'internal' ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™) - 'static' ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ -- ジェãƒãƒªãƒƒã‚¯ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ +- 型パラメーターを推論ã§ãã€å¼•æ•°ã®åž‹ãŒäº’æ›æ€§ãŒã‚ã‚‹é™ã‚Šã€ã‚¸ã‚§ãƒãƒªãƒƒã‚¯ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ - 'abstract' ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ - 戻り値ã®åž‹ã¯ 'void'ã€'Task'ã€ã¾ãŸã¯ 'ValueTask' ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ - 'async void' ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“ @@ -709,6 +739,16 @@ The type declaring these methods should also respect the following rules: '[DeploymentItem]' ã¯ã€ãƒ†ã‚¹ãƒˆ クラスã¾ãŸã¯ãƒ†ã‚¹ãƒˆ メソッドã§ã®ã¿æŒ‡å®šã§ãã¾ã™ + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + 'Assert.ThrowsException' ã®ä»£ã‚り㫠'Assert.ThrowsExactly' を使用ã™ã‚‹ + + + + Use newer methods to assert exceptions + æ–°ã—ã„メソッドを使用ã—ã¦ä¾‹å¤–をアサートã™ã‚‹ + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. 既定ã§ã¯ã€MSTest ã¯åŒã˜ã‚¢ã‚»ãƒ³ãƒ–リ内ã§ãƒ†ã‚¹ãƒˆã‚’順番ã«å®Ÿè¡Œã™ã‚‹ãŸã‚ã€é‡å¤§ãªãƒ‘フォーマンス制é™ãŒç”Ÿã˜ã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚アセンブリ属性 '[Parallelize]' を有効ã«ã—ã¦ä¸¦åˆ—ã§ãƒ†ã‚¹ãƒˆã‚’実行ã™ã‚‹ã‹ã€ã‚¢ã‚»ãƒ³ãƒ–リãŒä¸¦åˆ—化ã§ããªã„ã“ã¨ãŒã‚ã‹ã£ã¦ã„ã‚‹å ´åˆã¯ã€ã‚¢ã‚»ãƒ³ãƒ–リ レベル属性 '[DoNotParallelize]' を明示的ã«ä½¿ç”¨ã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚ diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 26033c6a6e..978d132ac7 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -20,21 +20,21 @@ The type declaring these methods should also respect the following rules: -The class should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. '[AssemblyCleanup]'으로 í‘œì‹œëœ ë©”ì„œë“œê°€ 유효하려면 ë‹¤ìŒ ë ˆì´ì•„ì›ƒì„ ë”°ë¼ì•¼ 합니다. --제네릭 í´ëž˜ìФì—서 선언할 수 없습니다. +- 제네릭 í´ëž˜ìФì—서 선언할 수 없습니다. - 'public'ì´ì–´ì•¼ 합니다. - 'static'ì´ì–´ì•¼ 합니다. - 'async void'ê°€ 아니어야 합니다. - 특수 메서드(종료ìž, ì—°ì‚°ìž...)ê°€ 아니어야 합니다. - ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. -- 매개 변수를 사용하지 않아야 합니다. +- 매개 변수를 사용하거나 'TestContext' 형ì‹ì˜ ë‹¨ì¼ ë§¤ê°œ 변수를 사용해서는 안 ë©ë‹ˆë‹¤. - 반환 형ì‹ì€ 'void', 'Task' ë˜ëŠ” 'ValueTask'여야 합니다. ì´ëŸ¬í•œ 메서드를 선언하는 형ì‹ì€ ë‹¤ìŒ ê·œì¹™ë„ ì¤€ìˆ˜í•´ì•¼ 합니다. --형ì‹ì€ í´ëž˜ìŠ¤ì—¬ì•¼ 합니다. --í´ëž˜ìŠ¤ëŠ” 'public' ë˜ëŠ” 'internal'ì´ì–´ì•¼ 합니다(테스트 프로ì íЏì—서 '[DiscoverInternals]' íŠ¹ì„±ì„ ì‚¬ìš©í•˜ëŠ” 경우). --í´ëž˜ìŠ¤ëŠ” 'static'ì´ ë˜ì–´ì„œëŠ” 안 ë©ë‹ˆë‹¤. --í´ëž˜ìŠ¤ëŠ” '[TestClass]'(ë˜ëŠ” íŒŒìƒ íŠ¹ì„±)로 표시ë˜ì–´ì•¼ 합니다. --í´ëž˜ìŠ¤ëŠ” ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. +- 형ì‹ì€ í´ëž˜ìŠ¤ì—¬ì•¼ 합니다. +- í´ëž˜ìŠ¤ëŠ” 'public' ë˜ëŠ” 'internal'ì´ì–´ì•¼ 합니다(테스트 프로ì íЏì—서 '[DiscoverInternals]' íŠ¹ì„±ì„ ì‚¬ìš©í•˜ëŠ” 경우). +- í´ëž˜ìŠ¤ëŠ” 'static'ì´ ë˜ì–´ì„œëŠ” 안 ë©ë‹ˆë‹¤. +- í´ëž˜ìŠ¤ëŠ” '[TestClass]'(ë˜ëŠ” íŒŒìƒ íŠ¹ì„±)로 표시ë˜ì–´ì•¼ 합니다. +- í´ëž˜ìŠ¤ëŠ” ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. @@ -117,14 +117,29 @@ The type declaring these methods should also respect the following rules: 어설션 ì¸ìˆ˜ëŠ” 올바른 순서로 전달ë˜ì–´ì•¼ 합니다. + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + ê°’ 형ì‹ì„ 비êµí•  때 'Assert.AreSame'/'Assert.AreNotSame' 대신 'Assert.AreEqual'/'Assert.AreNotEqual'ì„ ì‚¬ìš©í•©ë‹ˆë‹¤. ê°’ 형ì‹ì„ 'Assert.AreSame'/'Assert.AreNotSame'ì— ì „ë‹¬í•˜ë©´ boxed(새 개체 만들기)ë©ë‹ˆë‹¤. 'Assert.AreSame'/'Assert.AreNotSame'ì€ ì°¸ì¡°ë¡œ 비êµí•˜ë¯€ë¡œ boxingì´ ë°œìƒí•  때 'Assert.AreSame'ì´ ì‹¤íŒ¨í•˜ê³  'Assert.AreNotSame'ì´ í•­ìƒ ì „ë‹¬ë©ë‹ˆë‹¤. + + + + Use '{0}' instead of '{1}' when comparing value types + ê°’ 형ì‹ì„ 비êµí•  때 '{1}' 대신 '{0}' 사용 + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + ê°’ 형ì‹ì— 'Assert.AreSame' ë˜ëŠ” 'Assert.AreNotSame'ì„ ì‚¬ìš©í•˜ì§€ 마세요. + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - 예ìƒë˜ëŠ” 호출만 예ìƒë˜ëŠ” 예외를 throw하ë„ë¡ ë³´ìž¥í•˜ëŠ” '[ExpectedException]'보다 'Assert.ThrowsException' ë˜ëŠ” 'Assert.ThrowsExceptionAsync'를 사용하는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤. ë˜í•œ Assert API는 ë” ë§Žì€ ìœ ì—°ì„±ì„ ì œê³µí•˜ê³  ì˜ˆì™¸ì˜ ì¶”ê°€ ì†ì„±ì„ 어설션할 수 있ë„ë¡ í•©ë‹ˆë‹¤. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + 예ìƒë˜ëŠ” 호출만 예ìƒë˜ëŠ” 예외를 ë˜ì§€ë„ë¡ í•˜ê¸° 위해 '[ExpectedException]보다 'Assert.ThrowsExactly' ë˜ëŠ” 'Assert.ThrowsExactlyAsync'를 사용하는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤. ë˜í•œ 어설션 API는 ë” ë§Žì€ ìœ ì—°ì„±ì„ ì œê³µí•˜ê³  ì˜ˆì™¸ì˜ ì¶”ê°€ ì†ì„±ì„ 어설션할 수 있습니다. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - '[ExpectedException]'보다 'Assert.ThrowsException/ThrowsExceptionAsync' 권장 + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + '[ExpectedException]'보다 'Assert.ThrowsExactly/ThrowsExactlyAsync' 선호 @@ -140,7 +155,7 @@ The type declaring these methods should also respect the following rules: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -152,23 +167,23 @@ The type declaring these methods should also respect the following rules: -If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. '[ClassCleanup]'으로 í‘œì‹œëœ ë©”ì„œë“œê°€ 유효하려면 ë‹¤ìŒ ë ˆì´ì•„ì›ƒì„ ë”°ë¼ì•¼ 합니다. --'InheritanceBehavior' 모드가 설정ë˜ì§€ ì•Šì€ ì œë„¤ë¦­ í´ëž˜ìФì—서 선언할 수 없습니다. +- 'InheritanceBehavior' 모드가 설정ë˜ì§€ ì•Šì€ ì œë„¤ë¦­ í´ëž˜ìФì—서 선언할 수 없습니다. - 'public'ì´ì–´ì•¼ 합니다. - 'static'ì´ì–´ì•¼ 합니다. - 'async void'ê°€ 아니어야 합니다. - 특수 메서드(종료ìž, ì—°ì‚°ìž...)ê°€ 아니어야 합니다. - ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. -- 매개 변수를 사용하지 않아야 합니다. +- 매개 변수를 사용하거나 'TestContext' 형ì‹ì˜ ë‹¨ì¼ ë§¤ê°œ 변수를 사용해서는 안 ë©ë‹ˆë‹¤. - 반환 형ì‹ì€ 'void', 'Task' ë˜ëŠ” 'ValueTask'여야 합니다. - í´ëž˜ìŠ¤ê°€ 'abstract'ì¸ ê²½ìš° 'InheritanceBehavior.BeforeEachDerivedClass' 특성 매개 변수를 지정해야 합니다. - í´ëž˜ìŠ¤ê°€ 'sealed'ì¸ ê²½ìš° 'InheritanceBehavior.BeforeEachDerivedClass' 특성 매개 변수를 지정하면 안 ë©ë‹ˆë‹¤. ì´ëŸ¬í•œ 메서드를 선언하는 형ì‹ì€ ë‹¤ìŒ ê·œì¹™ë„ ì¤€ìˆ˜í•´ì•¼ 합니다. --형ì‹ì€ í´ëž˜ìŠ¤ì—¬ì•¼ 합니다. --í´ëž˜ìŠ¤ëŠ” 'public' ë˜ëŠ” 'internal'ì´ì–´ì•¼ 합니다(테스트 프로ì íЏì—서 '[DiscoverInternals]' íŠ¹ì„±ì„ ì‚¬ìš©í•˜ëŠ” 경우). --í´ëž˜ìŠ¤ëŠ” 'static'ì´ ë˜ì–´ì„œëŠ” 안 ë©ë‹ˆë‹¤. --í´ëž˜ìŠ¤ê°€ 'sealed'ì¸ ê²½ìš° '[TestClass]'(ë˜ëŠ” íŒŒìƒ íŠ¹ì„±)로 표시ë˜ì–´ì•¼ 합니다. --í´ëž˜ìŠ¤ëŠ” ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. +- 형ì‹ì€ í´ëž˜ìŠ¤ì—¬ì•¼ 합니다. +- í´ëž˜ìŠ¤ëŠ” 'public' ë˜ëŠ” 'internal'ì´ì–´ì•¼ 합니다(테스트 프로ì íЏì—서 '[DiscoverInternals]' íŠ¹ì„±ì„ ì‚¬ìš©í•˜ëŠ” 경우). +- í´ëž˜ìŠ¤ëŠ” 'static'ì´ ë˜ì–´ì„œëŠ” 안 ë©ë‹ˆë‹¤. +- í´ëž˜ìŠ¤ê°€ 'sealed'ì¸ ê²½ìš° '[TestClass]'(ë˜ëŠ” íŒŒìƒ íŠ¹ì„±)로 표시ë˜ì–´ì•¼ 합니다. +- í´ëž˜ìŠ¤ëŠ” ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. @@ -251,6 +266,16 @@ The type declaring these methods should also respect the following rules: DataRow ì¸ìˆ˜ 형ì‹ì€ 메서드 매개 변수 형ì‹ê³¼ ì¼ì¹˜í•´ì•¼ 합니다. ì¸ë±ìФì—서 불ì¼ì¹˜ ë°œìƒ: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 제네릭 매개 변수 '{0}' ì¶©ëŒí•˜ëŠ” ë‘ ê°€ì§€ 형ì‹ì„ 찾았습니다. ì¶©ëŒí•˜ëŠ” 형ì‹ì€ '{1}' '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + 제네릭 매개 변수 '{0}' 형ì‹ì„ 유추할 수 없습니다. + + DataRow should only be set on a test method DataRow는 테스트 메서드ì—서만 설정해야 함 @@ -353,18 +378,23 @@ The type declaring these methods should also respect the following rules: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - '[DynamicData]' 멤버 '{0}.{1}'ì€(는) 메서드ì´ë¯€ë¡œ 'DynamicDataSourceType.Method'를 설정해야 합니다. + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' 멤버 '{0}.{1}'ì€(는) 'DynamicDataSourceType.AutoDetect' ë˜ëŠ” 'DynamicDataSourceType.Method'를 사용해야 하는 메서드입니다.(명시ì ìœ¼ë¡œ 지정하지 ì•Šì€ ê²½ìš° ìžë™ ê²€ìƒ‰ì´ ê¸°ë³¸ê°’ì´ë©° 권장) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' 멤버 '{0}.{1}'ì€(는) ì†ì„±ì´ë‚˜ 메서드가 아닙니다. ì†ì„± ë° ë©”ì„œë“œë§Œ ì§€ì›ë©ë‹ˆë‹¤. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - '[DynamicData]' 멤버 '{0}.{1}'ì€(는) ì†ì„±ì´ë¯€ë¡œ 'DynamicDataSourceType.Property'를 설정해야 합니다. + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' 멤버 '{0}.{1}'ì€ ì†ì„±ì´ë¯€ë¡œ 'DynamicDataSourceType.AutoDetect' ë˜ëŠ” 'DynamicDataSourceType.Property'를 사용해야 합니다(명시ì ìœ¼ë¡œ 지정ë˜ì§€ ì•Šì€ ê²½ìš° ìžë™ ê²€ìƒ‰ì´ ê¸°ë³¸ê°’ì´ë©° 권장). - '[DynamicDta]' member '{0}.{1}' is found more than once - '[DynamicDta]' 멤버 '{0}.{1}'ì„(를) ë‘ ë²ˆ ì´ìƒ 찾았습니다. + '[DynamicData]' member '{0}.{1}' is found more than once + '[DynamicData]' 멤버 '{0}.{1}'ì„(를) ë‘ ë²ˆ ì´ìƒ 찾았습니다. @@ -629,7 +659,7 @@ The type declaring these methods should also respect the following rules: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -637,7 +667,7 @@ The type declaring these methods should also respect the following rules: '[TestMethod]' 특성으로 í‘œì‹œëœ ë©”ì„œë“œì¸ í…ŒìŠ¤íŠ¸ 메서드는 MSTestì—서 유효한 것으로 간주ë˜ë„ë¡ ë‹¤ìŒ ë ˆì´ì•„ì›ƒì„ ì¤€ìˆ˜í•´ì•¼ 합니다. - 'public'(í˜¹ì€ '[assembly: DiscoverInternals]' íŠ¹ì„±ì´ ì„¤ì •ëœ ê²½ìš° 'internal')ì´ì–´ì•¼ 합니다. - 'static'ì´ ì•„ë‹ˆì–´ì•¼ 합니다. -- ì œë„¤ë¦­ì´ ì•„ë‹ˆì–´ì•¼ 합니다. +- í˜•ì‹ ë§¤ê°œ 변수를 유추할 수 있고 ì¸ìˆ˜ 형ì‹ì´ 호환ë˜ëŠ” 경우 ì œë„¤ë¦­ì´ ë  ìˆ˜ 있습니다. - 'abstract'ê°€ 아니어야 합니다. - 반환 형ì‹ì€ 'void', 'Task' ë˜ëŠ” 'ValueTask'여야 합니다. - 'async void'ê°€ 아니어야 합니다. @@ -709,6 +739,16 @@ The type declaring these methods should also respect the following rules: '[DeploymentItem]'ì€(는) 테스트 í´ëž˜ìФ ë˜ëŠ” 테스트 메서드ì—ë§Œ 지정할 수 있습니다. + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + 'Assert.ThrowsException' 대신 'Assert.ThrowsExactly' 사용 + + + + Use newer methods to assert exceptions + 최신 메서드를 사용하여 예외 어설션 + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. 기본ì ìœ¼ë¡œ MSTest는 ë™ì¼í•œ 어셈블리 ë‚´ì—서 테스트를 순차ì ìœ¼ë¡œ 실행하므로 심ê°í•œ 성능 ì œí•œì´ ë°œìƒí•  수 있습니다. 어셈블리 특성 '[Parallelize]'ê°€ 병렬로 테스트를 실행하ë„ë¡ ì„¤ì •í•˜ê±°ë‚˜ 어셈블리가 병렬화할 수 없는 것으로 알려진 경우 어셈블리 수준 특성 '[DoNotParallelize]'ì„(를) 명시ì ìœ¼ë¡œ 사용하는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 58ece0939a..842e9f1974 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -19,22 +19,22 @@ The type declaring these methods should also respect the following rules: -The class shouldn't be 'static' -The class should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. - Metody oznaczone za pomocÄ… [AssemblyCleanup] powinny być zgodne z nastÄ™pujÄ…cym ukÅ‚adem, aby byÅ‚y prawidÅ‚owe: + Metody oznaczone za pomocÄ… „[AssemblyCleanup]†powinny być zgodne z nastÄ™pujÄ…cym ukÅ‚adem, aby byÅ‚y prawidÅ‚owe: — nie może być zadeklarowana w klasie ogólnej — powinna być typu „public†— powinna mieć wartość „static†— nie powinna to być wartość „async void†— nie powinna to być metoda specjalna (finalizator, operator...). — nie powinna być ogólna -— nie powinna przyjmować żadnego parametru +— nie powinien przyjmować żadnego parametru lub przyjmować pojedynczego parametru typu „TestContext†— zwracany typ powinien mieć wartość „voidâ€, „Taks†lub „ValueTask†Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych reguÅ‚: -— Typ powinien być klasÄ… -— Klasa powinna mieć wartość „public†lub „internal†(jeÅ›li projekt testowy używa atrybutu „[DiscoverInternals]â€) -— Klasa nie powinna mieć wartoÅ›ci „static†-— Klasa powinna być oznaczona znakiem „[TestClass]†(lub atrybutem pochodnym) -— Klasa nie powinna być ogólna. +— typ powinien być klasÄ… +— klasa powinna mieć wartość „public†lub „internal†(jeÅ›li projekt testowy używa atrybutu „[DiscoverInternals]â€) +— klasa nie powinna mieć wartoÅ›ci „static†+— klasa powinna być oznaczona „[TestClass]†(lub atrybutem pochodnym) +— klasa nie powinna być ogólna. @@ -117,14 +117,29 @@ Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych regu Argumenty asercji powinny być przekazywane w poprawnej kolejnoÅ›ci + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + Użyj metody Assert.AreEqual'/'Assert.AreNotEqual' zamiast assert.AreSame'/'Assert.AreNotSame' podczas porównywania typów wartoÅ›ci. Przekazanie typu wartoÅ›ci do elementu "Assert.AreSame"/"Assert.AreNotSame" zostanie opakowane (tworzenie nowego obiektu). Ponieważ funkcja "Assert.AreSame"/"Assert.AreNotSame" wykonuje porównanie przez referencjÄ™, operacja "Assert.AreSame" zakoÅ„czy siÄ™ niepowodzeniem, gdy nastÄ…pi konwersja boxing, a element "Assert.AreNotSame" zawsze przejdzie. + + + + Use '{0}' instead of '{1}' when comparing value types + Użyj '{0}' zamiast '{1}' podczas porównywania typów wartoÅ›ci + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + Nie używaj elementu "Assert.AreSame" ani "Assert.AreNotSame" z typami wartoÅ›ci + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Preferuj element „Assert.ThrowsException†lub „Assert.ThrowsExceptionAsync†niż „[ExpectedException]â€, ponieważ gwarantuje, że tylko oczekiwane wywoÅ‚anie zgÅ‚osi oczekiwany wyjÄ…tek. Interfejsy API potwierdzenia zapewniajÄ… również wiÄ™kszÄ… elastyczność i pozwalajÄ… na potwierdzenie dodatkowych wÅ‚aÅ›ciwoÅ›ci wyjÄ…tku. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Preferuj opcjÄ™ „Assert.ThrowsExactly†lub „Assert.ThrowsExactlyAsync†zamiast „[ExpectedException]â€, ponieważ gwarantuje ona, że tylko oczekiwane wywoÅ‚anie rzuci oczekiwany wyjÄ…tek. Interfejsy API potwierdzenia zapewniajÄ… również wiÄ™kszÄ… elastyczność i pozwalajÄ… na potwierdzenie dodatkowych wÅ‚aÅ›ciwoÅ›ci wyjÄ…tku. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Preferuj element „Assert.ThrowsException/ThrowsExceptionAsync†od „[ExpectedException]†+ Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Preferuj opcjÄ™ „Assert.ThrowsExactly/ThrowsExactlyAsync†zamiast „[ExpectedException]†@@ -140,7 +155,7 @@ Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych regu -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -151,24 +166,24 @@ The type declaring these methods should also respect the following rules: -The class shouldn't be 'static' -If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. - Metody oznaczone za pomocÄ… [ClassCleanup] powinny być zgodne z nastÄ™pujÄ…cym ukÅ‚adem, aby byÅ‚y prawidÅ‚owe: --nie może być zadeklarowana w klasie ogólnej bez ustawionego trybu „InheritanceBehavior†+ Metody oznaczone za pomocÄ… „[ClassCleanup]†powinny być zgodne z nastÄ™pujÄ…cym ukÅ‚adem, aby byÅ‚y prawidÅ‚owe: +— nie może być zadeklarowana w klasie ogólnej bez ustawionego trybu „InheritanceBehavior†— powinna być typu „public†— powinna mieć wartość „static†— nie powinna to być wartość „async void†— nie powinna to być metoda specjalna (finalizator, operator...). — nie powinna być ogólna -— nie powinna przyjmować żadnego parametru +— nie powinien przyjmować żadnego parametru lub przyjmować pojedynczego parametru typu „TestContext†— zwracany typ powinien mieć wartość „voidâ€, „Taks†lub „ValueTask†— parametr atrybutu „InheritanceBehavior.BeforeEachDerivedClass†powinien być okreÅ›lony, jeÅ›li klasa ma wartość „abstract†— parametr atrybutu „InheritanceBehavior.BeforeEachDerivedClass†nie powinien być okreÅ›lony, jeÅ›li klasa ma wartość „sealed†Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych reguÅ‚: -— Typ powinien być klasÄ… -— Klasa powinna mieć wartość „public†lub „internal†(jeÅ›li projekt testowy używa atrybutu „[DiscoverInternals]â€) -— Klasa nie powinna mieć wartoÅ›ci „static†-— JeÅ›li klasa ma wartość „sealedâ€, powinna być oznaczona znakiem „[TestClass]†(lub atrybutem pochodnym) -— Klasa nie powinna być ogólna. +— typ powinien być klasÄ… +— klasa powinna mieć wartość „public†lub „internal†(jeÅ›li projekt testowy używa atrybutu „[DiscoverInternals]â€) +— klasa nie powinna mieć wartoÅ›ci „static†+— jeÅ›li klasa ma wartość „sealedâ€, powinna być oznaczona za pomocÄ… „[TestClass]†(lub atrybutem pochodnym) +— klasa nie powinna być ogólna. @@ -251,6 +266,16 @@ Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych regu Typ argumentu DataRow powinien być zgodny z typem parametru metody. W indeksach wystÄ™pujÄ… niezgodnoÅ›ci: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Znaleziono dwa typy powodujÄ…ce konflikt dla parametru ogólnego '{0}'. Typy powodujÄ…ce konflikty sÄ… '{1}' i '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Nie można wywnioskować typu '{0}' parametru ogólnego. + + DataRow should only be set on a test method Element DataRow powinien być ustawiony tylko dla metody testowej @@ -353,18 +378,23 @@ Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych regu - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - Element czÅ‚onkowski „{0}.{1}†„[DynamicData]†jest metodÄ…, dlatego należy ustawić „DynamicDataSourceType.Method†+ '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' skÅ‚adowej "{0}.{1}" jest metodÄ…, dlatego należy użyć elementu "DynamicDataSourceType.AutoDetect" lub "DynamicDataSourceType.Method" (autowykrywanie jest domyÅ›lne, gdy nie zostaÅ‚o jawnie okreÅ›lone i jest zalecane) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + skÅ‚adowa '[DynamicData]' "{0}.{1}" nie jest wÅ‚aÅ›ciwoÅ›ciÄ… ani metodÄ…. ObsÅ‚ugiwane sÄ… tylko wÅ‚aÅ›ciwoÅ›ci i metody. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - Element czÅ‚onkowski „{0}.{1}†„[DynamicData]†jest wÅ‚aÅ›ciwoÅ›ciÄ…, dlatego należy ustawić „DynamicDataSourceType.Property†+ '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' skÅ‚adowa "{0}.{1}" jest wÅ‚aÅ›ciwoÅ›ciÄ…, dlatego należy użyć wÅ‚aÅ›ciwoÅ›ci "DynamicDataSourceType.AutoDetect" lub "DynamicDataSourceType.Property" (wykrywanie automatyczne jest domyÅ›lne, gdy nie jest jawnie okreÅ›lone i jest zalecane) - '[DynamicDta]' member '{0}.{1}' is found more than once - Element czÅ‚onkowski „{0}.{1}†„[DynamicData]†zostaÅ‚ znaleziony wiÄ™cej niż raz + '[DynamicData]' member '{0}.{1}' is found more than once + Element czÅ‚onkowski „[DynamicData]†„{0}.{1}†znaleziono wiÄ™cej niż raz @@ -629,19 +659,19 @@ Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych regu Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' - it should not be a special method (finalizer, operator...). Metody testowe, czyli metody oznaczone atrybutem „[TestMethod]â€, powinny uwzglÄ™dniać nastÄ™pujÄ…cy ukÅ‚ad, aby platforma MSTest uznaÅ‚a je za prawidÅ‚owe: -— powinna to być wartość „public†(lub „internalâ€, jeÅ›li ustawiono atrybut „[assembly: DiscoverInternals]â€) -— nie powinna to być wartość „static†-— nie powinna to być wartość ogólna -— nie powinna to być wartość „abstract†+— powinny mieć wartość „public†(lub „internalâ€, jeÅ›li ustawiono atrybut „[assembly: DiscoverInternals]â€) +— nie powinny mieć wartoÅ›ci „static†+— mogÄ… być ogólne, o ile można wywnioskować parametry typu i typy argumentów sÄ… zgodne +— nie powinny mieć wartoÅ›ci „abstract†— zwracany typ powinien mieć wartość „voidâ€, „Task†lub „ValueTask†-— nie powinna to być wartość „async void†-— nie powinna to być metoda specjalna (finalizator, operator...). +— nie powinny mieć wartoÅ›ci „async void†+— nie powinny być metodÄ… specjalnÄ… (finalizator, operator...). @@ -709,6 +739,16 @@ Typ deklarujÄ…cy te metody powinien również przestrzegać nastÄ™pujÄ…cych regu Element „[DeploymentItem]†można okreÅ›lić tylko dla klasy testowej lub metody testowej + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Użyj metody "Assert.ThrowsExactly" zamiast elementu "Assert.ThrowsException" + + + + Use newer methods to assert exceptions + Użyj nowszych metod, aby potwierdzić wyjÄ…tki + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. DomyÅ›lnie platforma MSTest uruchamia testy w ramach tego samego zestawu sekwencyjnie, co może prowadzić do poważnego ograniczenia wydajnoÅ›ci. Zaleca siÄ™ włączenie atrybutu zestawu „[Parallelize]â€, aby uruchamiać testy równolegle, lub jeÅ›li zestaw na to nie pozwala — użycie jawnie atrybutu poziomu zestawu „[DoNotParallelize]â€. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index 2ef8aefbc9..9f7c169e0c 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -19,21 +19,21 @@ The type declaring these methods should also respect the following rules: -The class shouldn't be 'static' -The class should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. - Os métodos marcados com “[AssemblyCleanup]†devem seguir o seguinte layout para serem válidos: --não podem ser declarados em uma classe genérica --devem ser "públicos" --devem ser "estáticos" --não devem ser "nulos assíncronos" --não devem ser um método especial (finalizador, operador...). --não devem ser genéricos --não devem usar nenhum parâmetro --o tipo de retorno deve ser “nuloâ€, “Tarefa†ou “ValueTask†+ Os métodos marcados com '[AssemblyCleanup]' devem seguir o seguinte layout para serem válidos: +-não pode ser declarado em uma classe genérica +-deve ser 'public' +-deve ser 'static' +-não deve ser 'async void' +-não deve ser um método especial (finalizador, operador...). +-ele não deve ser genérico +-ele não deve ter nenhum parâmetro ou usar um único parâmetro do tipo 'TestContext' +-o tipo de retorno deve ser 'void', 'Task' ou 'ValueTask' -O tipo que declara esses métodos também deve respeitar as seguintes regras: +O tipo declarando esses métodos também deve respeitar as seguintes regras: -O tipo deve ser uma classe --A classe deve ser “público†ou 'â€interno†(se o projeto de teste estiver usando o atributo “[DiscoverInternals]â€) --A classe não deve ser “estático†--A classe deve ser marcada com “[TestClass]†(ou um atributo derivado) +-A classe deve ser 'public' ou 'internal' (se o projeto de teste estiver usando o '[DiscoverInternals]' atributo) +-A classe não deve ser 'static' +-A classe deve ser marcada com '[TestClass]' (ou um atributo derivado) -a classe não deve ser genérica. @@ -117,14 +117,29 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Os argumentos de asserção devem ser passados na ordem correta + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + Use 'Assert.AreEqual'/'Assert.AreNotEqual' em vez de 'Assert.AreSame'/'Assert.AreNotSame' ao comparar tipos de valor. Passar um tipo de valor para 'Assert.AreSame'/'Assert.AreNotSame' será em caixa (criando um novo objeto). Como 'Assert.AreSame'/'Assert.AreNotSame' faz a comparação por referência, 'Assert.AreSame' falhará quando ocorrer boxing e 'Assert.AreNotSame' sempre será aprovado. + + + + Use '{0}' instead of '{1}' when comparing value types + Usar '{0}' em vez de '{1}' ao comparar tipos de valor + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + Não use 'Assert.AreSame' ou 'Assert.AreNotSame' com tipos de valor + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Prefira 'Assert.ThrowsException' ou 'Assert.ThrowsExceptionAsync' '[ExpectedException]', pois isso garante que somente a chamada esperada lance a exceção esperada. As APIs assert também fornecem mais flexibilidade e permitem declarar propriedades extras da exceção. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Prefira 'Assert.ThrowsExactly' ou 'Assert.ThrowsExactlyAsync' em vez de '[ExpectedException]', pois isso garante que apenas a chamada esperada lance a exceção esperada. As APIs de assert também oferecem mais flexibilidade e permitem que você afirme propriedades adicionais da exceção. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Preferir 'Assert.ThrowsException/ThrowsExceptionAsync' em vez de '[ExpectedException]' + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Prefira 'Assert.ThrowsExactly/ThrowsExactlyAsync' em vez de '[ExpectedException]' @@ -140,7 +155,7 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -151,23 +166,23 @@ The type declaring these methods should also respect the following rules: -The class shouldn't be 'static' -If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute) -the class should not be generic. - Os métodos marcados com “[ClassCleanup]†devem seguir o seguinte layout para serem válidos: --não podem ser declarados em uma classe genérica sem que o modo “InheritanceBehavior†esteja definido --devem ser "públicos" --devem ser "estáticos" --não devem ser "nulos assíncronos" --não devem ser um método especial (finalizador, operador...). --não devem ser genéricos --não devem usar nenhum parâmetro --o tipo de retorno deve ser “nuloâ€, “Tarefa†ou “ValueTask†--"InheritanceBehavior.BeforeEachDerivedClass" deve ser especificado se a classe for "abstrato" --'InheritanceBehavior.BeforeEachDerivedClass' não deverá ser especificado se a classe for “selado†+ Os métodos marcados com '[ClassCleanup]' devem seguir o seguinte layout para serem válidos: +-não pode ser declarado em uma classe genérica sem que o modo 'InheritanceBehavior' esteja definido +-deve ser 'public' +-deve ser 'static' +-não deve ser 'async void' +-não deve ser um método especial (finalizador, operador...). +-ele não deve ser genérico +-ele não deve ter nenhum parâmetro ou usar um único parâmetro do tipo 'TestContext' +-o tipo de retorno deve ser 'void', 'Task' ou 'ValueTask' +O parâmetro de atributo -'InheritanceBehavior.BeforeEachDerivedClass' deve ser especificado se a classe for 'abstract' +O parâmetro de atributo -'InheritanceBehavior.BeforeEachDerivedClass' não deve ser especificado se a classe for 'sealed' -O tipo que declara esses métodos também deve respeitar as seguintes regras: +O tipo declarando esses métodos também deve respeitar as seguintes regras: -O tipo deve ser uma classe --A classe deve ser “público†ou 'â€interno†(se o projeto de teste estiver usando o atributo “[DiscoverInternals]â€) --A classe não deve ser “estático†--Se a classe for “seladoâ€, ela deverá ser marcada com “[TestClass]†(ou um atributo derivado) +-A classe deve ser 'public' ou 'internal' (se o projeto de teste estiver usando o '[DiscoverInternals]' atributo) +-A classe não deve ser 'static' +-Se a classe for 'sealed', ela deverá ser marcada com '[TestClass]' (ou um atributo derivado) -a classe não deve ser genérica. @@ -251,6 +266,16 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: O tipo de argumento DataRow deve corresponder ao tipo de parâmetro do método. Incompatibilidades ocorrem nos índices: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Foram encontrados dois tipos conflitativos para parâmetro genérico '{0}'. Os tipos conflitativos são '{1}' e '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Não foi possível inferir o tipo '{0}' parâmetro genérico. + + DataRow should only be set on a test method DataRow só deve ser definido em um método de teste @@ -353,18 +378,23 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - O membro "{0}.{1}" de "[DynamicData]" é um método; portanto, você deve definir "DynamicDataSourceType.Method" + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' membro '{0}.{1}' é um método, portanto, você deve usar 'DynamicDataSourceType.AutoDetect' ou 'DynamicDataSourceType.Method' (detectar automaticamente é o padrão quando não especificado explicitamente e é recomendado) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' membro '{0}.{1}' não é uma propriedade nem um método. Somente propriedades e métodos têm suporte. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - O membro "{0}.{1}" de "[DynamicData]" é uma propriedade; portanto, você deve definir "DynamicDataSourceType.Property" + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' membro '{0}.{1}' é uma propriedade, portanto, você deve usar 'DynamicDataSourceType.AutoDetect' ou 'DynamicDataSourceType.Property' (detectar automaticamente é o padrão quando não especificado explicitamente e é recomendado) - '[DynamicDta]' member '{0}.{1}' is found more than once - O membro "{0}.{1}" de "[DynamicDta]" foi encontrado mais de uma vez + '[DynamicData]' member '{0}.{1}' is found more than once + O membro '[DynamicData]' '{0}.{1}' foi encontrado mais de uma vez @@ -629,7 +659,7 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -637,11 +667,11 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Os métodos de teste, métodos marcados com o atributo '[TestMethod]', devem respeitar o seguinte layout para que sejam considerados válidos pelo MSTest: - deve ser "público" (ou "interno" se o atributo "[assembly: DiscoverInternals]" estiver definido) - não pode ser ''estático'' -- não pode ser genérico +- ele deve ser genérico, desde que os parâmetros de tipo possam ser inferidos e os tipos de argumentos sejam compatíveis - não pode ser ''abstrato'' -- o tipo de retorno que será 'void', 'Task' ou 'ValueTask' +- o tipo de retorno deve ser 'nulo', 'Tarefa' ou 'ValueTask' - não pode ser ''nulo assíncrono'' -- não deve ser um método especial (finalizador, operador...). +— não deve ser um método especial (finalizador, operador...). @@ -709,6 +739,16 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: '[DeploymentItem]' pode ser especificado apenas na classe de teste ou método de teste + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + Usar 'Assert.ThrowsExactly' em vez de 'Assert.ThrowsException' + + + + Use newer methods to assert exceptions + Usar métodos mais recentes para declarar exceções + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. Por padrão, o MSTest executa testes no mesmo assembly sequencialmente, o que pode levar a limitações graves de desempenho. É recomendável habilitar o atributo de assembly ''[Parallelize]'' para executar testes em paralelo ou se o assembly for conhecido por não ser paralelizável, para usar explicitamente o atributo de nível de assembly ''[DoNotParallelize]''. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index e53eabd2a4..05d8602608 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -23,12 +23,13 @@ The type declaring these methods should also respect the following rules: – не может быть объÑвлен Ð´Ð»Ñ ÑƒÐ½Ð¸Ð²ÐµÑ€Ñального клаÑÑа ("generic"); – должен быть общедоÑтупным ("public"); – должен быть ÑтатичеÑким ("static"); -– не быть аÑинхронным и не возвращающим Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ("async void") -– не быть Ñпециальным (метод завершениÑ, оператор…). -– не быть общим ("generic") +– не должен быть аÑинхронным и не возвращающим Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ("async void") +– не должен быть Ñпециальным (метод завершениÑ, оператор…). +– не должен быть общим ("generic") ; -– не принимать никаких параметров; -– тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть "void", "Task" или "ValueTask"; +– не должен принимать никаких параметров или должен принимать один параметр типа TestContext +; +– должен иметь тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ "void", "Task" или "ValueTask"; Тип, объÑвлÑющий такие методы, также должен ÑоответÑтвовать Ñледующим правилам: – должен быть клаÑÑом; @@ -119,14 +120,29 @@ The type declaring these methods should also respect the following rules: Ðргументы проверочных утверждений должны передаватьÑÑ Ð² правильном порÑдке + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + При Ñравнении типов значений иÑпользуйте "Assert.AreEqual"/"Assert.AreNotEqual" вмеÑто "Assert.AreSame"/"Assert.AreNotSame". Передача типа Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð² "Assert.AreSame"/"Assert.AreNotSame" будет добавлена в рамку (Ñоздание нового объекта). ПоÑкольку "Assert.AreSame"/"Assert.AreNotSame" выполнÑет Ñравнение по ÑÑылке, при бокÑе "Assert.AreSame" будет выполнÑтьÑÑ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹, а "Assert.AreNotSame" вÑегда будет проходить. + + + + Use '{0}' instead of '{1}' when comparing value types + ИÑпользовать '{0}' вмеÑто '{1}' при Ñравнении типов значений + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + Ðе иÑпользуйте Assert.AreSame или Assert.AreNotSame Ñ Ñ‚Ð¸Ð¿Ð°Ð¼Ð¸ значений + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Предпочитайте "Assert.ThrowsException" или "Assert.ThrowsExceptionAsync" вмеÑто "[ExpectedException]", так как Ñто гарантирует, что только ожидаемый вызов приводит к ожидаемому иÑключению. API-интерфейÑÑ‹ ÑƒÑ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ‚Ð°ÐºÐ¶Ðµ обеÑпечивают дополнительную гибкоÑть и позволÑÑŽÑ‚ утверждать дополнительные ÑвойÑтва иÑключениÑ. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Предпочтите "Assert.ThrowsExactly' или 'Assert.ThrowsExactlyAsync" вмеÑто "[ExpectedException]", так как Ñто гарантирует, что только ожидаемый вызов вызовет ожидаемое иÑключение. API-интерфейÑÑ‹ ÑƒÑ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ‚Ð°ÐºÐ¶Ðµ обеÑпечивают дополнительную гибкоÑть и позволÑÑŽÑ‚ утверждать дополнительные ÑвойÑтва иÑключениÑ. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - Предпочитать "Assert.ThrowsException/ThrowsExceptionAsync" вмеÑто "[ExpectedException]" + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + Предпочтите "Assert.ThrowsExactly/ThrowsExactlyAsync" вмеÑто "[ExpectedException]" @@ -142,7 +158,7 @@ The type declaring these methods should also respect the following rules: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -157,12 +173,13 @@ The type declaring these methods should also respect the following rules: – не может быть объÑвлен Ð´Ð»Ñ ÑƒÐ½Ð¸Ð²ÐµÑ€Ñального клаÑÑа, еÑли не уÑтановлен режим InheritanceBehavior; – должен быть общедоÑтупным ("public"); – должен быть ÑтатичеÑким ("static"); -– не быть аÑинхронным и не возвращающим Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ("async void") -– не быть Ñпециальным (метод завершениÑ, оператор…). -– не быть общим ("generic") +– не должен быть аÑинхронным и не возвращающим Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ("async void") +– не должен быть Ñпециальным (метод завершениÑ, оператор…). +– не должен быть общим ("generic") ; -– не принимать никаких параметров; -– тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть "void", "Task" или "ValueTask"; +– не должен принимать никаких параметров или должен принимать один параметр типа TestContext +; +– должен иметь тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ "void", "Task" или "ValueTask"; – еÑли клаÑÑ Ð°Ð±Ñтрактный, должен быть указан параметр атрибута "InheritanceBehavior.BeforeEachDerivedClass"; – еÑли клаÑÑ Ð·Ð°Ð¿ÐµÑ‡Ð°Ñ‚Ð°Ð½Ð½Ñ‹Ð¹, не Ñледует указывать параметр атрибута "InheritanceBehavior.BeforeEachDerivedClass"; @@ -255,6 +272,16 @@ The type declaring these methods should also respect the following rules: Тип аргумента DataRow должен ÑоответÑтвовать типу параметра метода. Обнаружены неÑÐ¾Ð²Ð¿Ð°Ð´ÐµÐ½Ð¸Ñ Ð² Ñледующих индекÑах: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Обнаружены два конфликтующих типа Ð´Ð»Ñ ÑƒÐ½Ð¸Ð²ÐµÑ€Ñального параметра '{0}'. Конфликтуют типы '{1}' и '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Ðе удалоÑÑŒ определить тип универÑального '{0}' параметра. + + DataRow should only be set on a test method Значение DataRow Ñледует задавать только Ð´Ð»Ñ Ð¼ÐµÑ‚Ð¾Ð´Ð° теÑта @@ -357,18 +384,23 @@ The type declaring these methods should also respect the following rules: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - Элемент "[DynamicData]" "{0}.{1}" ÑвлÑетÑÑ Ð¼ÐµÑ‚Ð¾Ð´Ð¾Ð¼, поÑтому Ñледует задать "DynamicDataSourceType.Method" + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' "{0}.{1}" ÑвлÑетÑÑ Ð¼ÐµÑ‚Ð¾Ð´Ð¾Ð¼, поÑтому Ñледует иÑпользовать "DynamicDataSourceType.AutoDetect" или "DynamicDataSourceType.Method" (автообнаружение иÑпользуетÑÑ Ð¿Ð¾ умолчанию, еÑли не указано Ñвно, и рекомендуетÑÑ) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' "{0}.{1}" не ÑвлÑетÑÑ ÑвойÑтвом или методом. ПоддерживаютÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ ÑвойÑтва и методы. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - Элемент "[DynamicData]" "{0}.{1}" ÑвлÑетÑÑ ÑвойÑтвом, поÑтому Ñледует задать "DynamicDataSourceType.Property" + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' "{0}.{1}" ÑвлÑетÑÑ ÑвойÑтвом, поÑтому Ñледует иÑпользовать "DynamicDataSourceType.AutoDetect" или "DynamicDataSourceType.Property" (автообнаружение иÑпользуетÑÑ Ð¿Ð¾ умолчанию, еÑли не указано Ñвно и рекомендуетÑÑ) - '[DynamicDta]' member '{0}.{1}' is found more than once - Элемент "[DynamicDta]" "{0}.{1}" обнаружен неÑколько раз + '[DynamicData]' member '{0}.{1}' is found more than once + Элемент "[DynamicData]" "{0}.{1}" обнаружен неÑколько раз @@ -639,7 +671,7 @@ The type declaring these methods should also respect the following rules: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -647,9 +679,9 @@ The type declaring these methods should also respect the following rules: Методы теÑта (методы, помеченные атрибутом "[TestMethod]") должны Ñоблюдать Ñледующую Ñтруктуру, чтобы ÑчитатьÑÑ Ð´Ð¾Ð¿ÑƒÑтимыми в MSTest: – должно быть приÑвоено значение "public" (или "internal", еÑли задан атрибут "[assembly: DiscoverInternals]") – не Ñледует приÑваивать значение "static" -– не Ñледует приÑваивать универÑальное значение +– могут быть универÑальными, еÑли можно вывеÑти параметры типа, а типы аргументов ÑвлÑÑŽÑ‚ÑÑ ÑовмеÑтимыми – не Ñледует приÑваивать значение "abstract" -- возвращаемый тип должен быть "void", "Task" или "ValueTask" +– возвращаемый тип должен быть "void", "Task" или "ValueTask" – не Ñледует приÑваивать значение "async void" – Ñто должен быть Ñпециальный метод (метод завершениÑ, оператор...). @@ -719,6 +751,16 @@ The type declaring these methods should also respect the following rules: Указать "[DeploymentItem]" можно только Ð´Ð»Ñ Ñ‚ÐµÑтового клаÑÑа или метода. + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + ИÑпользуйте Assert.ThrowsExactly вмеÑто Assert.ThrowsException + + + + Use newer methods to assert exceptions + ИÑпользовать более новые методы Ð´Ð»Ñ ÑƒÑ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¸Ñключений + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. По умолчанию MSTest выполнÑет теÑты в одной Ñборке поÑледовательно, что может привеÑти к Ñерьезному ограничению производительноÑти. РекомендуетÑÑ Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ атрибут Ñборки "[Parallelize]", чтобы выполнÑть теÑты параллельно, или Ñвно иÑпользовать атрибут ÑƒÑ€Ð¾Ð²Ð½Ñ Ñборки "[DoNotParallelize]", еÑли извеÑтно, что Ñборка не поддерживает параллелизацию. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index fe3079d220..4c2b557734 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -24,12 +24,12 @@ The type declaring these methods should also respect the following rules: -'public' olmalıdır -'static' olmalıdır -'async void' olmamalıdır --özel bir yöntem (sonlandırıcı, iÅŸleç...) olmamalıdır. +-özel bir metot (sonlandırıcı, iÅŸleç...) olmamalıdır. - genel olmamalıdır -- herhangi bir parametre almamalıdır -- dönüş türü 'void', 'Task' veya 'ValueTask' olmalı +-herhangi bir parametre almamalı veya 'TestContext' türünde tek bir parametre almalıdır +- dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır -Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: +Bu metotları bildiren türün ayrıca aÅŸağıdaki kurallara uyması gerekir: -Tür bir sınıf olmalıdır -Sınıf 'public' veya 'internal' olmalıdır (test projesi '[DiscoverInternals]' niteliÄŸini kullanıyorsa) -Sınıf 'static' olmamalıdır @@ -117,14 +117,29 @@ Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: Onay deyimi bağımsız deÄŸiÅŸkenleri doÄŸru sıralama düzeninde geçirilmelidir + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + DeÄŸer türlerini karşılaÅŸtırılırken 'Assert.AreSame'/'Assert.AreNotSame' yerine 'Assert.AreEqual'/'Assert.AreNotSame' kullanın. 'Assert.AreSame'/'Assert.AreNotSame' deÄŸerine bir deÄŸer türü geçirilemedi (yeni bir nesne oluÅŸturuluyor). 'Assert.AreSame'/'Assert.AreNotSame' baÅŸvuruya göre karşılaÅŸtırmayı yapar, kutulama gerçekleÅŸirken 'Assert.AreSame' baÅŸarısız olur ve 'Assert.AreNotSame' her zaman baÅŸarılı olur. + + + + Use '{0}' instead of '{1}' when comparing value types + DeÄŸer '{0}' karşılaÅŸtırılırken '{1}' yerine farklı bir ad kullanın + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + DeÄŸer türleriyle 'Assert.AreSame' veya 'Assert.AreNotSame' kullanma + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - Yalnızca beklenen çaÄŸrının beklenen özel durumu oluÅŸturmasını saÄŸladığı için '[ExpectedException]' yerine 'Assert.ThrowsException' veya 'Assert.ThrowsExceptionAsync' tercih edin. Onaylama API'leri de daha fazla esneklik saÄŸlar ve özel durumun ek özelliklerini onaylamanıza izin verir. + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + Yalnızca beklenen çaÄŸrının beklenen özel durumu oluÅŸturmasını saÄŸladığı için '[ExpectedException]' yerine 'Assert.ThrowsExactly' veya 'Assert.ThrowsExceptionAsync' tercih edin. Onaylama API'leri de daha fazla esneklik saÄŸlar ve özel durumun ek özelliklerini onaylamanıza izin verir. - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - '[ExpectedException]' yerine 'Assert.ThrowsException/ThrowsExceptionAsync' tercih edin + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + '[ExpectedException]' yerine 'Assert.ThrowsExactly/ThrowsExactlyAsync' tercih edin @@ -140,7 +155,7 @@ Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -156,18 +171,18 @@ The type declaring these methods should also respect the following rules: -'public' olmalıdır -'static' olmalıdır -'async void' olmamalıdır --özel bir yöntem (sonlandırıcı, iÅŸleç...) olmamalıdır. +-özel bir metot (sonlandırıcı, iÅŸleç...) olmamalıdır. - genel olmamalıdır -- herhangi bir parametre almamalıdır -- dönüş türü 'void', 'Task' veya 'ValueTask' olmalı --Sınıf 'abstract' ise 'InheritanceBehavior.BeforeEachDerivedClass' öznitelik parametresi belirtilmelidir. +-herhangi bir parametre almamalı veya 'TestContext' türünde tek bir parametre almalıdır +- dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır +-Sınıf 'abstract' ise 'InheritanceBehavior.BeforeEachDerivedClass' öznitelik parametresi belirtilmelidir -'InheritanceBehavior.BeforeEachDerivedClass' öznitelik parametresi, sınıf 'sealed' ise belirtilmemelidir -Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: +Bu metotları bildiren türün ayrıca aÅŸağıdaki kurallara uyması gerekir: -Tür bir sınıf olmalıdır -Sınıf 'public' veya 'internal' olmalıdır (test projesi '[DiscoverInternals]' niteliÄŸini kullanıyorsa) -Sınıf 'static' olmamalıdır --Sınıf 'sealed' ise '[TestClass]' (veya türetilmiÅŸ bir öznitelik) ile iÅŸaretlenmelidir. +-Sınıf 'sealed' ise '[TestClass]' (veya türetilmiÅŸ bir öznitelik) ile iÅŸaretlenmelidir -sınıf genel olmamalıdır. @@ -251,6 +266,16 @@ Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: DataRow bağımsız deÄŸiÅŸken türü, yöntem parametresinin türüyle eÅŸleÅŸmelidir. Dizinler sırasında uyuÅŸmazlıklar oluÅŸtu: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + Genel parametre türü için iki çakışan tür '{0}'. Çakışan türler '{1}' '{2}'. + + + + The type of the generic parameter '{0}' could not be inferred. + Genel parametre türü '{0}' çıkarsanamadı. + + DataRow should only be set on a test method DataRow yalnızca bir test yönteminde ayarlanmalıdır @@ -353,18 +378,23 @@ Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - '[DynamicData]' üyesi '{0}.{1}' bir yöntem olduÄŸundan 'DynamicDataSourceType.Method' öğesini ayarlamalısınız + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' üyesi '{0}.{1}' bir metot olduÄŸundan 'DynamicDataSourceType.AutoDetect' veya 'DynamicDataSourceType.Method' kullanmalısınız (otomatik algılama açıkça belirtilmediÄŸinden varsayılandır ve önerilir) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' '{0}.{1}' üyesi bir özellik veya yöntem deÄŸil. Yalnızca özellikler ve yöntemler desteklenir. - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - '[DynamicData]' üyesi '{0}.{1}' bir özellik olduÄŸundan 'DynamicDataSourceType.Property' öğesini ayarlamalısınız + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' üyesi '{0}.{1}' bir özelliktir, bu nedenle 'DynamicDataSourceType.AutoDetect' veya 'DynamicDataSourceType.Property' kullanmalısınız (otomatik algılama açıkça belirtilmediÄŸinden varsayılandır ve önerilir) - '[DynamicDta]' member '{0}.{1}' is found more than once - '[DynamicDta]' üyesi '{0}.{1}' birden çok kez bulundu + '[DynamicData]' member '{0}.{1}' is found more than once + '[DynamicData]' üyesi '{0}.{1}' birden çok kez bulundu @@ -630,16 +660,16 @@ Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' - it should not be a special method (finalizer, operator...). '[TestMethod]' özniteliÄŸiyle iÅŸaretlenen test yöntemleri, MSTest: - tarafından geçerli kabul edilmesi için aÅŸağıdaki düzene göre düzenlenmelidir. + tarafından geçerli kabul edilebilmesi için aÅŸağıdaki düzene uygun olacak ÅŸekilde düzenlenmelidir - 'public' (veya '[assembly: DiscoverInternals]' özniteliÄŸi ayarlanmışsa 'internal') olmalıdır - 'static' olmamalıdır -- genel olmamalıdır +- Tür parametreleri çıkarsandıkça ve bağımsız deÄŸiÅŸken türleri uyumlu olduÄŸu sürece genel olmalıdır - 'abstract' olmamalıdır - dönüş türü 'void', 'Task' veya 'ValueTask' olmalıdır - 'async void' olmamalıdır @@ -711,6 +741,16 @@ Bu yöntemleri bildiren tipin ayrıca aÅŸağıdaki kurallara uyması gerekir: '[DeploymentItem]' yalnızca test sınıfında veya test yönteminde belirtilebilir + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + 'Assert.ThrowsException' yerine 'Assert.ThrowsExactly' kullanın + + + + Use newer methods to assert exceptions + Özel durumları onaylamak için daha yeni yöntemler kullanın + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. Varsayılan olarak, MSTest hizmeti testleri aynı derleme içinde sırayla çalıştırır ve bu durum ciddi performans sınırlamalarına yol açabilir. Testleri paralel yürütmek için '[Parallelize]' derleme özniteliÄŸini etkinleÅŸtirmeniz önerilir veya derlemenin paralelleÅŸtirilebilir olduÄŸu biliniyorsa, doÄŸrudan '[DoNotParallelize]' derleme düzeyi özniteliÄŸini kullanmanız önerilir. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index 8f9c7c1d0b..a6e50d1ef6 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -24,16 +24,16 @@ The type declaring these methods should also respect the following rules: - 它应为“public†- 它应为“static†- 它ä¸åº”为“async void†-- 它ä¸åº”是特殊方法(终结器ã€è¿ç®—符...)。 +- 它ä¸åº”是特殊方法(终结器ã€è¿ç®—符...)。 - 它ä¸åº”是泛型的 -- 它ä¸åº”é‡‡ç”¨ä»»ä½•å‚æ•° +-它ä¸åº”é‡‡ç”¨ä»»ä½•å‚æ•°ï¼Œæˆ–采用类型为“TestContextâ€çš„å•ä¸ªå‚æ•° - 返回类型应为“voidâ€ã€â€œTaskâ€æˆ–“ValueTask†-声明这些方法的类型还应éµå¾ªä»¥ä¸‹è§„则: +声明这些方法的类型还应éµå¾ªä»¥ä¸‹è§„则: -类型应为类 --类应为“publicâ€æˆ–“internalâ€(如果测试项目正在使用“[DiscoverInternals]â€å±žæ€§) +-类应为“publicâ€æˆ–“internalâ€ï¼ˆå¦‚果测试项目正在使用“[DiscoverInternals]â€å±žæ€§ï¼‰ -ç±»ä¸åº”为“static†--应使用“[TestClass]â€(或派生属性)标记类 +-应使用“[TestClass]â€ï¼ˆæˆ–派生属性)标记类 -ç±»ä¸åº”是泛型的。 @@ -117,14 +117,29 @@ The type declaring these methods should also respect the following rules: 应按正确的顺åºä¼ é€’æ–­è¨€å‚æ•° + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + 比较值类型时,请使用 “Assert.AreEqualâ€/“Assert.AreNotEqualâ€ è€Œä¸æ˜¯ “Assert.AreSameâ€/“Assert.AreNotSameâ€ã€‚将值类型传递给 “Assert.AreSameâ€/“Assert.AreNotSameâ€å°†è£…ç®± (创建新的对象)。由于 'Assert.AreSame'/'Assert.AreNotSame' 按引用进行比较,所以当装箱å‘生时,“Assert.AreSame†将失败,并且 “Assert.AreNotSame†将始终通过。 + + + + Use '{0}' instead of '{1}' when comparing value types + 比较值类型时使用 '{0}' è€Œä¸æ˜¯ '{1}' + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + ä¸è¦å°† “Assert.AreSame†或 “Assert.AreNotSame†与值类型一起使用 + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - 首选 "Assert.ThrowsException" 或 "Assert.ThrowsExceptionAsync" è€Œä¸æ˜¯ "[ExpectedException]",因为它确ä¿åªæœ‰é¢„期调用æ‰ä¼šå¼•å‘预期异常。断言 API 还æä¾›æ›´å¤šçš„çµæ´»æ€§ï¼Œå¹¶å…许你断言异常的é¢å¤–属性。 + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + 首选“Assert.ThrowsExactlyâ€æˆ–“Assert.ThrowsExactlyAsyncâ€ï¼Œè€Œä¸æ˜¯â€œ[ExpectedException]â€ï¼Œå› ä¸ºå®ƒç¡®ä¿åªæœ‰é¢„期调用æ‰ä¼šå¼•å‘预期异常。断言 API 还æä¾›æ›´å¤šçš„çµæ´»æ€§ï¼Œå¹¶å…许你断言异常的é¢å¤–属性。 - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - 首选 "Assert.ThrowsException/ThrowsExceptionAsync" è€Œä¸æ˜¯ "[ExpectedException]" + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + 首先“Assert.ThrowsExactly/ThrowsExactlyAsyncâ€è€Œä¸æ˜¯â€œ[ExpectedException]†@@ -140,7 +155,7 @@ The type declaring these methods should also respect the following rules: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -156,18 +171,18 @@ The type declaring these methods should also respect the following rules: - 它应为“public†- 它应为“static†- 它ä¸åº”为“async void†-- 它ä¸åº”是特殊方法(终结器ã€è¿ç®—符...)。 +- 它ä¸åº”是特殊方法(终结器ã€è¿ç®—符...)。 - 它ä¸åº”是泛型的 -- 它ä¸åº”é‡‡ç”¨ä»»ä½•å‚æ•° +-它ä¸åº”é‡‡ç”¨ä»»ä½•å‚æ•°ï¼Œæˆ–采用类型为“TestContextâ€çš„å•ä¸ªå‚æ•° - 返回类型应为“voidâ€ã€â€œTaskâ€æˆ–“ValueTask†- "InheritanceBehavior.BeforeEachDerivedClass" å±žæ€§å‚æ•°åº”在类为 "abstract" 时指定 - "InheritanceBehavior.BeforeEachDerivedClass" å±žæ€§å‚æ•°ä¸åº”在类为 "sealed" 时指定 -声明这些方法的类型还应éµå¾ªä»¥ä¸‹è§„则: +声明这些方法的类型还应éµå¾ªä»¥ä¸‹è§„则: -类型应为类 --类应为“publicâ€æˆ–“internalâ€(如果测试项目正在使用“[DiscoverInternals]â€å±žæ€§) +-类应为“publicâ€æˆ–“internalâ€ï¼ˆå¦‚果测试项目正在使用“[DiscoverInternals]â€å±žæ€§ï¼‰ -ç±»ä¸åº”为“static†--如果类为“sealedâ€ï¼Œåˆ™åº”使用“[TestClass]â€(或派生属性)标记该类 +-如果类为“sealedâ€ï¼Œåº”使用“[TestClass]â€ï¼ˆæˆ–派生属性)标记该类 -ç±»ä¸åº”是泛型的。 @@ -251,6 +266,16 @@ The type declaring these methods should also respect the following rules: DataRow 傿•°ç±»åž‹åº”ä¸Žæ–¹æ³•å‚æ•°ç±»åž‹åŒ¹é…。索引处出现ä¸åŒ¹é…: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + å‘çŽ°æ³›åž‹å‚æ•° '{0}' 的两个冲çªç±»åž‹ã€‚冲çªç±»åž‹ '{1}' å’Œ '{2}'。 + + + + The type of the generic parameter '{0}' could not be inferred. + 无法推断 '{0}' æ³›åž‹å‚æ•°çš„类型。 + + DataRow should only be set on a test method DataRow 应仅在测试方法上进行设置 @@ -353,18 +378,23 @@ The type declaring these methods should also respect the following rules: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - "[DynamicData]" æˆå‘˜ "{0}.{1}" 是一个方法,因此应设置 "DynamicDataSourceType.Method" + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' æˆå‘˜ “{0}.{1}†是一个方法,因此应使用 “DynamicDataSourceType.AutoDetect†或 “DynamicDataSourceType.Methodâ€ï¼Œ(æœªæ˜¾å¼æŒ‡å®šæ—¶ï¼Œè‡ªåŠ¨æ£€æµ‹ä¸ºé»˜è®¤å€¼ï¼Œå»ºè®®) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' æˆå‘˜ “{0}.{1}â€ æ—¢ä¸æ˜¯å±žæ€§ä¹Ÿä¸æ˜¯æ–¹æ³•。仅支æŒå±žæ€§å’Œæ–¹æ³•。 - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - "[DynamicData]" æˆå‘˜ "{0}.{1}" 是一个属性,因此应设置 "DynamicDataSourceType.Property" + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' æˆå‘˜ “{0}.{1}†是属性,因此应使用 “DynamicDataSourceType.AutoDetect†或 “DynamicDataSourceType.Propertyâ€ï¼Œ(è‡ªåŠ¨æ£€æµ‹åœ¨æœªæ˜¾å¼æŒ‡å®šæ—¶ä¸ºé»˜è®¤å€¼ï¼Œå»ºè®®) - '[DynamicDta]' member '{0}.{1}' is found more than once - 多次找到 "[DynamicDta]" æˆå‘˜ "{0}.{1}" + '[DynamicData]' member '{0}.{1}' is found more than once + 多次找到 "[DynamicData]" æˆå‘˜â€œ{0}.{1}†@@ -629,19 +659,19 @@ The type declaring these methods should also respect the following rules: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' - it should not be a special method (finalizer, operator...). - 测试方法 (标记有“[TestMethod]â€å±žæ€§çš„æ–¹æ³•) 应éµå¾ªä»¥ä¸‹å¸ƒå±€ï¼Œä»¥ä¾› MSTest 将其视为有效: -- 它应该是“public†(如果设置了“[assembly: DiscoverInternals]â€å±žæ€§ï¼Œåˆ™åº”该是“internalâ€) -- 它ä¸åº”为“static†-- 它ä¸åº”是泛型的 -- 它ä¸åº”为“abstract†-- 返回类型应为“voidâ€ã€â€œTaskâ€æˆ–“ValueTask†-- 它ä¸åº”为“async void†-- 它ä¸åº”是特殊方法 (è¿ç®—符ã€è¿ç®—符...)。 + 测试方法(标记有 "[TestMethod]" 属性的方法)应éµå¾ªä»¥ä¸‹å¸ƒå±€ï¼Œä»¥ä¾¿ MSTest 将其视为有效: +- 它应该是 "public" (如果设置了 "[assembly: DiscoverInternals]" 属性,则应该是 "internal") +- 它ä¸åº”为 "static" +- åªè¦å¯ä»¥æŽ¨æ–­ç±»åž‹å‚æ•°å¹¶ä¸”å‚æ•°ç±»åž‹å…¼å®¹ï¼Œå®ƒå°±åº”该是泛型 +- 它ä¸åº”为 "abstract" +- 返回类型应为 "voidâ€ã€"Task" 或 "ValueTask" +- 它ä¸åº”为 "async void" +- 它ä¸åº”是特殊方法(终结器ã€è¿ç®—符...)。 @@ -709,6 +739,16 @@ The type declaring these methods should also respect the following rules: åªèƒ½å¯¹æµ‹è¯•类或测试方法指定 "[DeploymentItem]" + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + 使用 “Assert.ThrowsExactlyâ€ è€Œä¸æ˜¯ “Assert.ThrowsException†+ + + + Use newer methods to assert exceptions + 使用较新的方法断言异常 + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. 默认情况下,MSTest 将按顺åºåœ¨åŒä¸€ç¨‹åºé›†ä¸­è¿è¡Œæµ‹è¯•,这å¯èƒ½ä¼šå¯¼è‡´ä¸¥é‡çš„æ€§èƒ½é™åˆ¶ã€‚建议å¯ç”¨ç¨‹åºé›†å±žæ€§â€œ[Parallelize]â€ä»¥å¹¶è¡Œè¿è¡Œæµ‹è¯•,或者如果已知程åºé›†ä¸å¯å¹¶è¡Œï¼Œåˆ™æ˜¾å¼ä½¿ç”¨ç¨‹åºé›†çº§åˆ«å±žæ€§â€œDoNotParallelizeâ€ã€‚ diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index 9a4ba567c7..125e6a1d2b 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -10,7 +10,7 @@ -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' The type declaring these methods should also respect the following rules: @@ -21,19 +21,19 @@ The type declaring these methods should also respect the following rules: -the class should not be generic. 標示為 '[AssemblyCleanup]' 的方法應該éµå¾ªä¸‹åˆ—é…ç½®æ‰èƒ½æœ‰æ•ˆ: -å…¶ä¸èƒ½åœ¨æ³›åž‹é¡žåˆ¥ä¸Šå®£å‘Š -- 其應為 'public' -- 其應為 'static' -- 其䏿‡‰ç‚º 'async void' -- 其䏿‡‰ç‚ºç‰¹æ®Šæ–¹æ³• (完æˆé …ã€é‹ç®—å­...)。 -- 其䏿‡‰ç‚ºæ³›åž‹ -- 其䏿‡‰æŽ¥å—ä»»ä½•åƒæ•¸ -- 傳回類型應為 'void'ã€'Task' 或 'ValueTask' +-其應為 'public' +-其應為 'static' +-其䏿‡‰ç‚º 'async void' +-其䏿‡‰ç‚ºç‰¹æ®Šæ–¹æ³• (完æˆé …ã€é‹ç®—å­...)。 +-其䏿‡‰ç‚ºæ³›åž‹ +-其䏿‡‰æŽ¥å—ä»»ä½•åƒæ•¸ï¼Œæˆ–接å—類型為 'TestContext' çš„å–®ä¸€åƒæ•¸ +-傳回類型應為 'void'ã€'Task' 或 'ValueTask' 宣告這些方法的類型還應éµå¾ªä»¥ä¸‹è¦å‰‡: -類型應為類別 -類別應為 'public' 或 'internal' (如果測試專案使用 '[DiscoverInternals]' 屬性) -é¡žåˆ¥ä¸æ‡‰ç‚º 'static' --類別應標示為 '[TestClass]' (或è¡ç”Ÿå±¬æ€§)。 +-類別應標示為 '[TestClass]' (或è¡ç”Ÿå±¬æ€§) -é¡žåˆ¥ä¸æ‡‰ç‚ºæ³›åž‹ã€‚ @@ -117,14 +117,29 @@ The type declaring these methods should also respect the following rules: 應該以正確的順åºå‚³éžåˆ¤æ–·æç¤ºå¼•數 + + Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + 比較值類型時,請使用 'Assert.AreEqual'/'Assert.AreNotEqual' å–代 'Assert.AreSame'/'Assert.AreNotSame'。將值類型傳éžè‡³ 『Assert.AreSameã€/'Assert.AreNotSame' 將會在建立新物件) (方塊化。因為 'Assert.AreSame'/'Assert.AreNotSame' ä¾åƒç…§é€²è¡Œæ¯”較,當進行拳擊時,'Assert.AreSame' 將會失敗,而且 'Assert.AreNotSame' 一律會通éŽã€‚ + + + + Use '{0}' instead of '{1}' when comparing value types + 比較值類型時,使用 '{0}' è€Œéž '{1}' + + + + Don't use 'Assert.AreSame' or 'Assert.AreNotSame' with value types + ä¸è¦åœ¨å€¼é¡žåž‹ä¸­ä½¿ç”¨ 'Assert.AreSame' 或 'Assert.AreNotSame' + + - Prefer 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exeption. - å好 'Assert.ThrowsException' or 'Assert.ThrowsExceptionAsync' è€Œéž '[ExpectedException]',因為它å¯ç¢ºä¿åªæœ‰é æœŸçš„呼嫿œƒæ“²å‡ºé æœŸçš„例外狀æ³ã€‚判斷æç¤º API 也æä¾›æ›´å¤šå½ˆæ€§ï¼Œä¸¦å…許您斷言例外狀æ³çš„é¡å¤–屬性。 + Prefer 'Assert.ThrowsExactly' or 'Assert.ThrowsExactlyAsync' over '[ExpectedException]' as it ensures that only the expected call throws the expected exception. The assert APIs also provide more flexibility and allow you to assert extra properties of the exception. + å好 'Assert.ThrowsExactly' 或 'Assert.ThrowsExactlyAsync' è€Œä¸æ˜¯ '[ExpectedException]',因為它å¯ç¢ºä¿åªæœ‰é æœŸçš„呼嫿œƒæ“²å‡ºé æœŸçš„例外狀æ³ã€‚判斷æç¤º API 也æä¾›æ›´å¤šå½ˆæ€§ï¼Œä¸¦å…許您判斷æç¤ºä¾‹å¤–ç‹€æ³çš„é¡å¤–屬性。 - Prefer 'Assert.ThrowsException/ThrowsExceptionAsync' over '[ExpectedException]' - å好 'Assert.ThrowsException/ThrowsExceptionAsync' è€Œéž '[ExpectedException]' + Prefer 'Assert.ThrowsExactly/ThrowsExactlyAsync' over '[ExpectedException]' + å好 'Assert.ThrowsExactly/ThrowsExactlyAsync' è€Œä¸æ˜¯ '[ExpectedException]' @@ -140,7 +155,7 @@ The type declaring these methods should also respect the following rules: -it should not be 'async void' -it should not be a special method (finalizer, operator...). -it should not be generic --it should not take any parameter +-it should either not take any parameter, or take a single parameter of type 'TestContext' -return type should be 'void', 'Task' or 'ValueTask' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should be specified if the class is 'abstract' -'InheritanceBehavior.BeforeEachDerivedClass' attribute parameter should not be specified if the class is 'sealed' @@ -153,21 +168,21 @@ The type declaring these methods should also respect the following rules: -the class should not be generic. 標示 '[ClassCleanup]' 的方法應該éµå¾ªä¸‹åˆ—é…ç½®ï¼Œæ‰æœƒæœ‰æ•ˆ: -未設定 'InheritanceBehavior' 模å¼çš„æƒ…æ³ä¸‹ä¸èƒ½åœ¨æ³›åž‹é¡žåˆ¥ä¸Šå®£å‘Šå®ƒ -- 其應為 'public' -- 其應為 'static' -- 其䏿‡‰ç‚º 'async void' -- 其䏿‡‰ç‚ºç‰¹æ®Šæ–¹æ³• (完æˆé …ã€é‹ç®—å­...)。 -- 其䏿‡‰ç‚ºæ³›åž‹ -- 其䏿‡‰æŽ¥å—ä»»ä½•åƒæ•¸ -- 傳回類型應為 'void'ã€'Task' 或 'ValueTask' -- 如果類別為 'abstract',應指定 'InheritanceBehavior.BeforeEachDerivedClass' å±¬æ€§åƒæ•¸ã€‚ -- 如果類別為 'sealed'ï¼Œå‰‡ä¸æ‡‰æŒ‡å®š 'InheritanceBehavior.BeforeEachDerivedClass' å±¬æ€§åƒæ•¸ã€‚ +-其應為 'public' +-其應為 'static' +-其䏿‡‰ç‚º 'async void' +-其䏿‡‰ç‚ºç‰¹æ®Šæ–¹æ³• (完æˆé …ã€é‹ç®—å­...)。 +-其䏿‡‰ç‚ºæ³›åž‹ +-其䏿‡‰æŽ¥å—ä»»ä½•åƒæ•¸ï¼Œæˆ–接å—類型為 'TestContext' çš„å–®ä¸€åƒæ•¸ +-傳回類型應為 'void'ã€'Task' 或 'ValueTask' +-如果類別為 'abstract',應指定 'InheritanceBehavior.BeforeEachDerivedClass' å±¬æ€§åƒæ•¸ +-如果類別為 'sealed'ï¼Œå‰‡ä¸æ‡‰æŒ‡å®š 'InheritanceBehavior.BeforeEachDerivedClass' å±¬æ€§åƒæ•¸ 宣告這些方法的類型還應éµå¾ªä»¥ä¸‹è¦å‰‡: -類型應為類別 -類別應為 'public' 或 'internal' (如果測試專案使用 '[DiscoverInternals]' 屬性) -é¡žåˆ¥ä¸æ‡‰ç‚º 'static' --如果類別是 'sealed',則應標示為 '[TestClass]' (或è¡ç”Ÿå±¬æ€§)。 +-如果類別是 'sealed',則應標示為 '[TestClass]' (或è¡ç”Ÿå±¬æ€§) -é¡žåˆ¥ä¸æ‡‰ç‚ºæ³›åž‹ã€‚ @@ -251,6 +266,16 @@ The type declaring these methods should also respect the following rules: DataRow å¼•æ•¸é¡žåž‹æ‡‰ç¬¦åˆæ–¹æ³•åƒæ•¸é¡žåž‹ã€‚索引發生ä¸ç¬¦: {0} + + Found two conflicting types for generic parameter '{0}'. The conflicting types are '{1}' and '{2}'. + 發ç¾å…©å€‹è¡çªçš„æ³›åž‹åƒæ•¸é¡žåž‹ '{0}'。è¡çªçš„類型 '{1}' 且 '{2}'。 + + + + The type of the generic parameter '{0}' could not be inferred. + ç„¡æ³•æŽ¨æ–·æ³›åž‹åƒæ•¸ '{0}' 的類型。 + + DataRow should only be set on a test method DataRow åªèƒ½åœ¨æ¸¬è©¦æ–¹æ³•上設定 @@ -353,18 +378,23 @@ The type declaring these methods should also respect the following rules: - '[DynamicData]' member '{0}.{1}' is a method so you should set 'DynamicDataSourceType.Method' - '[DynamicData]' æˆå“¡ '{0}.{1}' 是方法,因此您應該設定 'DynamicDataSourceType.Method' + '[DynamicData]' member '{0}.{1}' is a method so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Method' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' æˆå“¡ '{0}.{1}' 是方法,因此您應該使用 'DynamicDataSourceType.AutoDetect' 或 'DynamicDataSourceType.Method' (æœªæ˜Žç¢ºæŒ‡å®šæ™‚ï¼Œè‡ªå‹•åµæ¸¬æ˜¯é è¨­å€¼ï¼Œå»ºè­°) + + + + '[DynamicData]' member '{0}.{1}' is not a property nor a method. Only properties and methods are supported. + '[DynamicData]' æˆå“¡ 『{0}.{1}ã€ ä¸æ˜¯å±¬æ€§ä¹Ÿä¸æ˜¯æ–¹æ³•ã€‚åªæ”¯æ´å±¬æ€§å’Œæ–¹æ³•。 - '[DynamicData]' member '{0}.{1}' is a property so you should set 'DynamicDataSourceType.Property' - '[DynamicData]' æˆå“¡ '{0}.{1}' 是屬性,因此您應該設定 'DynamicDataSourceType.Property' + '[DynamicData]' member '{0}.{1}' is a property so you should use 'DynamicDataSourceType.AutoDetect' or 'DynamicDataSourceType.Property' (auto detect is the default when not specified explicitly, and is recommended) + '[DynamicData]' æˆå“¡ '{0}.{1}' 是屬性,因此您應該使用 'DynamicDataSourceType.AutoDetect' 或 'DynamicDataSourceType.Property' (æœªæ˜Žç¢ºæŒ‡å®šè‡ªå‹•åµæ¸¬æ˜¯é è¨­å€¼ï¼Œå»ºè­°) - '[DynamicDta]' member '{0}.{1}' is found more than once - å¤šæ¬¡ç™¼ç¾ '[DynamicDta]' æˆå“¡ '{0}.{1}' + '[DynamicData]' member '{0}.{1}' is found more than once + '[DynamicData]' æˆå“¡ '{0}。{1}' å¤šæ¬¡ç™¼ç¾ @@ -629,7 +659,7 @@ The type declaring these methods should also respect the following rules: Test methods, methods marked with the '[TestMethod]' attribute, should respect the following layout to be considered valid by MSTest: - it should be 'public' (or 'internal' if '[assembly: DiscoverInternals]' attribute is set) - it should not be 'static' -- it should not be generic +- it should may be generic as long as type parameters can be inferred and argument types are compatible - it should not be 'abstract' - return type should be 'void', 'Task' or 'ValueTask' - it should not be 'async void' @@ -637,7 +667,7 @@ The type declaring these methods should also respect the following rules: 測試方法 (標示為 '[TestMethod]' 屬性的方法) 應該éµå¾ªä¸‹åˆ—é…置,讓 MSTest 視為有效: - 它應該是 'public' (如果設定 '[assembly: DiscoverInternals]' 屬性,則為 'internal') - 它應該是 'static' -- 它ä¸èƒ½æ˜¯æ³›åž‹ +- åªè¦å¯ä»¥æŽ¨æ–·é¡žåž‹åƒæ•¸ä¸”引數類型相容,則其應為一般 - 它ä¸èƒ½æ˜¯ 'abstract' - 傳回型別應為 'void'ã€'Task' 或 'ValueTask' - 它ä¸èƒ½æ˜¯ 'async void' @@ -709,6 +739,16 @@ The type declaring these methods should also respect the following rules: '[DeploymentItem]' åªèƒ½åœ¨æ¸¬è©¦é¡žæˆ–測試方法上指定 + + Use 'Assert.ThrowsExactly' instead of 'Assert.ThrowsException' + 使用 'Assert.ThrowsExactly' è€Œéž 'Assert.ThrowsException' + + + + Use newer methods to assert exceptions + ä½¿ç”¨è¼ƒæ–°çš„æ–¹æ³•åˆ¤æ–·ä¾‹å¤–ç‹€æ³ + + By default, MSTest runs tests within the same assembly sequentially, which can lead to severe performance limitations. It is recommended to enable assembly attribute '[Parallelize]' to run tests in parallel, or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute '[DoNotParallelize]'. 根據é è¨­ï¼ŒMSTest 會ä¾é †åºåŸ·è¡Œåœ¨ç›¸åŒçµ„件內的測試,這å¯èƒ½æœƒå°Žè‡´åš´é‡çš„æ•ˆèƒ½é™åˆ¶ã€‚建議啟用組件屬性 '[Parallelize]' 來平行執行測試,或者如果已知組件無法平行處ç†ï¼Œå‰‡æ˜Žç¢ºä½¿ç”¨çµ„件層級的屬性 '[DoNotParallelize]'。 diff --git a/src/Analyzers/MSTest.Internal.Analyzers/MSTest.Internal.Analyzers.csproj b/src/Analyzers/MSTest.Internal.Analyzers/MSTest.Internal.Analyzers.csproj new file mode 100644 index 0000000000..d2e12ea51d --- /dev/null +++ b/src/Analyzers/MSTest.Internal.Analyzers/MSTest.Internal.Analyzers.csproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + true + false + false + + + *$(MSBuildProjectFile)* + + + + + + + + diff --git a/src/Analyzers/MSTest.Internal.Analyzers/MSTestObsoleteTypesSuppressor.cs b/src/Analyzers/MSTest.Internal.Analyzers/MSTestObsoleteTypesSuppressor.cs new file mode 100644 index 0000000000..a844a1ee92 --- /dev/null +++ b/src/Analyzers/MSTest.Internal.Analyzers/MSTestObsoleteTypesSuppressor.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace MSTest.Internal.Analyzers; + +/// +/// MSTESTINT1: Suppress type is obsolete for known MSTest types. +/// +#pragma warning disable RS1004 // Recommend adding language support to diagnostic analyzer - For internal use only. We don't have VB code. +[DiagnosticAnalyzer(LanguageNames.CSharp)] +#pragma warning restore RS1004 // Recommend adding language support to diagnostic analyzer +public sealed class MSTestObsoleteTypesSuppressor : DiagnosticSuppressor +{ + // CS0618: Member is obsolete. + // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0618 + private const string SuppressedDiagnosticId = "CS0618"; + + internal static readonly SuppressionDescriptor Rule = + new("MSTESTINT1", SuppressedDiagnosticId, "Type is obsolete only so we can change accessibility"); + + public override ImmutableArray SupportedSuppressions { get; } = ImmutableArray.Create(Rule); + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + foreach (Diagnostic diagnostic in context.ReportedDiagnostics) + { + // It's very tedious to list all types that we obsoleted. We know for sure that this message is + // for types that can be used internally but not externally. + const string PublicTypeObsoleteMessage = "We will remove or hide this type starting with v4. If you are using this type, reach out to our team on https://github.com/microsoft/testfx."; + if (diagnostic.GetMessage(CultureInfo.InvariantCulture).Contains(PublicTypeObsoleteMessage)) + { + context.ReportSuppression(Suppression.Create(Rule, diagnostic)); + } + } + } +} diff --git a/src/Analyzers/MSTest.Internal.Analyzers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Internal.Analyzers/WellKnownTypeNames.cs new file mode 100644 index 0000000000..9ef8eb536e --- /dev/null +++ b/src/Analyzers/MSTest.Internal.Analyzers/WellKnownTypeNames.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers.Helpers; + +internal static class WellKnownTypeNames +{ + public const string System = "System"; + public const string SystemCollectionsGenericIEnumerable1 = "System.Collections.Generic.IEnumerable`1"; + public const string SystemDescriptionAttribute = "System.ComponentModel.DescriptionAttribute"; + public const string SystemIAsyncDisposable = "System.IAsyncDisposable"; + public const string SystemIDisposable = "System.IDisposable"; + public const string SystemNullable = "System.Nullable`1"; + public const string SystemReflectionMethodInfo = "System.Reflection.MethodInfo"; + public const string SystemRuntimeCompilerServicesITuple = "System.Runtime.CompilerServices.ITuple"; + public const string SystemThreadingTasksTask = "System.Threading.Tasks.Task"; + public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; + public const string SystemThreadingTasksValueTask = "System.Threading.Tasks.ValueTask"; + public const string SystemThreadingTasksValueTask1 = "System.Threading.Tasks.ValueTask`1"; +} diff --git a/src/Package/MSTest.Sdk/MSTest.Sdk.csproj b/src/Package/MSTest.Sdk/MSTest.Sdk.csproj index 601f6ccd2f..21f72c2b41 100644 --- a/src/Package/MSTest.Sdk/MSTest.Sdk.csproj +++ b/src/Package/MSTest.Sdk/MSTest.Sdk.csproj @@ -35,7 +35,7 @@ - <_TemplateProperties>MSTestEngineVersion=$(MSTestEngineVersion);MSTestVersion=$(Version);MicrosoftTestingPlatformVersion=$(Version.Replace('$(VersionPrefix)', '$(TestingPlatformVersionPrefix)'));MicrosoftTestingEntrepriseExtensionsVersion=$(MicrosoftTestingInternalFrameworkVersion);MicrosoftNETTestSdkVersion=$(MicrosoftNETTestSdkVersion);MicrosoftTestingExtensionsCodeCoverageVersion=$(MicrosoftTestingExtensionsCodeCoverageVersion);MicrosoftPlaywrightVersion=$(MicrosoftPlaywrightVersion);AspireHostingTestingVersion=$(AspireHostingTestingVersion);MicrosoftTestingExtensionsFakesVersion=$(MicrosoftTestingExtensionsFakesVersion) + <_TemplateProperties>MSTestEngineVersion=$(MSTestEngineVersion);MSTestVersion=$(Version);MicrosoftTestingPlatformVersion=$(Version.Replace('$(VersionPrefix)', '$(TestingPlatformVersionPrefix)'));MicrosoftNETTestSdkVersion=$(MicrosoftNETTestSdkVersion);MicrosoftTestingExtensionsCodeCoverageVersion=$(MicrosoftTestingExtensionsCodeCoverageVersion);MicrosoftPlaywrightVersion=$(MicrosoftPlaywrightVersion);AspireHostingTestingVersion=$(AspireHostingTestingVersion);MicrosoftTestingExtensionsFakesVersion=$(MicrosoftTestingExtensionsFakesVersion) + true true - $(MicrosoftTestingEntrepriseExtensionsVersion) + $(MicrosoftTestingExtensionsCommonVersion) true - $(MicrosoftTestingEntrepriseExtensionsVersion) + $(MicrosoftTestingExtensionsCommonVersion) true @@ -34,9 +43,6 @@ - diff --git a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template index 1da4144322..cf6c6f5242 100644 --- a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template +++ b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template @@ -16,7 +16,6 @@ ${AspireHostingTestingVersion} ${MicrosoftNETTestSdkVersion} ${MicrosoftPlaywrightVersion} - ${MicrosoftTestingEntrepriseExtensionsVersion} ${MicrosoftTestingExtensionsCodeCoverageVersion} ${MicrosoftTestingExtensionsFakesVersion} ${MicrosoftTestingPlatformVersion} diff --git a/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets b/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets index 39e814bc83..b8f3011a78 100644 --- a/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets +++ b/src/Package/MSTest.Sdk/Sdk/VSTest/VSTest.targets @@ -16,9 +16,6 @@ - diff --git a/src/Package/MSTest/MSTest.Linux.nuspec b/src/Package/MSTest/MSTest.NonWindows.nuspec similarity index 84% rename from src/Package/MSTest/MSTest.Linux.nuspec rename to src/Package/MSTest/MSTest.NonWindows.nuspec index 0559e35a8b..2a3c74c888 100644 --- a/src/Package/MSTest/MSTest.Linux.nuspec +++ b/src/Package/MSTest/MSTest.NonWindows.nuspec @@ -37,6 +37,13 @@ + + + + + + + diff --git a/src/Package/MSTest/MSTest.csproj b/src/Package/MSTest/MSTest.csproj index 05b34847f1..5263dba60d 100644 --- a/src/Package/MSTest/MSTest.csproj +++ b/src/Package/MSTest/MSTest.csproj @@ -7,7 +7,7 @@ true MSTest.nuspec - MSTest.Linux.nuspec + MSTest.NonWindows.nuspec $(OutputPath) MSTest MSTest TestFramework TestAdapter VisualStudio Unittest MSTestV2 Microsoft diff --git a/src/Package/MSTest/MSTest.nuspec b/src/Package/MSTest/MSTest.nuspec index 7506c0555c..7341858a85 100644 --- a/src/Package/MSTest/MSTest.nuspec +++ b/src/Package/MSTest/MSTest.nuspec @@ -52,6 +52,13 @@ + + + + + + + PACKAGE.md diff --git a/src/Package/Microsoft.Testing.Platform.DotNetTestClient/Microsoft.Testing.Platform.DotNetTestClient.csproj b/src/Package/Microsoft.Testing.Platform.DotNetTestClient/Microsoft.Testing.Platform.DotNetTestClient.csproj new file mode 100644 index 0000000000..f5524c8883 --- /dev/null +++ b/src/Package/Microsoft.Testing.Platform.DotNetTestClient/Microsoft.Testing.Platform.DotNetTestClient.csproj @@ -0,0 +1,54 @@ + + + + $(NetCurrent) + $(TestingPlatformVersionPrefix) + + + + true + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Package/Microsoft.Testing.Platform.DotNetTestClient/PACKAGE.md b/src/Package/Microsoft.Testing.Platform.DotNetTestClient/PACKAGE.md new file mode 100644 index 0000000000..857a8f0a35 --- /dev/null +++ b/src/Package/Microsoft.Testing.Platform.DotNetTestClient/PACKAGE.md @@ -0,0 +1,7 @@ +# Microsoft.Testing.Platform.DotNetTestClient + +This package contains types that are required by `dotnet test` in dotnet/SDK to communicate with an Microsoft.Testing.Platform test executable. + +Supported platforms: + +- .NET 10 diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs index 7bed8e2ec8..195136e80c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpCommandLineProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs index ee85390bf8..95c11353cc 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpEnvironmentVariableProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; @@ -198,12 +196,12 @@ public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnl } } - return Task.FromResult(errors.Length > 0 ? ValidationResult.Invalid(errors.ToString()) : ValidationResult.Valid()); + return errors.Length > 0 ? Task.FromResult(ValidationResult.Invalid(errors.ToString())) : ValidationResult.ValidTask; static void AddError(StringBuilder errors, string variableName, string? expectedValue, string? actualValue) { string actualValueString = actualValue ?? ""; - errors.AppendLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpInvalidEnvironmentVariableValueErrorMessage, variableName, expectedValue, actualValueString)); + errors.AppendLine(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpInvalidEnvironmentVariableValueErrorMessage, variableName, expectedValue, actualValueString)); } #endif } diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs index 9fdcbf8e24..ecec7a268e 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/CrashDumpProcessLifetimeHandler.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.Messages; @@ -65,7 +63,7 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH } ApplicationStateGuard.Ensure(_netCoreCrashDumpGeneratorConfiguration.DumpFileNamePattern is not null); - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpProcessCrashedDumpFileCreated, testHostProcessInformation.PID))); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpProcessCrashedDumpFileCreated, testHostProcessInformation.PID))); string expectedDumpFile = _netCoreCrashDumpGeneratorConfiguration.DumpFileNamePattern.Replace("%p", testHostProcessInformation.PID.ToString(CultureInfo.InvariantCulture)); if (File.Exists(expectedDumpFile)) @@ -74,7 +72,7 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH } else { - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CannotFindExpectedCrashDumpFile, expectedDumpFile))); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CannotFindExpectedCrashDumpFile, expectedDumpFile))); foreach (string dumpFile in Directory.GetFiles(Path.GetDirectoryName(expectedDumpFile)!, "*.dmp")) { await _messageBus.PublishAsync(this, new FileArtifact(new FileInfo(dumpFile), CrashDumpResources.CrashDumpDisplayName, CrashDumpResources.CrashDumpArtifactDescription)); diff --git a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj index f711a190c2..6d7d59466d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.CrashDump/Microsoft.Testing.Extensions.CrashDump.csproj @@ -42,7 +42,6 @@ This package extends Microsoft Testing Platform to provide a crash dump function - diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs index aeff5caa2a..9469725e51 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpActivityIndicator.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Extensions.HangDump.Serializers; using Microsoft.Testing.Platform.CommandLine; @@ -130,7 +127,7 @@ await _namedPipeClient.RequestReplyAsync( // Wait the connection from the testhost controller await _singleConnectionNamedPipeServer.WaitConnectionAsync(cancellationToken).TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); - await _logger.LogTraceAsync($"Test host controller connected"); + await _logger.LogTraceAsync("Test host controller connected"); } catch (OperationCanceledException ex) when (ex.CancellationToken == cancellationToken) { @@ -227,7 +224,7 @@ private Task SignalActivityIndicatorAsync() _signalActivity.Reset(); } - _logger.LogDebug($"Exit 'SignalActivityIndicatorAsync'"); + _logger.LogDebug("Exit 'SignalActivityIndicatorAsync'"); return Task.CompletedTask; } @@ -245,10 +242,10 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio await _namedPipeClient.RequestReplyAsync(new SessionEndSerializerRequest(), cancellationToken) .TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout, cancellationToken); - await _logger.LogDebugAsync($"Signal for test session end'"); + await _logger.LogDebugAsync("Signal for test session end'"); await ExitSignalActivityIndicatorTaskAsync(); - await _logger.LogTraceAsync($"Signaled by process for it's exit"); + await _logger.LogTraceAsync("Signaled by process for it's exit"); _sessionEndCalled = true; } diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs index 823de1e328..b69d97480a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpCommandLineProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs index 6c855167bf..dd31977754 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpEnvironmentVariableProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs index e09eab94e6..548b15f972 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/HangDumpProcessLifetimeHandler.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -#if NETCOREAPP -using System.Runtime.InteropServices; -#endif - using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Extensions.HangDump.Serializers; using Microsoft.Testing.Platform; @@ -149,7 +144,7 @@ private async Task CallbackAsync(IRequest request) } else if (request is SessionEndSerializerRequest) { - await _logger.LogDebugAsync($"Session end received by the test host"); + await _logger.LogDebugAsync("Session end received by the test host"); _exitActivityIndicatorTask = true; #if NET if (_namedPipeClient is not null) @@ -225,7 +220,7 @@ public async Task OnTestHostProcessExitedAsync(ITestHostProcessInformation testH private async Task ActivityTimerAsync() { - _logger.LogDebug($"Wait for mutex name from the test host"); + _logger.LogDebug("Wait for mutex name from the test host"); if (!_mutexNameReceived.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout)) { @@ -249,7 +244,7 @@ private async Task ActivityTimerAsync() { if (_traceEnabled) { - _logger.LogTrace($"Wait for activity signal"); + _logger.LogTrace("Wait for activity signal"); } if (!_activityIndicatorMutex.WaitOne(_activityTimerValue)) @@ -276,7 +271,7 @@ private async Task ActivityTimerAsync() if (_traceEnabled) { - _logger.LogTrace($"Exit 'ActivityTimerAsync'"); + _logger.LogTrace("Exit 'ActivityTimerAsync'"); } } catch (AbandonedMutexException) @@ -295,7 +290,7 @@ private async Task ActivityTimerAsync() { try { - _logger.LogDebug($"Timeout is not fired release activity mutex handle to allow test host to close"); + _logger.LogDebug("Timeout is not fired release activity mutex handle to allow test host to close"); _activityIndicatorMutex.ReleaseMutex(); } catch (AbandonedMutexException) @@ -311,7 +306,7 @@ private async Task ActivityTimerAsync() } _activityIndicatorMutex.Dispose(); - _logger.LogDebug($"Activity indicator disposed"); + _logger.LogDebug("Activity indicator disposed"); if (timeoutFired) { @@ -325,7 +320,7 @@ private async Task TakeDumpAsync() ApplicationStateGuard.Ensure(_dumpType is not null); await _logger.LogInformationAsync($"Hang dump timeout({_activityTimerValue}) expired."); - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTimeoutExpired, _activityTimerValue))); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTimeoutExpired, _activityTimerValue))); string finalDumpFileName = _dumpFileNamePattern.Replace("%p", _testHostProcessInformation.PID.ToString(CultureInfo.InvariantCulture)); finalDumpFileName = Path.Combine(_configuration.GetTestResultDirectory(), finalDumpFileName); @@ -339,11 +334,11 @@ private async Task TakeDumpAsync() using (FileStream fs = File.OpenWrite(hangTestsFileName)) using (StreamWriter sw = new(fs)) { - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(ExtensionResources.RunningTestsWhileDumping)); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(ExtensionResources.RunningTestsWhileDumping)); foreach ((string testName, int seconds) in tests.Tests) { await sw.WriteLineAsync($"[{TimeSpan.FromSeconds(seconds)}] {testName}"); - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText($"[{TimeSpan.FromSeconds(seconds)}] {testName}")); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData($"[{TimeSpan.FromSeconds(seconds)}] {testName}")); } } @@ -352,7 +347,7 @@ private async Task TakeDumpAsync() await _logger.LogInformationAsync($"Creating dump filename {finalDumpFileName}"); - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, ExtensionResources.CreatingDumpFile, finalDumpFileName))); + await _outputDisplay.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.CreatingDumpFile, finalDumpFileName))); #if NETCOREAPP DiagnosticsClient diagnosticsClient = new(_testHostProcessInformation.PID); diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/FNV1HashHelper.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/FNV1HashHelper.cs index 26d5b0ea80..c25bfbbfab 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/FNV1HashHelper.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/Helpers/FNV1HashHelper.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Helpers; [ExcludeFromCodeCoverage] @@ -12,20 +10,6 @@ internal static class FNV_1aHashHelper /// Computes a hash of the string using the FNV-1a algorithm. /// Used by Roslyn. /// - public static uint ComputeStringHash(string s) - { - uint num = default; - if (s != null) - { - num = 2166136261u; - int num2 = 0; - while (num2 < s.Length) - { - num = (s[num2] ^ num) * 16777619; - num2++; - } - } - - return num; - } + public static uint ComputeStringHash(string input) => + input.Aggregate(2166136261u, (current, ch) => (ch ^ current) * 16777619); } diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt index 9a6866965b..7ae28f89cb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/PublicAPI/PublicAPI.Shipped.txt @@ -1,5 +1,5 @@ #nullable enable -Microsoft.Testing.Extensions.HangDumpExtensions Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook +Microsoft.Testing.Extensions.HangDumpExtensions static Microsoft.Testing.Extensions.HangDump.TestingPlatformBuilderHook.AddExtensions(Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! testApplicationBuilder, string![]! _) -> void static Microsoft.Testing.Extensions.HangDumpExtensions.AddHangDumpProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs b/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs index 7217148459..41f2c6586d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HangDump/WindowsMiniDumpWriteDump.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - using Microsoft.Win32.SafeHandles; namespace Microsoft.Testing.Extensions.Diagnostics; 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.MSBuild/MSBuildConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs index d46dbd47a5..124be7cbb5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildConsumer.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; - using Microsoft.Testing.Extensions.MSBuild.Serializers; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.Messages; diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs index dd4a62547c..7e830ab2b7 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/MSBuildTestApplicationLifecycleCallbacks.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Testing.Extensions.MSBuild.Serializers; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/FailedTestInfoRequestSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/FailedTestInfoRequestSerializer.cs index 2a4e07195f..e3425beaaf 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/FailedTestInfoRequestSerializer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/FailedTestInfoRequestSerializer.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Extensions.MSBuild.Serializers; -internal record FailedTestInfoRequest( +internal sealed record FailedTestInfoRequest( string DisplayName, bool IsCanceled, string? Duration, @@ -17,7 +17,7 @@ internal record FailedTestInfoRequest( string? CodeFilePath, int LineNumber) : IRequest; -internal class FailedTestInfoRequestSerializer : BaseSerializer, INamedPipeSerializer +internal sealed class FailedTestInfoRequestSerializer : BaseSerializer, INamedPipeSerializer { public int Id => 2; diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/ModuleInfoRequestSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/ModuleInfoRequestSerializer.cs index b5a9fe108d..062fd2d642 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/ModuleInfoRequestSerializer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/ModuleInfoRequestSerializer.cs @@ -6,9 +6,9 @@ namespace Microsoft.Testing.Extensions.MSBuild.Serializers; -internal record ModuleInfoRequest(string FrameworkDescription, string ProcessArchitecture, string TestResultFolder) : IRequest; +internal sealed record ModuleInfoRequest(string FrameworkDescription, string ProcessArchitecture, string TestResultFolder) : IRequest; -internal class ModuleInfoRequestSerializer : BaseSerializer, INamedPipeSerializer +internal sealed class ModuleInfoRequestSerializer : BaseSerializer, INamedPipeSerializer { public int Id => 1; diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/RunSummaryRequestSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/RunSummaryRequestSerializer.cs index e28a1c33e7..4a612df01d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/RunSummaryRequestSerializer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/Serializers/RunSummaryRequestSerializer.cs @@ -6,14 +6,14 @@ namespace Microsoft.Testing.Extensions.MSBuild.Serializers; -internal record RunSummaryInfoRequest( +internal sealed record RunSummaryInfoRequest( int Total, int TotalFailed, int TotalPassed, int TotalSkipped, string? Duration) : IRequest; -internal class RunSummaryInfoRequestSerializer : BaseSerializer, INamedPipeSerializer +internal sealed class RunSummaryInfoRequestSerializer : BaseSerializer, INamedPipeSerializer { public int Id => 3; diff --git a/src/Platform/Microsoft.Testing.Extensions.MSBuild/TestingPlatformBuilderHook.cs b/src/Platform/Microsoft.Testing.Extensions.MSBuild/TestingPlatformBuilderHook.cs index 53e32087d3..badb923f0d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.MSBuild/TestingPlatformBuilderHook.cs +++ b/src/Platform/Microsoft.Testing.Extensions.MSBuild/TestingPlatformBuilderHook.cs @@ -8,5 +8,5 @@ namespace Microsoft.Testing.Platform.MSBuild; public static class TestingPlatformBuilderHook { public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] _) - => MSBuildExtensions.AddMSBuild(testApplicationBuilder); + => testApplicationBuilder.AddMSBuild(); } 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..2a054009cb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/Resources/xlf/ExtensionResources.es.xlf @@ -0,0 +1,150 @@ + + + + + + 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 los {0} intentos +===================== + + + + 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 código de salida distinto de 2 (pruebas con errores). Error relacionado con una condición inesperada. '{0}' de código de salida + + + + + \ 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..ebc1cab4de --- /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 correctement terminée 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..6593d426d2 --- /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..9876de376e --- /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 pakiet testów w {0} próbach +===================== + + + + +===================== +Tests suite failed, total failed tests: {0}, exit code: {1}, attempt: {2}/{3} +===================== + + +===================== +Pakiet 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 +===================== + +===================== +Pakiet 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..0f89116a98 --- /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..a9c6d6ce52 --- /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 de 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 de 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..e558008090 --- /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..d40bbad9ef --- /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..954fb4dfe9 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryCommandLineOptionsProvider.cs @@ -0,0 +1,83 @@ +// 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.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..4117a5b75e --- /dev/null +++ b/src/Platform/Microsoft.Testing.Extensions.Retry/RetryOrchestrator.cs @@ -0,0 +1,344 @@ +// 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.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/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs index 9fa6fa9296..fe27fd2eb2 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClient.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Extensions.Telemetry; -internal class AppInsightTelemetryClient : ITelemetryClient +internal sealed class AppInsightTelemetryClient : ITelemetryClient { // Note: The InstrumentationKey should match the one of dotnet cli. private const string InstrumentationKey = "74cc1c9e-3e6e-4d05-b3fc-dde9101d0254"; diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs index 8e3665349c..642da76ed9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightTelemetryClientFactory.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Extensions.Telemetry; -internal class AppInsightTelemetryClientFactory : ITelemetryClientFactory +internal sealed class AppInsightTelemetryClientFactory : ITelemetryClientFactory { public ITelemetryClient Create(string? currentSessionId, string osVersion) => new AppInsightTelemetryClient(currentSessionId, osVersion); diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs index fc69d13e19..adb1e49b76 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsProvider.cs @@ -3,11 +3,7 @@ #if NETCOREAPP using System.Threading.Channels; -#else -using System.Collections.Concurrent; #endif -using System.Globalization; -using System.Text; using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.Configurations; @@ -135,7 +131,7 @@ private async Task IngestLoopAsync() { _client = null; - await _logger.LogErrorAsync($"Failed to initialize telemetry client", e); + await _logger.LogErrorAsync("Failed to initialize telemetry client", e); return; } @@ -199,14 +195,14 @@ private async Task IngestLoopAsync() { StringBuilder builder = new(); builder.AppendLine(CultureInfo.InvariantCulture, $"Send telemetry event: {eventName}"); - foreach (KeyValuePair keyValue in properties) + foreach ((string key, string value) in properties) { - builder.AppendLine(CultureInfo.InvariantCulture, $" {keyValue.Key}: {keyValue.Value}"); + builder.AppendLine(CultureInfo.InvariantCulture, $" {key}: {value}"); } - foreach (KeyValuePair keyValue in metrics) + foreach ((string key, double value) in metrics) { - builder.AppendLine(CultureInfo.InvariantCulture, $" {keyValue.Key}: {keyValue.Value.ToString("f", CultureInfo.InvariantCulture)}"); + builder.AppendLine(CultureInfo.InvariantCulture, $" {key}: {value.ToString("f", CultureInfo.InvariantCulture)}"); } await _logger.LogTraceAsync(builder.ToString()); @@ -223,7 +219,7 @@ private async Task IngestLoopAsync() // We could do better back-pressure. if (_logger.IsEnabled(LogLevel.Error) && (!lastLoggedError.HasValue || (lastLoggedError.Value - _clock.UtcNow).TotalSeconds > 3)) { - await _logger.LogErrorAsync($"Error during telemetry report.", ex); + await _logger.LogErrorAsync("Error during telemetry report.", ex); lastLoggedError = _clock.UtcNow; } } @@ -273,13 +269,17 @@ private static System.Text.RegularExpressions.Regex GetValidHashPattern() #endif #endif - public async Task LogEventAsync(string eventName, IDictionary paramsMap) + public +#if NETCOREAPP + async +#endif + Task LogEventAsync(string eventName, IDictionary paramsMap) { #if NETCOREAPP await _payloads.Writer.WriteAsync((eventName, paramsMap)); #else _payloads.Add((eventName, paramsMap)); - await Task.CompletedTask; + return Task.CompletedTask; #endif } diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs index 29f0e0ea24..4e391cb76f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/AppInsightsTelemetryProviderExtensions.cs @@ -8,10 +8,6 @@ using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.Telemetry; -#if NETCOREAPP -using System.Runtime.CompilerServices; -#endif - namespace Microsoft.Testing.Extensions; public static class AppInsightsTelemetryProviderExtensions diff --git a/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs b/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs index 09e5b7cc4d..b90059537b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs +++ b/src/Platform/Microsoft.Testing.Extensions.Telemetry/CIEnvironmentDetectorForTelemetry.cs @@ -7,7 +7,7 @@ namespace Microsoft.Testing.Extensions.Telemetry; // Detection of CI: https://learn.microsoft.com/dotnet/core/tools/telemetry#continuous-integration-detection // From: https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/Telemetry/CIEnvironmentDetectorForTelemetry.cs -internal class CIEnvironmentDetectorForTelemetry +internal sealed class CIEnvironmentDetectorForTelemetry { // Systems that provide boolean values only, so we can simply parse and check for true private static readonly string[] BooleanVariables = diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs index 91e972bf72..c51ba7e88d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport.Abstractions/GlobalSuppressions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - // We usually do not want to suppress issues through GlobalSuppressions file but in this case we have to do it because // we are not able to suppress the issue differently. #pragma warning disable IDE0076 diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj index 336a3f8003..0cfd477f7f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Microsoft.Testing.Extensions.TrxReport.csproj @@ -50,7 +50,6 @@ This package extends Microsoft Testing Platform to provide TRX test reports.]]> - diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs index a2102ed28d..45832d23db 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/ReportFileNameSerializer.cs @@ -11,7 +11,7 @@ internal sealed class ReportFileNameRequest(string fileName) : IRequest public string FileName { get; } = fileName; } -internal class ReportFileNameRequestSerializer : BaseSerializer, INamedPipeSerializer +internal sealed class ReportFileNameRequestSerializer : BaseSerializer, INamedPipeSerializer { public int Id => 1; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs index 7c3c41e2f0..b40b5f3ae8 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/Serializers/TestAdapterInformationsSerializer.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; -internal class TestAdapterInformationRequest : IRequest +internal sealed class TestAdapterInformationRequest : IRequest { public TestAdapterInformationRequest(string testAdapterId, string testAdapterVersion) { @@ -19,7 +19,7 @@ public TestAdapterInformationRequest(string testAdapterId, string testAdapterVer public string TestAdapterVersion { get; } } -internal class TestAdapterInformationRequestSerializer : BaseSerializer, INamedPipeSerializer +internal sealed class TestAdapterInformationRequestSerializer : BaseSerializer, INamedPipeSerializer { public int Id => 2; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs index 5738762730..05b2a63ed4 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.CommandLine.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.TestReports.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; @@ -12,7 +10,7 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; -internal class TrxCompareToolCommandLine : IToolCommandLineOptionsProvider +internal sealed class TrxCompareToolCommandLine : IToolCommandLineOptionsProvider { public const string BaselineTrxOptionName = "baseline-trx"; public const string TrxToCompareOptionName = "trx-to-compare"; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs index f270034078..3e885d9f74 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxCompareTool.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; -using System.Xml.Linq; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.OutputDevice; @@ -14,7 +10,7 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; -internal class TrxCompareTool : ITool, IOutputDeviceDataProducer +internal sealed class TrxCompareTool : ITool, IOutputDeviceDataProducer { public const string ToolName = "ms-trxcompare"; private readonly ICommandLineOptions _commandLineOptions; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs index 2ec8c47815..b8359cfd8d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxDataConsumer.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.TestReports.Resources; using Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; using Microsoft.Testing.Platform.Capabilities.TestFramework; @@ -230,7 +228,7 @@ public async Task OnTestSessionFinishingAsync(SessionUid sessionUid, Cancellatio { if (!_adapterSupportTrxCapability) { - await _outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateYellowConsoleColorText(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportFrameworkDoesNotSupportTrxReportCapability, _testFramework.DisplayName, _testFramework.Uid))); + await _outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, ExtensionResources.TrxReportFrameworkDoesNotSupportTrxReportCapability, _testFramework.DisplayName, _testFramework.Uid))); } ApplicationStateGuard.Ensure(_testStartTime is not null); diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs index d19aa3f54e..9663d49ce9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxEnvironmentVariableProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.TestReports.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs index 0a5e0991a8..c73914056f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxProcessLifetimeHandler.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.TestReports.Resources; using Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; using Microsoft.Testing.Platform.CommandLine; @@ -240,7 +238,7 @@ public void Dispose() } #endif - private class ExtensionInfo : IExtension + private sealed class ExtensionInfo : IExtension { public ExtensionInfo(string id, string semVer, string displayName, string description) { @@ -261,7 +259,7 @@ public ExtensionInfo(string id, string semVer, string displayName, string descri public Task IsEnabledAsync() => throw new NotImplementedException(); } - private class TestAdapterInfo : ITestFramework + private sealed class TestAdapterInfo : ITestFramework { public TestAdapterInfo(string id, string semVer) { diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs index 10d7a0399a..db577d90bd 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs @@ -4,11 +4,7 @@ #if NETCOREAPP using System.Buffers; #endif -using System.Globalization; using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml.Linq; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; @@ -16,6 +12,7 @@ using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Messages; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; @@ -28,14 +25,6 @@ internal sealed partial class TrxReportEngine private static readonly Regex InvalidXmlCharReplace = BuildInvalidXmlCharReplace(); private static readonly MatchEvaluator InvalidXmlEvaluator = ReplaceInvalidCharacterWithUniCodeEscapeSequence; - private static readonly Type[] FailedStates = - [ - typeof(FailedTestNodeStateProperty), - typeof(CancelledTestNodeStateProperty), - typeof(ErrorTestNodeStateProperty), - typeof(TimeoutTestNodeStateProperty) - ]; - private static readonly HashSet InvalidFileNameChars = [ '\"', @@ -469,7 +458,7 @@ private void AddResults(string testAppModule, XElement testRun, out XElement tes string outcome = "Passed"; TestNodeStateProperty? testState = testNode.Properties.SingleOrDefault(); if (testState is { } state - && FailedStates.Contains(testState.GetType())) + && TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeFailedProperties.Contains(testState.GetType())) { outcome = resultSummaryOutcome = "Failed"; } diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs index 5ce9dc10be..33e0e73cff 100644 --- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs +++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxTestApplicationLifecycleCallbacks.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.TestReports.Resources; using Microsoft.Testing.Extensions.TrxReport.Abstractions.Serializers; using Microsoft.Testing.Platform.CommandLine; @@ -14,7 +12,7 @@ namespace Microsoft.Testing.Extensions.TrxReport.Abstractions; -internal class TrxTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IDisposable +internal sealed class TrxTestApplicationLifecycleCallbacks : ITestApplicationLifecycleCallbacks, IDisposable { private readonly bool _isEnabled; private readonly IEnvironment _environment; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs index dad26b9207..261f1e58d5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.CommandLine; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs index 1646d8df09..174f2022c8 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/TestRunParametersCommandLineOptionsProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs index 934ac816f7..da7ff8d85c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml.Linq; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs index 57724e17e4..7801f753b1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Helpers/DebugUtils.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Extensions.VSTestBridge.Helpers; internal static class DebugUtils diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj index 64a5152e66..a3d08254e9 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Microsoft.Testing.Extensions.VSTestBridge.csproj @@ -5,8 +5,6 @@ - - diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs index 3cdfd97863..90d9101af0 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/Condition.cs @@ -2,10 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // NOTE: This file is copied as-is from VSTest source code. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs index 4d8a44bbee..e79410734a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ContextAdapterBase.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.CommandLine; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs index 6e596db037..456384b72c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FastFilter.cs @@ -3,9 +3,6 @@ // NOTE: This file is copied as-is from VSTest source code. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text.RegularExpressions; using Microsoft.Testing.Platform; @@ -195,14 +192,15 @@ private void AddProperty(string name, string value) { if (!_filterDictionaryBuilder.TryGetValue(name, out ImmutableHashSet.Builder? values)) { - values = ImmutableHashSet.CreateBuilder(StringComparer.OrdinalIgnoreCase); + values = ImmutableHashSet.CreateBuilder(StringComparer.OrdinalIgnoreCase)!; _filterDictionaryBuilder.Add(name, values); } values.Add(value); } - internal FastFilter? ToFastFilter() => ContainsValidFilter + internal FastFilter? ToFastFilter() + => ContainsValidFilter ? new FastFilter( _filterDictionaryBuilder.ToImmutableDictionary(kvp => kvp.Key, kvp => (ISet)_filterDictionaryBuilder[kvp.Key].ToImmutable()), _fastFilterOperation, diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs index 53531b3d83..4f771bfc0f 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpression.cs @@ -2,12 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // NOTE: This file is copied as-is from VSTest source code. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; - using Microsoft.Testing.Platform; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs index 8e2b757025..3b8e083a14 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FilterExpressionWrapper.cs @@ -2,9 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // NOTE: This file is copied as-is from VSTest source code. -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; - using Microsoft.Testing.Platform; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; @@ -12,7 +9,7 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; [ExcludeFromCodeCoverage] // Helper copied from VSTest source code -internal class FilterExpressionWrapper +internal sealed class FilterExpressionWrapper { /// /// FilterExpression corresponding to filter criteria. diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs index 98110bf4bb..68dd0bc29d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/MessageLoggerAdapter.cs @@ -53,11 +53,11 @@ public void SendMessage(TestMessageLevel testMessageLevel, string message) break; case TestMessageLevel.Warning: _logger.LogWarning(message); - _outputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateYellowConsoleColorText(message)).Await(); + _outputDevice.DisplayAsync(this, new WarningMessageOutputDeviceData(message)).Await(); break; case TestMessageLevel.Error: _logger.LogError(message); - _outputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(message)).Await(); + _outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(message)).Await(); break; default: throw new NotSupportedException($"Unsupported logging level '{testMessageLevel}'."); diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs index 81a3151552..60377070c1 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs @@ -3,8 +3,6 @@ #pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Extensions.TrxReport.Abstractions; using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.Extensions.Messages; @@ -162,21 +160,35 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, testNode.Properties.Add(new TimingProperty(new(testResult.StartTime, testResult.EndTime, testResult.Duration), [])); + var standardErrorMessages = new List(); + var standardOutputMessages = new List(); foreach (TestResultMessage testResultMessage in testResult.Messages) { if (testResultMessage.Category == TestResultMessage.StandardErrorCategory) { - testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardError", testResultMessage.Text ?? string.Empty)); - testNode.Properties.Add(new StandardErrorProperty(testResultMessage.Text ?? string.Empty)); + string message = testResultMessage.Text ?? string.Empty; + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardError", message)); + standardErrorMessages.Add(message); } if (testResultMessage.Category == TestResultMessage.StandardOutCategory) { - testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardOutput", testResultMessage.Text ?? string.Empty)); - testNode.Properties.Add(new StandardOutputProperty(testResultMessage.Text ?? string.Empty)); + string message = testResultMessage.Text ?? string.Empty; + testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardOutput", message)); + standardOutputMessages.Add(message); } } + if (standardErrorMessages.Count > 0) + { + testNode.Properties.Add(new StandardErrorProperty(string.Join(Environment.NewLine, standardErrorMessages))); + } + + if (standardOutputMessages.Count > 0) + { + testNode.Properties.Add(new StandardOutputProperty(string.Join(Environment.NewLine, standardOutputMessages))); + } + return testNode; } diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs index 3b321ef613..48ab8bbb84 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunContextAdapter.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Text; -using System.Xml.Linq; - using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.Messages; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs index ac2175740e..9ba6513f15 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsAdapter.cs @@ -3,9 +3,6 @@ #pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -using System.Globalization; -using System.Xml.Linq; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Platform; @@ -24,6 +21,18 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; /// internal sealed class RunSettingsAdapter : IRunSettings { + private static readonly string[] UnsupportedRunConfigurationSettings = [ + "DotnetHostPath", + "MaxCpuCount", + "TargetFrameworkVersion", + "TargetPlatform", + "TestAdaptersPaths", + "TestCaseFilter", + "TestSessionTimeout", + "TreatNoTestsAsError", + "TreatTestAdapterErrorsAsWarnings", + ]; + public RunSettingsAdapter( ICommandLineOptions commandLineOptions, IFileSystem fileSystem, @@ -92,49 +101,12 @@ private static void WarnOnUnsupportedEntries(XDocument document, IMessageLogger return; } - if (runConfigurationElement.Element("MaxCpuCount") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "MaxCpuCount")); - } - - if (runConfigurationElement.Element("TargetFrameworkVersion") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TargetFrameworkVersion")); - } - - if (runConfigurationElement.Element("TargetPlatform") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TargetPlatform")); - } - - if (runConfigurationElement.Element("TreatTestAdapterErrorsAsWarnings") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TreatTestAdapterErrorsAsWarnings")); - } - - if (runConfigurationElement.Element("TestAdaptersPaths") is not null) + foreach (string unsupportedRunConfigurationSetting in UnsupportedRunConfigurationSettings) { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TestAdaptersPaths")); - } - - if (runConfigurationElement.Element("TestCaseFilter") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TestCaseFilter")); - } - - if (runConfigurationElement.Element("TestSessionTimeout") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TestSessionTimeout")); - } - - if (runConfigurationElement.Element("DotnetHostPath") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "DotnetHostPath")); - } - - if (runConfigurationElement.Element("TreatNoTestsAsError") is not null) - { - messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, "TreatNoTestsAsError")); + if (runConfigurationElement.Element(unsupportedRunConfigurationSetting) is not null) + { + messageLogger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.InvariantCulture, ExtensionResources.UnsupportedRunconfigurationSetting, unsupportedRunConfigurationSetting)); + } } } } diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs index 736ba6833e..85cc81e2e2 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/RunSettingsPatcher.cs @@ -3,8 +3,6 @@ #pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -using System.Xml.Linq; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Platform; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt index b9a8f88f8d..16d6e94640 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Shipped.txt @@ -50,6 +50,7 @@ override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSess override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.RunTestsAsync(Microsoft.Testing.Extensions.VSTestBridge.Requests.VSTestRunTestExecutionRequest! request, Microsoft.Testing.Platform.Messages.IMessageBus! messageBus, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Uid.get -> string! override sealed Microsoft.Testing.Extensions.VSTestBridge.SynchronizedSingleSessionVSTestBridgedTestFramework.Version.get -> string! +static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddRunSettingsEnvironmentVariableProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddRunSettingsService(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddTestCaseFilterService(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddTestRunParametersService(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt index c27c99f4e5..7dc5c58110 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ #nullable enable -static Microsoft.Testing.Extensions.VSTestBridge.Helpers.TestApplicationBuilderExtensions.AddRunSettingsEnvironmentVariableProvider(this Microsoft.Testing.Platform.Builder.ITestApplicationBuilder! builder, Microsoft.Testing.Platform.Extensions.IExtension! extension) -> void diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs index 7cfbe47606..17ddf43eff 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/SynchronizedSingleSessionVSTestAndTestAnywhereAdapter.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.Testing.Extensions.VSTestBridge.Requests; using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Platform.Capabilities.TestFramework; diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs index 23a6896af9..b1e9e21a15 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider .cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml.Linq; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx index 3d687d988e..16ec9b3e2a 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/MSBuildResources.resx @@ -156,4 +156,12 @@ Tests succeeded: '{0}' [{1}|{2}] + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf index 8d3dc35a31..4d5d498aab 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.cs.xlf @@ -27,6 +27,21 @@ VýpoÄet úplné cesty nástroje se nezdaÅ™il. SpouÅ¡tÄ›Ä {0} + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Nepovedlo se najít hostitele {0} pro architekturu{1}. + +Tento problém můžete vyÅ™eÅ¡it instalací rozhraní .NET {1}. + +Zadanou architekturu najdete na adrese: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} Neplatná hodnota TargetPath, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf index b429cb8fd5..9df371921e 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.de.xlf @@ -27,6 +27,21 @@ Fehler bei der Berechnung des vollständigen Pfadtools Runner „{0}“ + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Der Host „{0}“ für die Architektur „{1}“ wurde nicht gefunden. + +Sie können das Problem beheben, indem Sie das .NET „{1}“ installieren. + +Das angegebene Framework finden Sie unter: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} Ungültiger TargetPath, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf index 433fecfb13..b441e35b36 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.es.xlf @@ -27,6 +27,21 @@ El cálculo de la herramienta de ruta de acceso completa es incorrecto. Runner '{0}' + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + No se encontró el host "{0}" para la arquitectura "{1}". + +Para resolver el problema, instale "{1}" .NET. + +El marco de trabajo especificado se puede encontrar en: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} TargetPath no válido, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf index a9db79e77f..4b952b1e7e 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.fr.xlf @@ -27,6 +27,21 @@ Désolé... Nous n’avons pas pu effectuer le calcul d’outil de chemin complet. Exécuteur « {0} » + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Nous n'avons pas trouvé l’hôte '{0}' de l’architecture '{1}'. + +Vous pouvez résoudre le problème en installant '{1}' .NET. + +L’infrastructure spécifiée se trouve à l’adresse suivante : + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} TargetPath non valide, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf index 654e88ead5..2844eabf0c 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.it.xlf @@ -27,6 +27,21 @@ Calcolo del percorso completo dello strumento non riuscito. Strumento di esecuzione '{0}' + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Non è stato possibile trovare l'host '{0}' per l'architettura '{1}'. + +È possibile risolvere il problema installando '{1}' .NET. + +Il framework specificato è disponibile all'indirizzo: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} TargetPath non valido, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf index 4e6f7689ef..ebbd3e6934 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ja.xlf @@ -27,6 +27,21 @@ 完全ãªãƒ‘ス ツールã®è¨ˆç®—ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ランナー '{0}' + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + '{1}' アーキテクãƒãƒ£ã® '{0}' ホストãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ + +'{1}' .NET をインストールã—ã¦ã“ã®å•題を解決ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚ + +指定ã•れãŸãƒ•ãƒ¬ãƒ¼ãƒ ãƒ¯ãƒ¼ã‚¯ã¯æ¬¡ã®å ´æ‰€ã«ã‚りã¾ã™: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} 無効㪠TargetPathã€{0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf index 133a392a42..bcf296a8bf 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ko.xlf @@ -27,6 +27,21 @@ ì „ì²´ 경로 ë„구를 계산하지 못했습니다. 러너 '{0}' + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + '{1}' 아키í…ì²˜ì— ëŒ€í•œ '{0}' 호스트를 ì°¾ì„ ìˆ˜ 없습니다. + +'{1}' .NETì„ ì„¤ì¹˜í•˜ì—¬ 문제를 í•´ê²°í•  수 있습니다. + +ë‹¤ìŒ ìœ„ì¹˜ì—서 ì§€ì •ëœ í”„ë ˆìž„ì›Œí¬ë¥¼ ì°¾ì„ ìˆ˜ 있습니다. + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} ìž˜ëª»ëœ TargetPath, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf index 47d2e8869a..379ac3ff97 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pl.xlf @@ -27,6 +27,21 @@ Obliczanie narzÄ™dzia peÅ‚nej Å›cieżki nie powiodÅ‚o siÄ™. ModuÅ‚ uruchamiajÄ…cy „{0}†+ + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Nie można odnaleźć hosta „{0}†dla architektury „{1}â€. + +Problem można rozwiÄ…zać, instalujÄ…c program .NET „{1}â€. + +OkreÅ›lonÄ… strukturÄ™ można znaleźć pod adresem: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} NieprawidÅ‚owa Å›cieżka docelowa, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf index e07d9e4ee3..7d18cd1dd3 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.pt-BR.xlf @@ -27,6 +27,21 @@ Falha no cálculo da ferramenta de caminho completo. Executor “{0}†+ + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Não foi possível localizar o host '{0}' para a arquitetura '{1}'. + +Você pode resolver o problema instalando o .NET '{1}'. + +A estrutura especificada pode ser encontrada em: + – https://aka.ms/dotnet-download + + Invalid TargetPath, {0} TargetPath inválido, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf index 10ae284b08..8297566eff 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.ru.xlf @@ -27,6 +27,21 @@ Сбой вычиÑÐ»ÐµÐ½Ð¸Ñ ÑредÑтва полного пути. СредÑтво Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚ÐµÑтов "{0}" + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + Ðе удалоÑÑŒ найти хоÑÑ‚ "{0}" Ð´Ð»Ñ Ð°Ñ€Ñ…Ð¸Ñ‚ÐµÐºÑ‚ÑƒÑ€Ñ‹ "{1}". + +Чтобы уÑтранить проблему, уÑтановите ''{1}'' .NET. + +Указанную платформу можно найти по адреÑу: + — https://aka.ms/dotnet-download + + Invalid TargetPath, {0} ÐедопуÑтимый TargetPath, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf index 21b5c1c465..b4934b11c0 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.tr.xlf @@ -27,6 +27,21 @@ Tam yol aracı hesaplaması baÅŸarısız. Çalıştırıcı '{0}' + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + '{1}' mimarisi için '{0}' ana bilgisayarı bulunamadı. + +'{1}' .NET’i yükleyerek sorunu çözebilirsiniz. + +Belirtilen çerçeve ÅŸu konumda bulunabilir: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} Geçersiz TargetPath, {0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf index e53d10fbaf..86df598ffe 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hans.xlf @@ -27,6 +27,21 @@ 完整路径工具计算失败。è¿è¡Œå™¨â€œ{0}†+ + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + 找ä¸åˆ°â€œ{1}â€ä½“系结构的“{0}â€ä¸»æœºã€‚ + +å¯ä»¥é€šè¿‡å®‰è£… “{1}†.NET æ¥è§£å†³æ­¤é—®é¢˜ã€‚ + +å¯åœ¨ä»¥ä¸‹ä½ç½®æ‰¾åˆ°æŒ‡å®šçš„æ¡†æž¶ï¼š + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} TargetPath {0} 无效 diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf index 1e6ce78dc8..392c6a45ae 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Resources/xlf/MSBuildResources.zh-Hant.xlf @@ -27,6 +27,21 @@ 完整路徑工具計算失敗。執行器 '{0}' + + Could not find '{0}' host for the '{1}' architecture. + +You can resolve the problem by installing the '{1}' .NET. + +The specified framework can be found at: + - https://aka.ms/dotnet-download + 找ä¸åˆ° '{1}' çµæ§‹çš„ '{0}' 主機。 + +您å¯ä»¥å®‰è£ '{1}' .NET 來解決å•題。 + +您å¯ä»¥åœ¨ä»¥ä¸‹ä½ç½®æ‰¾åˆ°æŒ‡å®šçš„æž¶æ§‹: + - https://aka.ms/dotnet-download + + Invalid TargetPath, {0} TargetPath 無效,{0} diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs index 475d6fa3a3..d3761b1970 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/ConfigurationFileTask.cs @@ -45,7 +45,7 @@ public override bool Execute() Log.LogMessage(MessageImportance.Normal, $"Microsoft Testing Platform configuration file: '{TestingPlatformConfigurationFileSource.ItemSpec}'"); if (!_fileSystem.Exist(TestingPlatformConfigurationFileSource.ItemSpec)) { - Log.LogMessage(MessageImportance.Normal, $"Microsoft Testing Platform configuration file not found"); + Log.LogMessage(MessageImportance.Normal, "Microsoft Testing Platform configuration file not found"); return true; } @@ -62,7 +62,7 @@ public override bool Execute() Log.LogMessage(MessageImportance.Normal, $"Configuration file found: '{TestingPlatformConfigurationFileSource.ItemSpec}'"); _fileSystem.CopyFile(TestingPlatformConfigurationFileSource.ItemSpec, finalFileName); FinalTestingPlatformConfigurationFile = new TaskItem(finalFileName); - Log.LogMessage(MessageImportance.Normal, $"Microsoft Testing Platform configuration file written"); + Log.LogMessage(MessageImportance.Normal, "Microsoft Testing Platform configuration file written"); return true; } diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs index c482f52b26..b54ea3236e 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/DotnetMuxerLocator.cs @@ -2,15 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // Adapted from https://github.com/microsoft/vstest/blob/main/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetHostHelper.cs -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - using Microsoft.Win32; namespace Microsoft.Testing.Platform.MSBuild.Tasks; -internal class DotnetMuxerLocator +internal sealed class DotnetMuxerLocator { private readonly string _muxerName; private readonly Process _currentProcess; @@ -86,7 +82,7 @@ public bool TryGetDotnetPathByArchitecture( if ((envVar == null || !Directory.Exists(envVar)) && targetArchitecture == PlatformArchitecture.X86 && isWinOs) { - envKey = $"DOTNET_ROOT(x86)"; + envKey = "DOTNET_ROOT(x86)"; envVar = Environment.GetEnvironmentVariable(envKey); } @@ -128,7 +124,7 @@ public bool TryGetDotnetPathByArchitecture( } } - _resolutionLog($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer was not found using DOTNET_ROOT* env variables."); + _resolutionLog("DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer was not found using DOTNET_ROOT* env variables."); // Try to search for global registration muxerPath = isWinOs ? GetMuxerFromGlobalRegistrationWin(targetArchitecture) : GetMuxerFromGlobalRegistrationOnUnix(targetArchitecture); @@ -155,7 +151,7 @@ public bool TryGetDotnetPathByArchitecture( return true; } - _resolutionLog($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer not found using global registrations"); + _resolutionLog("DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer not found using global registrations"); // Try searching in default installation location if it exists if (isWinOs) @@ -223,14 +219,14 @@ public bool TryGetDotnetPathByArchitecture( using var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); if (hklm == null) { - _resolutionLog($@"DotnetHostHelper.GetMuxerFromGlobalRegistrationWin: Missing SOFTWARE\dotnet\Setup\InstalledVersions subkey"); + _resolutionLog(@"DotnetHostHelper.GetMuxerFromGlobalRegistrationWin: Missing SOFTWARE\dotnet\Setup\InstalledVersions subkey"); return null; } using RegistryKey? dotnetInstalledVersion = hklm.OpenSubKey(@"SOFTWARE\dotnet\Setup\InstalledVersions"); if (dotnetInstalledVersion == null) { - _resolutionLog($@"DotnetHostHelper.GetMuxerFromGlobalRegistrationWin: Missing RegistryHive.LocalMachine for RegistryView.Registry32"); + _resolutionLog(@"DotnetHostHelper.GetMuxerFromGlobalRegistrationWin: Missing RegistryHive.LocalMachine for RegistryView.Registry32"); return null; } @@ -238,7 +234,7 @@ public bool TryGetDotnetPathByArchitecture( string? installLocation = nativeArch?.GetValue("InstallLocation")?.ToString(); if (installLocation == null) { - _resolutionLog($@"DotnetHostHelper.GetMuxerFromGlobalRegistrationWin: Missing registry InstallLocation"); + _resolutionLog(@"DotnetHostHelper.GetMuxerFromGlobalRegistrationWin: Missing registry InstallLocation"); return null; } @@ -311,7 +307,7 @@ public bool TryGetDotnetPathByArchitecture( // Check if the offset is invalid if (peHeader > fs.Length - 5) { - resolutionLog($"[GetMuxerArchitectureByPEHeaderOnWin]Invalid offset"); + resolutionLog("[GetMuxerArchitectureByPEHeaderOnWin]Invalid offset"); validImage = false; } @@ -325,7 +321,7 @@ public bool TryGetDotnetPathByArchitecture( if (reader.ReadUInt32() != 0x00004550) { validImage = false; - resolutionLog($"[GetMuxerArchitectureByPEHeaderOnWin]Missing PE signature"); + resolutionLog("[GetMuxerArchitectureByPEHeaderOnWin]Missing PE signature"); } if (validImage) @@ -373,7 +369,7 @@ public bool TryGetDotnetPathByArchitecture( } } - return archType is null ? throw new InvalidOperationException("Invalid image") : archType; + return archType ?? throw new InvalidOperationException("Invalid image"); } // See https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs index 14a8e0fad6..a344ed461d 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/FailedTestHelper.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; - using Microsoft.Testing.Extensions.MSBuild.Serializers; namespace Microsoft.Testing.Platform.MSBuild; diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs index 588f94cac9..f62a9d809e 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.cs @@ -3,11 +3,6 @@ #pragma warning disable CS8618 // Properties below are set by MSBuild. -using System.Diagnostics; -using System.Globalization; -using System.Runtime.InteropServices; -using System.Text; - using Microsoft.Build.Framework; using Microsoft.Testing.Extensions.MSBuild; using Microsoft.Testing.Extensions.MSBuild.Serializers; @@ -35,7 +30,7 @@ public class InvokeTestingPlatformTask : Build.Utilities.ToolTask, IDisposable private readonly CancellationTokenSource _waitForConnections = new(); private readonly List _connections = new(); private readonly StringBuilder _output = new(); - private readonly object _initLock = new(); + private readonly Lock _initLock = new(); private readonly Process _currentProcess = Process.GetCurrentProcess(); private readonly Architecture _currentProcessArchitecture = RuntimeInformation.ProcessArchitecture; @@ -141,7 +136,7 @@ protected override string ToolName else { Log.LogMessage(MessageImportance.Low, resolutionLog.ToString()); - Log.LogError(Resources.MSBuildResources.FullPathToolCalculationFailed, dotnetRunnerName); + Log.LogError(string.Format(CultureInfo.InvariantCulture, Resources.MSBuildResources.IncompatibleArchitecture, dotnetRunnerName, TestArchitecture.ItemSpec)); return null; } } @@ -420,7 +415,7 @@ private Task HandleRequestAsync(IRequest request) throw new NotImplementedException($"Request '{request.GetType()}' not supported."); } - private class MSBuildLogger : Logging.ILogger + private sealed class MSBuildLogger : Logging.ILogger { public bool IsEnabled(LogLevel logLevel) => false; diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs index 5d96eb1bf9..8edc1432e8 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/MSBuildCompatibilityHelper.cs @@ -3,8 +3,6 @@ #pragma warning disable CS8618 // Properties below are set by MSBuild. -using System.Reflection; - using Microsoft.Build.Framework; namespace Microsoft.Testing.Platform.MSBuild; diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs index 04f6ac93b2..bb2c276601 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/StackTraceHelper.cs @@ -1,14 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.MSBuild; -internal class StackTraceHelper +internal static class StackTraceHelper { private static Regex? s_regex; diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs index a1ce5ff9df..9b0bf36e79 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformAutoRegisteredExtensions.cs @@ -3,10 +3,6 @@ #pragma warning disable CS8618 // Properties below are set by MSBuild. -using System.Diagnostics; -using System.Globalization; -using System.Text; - using Microsoft.Build.Framework; using Microsoft.Build.Utilities; diff --git a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs index d8ddd2b51a..7620da93eb 100644 --- a/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs +++ b/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/TestingPlatformEntryPointTask.cs @@ -3,8 +3,6 @@ #pragma warning disable CS8618 // Properties below are set by MSBuild. -using System.Diagnostics; - using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -74,7 +72,7 @@ private static string GetEntryPointSourceCode(string language) { if (language == CSharpLanguageSymbol) { - return $$""" + return """ //------------------------------------------------------------------------------ // // This code was generated by Microsoft.Testing.Platform.MSBuild @@ -98,7 +96,7 @@ internal sealed class TestingPlatformEntryPoint } else if (language == VBLanguageSymbol) { - return $$""" + return """ '------------------------------------------------------------------------------ ' ' This code was generated by Microsoft.Testing.Platform.MSBuild @@ -125,7 +123,7 @@ End Module } else if (language == FSharpLanguageSymbol) { - return $$""" + return """ //------------------------------------------------------------------------------ // // This code was generated by Microsoft.Testing.Platform.MSBuild diff --git a/src/Platform/Microsoft.Testing.Platform/.editorconfig b/src/Platform/Microsoft.Testing.Platform/.editorconfig index 5c8e5dad5d..a9e2e322b6 100644 --- a/src/Platform/Microsoft.Testing.Platform/.editorconfig +++ b/src/Platform/Microsoft.Testing.Platform/.editorconfig @@ -1,3 +1,5 @@ +root = false + [*.{cs,vb}] # TPEXP: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. dotnet_diagnostic.TPEXP.severity = none diff --git a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs index 7b618018a9..75ae412209 100644 --- a/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs +++ b/src/Platform/Microsoft.Testing.Platform/Builder/TestApplication.cs @@ -1,15 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; -using System.Reflection; -#if NETCOREAPP -using System.Runtime.CompilerServices; -#endif -using System.Runtime.InteropServices; -using System.Text; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Helpers; @@ -136,7 +127,7 @@ private static async Task LogInformationAsync( } else { - await logger.LogInformationAsync($"Version attribute not found"); + await logger.LogInformationAsync("Version attribute not found"); } await logger.LogInformationAsync("Logging mode: " + (syncWrite ? "synchronous" : "asynchronous")); diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs index 13f0fb4571..97f4c19d24 100644 --- a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IBannerMessageOwnerCapability.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Capabilities.TestFramework; /// diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IGracefulStopTestExecutionCapability.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IGracefulStopTestExecutionCapability.cs new file mode 100644 index 0000000000..d152211c28 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/IGracefulStopTestExecutionCapability.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.Capabilities.TestFramework; + +/// +/// A capability to support stopping test execution gracefully, without cancelling/aborting everything. +/// This is used to support '--maximum-failed-tests'. +/// +/// +/// Test frameworks can choose to run any needed cleanup when cancellation is requested. +/// +[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] +public interface IGracefulStopTestExecutionCapability : ITestFrameworkCapability +{ + Task StopTestExecutionAsync(CancellationToken cancellationToken); +} diff --git a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/TestFrameworkCapabilitiesExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/TestFrameworkCapabilitiesExtensions.cs index 4c934c097c..69d3d4c90f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/TestFrameworkCapabilitiesExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Capabilities/TestFramework/TestFrameworkCapabilitiesExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Capabilities.TestFramework; [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs index 8fb0dba1ba..761c267be0 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Helpers; @@ -51,13 +46,13 @@ public CommandLineHandler(CommandLineParseResult parseResult, IReadOnlyCollectio internal CommandLineParseResult ParseResult { get; } - public async Task PrintInfoAsync(IPlatformOutputDevice platformOutputDevice, IReadOnlyList? availableTools = null) + public async Task PrintInfoAsync(IOutputDevice outputDevice, IReadOnlyList? availableTools = null) { // /!\ Info should not be localized as it serves debugging purposes. await DisplayPlatformInfoAsync(); - await platformOutputDevice.DisplayAsync(this, EmptyText); - await DisplayBuiltInExtensionsInfoAsync(platformOutputDevice); - await platformOutputDevice.DisplayAsync(this, EmptyText); + await outputDevice.DisplayAsync(this, EmptyText); + await DisplayBuiltInExtensionsInfoAsync(outputDevice); + await outputDevice.DisplayAsync(this, EmptyText); List toolExtensions = []; List nonToolExtensions = []; @@ -73,24 +68,24 @@ public async Task PrintInfoAsync(IPlatformOutputDevice platformOutputDevice, IRe } } - await DisplayRegisteredExtensionsInfoAsync(platformOutputDevice, nonToolExtensions); - await platformOutputDevice.DisplayAsync(this, EmptyText); - await DisplayRegisteredToolsInfoAsync(platformOutputDevice, availableTools, toolExtensions); - await platformOutputDevice.DisplayAsync(this, EmptyText); + await DisplayRegisteredExtensionsInfoAsync(outputDevice, nonToolExtensions); + await outputDevice.DisplayAsync(this, EmptyText); + await DisplayRegisteredToolsInfoAsync(outputDevice, availableTools, toolExtensions); + await outputDevice.DisplayAsync(this, EmptyText); return; async Task DisplayPlatformInfoAsync() { // Product title, do not translate. - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData("Microsoft Testing Platform:")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData("Microsoft Testing Platform:")); // TODO: Replace Assembly with IAssembly AssemblyInformationalVersionAttribute? version = Assembly.GetExecutingAssembly().GetCustomAttribute(); string versionInfo = version?.InformationalVersion ?? "Not Available"; - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Version: {versionInfo}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Version: {versionInfo}")); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Dynamic Code Supported: {_runtimeFeature.IsDynamicCodeSupported}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Dynamic Code Supported: {_runtimeFeature.IsDynamicCodeSupported}")); // TODO: Replace RuntimeInformation with IRuntimeInformation #if NETCOREAPP @@ -98,42 +93,42 @@ async Task DisplayPlatformInfoAsync() #else string runtimeInformation = $"{RuntimeInformation.FrameworkDescription}"; #endif - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Runtime information: {runtimeInformation}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Runtime information: {runtimeInformation}")); #if !NETCOREAPP #pragma warning disable IL3000 // Avoid accessing Assembly file path when publishing as a single file, this branch run only in .NET Framework string runtimeLocation = typeof(object).Assembly?.Location ?? "Not Found"; #pragma warning restore IL3000 // Avoid accessing Assembly file path when publishing as a single file - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Runtime location: {runtimeLocation}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Runtime location: {runtimeLocation}")); #endif string moduleName = _testApplicationModuleInfo.GetCurrentTestApplicationFullPath(); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Test module: {moduleName}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Test module: {moduleName}")); } - async Task DisplayOptionsAsync(IPlatformOutputDevice platformOutputDevice, IEnumerable options, int indentLevel) + async Task DisplayOptionsAsync(IOutputDevice outputDevice, IEnumerable options, int indentLevel) { string optionNameIndent = new(' ', indentLevel * 2); string optionInfoIndent = new(' ', (indentLevel + 1) * 2); foreach (CommandLineOption option in options.OrderBy(x => x.Name)) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionNameIndent}--{option.Name}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionNameIndent}--{option.Name}")); if (option.Arity.Min == option.Arity.Max) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Arity: {option.Arity.Min}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Arity: {option.Arity.Min}")); } else { string maxArityValue = option.Arity.Max == int.MaxValue ? "N" : $"{option.Arity.Max}"; - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Arity: {option.Arity.Min}..{maxArityValue}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Arity: {option.Arity.Min}..{maxArityValue}")); } - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Hidden: {option.IsHidden}")); - await platformOutputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Description: {option.Description}") { Padding = optionInfoIndent.Length }); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{optionInfoIndent}Hidden: {option.IsHidden}")); + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Description: {option.Description}") { Padding = optionInfoIndent.Length }); } } - async Task DisplayProvidersAsync(IPlatformOutputDevice platformOutputDevice, IEnumerable optionsProviders, int indentLevel) + async Task DisplayProvidersAsync(IOutputDevice outputDevice, IEnumerable optionsProviders, int indentLevel) { string providerIdIndent = new(' ', indentLevel * 2); string providerInfoIndent = new(' ', (indentLevel + 1) * 2); @@ -145,69 +140,69 @@ async Task DisplayProvidersAsync(IPlatformOutputDevice platformOutputDevice, IEn if (isFirst) { isFirst = false; - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerIdIndent}{provider.Uid}")); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerInfoIndent}Name: {provider.DisplayName}")); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerInfoIndent}Version: {provider.Version}")); - await platformOutputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Description: {provider.Description}") { Padding = providerInfoIndent.Length }); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerInfoIndent}Options:")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerIdIndent}{provider.Uid}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerInfoIndent}Name: {provider.DisplayName}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerInfoIndent}Version: {provider.Version}")); + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Description: {provider.Description}") { Padding = providerInfoIndent.Length }); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($"{providerInfoIndent}Options:")); } - await DisplayOptionsAsync(platformOutputDevice, provider.GetCommandLineOptions(), indentLevel + 2); + await DisplayOptionsAsync(outputDevice, provider.GetCommandLineOptions(), indentLevel + 2); } } } - async Task DisplayBuiltInExtensionsInfoAsync(IPlatformOutputDevice platformOutputDevice) + async Task DisplayBuiltInExtensionsInfoAsync(IOutputDevice outputDevice) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData("Built-in command line providers:")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData("Built-in command line providers:")); if (SystemCommandLineOptionsProviders.Count == 0) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no built-in command line providers.")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no built-in command line providers.")); } else { - await DisplayProvidersAsync(platformOutputDevice, SystemCommandLineOptionsProviders, 1); + await DisplayProvidersAsync(outputDevice, SystemCommandLineOptionsProviders, 1); } } - async Task DisplayRegisteredExtensionsInfoAsync(IPlatformOutputDevice platformOutputDevice, List nonToolExtensions) + async Task DisplayRegisteredExtensionsInfoAsync(IOutputDevice outputDevice, List nonToolExtensions) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData("Registered command line providers:")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData("Registered command line providers:")); if (nonToolExtensions.Count == 0) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered command line providers.")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered command line providers.")); } else { - await DisplayProvidersAsync(platformOutputDevice, nonToolExtensions, 1); + await DisplayProvidersAsync(outputDevice, nonToolExtensions, 1); } } - async Task DisplayRegisteredToolsInfoAsync(IPlatformOutputDevice platformOutputDevice, IReadOnlyList? availableTools, List toolExtensions) + async Task DisplayRegisteredToolsInfoAsync(IOutputDevice outputDevice, IReadOnlyList? availableTools, List toolExtensions) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData("Registered tools:")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData("Registered tools:")); if (availableTools is null || availableTools.Count == 0) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered tools.")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered tools.")); } else { var groupedToolExtensions = toolExtensions.GroupBy(x => x.ToolName).ToDictionary(x => x.Key, x => x.ToList()); foreach (ITool tool in availableTools.OrderBy(x => x.Uid)) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" {tool.Uid}")); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Command: {tool.Name}")); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Name: {tool.DisplayName}")); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData($" Version: {tool.Version}")); - await platformOutputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Description: {tool.Description}") { Padding = 4 }); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" Tool command line providers:")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" {tool.Uid}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Command: {tool.Name}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Name: {tool.DisplayName}")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData($" Version: {tool.Version}")); + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"Description: {tool.Description}") { Padding = 4 }); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(" Tool command line providers:")); if (groupedToolExtensions.TryGetValue(tool.Name, out List? providers)) { - await DisplayProvidersAsync(platformOutputDevice, providers, 3); + await DisplayProvidersAsync(outputDevice, providers, 3); } else { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered command line providers.")); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(" There are no registered command line providers.")); } } } @@ -232,7 +227,7 @@ public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out public bool IsDotNetTestPipeInvoked() => IsOptionSet(PlatformCommandLineProvider.DotNetTestPipeOptionKey); #pragma warning disable IDE0060 // Remove unused parameter, temporary we don't use it. - public async Task PrintHelpAsync(IPlatformOutputDevice platformOutputDevice, IReadOnlyList? availableTools = null) + public async Task PrintHelpAsync(IOutputDevice outputDevice, IReadOnlyList? availableTools = null) #pragma warning restore IDE0060 // Remove unused parameter { string applicationName = GetApplicationName(_testApplicationModuleInfo); @@ -240,7 +235,7 @@ public async Task PrintHelpAsync(IPlatformOutputDevice platformOutputDevice, IRe // Temporary disabled, we don't remove the code because could be useful in future. // PrintApplicationToolUsage(availableTools, applicationName); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Empty)); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Empty)); // Local functions static string GetApplicationName(ITestApplicationModuleInfo testApplicationModuleInfo) @@ -250,8 +245,7 @@ static string GetApplicationName(ITestApplicationModuleInfo testApplicationModul ? $"dotnet exec {Path.GetFileName(testApplicationModuleInfo.GetCurrentTestApplicationFullPath())}" : PlatformResources.HelpTestApplicationRunner; - async Task PrintOptionsAsync(IEnumerable optionProviders, int leftPaddingDepth, - bool builtInOnly = false) + async Task PrintOptionsAsync(IEnumerable optionProviders, bool builtInOnly = false) { CommandLineOption[] options = optionProviders @@ -267,9 +261,9 @@ async Task PrintOptionsAsync(IEnumerable opti foreach (CommandLineOption? option in options) { - await platformOutputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"--{option.Name}") { Padding = 4 }); - await platformOutputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData(option.Description) { Padding = 8 }); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Empty)); + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData($"--{option.Name}") { Padding = 4 }); + await outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData(option.Description) { Padding = 8 }); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Empty)); } return options.Length != 0; @@ -277,31 +271,31 @@ async Task PrintOptionsAsync(IEnumerable opti async Task PrintApplicationUsageAsync(string applicationName) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.HelpApplicationUsage, applicationName))); - await platformOutputDevice.DisplayAsync(this, EmptyText); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExecuteTestApplication)); - await platformOutputDevice.DisplayAsync(this, EmptyText); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.HelpApplicationUsage, applicationName))); + await outputDevice.DisplayAsync(this, EmptyText); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExecuteTestApplication)); + await outputDevice.DisplayAsync(this, EmptyText); RoslynDebug.Assert( !SystemCommandLineOptionsProviders.OfType().Any(), "System command line options should not have any tool option registered."); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpOptions)); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpOptions)); ICommandLineOptionsProvider[] nonToolsExtensionProviders = ExtensionsCommandLineOptionsProviders .Where(provider => provider is not IToolCommandLineOptionsProvider) .ToArray(); // By default, only system options are built-in but some extensions (e.g. retry) are considered as built-in too, // so we need to union the 2 collections before printing the options. - await PrintOptionsAsync(SystemCommandLineOptionsProviders.Union(nonToolsExtensionProviders), 1, builtInOnly: true); - await platformOutputDevice.DisplayAsync(this, EmptyText); + await PrintOptionsAsync(SystemCommandLineOptionsProviders.Union(nonToolsExtensionProviders), builtInOnly: true); + await outputDevice.DisplayAsync(this, EmptyText); - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExtensionOptions)); - if (!await PrintOptionsAsync(nonToolsExtensionProviders, 1)) + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpExtensionOptions)); + if (!await PrintOptionsAsync(nonToolsExtensionProviders)) { - await platformOutputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpNoExtensionRegistered)); + await outputDevice.DisplayAsync(this, new TextOutputDeviceData(PlatformResources.HelpNoExtensionRegistered)); } - await platformOutputDevice.DisplayAsync(this, EmptyText); + await outputDevice.DisplayAsync(this, EmptyText); } // Temporary disabled, we don't remove the code because could be useful in future. diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs index 8729bdba2c..796a345413 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs @@ -11,22 +11,28 @@ namespace Microsoft.Testing.Platform.CommandLine; internal sealed class CommandLineManager(IRuntimeFeature runtimeFeature, ITestApplicationModuleInfo testApplicationModuleInfo) : ICommandLineManager { - private readonly List> _commandLineProviderFactory = []; + private readonly List> _commandLineProviderFactory = []; private readonly IRuntimeFeature _runtimeFeature = runtimeFeature; private readonly ITestApplicationModuleInfo _testApplicationModuleInfo = testApplicationModuleInfo; public void AddProvider(Func commandLineProviderFactory) + { + Guard.NotNull(commandLineProviderFactory); + _commandLineProviderFactory.Add(_ => commandLineProviderFactory()); + } + + public void AddProvider(Func commandLineProviderFactory) { Guard.NotNull(commandLineProviderFactory); _commandLineProviderFactory.Add(commandLineProviderFactory); } - internal async Task BuildAsync(CommandLineParseResult parseResult) + internal async Task BuildAsync(CommandLineParseResult parseResult, IServiceProvider serviceProvider) { List commandLineOptionsProviders = []; - foreach (Func commandLineProviderFactory in _commandLineProviderFactory) + foreach (Func commandLineProviderFactory in _commandLineProviderFactory) { - ICommandLineOptionsProvider commandLineOptionsProvider = commandLineProviderFactory(); + ICommandLineOptionsProvider commandLineOptionsProvider = commandLineProviderFactory(serviceProvider); if (!await commandLineOptionsProvider.IsEnabledAsync()) { continue; diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOption.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOption.cs index 398da42025..2db11713d0 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOption.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOption.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; @@ -29,9 +26,10 @@ internal CommandLineOption(string name, string description, ArgumentArity arity, Guard.NotNullOrWhiteSpace(description); ArgumentGuard.Ensure(arity.Max >= arity.Min, nameof(arity), PlatformResources.CommandLineInvalidArityErrorMessage); + string errorMessage = string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineInvalidOptionName, name); for (int i = 0; i < name.Length; i++) { - ArgumentGuard.Ensure(char.IsLetterOrDigit(name[i]) || name[i] == '-' || name[i] == '?', nameof(name), string.Format(CultureInfo.InvariantCulture, PlatformResources.CommandLineInvalidOptionName, name)); + ArgumentGuard.Ensure(char.IsLetterOrDigit(name[i]) || name[i] == '-' || name[i] == '?', nameof(name), errorMessage); } Name = name; diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProxy.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProxy.cs index 6ec2f8f7d7..fd5d11df3d 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProxy.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsProxy.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.CommandLine; internal sealed class CommandLineOptionsProxy : ICommandLineOptions @@ -10,9 +8,8 @@ internal sealed class CommandLineOptionsProxy : ICommandLineOptions private ICommandLineOptions? _commandLineOptions; public bool IsOptionSet(string optionName) - => _commandLineOptions is null - ? throw new InvalidOperationException(Resources.PlatformResources.CommandLineOptionsNotReady) - : _commandLineOptions.IsOptionSet(optionName); + => _commandLineOptions?.IsOptionSet(optionName) ?? + throw new InvalidOperationException(Resources.PlatformResources.CommandLineOptionsNotReady); public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out string[]? arguments) => _commandLineOptions is null diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs index 56fa01c467..aa67608816 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Helpers; @@ -225,12 +222,24 @@ private static async Task ValidateOptionsArgumentsAsync( } private static async Task ValidateConfigurationAsync( - IEnumerable extensionsProviders, - IEnumerable systemProviders, + Dictionary>.KeyCollection extensionsProviders, + Dictionary>.KeyCollection systemProviders, ICommandLineOptions commandLineOptions) { - StringBuilder? stringBuilder = null; - foreach (ICommandLineOptionsProvider commandLineOptionsProvider in systemProviders.Union(extensionsProviders)) + StringBuilder? stringBuilder = await ValidateConfigurationAsync(systemProviders, commandLineOptions, null); + stringBuilder = await ValidateConfigurationAsync(extensionsProviders, commandLineOptions, stringBuilder); + + return stringBuilder?.Length > 0 + ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) + : ValidationResult.Valid(); + } + + private static async Task ValidateConfigurationAsync( + Dictionary>.KeyCollection providers, + ICommandLineOptions commandLineOptions, + StringBuilder? stringBuilder) + { + foreach (ICommandLineOptionsProvider commandLineOptionsProvider in providers) { ValidationResult result = await commandLineOptionsProvider.ValidateCommandLineOptionsAsync(commandLineOptions); if (!result.IsValid) @@ -241,9 +250,7 @@ private static async Task ValidateConfigurationAsync( } } - return stringBuilder?.Length > 0 - ? ValidationResult.Invalid(stringBuilder.ToTrimmedString()) - : ValidationResult.Valid(); + return stringBuilder; } private static string ToTrimmedString(this StringBuilder stringBuilder) diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs index 97cb900e77..eb91a4513d 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineHandler.cs @@ -10,5 +10,5 @@ internal interface ICommandLineHandler { bool IsHelpInvoked(); - Task PrintHelpAsync(IPlatformOutputDevice platformOutputDevice, IReadOnlyList? availableTools = null); + Task PrintHelpAsync(IOutputDevice outputDevice, IReadOnlyList? availableTools = null); } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineManager.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineManager.cs index 3155669676..481bd37a20 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineManager.cs @@ -15,4 +15,11 @@ public interface ICommandLineManager /// /// The factory method for creating the command line options provider. void AddProvider(Func commandLineProviderFactory); + + /// + /// Adds a command line options provider. + /// + /// The factory method for creating the command line options provider, given a service provider. + [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] + void AddProvider(Func commandLineProviderFactory); } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineOptions.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineOptions.cs index 30abc03a5e..bfb1e2455b 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineOptions.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ICommandLineOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.CommandLine; /// diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/MaxFailedTestsCommandLineOptionsProvider.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/MaxFailedTestsCommandLineOptionsProvider.cs new file mode 100644 index 0000000000..4d4878b34d --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/MaxFailedTestsCommandLineOptionsProvider.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.CommandLine; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Resources; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Platform.CommandLine; + +internal sealed class MaxFailedTestsCommandLineOptionsProvider(IExtension extension, IServiceProvider serviceProvider) : ICommandLineOptionsProvider +{ + internal const string MaxFailedTestsOptionKey = "maximum-failed-tests"; + + private static readonly IReadOnlyCollection OptionsCache = + [ + new(MaxFailedTestsOptionKey, PlatformResources.PlatformCommandLineMaxFailedTestsOptionDescription, ArgumentArity.ExactlyOne, isHidden: false), + ]; + + public string Uid => extension.Uid; + + public string Version => extension.Version; + + public string DisplayName => extension.DisplayName; + + public string Description => extension.Description; + + public IReadOnlyCollection GetCommandLineOptions() + => OptionsCache; + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions) + => ValidationResult.ValidTask; + + public Task ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments) + { + if (commandOption.Name == MaxFailedTestsOptionKey) + { + string arg = arguments[0]; + // We consider --maximum-failed-tests 0 as invalid. + // The idea is that we stop the execution when we *reach* the max failed tests, not when *exceed*. + // So the value 1 means, stop execution on the first failure. + return int.TryParse(arg, out int maxFailedTestsResult) && maxFailedTestsResult > 0 + ? ValidateCapabilityAsync() + : ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.MaxFailedTestsMustBePositive, arg)); + } + + throw ApplicationStateGuard.Unreachable(); + } + + private Task ValidateCapabilityAsync() + => serviceProvider.GetTestFrameworkCapabilities().Capabilities.OfType().Any() + ? ValidationResult.ValidTask + : ValidationResult.InvalidTask(PlatformResources.AbortForMaxFailedTestsCapabilityNotAvailable); +} diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs index eb4f7a79a5..a95ccc9e83 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ParseResult.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; - namespace Microsoft.Testing.Platform.CommandLine; internal sealed class CommandLineParseResult(string? toolName, IReadOnlyList options, IReadOnlyList errors) : IEquatable diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs index b3d03c6b6f..ed1bf536d1 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/Parser.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs index 34685687d1..557efac8e0 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/PlatformCommandLineProvider.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Helpers; @@ -31,6 +28,7 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider public const string MinimumExpectedTestsOptionKey = "minimum-expected-tests"; public const string TestHostControllerPIDOptionKey = "internal-testhostcontroller-pid"; public const string ExitOnProcessExitOptionKey = "exit-on-process-exit"; + public const string ConfigFileOptionKey = "config-file"; public const string ServerOptionKey = "server"; public const string ClientPortOptionKey = "client-port"; @@ -59,6 +57,7 @@ internal sealed class PlatformCommandLineProvider : ICommandLineOptionsProvider new(DiscoverTestsOptionKey, PlatformResources.PlatformCommandLineDiscoverTestsOptionDescription, ArgumentArity.Zero, false, isBuiltIn: true), new(IgnoreExitCodeOptionKey, PlatformResources.PlatformCommandLineIgnoreExitCodeOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), new(ExitOnProcessExitOptionKey, PlatformResources.PlatformCommandLineExitOnProcessExitOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), + new(ConfigFileOptionKey, PlatformResources.PlatformCommandLineConfigFileOptionDescription, ArgumentArity.ExactlyOne, false, isBuiltIn: true), // Hidden options new(HelpOptionQuestionMark, PlatformResources.PlatformCommandLineHelpOptionDescription, ArgumentArity.Zero, true, isBuiltIn: true), @@ -120,6 +119,25 @@ public Task ValidateOptionArgumentsAsync(CommandLineOption com } } + if (commandOption.Name == ConfigFileOptionKey) + { + string arg = arguments[0]; + if (!File.Exists(arg)) + { + try + { + // Get the full path for better error messages. + // As this is only for the purpose of throwing an exception, ignore any exceptions during the GetFullPath call. + arg = Path.GetFullPath(arg); + } + catch + { + } + + return ValidationResult.InvalidTask(string.Format(CultureInfo.InvariantCulture, PlatformResources.ConfigurationFileNotFound, arg)); + } + } + // Now validate the minimum expected tests option return IsMinimumExpectedTestsOptionValidAsync(commandOption, arguments); } diff --git a/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs b/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs index 6b4d9979c9..1b9d4c057b 100644 --- a/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform/CommandLine/ResponseFileHelper.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.Testing.Platform.Resources; // Most of the core logic is from: @@ -156,7 +153,7 @@ public static IEnumerable SplitCommandLine(string commandLine) void Advance() => pos++; - string CurrentToken() => commandLine.Substring(startTokenIndex, IndexOfEndOfToken()).ToString().Replace("\"", string.Empty); + string CurrentToken() => commandLine.Substring(startTokenIndex, IndexOfEndOfToken()).Replace("\"", string.Empty); int IndexOfEndOfToken() => pos - startTokenIndex; diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationProvider.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationProvider.cs index 07808c30aa..1807b234dc 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationProvider.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform.Configurations; diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationSource.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationSource.cs index 2b064dd169..8279902eac 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationSource.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/EnvironmentVariablesConfigurationSource.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Platform.Configurations; -internal class EnvironmentVariablesConfigurationSource(IEnvironment environmentVariables) : IConfigurationSource +internal sealed class EnvironmentVariablesConfigurationSource(IEnvironment environmentVariables) : IConfigurationSource { private readonly IEnvironment _environmentVariables = environmentVariables; diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs index 5583122c4d..65a7f2a870 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Text.Json; using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.netstandard.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.netstandard.cs index 74b89b15e0..338c563b07 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.netstandard.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationFileParser.netstandard.cs @@ -3,8 +3,6 @@ #if !NETCOREAPP -using System.Globalization; - using Jsonite; using Microsoft.Testing.Platform.Resources; @@ -33,7 +31,7 @@ public static (Dictionary SingleValueData, Dictionary SingleValueData, Dictionary PropertyToAllChildren) ParseStream(Stream input) { using StreamReader reader = new(input); - var doc = (JsonObject)Jsonite.Json.Deserialize(reader.ReadToEnd(), _settings); + var doc = (JsonObject)Json.Deserialize(reader.ReadToEnd(), _settings); if (doc is not null) { VisitObjectElement(doc); @@ -66,7 +64,7 @@ private void SavePropertyToAllChildren(object? property) throw new FormatException(string.Format(CultureInfo.InvariantCulture, PlatformResources.JsonConfigurationFileParserDuplicateKeyErrorMessage, key)); } - _propertyToAllChildren[key] = Jsonite.Json.Serialize(property, _settings); + _propertyToAllChildren[key] = Json.Serialize(property, _settings); } private void VisitArrayElement(JsonArray array) @@ -116,7 +114,7 @@ private void VisitValue(object? value) // Adapt to the System.Text.Json serialization outcome _singleValueData[key] = value is bool boolean ? CultureInfo.InvariantCulture.TextInfo.ToTitleCase(boolean.ToString()) - : value is string stringValue ? stringValue.Trim('\"') : Jsonite.Json.Serialize(value, _settings); + : value is string stringValue ? stringValue.Trim('\"') : Json.Serialize(value, _settings); break; } diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationProvider.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationProvider.cs index 68b80c53a5..600d5232a7 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationProvider.cs @@ -1,18 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.Configurations; internal sealed partial class JsonConfigurationSource { - internal sealed class JsonConfigurationProvider(ITestApplicationModuleInfo testApplicationModuleInfo, IFileSystem fileSystem, ILogger? logger) : IConfigurationProvider + internal sealed class JsonConfigurationProvider( + ITestApplicationModuleInfo testApplicationModuleInfo, + IFileSystem fileSystem, + CommandLineParseResult commandLineParseResult, + ILogger? logger) : IConfigurationProvider { private readonly ITestApplicationModuleInfo _testApplicationModuleInfo = testApplicationModuleInfo; private readonly IFileSystem _fileSystem = fileSystem; + private readonly CommandLineParseResult _commandLineParseResult = commandLineParseResult; private readonly ILogger? _logger = logger; private Dictionary? _propertyToAllChildren; private Dictionary? _singleValueData; @@ -29,13 +36,35 @@ private async Task LogInformationAsync(string message) public async Task LoadAsync() { - string configFileName = $"{Path.Combine( - Path.GetDirectoryName(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath())!, - Path.GetFileNameWithoutExtension(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath()))}{PlatformConfigurationConstants.PlatformConfigSuffixFileName}"; - if (!_fileSystem.Exists(configFileName)) + string configFileName; + if (_commandLineParseResult.TryGetOptionArgumentList(PlatformCommandLineProvider.ConfigFileOptionKey, out string[]? configOptions)) { - await LogInformationAsync($"Config file '{configFileName}' not found."); - return; + configFileName = configOptions[0]; + if (!_fileSystem.Exists(configFileName)) + { + try + { + // Get the full path for better error messages. + // As this is only for the purpose of throwing an exception, ignore any exceptions during the GetFullPath call. + configFileName = Path.GetFullPath(configFileName); + } + catch + { + } + + throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ConfigurationFileNotFound, configFileName), configFileName); + } + } + else + { + configFileName = $"{Path.Combine( + Path.GetDirectoryName(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath())!, + Path.GetFileNameWithoutExtension(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath()))}{PlatformConfigurationConstants.PlatformConfigSuffixFileName}"; + + if (!_fileSystem.Exists(configFileName)) + { + return; + } } await LogInformationAsync($"Config file '{configFileName}' loaded."); diff --git a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationSource.cs b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationSource.cs index 470ab1712b..3855c806e8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationSource.cs +++ b/src/Platform/Microsoft.Testing.Platform/Configurations/JsonConfigurationSource.cs @@ -34,5 +34,5 @@ internal sealed partial class JsonConfigurationSource(ITestApplicationModuleInfo public Task IsEnabledAsync() => Task.FromResult(true); public Task BuildAsync(CommandLineParseResult commandLineParseResult) - => Task.FromResult((IConfigurationProvider)new JsonConfigurationProvider(_testApplicationModuleInfo, _fileSystem, _fileLoggerProvider?.CreateLogger(typeof(JsonConfigurationProvider).ToString()))); + => Task.FromResult((IConfigurationProvider)new JsonConfigurationProvider(_testApplicationModuleInfo, _fileSystem, commandLineParseResult, _fileLoggerProvider?.CreateLogger(typeof(JsonConfigurationProvider).ToString()))); } diff --git a/src/Platform/Microsoft.Testing.Platform/Extensions/AbortForMaxFailedTestsExtension.cs b/src/Platform/Microsoft.Testing.Platform/Extensions/AbortForMaxFailedTestsExtension.cs new file mode 100644 index 0000000000..78e4999b3c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Extensions/AbortForMaxFailedTestsExtension.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.CommandLine; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestHost; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.Resources; +using Microsoft.Testing.Platform.Services; + +namespace Microsoft.Testing.Platform.Extensions; + +internal sealed class AbortForMaxFailedTestsExtension : IDataConsumer +{ + private readonly int? _maxFailedTests; + private readonly IGracefulStopTestExecutionCapability? _capability; + private readonly IStopPoliciesService _policiesService; + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; + private int _failCount; + + public AbortForMaxFailedTestsExtension( + ICommandLineOptions commandLineOptions, + IGracefulStopTestExecutionCapability? capability, + IStopPoliciesService policiesService, + ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource) + { + if (commandLineOptions.TryGetOptionArgumentList(MaxFailedTestsCommandLineOptionsProvider.MaxFailedTestsOptionKey, out string[]? args) && + int.TryParse(args[0], out int maxFailedTests) && + maxFailedTests > 0) + { + _maxFailedTests = maxFailedTests; + } + + _capability = capability; + _policiesService = policiesService; + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; + } + + public Type[] DataTypesConsumed { get; } = [typeof(TestNodeUpdateMessage)]; + + /// + public string Uid { get; } = nameof(AbortForMaxFailedTestsExtension); + + /// + public string Version { get; } = AppVersion.DefaultSemVer; + + /// + public string DisplayName { get; } = nameof(AbortForMaxFailedTestsExtension); + + /// + public string Description { get; } = PlatformResources.AbortForMaxFailedTestsDescription; + + /// + public Task IsEnabledAsync() => Task.FromResult(_maxFailedTests.HasValue && _capability is not null); + + public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + { + var node = (TestNodeUpdateMessage)value; + + // If we are called, the extension is enabled, which means both _maxFailedTests and capability are not null. + RoslynDebug.Assert(_maxFailedTests is not null); + RoslynDebug.Assert(_capability is not null); + + TestNodeStateProperty testNodeStateProperty = node.TestNode.Properties.Single(); + if (TestNodePropertiesCategories.WellKnownTestNodeTestRunOutcomeFailedProperties.Any(t => t == testNodeStateProperty.GetType()) && + ++_failCount >= _maxFailedTests.Value && + // If already triggered, don't do it again. + !_policiesService.IsMaxFailedTestsTriggered) + { + await _capability.StopTestExecutionAsync(_testApplicationCancellationTokenSource.CancellationToken); + await _policiesService.ExecuteMaxFailedTestsCallbacksAsync(_maxFailedTests.Value, _testApplicationCancellationTokenSource.CancellationToken); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/Extensions/CompositeExtensionsFactory.cs b/src/Platform/Microsoft.Testing.Platform/Extensions/CompositeExtensionsFactory.cs index 84a48ce170..5e10f0400b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Extensions/CompositeExtensionsFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/Extensions/CompositeExtensionsFactory.cs @@ -17,7 +17,7 @@ namespace Microsoft.Testing.Platform.Extensions; public class CompositeExtensionFactory : ICompositeExtensionFactory, ICloneable where TExtension : class, IExtension { - private readonly object _syncLock = new(); + private readonly Lock _syncLock = new(); private readonly Func? _factoryWithServiceProvider; private readonly Func? _factory; private TExtension? _instance; diff --git a/src/Platform/Microsoft.Testing.Platform/Extensions/ValidationResult.cs b/src/Platform/Microsoft.Testing.Platform/Extensions/ValidationResult.cs index f1161cfbc3..5b3a1e7f0b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Extensions/ValidationResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/Extensions/ValidationResult.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Extensions; /// diff --git a/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs b/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs index 6e70346b51..a4f5d1ba6f 100644 --- a/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs +++ b/src/Platform/Microsoft.Testing.Platform/GlobalSuppressions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - // We usually do not want to suppress issues through GlobalSuppressions file but in this case we have to do it because // we are not able to suppress the issue differently. #pragma warning disable IDE0076 diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/ActionResult.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/ActionResult.cs index 6f1407d5be..4e8ea92035 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/ActionResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/ActionResult.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Helpers; internal class ActionResult diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/ApplicationStateGuard.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/ApplicationStateGuard.cs index 49b361b7cc..4f3e8964a3 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/ApplicationStateGuard.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/ApplicationStateGuard.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Runtime.CompilerServices; - #if !PLATFORM_MSBUILD using Microsoft.Testing.Platform.Resources; #endif diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/ArgumentGuard.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/ArgumentGuard.cs index a22e2ad75c..b47f0bfe9e 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/ArgumentGuard.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/ArgumentGuard.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Helpers; internal static class ArgumentGuard diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/AsyncMonitorFactory.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/AsyncMonitorFactory.cs index c0e6470be8..cec9823762 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/AsyncMonitorFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/AsyncMonitorFactory.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.Helpers; -internal class SystemMonitorAsyncFactory : IAsyncMonitorFactory +internal sealed class SystemMonitorAsyncFactory : IAsyncMonitorFactory { public IAsyncMonitor Create() => new SystemAsyncMonitor(); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/DisposeHelper.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/DisposeHelper.cs index 8b588b8877..5ff940fa0b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/DisposeHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/DisposeHelper.cs @@ -36,8 +36,6 @@ public static async Task DisposeAsync(object? obj) { dcDisposable.Dispose(); } - - await Task.CompletedTask; #endif } } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/EnvironmentVariableConstants.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/EnvironmentVariableConstants.cs index a08556f301..a5a971b4c0 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/EnvironmentVariableConstants.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/EnvironmentVariableConstants.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Helpers; [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Use nameof pattern")] diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/ExitCodes.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/ExitCodes.cs index 28514ae2c0..79e8fd0864 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/ExitCodes.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/ExitCodes.cs @@ -22,4 +22,5 @@ internal static class ExitCodes public const int TestAdapterTestSessionFailure = 10; public const int DependentProcessExited = 11; public const int IncompatibleProtocolVersion = 12; + public const int TestExecutionStoppedForMaxFailedTests = 13; } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/NonCooperativeParentProcessListener.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/NonCooperativeParentProcessListener.cs index 75e7b77b3a..040ed07481 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/NonCooperativeParentProcessListener.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/NonCooperativeParentProcessListener.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.Testing.Platform.CommandLine; namespace Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/ObjectPool.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/ObjectPool.cs index 63716f3cdb..41b5d95a1b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/ObjectPool.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/ObjectPool.cs @@ -13,13 +13,6 @@ // #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug. // #endif -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -#if DETECT_LEAKS -using System.Runtime.CompilerServices; - -#endif namespace Microsoft.Testing.Platform.Helpers; /// @@ -128,9 +121,7 @@ internal ObjectPool(Factory factory) internal ObjectPool(Factory factory, int size) { -#pragma warning disable SA1405 // Debug.Assert should provide message text - Debug.Assert(size >= 1); -#pragma warning restore SA1405 // Debug.Assert should provide message text + RoslynDebug.Assert(size >= 1); _factory = factory; _items = new Element[size - 1]; } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynDebug.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynDebug.cs index 59940b9ff3..63e4afa6de 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynDebug.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynDebug.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - // Copied from https://github.com/dotnet/roslyn-analyzers/blob/main/src/Utilities/Compiler/Debug.cs namespace Microsoft.Testing.Platform; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynHashCode.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynHashCode.cs index 681f7d397a..947431977f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynHashCode.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynHashCode.cs @@ -47,8 +47,6 @@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */ using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Security.Cryptography; namespace Microsoft.Testing.Platform; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynString.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynString.cs index f70cdb5bad..609335384b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynString.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/RoslynString.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform; [SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "This is the replacement helper")] diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs index 3966c96b39..99f5c70729 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; -using System.Text; namespace Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IEnvironment.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IEnvironment.cs index 6a90462096..b44afb7377 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IEnvironment.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IEnvironment.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; - namespace Microsoft.Testing.Platform.Helpers; internal interface IEnvironment diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs index 2502a9b078..cc5dea6734 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/IProcessHandler.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Helpers; internal interface IProcessHandler diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs index b957247835..214fca68bf 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Helpers; [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Match the Task API")] diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemClock.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemClock.cs index a985b5dcb2..b16ac89135 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemClock.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemClock.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.Helpers; -internal class SystemClock : IClock +internal sealed class SystemClock : IClock { public DateTimeOffset UtcNow => DateTimeOffset.UtcNow; } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemConsole.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemConsole.cs index a93dcc91ab..c781997504 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemConsole.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemConsole.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if NET8_0_OR_GREATER -using System.Runtime.InteropServices; -#endif - namespace Microsoft.Testing.Platform.Helpers; internal sealed class SystemConsole : IConsole @@ -12,6 +8,8 @@ internal sealed class SystemConsole : IConsole private const int WriteBufferSize = 256; private static readonly StreamWriter CaptureConsoleOutWriter; + internal static TextWriter ConsoleOut { get; } + /// /// Gets the height of the buffer area. /// @@ -29,16 +27,21 @@ internal sealed class SystemConsole : IConsole private bool _suppressOutput; - static SystemConsole() => + static SystemConsole() + { + // This is the console that the ITerminal will be writing to. + // So, this is what NonAnsiTerminal need to "lock" on regardless of whether it changed later. + ConsoleOut = Console.Out; // From https://github.com/dotnet/runtime/blob/main/src/libraries/System.Console/src/System/Console.cs#L236 CaptureConsoleOutWriter = new StreamWriter( stream: Console.OpenStandardOutput(), - encoding: Console.Out.Encoding, + encoding: ConsoleOut.Encoding, bufferSize: WriteBufferSize, leaveOpen: true) { AutoFlush = true, }; + } // the following event does not make sense in the mobile scenarios, user cannot ctrl+c // but can just kill the app in the device via a gesture diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemEnvironment.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemEnvironment.cs index 977d8b52ef..a7ea4efe62 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemEnvironment.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemEnvironment.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Helpers; [SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs", Justification = "This is the wrapper for Environment type.")] diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemMainModule.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemMainModule.cs index 054d72d9d5..e85b8e599e 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemMainModule.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemMainModule.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Helpers; #if NETCOREAPP diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs index d33c9c7a54..7c7ed64699 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcess.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Helpers; internal sealed class SystemProcess : IProcess, IDisposable diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs index 6794651a5c..dbb7589ad8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemProcessHandler.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.Testing.Platform.Resources; namespace Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemRuntimeFeature.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemRuntimeFeature.cs index 436c3580cc..f6d536b611 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemRuntimeFeature.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemRuntimeFeature.cs @@ -1,13 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if NETCOREAPP -using System.Runtime.CompilerServices; -#endif - namespace Microsoft.Testing.Platform.Helpers; -internal class SystemRuntimeFeature : IRuntimeFeature +internal sealed class SystemRuntimeFeature : IRuntimeFeature { #if NETCOREAPP public bool IsDynamicCodeSupported => RuntimeFeature.IsDynamicCodeSupported; diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemStopwatch.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemStopwatch.cs index 210513d609..7ce0c0b08a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemStopwatch.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemStopwatch.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Helpers; internal sealed class SystemStopwatch : IStopwatch diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs index 7ce3974fb7..e482f7ab42 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/TaskExtensions.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -// using System.Diagnostics; -using System.Runtime.CompilerServices; - namespace Microsoft.Testing.Platform.Helpers; // The idea was taken from https://github.com/dotnet/aspnetcore/blob/main/src/Shared/TaskExtensions.cs diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs index 512dc9e6a8..6ebc717cdc 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/TestApplicationBuilderExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; @@ -15,4 +13,12 @@ public static class TestApplicationBuilderExtensions { public static void AddTreeNodeFilterService(this ITestApplicationBuilder testApplicationBuilder, IExtension extension) => testApplicationBuilder.CommandLine.AddProvider(() => new TreeNodeFilterCommandLineOptionsProvider(extension)); + + /// + /// Registers the command-line options provider for '--maximum-failed-tests'. + /// + /// The test application builder. + [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] + public static void AddMaximumFailedTestsService(this ITestApplicationBuilder builder, IExtension extension) + => builder.CommandLine.AddProvider(serviceProvider => new MaxFailedTestsCommandLineOptionsProvider(extension, serviceProvider)); } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/TimeSpanParser.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/TimeSpanParser.cs index 7208cf0f0f..ffd6a8aea4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/TimeSpanParser.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/TimeSpanParser.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.Helpers; internal static partial class TimeSpanParser diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/UILanguageOverride.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/UILanguageOverride.cs index e2244a0f58..4755b34dbb 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/UILanguageOverride.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/UILanguageOverride.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform; diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs index 15ea8f2ccd..0e146d7e9c 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs @@ -69,7 +69,11 @@ public async Task RunAsync() await DisposeServiceProviderAsync(ServiceProvider, isProcessShutdown: true); await DisposeHelper.DisposeAsync(ServiceProvider.GetService()); await DisposeHelper.DisposeAsync(PushOnlyProtocol); - await DisposeHelper.DisposeAsync(ServiceProvider.GetTestApplicationCancellationTokenSource()); + + // This is intentional that we are not disposing the CTS. + // An unobserved task exception could be raised after the dispose, and we want to use OutputDevice there + // which needs CTS down the path. + // await DisposeHelper.DisposeAsync(ServiceProvider.GetTestApplicationCancellationTokenSource()); } if (testApplicationCancellationToken.IsCancellationRequested) @@ -119,7 +123,7 @@ private async Task RunTestAppAsync(CancellationToken testApplicationCancell protected abstract Task InternalRunAsync(); - protected static async Task ExecuteRequestAsync(IPlatformOutputDevice outputDevice, ITestSessionContext testSessionInfo, + protected static async Task ExecuteRequestAsync(ProxyOutputDevice outputDevice, ITestSessionContext testSessionInfo, ServiceProvider serviceProvider, BaseMessageBus baseMessageBus, ITestFramework testFramework, TestHost.ClientInfo client) { CancellationToken testSessionCancellationToken = serviceProvider.GetTestSessionContext().CancellationToken; @@ -142,12 +146,12 @@ protected static async Task ExecuteRequestAsync(IPlatformOutputDevice outputDevi await DisplayAfterSessionEndRunAsync(outputDevice, testSessionInfo, testSessionCancellationToken); } - private static async Task DisplayBeforeSessionStartAsync(IPlatformOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken) + private static async Task DisplayBeforeSessionStartAsync(ProxyOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken) { // Display before session start await outputDevice.DisplayBeforeSessionStartAsync(); - if (outputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandler) + if (outputDevice.OriginalOutputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandler) { await testSessionLifetimeHandler.OnTestSessionStartingAsync( sessionInfo.SessionId, @@ -155,13 +159,13 @@ await testSessionLifetimeHandler.OnTestSessionStartingAsync( } } - private static async Task DisplayAfterSessionEndRunAsync(IPlatformOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken) + private static async Task DisplayAfterSessionEndRunAsync(ProxyOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken) { // Display after session end await outputDevice.DisplayAfterSessionEndRunAsync(); // We want to ensure that the output service is the last one to run - if (outputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandlerFinishing) + if (outputDevice.OriginalOutputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandlerFinishing) { await testSessionLifetimeHandlerFinishing.OnTestSessionFinishingAsync( sessionInfo.SessionId, diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs index 15adebb7ff..026b1365d8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs @@ -1,15 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Requests; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.Telemetry; @@ -83,7 +81,7 @@ protected override async Task InternalRunAsync() ITestSessionContext testSessionInfo = ServiceProvider.GetTestSessionContext(); await ExecuteRequestAsync( - ServiceProvider.GetPlatformOutputDevice(), + (ProxyOutputDevice)ServiceProvider.GetOutputDevice(), testSessionInfo, ServiceProvider, ServiceProvider.GetBaseMessageBus(), diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs index 783713fd63..91154642f3 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using System.Net.Sockets; using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Messages; +using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.Requests; +using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.Telemetry; @@ -20,7 +21,7 @@ namespace Microsoft.Testing.Platform.Hosts; -internal sealed partial class ServerTestHost : CommonTestHost, IServerTestHost, IDisposable +internal sealed partial class ServerTestHost : CommonTestHost, IServerTestHost, IDisposable, IOutputDeviceDataProducer { public const string ProtocolVersion = "1.0.0"; private readonly Func> _buildTestFrameworkAsync; @@ -93,13 +94,36 @@ public ServerTestHost( protected override bool RunTestApplicationLifeCycleCallbacks => true; + public string Uid => nameof(ServerTestHost); + + public string Version => AppVersion.DefaultSemVer; + + public string DisplayName => PlatformResources.ServerTestHostDisplayName; + + public string Description => PlatformResources.ServerTestHostDescription; + private void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) - => _logger.LogWarning($"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}"); + { + _logger.LogWarning($"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}"); + + // Looks like nothing in this message to really be localized? + // All are class names, method names, property names, and placeholders. So none is localizable? + ServiceProvider.GetOutputDevice().DisplayAsync( + this, + new WarningMessageOutputDeviceData( + $"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}")) + .GetAwaiter().GetResult(); + } private void OnTaskSchedulerUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { e.SetObserved(); _logger.LogWarning($"[ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {e.Exception}"); + + // Looks like nothing in this message to really be localized? + // All are class names, method names, property names, and placeholders. So none is localizable? + ServiceProvider.GetOutputDevice().DisplayAsync(this, new WarningMessageOutputDeviceData(PlatformResources.UnobservedTaskExceptionWarningMessage)) + .GetAwaiter().GetResult(); } [MemberNotNull(nameof(_messageHandler))] @@ -118,13 +142,6 @@ protected override async Task InternalRunAsync() await _logger.LogDebugAsync("Starting server mode"); _messageHandler = await _messageHandlerFactory.CreateMessageHandlerAsync(_testApplicationCancellationTokenSource.CancellationToken); - // Initialize the ServerLoggerForwarderProvider, it can be null if diagnostic is disabled. - ServerLoggerForwarderProvider? serviceLoggerForwarder = ServiceProvider.GetService(); - if (serviceLoggerForwarder is not null) - { - await serviceLoggerForwarder.InitializeAsync(this); - } - await HandleMessagesAsync(); (_messageHandler as IDisposable)?.Dispose(); @@ -268,7 +285,14 @@ private async Task HandleNotificationAsync(NotificationMessage message, Cancella Exception? cancellationException = rpcState.CancelRequest(); if (cancellationException is null) { + // This is intentionally not using PlatformResources.ExceptionDuringCancellationWarningMessage + // It's meant for troubleshooting and shouldn't be localized. + // The localized message that is user-facing will be displayed in the DisplayAsync call next line. await _logger.LogWarningAsync($"Exception during the cancellation of request id '{args.CancelRequestId}'"); + + await ServiceProvider.GetOutputDevice().DisplayAsync( + this, + new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExceptionDuringCancellationWarningMessage, args.CancelRequestId))); } } @@ -450,13 +474,16 @@ private async Task ExecuteRequestAsync(RequestArgsBase args, s DateTimeOffset adapterLoadStart = _clock.UtcNow; + ProxyOutputDevice outputDevice = ServiceProvider.GetRequiredService(); + await outputDevice.InitializeAsync(this); + // Build the per request adapter - ITestFramework perRequestTestFramework = await _buildTestFrameworkAsync(new( + ITestFramework perRequestTestFramework = await _buildTestFrameworkAsync(new TestFrameworkBuilderData( perRequestServiceProvider, requestFactory, invoker, filterFactory, - new ServerModePerCallOutputDevice(), + outputDevice.OriginalOutputDevice, [testNodeUpdateProcessor], _testFrameworkManager, _testSessionManager, @@ -475,7 +502,7 @@ private async Task ExecuteRequestAsync(RequestArgsBase args, s // Execute the request await ExecuteRequestAsync( - perRequestServiceProvider.GetPlatformOutputDevice(), + outputDevice, perRequestServiceProvider.GetTestSessionContext(), perRequestServiceProvider, perRequestServiceProvider.GetBaseMessageBus(), @@ -632,6 +659,11 @@ public void Dispose() { // Note: The lifetime of the _reader/_writer should be currently handled by the RunAsync() // We could consider creating a stateful engine that has the lifetime == server connection UP. + if (!ServiceProvider.GetUnhandledExceptionsPolicy().FastFailOnFailure) + { + AppDomain.CurrentDomain.UnhandledException -= OnCurrentDomainUnhandledException; + TaskScheduler.UnobservedTaskException -= OnTaskSchedulerUnobservedTaskException; + } } internal async Task SendTestUpdateCompleteAsync(Guid runId) @@ -700,9 +732,11 @@ await SendMessageAsync( } } + public Task IsEnabledAsync() => throw new NotImplementedException(); + private sealed class RpcInvocationState : IDisposable { - private readonly object _cancellationTokenSourceLock = new(); + private readonly Lock _cancellationTokenSourceLock = new(); private readonly CancellationTokenSource _cancellationTokenSource = new(); private volatile bool _isDisposed; diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs index 838dc78ef3..c948281d6d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkBuilderData.cs @@ -11,7 +11,7 @@ namespace Microsoft.Testing.Platform.Hosts; -internal class TestFrameworkBuilderData(ServiceProvider serviceProvider, ITestExecutionRequestFactory testExecutionRequestFactory, +internal sealed class TestFrameworkBuilderData(ServiceProvider serviceProvider, ITestExecutionRequestFactory testExecutionRequestFactory, ITestFrameworkInvoker testExecutionRequestInvoker, ITestExecutionFilterFactory testExecutionFilterFactory, IPlatformOutputDevice platformOutputDisplayService, IEnumerable serverPerCallConsumers, TestFrameworkManager testFrameworkManager, TestHostManager testSessionManager, MessageBusProxy messageBusProxy, diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkProxy.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkProxy.cs index 569bbae639..d48bcb76df 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkProxy.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestFrameworkProxy.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs index 674335a56c..925b000fd4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; -using System.Reflection; -using System.Runtime.InteropServices; - using Microsoft.Testing.Internal.Framework; using Microsoft.Testing.Platform.Builder; using Microsoft.Testing.Platform.Capabilities.TestFramework; @@ -33,7 +28,7 @@ namespace Microsoft.Testing.Platform.Hosts; -internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFeature, IEnvironment environment, IProcessHandler processHandler, ITestApplicationModuleInfo testApplicationModuleInfo) : ITestHostBuilder +internal sealed class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFeature, IEnvironment environment, IProcessHandler processHandler, ITestApplicationModuleInfo testApplicationModuleInfo) : ITestHostBuilder { private readonly IFileSystem _fileSystem = fileSystem; private readonly ITestApplicationModuleInfo _testApplicationModuleInfo = testApplicationModuleInfo; @@ -196,20 +191,25 @@ public async Task BuildAsync( LoggerFactoryProxy loggerFactoryProxy = new(); serviceProvider.TryAddService(loggerFactoryProxy); - // Add output display proxy, needed by command line manager. - // We don't add to the service right now because we need special treatment between console/server mode. - IPlatformOutputDevice platformOutputDevice = ((PlatformOutputDeviceManager)OutputDisplay).Build(serviceProvider); - // Add Terminal options provider CommandLine.AddProvider(() => new TerminalTestReporterCommandLineOptionsProvider()); // Build the command line service - we need special treatment because is possible that an extension query it during the creation. // Add Retry default argument commandlines - CommandLineHandler commandLineHandler = await ((CommandLineManager)CommandLine).BuildAsync(loggingState.CommandLineParseResult); + CommandLineHandler commandLineHandler = await ((CommandLineManager)CommandLine).BuildAsync(loggingState.CommandLineParseResult, serviceProvider); // Set the concrete command line options to the proxy. commandLineOptionsProxy.SetCommandLineOptions(commandLineHandler); + // This is needed by output device. + var policiesService = new StopPoliciesService(testApplicationCancellationTokenSource); + serviceProvider.AddService(policiesService); + + bool hasServerFlag = commandLineHandler.TryGetOptionArgumentList(PlatformCommandLineProvider.ServerOptionKey, out string[]? protocolName); + bool isJsonRpcProtocol = protocolName is null || protocolName.Length == 0 || protocolName[0].Equals(PlatformCommandLineProvider.JsonRpcProtocolName, StringComparison.OrdinalIgnoreCase); + + ProxyOutputDevice proxyOutputDevice = await ((PlatformOutputDeviceManager)OutputDisplay).BuildAsync(serviceProvider, hasServerFlag && isJsonRpcProtocol); + // Add FileLoggerProvider if needed if (loggingState.FileLoggerProvider is not null) { @@ -219,16 +219,6 @@ public async Task BuildAsync( // Get the command line options ICommandLineOptions commandLineOptions = serviceProvider.GetCommandLineOptions(); - // Register the server mode log forwarder if needed. We follow the console --diagnostic behavior. - bool hasServerFlag = commandLineHandler.TryGetOptionArgumentList(PlatformCommandLineProvider.ServerOptionKey, out string[]? protocolName); - bool isJsonRpcProtocol = protocolName is null || protocolName.Length == 0 || protocolName[0].Equals(PlatformCommandLineProvider.JsonRpcProtocolName, StringComparison.OrdinalIgnoreCase); - if (hasServerFlag && isJsonRpcProtocol) - { - ServerLoggerForwarderProvider serverLoggerProxy = new(loggingState.LogLevel, serviceProvider); - serviceProvider.AddService(serverLoggerProxy); - Logging.AddProvider((logLevel, services) => serverLoggerProxy); - } - // Build the logger factory. ILoggerFactory loggerFactory = await ((LoggingManager)Logging).BuildAsync(serviceProvider, loggingState.LogLevel, systemMonitor); @@ -236,19 +226,14 @@ public async Task BuildAsync( loggerFactoryProxy.SetLoggerFactory(loggerFactory); // Initialize the output device if needed. - if (await platformOutputDevice.IsEnabledAsync()) - { - await platformOutputDevice.TryInitializeAsync(); - } - else + if (await proxyOutputDevice.OriginalOutputDevice.IsEnabledAsync()) { - // If for some reason the custom output is not enabled we opt-in the default terminal output device. - platformOutputDevice = PlatformOutputDeviceManager.GetDefaultTerminalOutputDevice(serviceProvider); - await platformOutputDevice.TryInitializeAsync(); + await proxyOutputDevice.OriginalOutputDevice.TryInitializeAsync(); } // Add the platform output device to the service provider for both modes. - serviceProvider.TryAddService(platformOutputDevice); + serviceProvider.TryAddService(proxyOutputDevice); + serviceProvider.TryAddService(proxyOutputDevice.OriginalOutputDevice); // Create the test framework capabilities ITestFrameworkCapabilities testFrameworkCapabilities = TestFramework.TestFrameworkCapabilitiesFactory(serviceProvider); @@ -257,6 +242,9 @@ public async Task BuildAsync( await testFrameworkCapabilitiesAsyncInitializable.InitializeAsync(); } + // Register the test framework capabilities to be used by services + serviceProvider.AddService(testFrameworkCapabilities); + // If command line is not valid we return immediately. ValidationResult commandLineValidationResult = await CommandLineOptionsValidator.ValidateAsync( loggingState.CommandLineParseResult, @@ -264,14 +252,11 @@ public async Task BuildAsync( commandLineHandler.ExtensionsCommandLineOptionsProviders, commandLineHandler); - // Register the test framework capabilities to be used by services - serviceProvider.AddService(testFrameworkCapabilities); - if (!loggingState.CommandLineParseResult.HasTool && !commandLineValidationResult.IsValid) { - await DisplayBannerIfEnabledAsync(loggingState, platformOutputDevice, testFrameworkCapabilities); - await platformOutputDevice.DisplayAsync(commandLineHandler, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(commandLineValidationResult.ErrorMessage)); - await commandLineHandler.PrintHelpAsync(platformOutputDevice); + await DisplayBannerIfEnabledAsync(loggingState, proxyOutputDevice, testFrameworkCapabilities); + await proxyOutputDevice.DisplayAsync(commandLineHandler, new ErrorMessageOutputDeviceData(commandLineValidationResult.ErrorMessage)); + await commandLineHandler.PrintHelpAsync(proxyOutputDevice); return new InformativeCommandLineTestHost(ExitCodes.InvalidCommandLine, serviceProvider); } @@ -307,7 +292,7 @@ public async Task BuildAsync( // Display banner now because we need capture the output in case of MSBuild integration and we want to forward // to file disc also the banner, so at this point we need to have all services and configuration(result directory) built. - await DisplayBannerIfEnabledAsync(loggingState, platformOutputDevice, testFrameworkCapabilities); + await DisplayBannerIfEnabledAsync(loggingState, proxyOutputDevice, testFrameworkCapabilities); // Add global telemetry service. // Add at this point or the telemetry banner appearance order will be wrong, we want the testing app banner before the telemetry banner. @@ -326,10 +311,10 @@ public async Task BuildAsync( // Register the ITestApplicationResult TestApplicationResult testApplicationResult = new( - platformOutputDevice, - serviceProvider.GetTestApplicationCancellationTokenSource(), + proxyOutputDevice, serviceProvider.GetCommandLineOptions(), - serviceProvider.GetEnvironment()); + serviceProvider.GetEnvironment(), + policiesService); serviceProvider.AddService(testApplicationResult); // ============= SETUP COMMON SERVICE USED IN ALL MODES END ===============// @@ -339,14 +324,14 @@ public async Task BuildAsync( // ======= TOOLS MODE ======== // // Add the platform output device to the service provider. var toolsServiceProvider = (ServiceProvider)serviceProvider.Clone(); - toolsServiceProvider.TryAddService(platformOutputDevice); + toolsServiceProvider.TryAddService(proxyOutputDevice); IReadOnlyList toolsInformation = await ((ToolsManager)Tools).BuildAsync(toolsServiceProvider); if (loggingState.CommandLineParseResult.HasTool) { // Add the platform output device to the service provider. - serviceProvider.TryAddService(platformOutputDevice); + serviceProvider.TryAddService(proxyOutputDevice); - ToolsTestHost toolsTestHost = new(toolsInformation, serviceProvider, commandLineHandler, platformOutputDevice); + ToolsTestHost toolsTestHost = new(toolsInformation, serviceProvider, commandLineHandler, proxyOutputDevice); await LogTestHostCreatedAsync( serviceProvider, @@ -373,7 +358,7 @@ await LogTestHostCreatedAsync( } else { - await commandLineHandler.PrintHelpAsync(platformOutputDevice, toolsInformation); + await commandLineHandler.PrintHelpAsync(proxyOutputDevice, toolsInformation); } return new InformativeCommandLineTestHost(0, serviceProvider); @@ -382,7 +367,7 @@ await LogTestHostCreatedAsync( // If --info is invoked we return if (commandLineHandler.IsInfoInvoked()) { - await commandLineHandler.PrintInfoAsync(platformOutputDevice, toolsInformation); + await commandLineHandler.PrintInfoAsync(proxyOutputDevice, toolsInformation); return new InformativeCommandLineTestHost(0, serviceProvider); } @@ -390,6 +375,8 @@ await LogTestHostCreatedAsync( TestHostOrchestratorConfiguration testHostOrchestratorConfiguration = await TestHostOrchestratorManager.BuildAsync(serviceProvider); if (testHostOrchestratorConfiguration.TestHostOrchestrators.Length > 0 && !commandLineHandler.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey)) { + policiesService.ProcessRole = TestProcessRole.TestHostOrchestrator; + await proxyOutputDevice.HandleProcessRoleAsync(TestProcessRole.TestHostOrchestrator); return new TestHostOrchestratorHost(testHostOrchestratorConfiguration, serviceProvider); } @@ -397,7 +384,7 @@ await LogTestHostCreatedAsync( // Check if we're in the test host or we should check test controllers extensions // Environment variable check should not be needed but in case we will rollback to use only env var we will need it. if ((!testHostControllerInfo.HasTestHostController || - systemEnvironment.GetEnvironmentVariable($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_SKIPEXTENSION}_{testHostControllerInfo.GetTestHostControllerPID(true)}") != "1") + systemEnvironment.GetEnvironmentVariable($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_SKIPEXTENSION}_{testHostControllerInfo.GetTestHostControllerPID()}") != "1") && !commandLineHandler.IsOptionSet(PlatformCommandLineProvider.DiscoverTestsOptionKey)) { PassiveNode? passiveNode = null; @@ -417,7 +404,7 @@ await LogTestHostCreatedAsync( var testHostControllersServiceProvider = (ServiceProvider)serviceProvider.Clone(); // Add the platform output device to the service provider. - testHostControllersServiceProvider.TryAddService(platformOutputDevice); + testHostControllersServiceProvider.TryAddService(proxyOutputDevice); // Add the message bus proxy specific for the launchers. testHostControllersServiceProvider.TryAddService(new MessageBusProxy()); @@ -425,6 +412,8 @@ await LogTestHostCreatedAsync( if (testHostControllers.RequireProcessRestart) { testHostControllerInfo.IsCurrentProcessTestHostController = true; + policiesService.ProcessRole = TestProcessRole.TestHostController; + await proxyOutputDevice.HandleProcessRoleAsync(TestProcessRole.TestHostController); TestHostControllersTestHost testHostControllersTestHost = new(testHostControllers, testHostControllersServiceProvider, passiveNode, systemEnvironment, loggerFactory, systemClock); await LogTestHostCreatedAsync( @@ -438,6 +427,8 @@ await LogTestHostCreatedAsync( } // ======= TEST HOST MODE ======== // + policiesService.ProcessRole = TestProcessRole.TestHost; + await proxyOutputDevice.HandleProcessRoleAsync(TestProcessRole.TestHost); // Setup the test host working folder. // Out of the test host controller extension the current working directory is the test host working directory. @@ -516,8 +507,7 @@ await LogTestHostCreatedAsync( serviceProvider, BuildTestFrameworkAsync, (TestFrameworkManager)TestFramework, - (TestHostManager)TestHost, - _testApplicationModuleInfo); + (TestHostManager)TestHost); // If needed we wrap the host inside the TestHostControlledHost to automatically handle the shutdown of the connected pipe. ITestHost actualTestHost = testControllerConnection is not null @@ -552,7 +542,7 @@ await LogTestHostCreatedAsync( return null; } - string pipeEnvironmentVariable = $"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PIPENAME}_{testHostControllerInfo.GetTestHostControllerPID(true)}"; + string pipeEnvironmentVariable = $"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PIPENAME}_{testHostControllerInfo.GetTestHostControllerPID()}"; string pipeName = environment.GetEnvironmentVariable(pipeEnvironmentVariable) ?? throw new InvalidOperationException($"Unexpected null pipe name from environment variable '{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_PIPENAME}'"); // RemoveVariable the environment variable so that it doesn't get passed to the eventually children processes @@ -581,7 +571,7 @@ await client.RequestReplyAsync( return client; } - protected virtual void AddApplicationMetadata(IServiceProvider serviceProvider, Dictionary builderMetadata) + private void AddApplicationMetadata(IServiceProvider serviceProvider, Dictionary builderMetadata) { ITelemetryInformation telemetryInformation = serviceProvider.GetTelemetryInformation(); if (!telemetryInformation.IsEnabled) @@ -639,7 +629,7 @@ private static async Task LogTestHostCreatedAsync( } } - private async Task BuildTestFrameworkAsync(TestFrameworkBuilderData testFrameworkBuilderData) + private static async Task BuildTestFrameworkAsync(TestFrameworkBuilderData testFrameworkBuilderData) { // Add the message bus proxy ServiceProvider serviceProvider = testFrameworkBuilderData.ServiceProvider; @@ -678,9 +668,6 @@ private async Task BuildTestFrameworkAsync(TestFrameworkBuilderD serviceProvider.AllowTestAdapterFrameworkRegistration = false; } - // Virtual callback that allows to the VSTest mode to register custom services needed by the bridge. - AfterTestAdapterCreation(serviceProvider); - // Prepare the session lifetime handlers for the notifications List testSessionLifetimeHandlers = []; @@ -742,6 +729,17 @@ private async Task BuildTestFrameworkAsync(TestFrameworkBuilderD dataConsumersBuilder.Add(pushOnlyProtocolDataConsumer); } + var abortForMaxFailedTestsExtension = new AbortForMaxFailedTestsExtension( + serviceProvider.GetCommandLineOptions(), + serviceProvider.GetTestFrameworkCapabilities().GetCapability(), + serviceProvider.GetRequiredService(), + serviceProvider.GetTestApplicationCancellationTokenSource()); + + if (await abortForMaxFailedTestsExtension.IsEnabledAsync()) + { + dataConsumersBuilder.Add(abortForMaxFailedTestsExtension); + } + IDataConsumer[] dataConsumerServices = dataConsumersBuilder.ToArray(); // Build the message bus @@ -779,39 +777,29 @@ private async Task BuildTestFrameworkAsync(TestFrameworkBuilderD return testFramework; } - protected virtual ConsoleTestHost CreateConsoleTestHost( + private static ConsoleTestHost CreateConsoleTestHost( ServiceProvider serviceProvider, Func> buildTestFrameworkAsync, TestFrameworkManager testFrameworkManager, - TestHostManager testHostManager, - ITestApplicationModuleInfo testApplicationModuleInfo) + TestHostManager testHostManager) => new(serviceProvider, buildTestFrameworkAsync, testFrameworkManager, testHostManager); - protected virtual bool SkipAddingService(object service) => false; - - protected virtual void AfterTestAdapterCreation(ServiceProvider serviceProvider) - { - } - - private async Task AddServiceIfNotSkippedAsync(object service, ServiceProvider serviceProvider) + private static async Task AddServiceIfNotSkippedAsync(object service, ServiceProvider serviceProvider) { - if (!SkipAddingService(service)) + if (service is IExtension extension) { - if (service is IExtension extension) - { - if (await extension.IsEnabledAsync()) - { - serviceProvider.TryAddService(service); - } - } - else + if (await extension.IsEnabledAsync()) { serviceProvider.TryAddService(service); } } + else + { + serviceProvider.TryAddService(service); + } } - private async Task RegisterAsServiceOrConsumerOrBothAsync(object service, ServiceProvider serviceProvider, + private static async Task RegisterAsServiceOrConsumerOrBothAsync(object service, ServiceProvider serviceProvider, List dataConsumersBuilder) { if (service is IDataConsumer dataConsumer) @@ -832,7 +820,7 @@ private async Task RegisterAsServiceOrConsumerOrBothAsync(object service, Servic await AddServiceIfNotSkippedAsync(service, serviceProvider); } - private async Task DisplayBannerIfEnabledAsync(ApplicationLoggingState loggingState, IPlatformOutputDevice platformOutputDevice, + private async Task DisplayBannerIfEnabledAsync(ApplicationLoggingState loggingState, ProxyOutputDevice outputDevice, ITestFrameworkCapabilities testFrameworkCapabilities) { bool isNoBannerSet = loggingState.CommandLineParseResult.IsOptionSet(PlatformCommandLineProvider.NoBannerOptionKey); @@ -844,7 +832,8 @@ private async Task DisplayBannerIfEnabledAsync(ApplicationLoggingState loggingSt string? bannerMessage = bannerMessageOwnerCapability is not null ? await bannerMessageOwnerCapability.GetBannerMessageAsync() : null; - await platformOutputDevice.DisplayBannerAsync(bannerMessage); + + await outputDevice.DisplayBannerAsync(bannerMessage); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs index 79ee5575c2..47aa9ec5ce 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControlledHost.cs @@ -7,7 +7,7 @@ namespace Microsoft.Testing.Platform.Hosts; -internal class TestHostControlledHost(NamedPipeClient testHostControllerPipeClient, ITestHost innerTestHost, CancellationToken cancellationToken) : ITestHost, IDisposable +internal sealed class TestHostControlledHost(NamedPipeClient testHostControllerPipeClient, ITestHost innerTestHost, CancellationToken cancellationToken) : ITestHost, IDisposable #if NETCOREAPP #pragma warning disable SA1001 // Commas should be spaced correctly , IAsyncDisposable diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs index f88b94fa54..6eac525c93 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions; @@ -77,7 +73,7 @@ protected override async Task InternalRunAsync() ITelemetryCollector telemetry = ServiceProvider.GetTelemetryCollector(); ITelemetryInformation telemetryInformation = ServiceProvider.GetTelemetryInformation(); string? extensionInformation = null; - IPlatformOutputDevice platformOutputDevice = ServiceProvider.GetPlatformOutputDevice(); + var outputDevice = (ProxyOutputDevice)ServiceProvider.GetOutputDevice(); IConfiguration configuration = ServiceProvider.GetConfiguration(); try { @@ -203,7 +199,7 @@ protected override async Task InternalRunAsync() displayErrorMessageBuilder.AppendLine(CultureInfo.InvariantCulture, $"Provider '{extension.DisplayName}' (UID: {extension.Uid}) failed with error: {errorMessage}"); } - await platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(displayErrorMessageBuilder.ToString())); + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(displayErrorMessageBuilder.ToString())); await _logger.LogErrorAsync(logErrorMessageBuilder.ToString()); return ExitCodes.InvalidPlatformSetup; } @@ -227,7 +223,7 @@ protected override async Task InternalRunAsync() string testHostProcessStartupTime = _clock.UtcNow.ToString("HH:mm:ss.fff", CultureInfo.InvariantCulture); processStartInfo.EnvironmentVariables.Add($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME}_{currentPID}", testHostProcessStartupTime); await _logger.LogDebugAsync($"{EnvironmentVariableConstants.TESTINGPLATFORM_TESTHOSTCONTROLLER_TESTHOSTPROCESSSTARTTIME}_{currentPID} '{testHostProcessStartupTime}'"); - await _logger.LogDebugAsync($"Starting test host process"); + await _logger.LogDebugAsync("Starting test host process"); using IProcess testHostProcess = process.Start(processStartInfo); int? testHostProcessId = null; @@ -254,7 +250,7 @@ protected override async Task InternalRunAsync() using (CancellationTokenSource timeout = new(TimeSpan.FromSeconds(timeoutSeconds))) using (var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, abortRun)) { - await _logger.LogDebugAsync($"Wait connection from the test host process"); + await _logger.LogDebugAsync("Wait connection from the test host process"); await testHostControllerIpc.WaitConnectionAsync(linkedToken.Token); } @@ -264,7 +260,7 @@ protected override async Task InternalRunAsync() _waitForPid.Wait(timeout.Token); } - await _logger.LogDebugAsync($"Fire OnTestHostProcessStartedAsync"); + await _logger.LogDebugAsync("Fire OnTestHostProcessStartedAsync"); if (_testHostPID is null) { @@ -283,7 +279,7 @@ protected override async Task InternalRunAsync() } } - await _logger.LogDebugAsync($"Wait for test host process exit"); + await _logger.LogDebugAsync("Wait for test host process exit"); await testHostProcess.WaitForExitAsync(); if (_testHostsInformation.LifetimeHandlers.Length > 0) @@ -306,7 +302,7 @@ protected override async Task InternalRunAsync() await messageBusProxy.DisableAsync(); } - await platformOutputDevice.DisplayAfterSessionEndRunAsync(); + await outputDevice.DisplayAfterSessionEndRunAsync(); // We collect info about the extensions before the dispose to avoid possible issue with cleanup. if (telemetryInformation.IsEnabled) @@ -322,7 +318,7 @@ protected override async Task InternalRunAsync() if (!_testHostGracefullyClosed && !abortRun.IsCancellationRequested) { - await platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(string.Format(CultureInfo.InvariantCulture, PlatformResources.TestProcessDidNotExitGracefullyErrorMessage, exitCode))); + await outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.TestProcessDidNotExitGracefullyErrorMessage, exitCode))); } await _logger.LogInformationAsync($"TestHostControllersTestHost ended with exit code '{exitCode}' (real test host exit code '{testHostProcess.ExitCode}')' in '{consoleRunStarted.Elapsed}'"); diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostOchestratorHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostOchestratorHost.cs index 3f51ed08cc..6eca7b889f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostOchestratorHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostOchestratorHost.cs @@ -8,7 +8,7 @@ namespace Microsoft.Testing.Platform.Hosts; -internal class TestHostOrchestratorHost(TestHostOrchestratorConfiguration testHostOrchestratorConfiguration, ServiceProvider serviceProvider) : ITestHost +internal sealed class TestHostOrchestratorHost(TestHostOrchestratorConfiguration testHostOrchestratorConfiguration, ServiceProvider serviceProvider) : ITestHost { private readonly TestHostOrchestratorConfiguration _testHostOrchestratorConfiguration = testHostOrchestratorConfiguration; private readonly ServiceProvider _serviceProvider = serviceProvider; diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs index ee472d57fd..983cd2db40 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/ToolsTestHost.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.CommandLine; @@ -20,12 +16,12 @@ internal sealed class ToolsTestHost( IReadOnlyList toolsInformation, ServiceProvider serviceProvider, CommandLineHandler commandLineHandler, - IPlatformOutputDevice platformOutputDevice) : ITestHost, IOutputDeviceDataProducer + IOutputDevice outputDevice) : ITestHost, IOutputDeviceDataProducer { private readonly IReadOnlyList _toolsInformation = toolsInformation; private readonly ServiceProvider _serviceProvider = serviceProvider; private readonly CommandLineHandler _commandLineHandler = commandLineHandler; - private readonly IPlatformOutputDevice _platformOutputDevice = platformOutputDevice; + private readonly IOutputDevice _outputDevice = outputDevice; /// public string Uid => nameof(ToolsTestHost); @@ -64,21 +60,21 @@ public async Task RunAsync() { if (UnknownOptions(out string? unknownOptionsError, tool)) { - await _platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(unknownOptionsError)); + await _outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(unknownOptionsError)); console.WriteLine(); return ExitCodes.InvalidCommandLine; } if (ExtensionArgumentArityAreInvalid(out string? arityErrors, tool)) { - await _platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(arityErrors)); + await _outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(arityErrors)); return ExitCodes.InvalidCommandLine; } ValidationResult optionsArgumentsValidationResult = await ValidateOptionsArgumentsAsync(tool); if (!optionsArgumentsValidationResult.IsValid) { - await _platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(optionsArgumentsValidationResult.ErrorMessage)); + await _outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData(optionsArgumentsValidationResult.ErrorMessage)); return ExitCodes.InvalidCommandLine; } @@ -86,8 +82,8 @@ public async Task RunAsync() } } - await _platformOutputDevice.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText($"Tool '{toolNameToRun}' not found in the list of registered tools.")); - await _commandLineHandler.PrintHelpAsync(_platformOutputDevice); + await _outputDevice.DisplayAsync(this, new ErrorMessageOutputDeviceData($"Tool '{toolNameToRun}' not found in the list of registered tools.")); + await _commandLineHandler.PrintHelpAsync(_outputDevice); return ExitCodes.InvalidCommandLine; } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessExitRequest.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessExitRequest.cs index c3b34f5444..8e6d85f2a2 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessExitRequest.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessExitRequest.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.IPC.Models; -internal class TestHostProcessExitRequest(int returnCode) : IRequest +internal sealed class TestHostProcessExitRequest(int returnCode) : IRequest { public int ExitCode { get; } = returnCode; } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessPIDRequest.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessPIDRequest.cs index de12594ebb..f0b2f9220a 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessPIDRequest.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Models/TestHostProcessPIDRequest.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.IPC.Models; -internal class TestHostProcessPIDRequest(int pid) : IRequest +internal sealed class TestHostProcessPIDRequest(int pid) : IRequest { public int PID { get; } = pid; } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs index 9f75028cd4..4a12606676 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeBase.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - #if !PLATFORM_MSBUILD using Microsoft.Testing.Platform.Resources; #endif diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs index d49a7b9cf6..9161243880 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeClient.cs @@ -8,7 +8,6 @@ #endif using System.IO.Pipes; -using System.Runtime.InteropServices; #if NET using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs index 4a88dea246..89deb4723e 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/NamedPipeServer.cs @@ -4,9 +4,7 @@ #if NET using System.Buffers; #endif -using System.Globalization; using System.IO.Pipes; -using System.Runtime.InteropServices; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs index 2b0df73190..ee10295eae 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs @@ -10,8 +10,6 @@ using Microsoft.Testing.Platform.Resources; #endif -using System.Text; - namespace Microsoft.Testing.Platform.IPC.Serializers; internal abstract class BaseSerializer diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/FileLogger.cs b/src/Platform/Microsoft.Testing.Platform/Logging/FileLogger.cs index f48b06ef98..b099b3aee5 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/FileLogger.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/FileLogger.cs @@ -3,12 +3,7 @@ #if NETCOREAPP using System.Threading.Channels; -#else -using System.Collections.Concurrent; #endif -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/FileLoggerInformation.cs b/src/Platform/Microsoft.Testing.Platform/Logging/FileLoggerInformation.cs index 573172ca4b..2237492e9b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/FileLoggerInformation.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/FileLoggerInformation.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.Logging; -internal record FileLoggerInformation(bool SyncronousWrite, FileInfo LogFile, LogLevel LogLevel) : IFileLoggerInformation +internal sealed record FileLoggerInformation(bool SyncronousWrite, FileInfo LogFile, LogLevel LogLevel) : IFileLoggerInformation { public bool SyncronousWrite { get; init; } = SyncronousWrite; diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/LoggerFactoryProxy.cs b/src/Platform/Microsoft.Testing.Platform/Logging/LoggerFactoryProxy.cs index be1c9ac261..a8ac01eee4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/LoggerFactoryProxy.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/LoggerFactoryProxy.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.Logging; -internal class LoggerFactoryProxy : ILoggerFactory +internal sealed class LoggerFactoryProxy : ILoggerFactory { private ILoggerFactory? _loggerFactory; diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessage.cs b/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessage.cs index f114cb8656..74a0f910e4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessage.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessage.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessageInMemoryStore.cs b/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessageInMemoryStore.cs index aa8c89f33a..b0ba1dbe45 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessageInMemoryStore.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/ServerLogMessageInMemoryStore.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.Hosts; namespace Microsoft.Testing.Platform.Logging; diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLoggerForwarder.cs b/src/Platform/Microsoft.Testing.Platform/Logging/ServerLoggerForwarder.cs deleted file mode 100644 index a3a69548d2..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLoggerForwarder.cs +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#if NETCOREAPP -using System.Diagnostics.CodeAnalysis; -using System.Threading.Channels; -#else -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -#endif -using System.Globalization; - -using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.Platform.Hosts; -using Microsoft.Testing.Platform.Resources; - -namespace Microsoft.Testing.Platform.Logging; - -/// -/// This logger forwards the messages back to the server host so that they're -/// logged over RPC back to the client. -/// -internal sealed class ServerLoggerForwarder : ILogger, IDisposable -#if NETCOREAPP -#pragma warning disable SA1001 // Commas should be spaced correctly - , IAsyncDisposable -#pragma warning restore SA1001 // Commas should be spaced correctly -#endif -{ - private readonly LogLevel _logLevel; - private readonly IServerTestHost _serverTestHost; - private readonly Task _logLoop; -#if NETCOREAPP - private readonly Channel? _channel; -#else - private readonly BlockingCollection? _asyncLogs; -#endif - - private bool _isDisposed; - - // NOTE: We have to take the service provider because when the logger is created, the ServerTestHost is not yet registered. - public ServerLoggerForwarder(LogLevel logLevel, ITask task, IServerTestHost serverTestHost) - { - _logLevel = logLevel; - _serverTestHost = serverTestHost; -#if NETCOREAPP - _channel = Channel.CreateUnbounded(new UnboundedChannelOptions - { - // We process only 1 data at a time - SingleReader = true, - - // We don't know how many threads will call the Log method - SingleWriter = false, - - // We want to unlink the caller from the consumer - AllowSynchronousContinuations = false, - }); -#else - _asyncLogs = []; -#endif - _logLoop = task.Run(WriteLogMessageAsync, CancellationToken.None); - } - - private async Task WriteLogMessageAsync() - { -#if NETCOREAPP - // We do this check out of the try because we want to crash the process if the _channel is null. - ApplicationStateGuard.Ensure(_channel is not null); - - // We don't need cancellation token because the task will be stopped when the Channel is completed thanks to the call to Complete() inside the Dispose method. - while (await _channel.Reader.WaitToReadAsync()) - { - await PushServerLogMessageToTheMessageBusAsync(await _channel.Reader.ReadAsync()); - } -#else - ApplicationStateGuard.Ensure(_asyncLogs is not null); - - // We don't need cancellation token because the task will be stopped when the BlockingCollection is completed thanks to the call to CompleteAdding() - // inside the Dispose method. - foreach (ServerLogMessage message in _asyncLogs.GetConsumingEnumerable()) - { - await PushServerLogMessageToTheMessageBusAsync(message); - } -#endif - } - - public bool IsEnabled(LogLevel logLevel) => logLevel >= _logLevel; - - public void Log(LogLevel logLevel, TState state, Exception? exception, Func formatter) - { - if (!IsEnabled(logLevel)) - { - return; - } - - string message = formatter(state, exception); - ServerLogMessage logMessage = new(logLevel, message); - EnsureAsyncLogObjectsAreNotNull(); -#if NETCOREAPP - if (!_channel.Writer.TryWrite(logMessage)) - { - throw new InvalidOperationException("Failed to write the log to the channel"); - } -#else - _asyncLogs.Add(logMessage); -#endif - } - - public async Task LogAsync(LogLevel logLevel, TState state, Exception? exception, Func formatter) - { - if (!IsEnabled(logLevel)) - { - return; - } - - string message = formatter(state, exception); - ServerLogMessage logMessage = new(logLevel, message); - await PushServerLogMessageToTheMessageBusAsync(logMessage); - } - - private async Task PushServerLogMessageToTheMessageBusAsync(ServerLogMessage logMessage) - => await _serverTestHost.PushDataAsync(logMessage); - -#if NETCOREAPP - [MemberNotNull(nameof(_channel), nameof(_logLoop))] - private void EnsureAsyncLogObjectsAreNotNull() - { - ApplicationStateGuard.Ensure(_channel is not null); - ApplicationStateGuard.Ensure(_logLoop is not null); - } -#else - [MemberNotNull(nameof(_asyncLogs), nameof(_logLoop))] - private void EnsureAsyncLogObjectsAreNotNull() - { - ApplicationStateGuard.Ensure(_asyncLogs is not null); - ApplicationStateGuard.Ensure(_logLoop is not null); - } -#endif - - public void Dispose() - { - if (_isDisposed) - { - return; - } - - EnsureAsyncLogObjectsAreNotNull(); -#if NETCOREAPP - // Wait for all logs to be written - bool result = _channel.Writer.TryComplete(); - if (!_logLoop.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout)) - { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.TimeoutFlushingLogsErrorMessage, TimeoutHelper.DefaultHangTimeoutSeconds)); - } -#else - // Wait for all logs to be written - _asyncLogs.CompleteAdding(); - if (!_logLoop.Wait(TimeoutHelper.DefaultHangTimeSpanTimeout)) - { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.TimeoutFlushingLogsErrorMessage, TimeoutHelper.DefaultHangTimeoutSeconds)); - } -#endif - _isDisposed = true; - } - -#if NETCOREAPP - public async ValueTask DisposeAsync() - { - if (_isDisposed) - { - return; - } - - EnsureAsyncLogObjectsAreNotNull(); - - // Wait for all logs to be written - _channel.Writer.TryComplete(); - await _logLoop.TimeoutAfterAsync(TimeoutHelper.DefaultHangTimeSpanTimeout); - _isDisposed = true; - } -#endif -} diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLoggerForwarderProvider.cs b/src/Platform/Microsoft.Testing.Platform/Logging/ServerLoggerForwarderProvider.cs deleted file mode 100644 index 8b2ffaff02..0000000000 --- a/src/Platform/Microsoft.Testing.Platform/Logging/ServerLoggerForwarderProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Platform.Hosts; -using Microsoft.Testing.Platform.Services; - -namespace Microsoft.Testing.Platform.Logging; - -internal sealed class ServerLoggerForwarderProvider(LogLevel logLevel, IServiceProvider serviceProvider) - : ILoggerProvider -{ - private readonly LogLevel _logLevel = logLevel; - private readonly ServerLogMessageInMemoryStore _serverLogMessageInMemoryStore = new(logLevel); - - private ServerTestHost? _serverTestHost; - - // If we don't have the server test host, we just log to the in-memory store. - public ILogger CreateLogger(string categoryName) - => _serverTestHost is null - ? _serverLogMessageInMemoryStore - : new ServerLoggerForwarder(_logLevel, serviceProvider.GetTask(), _serverTestHost); - - public async Task InitializeAsync(ServerTestHost serverTestHost) - { - _serverTestHost = serverTestHost; - _serverLogMessageInMemoryStore.Initialize(serverTestHost); - - foreach (ServerLogMessage serverLogMessage in _serverLogMessageInMemoryStore) - { - await _serverTestHost.PushDataAsync(serverLogMessage); - } - - _serverLogMessageInMemoryStore.Clean(); - } -} diff --git a/src/Platform/Microsoft.Testing.Platform/Logging/TypeNameHelper.cs b/src/Platform/Microsoft.Testing.Platform/Logging/TypeNameHelper.cs index d61f3c57ea..addac6ed99 100644 --- a/src/Platform/Microsoft.Testing.Platform/Logging/TypeNameHelper.cs +++ b/src/Platform/Microsoft.Testing.Platform/Logging/TypeNameHelper.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Text; - namespace Microsoft.Testing.Platform.Logging; /// @@ -34,12 +31,6 @@ internal static class TypeNameHelper { typeof(ushort), "ushort" }, }; - [return: NotNullIfNotNull(nameof(item))] - public static string? GetTypeDisplayName(object? item, bool fullName = true) - => item == null - ? null - : GetTypeDisplayName(item.GetType(), fullName); - /// /// Pretty print a type name. /// diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/AsynchronousMessageBus.cs b/src/Platform/Microsoft.Testing.Platform/Messages/AsynchronousMessageBus.cs index baede6aec5..b473a41956 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/AsynchronousMessageBus.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/AsynchronousMessageBus.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; @@ -13,7 +9,7 @@ namespace Microsoft.Testing.Platform.Messages; -internal class AsynchronousMessageBus : BaseMessageBus, IMessageBus, IDisposable +internal sealed class AsynchronousMessageBus : BaseMessageBus, IMessageBus, IDisposable { // This is an arbitrary number of attempts to drain the message bus. // The number of attempts is configurable via the environment variable TESTINGPLATFORM_MESSAGEBUS_DRAINDATA_ATTEMPTS. @@ -155,9 +151,9 @@ public override async Task DrainDataAsync() StringBuilder builder = new(); builder.Append(CultureInfo.InvariantCulture, $"Publisher/Consumer loop detected during the drain after {stopwatch.Elapsed}.\n{builder}"); - foreach (KeyValuePair keyValuePair in consumerToDrain) + foreach ((AsyncConsumerDataProcessor key, long value) in consumerToDrain) { - builder.AppendLine(CultureInfo.InvariantCulture, $"Consumer '{keyValuePair.Key.DataConsumer}' payload received {keyValuePair.Value}."); + builder.AppendLine(CultureInfo.InvariantCulture, $"Consumer '{key.DataConsumer}' payload received {value}."); } throw new InvalidOperationException(builder.ToString()); @@ -165,9 +161,9 @@ public override async Task DrainDataAsync() totalNumberOfDrainAttempt--; anotherRound = false; - foreach (KeyValuePair> dataTypeConsumer in _dataTypeConsumers) + foreach (List dataProcessors in _dataTypeConsumers.Values) { - foreach (AsyncConsumerDataProcessor asyncMultiProducerMultiConsumerDataProcessor in dataTypeConsumer.Value) + foreach (AsyncConsumerDataProcessor asyncMultiProducerMultiConsumerDataProcessor in dataProcessors) { if (!consumerToDrain.TryGetValue(asyncMultiProducerMultiConsumerDataProcessor, out long _)) { @@ -194,9 +190,9 @@ public override async Task DisableAsync() _disabled = true; - foreach (KeyValuePair> dataTypeConsumer in _dataTypeConsumers) + foreach (List dataProcessors in _dataTypeConsumers.Values) { - foreach (AsyncConsumerDataProcessor asyncMultiProducerMultiConsumerDataProcessor in dataTypeConsumer.Value) + foreach (AsyncConsumerDataProcessor asyncMultiProducerMultiConsumerDataProcessor in dataProcessors) { await asyncMultiProducerMultiConsumerDataProcessor.CompleteAddingAsync(); } @@ -205,9 +201,9 @@ public override async Task DisableAsync() public override void Dispose() { - foreach (KeyValuePair> dataTypeConsumer in _dataTypeConsumers) + foreach (List dataProcessors in _dataTypeConsumers.Values) { - foreach (AsyncConsumerDataProcessor asyncMultiProducerMultiConsumerDataProcessor in dataTypeConsumer.Value) + foreach (AsyncConsumerDataProcessor asyncMultiProducerMultiConsumerDataProcessor in dataProcessors) { asyncMultiProducerMultiConsumerDataProcessor.Dispose(); } diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/ChannelConsumerDataProcessor.cs b/src/Platform/Microsoft.Testing.Platform/Messages/ChannelConsumerDataProcessor.cs index 6ef8484b97..2982aeed35 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/ChannelConsumerDataProcessor.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/ChannelConsumerDataProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETCOREAPP -using System.Diagnostics; using System.Threading.Channels; using Microsoft.Testing.Platform.Extensions.Messages; @@ -12,7 +11,7 @@ namespace Microsoft.Testing.Platform.Messages; [DebuggerDisplay("DataConsumer = {DataConsumer.Uid}")] -internal class AsyncConsumerDataProcessor : IDisposable +internal sealed class AsyncConsumerDataProcessor : IDisposable { private readonly ITask _task; private readonly CancellationToken _cancellationToken; diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/ConsumingEnumerableConsumerDataProcessor.cs b/src/Platform/Microsoft.Testing.Platform/Messages/ConsumingEnumerableConsumerDataProcessor.cs index 4779fa1ee1..da00c06387 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/ConsumingEnumerableConsumerDataProcessor.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/ConsumingEnumerableConsumerDataProcessor.cs @@ -2,15 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !NETCOREAPP -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform.Messages; -internal class AsyncConsumerDataProcessor : IDisposable +internal sealed class AsyncConsumerDataProcessor : IDisposable { // The default underlying collection is a ConcurrentQueue object, which provides first in, first out (FIFO) behavior. private readonly BlockingCollection<(IDataProducer DataProducer, IData Data)> _payloads = []; diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs b/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs index 7eaedb7c68..c25e478742 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/DataWithSessionUid.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - using Microsoft.Testing.Platform.TestHost; namespace Microsoft.Testing.Platform.Extensions.Messages; diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs b/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs index 273023fecd..c29dc9f6db 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - using Microsoft.Testing.Platform.TestHost; namespace Microsoft.Testing.Platform.Extensions.Messages; diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/MessageBusProxy.cs b/src/Platform/Microsoft.Testing.Platform/Messages/MessageBusProxy.cs index 889dcd77c7..20f550e813 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/MessageBusProxy.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/MessageBusProxy.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.Property.cs b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.Property.cs index a72c9adb96..17234b658a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.Property.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.Property.cs @@ -1,15 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Extensions.Messages; public sealed partial class PropertyBag { [DebuggerTypeProxy(typeof(PropertyDebugView))] - internal /* for testing */ class Property(IProperty current, Property? next = null) : IEnumerable + internal /* for testing */ sealed class Property(IProperty current, Property? next = null) : IEnumerable { public int Count { diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.PropertyBagEnumerable.cs b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.PropertyBagEnumerable.cs index d532cd64ac..06abb42e57 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.PropertyBagEnumerable.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.PropertyBagEnumerable.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; - namespace Microsoft.Testing.Platform.Extensions.Messages; public sealed partial class PropertyBag diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.cs b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.cs index ba48bb2277..2a0475dbc9 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Extensions.Messages; public sealed partial class PropertyBag diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs index e438002206..d0bc0aa444 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/PropertyBagData.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - namespace Microsoft.Testing.Platform.Extensions.Messages; public abstract class PropertyBagData(string displayName, string? description) : IData diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs index ff3bc023ed..5a1096a0b5 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNode.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - namespace Microsoft.Testing.Platform.Extensions.Messages; public class TestNode diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs index 01eb459e07..83afea914a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeProperties.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Extensions.Messages; /// diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs index 28604fb2f4..45a4db59a9 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestNodeUpdateMessage.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - using Microsoft.Testing.Platform.TestHost; namespace Microsoft.Testing.Platform.Extensions.Messages; diff --git a/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs b/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs index 02fc44e877..1e13d34da8 100644 --- a/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Messages/TestRequestExecutionTimeInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - namespace Microsoft.Testing.Platform.Extensions.Messages; internal readonly struct TestRequestExecutionTimeInfo(TimingInfo timingInfo) : IData diff --git a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj index 20f935bb6b..2aade9a71e 100644 --- a/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj +++ b/src/Platform/Microsoft.Testing.Platform/Microsoft.Testing.Platform.csproj @@ -48,7 +48,6 @@ This package provides the core platform and the .NET implementation of the proto - @@ -63,8 +62,6 @@ This package provides the core platform and the .NET implementation of the proto - - diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/ErrorMessageOutputDeviceData.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ErrorMessageOutputDeviceData.cs new file mode 100644 index 0000000000..e7b5cc0cb3 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ErrorMessageOutputDeviceData.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.OutputDevice; + +public sealed class ErrorMessageOutputDeviceData(string message) : IOutputDeviceData +{ + public string Message { get; } = message; +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/FormattedTextOutputDeviceDataBuilder.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/FormattedTextOutputDeviceDataBuilder.cs index cb396fb974..0741b46c0d 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/FormattedTextOutputDeviceDataBuilder.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/FormattedTextOutputDeviceDataBuilder.cs @@ -1,16 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace Microsoft.Testing.Platform.OutputDevice; +[EditorBrowsable(EditorBrowsableState.Never)] +[Obsolete("Do not use this class. This is present temporarily for internal usages (in Retry extension) and will be removed after fixing the internal usages", error: true)] internal static class FormattedTextOutputDeviceDataBuilder { public static FormattedTextOutputDeviceData CreateGreenConsoleColorText(string text) - => new(text) { ForegroundColor = new SystemConsoleColor { ConsoleColor = ConsoleColor.Green } }; + => new(text) { ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Green } }; public static FormattedTextOutputDeviceData CreateRedConsoleColorText(string text) - => new(text) { ForegroundColor = new SystemConsoleColor { ConsoleColor = ConsoleColor.Red } }; + => new(text) { ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Red } }; public static FormattedTextOutputDeviceData CreateYellowConsoleColorText(string text) - => new(text) { ForegroundColor = new SystemConsoleColor { ConsoleColor = ConsoleColor.Yellow } }; + => new(text) { ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Yellow } }; } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs index ac87b96c2a..22705e1968 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/IPlatformOutputDevice.cs @@ -2,14 +2,19 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.OutputDevice; namespace Microsoft.Testing.Platform.OutputDevice; -internal interface IPlatformOutputDevice : IExtension, IOutputDevice +internal interface IPlatformOutputDevice : IExtension { Task DisplayBannerAsync(string? bannerMessage); Task DisplayBeforeSessionStartAsync(); Task DisplayAfterSessionEndRunAsync(); + + Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data); + + Task HandleProcessRoleAsync(TestProcessRole processRole); } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs index 3c13dcc4a2..78d4dafb0f 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Testing.Platform.Logging; +using Microsoft.Testing.Platform.ServerMode; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.OutputDevice; @@ -15,15 +17,27 @@ public void SetPlatformOutputDevice(Func BuildAsync(ServiceProvider serviceProvider, bool useServerModeOutputDevice) { - if (_platformOutputDeviceFactory is not null) + // TODO: SetPlatformOutputDevice isn't public yet. + // Before exposing it, do we want to pass the "useServerModeOutputDevice" info to it? + IPlatformOutputDevice nonServerOutputDevice = _platformOutputDeviceFactory is null + ? GetDefaultTerminalOutputDevice(serviceProvider) + : _platformOutputDeviceFactory(serviceProvider); + + // If the externally provided output device is not enabled, we opt-in the default terminal output device. + if (_platformOutputDeviceFactory is not null && !await nonServerOutputDevice.IsEnabledAsync()) { - IPlatformOutputDevice platformOutputDevice = _platformOutputDeviceFactory(serviceProvider); - return platformOutputDevice; + nonServerOutputDevice = GetDefaultTerminalOutputDevice(serviceProvider); } - return GetDefaultTerminalOutputDevice(serviceProvider); + return new ProxyOutputDevice( + nonServerOutputDevice, + useServerModeOutputDevice + ? new ServerModePerCallOutputDevice( + serviceProvider.GetService(), + serviceProvider.GetRequiredService()) + : null); } public static TerminalOutputDevice GetDefaultTerminalOutputDevice(ServiceProvider serviceProvider) @@ -40,5 +54,6 @@ public static TerminalOutputDevice GetDefaultTerminalOutputDevice(ServiceProvide serviceProvider.GetCommandLineOptions(), serviceProvider.GetFileLoggerInformation(), serviceProvider.GetLoggerFactory(), - serviceProvider.GetClock()); + serviceProvider.GetClock(), + serviceProvider.GetRequiredService()); } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs new file mode 100644 index 0000000000..b2d0b2ed4c --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/ProxyOutputDevice.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Hosts; +using Microsoft.Testing.Platform.ServerMode; + +namespace Microsoft.Testing.Platform.OutputDevice; + +internal sealed class ProxyOutputDevice : IOutputDevice +{ + private readonly ServerModePerCallOutputDevice? _serverModeOutputDevice; + + public ProxyOutputDevice(IPlatformOutputDevice originalOutputDevice, ServerModePerCallOutputDevice? serverModeOutputDevice) + { + OriginalOutputDevice = originalOutputDevice; + _serverModeOutputDevice = serverModeOutputDevice; + } + + internal IPlatformOutputDevice OriginalOutputDevice { get; } + + public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) + { + await OriginalOutputDevice.DisplayAsync(producer, data); + if (_serverModeOutputDevice is not null) + { + await _serverModeOutputDevice.DisplayAsync(producer, data); + } + } + + internal async Task DisplayBannerAsync(string? bannerMessage) + { + await OriginalOutputDevice.DisplayBannerAsync(bannerMessage); + if (_serverModeOutputDevice is not null) + { + await _serverModeOutputDevice.DisplayBannerAsync(bannerMessage); + } + } + + internal async Task DisplayBeforeSessionStartAsync() + { + await OriginalOutputDevice.DisplayBeforeSessionStartAsync(); + if (_serverModeOutputDevice is not null) + { + await _serverModeOutputDevice.DisplayBeforeSessionStartAsync(); + } + } + + internal async Task DisplayAfterSessionEndRunAsync() + { + await OriginalOutputDevice.DisplayAfterSessionEndRunAsync(); + if (_serverModeOutputDevice is not null) + { + await _serverModeOutputDevice.DisplayAfterSessionEndRunAsync(); + } + } + + internal async Task InitializeAsync(ServerTestHost serverTestHost) + { + if (_serverModeOutputDevice is not null) + { + await _serverModeOutputDevice.InitializeAsync(serverTestHost); + } + } + + internal async Task HandleProcessRoleAsync(TestProcessRole processRole) + { + await OriginalOutputDevice.HandleProcessRoleAsync(processRole); + if (_serverModeOutputDevice is not null) + { + await _serverModeOutputDevice.HandleProcessRoleAsync(processRole); + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TargetFrameworkParser.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TargetFrameworkParser.cs index 1e8e4ec7a5..4d7b84441c 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TargetFrameworkParser.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TargetFrameworkParser.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - namespace Microsoft.Testing.Platform.OutputDevice; internal static class TargetFrameworkParser diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs index 3d949172d8..5dd4b3e884 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiDetector.cs @@ -5,8 +5,6 @@ // https://github.com/spectreconsole/spectre.console/blob/main/src/Spectre.Console/Internal/Backends/Ansi/AnsiDetector.cs // and from the supports-ansi project by Qingrong Ke // https://github.com/keqingrong/supports-ansi/blob/master/index.js -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs index 9c9341357c..55b320706a 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminal.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Runtime.InteropServices; -using System.Text; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; @@ -45,7 +41,7 @@ internal sealed class AnsiTerminal : ITerminal private readonly bool _useBusyIndicator; private readonly StringBuilder _stringBuilder = new(); private bool _isBatching; - private AnsiTerminalTestProgressFrame _currentFrame = new(Array.Empty(), 0, 0); + private AnsiTerminalTestProgressFrame _currentFrame = new(0, 0); public AnsiTerminal(IConsole console, string? baseDirectory) { @@ -274,27 +270,20 @@ public void SetCursorHorizontal(int position) /// public void EraseProgress() { - if (_currentFrame.ProgressCount == 0) + if (_currentFrame.RenderedLines == null || _currentFrame.RenderedLines.Count == 0) { return; } - AppendLine($"{AnsiCodes.CSI}{_currentFrame.ProgressCount + 2}{AnsiCodes.MoveUpToLineStart}"); + AppendLine($"{AnsiCodes.CSI}{_currentFrame.RenderedLines.Count + 2}{AnsiCodes.MoveUpToLineStart}"); Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInDisplay}"); _currentFrame.Clear(); } public void RenderProgress(TestProgressState?[] progress) { - AnsiTerminalTestProgressFrame newFrame = new(progress, Width, Height); - - // Do not render delta but clear everything if Terminal width or height have changed. - if (newFrame.Width != _currentFrame.Width || newFrame.Height != _currentFrame.Height) - { - EraseProgress(); - } - - newFrame.Render(_currentFrame, this); + AnsiTerminalTestProgressFrame newFrame = new(Width, Height); + newFrame.Render(_currentFrame, progress, terminal: this); _currentFrame = newFrame; } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs index c2b3d7da46..1d1bb64152 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/AnsiTerminalTestProgressFrame.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// @@ -12,43 +10,29 @@ internal sealed class AnsiTerminalTestProgressFrame { private const int MaxColumn = 250; - private readonly (TestProgressState TestProgressState, int DurationLength)[] _progressItems; - public int Width { get; } public int Height { get; } - public int ProgressCount { get; private set; } + public List? RenderedLines { get; set; } - public AnsiTerminalTestProgressFrame(TestProgressState?[] nodes, int width, int height) + public AnsiTerminalTestProgressFrame(int width, int height) { Width = Math.Min(width, MaxColumn); Height = height; - - _progressItems = new (TestProgressState, int)[nodes.Length]; - - foreach (TestProgressState? status in nodes) - { - if (status is not null) - { - _progressItems[ProgressCount++].TestProgressState = status; - } - } } - public void AppendTestWorkerProgress(int i, AnsiTerminal terminal) + public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgressItem currentLine, AnsiTerminal terminal) { - TestProgressState p = _progressItems[i].TestProgressState; + string durationString = HumanReadableDurationFormatter.Render(progress.Stopwatch.Elapsed); - string durationString = HumanReadableDurationFormatter.Render(p.Stopwatch.Elapsed); - - _progressItems[i].DurationLength = durationString.Length; + currentLine.RenderedDurationLength = durationString.Length; int nonReservedWidth = Width - (durationString.Length + 2); - int passed = p.PassedTests; - int failed = p.FailedTests; - int skipped = p.SkippedTests; + int passed = progress.PassedTests; + int failed = progress.FailedTests; + int skipped = progress.SkippedTests; int charsTaken = 0; terminal.Append('['); @@ -85,28 +69,27 @@ public void AppendTestWorkerProgress(int i, AnsiTerminal terminal) terminal.Append(']'); charsTaken++; - // -5 because we want to output at least 1 char from the name, and ' ...' followed by duration terminal.Append(' '); charsTaken++; - AppendToWidth(terminal, p.AssemblyName, nonReservedWidth, ref charsTaken); + AppendToWidth(terminal, progress.AssemblyName, nonReservedWidth, ref charsTaken); - if (charsTaken < nonReservedWidth && (p.TargetFramework != null || p.Architecture != null)) + if (charsTaken < nonReservedWidth && (progress.TargetFramework != null || progress.Architecture != null)) { int lengthNeeded = 0; lengthNeeded++; // for '(' - if (p.TargetFramework != null) + if (progress.TargetFramework != null) { - lengthNeeded += p.TargetFramework.Length; - if (p.Architecture != null) + lengthNeeded += progress.TargetFramework.Length; + if (progress.Architecture != null) { lengthNeeded++; // for '|' } } - if (p.Architecture != null) + if (progress.Architecture != null) { - lengthNeeded += p.Architecture.Length; + lengthNeeded += progress.Architecture.Length; } lengthNeeded++; // for ')' @@ -114,29 +97,41 @@ public void AppendTestWorkerProgress(int i, AnsiTerminal terminal) if ((charsTaken + lengthNeeded) < nonReservedWidth) { terminal.Append(" ("); - if (p.TargetFramework != null) + if (progress.TargetFramework != null) { - terminal.Append(p.TargetFramework); - if (p.Architecture != null) + terminal.Append(progress.TargetFramework); + if (progress.Architecture != null) { terminal.Append('|'); } } - if (p.Architecture != null) + if (progress.Architecture != null) { - terminal.Append(p.Architecture); + terminal.Append(progress.Architecture); } terminal.Append(')'); } } - if (!RoslynString.IsNullOrWhiteSpace(p.Detail)) - { - terminal.Append(" - "); - terminal.Append(p.Detail); - } + terminal.SetCursorHorizontal(Width - durationString.Length); + terminal.Append(durationString); + } + + public void AppendTestWorkerDetail(TestDetailState detail, RenderedProgressItem currentLine, AnsiTerminal terminal) + { + string durationString = HumanReadableDurationFormatter.Render(detail.Stopwatch?.Elapsed); + + currentLine.RenderedDurationLength = durationString.Length; + + int nonReservedWidth = Width - (durationString.Length + 2); + int charsTaken = 0; + + terminal.Append(" "); + charsTaken += 2; + + AppendToWidth(terminal, detail.Text, nonReservedWidth, ref charsTaken); terminal.SetCursorHorizontal(Width - durationString.Length); terminal.Append(durationString); @@ -166,60 +161,131 @@ private static void AppendToWidth(AnsiTerminal terminal, string text, int width, /// /// Render VT100 string to update from current to next frame. /// - public void Render(AnsiTerminalTestProgressFrame previousFrame, AnsiTerminal terminal) + public void Render(AnsiTerminalTestProgressFrame previousFrame, TestProgressState?[] progress, AnsiTerminal terminal) { - // Don't go up if we did not render progress in previous frame or we cleared it. - if (previousFrame.ProgressCount > 0) + // Clear everything if Terminal width or height have changed. + if (Width != previousFrame.Width || Height != previousFrame.Height) + { + terminal.EraseProgress(); + } + + // At the end of the terminal we're going to print the live progress. + // We re-render this progress by moving the cursor to the beginning of the previous progress + // and then overwriting the lines that have changed. + // The assumption we do here is that: + // - Each rendered line is a single line, i.e. a single detail cannot span multiple lines. + // - Each rendered detail can be tracked via a unique ID and version, so that we can + // quickly determine if the detail has changed since the last render. + + // Don't go up if we did not render any lines in previous frame or we already cleared them. + if (previousFrame.RenderedLines != null && previousFrame.RenderedLines.Count > 0) { // Move cursor back to 1st line of progress. - // +2 because we prepend 1 empty line before the progress - // and new line after the progress indicator. - terminal.MoveCursorUp(previousFrame.ProgressCount + 2); + // + 2 because we output and empty line right below. + terminal.MoveCursorUp(previousFrame.RenderedLines.Count + 2); } // When there is nothing to render, don't write empty lines, e.g. when we start the test run, and then we kick off build // in dotnet test, there is a long pause where we have no assemblies and no test results (yet). - if (ProgressCount > 0) + if (progress.Length > 0) { terminal.AppendLine(); } int i = 0; - for (; i < ProgressCount; i++) + RenderedLines = new List(progress.Length * 2); + List progresses = GenerateLinesToRender(progress); + + foreach (object item in progresses) { - // Optimize the rendering. When we have previous frame to compare with, we can decide to rewrite only part of the screen, - // rather than deleting whole line and have the line flicker. Most commonly this will rewrite just the time part of the line. - if (previousFrame.ProgressCount > i) + if (previousFrame.RenderedLines != null && previousFrame.RenderedLines.Count > i) { - if (previousFrame._progressItems[i].TestProgressState.LastUpdate != _progressItems[i].TestProgressState.LastUpdate) + if (item is TestProgressState progressItem) { - // Same everything except time. - string durationString = HumanReadableDurationFormatter.Render(_progressItems[i].TestProgressState.Stopwatch.Elapsed); + var currentLine = new RenderedProgressItem(progressItem.Id, progressItem.Version); + RenderedLines.Add(currentLine); - if (previousFrame._progressItems[i].DurationLength == durationString.Length) + // We have a line that was rendered previously, compare it and decide how to render. + RenderedProgressItem previouslyRenderedLine = previousFrame.RenderedLines[i]; + if (previouslyRenderedLine.ProgressId == progressItem.Id && false) { - terminal.SetCursorHorizontal(MaxColumn); - terminal.Append($"{AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(durationString.Length)}{durationString}"); - _progressItems[i].DurationLength = durationString.Length; + // This is the same progress item and it was not updated since we rendered it, only update the timestamp if possible to avoid flicker. + string durationString = HumanReadableDurationFormatter.Render(progressItem.Stopwatch.Elapsed); + + if (previouslyRenderedLine.RenderedDurationLength == durationString.Length) + { + // Duration is the same length rewrite just it. + terminal.SetCursorHorizontal(MaxColumn); + terminal.Append($"{AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(durationString.Length)}{durationString}"); + currentLine.RenderedDurationLength = durationString.Length; + } + else + { + // Duration is not the same length (it is longer because time moves only forward), we need to re-render the whole line + // to avoid writing the duration over the last portion of text: my.dll (1s) -> my.d (1m 1s) + terminal.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); + AppendTestWorkerProgress(progressItem, currentLine, terminal); + } } else { - // Render full line. + // These lines are different or the line was updated. Render the whole line. terminal.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); - AppendTestWorkerProgress(i, terminal); + AppendTestWorkerProgress(progressItem, currentLine, terminal); } } - else + + if (item is TestDetailState detailItem) { - // Render full line. - terminal.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); - AppendTestWorkerProgress(i, terminal); + var currentLine = new RenderedProgressItem(detailItem.Id, detailItem.Version); + RenderedLines.Add(currentLine); + + // We have a line that was rendered previously, compare it and decide how to render. + RenderedProgressItem previouslyRenderedLine = previousFrame.RenderedLines[i]; + if (previouslyRenderedLine.ProgressId == detailItem.Id && previouslyRenderedLine.ProgressVersion == detailItem.Version) + { + // This is the same progress item and it was not updated since we rendered it, only update the timestamp if possible to avoid flicker. + string durationString = HumanReadableDurationFormatter.Render(detailItem.Stopwatch?.Elapsed); + + if (previouslyRenderedLine.RenderedDurationLength == durationString.Length) + { + // Duration is the same length rewrite just it. + terminal.SetCursorHorizontal(MaxColumn); + terminal.Append($"{AnsiCodes.SetCursorHorizontal(MaxColumn)}{AnsiCodes.MoveCursorBackward(durationString.Length)}{durationString}"); + currentLine.RenderedDurationLength = durationString.Length; + } + else + { + // Duration is not the same length (it is longer because time moves only forward), we need to re-render the whole line + // to avoid writing the duration over the last portion of text: my.dll (1s) -> my.d (1m 1s) + terminal.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); + AppendTestWorkerDetail(detailItem, currentLine, terminal); + } + } + else + { + // These lines are different or the line was updated. Render the whole line. + terminal.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInLine}"); + AppendTestWorkerDetail(detailItem, currentLine, terminal); + } } } else { - // From now on we have to simply WriteLine - AppendTestWorkerProgress(i, terminal); + // We are rendering more lines than we rendered in previous frame + if (item is TestProgressState progressItem) + { + var currentLine = new RenderedProgressItem(progressItem.Id, progressItem.Version); + RenderedLines.Add(currentLine); + AppendTestWorkerProgress(progressItem, currentLine, terminal); + } + + if (item is TestDetailState detailItem) + { + var currentLine = new RenderedProgressItem(detailItem.Id, detailItem.Version); + RenderedLines.Add(currentLine); + AppendTestWorkerDetail(detailItem, currentLine, terminal); + } } // This makes the progress not stick to the last line on the command line, which is @@ -228,12 +294,57 @@ public void Render(AnsiTerminalTestProgressFrame previousFrame, AnsiTerminal ter terminal.AppendLine(); } - // clear no longer used lines - if (i < previousFrame.ProgressCount) + // We rendered more lines in previous frame. Clear them. + if (previousFrame.RenderedLines != null && i < previousFrame.RenderedLines.Count) { terminal.Append($"{AnsiCodes.CSI}{AnsiCodes.EraseInDisplay}"); } } - public void Clear() => ProgressCount = 0; + private List GenerateLinesToRender(TestProgressState?[] progress) + { + var linesToRender = new List(progress.Length); + + // Note: We want to render the list of active tests, but this can easily fill up the full screen. + // As such, we should balance the number of active tests shown per project. + // We do this by distributing the remaining lines for each projects. + TestProgressState[] progressItems = progress.OfType().ToArray(); + int linesToDistribute = (int)(Height * 0.7) - 1 - progressItems.Length; + var detailItems = new IEnumerable[progressItems.Length]; + IEnumerable sortedItemsIndices = Enumerable.Range(0, progressItems.Length).OrderBy(i => progressItems[i].TestNodeResultsState?.Count ?? 0); + + foreach (int sortedItemIndex in sortedItemsIndices) + { + detailItems[sortedItemIndex] = progressItems[sortedItemIndex].TestNodeResultsState?.GetRunningTasks( + linesToDistribute / progressItems.Length) + ?? Array.Empty(); + } + + for (int progressI = 0; progressI < progressItems.Length; progressI++) + { + linesToRender.Add(progressItems[progressI]); + linesToRender.AddRange(detailItems[progressI]); + } + + return linesToRender; + } + + public void Clear() => RenderedLines?.Clear(); + + internal sealed class RenderedProgressItem + { + public RenderedProgressItem(long id, long version) + { + ProgressId = id; + ProgressVersion = version; + } + + public long ProgressId { get; } + + public long ProgressVersion { get; } + + public int RenderedHeight { get; set; } + + public int RenderedDurationLength { get; set; } + } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ErrorMessage.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ErrorMessage.cs index 541ca6ecdd..f4e41aa43e 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ErrorMessage.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ErrorMessage.cs @@ -6,4 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// /// An error message that was sent to output during the build. /// -internal record ErrorMessage(string Text) : IProgressMessage; +internal sealed record ErrorMessage(string Text) : IProgressMessage; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs index cd057bc2e9..5e0a406ab8 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/ExceptionFlattener.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; -internal class ExceptionFlattener +internal sealed class ExceptionFlattener { internal static FlatException[] Flatten(string? errorMessage, Exception? exception) { @@ -12,8 +12,6 @@ internal static FlatException[] Flatten(string? errorMessage, Exception? excepti return Array.Empty(); } - List exceptions = new(); - string? message = !RoslynString.IsNullOrWhiteSpace(errorMessage) ? errorMessage : exception?.Message; string? type = exception?.GetType().FullName; string? stackTrace = exception?.StackTrace; @@ -51,4 +49,4 @@ internal static FlatException[] Flatten(string? errorMessage, Exception? excepti } } -internal record FlatException(string? ErrorMessage, string? ErrorType, string? StackTrace); +internal sealed record FlatException(string? ErrorMessage, string? ErrorType, string? StackTrace); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/FileUtilities.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/FileUtilities.cs index 4171136311..4cee07e414 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/FileUtilities.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/FileUtilities.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal static class FileUtilities diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/HumanReadableDurationFormatter.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/HumanReadableDurationFormatter.cs index 6d07e8d56b..b28f5f0aa0 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/HumanReadableDurationFormatter.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/HumanReadableDurationFormatter.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; - namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal static class HumanReadableDurationFormatter @@ -55,8 +52,13 @@ public static void Append(ITerminal terminal, TimeSpan duration, bool wrapInPare private static string GetFormattedPart(int value, bool hasParentValue, string suffix, int paddingWitdh = 2) => $"{(hasParentValue ? " " : string.Empty)}{(hasParentValue ? value.ToString(CultureInfo.InvariantCulture).PadLeft(paddingWitdh, '0') : value.ToString(CultureInfo.InvariantCulture))}{suffix}"; - public static string Render(TimeSpan duration, bool wrapInParentheses = true, bool showMilliseconds = false) + public static string Render(TimeSpan? duration, bool wrapInParentheses = true, bool showMilliseconds = false) { + if (duration is null) + { + return string.Empty; + } + bool hasParentValue = false; var stringBuilder = new StringBuilder(); @@ -66,35 +68,35 @@ public static string Render(TimeSpan duration, bool wrapInParentheses = true, bo stringBuilder.Append('('); } - if (duration.Days > 0) + if (duration.Value.Days > 0) { - stringBuilder.Append(CultureInfo.CurrentCulture, $"{duration.Days}d"); + stringBuilder.Append(CultureInfo.CurrentCulture, $"{duration.Value.Days}d"); hasParentValue = true; } - if (duration.Hours > 0 || hasParentValue) + if (duration.Value.Hours > 0 || hasParentValue) { - stringBuilder.Append(GetFormattedPart(duration.Hours, hasParentValue, "h")); + stringBuilder.Append(GetFormattedPart(duration.Value.Hours, hasParentValue, "h")); hasParentValue = true; } - if (duration.Minutes > 0 || hasParentValue) + if (duration.Value.Minutes > 0 || hasParentValue) { - stringBuilder.Append(GetFormattedPart(duration.Minutes, hasParentValue, "m")); + stringBuilder.Append(GetFormattedPart(duration.Value.Minutes, hasParentValue, "m")); hasParentValue = true; } - if (duration.Seconds > 0 || hasParentValue || !showMilliseconds) + if (duration.Value.Seconds > 0 || hasParentValue || !showMilliseconds) { - stringBuilder.Append(GetFormattedPart(duration.Seconds, hasParentValue, "s")); + stringBuilder.Append(GetFormattedPart(duration.Value.Seconds, hasParentValue, "s")); hasParentValue = true; } if (showMilliseconds) { - if (duration.Milliseconds >= 0 || hasParentValue) + if (duration.Value.Milliseconds >= 0 || hasParentValue) { - stringBuilder.Append(GetFormattedPart(duration.Milliseconds, hasParentValue, "ms", paddingWitdh: 3)); + stringBuilder.Append(GetFormattedPart(duration.Value.Milliseconds, hasParentValue, "ms", paddingWitdh: 3)); } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/IProgressMessage.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/IProgressMessage.cs index ae77f9d53c..f8f331ab23 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/IProgressMessage.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/IProgressMessage.cs @@ -6,6 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// /// Error or warning message that was sent to screen during the test run. /// -internal interface IProgressMessage -{ -} +internal interface IProgressMessage; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NativeMethods.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NativeMethods.cs index cd4ae2de81..e887cbc622 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NativeMethods.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NativeMethods.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal static class NativeMethods @@ -30,7 +27,7 @@ internal static bool IsWindows internal static (bool AcceptAnsiColorCodes, bool OutputIsScreen, uint? OriginalConsoleMode) QueryIsScreenAndTryEnableAnsiColorCodes(StreamHandleType handleType = StreamHandleType.StdOut) { - if (System.Console.IsOutputRedirected) + if (Console.IsOutputRedirected) { // There's no ANSI terminal support if console output is redirected. return (AcceptAnsiColorCodes: false, OutputIsScreen: false, OriginalConsoleMode: null); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs index e0af85e4fc..b72fa30d2e 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Resources; namespace Microsoft.Testing.Platform.OutputDevice.Terminal; @@ -16,7 +14,6 @@ internal sealed class NonAnsiTerminal : ITerminal { private readonly IConsole _console; private readonly ConsoleColor _defaultForegroundColor; - private readonly StringBuilder _stringBuilder = new(); private bool _isBatching; public NonAnsiTerminal(IConsole console) @@ -30,52 +27,16 @@ public NonAnsiTerminal(IConsole console) public int Height => _console.IsOutputRedirected ? int.MaxValue : _console.BufferHeight; public void Append(char value) - { - if (_isBatching) - { - _stringBuilder.Append(value); - } - else - { - _console.Write(value); - } - } + => _console.Write(value); public void Append(string value) - { - if (_isBatching) - { - _stringBuilder.Append(value); - } - else - { - _console.Write(value); - } - } + => _console.Write(value); public void AppendLine() - { - if (_isBatching) - { - _stringBuilder.AppendLine(); - } - else - { - _console.WriteLine(); - } - } + => _console.WriteLine(); public void AppendLine(string value) - { - if (_isBatching) - { - _stringBuilder.AppendLine(value); - } - else - { - _console.WriteLine(value); - } - } + => _console.WriteLine(value); public void AppendLink(string path, int? lineNumber) { @@ -87,26 +48,10 @@ public void AppendLink(string path, int? lineNumber) } public void SetColor(TerminalColor color) - { - if (_isBatching) - { - _console.Write(_stringBuilder.ToString()); - _stringBuilder.Clear(); - } - - _console.SetForegroundColor(ToConsoleColor(color)); - } + => _console.SetForegroundColor(ToConsoleColor(color)); public void ResetColor() - { - if (_isBatching) - { - _console.Write(_stringBuilder.ToString()); - _stringBuilder.Clear(); - } - - _console.SetForegroundColor(_defaultForegroundColor); - } + => _console.SetForegroundColor(_defaultForegroundColor); public void ShowCursor() { @@ -118,20 +63,48 @@ public void HideCursor() // nop } + // TODO: Refactor NonAnsiTerminal and AnsiTerminal such that we don't need StartUpdate/StopUpdate. + // It's much better if we use lock C# keyword instead of manually calling Monitor.Enter/Exit + // Using lock also ensures we don't accidentally have `await`s in between that could cause Exit to be on a different thread. public void StartUpdate() { if (_isBatching) { - throw new InvalidOperationException("Console is already in batching mode."); + throw new InvalidOperationException(PlatformResources.ConsoleIsAlreadyInBatchingMode); + } + + bool lockTaken = false; + // SystemConsole.ConsoleOut is set only once in static ctor. + // So we are sure we will be doing Monitor.Exit on the same instance. + // Note that we need to lock on System.Out for batching to work correctly. + // Consider the following scenario: + // 1. We call StartUpdate + // 2. We call a Write("A") + // 3. User calls Console.Write("B") from another thread. + // 4. We call a Write("C"). + // 5. We call StopUpdate. + // The expectation is that we see either ACB, or BAC, but not ABC. + // Basically, when doing batching, we want to ensure that everything we write is + // written continuously, without anything in-between. + // One option (and we used to do it), is that we append to a StringBuilder while batching + // Then at StopUpdate, we write the whole string at once. + // This works to some extent, but we cannot get it to work when SetColor kicks in. + // Console methods will internally lock on Console.Out, so we are locking on the same thing. + // This locking is the easiest way to get coloring to work correctly while preventing + // interleaving with user's calls to Console.Write methods. + Monitor.Enter(SystemConsole.ConsoleOut, ref lockTaken); + if (!lockTaken) + { + // Can this happen? :/ + throw new InvalidOperationException(); } - _stringBuilder.Clear(); _isBatching = true; } public void StopUpdate() { - _console.Write(_stringBuilder.ToString()); + Monitor.Exit(SystemConsole.ConsoleOut); _isBatching = false; } @@ -182,7 +155,6 @@ public void RenderProgress(TestProgressState?[] progress) // Use just ascii here, so we don't put too many restrictions on fonts needing to // properly show unicode, or logs being saved in particular encoding. - string? detail = !RoslynString.IsNullOrWhiteSpace(p.Detail) ? $"- {p.Detail}" : null; Append('['); SetColor(TerminalColor.DarkGreen); Append('+'); @@ -224,10 +196,12 @@ public void RenderProgress(TestProgressState?[] progress) Append(')'); } - if (!RoslynString.IsNullOrWhiteSpace(detail)) + TestDetailState? activeTest = p.TestNodeResultsState?.GetRunningTasks(1).FirstOrDefault(); + if (!RoslynString.IsNullOrWhiteSpace(activeTest?.Text)) { Append(" - "); - Append(detail); + Append(activeTest.Text); + Append(' '); } Append(durationString); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs index afcc850879..8d9b51950d 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.cs @@ -1,14 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !NET7_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -#endif - -using System.Globalization; -using System.Text.RegularExpressions; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; @@ -30,7 +22,19 @@ internal sealed partial class TerminalTestReporter : IDisposable internal Func CreateStopwatch { get; set; } = SystemStopwatch.StartNew; - private readonly Dictionary _assemblies = new(); + internal event EventHandler OnProgressStartUpdate + { + add => _terminalWithProgress.OnProgressStartUpdate += value; + remove => _terminalWithProgress.OnProgressStartUpdate -= value; + } + + internal event EventHandler OnProgressStopUpdate + { + add => _terminalWithProgress.OnProgressStopUpdate += value; + remove => _terminalWithProgress.OnProgressStopUpdate -= value; + } + + private readonly ConcurrentDictionary _assemblies = new(); private readonly List _artifacts = new(); @@ -51,7 +55,9 @@ internal sealed partial class TerminalTestReporter : IDisposable private bool? _shouldShowPassedTests; #if NET7_0_OR_GREATER - [GeneratedRegex(@$"^ at ((?.+) in (?.+):line (?\d+)|(?.+))$", RegexOptions.ExplicitCapture, 1000)] + // Specifying no timeout, the regex is linear. And the timeout does not measure the regex only, but measures also any + // thread suspends, so the regex gets blamed incorrectly. + [GeneratedRegex(@"^ at ((?.+) in (?.+):line (?\d+)|(?.+))$", RegexOptions.ExplicitCapture)] private static partial Regex GetFrameRegex(); #else private static Regex? s_regex; @@ -75,7 +81,9 @@ private static Regex GetFrameRegex() { // Get these resources: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx #pragma warning disable RS0030 // Do not use banned APIs - MethodInfo? getResourceStringMethod = typeof(Environment).GetMethod("GetResourceString", BindingFlags.Static | BindingFlags.NonPublic, null, [typeof(string)], null); + MethodInfo? getResourceStringMethod = typeof(Environment).GetMethod( + "GetResourceString", + BindingFlags.Static | BindingFlags.NonPublic, null, [typeof(string)], null); #pragma warning restore RS0030 // Do not use banned APIs if (getResourceStringMethod is not null) { @@ -96,15 +104,19 @@ private static Regex GetFrameRegex() string inPattern = string.Format(CultureInfo.InvariantCulture, inString, "(?.+)", @"(?\d+)"); - s_regex = new Regex(@$"^ {atString} ((?.+) {inPattern}|(?.+))$", RegexOptions.Compiled | RegexOptions.ExplicitCapture, matchTimeout: TimeSpan.FromSeconds(1)); + // Specifying no timeout, the regex is linear. And the timeout does not measure the regex only, but measures also any + // thread suspends, so the regex gets blamed incorrectly. + s_regex = new Regex(@$"^ {atString} ((?.+) {inPattern}|(?.+))$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); return s_regex; } #endif + private int _counter; + /// /// Initializes a new instance of the class with custom terminal and manual refresh for testing. /// - internal TerminalTestReporter(IConsole console, TerminalTestReporterOptions options) + public TerminalTestReporter(IConsole console, TerminalTestReporterOptions options) { _options = options; @@ -158,22 +170,18 @@ public void AssemblyRunStarted(string assembly, string? targetFramework, string? private TestProgressState GetOrAddAssemblyRun(string assembly, string? targetFramework, string? architecture, string? executionId) { string key = $"{assembly}|{targetFramework}|{architecture}|{executionId}"; - if (_assemblies.TryGetValue(key, out TestProgressState? asm)) + return _assemblies.GetOrAdd(key, _ => { - return asm; - } - - IStopwatch sw = CreateStopwatch(); - var assemblyRun = new TestProgressState(assembly, targetFramework, architecture, sw); - int slotIndex = _terminalWithProgress.AddWorker(assemblyRun); - assemblyRun.SlotIndex = slotIndex; + IStopwatch sw = CreateStopwatch(); + var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), assembly, targetFramework, architecture, sw); + int slotIndex = _terminalWithProgress.AddWorker(assemblyRun); + assemblyRun.SlotIndex = slotIndex; - _assemblies.Add(key, assemblyRun); - - return assemblyRun; + return assemblyRun; + }); } - internal void TestExecutionCompleted(DateTimeOffset endTime) + public void TestExecutionCompleted(DateTimeOffset endTime) { _testExecutionEndTime = endTime; _terminalWithProgress.StopShowingProgress(); @@ -372,6 +380,7 @@ internal void TestCompleted( string? targetFramework, string? architecture, string? executionId, + string testNodeUid, string displayName, TestOutcome outcome, TimeSpan duration, @@ -388,6 +397,7 @@ internal void TestCompleted( targetFramework, architecture, executionId, + testNodeUid, displayName, outcome, duration, @@ -403,6 +413,7 @@ internal void TestCompleted( string? targetFramework, string? architecture, string? executionId, + string testNodeUid, string displayName, TestOutcome outcome, TimeSpan duration, @@ -414,6 +425,11 @@ internal void TestCompleted( { TestProgressState asm = _assemblies[$"{assembly}|{targetFramework}|{architecture}|{executionId}"]; + if (_options.ShowActiveTests) + { + asm.TestNodeResultsState?.RemoveRunningTestNode(testNodeUid); + } + switch (outcome) { case TestOutcome.Error: @@ -653,6 +669,7 @@ private static void AppendAssemblyLinkTargetFrameworkAndArchitecture(ITerminal t bool weHaveFilePathAndCodeLine = !RoslynString.IsNullOrWhiteSpace(match.Groups["code"].Value); terminal.Append(PlatformResources.StackFrameAt); terminal.Append(' '); + if (weHaveFilePathAndCodeLine) { terminal.Append(match.Groups["code"].Value); @@ -798,7 +815,7 @@ public void ArtifactAdded(bool outOfProcess, string? assembly, string? targetFra /// /// Let the user know that cancellation was triggered. /// - internal void StartCancelling() + public void StartCancelling() { _wasCancelled = true; _terminalWithProgress.WriteToTerminal(terminal => @@ -853,7 +870,7 @@ internal void WriteWarningMessage(string assembly, string? targetFramework, stri internal void WriteErrorMessage(string assembly, string? targetFramework, string? architecture, string? executionId, Exception exception) => WriteErrorMessage(assembly, targetFramework, architecture, executionId, exception.ToString(), padding: null); - internal void WriteMessage(string text, SystemConsoleColor? color = null, int? padding = null) + public void WriteMessage(string text, SystemConsoleColor? color = null, int? padding = null) { if (color != null) { @@ -976,4 +993,24 @@ private static TerminalColor ToTerminalColor(ConsoleColor consoleColor) ConsoleColor.White => TerminalColor.White, _ => TerminalColor.Default, }; + + public void TestInProgress( + string assembly, + string? targetFramework, + string? architecture, + string testNodeUid, + string displayName, + string? executionId) + { + TestProgressState asm = _assemblies[$"{assembly}|{targetFramework}|{architecture}|{executionId}"]; + + if (_options.ShowActiveTests) + { + asm.TestNodeResultsState ??= new(Interlocked.Increment(ref _counter)); + asm.TestNodeResultsState.AddRunningTestNode( + Interlocked.Increment(ref _counter), testNodeUid, displayName, CreateStopwatch()); + } + + _terminalWithProgress.UpdateWorker(asm.SlotIndex); + } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs index d2e84bc340..478453758d 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs @@ -37,6 +37,11 @@ internal sealed class TerminalTestReporterOptions /// public Func ShowProgress { get; init; } = () => true; + /// + /// Gets a value indicating whether the active tests should be visible when the progress is shown. + /// + public bool ShowActiveTests { get; init; } + /// /// Gets a value indicating whether we should use ANSI escape codes or disable them. When true the capabilities of the console are autodetected. /// diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestDetailState.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestDetailState.cs new file mode 100644 index 0000000000..ad00e3ee42 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestDetailState.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.OutputDevice.Terminal; + +internal sealed class TestDetailState +{ + private string _text; + + public TestDetailState(long id, IStopwatch? stopwatch, string text) + { + Id = id; + Stopwatch = stopwatch; + _text = text; + } + + public long Id { get; } + + public long Version { get; set; } + + public IStopwatch? Stopwatch { get; } + + public string Text + { + get => _text; + set + { + if (!_text.Equals(value, StringComparison.Ordinal)) + { + Version++; + _text = value; + } + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs new file mode 100644 index 0000000000..db574dfa97 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestNodeResultsState.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Resources; + +namespace Microsoft.Testing.Platform.OutputDevice.Terminal; + +internal sealed class TestNodeResultsState +{ + public TestNodeResultsState(long id) + { + Id = id; + _summaryDetail = new(id, stopwatch: null, text: string.Empty); + } + + public long Id { get; } + + private readonly TestDetailState _summaryDetail; + private readonly ConcurrentDictionary _testNodeProgressStates = new(); + + public int Count => _testNodeProgressStates.Count; + + public void AddRunningTestNode(int id, string uid, string name, IStopwatch stopwatch) => _testNodeProgressStates[uid] = new TestDetailState(id, stopwatch, name); + + public void RemoveRunningTestNode(string uid) => _testNodeProgressStates.TryRemove(uid, out _); + + public IEnumerable GetRunningTasks(int maxCount) + { + var sortedDetails = _testNodeProgressStates + .Select(d => d.Value) + .OrderByDescending(d => d.Stopwatch?.Elapsed ?? TimeSpan.Zero) + .ToList(); + + bool tooManyItems = sortedDetails.Count > maxCount; + + if (tooManyItems) + { + // Note: If there's too many items to display, the summary will take up one line. + // As such, we can only take maxCount - 1 items. + int itemsToTake = maxCount - 1; + _summaryDetail.Text = + itemsToTake == 0 + // Note: If itemsToTake is 0, then we only show two lines, the project summary and the number of running tests. + ? string.Format(CultureInfo.CurrentCulture, PlatformResources.ActiveTestsRunning_FullTestsCount, sortedDetails.Count) + // If itemsToTake is larger, then we show the project summary, active tests, and the number of active tests that are not shown. + : $"... {string.Format(CultureInfo.CurrentCulture, PlatformResources.ActiveTestsRunning_MoreTestsCount, sortedDetails.Count - itemsToTake)}"; + sortedDetails = sortedDetails.Take(itemsToTake).ToList(); + } + + foreach (TestDetailState? detail in sortedDetails) + { + yield return detail; + } + + if (tooManyItems) + { + yield return _summaryDetail; + } + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs index d117e2df4f..7824e12bc0 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressState.cs @@ -7,8 +7,9 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; internal sealed class TestProgressState { - public TestProgressState(string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch) + public TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch) { + Id = id; Assembly = assembly; TargetFramework = targetFramework; Architecture = architecture; @@ -38,11 +39,13 @@ public TestProgressState(string assembly, string? targetFramework, string? archi public int TotalTests { get; internal set; } - public string? Detail { get; internal set; } + public TestNodeResultsState? TestNodeResultsState { get; internal set; } public int SlotIndex { get; internal set; } - public long LastUpdate { get; internal set; } + public long Id { get; internal set; } + + public long Version { get; internal set; } public List<(string? DisplayName, string? UID)> DiscoveredTests { get; internal set; } = new(); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressStateAwareTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressStateAwareTerminal.cs index f1b1f32c62..d369d99059 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressStateAwareTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressStateAwareTerminal.cs @@ -16,13 +16,13 @@ internal sealed partial class TestProgressStateAwareTerminal : IDisposable /// /// Protects access to state shared between the logger callbacks and the rendering thread. /// - private readonly object _lock = new(); + private readonly Lock _lock = new(); private readonly ITerminal _terminal; private readonly Func _showProgress; private readonly bool _writeProgressImmediatelyAfterOutput; private readonly int _updateEvery; - private TestProgressState?[] _progressItems = Array.Empty(); + private TestProgressState?[] _progressItems = []; private bool? _showProgressCached; /// @@ -31,6 +31,10 @@ internal sealed partial class TestProgressStateAwareTerminal : IDisposable private Thread? _refresher; private long _counter; + public event EventHandler? OnProgressStartUpdate; + + public event EventHandler? OnProgressStopUpdate; + /// /// The thread proc. /// @@ -42,6 +46,7 @@ private void ThreadProc() { lock (_lock) { + OnProgressStartUpdate?.Invoke(this, EventArgs.Empty); _terminal.StartUpdate(); try { @@ -50,6 +55,7 @@ private void ThreadProc() finally { _terminal.StopUpdate(); + OnProgressStopUpdate?.Invoke(this, EventArgs.Empty); } } } @@ -122,24 +128,35 @@ internal void WriteToTerminal(Action write) { lock (_lock) { - _terminal.StartUpdate(); - _terminal.EraseProgress(); - write(_terminal); - if (_writeProgressImmediatelyAfterOutput) + try { - _terminal.RenderProgress(_progressItems); + _terminal.StartUpdate(); + _terminal.EraseProgress(); + write(_terminal); + if (_writeProgressImmediatelyAfterOutput) + { + _terminal.RenderProgress(_progressItems); + } + } + finally + { + _terminal.StopUpdate(); } - - _terminal.StopUpdate(); } } else { lock (_lock) { - _terminal.StartUpdate(); - write(_terminal); - _terminal.StopUpdate(); + try + { + _terminal.StartUpdate(); + write(_terminal); + } + finally + { + _terminal.StopUpdate(); + } } } } @@ -164,7 +181,7 @@ internal void UpdateWorker(int slotIndex) TestProgressState? progress = _progressItems[slotIndex]; if (progress != null) { - progress.LastUpdate = _counter; + progress.Version = _counter; } } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs index 64c01d44af..e8eba56edc 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestRunArtifact.cs @@ -6,4 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// /// An artifact / attachment that was reported during run. /// -internal record TestRunArtifact(bool OutOfProcess, string? Assembly, string? TargetFramework, string? Architecture, string? ExecutionId, string? TestName, string Path); +internal sealed record TestRunArtifact(bool OutOfProcess, string? Assembly, string? TargetFramework, string? Architecture, string? ExecutionId, string? TestName, string Path); diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/WarningMessage.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/WarningMessage.cs index 2cd967504a..c938898aa7 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/WarningMessage.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/WarningMessage.cs @@ -6,4 +6,4 @@ namespace Microsoft.Testing.Platform.OutputDevice.Terminal; /// /// A warning message that was sent during run. /// -internal record WarningMessage(string Text) : IProgressMessage; +internal sealed record WarningMessage(string Text) : IProgressMessage; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs index a0601b4491..aef2c5bd24 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs @@ -1,12 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Text; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.Messages; @@ -25,7 +19,7 @@ namespace Microsoft.Testing.Platform.OutputDevice; /// /// Implementation of output device that writes to terminal with progress and optionally with ANSI. /// -internal partial class TerminalOutputDevice : IHotReloadPlatformOutputDevice, +internal sealed partial class TerminalOutputDevice : IHotReloadPlatformOutputDevice, IDataConsumer, IOutputDeviceDataProducer, ITestSessionLifetimeHandler, @@ -49,6 +43,7 @@ internal partial class TerminalOutputDevice : IHotReloadPlatformOutputDevice, private readonly IFileLoggerInformation? _fileLoggerInformation; private readonly ILoggerFactory _loggerFactory; private readonly IClock _clock; + private readonly IStopPoliciesService _policiesService; private readonly string? _longArchitecture; private readonly string? _shortArchitecture; @@ -72,7 +67,8 @@ internal partial class TerminalOutputDevice : IHotReloadPlatformOutputDevice, public TerminalOutputDevice(ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, IConsole console, ITestApplicationModuleInfo testApplicationModuleInfo, ITestHostControllerInfo testHostControllerInfo, IAsyncMonitor asyncMonitor, IRuntimeFeature runtimeFeature, IEnvironment environment, IProcessHandler process, IPlatformInformation platformInformation, - ICommandLineOptions commandLineOptions, IFileLoggerInformation? fileLoggerInformation, ILoggerFactory loggerFactory, IClock clock) + ICommandLineOptions commandLineOptions, IFileLoggerInformation? fileLoggerInformation, ILoggerFactory loggerFactory, IClock clock, + IStopPoliciesService policiesService) { _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; _console = console; @@ -87,6 +83,7 @@ public TerminalOutputDevice(ITestApplicationCancellationTokenSource testApplicat _fileLoggerInformation = fileLoggerInformation; _loggerFactory = loggerFactory; _clock = clock; + _policiesService = policiesService; if (_runtimeFeature.IsDynamicCodeSupported) { @@ -110,8 +107,15 @@ public TerminalOutputDevice(ITestApplicationCancellationTokenSource testApplicat } } - public Task InitializeAsync() + public async Task InitializeAsync() { + await _policiesService.RegisterOnAbortCallbackAsync( + () => + { + _terminalTestReporter?.StartCancelling(); + return Task.CompletedTask; + }); + if (_fileLoggerInformation is not null) { _logger = _loggerFactory.CreateLogger(GetType().ToString()); @@ -147,9 +151,7 @@ public Task InitializeAsync() // func. : () => _isVSTestMode || _isListTests || _isServerMode ? false - : _testHostControllerInfo.IsCurrentProcessTestHostController == null - ? null - : !_testHostControllerInfo.IsCurrentProcessTestHostController; + : !_testHostControllerInfo.IsCurrentProcessTestHostController; // This is single exe run, don't show all the details of assemblies and their summaries. _terminalTestReporter = new TerminalTestReporter(_console, new() @@ -160,12 +162,9 @@ public Task InitializeAsync() ShowPassedTests = showPassed, MinimumExpectedTests = PlatformCommandLineProvider.GetMinimumExpectedTests(_commandLineOptions), UseAnsi = !noAnsi, + ShowActiveTests = true, ShowProgress = shouldShowProgress, }); - - _testApplicationCancellationTokenSource.CancellationToken.Register(() => _terminalTestReporter.StartCancelling()); - - return Task.CompletedTask; } private string GetShortArchitecture(string runtimeIdentifier) @@ -183,7 +182,7 @@ private string GetShortArchitecture(string runtimeIdentifier) ]; /// - public virtual string Uid { get; } = nameof(TerminalOutputDevice); + public string Uid { get; } = nameof(TerminalOutputDevice); /// public string Version { get; } = AppVersion.DefaultSemVer; @@ -195,7 +194,7 @@ private string GetShortArchitecture(string runtimeIdentifier) public string Description { get; } = "Test Platform default console service"; /// - public virtual Task IsEnabledAsync() => Task.FromResult(true); + public Task IsEnabledAsync() => Task.FromResult(true); private async Task LogDebugAsync(string message) { @@ -205,7 +204,7 @@ private async Task LogDebugAsync(string message) } } - public virtual async Task DisplayBannerAsync(string? bannerMessage) + public async Task DisplayBannerAsync(string? bannerMessage) { RoslynDebug.Assert(_terminalTestReporter is not null); @@ -367,60 +366,41 @@ public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDevice { switch (data) { - case FormattedTextOutputDeviceData formattedTextOutputDeviceData: - if (formattedTextOutputDeviceData.ForegroundColor is SystemConsoleColor color) - { - switch (color.ConsoleColor) - { - case ConsoleColor.Red: - _terminalTestReporter.WriteErrorMessage(_assemblyName, _targetFramework, _shortArchitecture, executionId: null, formattedTextOutputDeviceData.Text, formattedTextOutputDeviceData.Padding); - break; - case ConsoleColor.Yellow: - _terminalTestReporter.WriteWarningMessage(_assemblyName, _targetFramework, _shortArchitecture, executionId: null, formattedTextOutputDeviceData.Text, formattedTextOutputDeviceData.Padding); - break; - default: - _terminalTestReporter.WriteMessage(formattedTextOutputDeviceData.Text, color, formattedTextOutputDeviceData.Padding); - break; - } - } - else - { - _terminalTestReporter.WriteMessage(formattedTextOutputDeviceData.Text, padding: formattedTextOutputDeviceData.Padding); - } + case FormattedTextOutputDeviceData formattedTextData: + await LogDebugAsync(formattedTextData.Text); + _terminalTestReporter.WriteMessage(formattedTextData.Text, formattedTextData.ForegroundColor as SystemConsoleColor, formattedTextData.Padding); + break; + case TextOutputDeviceData textData: + await LogDebugAsync(textData.Text); + _terminalTestReporter.WriteMessage(textData.Text); break; - case TextOutputDeviceData textOutputDeviceData: - { - await LogDebugAsync(textOutputDeviceData.Text); - _terminalTestReporter.WriteMessage(textOutputDeviceData.Text); - break; - } + case WarningMessageOutputDeviceData warningData: + await LogDebugAsync(warningData.Message); + _terminalTestReporter.WriteWarningMessage(_assemblyName, _targetFramework, _shortArchitecture, executionId: null, warningData.Message, null); + break; - case ExceptionOutputDeviceData exceptionOutputDeviceData: - { - await LogDebugAsync(exceptionOutputDeviceData.Exception.ToString()); - _terminalTestReporter.WriteErrorMessage(_assemblyName, _targetFramework, _shortArchitecture, executionId: null, exceptionOutputDeviceData.Exception); + case ErrorMessageOutputDeviceData errorData: + await LogDebugAsync(errorData.Message); + _terminalTestReporter.WriteErrorMessage(_assemblyName, _targetFramework, _shortArchitecture, executionId: null, errorData.Message, null); + break; - break; - } + case ExceptionOutputDeviceData exceptionOutputDeviceData: + await LogDebugAsync(exceptionOutputDeviceData.Exception.ToString()); + _terminalTestReporter.WriteErrorMessage(_assemblyName, _targetFramework, _shortArchitecture, executionId: null, exceptionOutputDeviceData.Exception); + break; } } } - public async Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) + public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationToken cancellationToken) { RoslynDebug.Assert(_terminalTestReporter is not null); - if (_isServerMode) - { - return; - } - - await Task.CompletedTask; - if (cancellationToken.IsCancellationRequested) + if (_isServerMode || cancellationToken.IsCancellationRequested) { - return; + return Task.CompletedTask; } switch (value) @@ -434,7 +414,13 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella switch (testNodeStateChanged.TestNode.Properties.SingleOrDefault()) { case InProgressTestNodeStateProperty: - // do nothing. + _terminalTestReporter.TestInProgress( + _assemblyName, + _targetFramework, + _shortArchitecture, + testNodeStateChanged.TestNode.Uid.Value, + testNodeStateChanged.TestNode.DisplayName, + executionId: null); break; case ErrorTestNodeStateProperty errorState: @@ -443,6 +429,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _targetFramework, _shortArchitecture, executionId: null, + testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Error, duration, @@ -460,6 +447,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _targetFramework, _shortArchitecture, executionId: null, + testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Fail, duration, @@ -477,6 +465,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _targetFramework, _shortArchitecture, executionId: null, + testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Timeout, duration, @@ -494,6 +483,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _targetFramework, _shortArchitecture, executionId: null, + testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Canceled, duration, @@ -511,6 +501,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _targetFramework, _shortArchitecture, executionId: null, + testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, outcome: TestOutcome.Passed, duration: duration, @@ -528,6 +519,7 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _targetFramework, _shortArchitecture, executionId: null, + testNodeStateChanged.TestNode.Uid.Value, testNodeStateChanged.TestNode.DisplayName, TestOutcome.Skipped, duration, @@ -589,8 +581,20 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella _testRequestExecutionTimeInfo = testRequestExecutionTimeInfo; break; } + + return Task.CompletedTask; } public void Dispose() => _terminalTestReporter?.Dispose(); + + public async Task HandleProcessRoleAsync(TestProcessRole processRole) + { + if (processRole == TestProcessRole.TestHost) + { + await _policiesService.RegisterOnMaxFailedTestsCallbackAsync( + async (maxFailedTests, _) => await DisplayAsync( + this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests)))); + } + } } diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/TestProcessRole.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TestProcessRole.cs new file mode 100644 index 0000000000..0955e45246 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/TestProcessRole.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform; + +internal enum TestProcessRole +{ + /// + /// Indicates that the currently running process is the test host. + /// + TestHost, + + /// + /// Indicates that the currently running process is the test host controller. + /// + TestHostController, + + /// + /// Indicates that the currently running process is the test host orchestrator. + /// + TestHostOrchestrator, +} diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/WarningMessageOutputDeviceData.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/WarningMessageOutputDeviceData.cs new file mode 100644 index 0000000000..f4b9ee22cb --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/WarningMessageOutputDeviceData.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.OutputDevice; + +public sealed class WarningMessageOutputDeviceData(string message) : IOutputDeviceData +{ + public string Message { get; } = message; +} diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt index ec952b1b5f..76bbf25eb7 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Shipped.txt @@ -1,7 +1,22 @@ #nullable enable [TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IBannerMessageOwnerCapability [TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IBannerMessageOwnerCapability.GetBannerMessageAsync() -> System.Threading.Tasks.Task! +[TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IGracefulStopTestExecutionCapability +[TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.IGracefulStopTestExecutionCapability.StopTestExecutionAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! [TPEXP]Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions +[TPEXP]Microsoft.Testing.Platform.CommandLine.ICommandLineManager.AddProvider(System.Func! commandLineProviderFactory) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Deconstruct(out string! StandardError) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.get -> string! +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.init -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! original) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(string! StandardError) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Deconstruct(out string! StandardOutput) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.get -> string! +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.init -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! original) -> void +[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(string! StandardOutput) -> void [TPEXP]Microsoft.Testing.Platform.Requests.IExecuteRequestCompletionNotifier [TPEXP]Microsoft.Testing.Platform.Requests.IExecuteRequestCompletionNotifier.Complete() -> void [TPEXP]Microsoft.Testing.Platform.Services.IClientInfo @@ -12,9 +27,27 @@ [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.CommitHash.get -> string? [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.Name.get -> string! [TPEXP]Microsoft.Testing.Platform.Services.IPlatformInformation.Version.get -> string? +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(object? obj) -> bool +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.GetHashCode() -> int +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.ToString() -> string! +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(object? obj) -> bool +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.GetHashCode() -> int +[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.ToString() -> string! [TPEXP]static Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions.GetCapability(this Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> T? [TPEXP]static Microsoft.Testing.Platform.Capabilities.TestFramework.TestFrameworkCapabilitiesExtensions.HasCapability(this Microsoft.Testing.Platform.Capabilities.TestFramework.ITestFrameworkCapabilities! capabilities) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool +[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool [TPEXP]static Microsoft.Testing.Platform.Services.ServiceProviderExtensions.GetClientInfo(this System.IServiceProvider! serviceProvider) -> Microsoft.Testing.Platform.Services.IClientInfo! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.EqualityContract.get -> System.Type! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? other) -> bool +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.EqualityContract.get -> System.Type! +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? other) -> bool +[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool Microsoft.Testing.Platform.Builder.ConfigurationOptions Microsoft.Testing.Platform.Builder.ConfigurationOptions.ConfigurationOptions() -> void Microsoft.Testing.Platform.Builder.ConfigurationOptions.ConfigurationSources.get -> Microsoft.Testing.Platform.Builder.ConfigurationSourcesOptions! @@ -180,6 +213,7 @@ Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.Key.get -> string! Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.Key.init -> void Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.TestMetadataProperty(string! Key, string! Value) -> void +Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.TestMetadataProperty(string! key) -> void Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.Value.get -> string! Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.Value.init -> void Microsoft.Testing.Platform.Extensions.Messages.TestMethodIdentifierProperty @@ -239,8 +273,8 @@ Microsoft.Testing.Platform.Extensions.Messages.TimingInfo.TimingInfo(System.Date Microsoft.Testing.Platform.Extensions.Messages.TimingProperty Microsoft.Testing.Platform.Extensions.Messages.TimingProperty.GlobalTiming.get -> Microsoft.Testing.Platform.Extensions.Messages.TimingInfo Microsoft.Testing.Platform.Extensions.Messages.TimingProperty.StepTimings.get -> Microsoft.Testing.Platform.Extensions.Messages.StepTimingInfo![]! -Microsoft.Testing.Platform.Extensions.Messages.TimingProperty.TimingProperty(Microsoft.Testing.Platform.Extensions.Messages.TimingInfo globalTiming) -> void Microsoft.Testing.Platform.Extensions.Messages.TimingProperty.TimingProperty(Microsoft.Testing.Platform.Extensions.Messages.TimingInfo globalTiming, Microsoft.Testing.Platform.Extensions.Messages.StepTimingInfo![]! stepTimings) -> void +Microsoft.Testing.Platform.Extensions.Messages.TimingProperty.TimingProperty(Microsoft.Testing.Platform.Extensions.Messages.TimingInfo globalTiming) -> void Microsoft.Testing.Platform.Extensions.OutputDevice.IOutputDeviceDataProducer Microsoft.Testing.Platform.Extensions.TestFramework.CloseTestSessionContext Microsoft.Testing.Platform.Extensions.TestFramework.CloseTestSessionContext.CancellationToken.get -> System.Threading.CancellationToken @@ -330,6 +364,9 @@ Microsoft.Testing.Platform.Logging.LogLevel.Trace = 0 -> Microsoft.Testing.Platf Microsoft.Testing.Platform.Logging.LogLevel.Warning = 3 -> Microsoft.Testing.Platform.Logging.LogLevel Microsoft.Testing.Platform.Messages.IMessageBus Microsoft.Testing.Platform.Messages.IMessageBus.PublishAsync(Microsoft.Testing.Platform.Extensions.Messages.IDataProducer! dataProducer, Microsoft.Testing.Platform.Extensions.Messages.IData! data) -> System.Threading.Tasks.Task! +Microsoft.Testing.Platform.OutputDevice.ErrorMessageOutputDeviceData +Microsoft.Testing.Platform.OutputDevice.ErrorMessageOutputDeviceData.ErrorMessageOutputDeviceData(string! message) -> void +Microsoft.Testing.Platform.OutputDevice.ErrorMessageOutputDeviceData.Message.get -> string! Microsoft.Testing.Platform.OutputDevice.ExceptionOutputDeviceData Microsoft.Testing.Platform.OutputDevice.ExceptionOutputDeviceData.Exception.get -> System.Exception! Microsoft.Testing.Platform.OutputDevice.ExceptionOutputDeviceData.ExceptionOutputDeviceData(System.Exception! exception) -> void @@ -352,15 +389,18 @@ Microsoft.Testing.Platform.OutputDevice.SystemConsoleColor.SystemConsoleColor() Microsoft.Testing.Platform.OutputDevice.TextOutputDeviceData Microsoft.Testing.Platform.OutputDevice.TextOutputDeviceData.Text.get -> string! Microsoft.Testing.Platform.OutputDevice.TextOutputDeviceData.TextOutputDeviceData(string! text) -> void +Microsoft.Testing.Platform.OutputDevice.WarningMessageOutputDeviceData +Microsoft.Testing.Platform.OutputDevice.WarningMessageOutputDeviceData.Message.get -> string! +Microsoft.Testing.Platform.OutputDevice.WarningMessageOutputDeviceData.WarningMessageOutputDeviceData(string! message) -> void Microsoft.Testing.Platform.Requests.DiscoverTestExecutionRequest -Microsoft.Testing.Platform.Requests.DiscoverTestExecutionRequest.DiscoverTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session) -> void Microsoft.Testing.Platform.Requests.DiscoverTestExecutionRequest.DiscoverTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session, Microsoft.Testing.Platform.Requests.ITestExecutionFilter! executionFilter) -> void +Microsoft.Testing.Platform.Requests.DiscoverTestExecutionRequest.DiscoverTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session) -> void Microsoft.Testing.Platform.Requests.IRequest Microsoft.Testing.Platform.Requests.IRequest.Session.get -> Microsoft.Testing.Platform.TestHost.TestSessionContext! Microsoft.Testing.Platform.Requests.ITestExecutionFilter Microsoft.Testing.Platform.Requests.RunTestExecutionRequest -Microsoft.Testing.Platform.Requests.RunTestExecutionRequest.RunTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session) -> void Microsoft.Testing.Platform.Requests.RunTestExecutionRequest.RunTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session, Microsoft.Testing.Platform.Requests.ITestExecutionFilter! executionFilter) -> void +Microsoft.Testing.Platform.Requests.RunTestExecutionRequest.RunTestExecutionRequest(Microsoft.Testing.Platform.TestHost.TestSessionContext! session) -> void Microsoft.Testing.Platform.Requests.TestExecutionRequest Microsoft.Testing.Platform.Requests.TestExecutionRequest.Filter.get -> Microsoft.Testing.Platform.Requests.ITestExecutionFilter! Microsoft.Testing.Platform.Requests.TestExecutionRequest.Session.get -> Microsoft.Testing.Platform.TestHost.TestSessionContext! @@ -430,11 +470,11 @@ static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogCritical(this Mic static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogCriticalAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> System.Threading.Tasks.Task! static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogDebug(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> void static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogDebugAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> System.Threading.Tasks.Task! -static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogError(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> void static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogError(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message, System.Exception! ex) -> void +static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogError(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> void static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogError(this Microsoft.Testing.Platform.Logging.ILogger! logger, System.Exception! ex) -> void -static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogErrorAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> System.Threading.Tasks.Task! static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogErrorAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message, System.Exception! ex) -> System.Threading.Tasks.Task! +static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogErrorAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> System.Threading.Tasks.Task! static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogErrorAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, System.Exception! ex) -> System.Threading.Tasks.Task! static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogInformation(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> void static Microsoft.Testing.Platform.Logging.LoggingExtensions.LogInformationAsync(this Microsoft.Testing.Platform.Logging.ILogger! logger, string! message) -> System.Threading.Tasks.Task! diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt index 96d3604bb5..7dc5c58110 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt @@ -1,32 +1 @@ #nullable enable -Microsoft.Testing.Platform.Extensions.Messages.TestMetadataProperty.TestMetadataProperty(string! key) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.get -> string! -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutput.init -> void -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.EqualityContract.get -> System.Type! -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.ToString() -> string! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? right) -> bool -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.GetHashCode() -> int -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(object? obj) -> bool -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty? other) -> bool -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty! original) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.Deconstruct(out string! StandardOutput) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardOutputProperty.StandardOutputProperty(string! StandardOutput) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.get -> string! -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardError.init -> void -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.EqualityContract.get -> System.Type! -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.ToString() -> string! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator !=(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool -[TPEXP]static Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.operator ==(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? left, Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? right) -> bool -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.GetHashCode() -> int -[TPEXP]override Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(object? obj) -> bool -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! -[TPEXP]virtual Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty? other) -> bool -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty! original) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.Deconstruct(out string! StandardError) -> void -[TPEXP]Microsoft.Testing.Platform.Extensions.Messages.StandardErrorProperty.StandardErrorProperty(string! StandardError) -> void diff --git a/src/Platform/Microsoft.Testing.Platform/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/Platform/Microsoft.Testing.Platform/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt index 2a894256ed..0b25149d85 100644 --- a/src/Platform/Microsoft.Testing.Platform/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/Platform/Microsoft.Testing.Platform/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -1,4 +1,10 @@ #nullable enable +~override Microsoft.Testing.Platform.Extensions.Messages.LinePosition.Equals(object obj) -> bool +~override Microsoft.Testing.Platform.Extensions.Messages.LinePosition.ToString() -> string +~override Microsoft.Testing.Platform.Extensions.Messages.LinePositionSpan.Equals(object obj) -> bool +~override Microsoft.Testing.Platform.Extensions.Messages.LinePositionSpan.ToString() -> string +~override Microsoft.Testing.Platform.Extensions.Messages.TimingInfo.Equals(object obj) -> bool +~override Microsoft.Testing.Platform.Extensions.Messages.TimingInfo.ToString() -> string abstract Microsoft.Testing.Platform.Extensions.Messages.FileLocationProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.FileLocationProperty! abstract Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty.$() -> Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty! Microsoft.Testing.Platform.Extensions.Messages.CancelledTestNodeStateProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.CancelledTestNodeStateProperty? other) -> bool @@ -155,9 +161,3 @@ virtual Microsoft.Testing.Platform.Extensions.Messages.KeyValuePairStringPropert virtual Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty.EqualityContract.get -> System.Type! virtual Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty.Equals(Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty? other) -> bool virtual Microsoft.Testing.Platform.Extensions.Messages.TestNodeStateProperty.PrintMembers(System.Text.StringBuilder! builder) -> bool -~override Microsoft.Testing.Platform.Extensions.Messages.LinePosition.Equals(object obj) -> bool -~override Microsoft.Testing.Platform.Extensions.Messages.LinePosition.ToString() -> string -~override Microsoft.Testing.Platform.Extensions.Messages.LinePositionSpan.Equals(object obj) -> bool -~override Microsoft.Testing.Platform.Extensions.Messages.LinePositionSpan.ToString() -> string -~override Microsoft.Testing.Platform.Extensions.Messages.TimingInfo.Equals(object obj) -> bool -~override Microsoft.Testing.Platform.Extensions.Messages.TimingInfo.ToString() -> string diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/IExecuteRequestCompletionNotifier.cs b/src/Platform/Microsoft.Testing.Platform/Requests/IExecuteRequestCompletionNotifier.cs index a60ec25302..09a2df1541 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/IExecuteRequestCompletionNotifier.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/IExecuteRequestCompletionNotifier.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Requests; [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs index c8204a2493..3fbc1c9e55 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/NopFilter.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Requests; [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs index b43a13b355..f44eb1e82b 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TestHostTestFrameworkInvoker.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.Testing.Platform.Capabilities; using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.Extensions.Messages; @@ -18,6 +16,7 @@ namespace Microsoft.Testing.Platform.Requests; +[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "HotReload needs to inherit and override ExecuteRequestAsync")] internal class TestHostTestFrameworkInvoker(IServiceProvider serviceProvider) : ITestFrameworkInvoker, IOutputDeviceDataProducer, IDataProducer { protected IServiceProvider ServiceProvider { get; } = serviceProvider; @@ -79,7 +78,7 @@ private async Task HandleTestSessionResultAsync(bool isSuccess, string? warningM if (warningMessage is not null) { IOutputDevice outputDisplay = ServiceProvider.GetOutputDevice(); - await outputDisplay.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateYellowConsoleColorText(warningMessage)); + await outputDisplay.DisplayAsync(this, new WarningMessageOutputDeviceData(warningMessage)); } if (!isSuccess) diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterOperator.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterOperator.cs index 9743f49f87..d518d7548f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterOperator.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/FilterOperator.cs @@ -19,9 +19,4 @@ internal enum FilterOperator /// Combine the following expressions with a logical OR. /// Or, - - /// - /// Filter the following expression by the given property. - /// - Equals, } diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs index 051583489c..b9c7da2819 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/OperatorKind.cs @@ -41,6 +41,11 @@ internal enum OperatorKind /// FilterEquals, + /// + /// Filter not equals operator. + /// + FilterNotEquals, + /// /// Operator used for combining multiple filters with a logical OR. /// diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs index c7ed1b6bfc..9a81e683df 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/TreeNodeFilter.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Resources; @@ -43,6 +38,7 @@ internal TreeNodeFilter(string filter) /// FILTER_EXPR = /// '(' FILTER_EXPR ')' /// | TOKEN '=' TOKEN + /// | TOKEN '!=' TOKEN /// | FILTER_EXPR OP FILTER_EXPR /// | TOKEN /// OP = '&' | '|' @@ -215,6 +211,13 @@ private static List ParseFilter(string filter) isPropAllowed = false; break; + case "!=": + operatorStack.Push(OperatorKind.FilterNotEquals); + + isOperatorAllowed = false; + isPropAllowed = false; + break; + default: expressionStack.Push(new ValueExpression(token)); @@ -316,7 +319,6 @@ private static void ProcessStackOperator(OperatorKind op, Stack FilterOperator.And, OperatorKind.Or => FilterOperator.Or, - OperatorKind.FilterEquals => FilterOperator.Equals, _ => throw ApplicationStateGuard.Unreachable(), }; @@ -324,6 +326,7 @@ private static void ProcessStackOperator(OperatorKind op, Stack TokenizeFilter(string filter) break; + case '!': + if (i + 1 < filter.Length && filter[i + 1] == '=') + { + if (lastStringTokenBuilder.Length > 0) + { + yield return lastStringTokenBuilder.ToString(); + lastStringTokenBuilder.Clear(); + } + + yield return "!="; + i++; + } + else + { + goto default; + } + + break; + default: lastStringTokenBuilder.Append(Regex.Escape(filter[i].ToString())); break; @@ -446,7 +474,13 @@ public bool MatchesFilter(string testNodeFullPath, PropertyBag filterablePropert if (currentFragmentIndex >= _filters.Count) { // Note: The regex for ** is .*.*, so we match against such a value expression. - return currentFragmentIndex > 0 && _filters.Last() is ValueExpression { Value: ".*.*" }; + FilterExpression lastFilter = _filters.Last(); + if (lastFilter is ValueAndPropertyExpression valueAndPropertyExpression) + { + lastFilter = valueAndPropertyExpression.Value; + } + + return currentFragmentIndex > 0 && lastFilter is ValueExpression { Value: ".*.*" }; } if (!MatchFilterPattern( diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueAndPropertyExpression.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueAndPropertyExpression.cs index f2930a0434..0c2403c349 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueAndPropertyExpression.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueAndPropertyExpression.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Requests; /// diff --git a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueExpression.cs b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueExpression.cs index bc2c713522..9cc04aec54 100644 --- a/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueExpression.cs +++ b/src/Platform/Microsoft.Testing.Platform/Requests/TreeNodeFilter/ValueExpression.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.Requests; // An expression representing a single value. diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx index 51d20861e7..4ca08c4751 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx +++ b/src/Platform/Microsoft.Testing.Platform/Resources/PlatformResources.resx @@ -665,4 +665,55 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is Specifies the minimum number of tests that are expected to run. + + Specifies a testconfig.json file. + + + The configuration file '{0}' specified with '--config-file' could not be found. + + + and {0} more + + + {0} tests running + + + Starting test session. + + + Starting test session. The log file path is '{0}'. + + + Finished test session. + + + Server mode test host + + + The 'ITestHost' implementation used when running server mode. + + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + {0} is the exception that was unhandled/unobserved + + + Exception during the cancellation of request id '{0}' + {0} is the request id + + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + {0} is the number of max failed tests. + + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + \ No newline at end of file diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf index 59c6383447..f0e1018a73 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.cs.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Aktuální testovací architektura neimplementuje rozhraní IGracefulStopTestExecutionCapability, které je vyžadováno pro funkci --maximum-failed-tests. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Rozšíření, které podporuje --maximum-failed-tests Když se dosáhne prahové hodnoty pro dané chyby, testovací bÄ›h se pÅ™eruší. + + Aborted PÅ™eruÅ¡eno @@ -119,12 +129,12 @@ Failed to read response file '{0}'. {1}. - Failed to read response file '{0}'. {1}. + ÄŒtení souboru odpovÄ›dí {0} selhalo. {1}. {1} is the exception The response file '{0}' was not found - The response file '{0}' was not found + Soubor odpovÄ›dí {0} nebyl nalezen. @@ -157,6 +167,11 @@ Už je zaregistrovaná stejná instance CompositeExtensonFactory. + + The configuration file '{0}' specified with '--config-file' could not be found. + Nebyl nalezen konfiguraÄní soubor {0} zadaný pomocí parametru --config-file. + + Could not find the default json configuration Nepovedlo se najít výchozí konfiguraci json. @@ -214,6 +229,11 @@ ZprostÅ™edkovatel {0} (UID: {1}) selhal s chybou: {2}. + + Exception during the cancellation of request id '{0}' + Výjimka pÅ™i ruÅ¡ení ID žádosti '{0}' + {0} is the request id + Exit code UkonÄovací kód @@ -266,6 +286,11 @@ selhalo s {0} upozornÄ›ním(i). + + Finished test session. + Testovací relace byla dokonÄena. + + For test Pro testování @@ -356,6 +381,11 @@ Rozhraní ILoggerFactory jeÅ¡tÄ› nebylo sestaveno. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + Možnost --maximum-failed-tests musí být kladné celé Äíslo. Hodnota '{0}' není platná. + + The message bus has not been built yet or is no more usable at this stage. SbÄ›rnice zpráv jeÅ¡tÄ› nebyla sestavena nebo už není v této fázi použitelná. @@ -371,6 +401,16 @@ PÅ™i použití protokolu jsonRpc byl oÄekáván parametr --client-port. + + and {0} more + a jeÅ¡tÄ› {0} + + + + {0} tests running + {0} spuÅ¡tÄ›né testy + + No serializer registered with ID '{0}' Není zaregistrovaný žádný serializátor s ID {0}. @@ -416,6 +456,11 @@ Zadejte port klienta. + + Specifies a testconfig.json file. + UrÄuje soubor testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ Dostupné hodnoty jsou Trace, Debug, Information, Warning, Error a Critical.Umožňuje zobrazit informace o testovací aplikaci .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + UrÄuje maximální poÄet neúspěšných testů, které pÅ™i pÅ™ekroÄení pÅ™eruší testovací bÄ›h. + + '--list-tests' and '--minimum-expected-tests' are incompatible options --list-tests a --minimum-expected-tests jsou nekompatibilní možnosti. @@ -512,7 +562,7 @@ Dostupné hodnoty jsou Trace, Debug, Information, Warning, Error a Critical. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + UrÄuje minimální poÄet testů, jejichž spuÅ¡tÄ›ní se oÄekává. @@ -593,6 +643,11 @@ Může mít jenom jeden argument jako Å™etÄ›zec ve formátu <value>[h|m|s] Proces mÄ›l být ukonÄen pÅ™ed tím, než jsme mohli urÄit tuto hodnotu. + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + Testovací relace se pÅ™eruÅ¡uje, protože se dosáhlo chyb ('{0}') zadaných možností --maximum-failed-tests. + {0} is the number of max failed tests. + Retry failed after {0} times Opakování se po {0} pokusech nezdaÅ™ilo. @@ -623,6 +678,16 @@ Může mít jenom jeden argument jako Å™etÄ›zec ve formátu <value>[h|m|s] Objekt pro vytváření filtru provádÄ›ní testů serveru + + The 'ITestHost' implementation used when running server mode. + Implementace ITestHost použitá pÅ™i spuÅ¡tÄ›ní režimu serveru + + + + Server mode test host + Testovací hostitel režimu serveru + + Cannot find service of type '{0}' Nelze najít službu typu {0}. @@ -678,6 +743,16 @@ Může mít jenom jeden argument jako Å™etÄ›zec ve formátu <value>[h|m|s] SpouÅ¡tí se server. Naslouchání na portu {0} + + Starting test session. + SpouÅ¡tí se testovací relace. + + + + Starting test session. The log file path is '{0}'. + SpouÅ¡tí se testovací relace. Cesta k souboru protokolu je '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Objekt pro vytváření ITestExecutionFilterFactory je už nastavený. @@ -887,6 +962,11 @@ Platné hodnoty jsou Normal a Detailed. Výchozí hodnota je Normal. KomunikaÄní protokol {0} není podporován. + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] NeoÅ¡etÅ™ená výjimka: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Toto umístÄ›ní programu se považuje za nedostupné. File={0}, Line={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf index 93010da43a..fc9812f0da 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.de.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Das aktuelle Testframework implementiert nicht "IGracefulStopTestExecutionCapability", das für das Feature "--maximum-failed-tests" erforderlich ist. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Erweiterung zur Unterstützung von "--maximum-failed-tests". Wenn ein angegebener Schwellenwert für Fehler erreicht ist, wird der Testlauf abgebrochen. + + Aborted Abgebrochen @@ -157,6 +167,11 @@ Die gleiche Instanz von "CompositeExtensonFactory" ist bereits registriert + + The configuration file '{0}' specified with '--config-file' could not be found. + Die mit „--config-file“ angegebene Konfigurationsdatei „{0}“ wurde nicht gefunden. + + Could not find the default json configuration Die standardmäßige JSON-Konfiguration wurde nicht gefunden. @@ -214,6 +229,11 @@ Anbieter "{0}" (UID: {1}) ist mit folgendem Fehler fehlgeschlagen: {2} + + Exception during the cancellation of request id '{0}' + Ausnahme beim Abbrechen der Anforderungs-ID '{0}' + {0} is the request id + Exit code Exitcode @@ -266,6 +286,11 @@ fehlerhaft mit {0} Warnung(en) + + Finished test session. + Die Testsitzung wurde beendet. + + For test Für Test @@ -356,6 +381,11 @@ Die ILoggerFactory wurde noch nicht erstellt. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + Die Option "--maximum-failed-tests" muss eine positive ganze Zahl sein. Der Wert '{0}' ist ungültig. + + The message bus has not been built yet or is no more usable at this stage. Der Nachrichtenbus wurde noch nicht erstellt oder kann zu diesem Zeitpunkt nicht mehr verwendet werden. @@ -371,6 +401,16 @@ "--client-port" wird erwartet, wenn das jsonRpc-Protokoll verwendet wird. + + and {0} more + und {0} weitere + + + + {0} tests running + {0} ausgeführten Tests + + No serializer registered with ID '{0}' Es ist kein Serialisierungsmodul mit der ID "{0}" registriert @@ -416,6 +456,11 @@ Geben Sie den Port des Clients an. + + Specifies a testconfig.json file. + Gibt eine testconfig.json-Datei an. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ Die verfügbaren Werte sind "Trace", "Debug", "Information", "Warning", "Error" Zeigen Sie .NET-Testanwendungsinformationen an. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Gibt die maximale Anzahl von Testfehlern an, bei deren Überschreiten der Testlauf abgebrochen wird. + + '--list-tests' and '--minimum-expected-tests' are incompatible options "--list-tests" und "--minimum-expected-tests" sind inkompatible Optionen. @@ -512,7 +562,7 @@ Die verfügbaren Werte sind "Trace", "Debug", "Information", "Warning", "Error" Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Gibt die Mindestanzahl von Tests an, die ausgeführt werden sollen. @@ -593,6 +643,11 @@ Nimmt ein Argument als Zeichenfolge im Format <value>[h|m|s], wobei "value Der Prozess hätte beendet werden müssen, bevor dieser Wert ermittelt werden kann + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + Die Testsitzung wird aufgrund von Erreichensfehlern ('{0}') abgebrochen, die durch die Option "--maximum-failed-tests" angegeben wurden. + {0} is the number of max failed tests. + Retry failed after {0} times Wiederholungsfehler nach {0} Mal @@ -623,6 +678,16 @@ Nimmt ein Argument als Zeichenfolge im Format <value>[h|m|s], wobei "value Ausführungsfilterfactory des Servertests + + The 'ITestHost' implementation used when running server mode. + Die implementierung "ITestHost", die beim Ausführen des Servermodus verwendet wird. + + + + Server mode test host + Testhost für Servermodus + + Cannot find service of type '{0}' Der Dienst vom Typ "{0}" wurde nicht gefunden. @@ -678,6 +743,16 @@ Nimmt ein Argument als Zeichenfolge im Format <value>[h|m|s], wobei "value Server wird gestartet. An Port "{0}" lauschen + + Starting test session. + Die Testsitzung wird gestartet. + + + + Starting test session. The log file path is '{0}'. + Die Testsitzung wird gestartet. Der Protokolldateipfad ist '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Eine "ITestExecutionFilterFactory"-Factory ist bereits festgelegt @@ -887,6 +962,11 @@ Gültige Werte sind „Normal“, „Detailed“. Der Standardwert ist „Normal Das Kommunikationsprotokoll "{0}" wird nicht unterstützt + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Ausnahmefehler: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Dieser Programmspeicherort wird als nicht erreichbar betrachtet. File='{0}' Line={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf index 91955cb0c2..5b85dc2400 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.es.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + El marco de pruebas actual no implementa "IGracefulStopTestExecutionCapability", que es necesario para la característica "--maximum-failed-tests". + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Extensión usada para admitir "--maximum-failed-tests". Cuando se alcance un umbral de errores determinado, se anulará la serie de pruebas. + + Aborted Anulado @@ -157,6 +167,11 @@ La misma instancia de "CompositeExtensonFactory" ya está registrada + + The configuration file '{0}' specified with '--config-file' could not be found. + No se ha encontrado el archivo de configuración "{0}" especificado con "--config-file". + + Could not find the default json configuration No se pudo encontrar la configuración json predeterminada @@ -214,6 +229,11 @@ Error del proveedor "{0}" (UID: {1}) con el error: {2} + + Exception during the cancellation of request id '{0}' + Excepción durante la cancelación del id. de solicitud '{0}' + {0} is the request id + Exit code Código de salida @@ -266,6 +286,11 @@ error con {0} advertencias + + Finished test session. + Finalizó la sesión de prueba. + + For test Para prueba @@ -356,6 +381,11 @@ ILoggerFactory aún no se ha compilado. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + La opción '--maximum-failed-tests' debe ser un entero positivo. El valor '{0}' no es válido. + + The message bus has not been built yet or is no more usable at this stage. El bus de mensajes aún no se ha compilado o ya no se puede usar en esta fase. @@ -371,6 +401,16 @@ Se esperaba --client-port cuando se usa el protocolo jsonRpc. + + and {0} more + y {0} más + + + + {0} tests running + {0} pruebas en ejecución + + No serializer registered with ID '{0}' No hay ningún serializador registrado con el id. '{0}' @@ -416,6 +456,11 @@ Especifique el puerto del cliente. + + Specifies a testconfig.json file. + Especifica un archivo testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ Los valores disponibles son 'Seguimiento', 'Depurar', 'Información', 'Advertenc Muestre la información de la aplicación de prueba de .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Especifica un número máximo de errores de prueba que, si se superan, anularán la serie de pruebas. + + '--list-tests' and '--minimum-expected-tests' are incompatible options “--list-tests†y “--minimum-expected-tests†son opciones incompatibles @@ -512,7 +562,7 @@ Los valores disponibles son 'Seguimiento', 'Depurar', 'Información', 'Advertenc Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Especifica el número mínimo de pruebas que se espera que se ejecuten. @@ -593,6 +643,11 @@ Toma un argumento como cadena con el formato <value>[h|m|s] donde 'value' El proceso debería haberse terminado para poder determinar este valor + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + La sesión de prueba se está anulando debido a errores ('{0}') especificados por la opción "--maximum-failed-tests". + {0} is the number of max failed tests. + Retry failed after {0} times Error al reintentar después de {0} veces @@ -623,6 +678,16 @@ Toma un argumento como cadena con el formato <value>[h|m|s] donde 'value' Fábrica de filtros de ejecución de pruebas de servidor + + The 'ITestHost' implementation used when running server mode. + Implementación de 'ITestHost' usada al ejecutar el modo de servidor. + + + + Server mode test host + Host de prueba en modo servidor + + Cannot find service of type '{0}' No se encuentra el servicio de tipo '{0}' @@ -678,6 +743,16 @@ Toma un argumento como cadena con el formato <value>[h|m|s] donde 'value' Iniciando el servidor. Escuchando en el puerto '{0}' + + Starting test session. + Iniciando sesión de prueba. + + + + Starting test session. The log file path is '{0}'. + Iniciando sesión de prueba. La ruta de acceso del archivo de registro es '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Ya se ha establecido una fábrica "ITestExecutionFilterFactory" @@ -887,6 +962,11 @@ Los valores válidos son 'Normal', 'Detallado'. El valor predeterminado es 'Norm No se admite el protocolo de comunicación '{0}' + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] excepción no controlada: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Se considera que esta ubicación del programa es inaccesible. Archivo=''{0}'' Línea={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf index a52c16c4c7..6f1b8156b4 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.fr.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Le framework de tests actuel n’implémente pas 'IGracefulStopTestExecutionCapability', qui est requis pour la fonctionnalité '--maximum-failed-tests'. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Extension utilisée pour prendre en charge '--maximum-failed-tests'. Quand un seuil d’échecs donné est atteint, la série de tests est abandonnée. + + Aborted Abandonné @@ -157,6 +167,11 @@ La même instance de « CompositeExtensonFactory » est déjà inscrite + + The configuration file '{0}' specified with '--config-file' could not be found. + Nous n’avons pas pu trouver le fichier de configuration « {0} » spécifié avec « --config-file ». + + Could not find the default json configuration Configuration JSON par défaut introuvable @@ -214,6 +229,11 @@ Désolé, échec de l’« {0} » du fournisseur (UID : {1}) avec l’erreur : {2} + + Exception during the cancellation of request id '{0}' + Exception lors de l’annulation de l’ID de demande '{0}' + {0} is the request id + Exit code Code de sortie @@ -266,6 +286,11 @@ a échoué avec {0} avertissement(s) + + Finished test session. + Session de test terminée. + + For test Pour le test @@ -356,6 +381,11 @@ ILoggerFactory n’a pas encore été généré. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + L’option '--maximum-failed-tests' doit être un entier positif. La valeur '{0}' n’est pas valide. + + The message bus has not been built yet or is no more usable at this stage. Le bus de messages n’a pas encore été généré ou n’est plus utilisable à ce stade. @@ -371,6 +401,16 @@ Attendu --client-port attendu lorsque le protocole jsonRpc est utilisé. + + and {0} more + et {0} de plus + + + + {0} tests running + {0} tests en cours d’exécution + + No serializer registered with ID '{0}' Aucun sérialiseur inscrit avec l’ID « {0} » @@ -416,6 +456,11 @@ Spécifier le port du client. + + Specifies a testconfig.json file. + Spécifie un fichier testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ Les valeurs disponibles sont « Trace », « Debug », « Information », Afficher les informations de l’application de test .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Spécifie le nombre maximal d’échecs de tests qui, lorsqu’ils sont dépassés, abandonnent la série de tests. + + '--list-tests' and '--minimum-expected-tests' are incompatible options « --list-tests » et « --minimum-expected-tests » sont des options incompatibles @@ -512,7 +562,7 @@ Les valeurs disponibles sont « Trace », « Debug », « Information », Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Spécifie le nombre minimal de tests censés s’exécuter. @@ -593,6 +643,11 @@ Prend un argument sous forme de chaîne au format <value>[h|m|s] où « v Le processus aurait dû s’arrêter avant que nous puissions déterminer cette valeur + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + La session de test est en cours d’abandon en raison d’échecs ('{0}') spécifiés par l’option '--maximum-failed-tests'. + {0} is the number of max failed tests. + Retry failed after {0} times Échec de la nouvelle tentative après {0} fois @@ -623,6 +678,16 @@ Prend un argument sous forme de chaîne au format <value>[h|m|s] où « v Fabrique de filtres d’exécution de tests du serveur + + The 'ITestHost' implementation used when running server mode. + Implémentation 'ITestHost' utilisée lors de l’exécution du mode serveur. + + + + Server mode test host + Hôte de test du mode serveur + + Cannot find service of type '{0}' Service de type « {0} » introuvable @@ -678,6 +743,16 @@ Prend un argument sous forme de chaîne au format <value>[h|m|s] où « v Démarrage du serveur. Écoute sur le port « {0} » + + Starting test session. + Démarrage de la session de test. + + + + Starting test session. The log file path is '{0}'. + Démarrage de la session de test. Le chemin d’accès au fichier journal est '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Désolé, une fabrique « ITestExecutionFilterFactory » est déjà définie @@ -887,6 +962,11 @@ Les valeurs valides sont « Normal » et « Détaillé ». La valeur par dé Le protocole de communication « {0} » n’est pas pris en charge + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] exception non prise en charge : {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Cet emplacement du programme est considéré comme inaccessible. File=« {0} », Ligne={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf index ffc131d874..948448d251 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.it.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Il framework di test corrente non implementa 'IGracefulStopTestExecutionCapability', necessario per la funzionalità '--maximum-failed-tests'. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Estensione usata per supportare '--maximum-failed-tests'. Quando viene raggiunta una soglia di errori specificata, l'esecuzione dei test verrà interrotta. + + Aborted Operazione interrotta @@ -157,6 +167,11 @@ La stessa istanza di 'CompositeExtensonFactory' è già registrata + + The configuration file '{0}' specified with '--config-file' could not be found. + Impossibile trovare il file di configurazione '{0}' specificato con '--config-file'. + + Could not find the default json configuration Non è stato possibile trovare la configurazione JSON predefinita @@ -214,6 +229,11 @@ Provider '{0}' (UID: {1}) non riuscito con errore: {2} + + Exception during the cancellation of request id '{0}' + Eccezione durante l'annullamento dell'ID richiesta '{0}' + {0} is the request id + Exit code Codice di uscita @@ -266,6 +286,11 @@ non riuscito con {0} avvisi + + Finished test session. + Sessione di test completata. + + For test Per test @@ -356,6 +381,11 @@ ILoggerFactory non è stato ancora compilato. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + L'opzione '--maximum-failed-tests' deve essere un numero intero positivo. Il valore '{0}' non è valido. + + The message bus has not been built yet or is no more usable at this stage. Il bus di messaggi non è stato ancora compilato o non è più utilizzabile in questa fase. @@ -371,6 +401,16 @@ Previsto --client-port quando viene usato il protocollo jsonRpc. + + and {0} more + e altri {0} + + + + {0} tests running + {0} test in esecuzione + + No serializer registered with ID '{0}' Nessun serializzatore registrato con ID '{0}' @@ -416,6 +456,11 @@ Specifica la porta del client. + + Specifies a testconfig.json file. + Specifica un file testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ I valori disponibili sono 'Trace', 'Debug', 'Information', 'Warning', 'Error' e Visualizza le informazioni sull'applicazione di test .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Specifica un numero massimo di errori di test che, se superati, interromperanno l'esecuzione dei test. + + '--list-tests' and '--minimum-expected-tests' are incompatible options '--list-tests' e '--minimum-expected-tests' sono opzioni incompatibili @@ -512,7 +562,7 @@ I valori disponibili sono 'Trace', 'Debug', 'Information', 'Warning', 'Error' e Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Specifica il numero minimo dei test che si prevede saranno eseguiti. @@ -593,6 +643,11 @@ Acquisisce un argomento come stringa nel formato <value>[h|m|s] dove 'valu Il processo dovrebbe essere terminato prima di poter determinare questo valore + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + La sessione di test verrà interrotta perché sono stati individuati errori ('{0}') specificati dall'opzione '--maximum-failed-tests'. + {0} is the number of max failed tests. + Retry failed after {0} times Tentativi non riusciti dopo {0} tentativi @@ -623,6 +678,16 @@ Acquisisce un argomento come stringa nel formato <value>[h|m|s] dove 'valu Factory del filtro per l'esecuzione dei test del server + + The 'ITestHost' implementation used when running server mode. + Implementazione di 'ITestHost' usata durante l'esecuzione della modalità server. + + + + Server mode test host + Host di test in modalità server + + Cannot find service of type '{0}' Impossibile trovare il servizio di tipo '{0}' @@ -678,6 +743,16 @@ Acquisisce un argomento come stringa nel formato <value>[h|m|s] dove 'valu Avvio del server. In ascolto sulla porta '{0}' + + Starting test session. + Avvio della sessione di test. + + + + Starting test session. The log file path is '{0}'. + Avvio della sessione di test. Il percorso del file di log è '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set È già impostata una factory 'ITestExecutionFilterFactory' @@ -887,6 +962,11 @@ I valori validi sono 'Normal', 'Detailed'. L'impostazione predefinita è 'Normal Il protocollo di comunicazione '{0}' non è supportato + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] eccezione non gestita: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} La posizione del programma è ritenuta non raggiungibile. File='{0}', Riga={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf index 93c55f9f9e..bc90b57f6c 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ja.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + ç¾åœ¨ã®ãƒ†ã‚¹ãƒˆ フレームワークã¯ã€'--maximum-failed-tests' 機能ã«å¿…è¦ãª 'IGracefulStopTestExecutionCapability' を実装ã—ã¦ã„ã¾ã›ã‚“。 + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + '--maximum-failed-tests' をサãƒãƒ¼ãƒˆã™ã‚‹ãŸã‚ã«ä½¿ç”¨ã•れる拡張機能。指定ã•れãŸã‚¨ãƒ©ãƒ¼ã®ã—ãã„値ã«é”ã™ã‚‹ã¨ã€ãƒ†ã‚¹ãƒˆã®å®Ÿè¡ŒãŒä¸­æ­¢ã•れã¾ã™ã€‚ + + Aborted 中止ã•れã¾ã—㟠@@ -157,6 +167,11 @@ 'CompositeExtensonFactory' ã®åŒã˜ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒæ—¢ã«ç™»éŒ²ã•れã¦ã„ã¾ã™ + + The configuration file '{0}' specified with '--config-file' could not be found. + '--config-file' ã§æŒ‡å®šã•ã‚ŒãŸæ§‹æˆãƒ•ァイル '{0}' ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚ + + Could not find the default json configuration 既定㮠JSON æ§‹æˆãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—㟠@@ -214,6 +229,11 @@ プロãƒã‚¤ãƒ€ãƒ¼ '{0}' (UID: {1}) ãŒæ¬¡ã®ã‚¨ãƒ©ãƒ¼ã§å¤±æ•—ã—ã¾ã—ãŸ: {2} + + Exception during the cancellation of request id '{0}' + è¦æ±‚ ID '{0}' ã®å–り消ã—中ã«ä¾‹å¤–ãŒç™ºç”Ÿã—ã¾ã—㟠+ {0} is the request id + Exit code 終了コード @@ -266,6 +286,11 @@ {0} ä»¶ã®è­¦å‘Šä»˜ãã§å¤±æ•—ã—ã¾ã—㟠+ + Finished test session. + テスト セッションを終了ã—ã¾ã—ãŸã€‚ + + For test テスト用 @@ -356,6 +381,11 @@ ILoggerFactory ã¯ã¾ã æ§‹ç¯‰ã•れã¦ã„ã¾ã›ã‚“。 + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + オプション '--maximum-failed-tests' ã¯æ­£ã®æ•´æ•°ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚'{0}' 値ãŒç„¡åйã§ã™ã€‚ + + The message bus has not been built yet or is no more usable at this stage. メッセージ ãƒã‚¹ã¯ã¾ã æ§‹ç¯‰ã•れã¦ã„ãªã„ã‹ã€ã“ã®æ®µéšŽã§ã¯ã“れ以上使用ã§ãã¾ã›ã‚“。 @@ -371,6 +401,16 @@ jsonRpc プロトコルを使用ã™ã‚‹å ´åˆã€--client-port ãŒå¿…è¦ã§ã™ã€‚ + + and {0} more + ãã®ä»– {0} ä»¶ + + + + {0} tests running + {0} テストを実行ã—ã¦ã„ã¾ã™ + + No serializer registered with ID '{0}' ID '{0}' ã§ç™»éŒ²ã•れãŸã‚·ãƒªã‚¢ãƒ©ã‚¤ã‚¶ãƒ¼ãŒã‚りã¾ã›ã‚“ @@ -416,6 +456,11 @@ クライアントã®ãƒãƒ¼ãƒˆã‚’指定ã—ã¾ã™ã€‚ + + Specifies a testconfig.json file. + testconfig.json ファイルを指定ã—ã¾ã™ã€‚ + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -506,6 +551,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an .NET テスト アプリケーション情報を表示ã—ã¾ã™ã€‚ + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + テストã®å®Ÿè¡Œã‚’中止ã™ã‚‹ãƒ†ã‚¹ãƒˆ ã‚¨ãƒ©ãƒ¼ã®æœ€å¤§æ•°ã‚’指定ã—ã¾ã™ã€‚ + + '--list-tests' and '--minimum-expected-tests' are incompatible options '--list-tests' 㨠'--minimum-expected-tests' ã¯äº’æ›æ€§ã®ãªã„オプションã§ã™ @@ -513,7 +563,7 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + 実行ã™ã‚‹å¿…è¦ãŒã‚ã‚‹ãƒ†ã‚¹ãƒˆã®æœ€å°æ•°ã‚’指定ã—ã¾ã™ã€‚ @@ -594,6 +644,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is ã“ã®å€¤ã‚’決定ã™ã‚‹å‰ã«ãƒ—ロセスを終了ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + '--maximum-failed-tests' ã‚ªãƒ—ã‚·ãƒ§ãƒ³ã§æŒ‡å®šã•れãŸã‚¨ãƒ©ãƒ¼ ('{0}') ã«é”ã—ãŸãŸã‚ã€ãƒ†ã‚¹ãƒˆ セッションを中止ã—ã¦ã„ã¾ã™ã€‚ + {0} is the number of max failed tests. + Retry failed after {0} times å†è©¦è¡ŒãŒ {0} 回後ã«å¤±æ•—ã—ã¾ã—㟠@@ -624,6 +679,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is サーãƒãƒ¼ テスト実行フィルター ファクトリ + + The 'ITestHost' implementation used when running server mode. + サーãƒãƒ¼ モードã®å®Ÿè¡Œæ™‚ã«ä½¿ç”¨ã•れる 'ITestHost' 実装。 + + + + Server mode test host + サーãƒãƒ¼ モード テスト ホスト + + Cannot find service of type '{0}' '{0}' åž‹ã®ã‚µãƒ¼ãƒ“スãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ @@ -679,6 +744,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is サーãƒãƒ¼ã‚’èµ·å‹•ã—ã¦ã„ã¾ã™ã€‚ãƒãƒ¼ãƒˆ '{0}' ã§èžã„ã¦ã„ã¾ã™ + + Starting test session. + テスト セッションを開始ã—ã¦ã„ã¾ã™ã€‚ + + + + Starting test session. The log file path is '{0}'. + テスト セッションを開始ã—ã¦ã„ã¾ã™ã€‚ログ ファイルã®ãƒ‘ス㌠'{0}' ã§ã™ã€‚ + + An 'ITestExecutionFilterFactory' factory is already set 'ITestExecutionFilterFactory' ãƒ•ã‚¡ã‚¯ãƒˆãƒªã¯æ—¢ã«è¨­å®šã•れã¦ã„ã¾ã™ @@ -888,6 +963,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 通信プロトコル '{0}' ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + ãƒãƒ³ãƒ‰ãƒ«ã•れãªã„例外 [ServerTestHost.OnTaskSchedulerUnobservedTaskException]: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} ã“ã®ãƒ—ログラムã®å ´æ‰€ã«åˆ°é”ã§ããªã„ã¨è€ƒãˆã‚‰ã‚Œã¾ã™ã€‚ファイル='{0}'ã€è¡Œ={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf index 02feafb53a..b066867ba1 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ko.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + 현재 테스트 프레임워í¬ëŠ” '--maximum-failed-tests' ê¸°ëŠ¥ì— í•„ìš”í•œ 'IGracefulStopTestExecutionCapability'를 구현하지 않습니다. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + '--maximum-failed-tests'를 ì§€ì›í•˜ëŠ” ë° ì‚¬ìš©ë˜ëŠ” 확장입니다. ì§€ì •ëœ ì‹¤íŒ¨ ìž„ê³„ê°’ì— ë„달하면 테스트 ì‹¤í–‰ì´ ì¤‘ë‹¨ë©ë‹ˆë‹¤. + + Aborted ì¤‘ë‹¨ë¨ @@ -157,6 +167,11 @@ ë™ì¼í•œ 'CompositeExtensonFactory' ì¸ìŠ¤í„´ìŠ¤ê°€ ì´ë¯¸ 등ë¡ë¨ + + The configuration file '{0}' specified with '--config-file' could not be found. + '--config-file'으로 ì§€ì •ëœ êµ¬ì„± íŒŒì¼ '{0}' ì°¾ì„ ìˆ˜ 없습니다. + + Could not find the default json configuration 기본 json êµ¬ì„±ì„ ì°¾ì„ ìˆ˜ 없습니다. @@ -214,6 +229,11 @@ ê³µê¸‰ìž '{0}'(UID: {1})ì´ ì˜¤ë¥˜ {2}(으)로 실패했습니다. + + Exception during the cancellation of request id '{0}' + 요청 ID를 취소하는 ë™ì•ˆ 예외가 '{0}' + {0} is the request id + Exit code 종료 코드 @@ -266,6 +286,11 @@ {0} 경고와 함께 실패 + + Finished test session. + 테스트 ì„¸ì…˜ì„ ë§ˆì³¤ìŠµë‹ˆë‹¤. + + For test 테스트용 @@ -356,6 +381,11 @@ ILoggerFactoryê°€ ì•„ì§ ë¹Œë“œë˜ì§€ 않았습니다. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + '--maximum-failed-tests' ì˜µì…˜ì€ ì–‘ì˜ ì •ìˆ˜ì—¬ì•¼ 합니다. '{0}' ê°’ì´ ìž˜ëª»ë˜ì—ˆìŠµë‹ˆë‹¤. + + The message bus has not been built yet or is no more usable at this stage. 메시지 버스가 ì•„ì§ ë¹Œë“œë˜ì§€ 않았거나 ì´ ë‹¨ê³„ì—서 ë” ì´ìƒ 사용할 수 없습니다. @@ -371,6 +401,16 @@ jsonRpc í”„ë¡œí† ì½œì„ ì‚¬ìš©í•˜ëŠ” 경우 --client-portê°€ 필요합니다. + + and {0} more + 외 {0}ê°œ + + + + {0} tests running + 실행 ì¤‘ì¸ í…ŒìŠ¤íŠ¸ {0} + + No serializer registered with ID '{0}' ID '{0}'(으)로 등ë¡ëœ ì§ë ¬ 변환기가 없습니다. @@ -416,6 +456,11 @@ í´ë¼ì´ì–¸íŠ¸ì˜ í¬íŠ¸ë¥¼ 지정합니다. + + Specifies a testconfig.json file. + testconfig.json 파ì¼ì„ 지정합니다. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an .NET 테스트 애플리케ì´ì…˜ 정보를 표시합니다. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + 테스트 ì‹¤í–‰ì„ ì¤‘ë‹¨í•˜ëŠ” 최대 테스트 실패 수를 지정합니다. + + '--list-tests' and '--minimum-expected-tests' are incompatible options '--list-tests' ë° '--minimum-expected-tests'는 호환ë˜ì§€ 않는 옵션입니다. @@ -512,7 +562,7 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + ì‹¤í–‰ë  ê²ƒìœ¼ë¡œ 예ìƒë˜ëŠ” 최소 테스트 수를 지정합니다. @@ -593,6 +643,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is ì´ ê°’ì„ ê²°ì •í•˜ë ¤ë©´ 프로세스가 종료ë˜ì–´ì•¼ 합니다. + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + '--maximum-failed-tests' ì˜µì…˜ì— ì§€ì •ëœ ì‹¤íŒ¨('{0}')ì— ë„달하여 테스트 ì„¸ì…˜ì´ ì¤‘ë‹¨ë©ë‹ˆë‹¤. + {0} is the number of max failed tests. + Retry failed after {0} times {0}회 후 다시 ì‹œë„ ì‹¤íŒ¨ @@ -623,6 +678,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 서버 테스트 실행 í•„í„° 팩터리 + + The 'ITestHost' implementation used when running server mode. + 서버 모드를 실행할 때 사용ë˜ëŠ” 'ITestHost' 구현입니다. + + + + Server mode test host + 서버 모드 테스트 호스트 + + Cannot find service of type '{0}' '{0}' 형ì‹ì˜ 서비스를 ì°¾ì„ ìˆ˜ 없습니다. @@ -678,6 +743,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 서버를 시작하는 중입니다. í¬íЏ '{0}'ì—서 수신 대기 중 + + Starting test session. + 테스트 ì„¸ì…˜ì„ ì‹œìž‘í•˜ëŠ” 중입니다. + + + + Starting test session. The log file path is '{0}'. + 테스트 ì„¸ì…˜ì„ ì‹œìž‘í•˜ëŠ” 중입니다. 로그 íŒŒì¼ ê²½ë¡œê°€ '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set 'ITestExecutionFilterFactory' 팩터리가 ì´ë¯¸ 설정ë˜ì–´ ìžˆìŒ @@ -887,6 +962,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 통신 프로토콜 '{0}'ì€(는) ì§€ì›ë˜ì§€ 않습니다. + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + 처리ë˜ì§€ ì•Šì€ ì˜ˆì™¸ [ServerTestHost.OnTaskSchedulerUnobservedTaskException]: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} ì´ í”„ë¡œê·¸ëž¨ ìœ„ì¹˜ì— ì—°ê²°í•  수 없는 것으로 ìƒê°ë©ë‹ˆë‹¤. File='{0}' Line={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf index 63a7e36fe1..7445602a49 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pl.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Bieżąca platforma testowa nie implementuje interfejsu "IGracefulStopTestExecutionCapability", który jest wymagany dla funkcji "--maximum-failed-tests". + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Rozszerzenie używane do obsÅ‚ugi "--maximum-failed-tests". Po osiÄ…gniÄ™ciu podanego progu niepowodzeÅ„ przebieg testu zostanie przerwany. + + Aborted Przerwano @@ -157,6 +167,11 @@ To samo wystÄ…pienie elementu „CompositeExtensonFactory†jest już zarejestrowane + + The configuration file '{0}' specified with '--config-file' could not be found. + Nie można odnaleźć pliku konfiguracji „{0}†okreÅ›lonego za pomocÄ… parametru „--config-fileâ€. + + Could not find the default json configuration Nie można odnaleźć domyÅ›lnej konfiguracji JSON @@ -214,6 +229,11 @@ Dostawca „{0}†(UID:{1}) nie powiódÅ‚ siÄ™ z powodu błędu: {2} + + Exception during the cancellation of request id '{0}' + WyjÄ…tek podczas anulowania '{0}' identyfikatora żądania + {0} is the request id + Exit code Kod zakoÅ„czenia @@ -266,6 +286,11 @@ zakoÅ„czono niepowodzeniem, z ostrzeżeniami w liczbie: {0} + + Finished test session. + ZakoÅ„czono sesjÄ™ testÄ…. + + For test Na potrzeby testu @@ -356,6 +381,11 @@ Obiekt ILoggerFactory nie zostaÅ‚ jeszcze skompilowany. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + Opcja "--maximum-failed-tests" musi być dodatniÄ… liczbÄ… caÅ‚kowitÄ…. Wartość '{0}' jest nieprawidÅ‚owa. + + The message bus has not been built yet or is no more usable at this stage. Magistrala komunikatów nie zostaÅ‚a jeszcze zbudowana lub nie można jej już na tym etapie użyteczna. @@ -371,6 +401,16 @@ Oczekiwano parametru --client-port, gdy jest używany protokół jsonRpc. + + and {0} more + i {0} wiÄ™cej + + + + {0} tests running + testy {0} uruchomione + + No serializer registered with ID '{0}' Nie zarejestrowano serializatora z identyfikatorem „{0}†@@ -416,6 +456,11 @@ OkreÅ›l port klienta. + + Specifies a testconfig.json file. + OkreÅ›la plik testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ DostÄ™pne wartoÅ›ci to „Traceâ€, „Debugâ€, „Informationâ€, „Warning WyÅ›wietl informacje o aplikacji testowej platformy .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + OkreÅ›la maksymalnÄ… liczbÄ™ niepowodzeÅ„ testów, które po przekroczeniu spowoduje przerwanie przebiegu testu. + + '--list-tests' and '--minimum-expected-tests' are incompatible options Opcje „--list-tests†i „--minimum-expected-tests†sÄ… niezgodne @@ -512,7 +562,7 @@ DostÄ™pne wartoÅ›ci to „Traceâ€, „Debugâ€, „Informationâ€, „Warning Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + OkreÅ›la minimalnÄ… liczbÄ™ testów, które majÄ… zostać uruchomione. @@ -593,6 +643,11 @@ Pobiera jeden argument jako ciÄ…g w formacie <value>[h|m|s], gdzie element Proces powinien zakoÅ„czyć siÄ™ przed ustaleniem tej wartoÅ›ci + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + Sesja testowa jest przerywana z powodu błędów ('{0}') okreÅ›lonych przez opcjÄ™ "--maximum-failed-tests". + {0} is the number of max failed tests. + Retry failed after {0} times Ponowna próba nie powiodÅ‚a siÄ™ po {0} razach @@ -623,6 +678,16 @@ Pobiera jeden argument jako ciÄ…g w formacie <value>[h|m|s], gdzie element Fabryka filtrów wykonywania testów serwera + + The 'ITestHost' implementation used when running server mode. + Implementacja "ITestHost" używana podczas uruchamiania trybu serwera. + + + + Server mode test host + Host testów trybu serwera + + Cannot find service of type '{0}' Nie można odnaleźć usÅ‚ugi typu „{0}†@@ -678,6 +743,16 @@ Pobiera jeden argument jako ciÄ…g w formacie <value>[h|m|s], gdzie element Uruchamianie serwera. NasÅ‚uchiwanie na porcie „{0}†+ + Starting test session. + Rozpoczynanie sesji testowej. + + + + Starting test session. The log file path is '{0}'. + Rozpoczynanie sesji testowej. Åšcieżka pliku dziennika jest '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Fabryka „ITestExecutionFilterFactory†jest już ustawiona @@ -887,6 +962,11 @@ PrawidÅ‚owe wartoÅ›ci to „Normalneâ€, „Szczegółoweâ€. Wartość domyÅ›ln Protokół komunikacyjny „{0}†nie jest obsÅ‚ugiwany + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] NieobsÅ‚ugiwany wyjÄ…tek: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Ta lokalizacja programu jest uważana za nieosiÄ…galnÄ…. Plik=„{0}â€, Wiersz={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf index a397aabdb9..5dd751afdc 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.pt-BR.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + A estrutura de teste atual não implementa 'IGracefulStopTestExecutionCapability', que é necessário para o recurso '--maximum-failed-tests'. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + Extensão usada para dar suporte a '--maximum-failed-tests'. Quando um determinado limite de falhas for atingido, a execução de teste será anulada. + + Aborted Anulado @@ -157,6 +167,11 @@ A mesma instância de “CompositeExtensonFactory†já está registrada + + The configuration file '{0}' specified with '--config-file' could not be found. + Não foi possível encontrar o arquivo de configuração '{0}' especificado com '--config-file'. + + Could not find the default json configuration Não foi possível localizar a configuração padrão do json @@ -214,6 +229,11 @@ O provedor ''{0}'' (UID: {1}) falhou com o erro: {2} + + Exception during the cancellation of request id '{0}' + Exceção durante o cancelamento da ID da solicitação '{0}' + {0} is the request id + Exit code Código de saída @@ -266,6 +286,11 @@ falhou com {0} aviso(s) + + Finished test session. + Sessão de teste concluída. + + For test Para teste @@ -356,6 +381,11 @@ O ILoggerFactory ainda não foi criado. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + A opção '--maximum-failed-tests' deve ser um inteiro positivo. O valor '{0}' é inválido. + + The message bus has not been built yet or is no more usable at this stage. O barramento de mensagens ainda não foi criado ou não pode mais ser usado nesse estágio. @@ -371,6 +401,16 @@ Esperado --client-port quando o protocolo jsonRpc é usado. + + and {0} more + e mais {0} + + + + {0} tests running + {0} testes em execução + + No serializer registered with ID '{0}' Nenhum serializador registrado com a ID “{0}†@@ -416,6 +456,11 @@ Especifique a porta do cliente. + + Specifies a testconfig.json file. + Especifica um arquivo testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ Os valores disponíveis são 'Rastreamento', 'Depuração', 'Informação', 'Avi Exibir informações do aplicativo de teste do .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Especifica um número máximo de falhas de teste que, quando excedido, anularão a execução de teste. + + '--list-tests' and '--minimum-expected-tests' are incompatible options '--list-tests' e '--minimum-expected-tests' são opções incompatíveis @@ -512,7 +562,7 @@ Os valores disponíveis são 'Rastreamento', 'Depuração', 'Informação', 'Avi Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Especifica o número mínimo de testes que devem ser executados. @@ -593,6 +643,11 @@ Recebe um argumento como cadeia de caracteres no formato <valor>[h|m|s] em O processo deve ter sido encerrado antes que possamos determinar esse valor + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + A sessão de teste está sendo anulada devido a falhas ('{0}') especificadas pela opção '--maximum-failed-tests'. + {0} is the number of max failed tests. + Retry failed after {0} times Falha na repetição após o {0} tempo @@ -623,6 +678,16 @@ Recebe um argumento como cadeia de caracteres no formato <valor>[h|m|s] em Fábrica de filtros de execução de teste de servidor + + The 'ITestHost' implementation used when running server mode. + A implementação 'ITestHost' usada ao executar o modo de servidor. + + + + Server mode test host + Host de teste do modo de servidor + + Cannot find service of type '{0}' Não é possível localizar o serviço do tipo "{0}" @@ -678,6 +743,16 @@ Recebe um argumento como cadeia de caracteres no formato <valor>[h|m|s] em Iniciando servidor. Escutando na porta “{0}†+ + Starting test session. + Iniciando sessão de teste. + + + + Starting test session. The log file path is '{0}'. + Iniciando sessão de teste. O caminho do arquivo de log '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Uma fábrica “ITestExecutionFilterFactory†já está definida @@ -887,6 +962,11 @@ Os valores válidos são “Normalâ€, “Detalhadoâ€. O padrão é “Normal O protocolo de comunicação “{0}†não tem suporte + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] exceção sem tratamento: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Este local do programa é considerado inacessível. Arquivo='{0}' Linha={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf index b478eea875..a1cd0e69ec 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.ru.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ð° теÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½Ðµ реализует параметр "IGracefulStopTestExecutionCapability", необходимый Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ "--maximum-failed-tests". + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + РаÑширение, иÑпользуемое Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ "--maximum-failed-tests". При превышении заданного порогового Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñбоев теÑтовый запуÑк будет прерван. + + Aborted Прервано @@ -157,6 +167,11 @@ Такой же ÑкземплÑÑ€ CompositeExtensonFactory уже зарегиÑтрирован + + The configuration file '{0}' specified with '--config-file' could not be found. + Ðе удалоÑÑŒ найти файл конфигурации "{0}", указанный Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ "--config-file". + + Could not find the default json configuration Ðе удалоÑÑŒ найти конфигурацию JSON по умолчанию @@ -214,6 +229,11 @@ Сбой поÑтавщика "{0}" (ИД пользователÑ: {1}) Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹: {2} + + Exception during the cancellation of request id '{0}' + ИÑключение при отмене запроÑа '{0}' + {0} is the request id + Exit code Код Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ @@ -266,6 +286,11 @@ Ñбой Ñ Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñми ({0}) + + Finished test session. + ТеÑтовый ÑÐµÐ°Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½. + + For test Ð”Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ @@ -356,6 +381,11 @@ Параметр ILoggerFactory еще не Ñоздан. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + Параметр "--maximum-failed-tests" должен быть положительным целым чиÑлом. ÐедопуÑтимое '{0}' значение. + + The message bus has not been built yet or is no more usable at this stage. Шина Ñообщений еще не поÑтроена или на данном Ñтапе непригодна Ð´Ð»Ñ Ð¸ÑпользованиÑ. @@ -371,6 +401,16 @@ ОжидаетÑÑ --client-port при иÑпользовании протокола jsonRpc. + + and {0} more + и еще {0} + + + + {0} tests running + {0} теÑтов + + No serializer registered with ID '{0}' Ðе зарегиÑтрирован Ñериализатор Ñ Ð˜Ð” "{0}" @@ -416,6 +456,11 @@ Укажите порт клиента. + + Specifies a testconfig.json file. + Указывает файл testconfig.json. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an Отображение Ñведений о теÑтовом приложении .NET. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Указывает макÑимальное чиÑло Ñбоев теÑтов, которые при превышении прервют теÑтовый запуÑк. + + '--list-tests' and '--minimum-expected-tests' are incompatible options Параметры "--list-tests" и "--minimum-expected-tests" неÑовмеÑтимы @@ -512,7 +562,7 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Указывает минимальное чиÑло теÑтов, которые должны быть запущены. @@ -593,6 +643,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is ПроцеÑÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть завершен, прежде чем мы Ñможем определить Ñто значение + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + ТеÑтовый ÑÐµÐ°Ð½Ñ Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ Ð¸Ð·-за Ñбоев ('{0}'), указанных параметром "--maximum-failed-tests". + {0} is the number of max failed tests. + Retry failed after {0} times Повторные попытки ({0}) завершилиÑÑŒ неудачно @@ -623,6 +678,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is Фабрика фильтров Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚ÐµÑтов Ñервера + + The 'ITestHost' implementation used when running server mode. + Ð ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ "ITestHost", иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ð¿Ñ€Ð¸ работе в режиме Ñервера. + + + + Server mode test host + ТеÑтовый хоÑÑ‚ в режиме Ñервера + + Cannot find service of type '{0}' Ðе удалоÑÑŒ найти Ñлужбу типа "{0}" @@ -678,6 +743,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is ВыполнÑетÑÑ Ð·Ð°Ð¿ÑƒÑк Ñервера. ПроÑлушивание порта "{0}" + + Starting test session. + ЗапуÑк теÑтового ÑеанÑа. + + + + Starting test session. The log file path is '{0}'. + ЗапуÑк теÑтового ÑеанÑа. Путь к файлу журнала '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Фабрика ITestExecutionFilterFactory уже наÑтроена @@ -887,6 +962,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. Протокол коммуникации "{0}" не поддерживаетÑÑ + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] необработанное иÑключение: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Похоже, что Ñто раÑположение программы ÑвлÑетÑÑ Ð½ÐµÐ´Ð¾Ñтупным. Файл="{0}", Ñтрока={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf index 4d0ad0f21e..e4be9f7978 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.tr.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + Geçerli test çerçevesi, '--maximum-failed-tests' özelliÄŸi için gerekli olan 'IGracefulStopTestExecutionCapability' gerçekleÅŸtiremiyor. + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + '--maximum-failed-tests' desteÄŸi için kullanılan uzantı. Belirtilen hatalar eÅŸiÄŸine ulaşıldığında test çalıştırması durdurulacak. + + Aborted Durduruldu @@ -119,12 +129,12 @@ Failed to read response file '{0}'. {1}. - Failed to read response file '{0}'. {1}. + '{0}' yanıt dosyası okunamadı. {1}. {1} is the exception The response file '{0}' was not found - The response file '{0}' was not found + '{0}' yanıt dosyası bulunamadı @@ -157,6 +167,11 @@ Aynı 'CompositeExtensonFactory' örneÄŸi zaten kayıtlı + + The configuration file '{0}' specified with '--config-file' could not be found. + '--config-file' ile belirtilen '{0}' yapılandırma dosyası bulunamadı. + + Could not find the default json configuration Varsayılan JSON yapılandırması bulunamadı @@ -214,6 +229,11 @@ '{0}' saÄŸlayıcısı '' (UID: {1}) ÅŸu hatayla baÅŸarısız oldu: {2} + + Exception during the cancellation of request id '{0}' + İstek kimliÄŸi iptali sırasında özel durum '{0}' + {0} is the request id + Exit code Çıkış kodu @@ -266,6 +286,11 @@ {0} uyarıyla baÅŸarısız oldu + + Finished test session. + Test oturumu bitti. + + For test Test için @@ -356,6 +381,11 @@ ILoggerFactory henüz derlenmedi. + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + '--maximum-failed-tests' seçeneÄŸi pozitif bir tamsayı olmalıdır. Belirtilen '{0}' geçerli deÄŸil. + + The message bus has not been built yet or is no more usable at this stage. İleti veri yolu henüz derlenmedi veya bu aÅŸamada artık kullanılamıyor. @@ -371,6 +401,16 @@ JsonRpc protokolü kullanıldığında --client-port bekleniyordu. + + and {0} more + ve {0} tane daha + + + + {0} tests running + {0} test çalıştırılıyor + + No serializer registered with ID '{0}' '{0}' kimliÄŸiyle kayıtlı seri hale getirici yok @@ -416,6 +456,11 @@ İstemcinin baÄŸlantı noktasını belirtir. + + Specifies a testconfig.json file. + testconfig.json dosyası belirtir. + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ Kullanılabilir deÄŸerler: 'Trace', 'Debug', 'Information', 'Warning', 'Error' v .NET test uygulaması bilgilerini görüntüler. + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + Aşıldığında test çalıştırmasını durduracak en fazla test hatası sayısını belirtir. + + '--list-tests' and '--minimum-expected-tests' are incompatible options '--list-tests' ve '--minimum-expected-tests' uyumsuz seçenekler @@ -512,7 +562,7 @@ Kullanılabilir deÄŸerler: 'Trace', 'Debug', 'Information', 'Warning', 'Error' v Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Çalıştırılması beklenen en düşük test sayısını belirtir. @@ -593,6 +643,11 @@ Bir bağımsız deÄŸiÅŸkeni, 'value' deÄŸerinin kayan olduÄŸu <value>[h|m| Bu deÄŸeri belirleyebilmemiz için süreçten çıkılmış olması gerekir + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + '--maximum-failed-tests' seçeneÄŸi tarafından belirtilen hatalara ('{0}') ulaşılamama nedeniyle test oturumu iptal edildi. + {0} is the number of max failed tests. + Retry failed after {0} times Yeniden deneme {0} deneme sonrasında baÅŸarısız oldu @@ -623,6 +678,16 @@ Bir bağımsız deÄŸiÅŸkeni, 'value' deÄŸerinin kayan olduÄŸu <value>[h|m| Sunucu test yürütme filtresi fabrikası + + The 'ITestHost' implementation used when running server mode. + Sunucu modu çalıştırılırken kullanılan 'ITestHost' uygulaması. + + + + Server mode test host + Sunucu modu test ana bilgisayarı + + Cannot find service of type '{0}' '{0}' türündeki hizmet bulunamıyor @@ -678,6 +743,16 @@ Bir bağımsız deÄŸiÅŸkeni, 'value' deÄŸerinin kayan olduÄŸu <value>[h|m| Sunucu baÅŸlatılıyor. '{0}' baÄŸlantı noktasında dinleme iÅŸlemi yapılıyor + + Starting test session. + Test oturumu baÅŸlatılıyor. + + + + Starting test session. The log file path is '{0}'. + Test oturumu baÅŸlatılıyor. Günlük dosyası yolu '{0}'. + + An 'ITestExecutionFilterFactory' factory is already set Bir 'ITestExecutionFilterFactory' fabrikası zaten ayarlanmış @@ -887,6 +962,11 @@ Geçerli deÄŸerler: ‘Normal’, ‘Ayrıntılı’. Varsayılan deÄŸer: ‘Nor '{0}' iletiÅŸim protokolü desteklenmiyor + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] özel durum: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} Bu program konumu eriÅŸilemez olarak görülüyor. Dosya='{0}', Satır={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf index 381bffaf18..bad8bc0553 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hans.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + 当剿µ‹è¯•框架未实现 “--maximum-failed-tests†功能所需的 “IGracefulStopTestExecutionCapabilityâ€ã€‚ + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + ç”¨äºŽæ”¯æŒ â€œ--maximum-failed-tests†的扩展。达到给定失败阈值时,将中止测试è¿è¡Œã€‚ + + Aborted 已中止 @@ -157,6 +167,11 @@ “CompositeExtensonFactoryâ€çš„åŒä¸€å®žä¾‹å·²æ³¨å†Œ + + The configuration file '{0}' specified with '--config-file' could not be found. + 找ä¸åˆ°ä½¿ç”¨ ‘--config-file’ 指定的é…置文件“{0}â€ã€‚ + + Could not find the default json configuration 找ä¸åˆ°é»˜è®¤ json é…ç½® @@ -214,6 +229,11 @@ æä¾›ç¨‹åº '{0}' (UID: {1}) 失败,出现错误: {2} + + Exception during the cancellation of request id '{0}' + å–æ¶ˆè¯·æ±‚ ID 期间出现异常 '{0}' + {0} is the request id + Exit code é€€å‡ºä»£ç  @@ -266,6 +286,11 @@ 失败,出现 {0} 警告 + + Finished test session. + å·²å®Œæˆæµ‹è¯•会è¯ã€‚ + + For test 用于测试 @@ -356,6 +381,11 @@ å°šæœªç”Ÿæˆ ILoggerFactory。 + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + 选项 “--maximum-failed-tests†必须是正整数。值 '{0}' 无效。 + + The message bus has not been built yet or is no more usable at this stage. æ¶ˆæ¯æ€»çº¿å°šæœªç”Ÿæˆæˆ–在此阶段ä¸å†å¯ç”¨ã€‚ @@ -371,6 +401,16 @@ 使用 jsonRpc å议时应为 --client-port。 + + and {0} more + 和其他 {0} 项 + + + + {0} tests running + 正在è¿è¡Œ {0} 测试 + + No serializer registered with ID '{0}' 没有使用 ID“{0}â€æ³¨å†Œåºåˆ—åŒ–ç¨‹åº @@ -416,6 +456,11 @@ 指定客户端的端å£ã€‚ + + Specifies a testconfig.json file. + 指定 testconfig.json 文件。 + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an 显示 .NET 测试应用程åºä¿¡æ¯ã€‚ + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + 指定超过测试失败数åŽå°†ä¸­æ­¢æµ‹è¯•è¿è¡Œçš„æœ€å¤§æ•°ç›®ã€‚ + + '--list-tests' and '--minimum-expected-tests' are incompatible options “--list-testsâ€å’Œâ€œ--minimum-expected-testsâ€æ˜¯ä¸å…¼å®¹çš„选项 @@ -512,7 +562,7 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + 指定预期è¿è¡Œçš„æœ€å°æµ‹è¯•数。 @@ -593,6 +643,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 在我们确定此值之å‰ï¼Œæµç¨‹åº”该已退出 + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + 由于达到“--maximum-failed-testsâ€é€‰é¡¹æŒ‡å®šçš„失败 ('{0}')ï¼Œæµ‹è¯•ä¼šè¯æ­£åœ¨ä¸­æ­¢ã€‚ + {0} is the number of max failed tests. + Retry failed after {0} times {0} 次之åŽé‡è¯•失败 @@ -623,6 +678,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is æœåŠ¡å™¨æµ‹è¯•æ‰§è¡Œç­›é€‰å·¥åŽ‚ + + The 'ITestHost' implementation used when running server mode. + è¿è¡ŒæœåŠ¡å™¨æ¨¡å¼æ—¶ä½¿ç”¨çš„ “ITestHost†实现。 + + + + Server mode test host + æœåŠ¡å™¨æ¨¡å¼æµ‹è¯•主机 + + Cannot find service of type '{0}' 找ä¸åˆ°â€œ{0}â€ç±»åž‹çš„æœåŠ¡ @@ -678,6 +743,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 正在å¯åЍæœåŠ¡å™¨ã€‚æ­£åœ¨ä¾¦å¬ç«¯å£â€œ{0}†+ + Starting test session. + 正在å¯åŠ¨æµ‹è¯•ä¼šè¯ã€‚ + + + + Starting test session. The log file path is '{0}'. + 正在å¯åŠ¨æµ‹è¯•ä¼šè¯ã€‚日志文件路径 '{0}'。 + + An 'ITestExecutionFilterFactory' factory is already set 已设置“ITestExecutionFilterFactoryâ€å·¥åŽ‚ @@ -887,6 +962,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 䏿”¯æŒé€šä¿¡å议“{0}†+ + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] 未ç»å¤„ç†çš„异常: {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} 此程åºä½ç½®è¢«è§†ä¸ºæ— æ³•访问。文件='{0}',行={1} diff --git a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf index 6a801d838c..7af1c56826 100644 --- a/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Platform/Resources/xlf/PlatformResources.zh-Hant.xlf @@ -2,6 +2,16 @@ + + The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature. + ç›®å‰çš„æ¸¬è©¦æž¶æ§‹æœªå¯¦ä½œ '--maximum-failed-tests' 功能所需的 'IGracefulStopTestExecutionCapability'。 + + + + Extension used to support '--maximum-failed-tests'. When a given failures threshold is reached, the test run will be aborted. + ç”¨ä¾†æ”¯æ´ ã€Ž--maximum-failed-tests〠的延伸模組。é”到指定的失敗閾值時,測試回åˆå°‡æœƒä¸­æ­¢ã€‚ + + Aborted 已中止 @@ -157,6 +167,11 @@ 已註冊 'CompositeExtensonFactory' çš„åŒä¸€åŸ·è¡Œå€‹é«” + + The configuration file '{0}' specified with '--config-file' could not be found. + 找ä¸åˆ°ä»¥ '--config-file' 指定的設定檔 '{0}'。 + + Could not find the default json configuration 找ä¸åˆ°é è¨­ JSON 設定 @@ -214,6 +229,11 @@ æä¾›è€… '{0}' (UID: {1}) 失敗,發生錯誤: {2} + + Exception during the cancellation of request id '{0}' + å–æ¶ˆè¦æ±‚標識碼 '{0}' æœŸé–“ç™¼ç”Ÿä¾‹å¤–ç‹€æ³ + {0} is the request id + Exit code çµæŸä»£ç¢¼ @@ -266,6 +286,11 @@ 失敗,有 {0} 個警告 + + Finished test session. + å·²å®Œæˆæ¸¬è©¦æœƒè©±ã€‚ + + For test 用於測試 @@ -356,6 +381,11 @@ 尚未建置 ILoggerFactory。 + + The option '--maximum-failed-tests' must be a positive integer. The value '{0}' is not valid. + é¸é … '--maximum-failed-tests' 必須是正整數。值 '{0}' 無效。 + + The message bus has not been built yet or is no more usable at this stage. 訊æ¯åŒ¯æµæŽ’尚未建置或在此階段無法å†ä½¿ç”¨ã€‚ @@ -371,6 +401,16 @@ 使用 jsonRpc 通訊å”定時,必須是 --client-port。 + + and {0} more + 和其他 {0} 個 + + + + {0} tests running + 正在執行 {0} 測試 + + No serializer registered with ID '{0}' 沒有使用識別碼 '{0}' 註冊的åºåˆ—åŒ–ç¨‹å¼ @@ -416,6 +456,11 @@ 指定用戶端的連接埠。 + + Specifies a testconfig.json file. + 指定 testconfig.json 檔案。 + + Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). @@ -505,6 +550,11 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an 顯示 .NET 測試應用程å¼è³‡è¨Šã€‚ + + Specifies a maximum number of test failures that, when exceeded, will abort the test run. + æŒ‡å®šè¶…éŽæ­¤æ•¸ç›®æ™‚ï¼Œæ¸¬è©¦å¤±æ•—æ¬¡æ•¸ä¸Šé™æœƒä¸­æ­¢æ¸¬è©¦å›žåˆã€‚ + + '--list-tests' and '--minimum-expected-tests' are incompatible options '--list-tests' å’Œ '--minimum-expected-tests' 是ä¸ç›¸å®¹çš„é¸é … @@ -512,7 +562,7 @@ The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', an Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + æŒ‡å®šé æœŸåŸ·è¡Œçš„æ¸¬è©¦æ•¸ç›®ä¸‹é™ã€‚ @@ -593,6 +643,11 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 在我們確定此值之å‰ï¼Œæµç¨‹æ‡‰å·²çµæŸ + + Test session is aborting due to reaching failures ('{0}') specified by the '--maximum-failed-tests' option. + 測試會話正在中止,因為é”到失敗 ('{0}') 『--maximum-failed-tests〠é¸é …指定。 + {0} is the number of max failed tests. + Retry failed after {0} times 在 {0} 次é‡è©¦ä¹‹å¾Œå¤±æ•— @@ -623,6 +678,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 伺æœå™¨æ¸¬è©¦åŸ·è¡Œç¯©é¸æ¢ä»¶ä¸­å¿ƒ + + The 'ITestHost' implementation used when running server mode. + 執行伺æœå™¨æ¨¡å¼æ™‚使用的 『ITestHost〠實作。 + + + + Server mode test host + 伺æœå™¨æ¨¡å¼æ¸¬è©¦ä¸»æ©Ÿ + + Cannot find service of type '{0}' 找ä¸åˆ°é¡žåž‹ '{0}' çš„æœå‹™ @@ -678,6 +743,16 @@ Takes one argument as string in the format <value>[h|m|s] where 'value' is 正在啟動伺æœå™¨ã€‚正在連接埠 '{0}' 上è†è½ + + Starting test session. + 正在啟動測試會話。 + + + + Starting test session. The log file path is '{0}'. + 正在啟動測試會話。記錄檔路徑 '{0}'。 + + An 'ITestExecutionFilterFactory' factory is already set 已設定 'ITestExecutionFilterFactory' 中心 @@ -887,6 +962,11 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'. 通訊å”定 '{0}' ä¸å—æ”¯æ´ + + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {0} + [ServerTestHost.OnTaskSchedulerUnobservedTaskException] 未處ç†çš„例外狀æ³ï¼š {0} + {0} is the exception that was unhandled/unobserved + This program location is thought to be unreachable. File='{0}' Line={1} 此程å¼ä½ç½®è¢«èªç‚ºç„¡æ³•連線。File='{0}' Line={1} diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs index db8f4d248c..6ef3e8686f 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestDataConsumer.cs similarity index 97% rename from src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs rename to src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestDataConsumer.cs index 63af8f949c..ab8b8865ba 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestDataConsumer.cs @@ -10,7 +10,7 @@ namespace Microsoft.Testing.Platform.IPC; -internal class DotnetTestDataConsumer : IPushOnlyProtocolConsumer +internal sealed class DotnetTestDataConsumer : IPushOnlyProtocolConsumer { private readonly DotnetTestConnection? _dotnetTestConnection; private readonly IEnvironment _environment; @@ -172,8 +172,6 @@ private static TestNodeDetails GetTestNodeDetails(TestNodeUpdateMessage testNode long? duration = null; string? reason = string.Empty; ExceptionMessage[]? exceptions = null; - string? errorStackTrace = string.Empty; - TestNodeStateProperty nodeState = testNodeUpdateMessage.TestNode.Properties.Single(); string? standardOutput = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.StandardOutput; string? standardError = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.StandardError; @@ -245,7 +243,7 @@ private static TestNodeDetails GetTestNodeDetails(TestNodeUpdateMessage testNode } } - public record TestNodeDetails(byte? State, long? Duration, string? Reason, ExceptionMessage[]? Exceptions, string? StandardOutput, string? StandardError); + public sealed record TestNodeDetails(byte? State, long? Duration, string? Reason, ExceptionMessage[]? Exceptions, string? StandardOutput, string? StandardError); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs index ae2924a6f9..9940477af9 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/HandshakeMessageSerializer.cs @@ -35,10 +35,10 @@ public void Serialize(object objectToSerialize, Stream stream) } WriteShort(stream, (ushort)handshakeMessage.Properties.Count); - foreach (KeyValuePair property in handshakeMessage.Properties) + foreach ((byte key, string value) in handshakeMessage.Properties) { - WriteField(stream, property.Key); - WriteField(stream, property.Value); + WriteField(stream, key); + WriteField(stream, value); } } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/IPushOnlyProtocolConsumer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/IPushOnlyProtocolConsumer.cs index b479afef2d..8ad796cc5c 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/IPushOnlyProtocolConsumer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/IPushOnlyProtocolConsumer.cs @@ -5,6 +5,4 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal interface IPushOnlyProtocolConsumer : IDataConsumer, ITestSessionLifetimeHandler -{ -} +internal interface IPushOnlyProtocolConsumer : IDataConsumer, ITestSessionLifetimeHandler; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ErrorCodes.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ErrorCodes.cs index ec80398a22..d001fa1d8c 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ErrorCodes.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ErrorCodes.cs @@ -1,12 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.ServerMode; [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Common pattern to use public static readonly fields")] -internal class ErrorCodes +internal sealed class ErrorCodes { #region JSON-RPC error codes // JSON-RPC specific error codes. diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs index c3410c2152..f829b100fd 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/Json.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Text; using System.Text.Json; using Microsoft.Testing.Platform.Extensions.Messages; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs index f559ce340c..b22ae843f5 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsonCollectionDeserializer.cs @@ -10,7 +10,7 @@ internal abstract class JsonCollectionDeserializer : JsonDeserializ internal abstract TCollection CreateObject(Json json, JsonElement element); } -internal class JsonCollectionDeserializer(Func createCollection, Action addItem) : JsonCollectionDeserializer +internal sealed class JsonCollectionDeserializer(Func createCollection, Action addItem) : JsonCollectionDeserializer where TCollection : ICollection { private readonly Func _createCollection = createCollection; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsoniteProperties.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsoniteProperties.cs index adf00d8a21..ec292d8a38 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsoniteProperties.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/Json/JsoniteProperties.cs @@ -4,4 +4,4 @@ namespace Microsoft.Testing.Platform.ServerMode.Json; // This object is needed to reuse jsonite's serialization shared code. -internal class JsoniteProperties : Dictionary; +internal sealed class JsoniteProperties : Dictionary; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs index 7bd1ec32d5..8733a93026 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/MessageHandlerFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; using System.Net; using System.Net.Sockets; @@ -14,7 +13,7 @@ namespace Microsoft.Testing.Platform.ServerMode; internal sealed partial class ServerModeManager { - internal class MessageHandlerFactory : IMessageHandlerFactory, IOutputDeviceDataProducer + internal sealed class MessageHandlerFactory : IMessageHandlerFactory, IOutputDeviceDataProducer { private readonly string? _host; private readonly int _port; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs index 52c8b93d64..70a8642f30 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PassiveNode.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Hosts; using Microsoft.Testing.Platform.Logging; @@ -10,7 +8,7 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal class PassiveNode : IDisposable +internal sealed class PassiveNode : IDisposable { private readonly IMessageHandlerFactory _messageHandlerFactory; private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs index e728b45fcf..0097e64ac7 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestServerDataConsumerService.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestTestSessionContext.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestTestSessionContext.cs index 729fa3bd84..374411b7ec 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestTestSessionContext.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/PerRequestTestSessionContext.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Platform.ServerMode; -internal class PerRequestTestSessionContext(CancellationToken rpcCancellationToken, CancellationToken testApplicationcancellationToken) : ITestSessionContext, IDisposable +internal sealed class PerRequestTestSessionContext(CancellationToken rpcCancellationToken, CancellationToken testApplicationcancellationToken) : ITestSessionContext, IDisposable { private readonly CancellationTokenSource _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(rpcCancellationToken, testApplicationcancellationToken); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs index be95ad9a19..941f900ed7 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/RpcMessages.cs @@ -12,19 +12,19 @@ internal abstract record RpcMessage(); /// A request is a message for which the server should return a corresponding /// or . /// -internal record RequestMessage(int Id, string Method, object? Params) : RpcMessage; +internal sealed record RequestMessage(int Id, string Method, object? Params) : RpcMessage; /// /// A notification message is a message that notifies the server of an event. /// There's no corresponding response that the server should send back and as such /// no Id is specified when sending a notification. /// -internal record NotificationMessage(string Method, object? Params) : RpcMessage; +internal sealed record NotificationMessage(string Method, object? Params) : RpcMessage; /// /// An error message is sent if some exception was thrown when processing the request. /// -internal record ErrorMessage(int Id, int ErrorCode, string Message, object? Data) : RpcMessage; +internal sealed record ErrorMessage(int Id, int ErrorCode, string Message, object? Data) : RpcMessage; /// /// An response message is sent if a request is handled successfully. @@ -33,59 +33,59 @@ internal record ErrorMessage(int Id, int ErrorCode, string Message, object? Data /// If the RPC handler returns a the /// will be returned as null. /// -internal record ResponseMessage(int Id, object? Result) : RpcMessage; +internal sealed record ResponseMessage(int Id, object? Result) : RpcMessage; -internal record InitializeRequestArgs(int ProcessId, ClientInfo ClientInfo, ClientCapabilities Capabilities); +internal sealed record InitializeRequestArgs(int ProcessId, ClientInfo ClientInfo, ClientCapabilities Capabilities); -internal record InitializeResponseArgs(int? ProcessId, ServerInfo ServerInfo, ServerCapabilities Capabilities); +internal sealed record InitializeResponseArgs(int? ProcessId, ServerInfo ServerInfo, ServerCapabilities Capabilities); internal record RequestArgsBase(Guid RunId, ICollection? TestNodes, string? GraphFilter); -internal record DiscoverRequestArgs(Guid RunId, ICollection? TestNodes, string? GraphFilter) : +internal sealed record DiscoverRequestArgs(Guid RunId, ICollection? TestNodes, string? GraphFilter) : RequestArgsBase(RunId, TestNodes, GraphFilter); internal record ResponseArgsBase; -internal record DiscoverResponseArgs : ResponseArgsBase; +internal sealed record DiscoverResponseArgs : ResponseArgsBase; -internal record RunRequestArgs(Guid RunId, ICollection? TestNodes, string? GraphFilter) : +internal sealed record RunRequestArgs(Guid RunId, ICollection? TestNodes, string? GraphFilter) : RequestArgsBase(RunId, TestNodes, GraphFilter); -internal record RunResponseArgs(Artifact[] Artifacts) : ResponseArgsBase; +internal sealed record RunResponseArgs(Artifact[] Artifacts) : ResponseArgsBase; -internal record Artifact(string Uri, string Producer, string Type, string DisplayName, string? Description = null); +internal sealed record Artifact(string Uri, string Producer, string Type, string DisplayName, string? Description = null); -internal record CancelRequestArgs(int CancelRequestId); +internal sealed record CancelRequestArgs(int CancelRequestId); -internal record ExitRequestArgs; +internal sealed record ExitRequestArgs; -internal record ClientInfo(string Name, string Version); +internal sealed record ClientInfo(string Name, string Version); -internal record ClientCapabilities(bool DebuggerProvider); +internal sealed record ClientCapabilities(bool DebuggerProvider); -internal record ClientTestingCapabilities(bool DebuggerProvider); +internal sealed record ClientTestingCapabilities(bool DebuggerProvider); -internal record ServerInfo(string Name, string Version); +internal sealed record ServerInfo(string Name, string Version); -internal record ServerCapabilities(ServerTestingCapabilities TestingCapabilities); +internal sealed record ServerCapabilities(ServerTestingCapabilities TestingCapabilities); -internal record ServerTestingCapabilities( +internal sealed record ServerTestingCapabilities( bool SupportsDiscovery, bool MultiRequestSupport, bool VSTestProviderSupport, bool SupportsAttachments, bool MultiConnectionProvider); -internal record TestNodeStateChangedEventArgs(Guid RunId, TestNodeUpdateMessage[]? Changes); +internal sealed record TestNodeStateChangedEventArgs(Guid RunId, TestNodeUpdateMessage[]? Changes); -internal record LogEventArgs(ServerLogMessage LogMessage); +internal sealed record LogEventArgs(ServerLogMessage LogMessage); -internal record TelemetryEventArgs(string EventName, IDictionary Metrics); +internal sealed record TelemetryEventArgs(string EventName, IDictionary Metrics); -internal record ProcessInfoArgs(string Program, string? Args, string? WorkingDirectory, IDictionary? EnvironmentVariables); +internal sealed record ProcessInfoArgs(string Program, string? Args, string? WorkingDirectory, IDictionary? EnvironmentVariables); -internal record AttachDebuggerInfoArgs(int ProcessId); +internal sealed record AttachDebuggerInfoArgs(int ProcessId); -internal record class TestsAttachments(RunTestAttachment[] Attachments); +internal sealed record class TestsAttachments(RunTestAttachment[] Attachments); -internal record class RunTestAttachment(string? Uri, string? Producer, string? Type, string? DisplayName, string? Description); +internal sealed record class RunTestAttachment(string? Uri, string? Producer, string? Type, string? DisplayName, string? Description); diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs index 045d23d934..a9ad5812b3 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/SerializerUtilities.cs @@ -26,7 +26,7 @@ static SerializerUtilities() Serializers = []; Deserializers = []; - Serializers[typeof(object)] = new ObjectSerializer(o => new Dictionary()); + Serializers[typeof(object)] = new ObjectSerializer(_ => new Dictionary()); Serializers[typeof(KeyValuePair)] = new ObjectSerializer>(o => { Dictionary values = new() @@ -138,7 +138,7 @@ static SerializerUtilities() [JsonRpcStrings.Description] = res.Description, }); - Serializers[typeof(DiscoverResponseArgs)] = new ObjectSerializer(res => new Dictionary { }); + Serializers[typeof(DiscoverResponseArgs)] = new ObjectSerializer(_ => new Dictionary()); Serializers[typeof(RunResponseArgs)] = new ObjectSerializer(res => new Dictionary { @@ -218,12 +218,12 @@ static SerializerUtilities() #if NETCOREAPP properties[namedKvpStringProperty.Name] = namedKvpStringProperty.Pairs; #else - Jsonite.JsonArray collection = []; - foreach (KeyValuePair item in namedKvpStringProperty.Pairs) + JsonArray collection = []; + foreach ((string? key, string? value) in namedKvpStringProperty.Pairs) { - Jsonite.JsonObject o = new() + JsonObject o = new() { - { item.Key, item.Value }, + { key, value }, }; collection.Add(o); } @@ -419,12 +419,12 @@ static SerializerUtilities() #if NETCOREAPP values[JsonRpcStrings.EnvironmentVariables] = ev.EnvironmentVariables; #else - Jsonite.JsonArray collection = []; - foreach (KeyValuePair item in ev.EnvironmentVariables) + JsonArray collection = []; + foreach ((string? key, string? value) in ev.EnvironmentVariables) { - Jsonite.JsonObject o = new() + JsonObject o = new() { - { item.Key, item.Value }, + { key, value }, }; collection.Add(o); } @@ -624,17 +624,17 @@ static SerializerUtilities() string displayName = string.Empty; PropertyBag propertyBag = new(); - foreach (KeyValuePair p in properties) + foreach ((string? key, object? value) in properties) { - if (p.Key == JsonRpcStrings.Uid) + if (key == JsonRpcStrings.Uid) { - uid = p.Value as string ?? string.Empty; + uid = value as string ?? string.Empty; continue; } - if (p.Key == JsonRpcStrings.DisplayName) + if (key == JsonRpcStrings.DisplayName) { - displayName = p.Value as string ?? string.Empty; + displayName = value as string ?? string.Empty; continue; } } @@ -669,7 +669,7 @@ static SerializerUtilities() return new CancelRequestArgs(id); }); - Deserializers[typeof(ExitRequestArgs)] = new ObjectDeserializer(properties => new ExitRequestArgs()); + Deserializers[typeof(ExitRequestArgs)] = new ObjectDeserializer(_ => new ExitRequestArgs()); // Deserialize an error Deserializers[typeof(ErrorMessage)] = new ObjectDeserializer(properties => diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs index 763311a238..c0bc80727b 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModeManager.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.Services; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs index 46f26549e2..1ae054fcbd 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/ServerModePerCallOutputDevice.cs @@ -3,12 +3,50 @@ using Microsoft.Testing.Platform.Extensions.OutputDevice; using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Hosts; +using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Resources; +using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.ServerMode; -internal class ServerModePerCallOutputDevice : IPlatformOutputDevice +internal sealed class ServerModePerCallOutputDevice : IPlatformOutputDevice, IOutputDeviceDataProducer { + private readonly FileLoggerProvider? _fileLoggerProvider; + private readonly IStopPoliciesService _policiesService; + private readonly ConcurrentBag _messages = new(); + + private IServerTestHost? _serverTestHost; + + private static readonly string[] NewLineStrings = { "\r\n", "\n" }; + + public ServerModePerCallOutputDevice(FileLoggerProvider? fileLoggerProvider, IStopPoliciesService policiesService) + { + _fileLoggerProvider = fileLoggerProvider; + _policiesService = policiesService; + } + + internal async Task InitializeAsync(IServerTestHost serverTestHost) + { + // Server mode output device is basically used to send messages to Test Explorer. + // For that, it needs the ServerTestHost. + // However, the ServerTestHost is available later than the time we create the output device. + // So, the server mode output device is initially created early without the ServerTestHost, and + // it keeps any messages in a list. + // Later when ServerTestHost is created and is available, we initialize the server mode output device. + // The initialization will setup the right state for pushing to Test Explorer, and will push any existing + // messages to Test Explorer as well. + _serverTestHost = serverTestHost; + + foreach (ServerLogMessage message in _messages) + { + await LogAsync(message); + } + + _messages.Clear(); + } + public string Uid => nameof(ServerModePerCallOutputDevice); public string Version => AppVersion.DefaultSemVer; @@ -17,13 +55,108 @@ internal class ServerModePerCallOutputDevice : IPlatformOutputDevice public string Description => nameof(ServerModePerCallOutputDevice); - public Task DisplayAfterSessionEndRunAsync() => Task.CompletedTask; + public async Task DisplayAfterSessionEndRunAsync() + => await LogAsync(LogLevel.Trace, PlatformResources.FinishedTestSession, padding: null); + + public async Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) + { + switch (data) + { + case FormattedTextOutputDeviceData formattedTextOutputDeviceData: + await LogAsync(LogLevel.Information, formattedTextOutputDeviceData.Text, formattedTextOutputDeviceData.Padding); + break; + + case TextOutputDeviceData textOutputDeviceData: + await LogAsync(LogLevel.Information, textOutputDeviceData.Text, padding: null); + break; + + case WarningMessageOutputDeviceData warningData: + await LogAsync(LogLevel.Warning, warningData.Message, padding: null); + break; + + case ErrorMessageOutputDeviceData errorData: + await LogAsync(LogLevel.Error, errorData.Message, padding: null); + break; + + case ExceptionOutputDeviceData exceptionOutputDeviceData: + await LogAsync(LogLevel.Error, exceptionOutputDeviceData.Exception.ToString(), padding: null); + break; + } + } + + public async Task DisplayBannerAsync(string? bannerMessage) + { + if (bannerMessage is not null) + { + await LogAsync(LogLevel.Debug, bannerMessage, padding: null); + } + } + + public async Task DisplayBeforeSessionStartAsync() + { + if (_fileLoggerProvider is { FileLogger.FileName: { } logFileName }) + { + await LogAsync(LogLevel.Trace, string.Format(CultureInfo.InvariantCulture, PlatformResources.StartingTestSessionWithLogFilePath, logFileName), padding: null); + } + else + { + await LogAsync(LogLevel.Trace, PlatformResources.StartingTestSession, padding: null); + } + } + + public Task IsEnabledAsync() => Task.FromResult(true); + + private async Task LogAsync(LogLevel logLevel, string message, int? padding) + => await LogAsync(GetServerLogMessage(logLevel, message, padding)); + + private async Task LogAsync(ServerLogMessage message) + { + if (_serverTestHost is null) + { + _messages.Add(message); + } + else + { + await _serverTestHost.PushDataAsync(message); + } + } + + private static ServerLogMessage GetServerLogMessage(LogLevel logLevel, string message, int? padding) + => new(logLevel, GetIndentedMessage(message, padding)); + + private static string GetIndentedMessage(string message, int? padding) + { + int paddingValue = padding.GetValueOrDefault(); + if (paddingValue == 0) + { + return message; + } + + string indent = new(' ', paddingValue); - public Task DisplayAsync(IOutputDeviceDataProducer producer, IOutputDeviceData data) => Task.CompletedTask; + if (!message.Contains('\n')) + { + return indent + message; + } - public Task DisplayBannerAsync(string? bannerMessage) => Task.CompletedTask; + string[] lines = message.Split(NewLineStrings, StringSplitOptions.None); + StringBuilder builder = new(); + foreach (string line in lines) + { + builder.Append(indent); + builder.AppendLine(line); + } - public Task DisplayBeforeSessionStartAsync() => Task.CompletedTask; + return builder.ToString(); + } - public Task IsEnabledAsync() => Task.FromResult(false); + public async Task HandleProcessRoleAsync(TestProcessRole processRole) + { + if (processRole == TestProcessRole.TestHost) + { + await _policiesService.RegisterOnMaxFailedTestsCallbackAsync( + async (maxFailedTests, _) => await DisplayAsync( + this, new TextOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ReachedMaxFailedTestsMessage, maxFailedTests)))); + } + } } diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs index e092e70a4d..c93aabf846 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/StreamMessageHandler.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - #if NETCOREAPP using System.Buffers; #else diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs index 91278adca4..0c26a1c096 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/JsonRpc/TestNodeStateChangeAggregator.cs @@ -15,7 +15,7 @@ internal sealed partial class ServerTestHost /// This is done to minimize the number of RPC messages sent. /// /// The caller needs to ensure thread-safety. - internal class TestNodeStateChangeAggregator(Guid runId) + internal sealed class TestNodeStateChangeAggregator(Guid runId) { // Note: Currently there's no cascading node changes we need to deal with. private readonly List _stateChanges = []; diff --git a/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs b/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs index 62ce3e3186..9028714672 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Platform.Services; -internal class CTRLPlusCCancellationTokenSource : ITestApplicationCancellationTokenSource, IDisposable +internal sealed class CTRLPlusCCancellationTokenSource : ITestApplicationCancellationTokenSource, IDisposable { private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly ILogger? _logger; diff --git a/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs b/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs index 5053c18b19..b8346d8472 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/CurrentTestApplicationModuleInfo.cs @@ -1,13 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if NETCOREAPP -using System.Diagnostics.CodeAnalysis; -#endif - -using System.Reflection; -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform.Services; @@ -22,7 +15,7 @@ public bool IsCurrentTestApplicationHostDotnetMuxer { get { - string? processPath = GetProcessPath(_environment, _process, false); + string? processPath = GetProcessPath(_environment, _process); return processPath is not null && Path.GetFileNameWithoutExtension(processPath) == "dotnet"; } diff --git a/src/Platform/Microsoft.Testing.Platform/Services/IClientInfo.cs b/src/Platform/Microsoft.Testing.Platform/Services/IClientInfo.cs index b6a5d573b8..cb984ad86a 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/IClientInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/IClientInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Services; [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] diff --git a/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs b/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs index 2a90bf3569..92ba052ba9 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/IPlatformInformation.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - namespace Microsoft.Testing.Platform.Services; [Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")] diff --git a/src/Platform/Microsoft.Testing.Platform/Services/IStopPoliciesService.cs b/src/Platform/Microsoft.Testing.Platform/Services/IStopPoliciesService.cs new file mode 100644 index 0000000000..d82def8818 --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Services/IStopPoliciesService.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.Services; + +internal interface IStopPoliciesService +{ + bool IsMaxFailedTestsTriggered { get; } + + bool IsAbortTriggered { get; } + + Task RegisterOnMaxFailedTestsCallbackAsync(Func callback); + + Task RegisterOnAbortCallbackAsync(Func callback); + + Task ExecuteMaxFailedTestsCallbacksAsync(int maxFailedTests, CancellationToken cancellationToken); + + Task ExecuteAbortCallbacksAsync(); +} diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ITestApplicationProcessExitCode.cs b/src/Platform/Microsoft.Testing.Platform/Services/ITestApplicationProcessExitCode.cs index 1f8a11274b..880295b739 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ITestApplicationProcessExitCode.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ITestApplicationProcessExitCode.cs @@ -19,7 +19,7 @@ internal interface ITestApplicationProcessExitCode : IDataConsumer Statistics GetStatistics(); } -internal class Statistics +internal sealed class Statistics { public int TotalRanTests { get; set; } diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs index c39ef4aedd..556e89cc09 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Extensions.TestFramework; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Extensions.TestHostControllers; @@ -83,17 +81,7 @@ public IEnumerable GetServicesInternal( foreach (object serviceInstance in _services) { -#if !NETCOREAPP - if (serviceType.IsAssignableFrom(serviceInstance.GetType())) - { - yield return serviceInstance; - if (stopAtFirst) - { - yield break; - } - } -#else - if (serviceInstance.GetType().IsAssignableTo(serviceType)) + if (serviceType.IsInstanceOfType(serviceInstance)) { yield return serviceInstance; if (stopAtFirst) @@ -101,7 +89,6 @@ public IEnumerable GetServicesInternal( yield break; } } -#endif } } diff --git a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs index bb694a845b..f817c49e38 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/ServiceProviderExtensions.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.Testing.Platform.Capabilities.TestFramework; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; diff --git a/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs b/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs new file mode 100644 index 0000000000..e92049f5aa --- /dev/null +++ b/src/Platform/Microsoft.Testing.Platform/Services/StopPoliciesService.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Helpers; + +namespace Microsoft.Testing.Platform.Services; + +internal sealed class StopPoliciesService : IStopPoliciesService +{ + private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource; + + private BlockingCollection>? _maxFailedTestsCallbacks; + private BlockingCollection>? _abortCallbacks; + private int _lastMaxFailedTests; + + public StopPoliciesService(ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource) + { + _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; + +#pragma warning disable VSTHRD101 // Avoid unsupported async delegates + // Note: If cancellation already requested, Register will still invoke the callback. + testApplicationCancellationTokenSource.CancellationToken.Register(async () => await ExecuteAbortCallbacksAsync()); +#pragma warning restore VSTHRD101 // Avoid unsupported async delegates + } + + internal TestProcessRole? ProcessRole { get; set; } + + public bool IsMaxFailedTestsTriggered { get; private set; } + + public bool IsAbortTriggered { get; private set; } + + private static void RegisterCallback(ref BlockingCollection? callbacks, T callback) + => (callbacks ??= new()).Add(callback); + + public async Task ExecuteMaxFailedTestsCallbacksAsync(int maxFailedTests, CancellationToken cancellationToken) + { + _lastMaxFailedTests = maxFailedTests; + IsMaxFailedTestsTriggered = true; + if (_maxFailedTestsCallbacks is null) + { + return; + } + + foreach (Func callback in _maxFailedTestsCallbacks) + { + // For now, we are fine if the callback crashed us. It shouldn't happen for our + // current usage anyway and the APIs around this are all internal for now. + await callback.Invoke(maxFailedTests, cancellationToken); + } + } + + public async Task ExecuteAbortCallbacksAsync() + { + IsAbortTriggered = true; + + if (_abortCallbacks is null) + { + return; + } + + foreach (Func callback in _abortCallbacks) + { + // For now, we are fine if the callback crashed us. It shouldn't happen for our + // current usage anyway and the APIs around this are all internal for now. + await callback.Invoke(); + } + } + + public async Task RegisterOnMaxFailedTestsCallbackAsync(Func callback) + { + if (ProcessRole != TestProcessRole.TestHost) + { + throw ApplicationStateGuard.Unreachable(); + } + + if (IsMaxFailedTestsTriggered) + { + await callback(_lastMaxFailedTests, _testApplicationCancellationTokenSource.CancellationToken); + } + + RegisterCallback(ref _maxFailedTestsCallbacks, callback); + } + + public async Task RegisterOnAbortCallbackAsync(Func callback) + { + if (IsAbortTriggered) + { + await callback(); + } + + RegisterCallback(ref _abortCallbacks, callback); + } +} diff --git a/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs b/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs index e01fad11fe..a8b5149079 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.OutputDevice; @@ -13,20 +11,28 @@ namespace Microsoft.Testing.Platform.Services; -internal sealed class TestApplicationResult( - IOutputDevice outputService, - ITestApplicationCancellationTokenSource testApplicationCancellationTokenSource, - ICommandLineOptions commandLineOptions, - IEnvironment environment) : ITestApplicationProcessExitCode, IOutputDeviceDataProducer +internal sealed class TestApplicationResult : ITestApplicationProcessExitCode, IOutputDeviceDataProducer { - private readonly IOutputDevice _outputService = outputService; - private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource; - private readonly ICommandLineOptions _commandLineOptions = commandLineOptions; - private readonly IEnvironment _environment = environment; + private readonly IOutputDevice _outputService; + private readonly ICommandLineOptions _commandLineOptions; + private readonly IEnvironment _environment; + private readonly IStopPoliciesService _policiesService; private readonly List _failedTests = []; private int _totalRanTests; private bool _testAdapterTestSessionFailure; + public TestApplicationResult( + IOutputDevice outputService, + ICommandLineOptions commandLineOptions, + IEnvironment environment, + IStopPoliciesService policiesService) + { + _outputService = outputService; + _commandLineOptions = commandLineOptions; + _environment = environment; + _policiesService = policiesService; + } + /// public string Uid { get; } = nameof(TestApplicationResult); @@ -81,9 +87,10 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo public int GetProcessExitCode() { int exitCode = ExitCodes.Success; + exitCode = exitCode == ExitCodes.Success && _policiesService.IsMaxFailedTestsTriggered ? ExitCodes.TestExecutionStoppedForMaxFailedTests : exitCode; exitCode = exitCode == ExitCodes.Success && _testAdapterTestSessionFailure ? ExitCodes.TestAdapterTestSessionFailure : exitCode; exitCode = exitCode == ExitCodes.Success && _failedTests.Count > 0 ? ExitCodes.AtLeastOneTestFailed : exitCode; - exitCode = exitCode == ExitCodes.Success && _testApplicationCancellationTokenSource.CancellationToken.IsCancellationRequested ? ExitCodes.TestSessionAborted : exitCode; + exitCode = exitCode == ExitCodes.Success && _policiesService.IsAbortTriggered ? ExitCodes.TestSessionAborted : exitCode; // If the user has specified the VSTestAdapterMode option, then we don't want to return a non-zero exit code if no tests ran. if (!_commandLineOptions.IsOptionSet(PlatformCommandLineProvider.VSTestAdapterModeOptionKey)) @@ -121,7 +128,7 @@ public async Task SetTestAdapterTestSessionFailureAsync(string errorMessage) { TestAdapterTestSessionFailureErrorMessage = errorMessage; _testAdapterTestSessionFailure = true; - await _outputService.DisplayAsync(this, FormattedTextOutputDeviceDataBuilder.CreateRedConsoleColorText(errorMessage)); + await _outputService.DisplayAsync(this, new ErrorMessageOutputDeviceData(errorMessage)); } public Statistics GetStatistics() diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs index 5d26c4fd12..ff91fab992 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETCOREAPP -using System.Text; using System.Text.Json; #else using Jsonite; diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryProperties.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryProperties.cs index 3de0bc887a..c00cf6f6c7 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryProperties.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/TelemetryProperties.cs @@ -5,9 +5,9 @@ namespace Microsoft.Testing.Platform.Telemetry; internal static class TelemetryProperties { - public const string VersionPropertyName = $"telemetry version"; + public const string VersionPropertyName = "telemetry version"; public const string SessionId = "session id"; - public const string ReporterIdPropertyName = $"reporter id"; + public const string ReporterIdPropertyName = "reporter id"; public const string IsCIPropertyName = "is ci"; public const string VersionValue = "19"; diff --git a/src/Platform/Microsoft.Testing.Platform/TestFramework/ExecuteRequestContext.cs b/src/Platform/Microsoft.Testing.Platform/TestFramework/ExecuteRequestContext.cs index d990139d3c..2a38535230 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestFramework/ExecuteRequestContext.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestFramework/ExecuteRequestContext.cs @@ -3,8 +3,6 @@ #pragma warning disable RS0016 // Add public types and members to the declared API -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.Messages; using Microsoft.Testing.Platform.Requests; diff --git a/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs b/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs index 6027b14792..d122e4737e 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestFramework/TestFrameworkManager.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Internal.Framework; -internal class TestFrameworkManager( +internal sealed class TestFrameworkManager( Func testFrameworkFactory, Func testFrameworkCapabilitiesFactory) : ITestFrameworkManager diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs index c591311896..684e3cfeec 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/TestHostManager.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHost/TestSessionLifetimeHandlersContainer.cs b/src/Platform/Microsoft.Testing.Platform/TestHost/TestSessionLifetimeHandlersContainer.cs index 667bc8d76d..1526535287 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHost/TestSessionLifetimeHandlersContainer.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHost/TestSessionLifetimeHandlersContainer.cs @@ -3,7 +3,7 @@ namespace Microsoft.Testing.Platform.Extensions.TestHost; -internal class TestSessionLifetimeHandlersContainer +internal sealed class TestSessionLifetimeHandlersContainer { public TestSessionLifetimeHandlersContainer(IEnumerable testSessionLifetimeHandlers) => TestSessionLifetimeHandlers = testSessionLifetimeHandlers; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/EnvironmentVariables.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/EnvironmentVariables.cs index ca8aa730b4..4ca6397932 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/EnvironmentVariables.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/EnvironmentVariables.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.Testing.Platform.Extensions.TestHostControllers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Resources; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/IReadOnlyEnvironmentVariables.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/IReadOnlyEnvironmentVariables.cs index 12b9fadc1b..99f816a186 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/IReadOnlyEnvironmentVariables.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/IReadOnlyEnvironmentVariables.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.Testing.Platform.Extensions.TestHostControllers; /// diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs index 5f95d7325b..6bad942616 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/PassiveNodeDataConsumer.cs @@ -8,7 +8,7 @@ namespace Microsoft.Testing.Platform.TestHostControllers; -internal class PassiveNodeDataConsumer : IDataConsumer, IDisposable +internal sealed class PassiveNodeDataConsumer : IDataConsumer, IDisposable { private const string FileType = "file"; private readonly PassiveNode? _passiveNode; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs index 8e7021df5c..6e4e6bac6e 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/SystemEnvironmentVariableProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; - using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.TestHostControllers; using Microsoft.Testing.Platform.Helpers; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllerInfo.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllerInfo.cs index d5acd12d96..74df6d11ca 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllerInfo.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllerInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.CommandLine; namespace Microsoft.Testing.Platform.TestHostControllers; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs index 5635675e1b..63f76ad980 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostControllersManager.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.TestHost; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostProcessInformation.cs b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostProcessInformation.cs index 7006bd8c44..c67c3cfa0b 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostProcessInformation.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostControllers/TestHostProcessInformation.cs @@ -6,7 +6,7 @@ namespace Microsoft.Testing.Platform.TestHostControllers; -internal class TestHostProcessInformation : ITestHostProcessInformation +internal sealed class TestHostProcessInformation : ITestHostProcessInformation { private readonly int? _exitCode; private readonly bool? _hasExitedGracefully; diff --git a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs index 72d36b4723..d6f7a628bf 100644 --- a/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs +++ b/src/Platform/Microsoft.Testing.Platform/TestHostOrcherstrator/TestHostOrchestratorManager.cs @@ -1,14 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.Extensions.TestHostOrchestrator; -internal class TestHostOrchestratorManager : ITestHostOrchestratorManager +internal sealed class TestHostOrchestratorManager : ITestHostOrchestratorManager { private List>? _factories; diff --git a/src/TestFramework/TestFramework.Extensions/AppModel.cs b/src/TestFramework/TestFramework.Extensions/AppModel.cs index 2b8f49683b..9efd65874c 100644 --- a/src/TestFramework/TestFramework.Extensions/AppModel.cs +++ b/src/TestFramework/TestFramework.Extensions/AppModel.cs @@ -2,10 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if WIN_UI -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Text; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AppContainer; /// diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs index c4bc2f1b17..9aa48c7e3a 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/UWP_UITestMethodAttribute.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if WINDOWS_UWP -using System.Runtime.CompilerServices; namespace Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs index a450e0b878..e440b2f31d 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/WinUITestTargetAttribute.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if WIN_UI -using System.Globalization; namespace Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; diff --git a/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs b/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs index ec26f36df5..88d9835a0d 100644 --- a/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs +++ b/src/TestFramework/TestFramework.Extensions/Attributes/WinUI_UITestMethodAttribute.cs @@ -2,9 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if WIN_UI -using System.Reflection; -using System.Runtime.CompilerServices; - using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; diff --git a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElementCollection.cs b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElementCollection.cs index b2bd44da6f..fa3293b942 100644 --- a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElementCollection.cs +++ b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/DataSourceElementCollection.cs @@ -4,7 +4,6 @@ #if NETFRAMEWORK using System.Configuration; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs index 91fde548cd..602e795cfc 100644 --- a/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs +++ b/src/TestFramework/TestFramework.Extensions/ConfigurationSettings/TestConfigurationSection.cs @@ -24,7 +24,7 @@ public sealed class TestConfigurationSection : ConfigurationSection /// Gets the collection of properties. /// /// - /// The of properties for the element. + /// The of properties for the element. /// protected override ConfigurationPropertyCollection Properties { get; } = [DataSourcesValue]; } diff --git a/src/TestFramework/TestFramework.Extensions/Friends.cs b/src/TestFramework/TestFramework.Extensions/Friends.cs index a6f2073344..57566ec3fd 100644 --- a/src/TestFramework/TestFramework.Extensions/Friends.cs +++ b/src/TestFramework/TestFramework.Extensions/Friends.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // Friend assemblies -using System.Runtime.CompilerServices; - [assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("MSTestAdapter.PlatformServices.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.Linux.nuspec b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec similarity index 72% rename from src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.Linux.nuspec rename to src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec index cda91f2b76..db5be825a8 100644 --- a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.Linux.nuspec +++ b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.NonWindows.nuspec @@ -3,11 +3,24 @@ $CommonMetadataElements$ - - - - - + + + + + + + + + + + + + + + + + + PACKAGE.md @@ -52,6 +65,14 @@ + + + + + + + + diff --git a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec index 3c7f9c9f87..39c53e3c29 100644 --- a/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec +++ b/src/TestFramework/TestFramework.Extensions/MSTest.TestFramework.nuspec @@ -3,13 +3,30 @@ $CommonMetadataElements$ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + PACKAGE.md @@ -70,6 +87,14 @@ + + + + + + + + diff --git a/src/TestFramework/TestFramework.Extensions/MessageLevel.cs b/src/TestFramework/TestFramework.Extensions/MessageLevel.cs new file mode 100644 index 0000000000..064b617131 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/MessageLevel.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Specifies the severity level of messages displayed using the API. +/// +public enum MessageLevel +{ + /// + /// The message will be displayed as informational, typically used for general updates or non-critical messages. + /// + Informational, + + /// + /// The message will be displayed as a warning, indicating a potential issue or something requiring attention. + /// + Warning, + + /// + /// The message will be displayed as an error, representing a significant issue or failure. + /// + Error, +} diff --git a/src/TestFramework/TestFramework.Extensions/PrivateObject.cs b/src/TestFramework/TestFramework.Extensions/PrivateObject.cs index 48a7b255eb..0fc6b2fb91 100644 --- a/src/TestFramework/TestFramework.Extensions/PrivateObject.cs +++ b/src/TestFramework/TestFramework.Extensions/PrivateObject.cs @@ -2,9 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETFRAMEWORK -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -73,7 +70,7 @@ public PrivateObject(string assemblyName, string typeName, params object?[]? arg /// /// Name of the assembly. /// fully qualified name. - /// An array of objects representing the number, order, and type of the parameters for the constructor to get. + /// An array of objects representing the number, order, and type of the parameters for the constructor to get. /// Arguments to pass to the constructor. public PrivateObject(string assemblyName, string typeName, Type[]? parameterTypes, object?[]? args) : this(Type.GetType(string.Format(CultureInfo.InvariantCulture, "{0}, {1}", typeName, assemblyName), false), parameterTypes, args) @@ -97,7 +94,7 @@ public PrivateObject(Type type, params object?[]? args) /// specified type. /// /// type of the object to create. - /// An array of objects representing the number, order, and type of the parameters for the constructor to get. + /// An array of objects representing the number, order, and type of the parameters for the constructor to get. /// Arguments to pass to the constructor. public PrivateObject(Type type, Type[]? parameterTypes, object?[]? args) { @@ -241,7 +238,7 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// An array of objects representing the number, order, and type of the parameters for the method to get. + /// An array of objects representing the number, order, and type of the parameters for the method to get. /// Arguments to pass to the member to invoke. /// Result of method call. public object? Invoke(string name, Type[] parameterTypes, object?[]? args) => Invoke(name, parameterTypes, args, CultureInfo.InvariantCulture); @@ -250,7 +247,7 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// An array of objects representing the number, order, and type of the parameters for the method to get. + /// An array of objects representing the number, order, and type of the parameters for the method to get. /// Arguments to pass to the member to invoke. /// An array of types corresponding to the types of the generic arguments. /// Result of method call. @@ -269,7 +266,7 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// An array of objects representing the number, order, and type of the parameters for the method to get. + /// An array of objects representing the number, order, and type of the parameters for the method to get. /// Arguments to pass to the member to invoke. /// Culture info. /// Result of method call. @@ -279,7 +276,7 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// Arguments to pass to the member to invoke. /// Result of method call. public object? Invoke(string name, BindingFlags bindingFlags, params object?[]? args) => Invoke(name, bindingFlags, null, args, CultureInfo.InvariantCulture); @@ -288,8 +285,8 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// A bitmask comprised of one or more that specify how the search is conducted. - /// An array of objects representing the number, order, and type of the parameters for the method to get. + /// A bitmask comprised of one or more that specify how the search is conducted. + /// An array of objects representing the number, order, and type of the parameters for the method to get. /// Arguments to pass to the member to invoke. /// Result of method call. public object? Invoke(string name, BindingFlags bindingFlags, Type[] parameterTypes, object?[]? args) => Invoke(name, bindingFlags, parameterTypes, args, CultureInfo.InvariantCulture); @@ -298,7 +295,7 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// Arguments to pass to the member to invoke. /// Culture info. /// Result of method call. @@ -308,8 +305,8 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// A bitmask comprised of one or more that specify how the search is conducted. - /// An array of objects representing the number, order, and type of the parameters for the method to get. + /// A bitmask comprised of one or more that specify how the search is conducted. + /// An array of objects representing the number, order, and type of the parameters for the method to get. /// Arguments to pass to the member to invoke. /// Culture info. /// Result of method call. @@ -319,8 +316,8 @@ public override bool Equals(object? obj) /// Invokes the specified method. /// /// Name of the method. - /// A bitmask comprised of one or more that specify how the search is conducted. - /// An array of objects representing the number, order, and type of the parameters for the method to get. + /// A bitmask comprised of one or more that specify how the search is conducted. + /// An array of objects representing the number, order, and type of the parameters for the method to get. /// Arguments to pass to the member to invoke. /// Culture info. /// An array of types corresponding to the types of the generic arguments. @@ -408,7 +405,7 @@ public void SetArrayElement(string name, object value, params int[] indices) /// Gets the array element using array of subscripts for each dimension. /// /// Name of the member. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// the indices of array. /// An array of elements. public object GetArrayElement(string name, BindingFlags bindingFlags, params int[] indices) @@ -423,7 +420,7 @@ public object GetArrayElement(string name, BindingFlags bindingFlags, params int /// Sets the array element using array of subscripts for each dimension. /// /// Name of the member. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// Value to set. /// the indices of array. public void SetArrayElement(string name, BindingFlags bindingFlags, object value, params int[] indices) @@ -460,7 +457,7 @@ public void SetField(string name, object value) /// Gets the field. /// /// Name of the field. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// The field. public object? GetField(string name, BindingFlags bindingFlags) { @@ -472,7 +469,7 @@ public void SetField(string name, object value) /// Sets the field. /// /// Name of the field. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// value to set. public void SetField(string name, BindingFlags bindingFlags, object? value) { @@ -506,7 +503,7 @@ public void SetFieldOrProperty(string name, object value) /// Gets the field or property. /// /// Name of the field or property. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// The field or property. public object? GetFieldOrProperty(string name, BindingFlags bindingFlags) { @@ -518,7 +515,7 @@ public void SetFieldOrProperty(string name, object value) /// Sets the field or property. /// /// Name of the field or property. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// value to set. public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? value) { @@ -538,7 +535,7 @@ public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? v /// Gets the property. /// /// Name of the property. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// Arguments to pass to the member to invoke. /// The property. public object? GetProperty(string name, Type[]? parameterTypes, object?[]? args) => GetProperty(name, BindToEveryThing, parameterTypes, args); @@ -555,7 +552,7 @@ public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? v /// Set the property. /// /// Name of the property. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// value to set. /// Arguments to pass to the member to invoke. public void SetProperty(string name, Type[]? parameterTypes, object? value, object?[]? args) => SetProperty(name, BindToEveryThing, value, parameterTypes, args); @@ -564,7 +561,7 @@ public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? v /// Gets the property. /// /// Name of the property. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// Arguments to pass to the member to invoke. /// The property. public object? GetProperty(string name, BindingFlags bindingFlags, params object?[]? args) => GetProperty(name, bindingFlags, null, args); @@ -573,8 +570,8 @@ public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? v /// Gets the property. /// /// Name of the property. - /// A bitmask comprised of one or more that specify how the search is conducted. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// A bitmask comprised of one or more that specify how the search is conducted. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// Arguments to pass to the member to invoke. /// The property. public object? GetProperty(string name, BindingFlags bindingFlags, Type[]? parameterTypes, object?[]? args) @@ -594,7 +591,7 @@ public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? v /// Sets the property. /// /// Name of the property. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// value to set. /// Arguments to pass to the member to invoke. public void SetProperty(string name, BindingFlags bindingFlags, object value, params object?[]? args) => SetProperty(name, bindingFlags, value, null, args); @@ -603,9 +600,9 @@ public void SetFieldOrProperty(string name, BindingFlags bindingFlags, object? v /// Sets the property. /// /// Name of the property. - /// A bitmask comprised of one or more that specify how the search is conducted. + /// A bitmask comprised of one or more that specify how the search is conducted. /// value to set. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// Arguments to pass to the member to invoke. public void SetProperty(string name, BindingFlags bindingFlags, object? value, Type[]? parameterTypes, object?[]? args) { diff --git a/src/TestFramework/TestFramework.Extensions/PrivateType.cs b/src/TestFramework/TestFramework.Extensions/PrivateType.cs index c9733d65cb..0b696c7ca3 100644 --- a/src/TestFramework/TestFramework.Extensions/PrivateType.cs +++ b/src/TestFramework/TestFramework.Extensions/PrivateType.cs @@ -3,9 +3,6 @@ #if NETFRAMEWORK -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// @@ -59,7 +56,7 @@ public PrivateType(Type type) => /// Invokes static member. /// /// Name of the member to InvokeHelper. - /// An array of objects representing the number, order, and type of the parameters for the method to invoke. + /// An array of objects representing the number, order, and type of the parameters for the method to invoke. /// Arguments to the invocation. /// Result of invocation. public object InvokeStatic(string name, Type[]? parameterTypes, object?[]? args) => InvokeStatic(name, parameterTypes, args, CultureInfo.InvariantCulture); @@ -68,7 +65,7 @@ public PrivateType(Type type) => /// Invokes static member. /// /// Name of the member to InvokeHelper. - /// An array of objects representing the number, order, and type of the parameters for the method to invoke. + /// An array of objects representing the number, order, and type of the parameters for the method to invoke. /// Arguments to the invocation. /// An array of types corresponding to the types of the generic arguments. /// Result of invocation. @@ -87,7 +84,7 @@ public PrivateType(Type type) => /// Invokes the static method. /// /// Name of the member. - /// An array of objects representing the number, order, and type of the parameters for the method to invoke. + /// An array of objects representing the number, order, and type of the parameters for the method to invoke. /// Arguments to the invocation. /// Culture info. /// Result of invocation. @@ -107,7 +104,7 @@ public PrivateType(Type type) => /// /// Name of the member. /// Additional invocation attributes. - /// An array of objects representing the number, order, and type of the parameters for the method to invoke. + /// An array of objects representing the number, order, and type of the parameters for the method to invoke. /// Arguments to the invocation. /// Result of invocation. public object InvokeStatic(string name, BindingFlags bindingFlags, Type[]? parameterTypes, object?[]? args) => InvokeStatic(name, bindingFlags, parameterTypes, args, CultureInfo.InvariantCulture); @@ -127,7 +124,7 @@ public PrivateType(Type type) => /// /// Name of the member. /// Additional invocation attributes. - /// /// An array of objects representing the number, order, and type of the parameters for the method to invoke. + /// /// An array of objects representing the number, order, and type of the parameters for the method to invoke. /// Arguments to the invocation. /// Culture. /// Result of invocation. @@ -138,7 +135,7 @@ public PrivateType(Type type) => /// /// Name of the member. /// Additional invocation attributes. - /// /// An array of objects representing the number, order, and type of the parameters for the method to invoke. + /// /// An array of objects representing the number, order, and type of the parameters for the method to invoke. /// Arguments to the invocation. /// Culture. /// An array of types corresponding to the types of the generic arguments. @@ -354,7 +351,7 @@ public void SetStaticFieldOrProperty(string name, BindingFlags bindingFlags, obj /// /// Name of the property. /// Value to be set to field or property. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// Arguments to pass to the member to invoke. public void SetStaticProperty(string name, object value, Type[]? parameterTypes, object?[]? args) => SetStaticProperty(name, BindingFlags.SetProperty, value, parameterTypes, args); @@ -372,7 +369,7 @@ public void SetStaticFieldOrProperty(string name, BindingFlags bindingFlags, obj /// /// Name of the property. /// Additional invocation attributes. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// Arguments to pass to the member to invoke. /// The static property. public object GetStaticProperty(string name, BindingFlags bindingFlags, Type[]? parameterTypes, object?[]? args) @@ -403,7 +400,7 @@ public object GetStaticProperty(string name, BindingFlags bindingFlags, Type[]? /// Name of the property. /// Additional invocation attributes. /// Value to be set to field or property. - /// An array of objects representing the number, order, and type of the parameters for the indexed property. + /// An array of objects representing the number, order, and type of the parameters for the indexed property. /// Arguments to pass to the member to invoke. public void SetStaticProperty(string name, BindingFlags bindingFlags, object value, Type[]? parameterTypes, object?[]? args) { diff --git a/src/TestFramework/TestFramework.Extensions/Properties/AssemblyInfo.cs b/src/TestFramework/TestFramework.Extensions/Properties/AssemblyInfo.cs index 9d2e4d8f12..e79fe7763d 100644 --- a/src/TestFramework/TestFramework.Extensions/Properties/AssemblyInfo.cs +++ b/src/TestFramework/TestFramework.Extensions/Properties/AssemblyInfo.cs @@ -1,12 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if !WIN_UI -using System.Runtime.InteropServices; -#else -using System.Runtime.Versioning; -#endif - #if !WIN_UI [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt index 171e5fe5e8..a6d5027834 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.AddResultFile(string! fileName) -> void abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.Properties.get -> System.Collections.IDictionary! abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.Write(string! format, params object?[]! args) -> void @@ -7,10 +7,16 @@ abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.WriteLine(stri abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.WriteLine(string? message) -> void Microsoft.VisualStudio.TestTools.UnitTesting.TestContext Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestContext() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestData.get -> object?[]? +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestData.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDisplayName.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDisplayName.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestException.get -> System.Exception? +Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestException.set -> void virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CancellationTokenSource.get -> System.Threading.CancellationTokenSource! virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CancellationTokenSource.set -> void virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.CurrentTestOutcome.get -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.FullyQualifiedTestClassName.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.ManagedMethod.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.ManagedType.get -> string? -virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestName.get -> string? \ No newline at end of file +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestName.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt index c45dd334cb..5b7e887699 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/PublicAPI.Unshipped.txt @@ -1,7 +1,6 @@ -#nullable enable -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDisplayName.get -> string? -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDisplayName.set -> void -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestData.get -> object?[]? -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestData.set -> void -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestException.get -> System.Exception? -Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestException.set -> void +#nullable enable +abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DisplayMessage(Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel messageLevel, string! message) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel +Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel.Error = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel +Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel.Informational = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel +Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel.Warning = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Shipped.txt index db3836f496..4858a49c17 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Shipped.txt @@ -21,17 +21,17 @@ Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceElementCollection.this[in Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceElementCollection.this[int index].set -> void Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceElementCollection.this[string! name].get -> Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceElement! Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetArrayElement(string! name, params int[]! indices) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetArrayElement(string! name, System.Reflection.BindingFlags bindingFlags, params int[]! indices) -> object! -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetField(string! name) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetField(string! name, System.Reflection.BindingFlags bindingFlags) -> object? -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetFieldOrProperty(string! name) -> object? +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetField(string! name) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetFieldOrProperty(string! name, System.Reflection.BindingFlags bindingFlags) -> object? +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetFieldOrProperty(string! name) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetProperty(string! name, params object?[]? args) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetProperty(string! name, System.Reflection.BindingFlags bindingFlags, params object?[]? args) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.GetProperty(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args) -> object? @@ -43,12 +43,12 @@ Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]! parameterTypes, object?[]? args) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo! culture) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo? culture, System.Type![]? typeArguments) -> object? -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Type![]! parameterTypes, object?[]? args) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Type![]! parameterTypes, object?[]? args, System.Type![]! typeArguments) -> object? +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Type![]! parameterTypes, object?[]? args) -> object? Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Invoke(string! name, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo! culture) -> object? -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(object! obj) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(object! obj, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType! type) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(object! obj, string! memberToAccess) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(object! obj) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(string! assemblyName, string! typeName, params object?[]? args) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(string! assemblyName, string! typeName, System.Type![]? parameterTypes, object?[]? args) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.PrivateObject(System.Type! type, params object?[]? args) -> void @@ -69,10 +69,10 @@ Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject.Target.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticArrayElement(string! name, params int[]! indices) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticArrayElement(string! name, System.Reflection.BindingFlags bindingFlags, params int[]! indices) -> object! -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticField(string! name) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticField(string! name, System.Reflection.BindingFlags bindingFlags) -> object! -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticFieldOrProperty(string! name) -> object! +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticField(string! name) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticFieldOrProperty(string! name, System.Reflection.BindingFlags bindingFlags) -> object! +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticFieldOrProperty(string! name) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticProperty(string! name, params object?[]? args) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticProperty(string! name, System.Reflection.BindingFlags bindingFlags, params object?[]? args) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.GetStaticProperty(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args) -> object! @@ -80,12 +80,12 @@ Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! na Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, params object?[]? args) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, object?[]? args, System.Globalization.CultureInfo? culture) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, params object?[]? args) -> object! -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args) -> object! -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo? culture) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo? culture, System.Type![]? typeArguments) -> object! -Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Type![]? parameterTypes, object?[]? args) -> object! +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo? culture) -> object! +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Reflection.BindingFlags bindingFlags, System.Type![]? parameterTypes, object?[]? args) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Type![]? parameterTypes, object?[]? args, System.Globalization.CultureInfo? culture) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Type![]? parameterTypes, object?[]? args, System.Type![]! typeArguments) -> object! +Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.InvokeStatic(string! name, System.Type![]? parameterTypes, object?[]? args) -> object! Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.PrivateType(string! assemblyName, string! typeName) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.PrivateType(System.Type! type) -> void Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType.ReferencedType.get -> System.Type! @@ -113,4 +113,4 @@ virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDir.get -> virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestLogsDir.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestResultsDirectory.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunDirectory.get -> string? -virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? \ No newline at end of file +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net462/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt index e713c74671..e9b28f1b28 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Shipped.txt @@ -7,4 +7,4 @@ Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.WinUITestTargetAttribu Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.WinUITestTargetAttribute.WinUITestTargetAttribute(System.Type! applicationType) -> void override Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.Execute(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod! testMethod) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! static Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.DispatcherQueue.get -> Microsoft.UI.Dispatching.DispatcherQueue? -static Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.DispatcherQueue.set -> void \ No newline at end of file +static Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.DispatcherQueue.set -> void diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0-windows10.0.18362.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Shipped.txt index f9d5205b2e..3b3aac1df0 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Shipped.txt @@ -1,7 +1,7 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DeploymentDirectory.get -> string? @@ -11,4 +11,4 @@ virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDir.get -> virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestLogsDir.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestResultsDirectory.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunDirectory.get -> string? -virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? \ No newline at end of file +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net6.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Shipped.txt index f9d5205b2e..3b3aac1df0 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Shipped.txt @@ -1,7 +1,7 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DeploymentDirectory.get -> string? @@ -11,4 +11,4 @@ virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDir.get -> virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestLogsDir.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestResultsDirectory.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunDirectory.get -> string? -virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? \ No newline at end of file +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net7.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Shipped.txt index f9d5205b2e..3b3aac1df0 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -1,7 +1,7 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DeploymentDirectory.get -> string? @@ -11,4 +11,4 @@ virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDir.get -> virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestLogsDir.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestResultsDirectory.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunDirectory.get -> string? -virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? \ No newline at end of file +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net9.0/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net9.0/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..3b3aac1df0 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net9.0/PublicAPI.Shipped.txt @@ -0,0 +1,14 @@ +#nullable enable +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DeploymentDirectory.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.ResultsDirectory.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDeploymentDir.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDir.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestLogsDir.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestResultsDirectory.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunDirectory.get -> string? +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/net9.0/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/net9.0/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/net9.0/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt index e6913a4006..3b3aac1df0 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Shipped.txt @@ -1,7 +1,7 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DeploymentDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/netcoreapp3.1/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt index f9d5205b2e..3b3aac1df0 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -1,7 +1,7 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute -Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path, string? outputDirectory) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.DeploymentItemAttribute(string? path) -> void Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.OutputDirectory.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DeploymentItemAttribute.Path.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DeploymentDirectory.get -> string? @@ -11,4 +11,4 @@ virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestDir.get -> virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestLogsDir.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestResultsDirectory.get -> string? virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunDirectory.get -> string? -virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? \ No newline at end of file +virtual Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.TestRunResultsDirectory.get -> string? diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt index f78acd369f..63494b4a7a 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.UITestMethodAttribute() -> void -override Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.Execute(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod! testMethod) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! \ No newline at end of file +override Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer.UITestMethodAttribute.Execute(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod! testMethod) -> Microsoft.VisualStudio.TestTools.UnitTesting.TestResult![]! diff --git a/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt index 815c92006a..7dc5c58110 100644 --- a/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework.Extensions/PublicAPI/uap10.0.16299/PublicAPI.Unshipped.txt @@ -1 +1 @@ -#nullable enable \ No newline at end of file +#nullable enable diff --git a/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs b/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs index 89a1ed7c31..ceca146b91 100644 --- a/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs +++ b/src/TestFramework/TestFramework.Extensions/RuntimeTypeHelper.cs @@ -2,15 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETFRAMEWORK -using System.Diagnostics.CodeAnalysis; -using System.Reflection; namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Provides method signature discovery for generic methods. /// -internal class RuntimeTypeHelper +internal sealed class RuntimeTypeHelper { /// /// Compares the method signatures of these two methods. diff --git a/src/TestFramework/TestFramework.Extensions/TestContext.cs b/src/TestFramework/TestFramework.Extensions/TestContext.cs index 95f08dcde9..feb96117db 100644 --- a/src/TestFramework/TestFramework.Extensions/TestContext.cs +++ b/src/TestFramework/TestFramework.Extensions/TestContext.cs @@ -1,13 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; #if NETFRAMEWORK using System.Data; using System.Data.Common; #endif -using System.Diagnostics; -using System.Globalization; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -51,7 +48,7 @@ public abstract class TestContext /// /// Gets or sets the cancellation token source. This token source is canceled when test times out. Also when explicitly canceled the test will be aborted. /// - public virtual CancellationTokenSource CancellationTokenSource { get; protected set; } = new(); + public virtual CancellationTokenSource CancellationTokenSource { get; protected internal set; } = new(); public object?[]? TestData { get; protected set; } @@ -201,12 +198,14 @@ public abstract class TestContext /// the arguments. public abstract void WriteLine(string format, params object?[] args); + public abstract void DisplayMessage(MessageLevel messageLevel, string message); + private T? GetProperty(string name) where T : class { DebugEx.Assert(Properties is not null, "Properties is null"); #if WINDOWS_UWP || WIN_UI - if (!((System.Collections.Generic.IDictionary)Properties).TryGetValue(name, out object? propertyValue)) + if (!((IDictionary)Properties).TryGetValue(name, out object? propertyValue)) { return null; } diff --git a/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj b/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj index 5dcc9df4a5..7aadfd3436 100644 --- a/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj +++ b/src/TestFramework/TestFramework.Extensions/TestFramework.Extensions.csproj @@ -13,7 +13,7 @@ true MSTest.TestFramework.nuspec - MSTest.TestFramework.Linux.nuspec + MSTest.TestFramework.NonWindows.nuspec $(OutputPath) MSTest.TestFramework MSTest TestFramework Unittest MSTestV2 Microsoft Test Testing TDD Framework @@ -81,6 +81,10 @@ + + + + PreserveNewest diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs index b8b2831400..3207dae62c 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; +using System.ComponentModel; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,10 +12,360 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed partial class Assert { + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertAreEqualInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly object? _expected; + private readonly object? _actual; + + public AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend) + { + _expected = expected!; + shouldAppend = AreEqualFailing(expected, actual); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _expected = expected; + _actual = actual; + } + } + + public AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, IEqualityComparer? comparer, out bool shouldAppend) + { + shouldAppend = AreEqualFailing(expected, actual, comparer); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _expected = expected; + _actual = actual; + } + } + + public AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, IEquatable? expected, IEquatable? actual, out bool shouldAppend) + { + shouldAppend = AreEqualFailing(expected, actual); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _expected = expected; + _actual = actual; + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertAreEqualFailed(_expected, _actual, _builder.ToString()); + } + } + + public void AppendLiteral(string? value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertAreNotEqualInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly object? _notExpected; + private readonly object? _actual; + + public AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend) + : this(literalLength, formattedCount, notExpected, actual, null, out shouldAppend) + { + } + + public AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, IEqualityComparer? comparer, out bool shouldAppend) + { + shouldAppend = AreNotEqualFailing(notExpected, actual, comparer); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _notExpected = notExpected; + _actual = actual; + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertAreNotEqualFailed(_notExpected, _actual, _builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertNonGenericAreEqualInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly Action? _failAction; + + public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, float expected, float actual, float delta, out bool shouldAppend) + { + shouldAppend = AreEqualFailing(expected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal expected, decimal actual, decimal delta, out bool shouldAppend) + { + shouldAppend = AreEqualFailing(expected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, long expected, long actual, long delta, out bool shouldAppend) + { + shouldAppend = AreEqualFailing(expected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, double expected, double actual, double delta, out bool shouldAppend) + { + shouldAppend = AreEqualFailing(expected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, out bool shouldAppend) + : this(literalLength, formattedCount, expected, actual, ignoreCase, CultureInfo.InvariantCulture, out shouldAppend) + { + } + + public AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, [NotNull] CultureInfo? culture, out bool shouldAppend) + { + Guard.NotNull(culture); + shouldAppend = AreEqualFailing(expected, actual, ignoreCase, culture); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreEqualFailed(expected, actual, ignoreCase, culture, userMessage); + } + } + + internal void ComputeAssertion() + => _failAction?.Invoke(_builder!.ToString()); + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertNonGenericAreNotEqualInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly Action? _failAction; + + public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, float notExpected, float actual, float delta, out bool shouldAppend) + { + shouldAppend = AreNotEqualFailing(notExpected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal notExpected, decimal actual, decimal delta, out bool shouldAppend) + { + shouldAppend = AreNotEqualFailing(notExpected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, long notExpected, long actual, long delta, out bool shouldAppend) + { + shouldAppend = AreNotEqualFailing(notExpected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, double notExpected, double actual, double delta, out bool shouldAppend) + { + shouldAppend = AreNotEqualFailing(notExpected, actual, delta); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); + } + } + + public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, out bool shouldAppend) + : this(literalLength, formattedCount, notExpected, actual, ignoreCase, CultureInfo.InvariantCulture, out shouldAppend) + { + } + + public AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, [NotNull] CultureInfo? culture, out bool shouldAppend) + { + Guard.NotNull(culture); + shouldAppend = AreNotEqualFailing(notExpected, actual, ignoreCase, culture); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + _failAction = userMessage => ThrowAssertAreNotEqualFailed(notExpected, actual, userMessage); + } + } + + internal void ComputeAssertion() + => _failAction?.Invoke(_builder!.ToString()); + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -36,7 +385,7 @@ public static void AreEqual(T? expected, T? actual) /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -48,8 +397,8 @@ public static void AreEqual(T? expected, T? actual) /// The second value to compare. This is the value produced by the code under test. /// /// - /// The implementation to use when comparing keys, - /// or null to use the default . + /// The implementation to use when comparing keys, + /// or null to use the default . /// /// /// Thrown if is not equal to . @@ -60,7 +409,7 @@ public static void AreEqual(T? expected, T? actual, IEqualityComparer? com /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -83,6 +432,12 @@ public static void AreEqual(T? expected, T? actual, IEqualityComparer? com public static void AreEqual(T? expected, T? actual, string? message) => AreEqual(expected, actual, null, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(T? expected, T? actual, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual))] ref AssertAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. @@ -98,8 +453,8 @@ public static void AreEqual(T? expected, T? actual, string? message) /// The second value to compare. This is the value produced by the code under test. /// /// - /// The implementation to use when comparing keys, - /// or null to use the default . + /// The implementation to use when comparing keys, + /// or null to use the default . /// /// /// The message to include in the exception when @@ -113,10 +468,16 @@ public static void AreEqual(T? expected, T? actual, string? message) public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer, string? message) => AreEqual(expected, actual, comparer, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(comparer))] ref AssertAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -157,8 +518,8 @@ public static void AreEqual(T? expected, T? actual, [StringSyntax(StringSynta /// The second value to compare. This is the value produced by the code under test. /// /// - /// The implementation to use when comparing keys, - /// or null to use the default . + /// The implementation to use when comparing keys, + /// or null to use the default . /// /// /// The message to include in the exception when @@ -175,36 +536,19 @@ public static void AreEqual(T? expected, T? actual, [StringSyntax(StringSynta public static void AreEqual(T? expected, T? actual, IEqualityComparer? comparer, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - IEqualityComparer localComparer = comparer ?? EqualityComparer.Default; - if (localComparer.Equals(expected!, actual!)) + if (!AreEqualFailing(expected, actual, comparer)) { return; } string userMessage = BuildUserMessage(message, parameters); - string finalMessage = actual != null && expected != null && !actual.GetType().Equals(expected.GetType()) - ? string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDifferentTypesFailMsg, - userMessage, - ReplaceNulls(expected), - expected.GetType().FullName, - ReplaceNulls(actual), - actual.GetType().FullName) - : string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualFailMsg, - userMessage, - ReplaceNulls(expected), - ReplaceNulls(actual)); - - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertAreEqualFailed(expected, actual, userMessage); } /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -224,7 +568,7 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual) /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -247,10 +591,16 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual) public static void AreEqual(IEquatable? expected, IEquatable? actual, string? message) => AreEqual(expected, actual, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(IEquatable? expected, IEquatable? actual, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual))] ref AssertAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified values are equal and throws an exception /// if the two values are not equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -275,17 +625,76 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual, s /// public static void AreEqual(IEquatable? expected, IEquatable? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (actual is null && expected is null) + if (!AreEqualFailing(expected, actual)) { return; } - if (actual?.Equals(expected) == true) + string userMessage = BuildUserMessage(message, parameters); + ThrowAssertAreEqualFailed(expected, actual, userMessage); + } + + private static bool AreEqualFailing(T? expected, T? actual) + => AreEqualFailing(expected, actual, null); + + private static bool AreEqualFailing(T? expected, T? actual, IEqualityComparer? comparer) + => !(comparer ?? EqualityComparer.Default).Equals(expected!, actual!); + + private static bool AreEqualFailing(IEquatable? expected, IEquatable? actual) + => (actual is not null || expected is not null) && actual?.Equals(expected) != true; + + private static bool AreEqualFailing(string? expected, string? actual, bool ignoreCase, CultureInfo culture) + => CompareInternal(expected, actual, ignoreCase, culture) != 0; + + private static bool AreEqualFailing(float expected, float actual, float delta) + { + if (float.IsNaN(delta) || delta < 0) { - return; + // NaN and negative values don't make sense as a delta value. + throw new ArgumentOutOfRangeException(nameof(delta)); } - string userMessage = BuildUserMessage(message, parameters); + if (expected.Equals(actual)) + { + return false; + } + + // If both floats are NaN, then they were considered equal in the previous check. + // If only one of them is NaN, then they are not equal regardless of the value of delta. + // Then, the subtraction comparison to delta isn't involving NaNs. + return float.IsNaN(expected) || float.IsNaN(actual) || + Math.Abs(expected - actual) > delta; + } + + private static bool AreEqualFailing(double expected, double actual, double delta) + { + if (double.IsNaN(delta) || delta < 0) + { + // NaN and negative values don't make sense as a delta value. + throw new ArgumentOutOfRangeException(nameof(delta)); + } + + if (expected.Equals(actual)) + { + return false; + } + + // If both doubles are NaN, then they were considered equal in the previous check. + // If only one of them is NaN, then they are not equal regardless of the value of delta. + // Then, the subtraction comparison to delta isn't involving NaNs. + return double.IsNaN(expected) || double.IsNaN(actual) || + Math.Abs(expected - actual) > delta; + } + + private static bool AreEqualFailing(decimal expected, decimal actual, decimal delta) + => Math.Abs(expected - actual) > delta; + + private static bool AreEqualFailing(long expected, long actual, long delta) + => Math.Abs(expected - actual) > delta; + + [DoesNotReturn] + private static void ThrowAssertAreEqualFailed(object? expected, object? actual, string userMessage) + { string finalMessage = actual != null && expected != null && !actual.GetType().Equals(expected.GetType()) ? string.Format( CultureInfo.CurrentCulture, @@ -301,6 +710,40 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual, [ userMessage, ReplaceNulls(expected), ReplaceNulls(actual)); + ThrowAssertFailed("Assert.AreEqual", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertAreEqualFailed(T expected, T actual, T delta, string userMessage) + where T : struct, IConvertible + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualDeltaFailMsg, + userMessage, + expected.ToString(CultureInfo.CurrentCulture.NumberFormat), + actual.ToString(CultureInfo.CurrentCulture.NumberFormat), + delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); + ThrowAssertFailed("Assert.AreEqual", finalMessage); + } + + [DoesNotReturn] + private static void ThrowAssertAreEqualFailed(string? expected, string? actual, bool ignoreCase, CultureInfo culture, string userMessage) + { + // If the user requested to match case, and the difference between expected/actual is casing only, then we use a different message. + string finalMessage = !ignoreCase && CompareInternal(expected, actual, ignoreCase: true, culture) == 0 + ? string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualCaseFailMsg, + userMessage, + ReplaceNulls(expected), + ReplaceNulls(actual)) + : string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreEqualFailMsg, + userMessage, + ReplaceNulls(expected), + ReplaceNulls(actual)); ThrowAssertFailed("Assert.AreEqual", finalMessage); } @@ -308,7 +751,7 @@ public static void AreEqual(IEquatable? expected, IEquatable? actual, [ /// /// Tests whether the specified values are unequal and throws an exception /// if the two values are equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -342,8 +785,8 @@ public static void AreNotEqual(T? notExpected, T? actual) /// The second value to compare. This is the value produced by the code under test. /// /// - /// The implementation to use when comparing keys, - /// or null to use the default . + /// The implementation to use when comparing keys, + /// or null to use the default . /// /// /// Thrown if is equal to . @@ -354,7 +797,7 @@ public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer /// Tests whether the specified values are unequal and throws an exception /// if the two values are equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -377,6 +820,12 @@ public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer(T? notExpected, T? actual, string? message) => AreNotEqual(notExpected, actual, null, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(T? notExpected, T? actual, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual))] ref AssertAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified values are unequal and throws an exception /// if the two values are equal. @@ -393,8 +842,8 @@ public static void AreNotEqual(T? notExpected, T? actual, string? message) /// The second value to compare. This is the value produced by the code under test. /// /// - /// The implementation to use when comparing keys, - /// or null to use the default . + /// The implementation to use when comparing keys, + /// or null to use the default . /// /// /// The message to include in the exception when @@ -407,10 +856,16 @@ public static void AreNotEqual(T? notExpected, T? actual, string? message) public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer, string? message) => AreNotEqual(notExpected, actual, comparer, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(comparer))] ref AssertAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified values are unequal and throws an exception /// if the two values are equal. - /// The equality is computed using the default . + /// The equality is computed using the default . /// /// /// The type of values to compare. @@ -452,8 +907,8 @@ public static void AreNotEqual(T? notExpected, T? actual, [StringSyntax(Strin /// The second value to compare. This is the value produced by the code under test. /// /// - /// The implementation to use when comparing keys, - /// or null to use the default . + /// The implementation to use when comparing keys, + /// or null to use the default . /// /// /// The message to include in the exception when @@ -469,20 +924,13 @@ public static void AreNotEqual(T? notExpected, T? actual, [StringSyntax(Strin public static void AreNotEqual(T? notExpected, T? actual, IEqualityComparer? comparer, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - IEqualityComparer localComparer = comparer ?? EqualityComparer.Default; - if (!localComparer.Equals(notExpected!, actual!)) + if (!AreNotEqualFailing(notExpected, actual, comparer)) { return; } string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreNotEqualFailMsg, - userMessage, - ReplaceNulls(notExpected), - ReplaceNulls(actual)); - ThrowAssertFailed("Assert.AreNotEqual", finalMessage); + ThrowAssertAreNotEqualFailed(notExpected, actual, userMessage); } /// @@ -534,6 +982,12 @@ public static void AreEqual(float expected, float actual, float delta) public static void AreEqual(float expected, float actual, float delta, string? message) => AreEqual(expected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(float expected, float actual, float delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified floats are equal and throws an exception /// if they are not equal. @@ -564,30 +1018,10 @@ public static void AreEqual(float expected, float actual, float delta, string? m public static void AreEqual(float expected, float actual, float delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (float.IsNaN(expected) || float.IsNaN(actual) || float.IsNaN(delta)) + if (AreEqualFailing(expected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDeltaFailMsg, - userMessage, - expected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); - } - - if (Math.Abs(expected - actual) > delta) - { - string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDeltaFailMsg, - userMessage, - expected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); } } @@ -640,6 +1074,12 @@ public static void AreNotEqual(float notExpected, float actual, float delta) public static void AreNotEqual(float notExpected, float actual, float delta, string? message) => AreNotEqual(notExpected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(float notExpected, float actual, float delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified floats are unequal and throws an exception /// if they are equal. @@ -670,20 +1110,34 @@ public static void AreNotEqual(float notExpected, float actual, float delta, str public static void AreNotEqual(float notExpected, float actual, float delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (Math.Abs(notExpected - actual) <= delta) + if (AreNotEqualFailing(notExpected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreNotEqualDeltaFailMsg, - userMessage, - notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreNotEqual", finalMessage); + ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); } } + private static bool AreNotEqualFailing(float notExpected, float actual, float delta) + { + if (float.IsNaN(delta) || delta < 0) + { + // NaN and negative values don't make sense as a delta value. + throw new ArgumentOutOfRangeException(nameof(delta)); + } + + if (float.IsNaN(notExpected) && float.IsNaN(actual)) + { + // If both notExpected and actual are NaN, then AreNotEqual should fail. + return true; + } + + // Note: if both notExpected and actual are NaN, that was handled separately above. + // Now, if both are numerics, then the logic is good. + // And, if only one of them is NaN, we know they are not equal, meaning AreNotEqual shouldn't fail. + // And in this case we will correctly be returning false, because NaN <= anything is always false. + return Math.Abs(notExpected - actual) <= delta; + } + /// /// Tests whether the specified decimals are equal and throws an exception /// if they are not equal. @@ -733,6 +1187,12 @@ public static void AreEqual(decimal expected, decimal actual, decimal delta) public static void AreEqual(decimal expected, decimal actual, decimal delta, string? message) => AreEqual(expected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(decimal expected, decimal actual, decimal delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified decimals are equal and throws an exception /// if they are not equal. @@ -763,17 +1223,10 @@ public static void AreEqual(decimal expected, decimal actual, decimal delta, str public static void AreEqual(decimal expected, decimal actual, decimal delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (Math.Abs(expected - actual) > delta) + if (AreEqualFailing(expected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDeltaFailMsg, - userMessage, - expected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); } } @@ -826,6 +1279,12 @@ public static void AreNotEqual(decimal notExpected, decimal actual, decimal delt public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, string? message) => AreNotEqual(notExpected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified decimals are unequal and throws an exception /// if they are equal. @@ -856,20 +1315,16 @@ public static void AreNotEqual(decimal notExpected, decimal actual, decimal delt public static void AreNotEqual(decimal notExpected, decimal actual, decimal delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (Math.Abs(notExpected - actual) <= delta) + if (AreNotEqualFailing(notExpected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreNotEqualDeltaFailMsg, - userMessage, - notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreNotEqual", finalMessage); + ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); } } + private static bool AreNotEqualFailing(decimal notExpected, decimal actual, decimal delta) + => Math.Abs(notExpected - actual) <= delta; + /// /// Tests whether the specified longs are equal and throws an exception /// if they are not equal. @@ -919,6 +1374,12 @@ public static void AreEqual(long expected, long actual, long delta) public static void AreEqual(long expected, long actual, long delta, string? message) => AreEqual(expected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(long expected, long actual, long delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified longs are equal and throws an exception /// if they are not equal. @@ -949,17 +1410,10 @@ public static void AreEqual(long expected, long actual, long delta, string? mess public static void AreEqual(long expected, long actual, long delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (Math.Abs(expected - actual) > delta) + if (AreEqualFailing(expected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDeltaFailMsg, - userMessage, - expected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); } } @@ -1012,6 +1466,12 @@ public static void AreNotEqual(long notExpected, long actual, long delta) public static void AreNotEqual(long notExpected, long actual, long delta, string? message) => AreNotEqual(notExpected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(long notExpected, long actual, long delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified longs are unequal and throws an exception /// if they are equal. @@ -1042,20 +1502,16 @@ public static void AreNotEqual(long notExpected, long actual, long delta, string public static void AreNotEqual(long notExpected, long actual, long delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (Math.Abs(notExpected - actual) <= delta) + if (AreNotEqualFailing(notExpected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreNotEqualDeltaFailMsg, - userMessage, - notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreNotEqual", finalMessage); + ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); } } + private static bool AreNotEqualFailing(long notExpected, long actual, long delta) + => Math.Abs(notExpected - actual) <= delta; + /// /// Tests whether the specified doubles are equal and throws an exception /// if they are not equal. @@ -1104,6 +1560,12 @@ public static void AreEqual(double expected, double actual, double delta) public static void AreEqual(double expected, double actual, double delta, string? message) => AreEqual(expected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(double expected, double actual, double delta, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(delta))] ref AssertNonGenericAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified doubles are equal and throws an exception /// if they are not equal. @@ -1133,30 +1595,10 @@ public static void AreEqual(double expected, double actual, double delta, string public static void AreEqual(double expected, double actual, double delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (double.IsNaN(expected) || double.IsNaN(actual) || double.IsNaN(delta)) + if (AreEqualFailing(expected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDeltaFailMsg, - userMessage, - expected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); - } - - if (Math.Abs(expected - actual) > delta) - { - string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualDeltaFailMsg, - userMessage, - expected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertAreEqualFailed(expected, actual, delta, userMessage); } } @@ -1209,6 +1651,12 @@ public static void AreNotEqual(double notExpected, double actual, double delta) public static void AreNotEqual(double notExpected, double actual, double delta, string? message) => AreNotEqual(notExpected, actual, delta, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(double notExpected, double actual, double delta, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(delta))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified doubles are unequal and throws an exception /// if they are equal. @@ -1239,20 +1687,48 @@ public static void AreNotEqual(double notExpected, double actual, double delta, public static void AreNotEqual(double notExpected, double actual, double delta, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (Math.Abs(notExpected - actual) <= delta) + if (AreNotEqualFailing(notExpected, actual, delta)) { string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreNotEqualDeltaFailMsg, - userMessage, - notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat), - actual.ToString(CultureInfo.CurrentCulture.NumberFormat), - delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); - ThrowAssertFailed("Assert.AreNotEqual", finalMessage); + ThrowAssertAreNotEqualFailed(notExpected, actual, delta, userMessage); } } + private static bool AreNotEqualFailing(double notExpected, double actual, double delta) + { + if (double.IsNaN(delta) || delta < 0) + { + // NaN and negative values don't make sense as a delta value. + throw new ArgumentOutOfRangeException(nameof(delta)); + } + + if (double.IsNaN(notExpected) && double.IsNaN(actual)) + { + // If both notExpected and actual are NaN, then AreNotEqual should fail. + return true; + } + + // Note: if both notExpected and actual are NaN, that was handled separately above. + // Now, if both are numerics, then the logic is good. + // And, if only one of them is NaN, we know they are not equal, meaning AreNotEqual shouldn't fail. + // And in this case we will correctly be returning false, because NaN <= anything is always false. + return Math.Abs(notExpected - actual) <= delta; + } + + [DoesNotReturn] + private static void ThrowAssertAreNotEqualFailed(T notExpected, T actual, T delta, string userMessage) + where T : struct, IConvertible + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreNotEqualDeltaFailMsg, + userMessage, + notExpected.ToString(CultureInfo.CurrentCulture.NumberFormat), + actual.ToString(CultureInfo.CurrentCulture.NumberFormat), + delta.ToString(CultureInfo.CurrentCulture.NumberFormat)); + ThrowAssertFailed("Assert.AreNotEqual", finalMessage); + } + /// /// Tests whether the specified strings are equal and throws an exception /// if they are not equal. The invariant culture is used for the comparison. @@ -1298,6 +1774,12 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase) public static void AreEqual(string? expected, string? actual, bool ignoreCase, string? message) => AreEqual(expected, actual, ignoreCase, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(string? expected, string? actual, bool ignoreCase, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(ignoreCase))] ref AssertNonGenericAreEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified strings are equal and throws an exception /// if they are not equal. The invariant culture is used for the comparison. @@ -1380,6 +1862,16 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase, [NotNull] CultureInfo? culture, string? message) => AreEqual(expected, actual, ignoreCase, culture, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreEqual(string? expected, string? actual, bool ignoreCase, +#pragma warning restore IDE0060 // Remove unused parameter + [NotNull] CultureInfo? culture, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual), nameof(ignoreCase), nameof(culture))] ref AssertNonGenericAreEqualInterpolatedStringHandler message) + { + CheckParameterNotNull(culture, "Assert.AreEqual", nameof(culture), string.Empty); + message.ComputeAssertion(); + } + /// /// Tests whether the specified strings are equal and throws an exception /// if they are not equal. @@ -1412,28 +1904,13 @@ public static void AreEqual(string? expected, string? actual, bool ignoreCase, [NotNull] CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { CheckParameterNotNull(culture, "Assert.AreEqual", "culture", string.Empty); - if (CompareInternal(expected, actual, ignoreCase, culture) == 0) + if (!AreEqualFailing(expected, actual, ignoreCase, culture)) { return; } string userMessage = BuildUserMessage(message, parameters); - string finalMessage = !ignoreCase && CompareInternal(expected, actual, ignoreCase, culture) == 0 - ? string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualCaseFailMsg, - userMessage, - ReplaceNulls(expected), - ReplaceNulls(actual)) - : string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreEqualFailMsg, - userMessage, - ReplaceNulls(expected), - ReplaceNulls(actual)); - - // Comparison failed. Check if it was a case-only failure. - ThrowAssertFailed("Assert.AreEqual", finalMessage); + ThrowAssertAreEqualFailed(expected, actual, ignoreCase, culture, userMessage); } /// @@ -1483,6 +1960,12 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, string? message) => AreNotEqual(notExpected, actual, ignoreCase, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(ignoreCase))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified strings are unequal and throws an exception /// if they are equal. The invariant culture is used for the comparison. @@ -1567,6 +2050,16 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC CultureInfo? culture, string? message) => AreNotEqual(notExpected, actual, ignoreCase, culture, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotEqual(string? notExpected, string? actual, bool ignoreCase, +#pragma warning restore IDE0060 // Remove unused parameter + CultureInfo? culture, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual), nameof(ignoreCase), nameof(culture))] ref AssertNonGenericAreNotEqualInterpolatedStringHandler message) + { + CheckParameterNotNull(culture, "Assert.AreNotEqual", nameof(culture), string.Empty); + message.ComputeAssertion(); + } + /// /// Tests whether the specified strings are unequal and throws an exception /// if they are equal. @@ -1600,12 +2093,24 @@ public static void AreNotEqual(string? notExpected, string? actual, bool ignoreC CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { CheckParameterNotNull(culture, "Assert.AreNotEqual", "culture", string.Empty); - if (CompareInternal(notExpected, actual, ignoreCase, culture) != 0) + if (!AreNotEqualFailing(notExpected, actual, ignoreCase, culture)) { return; } string userMessage = BuildUserMessage(message, parameters); + ThrowAssertAreNotEqualFailed(notExpected, actual, userMessage); + } + + private static bool AreNotEqualFailing(string? notExpected, string? actual, bool ignoreCase, CultureInfo culture) + => CompareInternal(notExpected, actual, ignoreCase, culture) == 0; + + private static bool AreNotEqualFailing(T? notExpected, T? actual, IEqualityComparer? comparer) + => (comparer ?? EqualityComparer.Default).Equals(notExpected!, actual!); + + [DoesNotReturn] + private static void ThrowAssertAreNotEqualFailed(object? notExpected, object? actual, string userMessage) + { string finalMessage = string.Format( CultureInfo.CurrentCulture, FrameworkMessages.AreNotEqualFailMsg, diff --git a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs index 07115c97d1..0a7799900d 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.AreSame.cs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; +using System.ComponentModel; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,6 +12,124 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed partial class Assert { + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertAreSameInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly TArgument? _expected; + private readonly TArgument? _actual; + + public AssertAreSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend) + { + _expected = expected; + _actual = actual; + shouldAppend = IsAreSameFailing(expected, actual); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertAreSameFailed(_expected, _actual, _builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertAreNotSameInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + + public AssertAreNotSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend) + { + shouldAppend = IsAreNotSameFailing(notExpected, actual); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertAreNotSameFailed(_builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + /// /// Tests whether the specified objects both refer to the same object and /// throws an exception if the two inputs do not refer to the same object. @@ -58,6 +175,12 @@ public static void AreSame(T? expected, T? actual) public static void AreSame(T? expected, T? actual, string? message) => AreSame(expected, actual, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreSame(T? expected, T? actual, [InterpolatedStringHandlerArgument(nameof(expected), nameof(actual))] ref AssertAreSameInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified objects both refer to the same object and /// throws an exception if the two inputs do not refer to the same object. @@ -85,23 +208,28 @@ public static void AreSame(T? expected, T? actual, string? message) /// public static void AreSame(T? expected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (ReferenceEquals(expected, actual)) + if (!IsAreSameFailing(expected, actual)) { return; } string userMessage = BuildUserMessage(message, parameters); - string finalMessage = userMessage; + ThrowAssertAreSameFailed(expected, actual, userMessage); + } + + private static bool IsAreSameFailing(T? expected, T? actual) + => !ReferenceEquals(expected, actual); - if (expected is ValueType) + [DoesNotReturn] + private static void ThrowAssertAreSameFailed(T? expected, T? actual, string userMessage) + { + string finalMessage = userMessage; + if (expected is ValueType && actual is ValueType) { - if (actual is ValueType) - { - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.AreSameGivenValues, - userMessage); - } + finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.AreSameGivenValues, + userMessage); } ThrowAssertFailed("Assert.AreSame", finalMessage); @@ -154,6 +282,12 @@ public static void AreNotSame(T? notExpected, T? actual) public static void AreNotSame(T? notExpected, T? actual, string? message) => AreNotSame(notExpected, actual, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void AreNotSame(T? notExpected, T? actual, [InterpolatedStringHandlerArgument(nameof(notExpected), nameof(actual))] ref AssertAreNotSameInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified objects refer to different objects and /// throws an exception if the two inputs refer to the same object. @@ -182,9 +316,16 @@ public static void AreNotSame(T? notExpected, T? actual, string? message) /// public static void AreNotSame(T? notExpected, T? actual, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (ReferenceEquals(notExpected, actual)) + if (IsAreNotSameFailing(notExpected, actual)) { - ThrowAssertFailed("Assert.AreNotSame", BuildUserMessage(message, parameters)); + ThrowAssertAreNotSameFailed(BuildUserMessage(message, parameters)); } } + + private static bool IsAreNotSameFailing(T? notExpected, T? actual) + => ReferenceEquals(notExpected, actual); + + [DoesNotReturn] + private static void ThrowAssertAreNotSameFailed(string userMessage) + => ThrowAssertFailed("Assert.AreNotSame", userMessage); } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs b/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs index 280c261902..e86761c182 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Fail.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs b/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs index 7a53795dee..1124379a0c 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.Inconclusive.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs index 81fe910c6c..d758d4a961 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.IsInstanceOfType.cs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; +using System.ComponentModel; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,6 +12,246 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed partial class Assert { + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertIsInstanceOfTypeInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly object? _value; + private readonly Type? _expectedType; + + public AssertIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, Type? expectedType, out bool shouldAppend) + { + _value = value; + _expectedType = expectedType; + shouldAppend = IsInstanceOfTypeFailing(value, expectedType); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsInstanceOfTypeFailed(_value, _expectedType, _builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertGenericIsInstanceOfTypeInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly object? _value; + + public AssertGenericIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) + { + _value = value; + shouldAppend = IsInstanceOfTypeFailing(value, typeof(TArg)); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertIsNotInstanceOfTypeInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly object? _value; + private readonly Type? _wrongType; + + public AssertIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, Type? wrongType, out bool shouldAppend) + { + _value = value; + _wrongType = wrongType; + shouldAppend = IsNotInstanceOfTypeFailing(value, wrongType); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsNotInstanceOfTypeFailed(_value, _wrongType, _builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + private readonly object? _value; + + public AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) + { + _value = value; + shouldAppend = IsNotInstanceOfTypeFailing(value, typeof(TArg)); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsNotInstanceOfTypeFailed(_value, typeof(TArg), _builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + /// /// Tests whether the specified object is an instance of the expected /// type and throws an exception if the expected type is not in the @@ -48,7 +287,7 @@ public static void IsInstanceOfType([NotNull] object? value) /// /// The expected type of . public static void IsInstanceOfType([NotNull] object? value, out T instance) - => IsInstanceOfType(value, out instance, string.Empty, null); + => IsInstanceOfType(value, out instance, string.Empty, null); /// /// Tests whether the specified object is an instance of the expected @@ -74,6 +313,14 @@ public static void IsInstanceOfType([NotNull] object? value, out T instance) public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, string? message) => IsInstanceOfType(value, expectedType, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, [InterpolatedStringHandlerArgument(nameof(value), nameof(expectedType))] ref AssertIsInstanceOfTypeInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that. + => message.ComputeAssertion(); +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + /// /// Tests whether the specified object is an instance of the generic /// type and throws an exception if the generic type is not in the @@ -83,6 +330,14 @@ public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? exp public static void IsInstanceOfType([NotNull] object? value, string? message) => IsInstanceOfType(value, typeof(T), message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsInstanceOfType([NotNull] object? value, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertGenericIsInstanceOfTypeInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that. + => message.ComputeAssertion(); +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + /// /// Tests whether the specified object is an instance of the generic /// type and throws an exception if the generic type is not in the @@ -90,7 +345,16 @@ public static void IsInstanceOfType([NotNull] object? value, string? message) /// /// The expected type of . public static void IsInstanceOfType([NotNull] object? value, out T instance, string? message) - => IsInstanceOfType(value, out instance, message, null); + => IsInstanceOfType(value, out instance, message, null); + + /// + public static void IsInstanceOfType([NotNull] object? value, out T instance, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertGenericIsInstanceOfTypeInterpolatedStringHandler message) +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that. + { + message.ComputeAssertion(); + instance = (T)value!; + } +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. /// /// Tests whether the specified object is an instance of the expected @@ -119,23 +383,30 @@ public static void IsInstanceOfType([NotNull] object? value, out T instance, public static void IsInstanceOfType([NotNull] object? value, [NotNull] Type? expectedType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (expectedType == null || value == null) + if (IsInstanceOfTypeFailing(value, expectedType)) { - ThrowAssertFailed("Assert.IsInstanceOfType", BuildUserMessage(message, parameters)); + ThrowAssertIsInstanceOfTypeFailed(value, expectedType, BuildUserMessage(message, parameters)); } + } - Type elementType = value.GetType(); - if (!expectedType.IsAssignableFrom(elementType)) + private static bool IsInstanceOfTypeFailing([NotNullWhen(false)] object? value, [NotNullWhen(false)] Type? expectedType) + => expectedType == null || value == null || !expectedType.IsInstanceOfType(value); + + [DoesNotReturn] + private static void ThrowAssertIsInstanceOfTypeFailed(object? value, Type? expectedType, string userMessage) + { + string finalMessage = userMessage; + if (expectedType is not null && value is not null) { - string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( + finalMessage = string.Format( CultureInfo.CurrentCulture, FrameworkMessages.IsInstanceOfFailMsg, userMessage, expectedType.ToString(), value.GetType().ToString()); - ThrowAssertFailed("Assert.IsInstanceOfType", finalMessage); } + + ThrowAssertFailed("Assert.IsInstanceOfType", finalMessage); } /// @@ -213,6 +484,14 @@ public static void IsNotInstanceOfType(object? value) public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, string? message) => IsNotInstanceOfType(value, wrongType, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, [InterpolatedStringHandlerArgument(nameof(value), nameof(wrongType))] ref AssertIsNotInstanceOfTypeInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that. + => message.ComputeAssertion(); +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + /// /// Tests whether the specified object is not an instance of the wrong generic /// type and throws an exception if the specified type is in the @@ -222,6 +501,12 @@ public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, public static void IsNotInstanceOfType(object? value, string? message) => IsNotInstanceOfType(value, typeof(T), message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsNotInstanceOfType(object? value, [InterpolatedStringHandlerArgument(nameof(value))] AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified object is not an instance of the wrong /// type and throws an exception if the specified type is in the @@ -249,29 +534,32 @@ public static void IsNotInstanceOfType(object? value, string? message) public static void IsNotInstanceOfType(object? value, [NotNull] Type? wrongType, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (wrongType == null) + if (IsNotInstanceOfTypeFailing(value, wrongType)) { - ThrowAssertFailed("Assert.IsNotInstanceOfType", BuildUserMessage(message, parameters)); + ThrowAssertIsNotInstanceOfTypeFailed(value, wrongType, BuildUserMessage(message, parameters)); } + } - // Null is not an instance of any type. - if (value == null) - { - return; - } + private static bool IsNotInstanceOfTypeFailing(object? value, [NotNullWhen(false)] Type? wrongType) + => wrongType is null || + // Null is not an instance of any type. + (value is not null && wrongType.IsInstanceOfType(value)); - Type elementType = value.GetType(); - if (wrongType.IsAssignableFrom(elementType)) + [DoesNotReturn] + private static void ThrowAssertIsNotInstanceOfTypeFailed(object? value, Type? wrongType, string userMessage) + { + string finalMessage = userMessage; + if (wrongType is not null) { - string userMessage = BuildUserMessage(message, parameters); - string finalMessage = string.Format( + finalMessage = string.Format( CultureInfo.CurrentCulture, FrameworkMessages.IsNotInstanceOfFailMsg, userMessage, wrongType.ToString(), - value.GetType().ToString()); - ThrowAssertFailed("Assert.IsNotInstanceOfType", finalMessage); + value!.GetType().ToString()); } + + ThrowAssertFailed("Assert.IsNotInstanceOfType", finalMessage); } /// diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs index 5c6300c80e..74dd9a5e51 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.IsNull.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -12,6 +12,120 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed partial class Assert { + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertIsNullInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + + public AssertIsNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) + { + shouldAppend = IsNullFailing(value); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsNullFailed(_builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertIsNotNullInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + + public AssertIsNotNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) + { + shouldAppend = IsNotNullFailing(value); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsNotNullFailed(_builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + /// /// Tests whether the specified object is null and throws an exception /// if it is not. @@ -42,6 +156,12 @@ public static void IsNull(object? value) public static void IsNull(object? value, string? message) => IsNull(value, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsNull(object? value, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertIsNullInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified object is null and throws an exception /// if it is not. @@ -61,12 +181,17 @@ public static void IsNull(object? value, string? message) /// public static void IsNull(object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (value != null) + if (IsNullFailing(value)) { - ThrowAssertFailed("Assert.IsNull", BuildUserMessage(message, parameters)); + ThrowAssertIsNullFailed(BuildUserMessage(message, parameters)); } } + private static bool IsNullFailing(object? value) => value is not null; + + private static void ThrowAssertIsNullFailed(string message) + => ThrowAssertFailed("Assert.IsNull", message); + /// /// Tests whether the specified object is non-null and throws an exception /// if it is null. @@ -97,6 +222,14 @@ public static void IsNotNull([NotNull] object? value) public static void IsNotNull([NotNull] object? value, string? message) => IsNotNull(value, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsNotNull([NotNull] object? value, [InterpolatedStringHandlerArgument(nameof(value))] ref AssertIsNotNullInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - Not sure how to express the semantics to the compiler, but the implementation guarantees that. + => message.ComputeAssertion(); +#pragma warning restore CS8777 // Parameter must have a non-null value when exiting. + /// /// Tests whether the specified object is non-null and throws an exception /// if it is null. @@ -116,9 +249,15 @@ public static void IsNotNull([NotNull] object? value, string? message) /// public static void IsNotNull([NotNull] object? value, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (value == null) + if (IsNotNullFailing(value)) { - ThrowAssertFailed("Assert.IsNotNull", BuildUserMessage(message, parameters)); + ThrowAssertIsNotNullFailed(BuildUserMessage(message, parameters)); } } + + private static bool IsNotNullFailing([NotNullWhen(false)] object? value) => value is null; + + [DoesNotReturn] + private static void ThrowAssertIsNotNullFailed(string message) + => ThrowAssertFailed("Assert.IsNotNull", message); } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs b/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs index a28b3aa331..53815e5b36 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.IsTrue.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -12,6 +12,120 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed partial class Assert { + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertIsTrueInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + + public AssertIsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend) + { + shouldAppend = IsTrueFailing(condition); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsTrueFailed(_builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertIsFalseInterpolatedStringHandler + { + private readonly StringBuilder? _builder; + + public AssertIsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend) + { + shouldAppend = IsFalseFailing(condition); + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal void ComputeAssertion() + { + if (_builder is not null) + { + ThrowAssertIsFalseFailed(_builder.ToString()); + } + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + /// /// Tests whether the specified condition is true and throws an exception /// if the condition is false. @@ -55,6 +169,12 @@ public static void IsTrue([DoesNotReturnIf(false)] bool? condition) public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? message) => IsTrue(condition, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsTrue([DoesNotReturnIf(false)] bool condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsTrueInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified condition is true and throws an exception /// if the condition is false. @@ -72,6 +192,12 @@ public static void IsTrue([DoesNotReturnIf(false)] bool condition, string? messa public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? message) => IsTrue(condition, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsTrue([DoesNotReturnIf(false)] bool? condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsTrueInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified condition is true and throws an exception /// if the condition is false. @@ -92,9 +218,9 @@ public static void IsTrue([DoesNotReturnIf(false)] bool? condition, string? mess public static void IsTrue([DoesNotReturnIf(false)] bool condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (!condition) + if (IsTrueFailing(condition)) { - ThrowAssertFailed("Assert.IsTrue", BuildUserMessage(message, parameters)); + ThrowAssertIsTrueFailed(BuildUserMessage(message, parameters)); } } @@ -118,12 +244,21 @@ public static void IsTrue([DoesNotReturnIf(false)] bool condition, [StringSyntax public static void IsTrue([DoesNotReturnIf(false)] bool? condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (condition is false or null) + if (IsTrueFailing(condition)) { - ThrowAssertFailed("Assert.IsTrue", BuildUserMessage(message, parameters)); + ThrowAssertIsTrueFailed(BuildUserMessage(message, parameters)); } } + private static bool IsTrueFailing(bool? condition) + => condition is false or null; + + private static bool IsTrueFailing(bool condition) + => !condition; + + private static void ThrowAssertIsTrueFailed(string message) + => ThrowAssertFailed("Assert.IsTrue", message); + /// /// Tests whether the specified condition is false and throws an exception /// if the condition is true. @@ -167,6 +302,12 @@ public static void IsFalse([DoesNotReturnIf(true)] bool? condition) public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? message) => IsFalse(condition, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsFalse([DoesNotReturnIf(true)] bool condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsFalseInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified condition is false and throws an exception /// if the condition is true. @@ -184,6 +325,12 @@ public static void IsFalse([DoesNotReturnIf(true)] bool condition, string? messa public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? message) => IsFalse(condition, message, null); + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static void IsFalse([DoesNotReturnIf(true)] bool? condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref AssertIsFalseInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + => message.ComputeAssertion(); + /// /// Tests whether the specified condition is false and throws an exception /// if the condition is true. @@ -204,9 +351,9 @@ public static void IsFalse([DoesNotReturnIf(true)] bool? condition, string? mess public static void IsFalse([DoesNotReturnIf(true)] bool condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (condition) + if (IsFalseFailing(condition)) { - ThrowAssertFailed("Assert.IsFalse", BuildUserMessage(message, parameters)); + ThrowAssertIsFalseFailed(BuildUserMessage(message, parameters)); } } @@ -230,9 +377,19 @@ public static void IsFalse([DoesNotReturnIf(true)] bool condition, [StringSyntax public static void IsFalse([DoesNotReturnIf(true)] bool? condition, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? message, params object?[]? parameters) { - if (condition is true or null) + if (IsFalseFailing(condition)) { - ThrowAssertFailed("Assert.IsFalse", BuildUserMessage(message, parameters)); + ThrowAssertIsFalseFailed(BuildUserMessage(message, parameters)); } } + + private static bool IsFalseFailing(bool? condition) + => condition is true or null; + + private static bool IsFalseFailing(bool condition) + => condition; + + [DoesNotReturn] + private static void ThrowAssertIsFalseFailed(string userMessage) + => ThrowAssertFailed("Assert.IsFalse", userMessage); } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs b/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs index 9d88e3dc0a..e6f380b084 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.cs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; +using System.ComponentModel; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,6 +12,256 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// public sealed partial class Assert { + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertNonStrictThrowsInterpolatedStringHandler + where TException : Exception + { + private readonly StringBuilder? _builder; + private readonly ThrowsExceptionState _state; + + public AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, Action action, out bool shouldAppend) + { + _state = IsThrowsFailing(action, isStrictType: false, "Throws"); + shouldAppend = _state.FailAction is not null; + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal TException ComputeAssertion() + { + if (_state.FailAction is not null) + { + _state.FailAction(_builder!.ToString()); + } + else + { + return (TException)_state.ExceptionThrown!; + } + + // This will not hit, but need it for compiler. + return null!; + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters + } + + [InterpolatedStringHandler] + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct AssertThrowsExactlyInterpolatedStringHandler + where TException : Exception + { + private readonly StringBuilder? _builder; + private readonly ThrowsExceptionState _state; + + public AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, Action action, out bool shouldAppend) + { + _state = IsThrowsFailing(action, isStrictType: true, "ThrowsExactly"); + shouldAppend = _state.FailAction is not null; + if (shouldAppend) + { + _builder = new StringBuilder(literalLength + formattedCount); + } + } + + internal TException ComputeAssertion() + { + if (_state.FailAction is not null) + { + _state.FailAction(_builder!.ToString()); + } + else + { + return (TException)_state.ExceptionThrown!; + } + + // This will not hit, but need it for compiler. + return null!; + } + + public void AppendLiteral(string value) => _builder!.Append(value); + + public void AppendFormatted(T value) => AppendFormatted(value, format: null); + +#if NETCOREAPP3_1_OR_GREATER + public void AppendFormatted(ReadOnlySpan value) => _builder!.Append(value); + +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) => AppendFormatted(value.ToString(), alignment, format); +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads +#endif + + // NOTE: All the overloads involving format and/or alignment are not super efficient. + // This code path is only for when an assert is failing, so that's not the common scenario + // and should be okay if not very optimized. + // A more efficient implementation that can be used for .NET 6 and later is to delegate the work to + // the BCL's StringBuilder.AppendInterpolatedStringHandler + public void AppendFormatted(T value, string? format) => _builder!.AppendFormat(null, $"{{0:{format}}}", value); + + public void AppendFormatted(T value, int alignment) => _builder!.AppendFormat(null, $"{{0,{alignment}}}", value); + + public void AppendFormatted(T value, int alignment, string? format) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(string? value) => _builder!.Append(value); + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); + + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _builder!.AppendFormat(null, $"{{0,{alignment}:{format}}}", value); +#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters +#pragma warning restore RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads + } + + /// + /// Asserts that the delegate throws an exception of type + /// (or derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// The message to include in the exception when does not throws exception of type . + /// + /// + /// An array of parameters to use when formatting . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static TException Throws(Action action, string message = "", params object[] messageArgs) + where TException : Exception + => ThrowsException(action, isStrictType: false, message, parameters: messageArgs); + + /// + /// Asserts that the delegate throws an exception of type + /// (or derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// A func that takes the thrown Exception (or null if the action didn't throw any exception) to construct the message to include in the exception when does not throws exception of type . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static TException Throws(Action action, Func messageBuilder) + where TException : Exception + => ThrowsException(action, isStrictType: false, messageBuilder); + + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static TException Throws(Action action, [InterpolatedStringHandlerArgument(nameof(action))] ref AssertNonStrictThrowsInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + where TException : Exception + => message.ComputeAssertion(); + + /// + /// Asserts that the delegate throws an exception of type + /// (and not of derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// The message to include in the exception when does not throws exception of type . + /// + /// + /// An array of parameters to use when formatting . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static TException ThrowsExactly(Action action, string message = "", params object[] messageArgs) + where TException : Exception + => ThrowsException(action, isStrictType: true, message, parameters: messageArgs); + + /// + /// Asserts that the delegate throws an exception of type + /// (and not of derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// A func that takes the thrown Exception (or null if the action didn't throw any exception) to construct the message to include in the exception when does not throws exception of type . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static TException ThrowsExactly(Action action, Func messageBuilder) + where TException : Exception + => ThrowsException(action, isStrictType: true, messageBuilder); + + /// +#pragma warning disable IDE0060 // Remove unused parameter - https://github.com/dotnet/roslyn/issues/76578 + public static TException ThrowsExactly(Action action, [InterpolatedStringHandlerArgument(nameof(action))] ref AssertThrowsExactlyInterpolatedStringHandler message) +#pragma warning restore IDE0060 // Remove unused parameter + where TException : Exception + => message.ComputeAssertion(); + /// /// Tests whether the code specified by delegate throws exact given exception /// of type (and not of derived type) and throws AssertFailedException @@ -22,7 +271,7 @@ public sealed partial class Assert /// Delegate to code to be tested and which is expected to throw exception. /// /// - /// Type of exception expected to be thrown. + /// The exact type of exception expected to be thrown. /// /// /// Thrown if does not throws exception of type . @@ -30,6 +279,7 @@ public sealed partial class Assert /// /// The exception that was thrown. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static T ThrowsException(Action action) where T : Exception => ThrowsException(action, string.Empty, null); @@ -55,6 +305,7 @@ public static T ThrowsException(Action action) /// /// The exception that was thrown. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static T ThrowsException(Action action, string message) where T : Exception => ThrowsException(action, message, null); @@ -76,6 +327,7 @@ public static T ThrowsException(Action action, string message) /// /// The exception that was thrown. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static T ThrowsException(Func action) where T : Exception => ThrowsException(action, string.Empty, null); @@ -101,6 +353,7 @@ public static T ThrowsException(Func action) /// /// The exception that was thrown. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static T ThrowsException(Func action, string message) where T : Exception => ThrowsException(action, message, null); @@ -129,6 +382,7 @@ public static T ThrowsException(Func action, string message) /// /// The exception that was thrown. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static T ThrowsException(Func action, string message, params object?[]? parameters) where T : Exception #pragma warning disable IDE0053 // Use expression body for lambda expression @@ -160,47 +414,153 @@ public static T ThrowsException(Func action, string message, params /// /// The exception that was thrown. /// - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and format appropriately.")] + [EditorBrowsable(EditorBrowsableState.Never)] public static T ThrowsException(Action action, string message, params object?[]? parameters) where T : Exception + => ThrowsException(action, isStrictType: true, message, parameters: parameters); + + private static TException ThrowsException(Action action, bool isStrictType, string message, [CallerMemberName] string assertMethodName = "", params object?[]? parameters) + where TException : Exception { Guard.NotNull(action); Guard.NotNull(message); - string userMessage, finalMessage; - try + ThrowsExceptionState state = IsThrowsFailing(action, isStrictType, assertMethodName); + if (state.FailAction is not null) { - action(); + state.FailAction(BuildUserMessage(message, parameters)); } - catch (Exception ex) + else { - if (!typeof(T).Equals(ex.GetType())) - { - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.WrongExceptionThrown, - userMessage, - typeof(T), - ex.GetType()); - ThrowAssertFailed("Assert.ThrowsException", finalMessage); - } - - return (T)ex; + return (TException)state.ExceptionThrown!; } - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.NoExceptionThrown, - userMessage, - typeof(T)); - ThrowAssertFailed("Assert.ThrowsException", finalMessage); + // This will not hit, but need it for compiler. + return null!; + } + + private static TException ThrowsException(Action action, bool isStrictType, Func messageBuilder, [CallerMemberName] string assertMethodName = "") + where TException : Exception + { + Guard.NotNull(action); + Guard.NotNull(messageBuilder); + + ThrowsExceptionState state = IsThrowsFailing(action, isStrictType, assertMethodName); + if (state.FailAction is not null) + { + state.FailAction(messageBuilder(state.ExceptionThrown)); + } + else + { + return (TException)state.ExceptionThrown!; + } // This will not hit, but need it for compiler. - return null; + return null!; } + /// + /// Asserts that the delegate throws an exception of type + /// (or derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// The message to include in the exception when does not throws exception of type . + /// + /// + /// An array of parameters to use when formatting . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static Task ThrowsAsync(Func action, string message = "", params object[] messageArgs) + where TException : Exception + => ThrowsExceptionAsync(action, isStrictType: false, message, parameters: messageArgs); + + /// + /// Asserts that the delegate throws an exception of type + /// (and not of derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// The message to include in the exception when does not throws exception of type . + /// + /// + /// An array of parameters to use when formatting . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static Task ThrowsExactlyAsync(Func action, string message = "", params object[] messageArgs) + where TException : Exception + => ThrowsExceptionAsync(action, isStrictType: true, message, parameters: messageArgs); + + /// + /// Asserts that the delegate throws an exception of type + /// (or derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// A func that takes the thrown Exception (or null if the action didn't throw any exception) to construct the message to include in the exception when does not throws exception of type . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static Task ThrowsAsync(Func action, Func messageBuilder) + where TException : Exception + => ThrowsExceptionAsync(action, isStrictType: false, messageBuilder); + + /// + /// Asserts that the delegate throws an exception of type + /// (and not of derived type) and throws AssertFailedException if code does not throws exception or throws + /// exception of type other than . + /// + /// + /// Delegate to code to be tested and which is expected to throw exception. + /// + /// + /// A func that takes the thrown Exception (or null if the action didn't throw any exception) to construct the message to include in the exception when does not throws exception of type . + /// + /// + /// The type of exception expected to be thrown. + /// + /// + /// Thrown if does not throws exception of type . + /// + /// + /// The exception that was thrown. + /// + public static Task ThrowsExactlyAsync(Func action, Func messageBuilder) + where TException : Exception + => ThrowsExceptionAsync(action, isStrictType: true, messageBuilder); + /// /// Tests whether the code specified by delegate throws exact given exception /// of type (and not of derived type) and throws AssertFailedException @@ -218,6 +578,7 @@ public static T ThrowsException(Action action, string message, params object? /// /// The executing the delegate. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static async Task ThrowsExceptionAsync(Func action) where T : Exception => await ThrowsExceptionAsync(action, string.Empty, null) @@ -240,6 +601,7 @@ public static async Task ThrowsExceptionAsync(Func action) /// /// The executing the delegate. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static async Task ThrowsExceptionAsync(Func action, string message) where T : Exception => await ThrowsExceptionAsync(action, message, null) @@ -265,43 +627,150 @@ public static async Task ThrowsExceptionAsync(Func action, string me /// /// The executing the delegate. /// + [EditorBrowsable(EditorBrowsableState.Never)] public static async Task ThrowsExceptionAsync(Func action, string message, params object?[]? parameters) where T : Exception + => await ThrowsExceptionAsync(action, true, message, parameters: parameters) + .ConfigureAwait(false); + + private static async Task ThrowsExceptionAsync(Func action, bool isStrictType, string message, [CallerMemberName] string assertMethodName = "", params object?[]? parameters) + where TException : Exception { Guard.NotNull(action); Guard.NotNull(message); - string userMessage, finalMessage; + ThrowsExceptionState state = await IsThrowsAsyncFailingAsync(action, isStrictType, assertMethodName).ConfigureAwait(false); + if (state.FailAction is not null) + { + state.FailAction(BuildUserMessage(message, parameters)); + } + else + { + return (TException)state.ExceptionThrown!; + } + + // This will not hit, but need it for compiler. + return null!; + } + + private static async Task ThrowsExceptionAsync(Func action, bool isStrictType, Func messageBuilder, [CallerMemberName] string assertMethodName = "") + where TException : Exception + { + Guard.NotNull(action); + Guard.NotNull(messageBuilder); + + ThrowsExceptionState state = await IsThrowsAsyncFailingAsync(action, isStrictType, assertMethodName).ConfigureAwait(false); + if (state.FailAction is not null) + { + state.FailAction(messageBuilder(state.ExceptionThrown)); + } + else + { + return (TException)state.ExceptionThrown!; + } + + // This will not hit, but need it for compiler. + return null!; + } + + private static async Task IsThrowsAsyncFailingAsync(Func action, bool isStrictType, string assertMethodName) + where TException : Exception + { try { await action().ConfigureAwait(false); } catch (Exception ex) { - if (!typeof(T).Equals(ex.GetType())) + bool isExceptionOfType = isStrictType + ? typeof(TException) == ex.GetType() + : ex is TException; + + return isExceptionOfType + ? ThrowsExceptionState.CreateNotFailingState(ex) + : ThrowsExceptionState.CreateFailingState( + userMessage => + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.WrongExceptionThrown, + userMessage, + typeof(TException), + ex.GetType()); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }, ex); + } + + return ThrowsExceptionState.CreateFailingState( + failAction: userMessage => { - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( + string finalMessage = string.Format( CultureInfo.CurrentCulture, - FrameworkMessages.WrongExceptionThrown, + FrameworkMessages.NoExceptionThrown, userMessage, - typeof(T), - ex.GetType()); - ThrowAssertFailed("Assert.ThrowsException", finalMessage); - } + typeof(TException)); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }, null); + } + + private static ThrowsExceptionState IsThrowsFailing(Action action, bool isStrictType, string assertMethodName) + where TException : Exception + { + try + { + action(); + } + catch (Exception ex) + { + bool isExceptionOfType = isStrictType + ? typeof(TException) == ex.GetType() + : ex is TException; - return (T)ex; + return isExceptionOfType + ? ThrowsExceptionState.CreateNotFailingState(ex) + : ThrowsExceptionState.CreateFailingState( + userMessage => + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.WrongExceptionThrown, + userMessage, + typeof(TException), + ex.GetType()); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }, ex); } - userMessage = BuildUserMessage(message, parameters); - finalMessage = string.Format( - CultureInfo.CurrentCulture, - FrameworkMessages.NoExceptionThrown, - userMessage, - typeof(T)); - ThrowAssertFailed("Assert.ThrowsException", finalMessage); + return ThrowsExceptionState.CreateFailingState( + failAction: userMessage => + { + string finalMessage = string.Format( + CultureInfo.CurrentCulture, + FrameworkMessages.NoExceptionThrown, + userMessage, + typeof(TException)); + ThrowAssertFailed("Assert." + assertMethodName, finalMessage); + }, null); + } - // This will not hit, but need it for compiler. - return null!; + private readonly struct ThrowsExceptionState + { + public Exception? ExceptionThrown { get; } + + public Action? FailAction { get; } + + private ThrowsExceptionState(Exception? exceptionThrown, Action? failAction) + { + // If the assert is failing, failAction should be non-null, and exceptionWhenNotFailing may or may not be null. + // If the assert is not failing, exceptionWhenNotFailing should be non-null, and failAction should be null. + ExceptionThrown = exceptionThrown; + FailAction = failAction; + } + + public static ThrowsExceptionState CreateFailingState(Action failAction, Exception? exceptionThrown) + => new(exceptionThrown, failAction); + + public static ThrowsExceptionState CreateNotFailingState(Exception exception) + => new(exception, failAction: null); } } diff --git a/src/TestFramework/TestFramework/Assertions/Assert.cs b/src/TestFramework/TestFramework/Assertions/Assert.cs index a6ec288cf0..f630895073 100644 --- a/src/TestFramework/TestFramework/Assertions/Assert.cs +++ b/src/TestFramework/TestFramework/Assertions/Assert.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs index b43825c247..1ddbe5a731 100644 --- a/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/CollectionAssert.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// @@ -1563,8 +1558,8 @@ private static bool FindMismatchedElement(IEnumerable expected, IEnumerab // $ CONSIDER: comparison, which should result in ~n*log(n) + m*log(m) + n. // Count the occurrences of each object in the both collections - Dictionary expectedElements = GetElementCounts(expected, comparer, out int expectedNulls); - Dictionary actualElements = GetElementCounts(actual, comparer, out int actualNulls); + Dictionary expectedElements = GetElementCounts(expected, comparer, out int expectedNulls); + Dictionary actualElements = GetElementCounts(actual, comparer, out int actualNulls); if (actualNulls != expectedNulls) { @@ -1684,7 +1679,7 @@ private static string ConstructFinalMessage( /// /// compares the objects using object.Equals. /// - private class ObjectComparer : IComparer + private sealed class ObjectComparer : IComparer { int IComparer.Compare(object? x, object? y) => Equals(x, y) ? 0 : -1; } diff --git a/src/TestFramework/TestFramework/Assertions/StringAssert.cs b/src/TestFramework/TestFramework/Assertions/StringAssert.cs index 3f0de64de9..b4f88415d3 100644 --- a/src/TestFramework/TestFramework/Assertions/StringAssert.cs +++ b/src/TestFramework/TestFramework/Assertions/StringAssert.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text.RegularExpressions; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs index 661f2fd551..040c794cf4 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataRowAttribute.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting.Internal; namespace Microsoft.VisualStudio.TestTools.UnitTesting; @@ -11,7 +9,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// Attribute to define in-line data for a test method. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] -public class DataRowAttribute : Attribute, ITestDataSource +public class DataRowAttribute : Attribute, ITestDataSource, ITestDataSourceUnfoldingCapability, ITestDataSourceIgnoreCapability { /// /// Initializes a new instance of the class. @@ -55,6 +53,14 @@ public DataRowAttribute(string?[]? stringArrayData) /// public string? DisplayName { get; set; } + /// + /// Gets or sets a reason to ignore the specific test case. Setting the property to non-null value will ignore the test case. + /// + public string? IgnoreMessage { get; set; } + + /// + public TestDataSourceUnfoldingStrategy UnfoldingStrategy { get; set; } = TestDataSourceUnfoldingStrategy.Auto; + /// public IEnumerable GetData(MethodInfo methodInfo) => [Data]; diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs index e5de74e0b9..bd1f890128 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataSourceAttribute.cs @@ -1,17 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specifies connection string, table name and row access method for data driven testing. /// -/// +/// +/// +/// This works only on .NET Framework and is not supported on .NET Core or later. +/// +/// The following shows example usages for this attribute: +/// /// [DataSource("Provider=SQLOLEDB.1;Data Source=source;Integrated Security=SSPI;Initial Catalog=EqtCoverage;Persist Security Info=False", "MyTable")] /// [DataSource("dataSourceNameFromConfigFile")]. -/// +/// +/// +/// +/// [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "Compat")] [AttributeUsage(AttributeTargets.Method)] public sealed class DataSourceAttribute : Attribute @@ -97,7 +103,7 @@ public DataSourceAttribute(string connectionString, string tableName) /// /// /// - /// One of the values. If the is not initialized, this will return the default value . + /// One of the values. If the is not initialized, this will return the default value . /// public DataAccessMethod DataAccessMethod { get; } diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs index 70d7372b43..9232c36f59 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DataTestMethodAttribute.cs @@ -1,12 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// -/// Attribute for data driven test where data can be specified in-line. +/// This attribute doesn't currently provide any different functionality compared to . It's only +/// present for backward compatibility. Using is recommended, even for parameterized tests. /// [AttributeUsage(AttributeTargets.Method)] +[EditorBrowsable(EditorBrowsableState.Never)] public class DataTestMethodAttribute : TestMethodAttribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs index 151685f3b8..370ab10af2 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataAttribute.cs @@ -1,13 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#if NETCOREAPP || NET471_OR_GREATER -using System.Collections; -using System.Runtime.CompilerServices; -#endif -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; +using System.ComponentModel; using Microsoft.VisualStudio.TestTools.UnitTesting.Internal; @@ -27,13 +21,18 @@ public enum DynamicDataSourceType /// Data is declared in method. /// Method = 1, + + /// + /// The data source type is auto-detected. + /// + AutoDetect = 2, } /// /// Attribute to define dynamic data for a test method. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] -public sealed class DynamicDataAttribute : Attribute, ITestDataSource, ITestDataSourceEmptyDataSourceExceptionInfo +public sealed class DynamicDataAttribute : Attribute, ITestDataSource, ITestDataSourceEmptyDataSourceExceptionInfo, ITestDataSourceUnfoldingCapability, ITestDataSourceIgnoreCapability { private readonly string _dynamicDataSourceName; private readonly DynamicDataSourceType _dynamicDataSourceType; @@ -49,12 +48,25 @@ public sealed class DynamicDataAttribute : Attribute, ITestDataSource, ITestData /// /// Specifies whether the data is stored as property or in method. /// - public DynamicDataAttribute(string dynamicDataSourceName, DynamicDataSourceType dynamicDataSourceType = DynamicDataSourceType.Property) + [EditorBrowsable(EditorBrowsableState.Never)] + public DynamicDataAttribute(string dynamicDataSourceName, DynamicDataSourceType dynamicDataSourceType) { _dynamicDataSourceName = dynamicDataSourceName; _dynamicDataSourceType = dynamicDataSourceType; } + /// + /// Initializes a new instance of the class. + /// + /// + /// The name of method or property having test data. + /// + public DynamicDataAttribute(string dynamicDataSourceName) + { + _dynamicDataSourceName = dynamicDataSourceName; + _dynamicDataSourceType = DynamicDataSourceType.AutoDetect; + } + /// /// Initializes a new instance of the class when the test data is present in a class different /// from test method's class. @@ -69,9 +81,24 @@ public DynamicDataAttribute(string dynamicDataSourceName, DynamicDataSourceType /// /// Specifies whether the data is stored as property or in method. /// - public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclaringType, DynamicDataSourceType dynamicDataSourceType = DynamicDataSourceType.Property) + [EditorBrowsable(EditorBrowsableState.Never)] + public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclaringType, DynamicDataSourceType dynamicDataSourceType) : this(dynamicDataSourceName, dynamicDataSourceType) => _dynamicDataDeclaringType = dynamicDataDeclaringType; + /// + /// Initializes a new instance of the class when the test data is present in a class different + /// from test method's class. + /// + /// + /// The name of method or property having test data. + /// + /// + /// The declaring type of property or method having data. Useful in cases when declaring type is present in a class different from + /// test method's class. If null, declaring type defaults to test method's class type. + /// + public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclaringType) + : this(dynamicDataSourceName) => _dynamicDataDeclaringType = dynamicDataDeclaringType; + internal static TestIdGenerationStrategy TestIdGenerationStrategy { get; set; } /// @@ -85,75 +112,49 @@ public DynamicDataAttribute(string dynamicDataSourceName, Type dynamicDataDeclar public Type? DynamicDataDisplayNameDeclaringType { get; set; } /// - public IEnumerable GetData(MethodInfo methodInfo) => DynamicDataProvider.Instance.GetData(_dynamicDataDeclaringType, _dynamicDataSourceType, _dynamicDataSourceName, methodInfo); + public TestDataSourceUnfoldingStrategy UnfoldingStrategy { get; set; } = TestDataSourceUnfoldingStrategy.Auto; - /// - public string? GetDisplayName(MethodInfo methodInfo, object?[]? data) - { - if (DynamicDataDisplayName != null) - { - Type? dynamicDisplayNameDeclaringType = DynamicDataDisplayNameDeclaringType ?? methodInfo.DeclaringType; - DebugEx.Assert(dynamicDisplayNameDeclaringType is not null, "Declaring type of test data cannot be null."); - - MethodInfo method = dynamicDisplayNameDeclaringType.GetTypeInfo().GetDeclaredMethod(DynamicDataDisplayName) - ?? throw new ArgumentNullException($"{DynamicDataSourceType.Method} {DynamicDataDisplayName}"); - ParameterInfo[] parameters = method.GetParameters(); - return parameters.Length != 2 || - parameters[0].ParameterType != typeof(MethodInfo) || - parameters[1].ParameterType != typeof(object[]) || - method.ReturnType != typeof(string) || - !method.IsStatic || - !method.IsPublic - ? throw new ArgumentNullException( - string.Format( - CultureInfo.InvariantCulture, - FrameworkMessages.DynamicDataDisplayName, - DynamicDataDisplayName, - nameof(String), - string.Join(", ", nameof(MethodInfo), typeof(object[]).Name))) - : method.Invoke(null, [methodInfo, data]) as string; - } + /// + /// Gets or sets a reason to ignore this dynamic data source. Setting the property to non-null value will ignore the dynamic data source. + /// + public string? IgnoreMessage { get; set; } - return TestDataSourceUtilities.ComputeDefaultDisplayName(methodInfo, data, TestIdGenerationStrategy); - } + /// + public IEnumerable GetData(MethodInfo methodInfo) + => DynamicDataProvider.Instance.GetData(_dynamicDataDeclaringType, _dynamicDataSourceType, _dynamicDataSourceName, methodInfo); - private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnumerable? data) + /// + public string? GetDisplayName(MethodInfo methodInfo, object?[]? data) { - if (dataSource is IEnumerable enumerableObjectArray) + if (DynamicDataDisplayName == null) { - data = enumerableObjectArray; - return true; + return TestDataSourceUtilities.ComputeDefaultDisplayName(methodInfo, data, TestIdGenerationStrategy); } -#if NETCOREAPP || NET471_OR_GREATER - if (dataSource is IEnumerable enumerable) + Type? dynamicDisplayNameDeclaringType = DynamicDataDisplayNameDeclaringType ?? methodInfo.DeclaringType; + DebugEx.Assert(dynamicDisplayNameDeclaringType is not null, "Declaring type of test data cannot be null."); + + MethodInfo method = dynamicDisplayNameDeclaringType.GetTypeInfo().GetDeclaredMethod(DynamicDataDisplayName) + ?? throw new ArgumentNullException($"{DynamicDataSourceType.Method} {DynamicDataDisplayName}"); + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length != 2 + || parameters[0].ParameterType != typeof(MethodInfo) + || parameters[1].ParameterType != typeof(object[]) + || method.ReturnType != typeof(string) + || !method.IsStatic + || !method.IsPublic) { - List objects = new(); - foreach (object? entry in enumerable) - { - if (entry is not ITuple tuple - || (objects.Count > 0 && objects[^1].Length != tuple.Length)) - { - data = null; - return false; - } - - object[] array = new object[tuple.Length]; - for (int i = 0; i < tuple.Length; i++) - { - array[i] = tuple[i]!; - } - - objects.Add(array); - } - - data = objects; - return true; + throw new ArgumentNullException( + string.Format( + CultureInfo.InvariantCulture, + FrameworkMessages.DynamicDataDisplayName, + DynamicDataDisplayName, + nameof(String), + string.Join(", ", nameof(MethodInfo), typeof(object[]).Name))); } -#endif - data = null; - return false; + // Try to get the display name from the method. + return method.Invoke(null, [methodInfo, data]) as string; } string? ITestDataSourceEmptyDataSourceExceptionInfo.GetPropertyOrMethodNameForEmptyDataSourceException() diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataProvider.cs b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataProvider.cs index 9cc9745589..6b04212aa6 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataProvider.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/DynamicDataProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; internal static class DynamicDataProvider diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/IDynamicDataOperations.cs b/src/TestFramework/TestFramework/Attributes/DataSource/IDynamicDataOperations.cs index b54bef8ccc..e865440d94 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/IDynamicDataOperations.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/IDynamicDataOperations.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; internal interface IDynamicDataOperations diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs index 9d7bd1f1db..6d957075d6 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryAttribute.cs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Specifies how to discover tests. /// [AttributeUsage(AttributeTargets.Assembly)] +[EditorBrowsable(EditorBrowsableState.Never)] public class TestDataSourceDiscoveryAttribute : Attribute { /// @@ -15,7 +18,8 @@ public class TestDataSourceDiscoveryAttribute : Attribute /// /// The to use when discovering tests. /// - public TestDataSourceDiscoveryAttribute(TestDataSourceDiscoveryOption discoveryOption) => DiscoveryOption = discoveryOption; + public TestDataSourceDiscoveryAttribute(TestDataSourceDiscoveryOption discoveryOption) + => DiscoveryOption = discoveryOption; /// /// Gets the discovery option. diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryOption.cs b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryOption.cs index 46c3be787b..1259baf4a7 100644 --- a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryOption.cs +++ b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceDiscoveryOption.cs @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The supported discovery modes for tests. /// +[EditorBrowsable(EditorBrowsableState.Never)] public enum TestDataSourceDiscoveryOption { /// diff --git a/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceOptionsAttribute.cs b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceOptionsAttribute.cs new file mode 100644 index 0000000000..6550f28093 --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/DataSource/TestDataSourceOptionsAttribute.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Specifies options for all of the current assembly. +/// +/// +/// These options can be override by individual attribute. +[AttributeUsage(AttributeTargets.Assembly, Inherited = false)] +public sealed class TestDataSourceOptionsAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// The to use when executing parameterized tests. + /// + public TestDataSourceOptionsAttribute(TestDataSourceUnfoldingStrategy unfoldingStrategy) + => UnfoldingStrategy = unfoldingStrategy; + + /// + /// Gets the test unfolding strategy. + /// + public TestDataSourceUnfoldingStrategy UnfoldingStrategy { get; } +} diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs index 6032f64691..7ed4c41298 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionAttribute.cs @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Attribute that specifies to expect an exception of the specified type. /// +/// +/// We recommend using one of the Assert.ThrowsException or Assert.ThrowsExceptionAsync overload instead of using this attribute. +/// [AttributeUsage(AttributeTargets.Method)] public sealed class ExpectedExceptionAttribute : ExpectedExceptionBaseAttribute { diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs index 6efdc80d90..1ce1a23c30 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/ExpectedExceptionBaseAttribute.cs @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Base class for attributes that specify to expect an exception from a unit test. /// +/// +/// We recommend using one of the Assert.ThrowsException or Assert.ThrowsExceptionAsync overload instead of using this attribute. +/// [AttributeUsage(AttributeTargets.Method)] public abstract class ExpectedExceptionBaseAttribute : Attribute { diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs index 0f449e9b87..c175d68358 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/IgnoreAttribute.cs @@ -4,13 +4,16 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// -/// The ignore attribute. +/// This attribute is used to ignore a test class or a test method, with an optional message. /// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +/// +/// This attribute isn't inherited. Applying it to a base class will not cause derived classes to be ignored. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false)] public sealed class IgnoreAttribute : Attribute { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with an empty message. /// public IgnoreAttribute() : this(string.Empty) @@ -26,7 +29,7 @@ public IgnoreAttribute() public IgnoreAttribute(string? message) => IgnoreMessage = message; /// - /// Gets the owner. + /// Gets the ignore message indicating the reason for ignoring the test method or test class. /// public string? IgnoreMessage { get; } } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/STATestClassAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/STATestClassAttribute.cs index 958cd4358e..a82ef52330 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/STATestClassAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/STATestClassAttribute.cs @@ -6,7 +6,5 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The test class attribute. /// -[AttributeUsage(AttributeTargets.Class)] -public class STATestClassAttribute : TestClassAttribute -{ -} +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +public class STATestClassAttribute : TestClassAttribute; diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs index c592252ee1..fd4633692e 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestCategoryAttribute.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs index 77d80855cd..0da97a08d2 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestClassAttribute.cs @@ -4,9 +4,17 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// -/// The test class attribute. +/// This attribute is used to mark test classes. /// -[AttributeUsage(AttributeTargets.Class)] +/// +/// Test classes must be: +/// +/// public, or if is used then it can be internal. +/// not static +/// not generic +/// +/// +[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class TestClassAttribute : Attribute { /// @@ -18,4 +26,9 @@ public class TestClassAttribute : Attribute public virtual TestMethodAttribute? GetTestMethodAttribute(TestMethodAttribute? testMethodAttribute) => // If TestMethod is not extended by derived class then return back the original TestMethodAttribute testMethodAttribute; + + /// + /// Gets or sets a reason to ignore the test class. Setting the property to non-null value will ignore the test class. + /// + public string? IgnoreMessage { get; set; } } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs index bca969e00f..1d42b34d43 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestMethodAttribute.cs @@ -3,9 +3,29 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved - The warning is for ValueTask. /// -/// The test method attribute. +/// This attribute is used to mark test methods. /// +/// +/// +/// +/// When using other attributes like or , it +/// the use of is still required. +/// +/// +/// Test methods must be: +/// +/// public, or if is used then it can be internal. +/// not static +/// not generic +/// not abstract +/// return type is either , , or . If , then it shouldn't be . +/// +/// +/// +/// +#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved// XML comment has cref attribute that could not be resolved [AttributeUsage(AttributeTargets.Method)] public class TestMethodAttribute : Attribute { @@ -30,6 +50,11 @@ public TestMethodAttribute() /// public string? DisplayName { get; } + /// + /// Gets or sets a reason to ignore the test method. Setting the property to non-null value will ignore the test method. + /// + public string? IgnoreMessage { get; set; } + /// /// Executes a test method. /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestPropertyAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestPropertyAttribute.cs index 89d61dd3fe..522f6ed906 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestPropertyAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestPropertyAttribute.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// /// The test property attribute. /// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] public class TestPropertyAttribute : Attribute { /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs index 1b0b35fe7b..0ccf2e316c 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestResult.cs @@ -24,6 +24,8 @@ public class TestResult /// public UnitTestOutcome Outcome { get; set; } + internal string? IgnoreReason { get; set; } + /// /// Gets or sets the exception thrown when test is failed. /// diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs index 6258616497..3c5aab0c09 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TestTimeout.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Friends.cs b/src/TestFramework/TestFramework/Friends.cs index 6d6ee74825..ebbc3313b8 100644 --- a/src/TestFramework/TestFramework/Friends.cs +++ b/src/TestFramework/TestFramework/Friends.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.CompilerServices; - [assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/TestFramework/TestFramework/GenericParameterHelper.cs b/src/TestFramework/TestFramework/GenericParameterHelper.cs index 94e0308c15..a689fed725 100644 --- a/src/TestFramework/TestFramework/GenericParameterHelper.cs +++ b/src/TestFramework/TestFramework/GenericParameterHelper.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Interfaces/EmptyDataSourceExceptionInfoExtensions.cs b/src/TestFramework/TestFramework/Interfaces/EmptyDataSourceExceptionInfoExtensions.cs index 6033eaa74f..b281bb829c 100644 --- a/src/TestFramework/TestFramework/Interfaces/EmptyDataSourceExceptionInfoExtensions.cs +++ b/src/TestFramework/TestFramework/Interfaces/EmptyDataSourceExceptionInfoExtensions.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; internal static class EmptyDataSourceExceptionInfoExtensions diff --git a/src/TestFramework/TestFramework/Interfaces/ITestDataSource.cs b/src/TestFramework/TestFramework/Interfaces/ITestDataSource.cs index 55fe97f6e6..ea1d9dbc26 100644 --- a/src/TestFramework/TestFramework/Interfaces/ITestDataSource.cs +++ b/src/TestFramework/TestFramework/Interfaces/ITestDataSource.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Interfaces/ITestDataSourceEmptyDataSourceExceptionInfo.cs b/src/TestFramework/TestFramework/Interfaces/ITestDataSourceEmptyDataSourceExceptionInfo.cs index e704a2f5ee..17aaece396 100644 --- a/src/TestFramework/TestFramework/Interfaces/ITestDataSourceEmptyDataSourceExceptionInfo.cs +++ b/src/TestFramework/TestFramework/Interfaces/ITestDataSourceEmptyDataSourceExceptionInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; internal interface ITestDataSourceEmptyDataSourceExceptionInfo diff --git a/src/TestFramework/TestFramework/Interfaces/ITestDataSourceIgnoreCapability.cs b/src/TestFramework/TestFramework/Interfaces/ITestDataSourceIgnoreCapability.cs new file mode 100644 index 0000000000..0a22b7dc5f --- /dev/null +++ b/src/TestFramework/TestFramework/Interfaces/ITestDataSourceIgnoreCapability.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Specifies the capability of a test data source to be ignored and define the ignore reason. +/// +public interface ITestDataSourceIgnoreCapability +{ + /// + /// Gets or sets a reason to ignore the test data source. Setting the property to non-null value will ignore the test data source. + /// + string? IgnoreMessage { get; set; } +} diff --git a/src/TestFramework/TestFramework/Interfaces/ITestDataSourceUnfoldingCapability.cs b/src/TestFramework/TestFramework/Interfaces/ITestDataSourceUnfoldingCapability.cs new file mode 100644 index 0000000000..2f5ced8910 --- /dev/null +++ b/src/TestFramework/TestFramework/Interfaces/ITestDataSourceUnfoldingCapability.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Specifies the capability of a test data source to define how parameterized tests should be executed, either as +/// individual test cases for each data row or as a single test case. This affects the test results and the UI +/// representation of the tests. +/// +public interface ITestDataSourceUnfoldingCapability +{ + TestDataSourceUnfoldingStrategy UnfoldingStrategy { get; } +} + +/// +/// Specifies how parameterized tests should be executed, either as individual test cases for each data row or as a +/// single test case. This affects the test results and the UI representation of the tests. +/// +public enum TestDataSourceUnfoldingStrategy : byte +{ + /// + /// MSTest will decide whether to unfold the parameterized test based on value from the assembly level attribute + /// . If no assembly level attribute is specified, then the default + /// configuration is to unfold. + /// + Auto, + + /// + /// Each data row is treated as a separate test case. + /// + Unfold, + + /// + /// The parameterized test is not unfolded; all data rows are treated as a single test case. + /// + Fold, +} diff --git a/src/TestFramework/TestFramework/Interfaces/ITestMethod.cs b/src/TestFramework/TestFramework/Interfaces/ITestMethod.cs index 7075480d95..3f406901e8 100644 --- a/src/TestFramework/TestFramework/Interfaces/ITestMethod.cs +++ b/src/TestFramework/TestFramework/Interfaces/ITestMethod.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Internal/DebugEx.cs b/src/TestFramework/TestFramework/Internal/DebugEx.cs index 2f988ea9b1..8ce56c4b86 100644 --- a/src/TestFramework/TestFramework/Internal/DebugEx.cs +++ b/src/TestFramework/TestFramework/Internal/DebugEx.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; [SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] diff --git a/src/TestFramework/TestFramework/Internal/ReflectionTestMethodInfo.cs b/src/TestFramework/TestFramework/Internal/ReflectionTestMethodInfo.cs index faccb92a42..6eee7ba64e 100644 --- a/src/TestFramework/TestFramework/Internal/ReflectionTestMethodInfo.cs +++ b/src/TestFramework/TestFramework/Internal/ReflectionTestMethodInfo.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting.Internal; internal sealed class ReflectionTestMethodInfo : MethodInfo @@ -30,6 +27,12 @@ public ReflectionTestMethodInfo(MethodInfo methodInfo, string? displayName) public override Type? ReflectedType => _methodInfo.ReflectedType; + public override bool ContainsGenericParameters => _methodInfo.ContainsGenericParameters; + + public override bool IsGenericMethod => _methodInfo.IsGenericMethod; + + public override bool IsGenericMethodDefinition => _methodInfo.IsGenericMethodDefinition; + public override MethodInfo GetBaseDefinition() => _methodInfo.GetBaseDefinition(); public override object[] GetCustomAttributes(bool inherit) => _methodInfo.GetCustomAttributes(inherit); @@ -43,4 +46,10 @@ public ReflectionTestMethodInfo(MethodInfo methodInfo, string? displayName) public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) => _methodInfo.Invoke(obj, invokeAttr, binder, parameters, culture); public override bool IsDefined(Type attributeType, bool inherit) => _methodInfo.IsDefined(attributeType, inherit); + + public override MethodInfo MakeGenericMethod(params Type[] typeArguments) => new ReflectionTestMethodInfo(_methodInfo.MakeGenericMethod(typeArguments), DisplayName); + + public override Type[] GetGenericArguments() => _methodInfo.GetGenericArguments(); + + public override MethodInfo GetGenericMethodDefinition() => _methodInfo.GetGenericMethodDefinition(); } diff --git a/src/TestFramework/TestFramework/Internal/StringEx.cs b/src/TestFramework/TestFramework/Internal/StringEx.cs index ac7c314feb..27a38704bc 100644 --- a/src/TestFramework/TestFramework/Internal/StringEx.cs +++ b/src/TestFramework/TestFramework/Internal/StringEx.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; internal static class StringEx diff --git a/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs b/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs index 4a5efebcb5..525f25fda2 100644 --- a/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs +++ b/src/TestFramework/TestFramework/Internal/TestDataSourceUtilities.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting.Internal; internal static class TestDataSourceUtilities diff --git a/src/TestFramework/TestFramework/Internal/UtfHelper.cs b/src/TestFramework/TestFramework/Internal/UtfHelper.cs index 73ebb2c873..63356e0cb7 100644 --- a/src/TestFramework/TestFramework/Internal/UtfHelper.cs +++ b/src/TestFramework/TestFramework/Internal/UtfHelper.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Text; - namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// diff --git a/src/TestFramework/TestFramework/Logger.cs b/src/TestFramework/TestFramework/Logger.cs index c12dcae50f..46c4bbd224 100644 --- a/src/TestFramework/TestFramework/Logger.cs +++ b/src/TestFramework/TestFramework/Logger.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - namespace Microsoft.VisualStudio.TestTools.UnitTesting.Logging; /// diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt index 23ce9ae2e1..d2f9fb2434 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable abstract Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute.Verify(System.Exception! exception) -> void abstract Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryBaseAttribute.TestCategories.get -> System.Collections.Generic.IList! Microsoft.VisualStudio.TestTools.UnitTesting.AssemblyCleanupAttribute @@ -8,17 +8,17 @@ Microsoft.VisualStudio.TestTools.UnitTesting.AssemblyInitializeAttribute.Assembl Microsoft.VisualStudio.TestTools.UnitTesting.Assert Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException() -> void -Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(string! msg, System.Exception! ex) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException.AssertFailedException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException() -> void -Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(string! msg, System.Exception! ex) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException.AssertInconclusiveException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute() -> void Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior cleanupBehavior) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior inheritanceBehavior) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior inheritanceBehavior, Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior cleanupBehavior) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.ClassCleanupAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior inheritanceBehavior) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.CleanupBehavior.get -> Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior? Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute.InheritanceBehavior.get -> Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior @@ -51,6 +51,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.DataRowAttribute(s Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.DisplayName.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.DisplayName.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.GetData(System.Reflection.MethodInfo! methodInfo) -> System.Collections.Generic.IEnumerable! +Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.UnfoldingStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy +Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.UnfoldingStrategy.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute.ConnectionString.get -> string? Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute.DataAccessMethod.get -> Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod @@ -79,6 +81,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataDis Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataDisplayNameDeclaringType.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.GetData(System.Reflection.MethodInfo! methodInfo) -> System.Collections.Generic.IEnumerable! Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.GetDisplayName(System.Reflection.MethodInfo! methodInfo, object?[]? data) -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.UnfoldingStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.UnfoldingStrategy.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Method = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Property = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType @@ -89,8 +93,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute.AllowDerivedTypes.get -> bool Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute.AllowDerivedTypes.set -> void Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute.ExceptionType.get -> System.Type! -Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute.ExpectedExceptionAttribute(System.Type! exceptionType) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute.ExpectedExceptionAttribute(System.Type! exceptionType, string! noExceptionMessage) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute.ExpectedExceptionAttribute(System.Type! exceptionType) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute.ExpectedExceptionBaseAttribute() -> void Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionBaseAttribute.ExpectedExceptionBaseAttribute(string? noExceptionMessage) -> void @@ -113,11 +117,13 @@ Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior.BeforeEachDeriv Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior.None = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException() -> void -Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(string! msg, System.Exception! ex) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.InternalTestFailureException.InternalTestFailureException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource.GetData(System.Reflection.MethodInfo! methodInfo) -> System.Collections.Generic.IEnumerable! Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSource.GetDisplayName(System.Reflection.MethodInfo! methodInfo, object?[]? data) -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceUnfoldingCapability +Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceUnfoldingCapability.UnfoldingStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.Arguments.get -> object?[]? Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod.GetAllAttributes(bool inherit) -> System.Attribute![]? @@ -163,6 +169,13 @@ Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryAttribute.Te Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryOption Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryOption.DuringDiscovery = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryOption Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryOption.DuringExecution = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceDiscoveryOption +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceOptionsAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceOptionsAttribute.TestDataSourceOptionsAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy unfoldingStrategy) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceOptionsAttribute.UnfoldingStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy.Auto = 0 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy.Fold = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy +Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy.Unfold = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestDataSourceUnfoldingStrategy Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy.DisplayName = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy.FullyQualified = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy @@ -220,8 +233,8 @@ Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.TimeoutAttribute(i Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute.TimeoutAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.TestTimeout timeout) -> void Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException() -> void -Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(string! msg, System.Exception! ex) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestAssertException.UnitTestAssertException(string! msg) -> void Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome.Aborted = 6 -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome.Error = 4 -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome @@ -239,187 +252,187 @@ Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute.WorkItemAttribute override Microsoft.VisualStudio.TestTools.UnitTesting.GenericParameterHelper.Equals(object? obj) -> bool override Microsoft.VisualStudio.TestTools.UnitTesting.GenericParameterHelper.GetHashCode() -> int override Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute.TestCategories.get -> System.Collections.Generic.IList! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual, string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Equals(object? objA, object? objB) -> bool static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail() -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail(string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail(string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Fail(string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive() -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive(string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive(string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Inconclusive(string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ReplaceNullChars(string? input) -> string? static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.That.get -> Microsoft.VisualStudio.TestTools.UnitTesting.Assert! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Action! action) -> T! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Action! action, string! message) -> T! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Action! action, string! message, params object?[]? parameters) -> T! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Func! action) -> T! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Func! action, string! message) -> T! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Action! action, string! message) -> T! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Action! action) -> T! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Func! action, string! message, params object?[]? parameters) -> T! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExceptionAsync(System.Func! action) -> System.Threading.Tasks.Task! -static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExceptionAsync(System.Func! action, string! message) -> System.Threading.Tasks.Task! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Func! action, string! message) -> T! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsException(System.Func! action) -> T! static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExceptionAsync(System.Func! action, string! message, params object?[]? parameters) -> System.Threading.Tasks.Task! -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreInstancesOfType(System.Collections.ICollection? collection, System.Type? expectedType) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreInstancesOfType(System.Collections.ICollection? collection, System.Type? expectedType, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExceptionAsync(System.Func! action, string! message) -> System.Threading.Tasks.Task! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExceptionAsync(System.Func! action) -> System.Threading.Tasks.Task! static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreInstancesOfType(System.Collections.ICollection? collection, System.Type? expectedType, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreNotNull(System.Collections.ICollection? collection) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreNotNull(System.Collections.ICollection? collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreInstancesOfType(System.Collections.ICollection? collection, System.Type? expectedType, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreInstancesOfType(System.Collections.ICollection? collection, System.Type? expectedType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreNotNull(System.Collections.ICollection? collection, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreUnique(System.Collections.ICollection? collection) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreUnique(System.Collections.ICollection? collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreNotNull(System.Collections.ICollection? collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreNotNull(System.Collections.ICollection? collection) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreUnique(System.Collections.ICollection? collection, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreUnique(System.Collections.ICollection? collection, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AllItemsAreUnique(System.Collections.ICollection? collection) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual(System.Collections.ICollection? expected, System.Collections.ICollection? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual, System.Collections.IComparer? comparer) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEqual(System.Collections.ICollection? notExpected, System.Collections.ICollection? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.ICollection? expected, System.Collections.ICollection? actual) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Contains(System.Collections.ICollection? collection, object? element) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Contains(System.Collections.ICollection? collection, object? element, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreNotEquivalent(System.Collections.Generic.IEnumerable? expected, System.Collections.Generic.IEnumerable? actual, System.Collections.Generic.IEqualityComparer? comparer) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Contains(System.Collections.ICollection? collection, object? element, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Contains(System.Collections.ICollection? collection, object? element, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Contains(System.Collections.ICollection? collection, object? element) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.DoesNotContain(System.Collections.ICollection? collection, object? element) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsNotSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.IsSubsetOf(System.Collections.ICollection? subset, System.Collections.ICollection? superset) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.That.get -> Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert! static Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.TestIdGenerationStrategy.get -> Microsoft.VisualStudio.TestTools.UnitTesting.TestIdGenerationStrategy static Microsoft.VisualStudio.TestTools.UnitTesting.Logging.Logger.LogMessage(string! format, params object?[]! args) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.Logging.Logger.OnLogMessage -> Microsoft.VisualStudio.TestTools.UnitTesting.Logging.Logger.LogMessageHandler? -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, string? message, System.StringComparison comparisonType, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring, System.StringComparison comparisonType) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.DoesNotMatch(string? value, System.Text.RegularExpressions.Regex? pattern) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.DoesNotMatch(string? value, System.Text.RegularExpressions.Regex? pattern, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains(string? value, string? substring) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.DoesNotMatch(string? value, System.Text.RegularExpressions.Regex? pattern, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.DoesNotMatch(string? value, System.Text.RegularExpressions.Regex? pattern, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.DoesNotMatch(string? value, System.Text.RegularExpressions.Regex? pattern) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message, System.StringComparison comparisonType, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring, System.StringComparison comparisonType) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.EndsWith(string? value, string? substring) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern, string? message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Matches(string? value, System.Text.RegularExpressions.Regex? pattern) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, params object?[]? parameters) -> void -static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, System.StringComparison comparisonType, params object?[]? parameters) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message, System.StringComparison comparisonType) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, string? message) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring, System.StringComparison comparisonType) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.StartsWith(string? value, string? substring) -> void static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.That.get -> Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert! static readonly Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupExecutionAttribute.DefaultClassCleanupLifecycle -> Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior static readonly Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute.DefaultDataAccessMethod -> Microsoft.VisualStudio.TestTools.UnitTesting.DataAccessMethod diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index ab058de62d..01ad108d22 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1,249 @@ -#nullable enable +#nullable enable +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendLiteral(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, System.IEquatable? expected, System.IEquatable? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AssertAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, System.Collections.Generic.IEqualityComparer? comparer, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AssertAreNotEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AssertAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, System.Collections.Generic.IEqualityComparer? comparer, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AssertAreNotSameInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AssertAreNotSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? notExpected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AssertAreSameInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AssertAreSameInterpolatedStringHandler(int literalLength, int formattedCount, TArgument? expected, TArgument? actual, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AssertGenericIsInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AssertGenericIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AssertIsFalseInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AssertIsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AssertIsInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AssertIsInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, System.Type? expectedType, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AssertIsNotInstanceOfTypeInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AssertIsNotInstanceOfTypeInterpolatedStringHandler(int literalLength, int formattedCount, object? value, System.Type? wrongType, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AssertIsNotNullInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AssertIsNotNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AssertIsNullInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AssertIsNullInterpolatedStringHandler(int literalLength, int formattedCount, object? value, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AssertIsTrueInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AssertIsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool? condition, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal expected, decimal actual, decimal delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, double expected, double actual, double delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, float expected, float actual, float delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, long expected, long actual, long delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AssertNonGenericAreEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, decimal notExpected, decimal actual, decimal delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, double notExpected, double actual, double delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, float notExpected, float actual, float delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, long notExpected, long actual, long delta, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AssertNonGenericAreNotEqualInterpolatedStringHandler(int literalLength, int formattedCount, string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AssertNonStrictThrowsInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(string? value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value, int alignment) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value, int alignment, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(T value, string? format) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendLiteral(string! value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler() -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AssertThrowsExactlyInterpolatedStringHandler(int literalLength, int formattedCount, System.Action! action, out bool shouldAppend) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.IgnoreMessage.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.DataRowAttribute.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.IgnoreMessage.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.AutoDetect = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType +*REMOVED*Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType = Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Property) -> void +*REMOVED*Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataAttribute.DynamicDataAttribute(string! dynamicDataSourceName, System.Type! dynamicDataDeclaringType, Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType dynamicDataSourceType = Microsoft.VisualStudio.TestTools.UnitTesting.DynamicDataSourceType.Property) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceIgnoreCapability +Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceIgnoreCapability.IgnoreMessage.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.ITestDataSourceIgnoreCapability.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.IgnoreMessage.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.IgnoreMessage.get -> string? +Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute.IgnoreMessage.set -> void +Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome.Ignored = 10 -> Microsoft.VisualStudio.TestTools.UnitTesting.UnitTestOutcome +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(decimal expected, decimal actual, decimal delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(double expected, double actual, double delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(float expected, float actual, float delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(long expected, long actual, long delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(string? expected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(System.IEquatable? expected, System.IEquatable? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreEqual(T? expected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(decimal notExpected, decimal actual, decimal delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(double notExpected, double actual, double delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(float notExpected, float actual, float delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(long notExpected, long actual, long delta, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(string? notExpected, string? actual, bool ignoreCase, System.Globalization.CultureInfo? culture, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotEqual(T? notExpected, T? actual, System.Collections.Generic.IEqualityComparer? comparer, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreNotSame(T? notExpected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AreSame(T? expected, T? actual, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsFalse(bool? condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, System.Type? expectedType, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, out T instance, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsInstanceOfType(object? value, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, System.Type? wrongType, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotInstanceOfType(object? value, Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNotNull(object? value, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsNull(object? value, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.IsTrue(bool? condition, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler message) -> void +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler message) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, string! message = "", params object![]! messageArgs) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.Throws(System.Action! action, System.Func! messageBuilder) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsAsync(System.Func! action, string! message = "", params object![]! messageArgs) -> System.Threading.Tasks.Task! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsAsync(System.Func! action, System.Func! messageBuilder) -> System.Threading.Tasks.Task! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, ref Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler message) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, string! message = "", params object![]! messageArgs) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactly(System.Action! action, System.Func! messageBuilder) -> TException! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactlyAsync(System.Func! action, string! message = "", params object![]! messageArgs) -> System.Threading.Tasks.Task! +static Microsoft.VisualStudio.TestTools.UnitTesting.Assert.ThrowsExactlyAsync(System.Func! action, System.Func! messageBuilder) -> System.Threading.Tasks.Task! diff --git a/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Shipped.txt b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..9a182b770a --- /dev/null +++ b/src/TestFramework/TestFramework/PublicAPI/net/PublicAPI.Unshipped.txt @@ -0,0 +1,33 @@ +#nullable enable +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreNotSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertAreSameInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertGenericIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsFalseInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotInstanceOfTypeInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNotNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsNullInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertIsTrueInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonGenericAreNotEqualInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertNonStrictThrowsInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.Assert.AssertThrowsExactlyInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan value, int alignment = 0, string? format = null) -> void diff --git a/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs b/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs index 67efb2d944..9fd4712ea7 100644 --- a/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs +++ b/src/TestFramework/TestFramework/Resources/FrameworkMessages.Designer.cs @@ -295,7 +295,7 @@ internal static string DynamicDataIEnumerableEmpty { } /// - /// Looks up a localized string similar to Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core).. + /// Looks up a localized string similar to Property or method {0} on {1} return type is not assignable to 'IEnumerable'.. /// internal static string DynamicDataIEnumerableNull { get { diff --git a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx index 8a0a86b9df..dd36b5c2f8 100644 --- a/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx +++ b/src/TestFramework/TestFramework/Resources/FrameworkMessages.resx @@ -263,7 +263,7 @@ Actual: {2} UITestMethodAttribute.DispatcherQueue should not be null. To use UITestMethodAttribute within a WinUI Desktop App, remember to set the static UITestMethodAttribute.DispatcherQueue during the test initialization. - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. Value returned by property or method {0} shouldn't be null. @@ -287,4 +287,4 @@ Actual: {2} Dynamic data property '{0}' should be static and have a getter. - \ No newline at end of file + diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf index c5f80ae742..efcae52e82 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.cs.xlf @@ -253,8 +253,8 @@ SkuteÄnost: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - Vlastnost nebo metoda {0} na návratovém typu {1} se nedá pÅ™iÅ™adit k „IEnumerable<object[]>“ (ani „IEnumerable<ITuple>“ pro .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + Vlastnost nebo metoda {0} na návratovém typu {1} se nedá pÅ™iÅ™adit k „IEnumerable“. diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf index 28fadee2b5..57407eec58 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.de.xlf @@ -253,8 +253,8 @@ Tatsächlich: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - Die Eigenschaft oder Methode "{0}" für Rückgabetyp "{1}" kann "IEnumerable<object[]>" nicht zugewiesen werden (auch nicht "IEnumerable<ITuple>" für .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + Eigenschaft oder Methode {0} für {1} Rückgabetyp kann „IEnumerable“ nicht zugewiesen werden. diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf index cd8b47406d..8a290bc795 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.es.xlf @@ -253,8 +253,8 @@ Real: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - La propiedad o el método {0} en {1} tipo de valor devuelto no se puede asignar a "IEnumerable<objecto[]>" (ni a "IEnumerable<ITuple>" para .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + La propiedad o el método {0} en el tipo de retorno {1} no se puede asignar a "IEnumerable". diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf index 5ed63942ad..18dbed07b6 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.fr.xlf @@ -253,8 +253,8 @@ Réel : {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - La propriété ou la méthode {0} sur le type de retour {1} ne peut pas être attribuée à « IEnumerable<object[]> » (ni à « IEnumerable<ITuple> » pour .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + La propriété ou la méthode {0} sur le type de retour {1} ne peut pas être attribuée à « IEnumerable ». diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf index 71bcd535ff..1006368d73 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.it.xlf @@ -253,8 +253,8 @@ Effettivo: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - La proprietà o il metodo {0} su {1} tipo restituito non è assegnabile a 'IEnumerable<object[]>' (né a 'IEnumerable<ITuple>' per .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + La proprietà o il metodo {0} su {1} tipo restituito non è assegnabile a 'IEnumerable'. diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf index b59c9fcec1..b58f7b2049 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ja.xlf @@ -253,8 +253,8 @@ Actual: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - {1} 戻り値ã®åž‹ã®ãƒ—ロパティã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ {0} ã¯ã€'IEnumerable<object[]>' (.NET Core ã®å ´åˆã¯ 'IEnumerable<ITuple>' ã«å‰²ã‚Šå½“ã¦ã§ãã¾ã›ã‚“)。 + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + {1} 戻り値ã®åž‹ã®ãƒ—ロパティã¾ãŸã¯ãƒ¡ã‚½ãƒƒãƒ‰ {0} ã¯ã€'IEnumerable' ã«å‰²ã‚Šå½“ã¦ã§ãã¾ã›ã‚“。 diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf index 115b45d027..fe070e4435 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ko.xlf @@ -253,8 +253,8 @@ Actual: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - {1} 반환 형ì‹ì˜ ì†ì„± ë˜ëŠ” 메서드 {0}ì€(는) 'IEnumerable<object[]>'(.NET Coreì˜ ê²½ìš° 'IEnumerable<ITuple>')ì— í• ë‹¹í•  수 없습니다. + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + {1} 반환 형ì‹ì˜ ì†ì„± ë˜ëŠ” 메서드 {0}ì€(는) 'IEnumerable'ì— í• ë‹¹í•  수 없습니다. diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf index a0679c8cdf..e4b1caef63 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pl.xlf @@ -253,8 +253,8 @@ Rzeczywiste: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - WÅ‚aÅ›ciwoÅ›ci lub metody {0} w zwracanym typie {1} nie można przypisać do elementu „IEnumerable<object[]>†(ani „IEnumerable<ITuple>†dla platformy .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + WÅ‚aÅ›ciwoÅ›ci lub metody {0} dla zwracanego typu {1} nie można przypisać do elementu „IEnumerableâ€. diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf index cb285ff60e..9adbd43bab 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.pt-BR.xlf @@ -253,8 +253,8 @@ Real: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - A propriedade ou método {0} no {1} tipo de retorno não pode ser atribuído a 'IEnumerable<object[]>†(nem 'IEnumerable<ITuple>†para .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + A propriedade ou o método {0} no tipo de retorno {1} não podem ser atribuídos a "IEnumerable". diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf index 2daf879f2e..5c4480127b 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.ru.xlf @@ -253,8 +253,8 @@ Actual: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - СвойÑтво или метод {0} возвращаемого типа {1} не могут быть назначены объекту "IEnumerable<[]>" (или "IEnumerable<ITuple>" Ð´Ð»Ñ .NET Core). + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + СвойÑтво или метод {0} в {1} тип возвращаемого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð½Ðµ может быть назначен "IEnumerable". diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf index 870ccfbf3d..b6bd361bb9 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.tr.xlf @@ -253,8 +253,8 @@ Gerçekte olan: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - {1} dönüş türündeki {0} özelliÄŸi veya yöntemi 'IEnumerable<object[]>' öğesine (veya .NET Core için 'IEnumerable<ITuple>' öğesine) atanamaz. + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + {1} dönüş türündeki {0} özelliÄŸi veya yöntemi 'IEnumerable' öğesine atanamaz. diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf index 8ef99214ca..3743d96cd0 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hans.xlf @@ -253,8 +253,8 @@ Actual: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - {1} 返回类型上的属性或方法 {0} ä¸èƒ½åˆ†é…ç»™ "IEnumerable<object[]>" (对于 .NET Core,也ä¸èƒ½åˆ†é…ç»™ "IEnumerable<ITuple>")。 + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + {1} 返回类型上的属性或方法 {0} ä¸èƒ½åˆ†é…ç»™ "IEnumerable"。 diff --git a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf index 4fb282577d..8ab05baa18 100644 --- a/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf +++ b/src/TestFramework/TestFramework/Resources/xlf/FrameworkMessages.zh-Hant.xlf @@ -253,8 +253,8 @@ Actual: {2} - Property or method {0} on {1} return type is not assignable to 'IEnumerable<object[]>' (nor 'IEnumerable<ITuple>' for .NET Core). - {1} 傳回類型上的屬性或方法 {0} 無法指派給 'IEnumerable<object[]>' (或 'IEnumerable<ITuple>' 給 .NET Core) + Property or method {0} on {1} return type is not assignable to 'IEnumerable'. + {1} 傳回類型上的屬性或方法 {0} 無法指派給 'IEnumerable'。 diff --git a/src/TestFramework/TestFramework/TestFramework.csproj b/src/TestFramework/TestFramework/TestFramework.csproj index 8f67b0069d..79ae688ade 100644 --- a/src/TestFramework/TestFramework/TestFramework.csproj +++ b/src/TestFramework/TestFramework/TestFramework.csproj @@ -14,6 +14,9 @@ + + + diff --git a/src/TestFramework/TestFramework/UnitTestOutcome.cs b/src/TestFramework/TestFramework/UnitTestOutcome.cs index 0f6658de56..e5895913e5 100644 --- a/src/TestFramework/TestFramework/UnitTestOutcome.cs +++ b/src/TestFramework/TestFramework/UnitTestOutcome.cs @@ -59,4 +59,9 @@ public enum UnitTestOutcome : int /// The specific test cannot be found. /// NotFound, + + /// + /// Test is marked as ignored. + /// + Ignored, } diff --git a/test/.editorconfig b/test/.editorconfig index d2c074affd..f7922ee56d 100644 --- a/test/.editorconfig +++ b/test/.editorconfig @@ -8,56 +8,31 @@ root = false #### .NET Coding Conventions #### -# VSTHRD200: Use "Async" suffix for async methods -dotnet_diagnostic.VSTHRD200.severity = suggestion - -# SA1118: Parameter should not span multiple lines -dotnet_diagnostic.SA1118.severity = suggestion - -# SA1201: Elements should appear in the correct order -dotnet_diagnostic.SA1201.severity = suggestion - -# SA1203: Constants should appear before fields -dotnet_diagnostic.SA1203.severity = suggestion - -# SA1311: Static readonly fields should begin with upper-case letter -dotnet_diagnostic.SA1311.severity = none - -# SA1602: Enumeration items should be documented -dotnet_diagnostic.SA1602.severity = none - -# CA1000: Do not declare static members on generic types -dotnet_diagnostic.CA1000.severity = suggestion - -# CA1018: Mark attributes with AttributeUsageAttribute -dotnet_diagnostic.CA1018.severity = none - -# CA1707: Identifiers should not contain underscores -dotnet_diagnostic.CA1707.severity = none - -# CA1711: Identifiers should not have incorrect suffix -dotnet_diagnostic.CA1711.severity = none - -# CA1816: Dispose methods should call SuppressFinalize -dotnet_diagnostic.CA1816.severity = none - -# CA1822: Mark members as static -dotnet_diagnostic.CA1822.severity = none - -# CA1852: Type can be sealed -dotnet_diagnostic.CA1852.severity = none - -# CA1859: Change return type to be more specific -dotnet_diagnostic.CA1859.severity = none - -# CA1861: Avoid constant arrays as arguments -dotnet_diagnostic.CA1861.severity = none - -# CA2201: Do not raise reserved exception types -dotnet_diagnostic.CA2201.severity = none - -# CA3075: Insecure DTD processing in XML -dotnet_diagnostic.CA3075.severity = none - -# IDE0060: Remove unused parameter -dotnet_diagnostic.IDE0060.severity = none +dotnet_diagnostic.VSTHRD200.severity = suggestion # VSTHRD200: Use "Async" suffix for async methods + +dotnet_diagnostic.SA1118.severity = suggestion # SA1118: Parameter should not span multiple lines +dotnet_diagnostic.SA1201.severity = suggestion # SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1203.severity = suggestion # SA1203: Constants should appear before fields +dotnet_diagnostic.SA1311.severity = none # SA1311: Static readonly fields should begin with upper-case letter +dotnet_diagnostic.SA1602.severity = none # SA1602: Enumeration items should be documented + +dotnet_diagnostic.CA1000.severity = suggestion # CA1000: Do not declare static members on generic types +dotnet_diagnostic.CA1018.severity = none # CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1707.severity = none # CA1707: Identifiers should not contain underscores +dotnet_diagnostic.CA1711.severity = none # CA1711: Identifiers should not have incorrect suffix +dotnet_diagnostic.CA1816.severity = none # CA1816: Dispose methods should call SuppressFinalize +dotnet_diagnostic.CA1822.severity = none # CA1822: Mark members as static +dotnet_diagnostic.CA1852.severity = none # CA1852: Type can be sealed +dotnet_diagnostic.CA1859.severity = none # CA1859: Change return type to be more specific +dotnet_diagnostic.CA1861.severity = none # CA1861: Avoid constant arrays as arguments +dotnet_diagnostic.CA2201.severity = none # CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA3075.severity = none # CA3075: Insecure DTD processing in XML + +dotnet_diagnostic.IDE0060.severity = none # IDE0060: Remove unused parameter + +dotnet_diagnostic.MSTEST0001.severity = warning # MSTEST0001: Explicitly enable or disable tests parallelization +dotnet_diagnostic.MSTEST0016.severity = warning # MSTEST0016: Test class should have test method +dotnet_diagnostic.MSTEST0017.severity = warning # MSTEST0017: Assertion arguments should be passed in the correct order +dotnet_diagnostic.MSTEST0029.severity = warning # MSTEST0029: Public methods should be test methods +dotnet_diagnostic.MSTEST0030.severity = warning # MSTEST0030: Type containing '[TestMethod]' should be marked with '[TestClass]' +dotnet_diagnostic.MSTEST0032.severity = warning # MSTEST0032: Assertion condition is always true diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets index 9f0a8ac53b..5697bbf9cd 100644 --- a/test/Directory.Build.targets +++ b/test/Directory.Build.targets @@ -6,8 +6,9 @@ enable Exe - + true false + false $(PlatformTarget) x64 $(MSBuildProjectName)_$(TargetFramework)_$(Configuration)_$(Architecture) @@ -22,22 +23,24 @@ - - - - + - - + + + + + + + - + diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs index a96340353a..d2fd2c24e5 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AbortionTests.cs @@ -1,24 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; using Microsoft.Testing.Platform.Helpers; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class AbortionTests : AcceptanceTestBase +[TestClass] +public sealed class AbortionTests : AcceptanceTestBase { private const string AssetName = "Abort"; - private readonly TestAssetFixture _testAssetFixture; - public AbortionTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task AbortWithCTRLPlusC_CancellingTests(string tfm) { // We expect the same semantic for Linux, the test setup is not cross and we're using specific @@ -28,7 +23,7 @@ public async Task AbortWithCTRLPlusC_CancellingTests(string tfm) return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); string fileCreationPath = Path.Combine(testHost.DirectoryName, "fileCreation"); File.WriteAllText(fileCreationPath, string.Empty); @@ -43,8 +38,7 @@ public async Task AbortWithCTRLPlusC_CancellingTests(string tfm) testHostResult.AssertOutputMatchesRegex("Canceling the test session.*"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string Sources = """ #file Abort.csproj diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs new file mode 100644 index 0000000000..43fcdcea3a --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AnalyzersTests.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestClass] +public sealed class AnalyzersTests : AcceptanceTestBase +{ + [TestMethod] + public async Task AnalyzerMessagesShouldBeLocalized() + { + string code = """ +#file Analyzers.csproj + + + + + true + $TargetFrameworks$ + true + + + + +#file UnitTest1.cs +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + [DataRow(0)] + public void TestMethod() + { + } +} +""".PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion); + + using TestAsset testAsset = await TestAsset.GenerateAssetAsync("Analyzers", code); + DotnetMuxerResult result = await DotnetCli.RunAsync($"build {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, environmentVariables: new() + { + ["DOTNET_CLI_UI_LANGUAGE"] = "it-IT", + ["PreferredUILang"] = "it-IT", + ["VSLang"] = "1040", + }, warnAsError: false); + result.AssertOutputContains("DataRow deve essere impostato solo su un metodo di test"); + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs index 780b689d78..1cad3bfabc 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolutionTests.cs @@ -6,17 +6,13 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class AssemblyResolutionTests : AcceptanceTestBase +[TestClass] +public sealed class AssemblyResolutionTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public AssemblyResolutionTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - + [TestMethod] public async Task AssemblyResolution_WhenNotSpecified_TestFails() { - TestHostResult testHostResult = await _testAssetFixture.TestHost.ExecuteAsync(); + TestHostResult testHostResult = await AssetFixture.TestHost.ExecuteAsync(); // Assert testHostResult.AssertExitCodeIs(2); @@ -24,10 +20,11 @@ public async Task AssemblyResolution_WhenNotSpecified_TestFails() testHostResult.AssertOutputContainsSummary(failed: 1, passed: 0, skipped: 0); } + [TestMethod] public async Task AssemblyResolution_WhenSpecified_TestSucceeds() { // Arrange - string runSettingsFilePath = Path.Combine(_testAssetFixture.TestHost.DirectoryName, ".runsettings"); + string runSettingsFilePath = Path.Combine(AssetFixture.TestHost.DirectoryName, ".runsettings"); File.WriteAllText(runSettingsFilePath, $""" @@ -35,14 +32,14 @@ public async Task AssemblyResolution_WhenSpecified_TestSucceeds() - + """); // Act - TestHostResult testHostResult = await _testAssetFixture.TestHost.ExecuteAsync($"--settings {runSettingsFilePath}"); + TestHostResult testHostResult = await AssetFixture.TestHost.ExecuteAsync($"--settings {runSettingsFilePath}"); // Assert testHostResult.AssertExitCodeIs(0); @@ -50,12 +47,11 @@ public async Task AssemblyResolution_WhenSpecified_TestSucceeds() testHostResult.AssertOutputDoesNotContain("System.IO.FileNotFoundException: Could not load file or assembly 'MSTest.Extensibility.Samples"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : IAsyncInitializable, IDisposable + public sealed class TestAssetFixture : ITestAssetFixture { public const string ProjectName = "AssemblyResolution.Main"; private const string TestProjectName = "AssemblyResolution.Test"; - private static readonly string TargetFramework = TargetFrameworks.NetCurrent.Arguments; + private static readonly string TargetFramework = TargetFrameworks.NetCurrent; private readonly TempDirectory _testAssetDirectory = new(); @@ -69,10 +65,10 @@ public void Dispose() MainDllFolder?.Dispose(); } - public async Task InitializeAsync(InitializationContext context) + public async Task InitializeAsync() { VSSolution solution = CreateTestAsset(); - DotnetMuxerResult result = await DotnetCli.RunAsync($"build -nodeReuse:false {solution.SolutionFile} -c Release", acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult result = await DotnetCli.RunAsync($"build -nodeReuse:false {solution.SolutionFile} -c Release", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, result.ExitCode); TestHost = TestHost.LocateFrom(solution.Projects.Skip(1).Single().FolderPath, TestProjectName, TargetFramework); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs index 993d5393b7..cf839e1f41 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/AssemblyResolverTests.cs @@ -7,17 +7,12 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class AssemblyResolverTests : AcceptanceTestBase +[TestClass] +public class AssemblyResolverTests : AcceptanceTestBase { private const string AssetName = "AssemblyResolverCrash"; - private readonly TestAssetFixture _testAssetFixture; - - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public AssemblyResolverTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; + [TestMethod] public async Task RunningTests_DoesNotHitResourceRecursionIssueAndDoesNotCrashTheRunner() { if (!OperatingSystem.IsWindows()) @@ -26,15 +21,14 @@ public async Task RunningTests_DoesNotHitResourceRecursionIssueAndDoesNotCrashTh return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetFramework[0].Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetFramework[0]); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.Success); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CancellationTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CancellationTests.cs index bab603e279..e3ece73a25 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CancellationTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CancellationTests.cs @@ -6,17 +6,13 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class CancellationTests : AcceptanceTestBase +[TestClass] +public sealed class CancellationTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public CancellationTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - + [TestMethod] public async Task WhenCancelingTestContextTokenInAssemblyInit_MessageIsAsExpected() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { ["ASSEMBLYINIT_CANCEL"] = "1", @@ -28,9 +24,25 @@ public async Task WhenCancelingTestContextTokenInAssemblyInit_MessageIsAsExpecte testHostResult.AssertOutputContains("Failed!"); } + [TestMethod] + public async Task WhenCancelingTestContextParameterTokenInAssemblyCleanup_MessageIsAsExpected() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["ASSEMBLYCLEANUP_CONTEXT_PARAMETER_CANCEL"] = "1", + }); + + // Assert + testHostResult.AssertExitCodeIs(2); + testHostResult.AssertOutputContains("Assembly cleanup method 'UnitTest1.AssemblyCleanup' was canceled"); + testHostResult.AssertOutputContains("Failed!"); + } + + [TestMethod] public async Task WhenCancelingTestContextTokenInClassInit_MessageIsAsExpected() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { ["CLASSINIT_CANCEL"] = "1", @@ -42,9 +54,10 @@ public async Task WhenCancelingTestContextTokenInClassInit_MessageIsAsExpected() testHostResult.AssertOutputContains("Failed!"); } + [TestMethod] public async Task WhenCancelingTestContextTokenInTestInit_MessageIsAsExpected() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { ["TESTINIT_CANCEL"] = "1", @@ -56,9 +69,10 @@ public async Task WhenCancelingTestContextTokenInTestInit_MessageIsAsExpected() testHostResult.AssertOutputContains("Failed!"); } + [TestMethod] public async Task WhenCancelingTestContextTokenInTestCleanup_MessageIsAsExpected() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { ["TESTCLEANUP_CANCEL"] = "1", @@ -70,9 +84,25 @@ public async Task WhenCancelingTestContextTokenInTestCleanup_MessageIsAsExpected testHostResult.AssertOutputContains("Failed!"); } + [TestMethod] + public async Task WhenCancelingTestContextParameterTokenInClassCleanup_MessageIsAsExpected() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["CLASSCLEANUP_CONTEXT_PARAMETER_CANCEL"] = "1", + }); + + // Assert + testHostResult.AssertExitCodeIs(2); + testHostResult.AssertOutputContains("Class cleanup method 'UnitTest2.ClassCleanup' was canceled"); + testHostResult.AssertOutputContains("Failed!"); + } + + [TestMethod] public async Task WhenCancelingTestContextTokenInTestMethod_MessageIsAsExpected() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { ["TESTMETHOD_CANCEL"] = "1", @@ -80,12 +110,11 @@ public async Task WhenCancelingTestContextTokenInTestMethod_MessageIsAsExpected( // Assert testHostResult.AssertExitCodeIs(2); - testHostResult.AssertOutputContains("Test 'TestMethod' execution has been aborted."); + testHostResult.AssertOutputContains("Test 'TestMethod' was canceled"); testHostResult.AssertOutputContains("Failed!"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestCancellation"; @@ -134,6 +163,16 @@ public static void AssemblyInitialize(TestContext testContext) } } + [AssemblyCleanup] + public static void AssemblyCleanup(TestContext testContext) + { + if (Environment.GetEnvironmentVariable("ASSEMBLYCLEANUP_CONTEXT_PARAMETER_CANCEL") == "1") + { + testContext.CancellationTokenSource.Cancel(); + testContext.CancellationTokenSource.Token.ThrowIfCancellationRequested(); + } + } + [ClassInitialize] public static void ClassInitialize(TestContext testContext) { @@ -176,6 +215,25 @@ public void TestMethod() } } } + +[TestClass] +public class UnitTest2 +{ + [ClassCleanup] + public static void ClassCleanup(TestContext testContext) + { + if (Environment.GetEnvironmentVariable("CLASSCLEANUP_CONTEXT_PARAMETER_CANCEL") == "1") + { + testContext.CancellationTokenSource.Cancel(); + testContext.CancellationTokenSource.Token.ThrowIfCancellationRequested(); + } + } + + [TestMethod] + public void TestMethod() + { + } +} """; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ConfigurationSettingsTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ConfigurationSettingsTests.cs index 4e2e71f877..8637871e8b 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ConfigurationSettingsTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ConfigurationSettingsTests.cs @@ -7,18 +7,14 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class ConfigurationSettingsTests : AcceptanceTestBase +[TestClass] +public sealed class ConfigurationSettingsTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public ConfigurationSettingsTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestConfigJson_AndRunSettingsHasMstest_Throws(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPathWithMSTestRunSettings, TestAssetFixture.ProjectNameWithMSTestRunSettings, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPathWithMSTestRunSettings, TestAssetFixture.ProjectNameWithMSTestRunSettings, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings"); // Assert @@ -26,10 +22,11 @@ public async Task TestConfigJson_AndRunSettingsHasMstest_Throws(string tfm) testHostResult.AssertStandardErrorContains("Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files."); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestConfigJson_AndRunSettingsHasMstestv2_Throws(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPathWithMSTestV2RunSettings, TestAssetFixture.ProjectNameWithMSTestV2RunSettings, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPathWithMSTestV2RunSettings, TestAssetFixture.ProjectNameWithMSTestV2RunSettings, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings"); // Assert @@ -37,28 +34,68 @@ public async Task TestConfigJson_AndRunSettingsHasMstestv2_Throws(string tfm) testHostResult.AssertStandardErrorContains("Both '.runsettings' and '.testconfig.json' files have been detected. Please select only one of these test configuration files."); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestConfigJson_AndRunSettingsWithoutMstest_OverrideRunConfigration(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings"); // Assert testHostResult.AssertExitCodeIs(ExitCodes.Success); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestConfigJson_WithoutRunSettings_BuildSuccess(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); // Assert testHostResult.AssertExitCodeIs(ExitCodes.Success); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + [TestMethod] + public async Task TestWithConfigFromCommandLineWithMapInconclusiveToFailedIsTrue() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--config-file dummyconfigfile_map.json", environmentVariables: new() + { + ["TestWithConfigFromCommandLine"] = "true", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContainsSummary(failed: 1, passed: 1, skipped: 0); + } + + [TestMethod] + public async Task TestWithConfigFromCommandLineWithMapInconclusiveToFailedIsFalse() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--config-file dummyconfigfile_doNotMap.json", environmentVariables: new() + { + ["TestWithConfigFromCommandLine"] = "true", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 1); + } + + [TestMethod] + public async Task TestWithConfigFromCommandLineWithNonExistingFile() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--config-file dummyconfigfile_not_existing_file.json", environmentVariables: new() + { + ["TestWithConfigFromCommandLine"] = "true", + }); + + testHostResult.AssertStandardErrorContains("FileNotFoundException"); + testHostResult.AssertStandardErrorContains("dummyconfigfile_not_existing_file.json"); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "ConfigurationSettings"; public const string ProjectNameWithMSTestRunSettings = "ConfigurationMSTestSettings"; @@ -127,6 +164,12 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test Always + + Always + + + Always + @@ -143,6 +186,24 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test $AppendSettings$ +#file dummyconfigfile_map.json +{ + "mstest": { + "execution": { + "mapInconclusiveToFailed": true, + }, + } +} + +#file dummyconfigfile_doNotMap.json +{ + "mstest": { + "execution": { + "mapInconclusiveToFailed": false, + }, + } +} + #file $ProjectName$.testconfig.json { "mstest": { @@ -197,6 +258,7 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test #file UnitTest1.cs +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -206,6 +268,15 @@ public class UnitTest1 public void TestMethod() { } + + [TestMethod] + public void TestWithConfigFromCommandLine() + { + if (Environment.GetEnvironmentVariable("TestWithConfigFromCommandLine") == "true") + { + Assert.Inconclusive("Inconclusive TestWithConfigFromCommandLine"); + } + } } """; } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DataSourceTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DataSourceTests.cs index 375dedad5f..f3ea941e28 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DataSourceTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DataSourceTests.cs @@ -7,8 +7,8 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class DataSourceTests : AcceptanceTestBase +[TestClass] +public sealed class DataSourceTests : AcceptanceTestBase { private const string SourceCode = """ #file DataSourceTests.csproj @@ -83,11 +83,7 @@ public void MyTest() 1,1,1 """; - private readonly AcceptanceFixture _acceptanceFixture; - - public DataSourceTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - + [TestMethod] public async Task TestDataSourceFromAppConfig() { if (!OperatingSystem.IsWindows()) @@ -105,7 +101,7 @@ public async Task TestDataSourceFromAppConfig() await DotnetCli.RunAsync( $"build {generator.TargetAssetPath} -c Release", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, retryCount: 0); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "DataSourceTests", "net472"); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs index 3bed38780c..85a3cc1970 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/DotnetTestCliTests.cs @@ -6,16 +6,13 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class DotnetTestCliTests : AcceptanceTestBase +[TestClass] +public class DotnetTestCliTests : AcceptanceTestBase { private const string AssetName = "MSTestProject"; - private readonly AcceptanceFixture _acceptanceFixture; - public DotnetTestCliTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixTfmBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task DotnetTest_Should_Execute_Tests(string tfm, BuildConfiguration buildConfiguration) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -28,9 +25,9 @@ public async Task DotnetTest_Should_Execute_Tests(string tfm, BuildConfiguration .PatchCodeWithReplace("$OutputType$", string.Empty) .PatchCodeWithReplace("$Extra$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -m:1 -nodeReuse:false {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -m:1 -nodeReuse:false {generator.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); // There is whitespace difference in output in parent and public repo that depends on the version of the dotnet SDK used. - compilationResult.AssertOutputRegEx(@"Passed!\s+-\s+Failed:\s+0,\s+Passed:\s+1,\s+Skipped:\s+0,\s+Total:\s+1"); + compilationResult.AssertOutputMatchesRegex(@"Passed!\s+-\s+Failed:\s+0,\s+Passed:\s+1,\s+Skipped:\s+0,\s+Total:\s+1"); } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs new file mode 100644 index 0000000000..4eb3aaeb1b --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/GenericTestMethodTests.cs @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestClass] +public class GenericTestMethodTests : AcceptanceTestBase +{ + [TestMethod] + public async Task TestDifferentGenericMethodTestCases() + { + var testHost = TestHost.LocateFrom(AssetFixture.GetAssetPath("GenericTestMethodTests"), "GenericTestMethodTests", TargetFrameworks.NetCurrent); + + TestHostResult testHostResult = await testHost.ExecuteAsync(); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputMatchesRegex( + """ + failed AMethodWithBadConstraints \(0\) \(\d+ms\) + GenericArguments\[0], 'System\.Int32', on 'Void AMethodWithBadConstraints\[T]\(T\)' violates the constraint of type 'T'\. + at .+? + failed NonParameterizedTestMethod \(\d+ms\) + The generic test method 'NonParameterizedTestMethod' doesn't have arguments, so the generic parameter cannot be inferred\. + failed ParameterizedMethodSimple \(1\) \(\d+ms\) + Assert\.Fail failed\. Test method 'ParameterizedMethodSimple' did run with parameter '1' and type 'System\.Byte'\. + at .+? + failed ParameterizedMethodSimple \(2\) \(\d+ms\) + Assert\.Fail failed\. Test method 'ParameterizedMethodSimple' did run with parameter '2' and type 'System\.Int32'\. + at .+? + failed ParameterizedMethodSimple \("Hello world"\) \(\d+ms\) + Assert\.Fail failed\. Test method 'ParameterizedMethodSimple' did run with parameter 'Hello world' and type 'System\.String'\. + at .+? + failed ParameterizedMethodSimple \(null\) \(\d+ms\) + Test method TestClass\.ParameterizedMethodSimple threw exception: + System\.InvalidOperationException: The type of the generic parameter 'T' could not be inferred\. + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(1,"Hello world",2,3\) \(\d+ms\) + Test method TestClass\.ParameterizedMethodTwoGenericParametersAndFourMethodParameters threw exception: + System\.InvalidOperationException: Found two conflicting types for generic parameter 'T2'\. The conflicting types are 'System\.Byte' and 'System\.Int32'\. + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(null,"Hello world","Hello again",3\) \(\d+ms\) + Assert\.Fail failed\. Test method 'ParameterizedMethodTwoGenericParametersAndFourMethodParameters' did run with parameters '', 'Hello world', 'Hello again', '3' and generic types 'System\.Int32', 'System\.String'\. + at .+? + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \("Hello hello","Hello world",null,null\) \(\d+ms\) + Test method TestClass\.ParameterizedMethodTwoGenericParametersAndFourMethodParameters threw exception: + System\.InvalidOperationException: The type of the generic parameter 'T1' could not be inferred\. + failed ParameterizedMethodTwoGenericParametersAndFourMethodParameters \(null,null,null,null\) \(\d+ms\) + Test method TestClass\.ParameterizedMethodTwoGenericParametersAndFourMethodParameters threw exception: + System\.InvalidOperationException: The type of the generic parameter 'T1' could not be inferred\. + failed ParameterizedMethodSimpleParams \(1\) \(\d+ms\) + Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. + at .+? + failed ParameterizedMethodSimpleParams \(1,2\) \(\d+ms\) + Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. + at .+? + failed ParameterizedMethodSimpleParams \("Hello world"\) \(\d+ms\) + Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. + at .+? + failed ParameterizedMethodSimpleParams \(null\) \(\d+ms\) + Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. + at .+? + failed ParameterizedMethodSimpleParams \(null,"Hello world"\) \(\d+ms\) + Cannot create an instance of T\[] because Type\.ContainsGenericParameters is true\. + at .+? + """, RegexOptions.Singleline); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return ("GenericTestMethodTests", "GenericTestMethodTests", + SourceGenericTestMethod + .PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceGenericTestMethod = """ +#file GenericTestMethodTests.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + +#file UnitTest1.cs + +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass +{ + [TestMethod] + [DataRow(0)] + public void AMethodWithBadConstraints(T p) where T : IDisposable + => Assert.Fail($"Test method 'AMethodWithBadConstraints' did run with T type being '{typeof(T)}'."); + + [TestMethod] + public void NonParameterizedTestMethod() + => Assert.Fail("Test method 'NonParameterizedTestMethod' did run."); + + [TestMethod] + [DataRow((byte)1)] + [DataRow((int)2)] + [DataRow("Hello world")] + [DataRow(null)] + public void ParameterizedMethodSimple(T parameter) + => Assert.Fail($"Test method 'ParameterizedMethodSimple' did run with parameter '{parameter?.ToString() ?? ""}' and type '{typeof(T)}'."); + + [TestMethod] + [DataRow((byte)1, "Hello world", (int)2, 3)] + [DataRow(null, "Hello world", "Hello again", 3)] + [DataRow("Hello hello", "Hello world", null, null)] + [DataRow(null, null, null, null)] + public void ParameterizedMethodTwoGenericParametersAndFourMethodParameters(T2 p1, string p2, T2 p3, T1 p4) + => Assert.Fail($"Test method 'ParameterizedMethodTwoGenericParametersAndFourMethodParameters' did run with parameters '{p1?.ToString() ?? ""}', '{p2 ?? ""}', '{p3?.ToString() ?? ""}', '{p4?.ToString() ?? ""}' and generic types '{typeof(T1)}', '{typeof(T2)}'."); + + [TestMethod] + [DataRow((byte)1)] + [DataRow((byte)1, 2)] + [DataRow("Hello world")] + [DataRow(null)] + [DataRow(null, "Hello world")] + public void ParameterizedMethodSimpleParams(params T[] parameter) + => Assert.Fail($"Test method 'ParameterizedMethodSimple' did run with parameter '{string.Join(",", parameter)}' and type '{typeof(T)}'."); +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs index 6f5fd33638..d43193af01 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -7,21 +7,16 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class HelpInfoTests : AcceptanceTestBase +[TestClass] +public class HelpInfoTests : AcceptanceTestBase { private const string AssetName = "HelpInfo"; - private readonly TestAssetFixture _testAssetFixture; - - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public HelpInfoTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegisteredExtension(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -31,6 +26,8 @@ public async Task Help_WhenMSTestExtensionRegistered_OutputHelpContentOfRegister Usage {AssetName}* [option providers] [extension option providers] Execute a .NET Test Application. Options: + --config-file + Specifies a testconfig.json file. --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag @@ -69,6 +66,8 @@ Takes one argument as string in the format [h|m|s] where 'value' is float Extension options: --filter Filters tests using the given expression. For more information, see the Filter option details section. For more information and examples on how to use selective unit test filtering, see https://learn.microsoft.com/dotnet/core/testing/selective-unit-tests. + --maximum-failed-tests + Specifies a maximum number of test failures that, when exceeded, will abort the test run. --no-ansi Disable outputting ANSI escape characters to screen. --no-progress @@ -85,10 +84,11 @@ Output verbosity when reporting tests. testHostResult.AssertOutputMatchesLines(wildcardMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegisteredExtension(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -111,13 +111,16 @@ public async Task Info_WhenMSTestExtensionRegistered_OutputInfoContentOfRegister Arity: 1..N Hidden: False Description: Specify or override a key-value pair parameter. For more information and examples, see https://learn.microsoft.com/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file#testrunparameters + --maximum-failed-tests + Arity: 1 + Hidden: False + Description: Specifies a maximum number of test failures that, when exceeded, will abort the test run. """; testHostResult.AssertOutputContains(output); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs index 40815c38f3..65c4843804 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/IgnoreTests.cs @@ -3,43 +3,130 @@ using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class IgnoreTests : AcceptanceTestBase +[TestClass] +public sealed class IgnoreTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public IgnoreTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - + [TestMethod] public async Task ClassCleanup_Inheritance_WhenClassIsSkipped() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --filter ClassName!~TestClassWithAssemblyInitialize"); // Assert - testHostResult.AssertExitCodeIs(0); - testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 1); + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 12, skipped: 9); testHostResult.AssertOutputContains("SubClass.Method"); } + [TestMethod] public async Task WhenAllTestsAreIgnored_AssemblyInitializeAndCleanupAreSkipped() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --filter TestClassWithAssemblyInitialize"); // Assert - testHostResult.AssertExitCodeIs(8); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 0, skipped: 1); testHostResult.AssertOutputDoesNotContain("AssemblyInitialize"); testHostResult.AssertOutputDoesNotContain("AssemblyCleanup"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + [TestMethod] + public async Task WhenTestClassIsIgnoredViaIgnoreMessageProperty() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --filter TestClassWithIgnoreMessage"); + + // Assert + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 0, skipped: 1); + } + + [TestMethod] + public async Task WhenTestMethodIsIgnoredViaIgnoreMessageProperty() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --filter TestClassWithMethodUsingIgnoreMessage"); + + // Assert + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 1); + } + + [TestMethod] + public async Task WhenSpecificDataSourceIsIgnoredViaIgnoreMessageProperty() + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --filter TestClassWithDataSourcesUsingIgnoreMessage"); + + // Assert + testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertOutputContains("TestInitialize: TestMethod1 (0)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod1 (0)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod1 (2)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod1 (2)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod2 (0)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod2 (0)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod2 (1)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod2 (1)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod2 (2)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod2 (2)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod3 (0)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod3 (0)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod3 (2)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod3 (2)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod4 (0)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod4 (0)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod4 (1)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod4 (1)"); + + testHostResult.AssertOutputContains("TestInitialize: TestMethod4 (2)"); + testHostResult.AssertOutputContains("TestCleanup: TestMethod4 (2)"); + + testHostResult.AssertOutputContains("skipped TestMethod1"); + testHostResult.AssertOutputContains("skipped TestMethod2"); + testHostResult.AssertOutputContains("skipped TestMethod3 (1)"); + testHostResult.AssertOutputContains("skipped TestMethod4 (3)"); + testHostResult.AssertOutputContains("skipped TestMethod4 (4)"); + testHostResult.AssertOutputContains("skipped TestMethod4 (5)"); + + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod1 (1)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod1 (1)"); + + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod2 (3)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod2 (3)"); + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod2 (4)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod2 (4)"); + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod2 (5)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod2 (5)"); + + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod3 (1)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod3 (1)"); + + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod4 (3)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod4 (3)"); + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod4 (4)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod4 (4)"); + testHostResult.AssertOutputDoesNotContain("TestInitialize: TestMethod4 (5)"); + testHostResult.AssertOutputDoesNotContain("TestCleanup: TestMethod4 (5)"); + + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 10, skipped: 6); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestIgnore"; @@ -85,6 +172,7 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test #file UnitTest1.cs using System; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -150,6 +238,100 @@ public void TestMethod1() { } } + +[TestClass(IgnoreMessage = "This test class is ignored")] +public class TestClassWithIgnoreMessage +{ + [TestMethod] + public void TestMethod1() + { + } +} + +[TestClass] +public class TestClassWithMethodUsingIgnoreMessage +{ + [TestMethod(IgnoreMessage = "This test method is ignored")] + public void TestMethod1() + { + } + + [TestMethod] + public void TestMethod2() + { + } +} + +[TestClass] +public class TestClassWithDataSourcesUsingIgnoreMessage +{ + private readonly TestContext _testContext; + + public TestClassWithDataSourcesUsingIgnoreMessage(TestContext testContext) + => _testContext = testContext; + + [TestMethod] // 1 skipped, 2 pass + [DataRow(0, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + [DataRow(1, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold, IgnoreMessage = "This data row is ignored")] + [DataRow(2, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + public void TestMethod1(int i) + { + } + + [TestMethod] // 1 skipped (folded), 3 pass + [DynamicData("Data1", UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + [DynamicData("Data2", UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold, IgnoreMessage = "This source is ignored")] + public void TestMethod2(int i) + { + } + + [TestMethod] // 1 skipped, 2 pass + [DataRow(0, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold)] + [DataRow(1, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold, IgnoreMessage = "This data row is ignored")] + [DataRow(2, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold)] + public void TestMethod3(int i) + { + } + + [TestMethod] // 3 skipped (unfolded), 3 pass + [DynamicData("Data1", UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold)] + [DynamicData("Data2", UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold, IgnoreMessage = "This source is ignored")] + public void TestMethod4(int i) + { + } + + [TestInitialize] + public void TestInit() + { + Console.WriteLine($"TestInitialize: {_testContext.TestDisplayName}"); + } + + [TestCleanup] + public void TestClean() + { + Console.WriteLine($"TestCleanup: {_testContext.TestDisplayName}"); + } + + public static IEnumerable Data1 + { + get + { + yield return new object[] { 0 }; + yield return new object[] { 1 }; + yield return new object[] { 2 }; + } + } + + public static IEnumerable Data2 + { + get + { + yield return new object[] { 3 }; + yield return new object[] { 4 }; + yield return new object[] { 5 }; + } + } +} """; } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs deleted file mode 100644 index 46a0a47ec4..0000000000 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/InitializeAndCleanupTimeoutTests.cs +++ /dev/null @@ -1,733 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Diagnostics; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests; -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; - -namespace MSTest.Acceptance.IntegrationTests; - -// [TestGroup] -public class InitializeAndCleanupTimeoutTests : AcceptanceTestBase -{ - private static readonly Dictionary InfoByKind = new() - { - ["assemblyInit"] = ("TestClass.AssemblyInit", "Assembly initialize", "ASSEMBLYINIT", "AssemblyInitializeTimeout"), - ["assemblyCleanup"] = ("TestClass.AssemblyCleanupMethod", "Assembly cleanup", "ASSEMBLYCLEANUP", "AssemblyCleanupTimeout"), - ["classInit"] = ("TestClass.ClassInit", "Class initialize", "CLASSINIT", "ClassInitializeTimeout"), - ["baseClassInit"] = ("TestClassBase.ClassInitBase", "Class initialize", "BASE_CLASSINIT", "ClassInitializeTimeout"), - ["classCleanup"] = ("TestClass.ClassCleanupMethod", "Class cleanup", "CLASSCLEANUP", "ClassCleanupTimeout"), - ["baseClassCleanup"] = ("TestClassBase.ClassCleanupBase", "Class cleanup", "BASE_CLASSCLEANUP", "ClassCleanupTimeout"), - ["testInit"] = ("TestClass.TestInit", "Test initialize", "TESTINIT", "TestInitializeTimeout"), - ["testCleanup"] = ("TestClass.TestCleanupMethod", "Test cleanup", "TESTCLEANUP", "TestCleanupTimeout"), - }; - - private readonly TestAssetFixture _testAssetFixture; - - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public InitializeAndCleanupTimeoutTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyInit_WhenTestContextCanceled_AssemblyInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestWasCanceledAsync(_testAssetFixture.CodeWithSixtySecTimeoutAssetPath, TestAssetFixture.CodeWithSixtySecTimeout, - tfm, "TESTCONTEXT_CANCEL_", "assemblyInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyInit_WhenTimeoutExpires_AssemblyInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, - tfm, "LONG_WAIT_", "assemblyInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_AssemblyInitializeExits(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "TIMEOUT_", "assemblyInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInit_WhenTestContextCanceled_ClassInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestWasCanceledAsync(_testAssetFixture.CodeWithSixtySecTimeoutAssetPath, TestAssetFixture.CodeWithSixtySecTimeout, tfm, - "TESTCONTEXT_CANCEL_", "classInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInit_WhenTimeoutExpires_ClassInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "classInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "TIMEOUT_", "classInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInitBase_WhenTestContextCanceled_ClassInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestWasCanceledAsync(_testAssetFixture.CodeWithSixtySecTimeoutAssetPath, TestAssetFixture.CodeWithSixtySecTimeout, tfm, - "TESTCONTEXT_CANCEL_", "baseClassInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInitBase_WhenTimeoutExpires_ClassInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "baseClassInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInitBase_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "TIMEOUT_", "baseClassInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyInitialize_WhenTimeoutExpires_FromRunSettings_AssemblyInitializeIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "assemblyInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "classInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task BaseClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "baseClassInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyInitialize_WhenTimeoutExpires_AssemblyInitializeIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "assemblyInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassInitialize_WhenTimeoutExpires_ClassInitializeIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "classInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task BaseClassInitialize_WhenTimeoutExpires_ClassInitializeIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "baseClassInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassCleanupBase_WhenTimeoutExpires_ClassCleanupTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "baseClassCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassCleanup_WhenTimeoutExpires_ClassCleanupTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "classCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "classCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task BaseClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "baseClassCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task ClassCleanup_WhenTimeoutExpires_ClassCleanupIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "classCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task BaseClassCleanup_WhenTimeoutExpires_ClassCleanupIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "baseClassCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "assemblyCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyCleanup_WhenTimeoutExpires_FromRunSettings_AssemblyCleanupIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "assemblyCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "assemblyCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task TestInitialize_WhenTimeoutExpires_TestInitializeTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "testInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task TestInitialize_WhenTimeoutExpires_FromRunSettings_TestInitializeIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "testInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task TestInitialize_WhenTimeoutExpires_TestInitializeIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "testInit"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task TestCleanup_WhenTimeoutExpires_TestCleanupTaskIsCanceled(string tfm) - => await RunAndAssertTestTimedOutAsync(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, - "LONG_WAIT_", "testCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task TestCleanup_WhenTimeoutExpires_FromRunSettings_TestCleanupIsCanceled(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "testCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task TestCleanup_WhenTimeoutExpires_TestCleanupIsCanceled_AttributeTakesPrecedence(string tfm) - => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "testCleanup"); - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenAssemblyInitTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_ASSEMBLYINIT"] = "1" }); - - testHostResult.AssertOutputContains("AssemblyInit started"); - testHostResult.AssertOutputContains("Assembly initialize method 'TestClass.AssemblyInit' timed out"); - testHostResult.AssertOutputDoesNotContain("AssemblyInit Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("AssemblyInit completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenAssemblyCleanupTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_ASSEMBLYCLEANUP"] = "1" }); - - testHostResult.AssertOutputContains("AssemblyCleanup started"); - testHostResult.AssertOutputContains("Assembly cleanup method 'TestClass.AssemblyCleanup' was canceled"); - testHostResult.AssertOutputDoesNotContain("AssemblyCleanup Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("AssemblyCleanup completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenClassInitTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_CLASSINIT"] = "1" }); - - testHostResult.AssertOutputContains("ClassInit started"); - testHostResult.AssertOutputContains("Class initialize method 'TestClass.ClassInit' was canceled"); - testHostResult.AssertOutputDoesNotContain("ClassInit Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("ClassInit completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenClassCleanupTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_CLASSCLEANUP"] = "1" }); - - testHostResult.AssertOutputContains("ClassCleanup started"); - testHostResult.AssertOutputContains("Class cleanup method 'TestClass.ClassCleanup' was canceled"); - testHostResult.AssertOutputDoesNotContain("ClassCleanup Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("ClassCleanup completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenTestInitTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_TESTINIT"] = "1" }); - - testHostResult.AssertOutputContains("TestInit started"); - testHostResult.AssertOutputContains("Test initialize method 'TestClass.TestInit' was canceled"); - testHostResult.AssertOutputDoesNotContain("TestInit completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenTestCleanupTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_TESTCLEANUP"] = "1" }); - - testHostResult.AssertOutputContains("TestCleanup started"); - testHostResult.AssertOutputContains("Test cleanup method 'TestClass.TestCleanup' was canceled"); - testHostResult.AssertOutputDoesNotContain("TestCleanup completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenTestMethodTimeoutExpires_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["TASKDELAY_TESTMETHOD"] = "1" }); - - testHostResult.AssertOutputContains("TestMethod started"); - testHostResult.AssertOutputContains("Test 'TestMethod' execution has been aborted."); - testHostResult.AssertOutputDoesNotContain("TestMethod completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenAssemblyInitTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_ASSEMBLYINIT"] = "1" }); - - testHostResult.AssertOutputContains("AssemblyInit started"); - testHostResult.AssertOutputContains("Assembly initialize method 'TestClass.AssemblyInit' timed out"); - testHostResult.AssertOutputContains("AssemblyInit Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("AssemblyInit completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenAssemblyCleanupTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_ASSEMBLYCLEANUP"] = "1" }); - - testHostResult.AssertOutputContains("AssemblyCleanup started"); - testHostResult.AssertOutputContains("Assembly cleanup method 'TestClass.AssemblyCleanup' timed out"); - testHostResult.AssertOutputContains("AssemblyCleanup Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("AssemblyCleanup completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenClassInitTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_CLASSINIT"] = "1" }); - - testHostResult.AssertOutputContains("ClassInit started"); - testHostResult.AssertOutputContains("Class initialize method 'TestClass.ClassInit' timed out"); - testHostResult.AssertOutputContains("ClassInit Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("ClassInit completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenClassCleanupTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_CLASSCLEANUP"] = "1" }); - - testHostResult.AssertOutputContains("ClassCleanup started"); - testHostResult.AssertOutputContains("Class cleanup method 'TestClass.ClassCleanup' timed out "); - testHostResult.AssertOutputContains("ClassCleanup Thread.Sleep completed"); - testHostResult.AssertOutputDoesNotContain("ClassCleanup completed"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenTestInitTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_TESTINIT"] = "1" }); - - testHostResult.AssertOutputContains("TestInit started"); - testHostResult.AssertOutputDoesNotContain("TestInit completed"); - testHostResult.AssertOutputContains("Test initialize method 'TestClass.TestInit' timed out"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenTestCleanupTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_TESTCLEANUP"] = "1" }); - - testHostResult.AssertOutputContains("TestCleanup started"); - testHostResult.AssertOutputDoesNotContain("TestCleanup completed"); - testHostResult.AssertOutputContains("Test cleanup method 'TestClass.TestCleanup' timed out"); - } - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] - public async Task CooperativeCancellation_WhenTestMethodTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) - { - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync( - "--settings my.runsettings", - new() { ["CHECKTOKEN_TESTMETHOD"] = "1" }); - - testHostResult.AssertOutputContains("TestMethod started"); - testHostResult.AssertOutputContains("Test 'TestMethod' execution has been aborted."); - } - - private async Task RunAndAssertTestWasCanceledAsync(string rootFolder, string assetName, string tfm, string envVarPrefix, string entryKind) - { - var testHost = TestHost.LocateFrom(rootFolder, assetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { { envVarPrefix + InfoByKind[entryKind].EnvVarSuffix, "1" } }); - testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' was canceled"); - } - - private async Task RunAndAssertTestTimedOutAsync(string rootFolder, string assetName, string tfm, string envVarPrefix, string entryKind) - { - var testHost = TestHost.LocateFrom(rootFolder, assetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { { envVarPrefix + InfoByKind[entryKind].EnvVarSuffix, "1" } }); - testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out after 1000ms"); - } - - private async Task RunAndAssertWithRunSettingsAsync(string tfm, int timeoutValue, bool assertAttributePrecedence, string entryKind) - { - string runSettingsEntry = InfoByKind[entryKind].RunSettingsEntryName; - string runSettings = $""" - - - - - - <{runSettingsEntry}>{timeoutValue} - - -"""; - - // if assertAttributePrecedence is set we will use CodeWithOneSecTimeoutAssetPath - timeoutValue = assertAttributePrecedence ? 1000 : timeoutValue; - - TestHost testHost = assertAttributePrecedence - ? TestHost.LocateFrom(_testAssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm) - : TestHost.LocateFrom(_testAssetFixture.CodeWithNoTimeoutAssetPath, TestAssetFixture.CodeWithNoTimeout, tfm); - string runSettingsFilePath = Path.Combine(testHost.DirectoryName, $"{Guid.NewGuid():N}.runsettings"); - File.WriteAllText(runSettingsFilePath, runSettings); - - var stopwatch = Stopwatch.StartNew(); - TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { { $"TIMEOUT_{InfoByKind[entryKind].EnvVarSuffix}", "1" } }); - stopwatch.Stop(); - - if (assertAttributePrecedence) - { - Assert.IsTrue(stopwatch.Elapsed.TotalSeconds < 25); - } - - testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out after {timeoutValue}ms"); - } - - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) - { - public const string CodeWithOneSecTimeout = nameof(CodeWithOneSecTimeout); - public const string CodeWithSixtySecTimeout = nameof(CodeWithSixtySecTimeout); - public const string CodeWithNoTimeout = nameof(CodeWithNoTimeout); - public const string CooperativeTimeout = nameof(CooperativeTimeout); - - private const string CooperativeTimeoutSourceCode = """ -#file $ProjectName$.csproj - - - - Exe - true - $TargetFrameworks$ - - - - - - - - - - PreserveNewest - - - - - -#file my.runsettings - - - false - - - -#file UnitTest1.cs -using System; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public class TestClass -{ - private static TestContext _assemblyTestContext; - private static TestContext _classTestContext; - - [Timeout(100, CooperativeCancellation = true)] - [AssemblyInitialize] - public static async Task AssemblyInit(TestContext testContext) - { - _assemblyTestContext = testContext; - await DoWork("ASSEMBLYINIT", "AssemblyInit", testContext); - } - - [Timeout(100, CooperativeCancellation = true)] - [AssemblyCleanup] - public static async Task AssemblyCleanup() - => await DoWork("ASSEMBLYCLEANUP", "AssemblyCleanup", _assemblyTestContext); - - [Timeout(100, CooperativeCancellation = true)] - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - _classTestContext = testContext; - await DoWork("CLASSINIT", "ClassInit", testContext); - } - - [Timeout(100, CooperativeCancellation = true)] - [ClassCleanup(ClassCleanupBehavior.EndOfClass)] - public static async Task ClassCleanup() - => await DoWork("CLASSCLEANUP", "ClassCleanup", _classTestContext); - - public TestContext TestContext { get; set; } - - [Timeout(100, CooperativeCancellation = true)] - [TestInitialize] - public async Task TestInit() - => await DoWork("TESTINIT", "TestInit", TestContext); - - [Timeout(100, CooperativeCancellation = true)] - [TestCleanup] - public async Task TestCleanup() - => await DoWork("TESTCLEANUP", "TestCleanup", TestContext); - - [Timeout(100, CooperativeCancellation = true)] - [TestMethod] - public async Task TestMethod() - => await DoWork("TESTMETHOD", "TestMethod", TestContext); - - private static async Task DoWork(string envVarSuffix, string stepName, TestContext testContext) - { - Console.WriteLine($"{stepName} started"); - - if (Environment.GetEnvironmentVariable($"TASKDELAY_{envVarSuffix}") == "1") - { - await Task.Delay(10_000, testContext.CancellationTokenSource.Token); - } - else - { - System.Threading.Thread.Sleep(200); - Console.WriteLine($"{stepName} Thread.Sleep completed"); - if (Environment.GetEnvironmentVariable($"CHECKTOKEN_{envVarSuffix}") == "1") - { - testContext.CancellationTokenSource.Token.ThrowIfCancellationRequested(); - } - - } - - Console.WriteLine($"{stepName} completed"); - } -} -"""; - - private const string SourceCode = """ -#file $ProjectName$.csproj - - - - Exe - true - $TargetFrameworks$ - - - - - - - - - -#file UnitTest1.cs - -using System; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -public class TestClassBase -{ - $TimeoutAttribute$ - [ClassInitialize(inheritanceBehavior: InheritanceBehavior.BeforeEachDerivedClass)] - public static async Task ClassInitBase(TestContext testContext) - { - if (Environment.GetEnvironmentVariable("TESTCONTEXT_CANCEL_BASE_CLASSINIT") == "1") - { - testContext.CancellationTokenSource.Cancel(); - await Task.Delay(10_000); - } - else if (Environment.GetEnvironmentVariable("LONG_WAIT_BASE_CLASSINIT") == "1") - { - await Task.Delay(10_000); - } - else if (Environment.GetEnvironmentVariable("TIMEOUT_BASE_CLASSINIT") == "1") - { - await Task.Delay(10_000, testContext.CancellationTokenSource.Token); - } - else - { - await Task.CompletedTask; - } - } - - $TimeoutAttribute$ - [ClassCleanup(inheritanceBehavior: InheritanceBehavior.BeforeEachDerivedClass)] - public static async Task ClassCleanupBase() - { - if (Environment.GetEnvironmentVariable("LONG_WAIT_BASE_CLASSCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_BASE_CLASSCLEANUP") == "1") - { - await Task.Delay(10_000); - } - else - { - await Task.CompletedTask; - } - } - -} - -[TestClass] -public class TestClass : TestClassBase -{ - $TimeoutAttribute$ - [AssemblyInitialize] - public static async Task AssemblyInit(TestContext testContext) - { - if (Environment.GetEnvironmentVariable("TESTCONTEXT_CANCEL_ASSEMBLYINIT") == "1") - { - testContext.CancellationTokenSource.Cancel(); - await Task.Delay(10_000); - } - else if (Environment.GetEnvironmentVariable("LONG_WAIT_ASSEMBLYINIT") == "1") - { - await Task.Delay(10_000); - } - else if (Environment.GetEnvironmentVariable("TIMEOUT_ASSEMBLYINIT") == "1") - { - await Task.Delay(60_000, testContext.CancellationTokenSource.Token); - } - else - { - await Task.CompletedTask; - } - } - - $TimeoutAttribute$ - [AssemblyCleanup] - public static async Task AssemblyCleanupMethod() - { - if (Environment.GetEnvironmentVariable("LONG_WAIT_ASSEMBLYCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_ASSEMBLYCLEANUP") == "1") - { - await Task.Delay(10_000); - } - else - { - await Task.CompletedTask; - } - } - - $TimeoutAttribute$ - [ClassInitialize] - public static async Task ClassInit(TestContext testContext) - { - if (Environment.GetEnvironmentVariable("TESTCONTEXT_CANCEL_CLASSINIT") == "1") - { - testContext.CancellationTokenSource.Cancel(); - await Task.Delay(10_000); - } - else if (Environment.GetEnvironmentVariable("LONG_WAIT_CLASSINIT") == "1") - { - await Task.Delay(10_000); - } - else if (Environment.GetEnvironmentVariable("TIMEOUT_CLASSINIT") == "1") - { - await Task.Delay(60_000, testContext.CancellationTokenSource.Token); - } - else - { - await Task.CompletedTask; - } - } - - $TimeoutAttribute$ - [ClassCleanup] - public static async Task ClassCleanupMethod() - { - if (Environment.GetEnvironmentVariable("LONG_WAIT_CLASSCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_CLASSCLEANUP") == "1") - { - await Task.Delay(10_000); - } - else - { - await Task.CompletedTask; - } - } - - $TimeoutAttribute$ - [TestInitialize] - public async Task TestInit() - { - if (Environment.GetEnvironmentVariable("LONG_WAIT_TESTINIT") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_TESTINIT") == "1") - { - await Task.Delay(10_000); - } - else - { - await Task.CompletedTask; - } - } - - $TimeoutAttribute$ - [TestCleanup] - public async Task TestCleanupMethod() - { - if (Environment.GetEnvironmentVariable("LONG_WAIT_TESTCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_TESTCLEANUP") == "1") - { - await Task.Delay(10_000); - } - else - { - await Task.CompletedTask; - } - } - - [TestMethod] - public Task Test1() => Task.CompletedTask; -} -"""; - - public string CodeWithOneSecTimeoutAssetPath => GetAssetPath(CodeWithOneSecTimeout); - - public string CodeWithSixtySecTimeoutAssetPath => GetAssetPath(CodeWithSixtySecTimeout); - - public string CodeWithNoTimeoutAssetPath => GetAssetPath(CodeWithNoTimeout); - - public string CooperativeTimeoutAssetPath => GetAssetPath(CooperativeTimeout); - - public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() - { - yield return (CodeWithNoTimeout, CodeWithNoTimeout, - SourceCode - .PatchCodeWithReplace("$TimeoutAttribute$", string.Empty) - .PatchCodeWithReplace("$ProjectName$", CodeWithNoTimeout) - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - - yield return (CodeWithOneSecTimeout, CodeWithOneSecTimeout, - SourceCode - .PatchCodeWithReplace("$TimeoutAttribute$", "[Timeout(1000)]") - .PatchCodeWithReplace("$ProjectName$", CodeWithOneSecTimeout) - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - - yield return (CodeWithSixtySecTimeout, CodeWithSixtySecTimeout, - SourceCode - .PatchCodeWithReplace("$TimeoutAttribute$", "[Timeout(60000)]") - .PatchCodeWithReplace("$ProjectName$", CodeWithSixtySecTimeout) - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - - yield return (CooperativeTimeout, CooperativeTimeout, - CooperativeTimeoutSourceCode - .PatchCodeWithReplace("$ProjectName$", CooperativeTimeout) - .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - } - } -} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs index 8cb7d672e8..770b7eb021 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSBuildRunnerTests.cs @@ -6,19 +6,15 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class MSBuildRunnerTests : AcceptanceTestBase +[TestClass] +public class MSBuildRunnerTests : AcceptanceTestBase { private const string AssetName = "MSTestProject"; private const string DotnetTestVerb = "test"; - private readonly AcceptanceFixture _acceptanceFixture; - public MSBuildRunnerTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - internal static IEnumerable> GetBuildMatrix() + internal static IEnumerable<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm, string Command)> GetBuildMatrix() { - foreach (TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)> entry in GetBuildMatrixSingleAndMultiTfmBuildConfiguration()) + foreach ((string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm) entry in GetBuildMatrixSingleAndMultiTfmBuildConfiguration()) { foreach (string command in new string[] { @@ -26,13 +22,13 @@ public MSBuildRunnerTests(ITestExecutionContext testExecutionContext, Acceptance DotnetTestVerb, }) { - yield return new TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm, string Command)>( - (entry.Arguments.SingleTfmOrMultiTfm, entry.Arguments.BuildConfiguration, entry.Arguments.IsMultiTfm, command), $"{(entry.Arguments.IsMultiTfm ? "multitfm" : entry.Arguments.SingleTfmOrMultiTfm)},{entry.Arguments.BuildConfiguration},{command}"); + yield return new(entry.SingleTfmOrMultiTfm, entry.BuildConfiguration, entry.IsMultiTfm, command); } } } - [ArgumentsProvider(nameof(GetBuildMatrix))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method)] public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests(string singleTfmOrMultiTfm, BuildConfiguration buildConfiguration, bool isMultiTfm, string command) { // Get the template project @@ -68,24 +64,24 @@ public async Task MSBuildTestTarget_SingleAndMultiTfm_Should_Run_Solution_Tests( } // Build the solution - DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - restoreResult.AssertOutputNotContains("An approximate best match of"); - DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -m:1 -nodeReuse:false {solution.SolutionFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + restoreResult.AssertOutputDoesNotContain("An approximate best match of"); + DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -m:1 -nodeReuse:false {solution.SolutionFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); if (isMultiTfm) { foreach (string tfm in singleTfmOrMultiTfm.Split(';')) { - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject0\..*' \[{tfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject1\..*' \[{tfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject2\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject0\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject1\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject2\..*' \[{tfm}\|x64\]"); } } else { - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject0\..*' \[{singleTfmOrMultiTfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject1\..*' \[{singleTfmOrMultiTfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject2\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject0\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject1\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject2\..*' \[{singleTfmOrMultiTfm}\|x64\]"); } } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj index 06e198a550..b204fdef59 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MSTest.Acceptance.IntegrationTests.csproj @@ -3,6 +3,7 @@ $(NetCurrent) false + true $(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS Exe true diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs new file mode 100644 index 0000000000..6d11054c28 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; +using Microsoft.Testing.Platform.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestClass] +public sealed class MaxFailedTestsExtensionTests : AcceptanceTestBase +{ + private const string AssetName = nameof(MaxFailedTestsExtensionTests); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task SimpleMaxFailedTestsScenario(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + + TestHostResult testHostResult = await testHost.ExecuteAsync("--maximum-failed-tests 3"); + testHostResult.AssertExitCodeIs(ExitCodes.TestExecutionStoppedForMaxFailedTests); + + int total = int.Parse(Regex.Match(testHostResult.StandardOutput, @"total: (\d+)").Groups[1].Value, CultureInfo.InvariantCulture); + + // We can't know the number of tests that will be executed exactly due to the async + // nature of publish/consume on the platform side. But we expect the cancellation to + // happen "fast" enough that we don't execute all tests. + Assert.IsTrue(total < 12); + Assert.IsTrue(total >= 5); + + testHostResult = await testHost.ExecuteAsync(); + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + + total = int.Parse(Regex.Match(testHostResult.StandardOutput, @"total: (\d+)").Groups[1].Value, CultureInfo.InvariantCulture); + Assert.AreEqual(12, total); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string Sources = """ +#file MaxFailedTestsExtensionTests.csproj + + + $TargetFrameworks$ + true + Exe + enable + preview + + + + + + + +#file UnitTest1.cs +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void Test1() + { + Assert.Fail(); + } + + [TestMethod] + public void Test2() + { + } + + [TestMethod] + public void Test3() + { + } + + [TestMethod] + public void Test4() + { + Assert.Fail(); + } + + [TestMethod] + public void Test5() + { + Assert.Fail(); + } + + [TestMethod] + public async Task Test6() + { + await Task.Delay(10); + } + + [TestMethod] + public async Task Test7() + { + await Task.Delay(10); + } + + [TestMethod] + public async Task Test8() + { + await Task.Delay(10); + } + + [TestMethod] + public async Task Test9() + { + await Task.Delay(10); + } + + [TestMethod] + public async Task Test10() + { + await Task.Delay(10); + } + + [TestMethod] + public async Task Test11() + { + await Task.Delay(10); + } + + [TestMethod] + public async Task Test12() + { + await Task.Delay(10); + } +} +"""; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs index c0efb2bae8..ad84b6721b 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/NativeAotTests.cs @@ -6,8 +6,8 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class NativeAotTests : AcceptanceTestBase +[TestClass] +public class NativeAotTests : AcceptanceTestBase { private const string SourceCode = """ #file NativeAotTests.csproj @@ -82,41 +82,45 @@ public void TestMethod3(int a, int b) } """; - private readonly AcceptanceFixture _acceptanceFixture; - - public NativeAotTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - + [TestMethod] public async Task NativeAotTests_WillRunWithExitCodeZero() + { + // The hosted AzDO agents for Mac OS don't have the required tooling for us to test Native AOT. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return; + } + // The native AOT publication is pretty flaky and is often failing on CI with "fatal error LNK1136: invalid or corrupt file", // or sometimes doesn't fail but the native code generation is not done. // Retrying the restore/publish on fresh asset seems to be more effective than retrying on the same asset. - => await RetryHelper.RetryAsync( + await RetryHelper.RetryAsync( async () => { using TestAsset generator = await TestAsset.GenerateAssetAsync( "NativeAotTests", SourceCode .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion) - .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent.Arguments) + .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) .PatchCodeWithReplace("$MSTestEngineVersion$", MSTestEngineVersion), addPublicFeeds: true); await DotnetCli.RunAsync( $"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, retryCount: 0); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( $"publish -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + timeoutInSeconds: 90, retryCount: 0); compilationResult.AssertOutputContains("Generating native code"); - var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent.Arguments, RID, Verb.publish); + var testHost = TestHost.LocateFrom(generator.TargetAssetPath, "NativeAotTests", TargetFrameworks.NetCurrent, RID, Verb.publish); TestHostResult result = await testHost.ExecuteAsync(); result.AssertExitCodeIs(0); }, times: 15, every: TimeSpan.FromSeconds(5)); + } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs new file mode 100644 index 0000000000..a52aaf0899 --- /dev/null +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/OutputTests.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.Acceptance.IntegrationTests; +using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; + +namespace MSTest.Acceptance.IntegrationTests; + +[TestClass] +public sealed class OutputTests : AcceptanceTestBase +{ + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task DetailedOutputIsAsExpected(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--output detailed"); + + // Assert + testHostResult.AssertOutputContains("Assert.AreEqual failed. Expected:<1>. Actual:<2>."); + testHostResult.AssertOutputContains(""" + Standard output + Console message + TestContext Messages: + TestContext message + Error output + """); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + public const string ProjectName = "TestOutput"; + + public string ProjectPath => GetAssetPath(ProjectName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (ProjectName, ProjectName, + SourceCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + } + + private const string SourceCode = """ +#file TestOutput.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + +#file UnitTest1.cs +using System; +using System.Diagnostics; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class UnitTest1 +{ + public TestContext TestContext { get; set; } + + [TestMethod] + public void TestMethod() + { + Debug.WriteLine("Debug message"); + Console.WriteLine("Console message"); + TestContext.WriteLine("TestContext message"); + + Assert.AreEqual(1, 2); + } +} +"""; + } +} diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs index 74583168d5..54d66170a0 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ParameterizedTestTests.cs @@ -7,45 +7,45 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class ParameterizedTestTests : AcceptanceTestBase +[TestClass] +public class ParameterizedTestTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; private const string DynamicDataAssetName = "DynamicData"; private const string DataSourceAssetName = "DataSource"; - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public ParameterizedTestTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string currentTfm) => await RunTestsAsync(currentTfm, DynamicDataAssetName, true); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task SendingEmptyDataToDataSourceTest_WithSettingConsiderEmptyDataSourceAsInconclusive_Passes(string currentTfm) => await RunTestsAsync(currentTfm, DataSourceAssetName, true); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task SendingEmptyDataToDynamicDataTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string currentTfm) => await RunTestsAsync(currentTfm, DynamicDataAssetName, false); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task SendingEmptyDataToDataSourceTest_WithSettingConsiderEmptyDataSourceAsInconclusiveToFalse_Fails(string currentTfm) => await RunTestsAsync(currentTfm, DataSourceAssetName, false); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task SendingEmptyDataToDynamicDataTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string currentTfm) => await RunTestsAsync(currentTfm, DynamicDataAssetName, null); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task SendingEmptyDataToDataSourceTest_WithoutSettingConsiderEmptyDataSourceAsInconclusive_Fails(string currentTfm) => await RunTestsAsync(currentTfm, DataSourceAssetName, null); - private async Task RunTestsAsync(string currentTfm, string assetName, bool? isEmptyDataInconclusive) + private static async Task RunTestsAsync(string currentTfm, string assetName, bool? isEmptyDataInconclusive) { - var testHost = TestHost.LocateFrom(_testAssetFixture.GetAssetPath(assetName), assetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.GetAssetPath(assetName), assetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync(SetupRunSettingsAndGetArgs(isEmptyDataInconclusive)); @@ -79,8 +79,7 @@ private async Task RunTestsAsync(string currentTfm, string assetName, bool? isEm } } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string DynamicDataTargetAssetPath => GetAssetPath(DynamicDataAssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs index f2d4e27817..116b5bef8d 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Program.cs @@ -1,12 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.Testing.Extensions; -using Microsoft.Testing.Internal.Framework.Configurations; -using MSTest.Acceptance.IntegrationTests; +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] // Opt-out telemetry Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); @@ -16,9 +14,7 @@ ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework( - new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), - new SourceGeneratedTestNodesBuilder()); +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Properties/launchSettings.json b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Properties/launchSettings.json index f422322857..cdc273e704 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Properties/launchSettings.json +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "MSTest.Acceptance.IntegrationTests": { "commandName": "Project", - "commandLineArgs": "--treenode-filter /*/*/*/**" + "commandLineArgs": "" } } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs index 750930798b..f26805a69a 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/PublishAotNonNativeTests.cs @@ -10,23 +10,21 @@ namespace MSTest.Acceptance.IntegrationTests; /// When PublishAOT=true is set on a project, it will set IsDynamicCodeSupported to false, but the code will still run as managed /// and VSTest is still able to find tests in the dll. /// -[TestGroup] -public sealed class PublishAotNonNativeTests : AcceptanceTestBase +[TestClass] +public sealed class PublishAotNonNativeTests : AcceptanceTestBase { private const string AssetName = "PublishAotNonNative"; - public PublishAotNonNativeTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - + [TestMethod] public async Task RunTests_ThatEnablePublishAOT_ButDontBuildToNative() { using TestAsset generator = await TestAsset.GenerateAssetAsync( - AssetName, - SourceCode - .PatchCodeWithReplace("$TargetFramework$", $"{TargetFrameworks.NetCurrent.Arguments}") - .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + AssetName, + SourceCode + .PatchCodeWithReplace("$TargetFramework$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c Debug {generator.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c Debug {generator.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); // In the real-world issue, access to path C:\Program Files\dotnet\ is denied, but we run this from a local .dotnet folder, where we have write access. // So instead of relying on the test run failing because of AccessDenied, we check the output, and see where TestResults were placed. @@ -80,6 +78,4 @@ public void TestMethod1() } } """; - - private readonly AcceptanceFixture _acceptanceFixture; } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs index 38255a5b64..70bb55ddc9 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunnerTests.cs @@ -5,21 +5,17 @@ using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using SL = Microsoft.Build.Logging.StructuredLogger; using SystemTask = System.Threading.Tasks.Task; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class RunnerTests : AcceptanceTestBase +[TestClass] +public class RunnerTests : AcceptanceTestBase { private const string AssetName = "MSTestProject"; - private readonly AcceptanceFixture _acceptanceFixture; - public RunnerTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async SystemTask EnableMSTestRunner_True_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -34,22 +30,22 @@ public async SystemTask EnableMSTestRunner_True_Will_Run_Standalone(string tfm, string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( $"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + AcceptanceFixture.NuGetGlobalPackagesFolder.Path); compilationResult = await DotnetCli.RunAsync( $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - SL.Build binLog = Serialization.Read(binlogFile); - Assert.IsNotEmpty(binLog.FindChildrenRecursive() - .Where(x => x.Title.Contains("ProjectCapability")) - .Where(x => x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); + Build binLog = Serialization.Read(binlogFile); + Assert.AreNotEqual(0, binLog.FindChildrenRecursive() + .Count(x => x.Title.Contains("ProjectCapability") && x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async SystemTask EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_Standalone(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -75,16 +71,17 @@ public async SystemTask EnableMSTestRunner_True_WithCustomEntryPoint_Will_Run_St preview """)); string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); await DotnetCli.RunAsync( $"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + AcceptanceFixture.NuGetGlobalPackagesFolder.Path); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async SystemTask EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoint_From_Tpv2_SDK(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -97,17 +94,17 @@ public async SystemTask EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoi .PatchCodeWithReplace("$OutputType$", "Exe") .PatchCodeWithReplace("$Extra$", string.Empty)); string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); try { - await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"{verb} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -bl:{binlogFile} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); var testHost = TestHost.LocateFrom(generator.TargetAssetPath, AssetName, tfm, buildConfiguration: buildConfiguration, verb: verb); TestHostResult testHostResult = await testHost.ExecuteAsync(); Assert.AreEqual(string.Empty, testHostResult.StandardOutput); } catch (Exception ex) { - if (TargetFrameworks.NetFramework.Any(x => x.Arguments == tfm)) + if (TargetFrameworks.NetFramework.Any(x => x == tfm)) { Assert.IsTrue(ex.Message.Contains("Program does not contain a static 'Main' method suitable for an entry point"), ex.Message); @@ -117,7 +114,8 @@ public async SystemTask EnableMSTestRunner_False_Will_Run_Empty_Program_EntryPoi } } - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async SystemTask EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer_Capability(string tfm, BuildConfiguration buildConfiguration, Verb verb) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -131,12 +129,11 @@ public async SystemTask EnableMSTestRunner_False_Wont_Flow_TestingPlatformServer .PatchCodeWithReplace("$Extra$", string.Empty)); string binlogFile = Path.Combine(generator.TargetAssetPath, "msbuild.binlog"); - await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - await DotnetCli.RunAsync($"{verb} -bl:{binlogFile} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID} ", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {generator.TargetAssetPath} -r {RID}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"{verb} -bl:{binlogFile} -m:1 -nodeReuse:false {generator.TargetAssetPath} -c {buildConfiguration} -r {RID} ", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); - SL.Build binLog = Serialization.Read(binlogFile); - Assert.IsEmpty(binLog.FindChildrenRecursive() - .Where(x => x.Title.Contains("ProjectCapability")) - .Where(x => x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); + Build binLog = Serialization.Read(binlogFile); + Assert.IsFalse(binLog.FindChildrenRecursive() + .Any(x => x.Title.Contains("ProjectCapability") && x.Children.Any(c => ((Item)c).Name == "TestingPlatformServer"))); } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs index d7d87424a3..1f2cc307aa 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/RunsettingsTests.cs @@ -1,59 +1,41 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; using Microsoft.Testing.Platform.Helpers; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class RunSettingsTests : AcceptanceTestBase +[TestClass] +public sealed class RunSettingsTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public RunSettingsTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - internal static IEnumerable> TfmList() - { - yield return TargetFrameworks.NetCurrent; - yield return TargetFrameworks.NetFramework.First(); - } - - internal static IEnumerable> LocalizationTestCases() + internal static IEnumerable<(string? TestingPlatformUILanguage, string? DotnetCLILanguage, string? VSLang, string ExpectedLocale)> LocalizationTestCases() { // Show that TestingPlatformUILanguage is respected. - yield return new TestArgumentsEntry<(string?, string?, string?, string)>(("fr-FR", null, null, "fr-FR"), "TestingPlatformUILanguage: fr-FR, expected: fr-FR"); + yield return new("fr-FR", null, null, "fr-FR"); // Show that TestingPlatformUILanguage takes precedence over DotnetCLILanguage. - yield return new TestArgumentsEntry<(string?, string?, string?, string)>(("fr-FR", "it-IT", null, "fr-FR"), "TestingPlatformUILanguage: fr-FR, CLI: it-IT, expected: fr-FR"); + yield return new("fr-FR", "it-IT", null, "fr-FR"); // Show that DotnetCLILanguage is respected. - yield return new TestArgumentsEntry<(string?, string?, string?, string)>((null, "it-IT", null, "it-IT"), "CLI: it-IT, expected: it-IT"); + yield return new(null, "it-IT", null, "it-IT"); // Show that DotnetCLILanguage takes precedence over VSLang. - yield return new TestArgumentsEntry<(string?, string?, string?, string)>((null, "it-IT", "fr-FR", "it-IT"), "CLI: it-IT, VSLang: fr-FR, expected: it-IT"); + yield return new(null, "it-IT", "fr-FR", "it-IT"); // Show that VSLang is respected. - yield return new TestArgumentsEntry<(string?, string?, string?, string)>((null, null, "it-IT", "it-IT"), "VSLang: it-IT, expected: it-IT"); + yield return new(null, null, "it-IT", "it-IT"); // Show that TestingPlatformUILanguage takes precedence over everything. - yield return new TestArgumentsEntry<(string?, string?, string?, string)>(("fr-FR", "it-IT", "it-IT", "fr-FR"), "TestingPlatformUILanguage: fr-FR, CLI: it-IT, VSLang: it-IT, expected: fr-FR"); + yield return new("fr-FR", "it-IT", "it-IT", "fr-FR"); } - [ArgumentsProvider(nameof(TfmList))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task UnsupportedRunSettingsEntriesAreFlagged(string tfm) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && tfm == TargetFrameworks.NetFramework.First().Arguments) - { - return; - } - - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings"); // Assert @@ -71,21 +53,22 @@ public async Task UnsupportedRunSettingsEntriesAreFlagged(string tfm) testHostResult.AssertOutputContains("Runsettings attribute 'TreatNoTestsAsError' is not supported by Microsoft.Testing.Platform and will be ignored"); } - [ArgumentsProvider(nameof(LocalizationTestCases))] - public async Task UnsupportedRunSettingsEntriesAreFlagged_Localization((string? TestingPlatformUILanguage, string? DotnetCLILanguage, string? VSLang, string? ExpectedLocale) testArgument) + [TestMethod] + [DynamicData(nameof(LocalizationTestCases), DynamicDataSourceType.Method)] + public async Task UnsupportedRunSettingsEntriesAreFlagged_Localization(string? testingPlatformUILanguage, string? dotnetCLILanguage, string? vsLang, string? expectedLocale) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings", environmentVariables: new() { - ["TESTINGPLATFORM_UI_LANGUAGE"] = testArgument.TestingPlatformUILanguage, - ["DOTNET_CLI_UI_LANGUAGE"] = testArgument.DotnetCLILanguage, - ["VSLANG"] = testArgument.VSLang is null ? null : new CultureInfo(testArgument.VSLang).LCID.ToString(CultureInfo.CurrentCulture), + ["TESTINGPLATFORM_UI_LANGUAGE"] = testingPlatformUILanguage, + ["DOTNET_CLI_UI_LANGUAGE"] = dotnetCLILanguage, + ["VSLANG"] = vsLang is null ? null : new CultureInfo(vsLang).LCID.ToString(CultureInfo.CurrentCulture), }); // Assert testHostResult.AssertExitCodeIs(0); - switch (testArgument.ExpectedLocale) + switch (expectedLocale) { case "fr-FR": testHostResult.AssertOutputContains("Les loggers Runsettings ne sont pas pris en charge par Microsoft.Testing.Platform et seront ignorés"); @@ -100,8 +83,7 @@ public async Task UnsupportedRunSettingsEntriesAreFlagged_Localization((string? } } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestRunSettings"; @@ -111,7 +93,7 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test { yield return (ProjectName, ProjectName, SourceCode - .PatchTargetFrameworks(TargetFrameworks.NetCurrent, TargetFrameworks.NetFramework.First()) + .PatchTargetFrameworks(TargetFrameworks.All) .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestClassTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestClassTests.cs index 3c85fd28f2..c6bfcd7ef7 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestClassTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestClassTests.cs @@ -1,27 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class STATestClassTests : AcceptanceTestBase +[TestClass] +public sealed class STATestClassTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; private const string AssetName = "STATestClass"; private const string TimeoutAssetName = "TimeoutSTATestClass"; private const string CooperativeTimeoutAssetName = "CooperativeTimeoutSTATestClass"; - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public STATestClassTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestClass_OnWindows_OnLifeCycleTestClass_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -29,7 +22,7 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClass_FixturesAndMethods return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClass"); @@ -46,7 +39,8 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClass_FixturesAndMethods testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -54,7 +48,7 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClassWithLastTestSkipped"); @@ -71,7 +65,8 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestClass_OnWindows_OnLifeCycleTestClass_WithTimeout_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -79,7 +74,7 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClass_WithTimeout_Fixtur return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetTimeoutAssetPath, TimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetTimeoutAssetPath, TimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClass"); @@ -96,7 +91,8 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClass_WithTimeout_Fixtur testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped_WithTimeout_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -104,7 +100,7 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetTimeoutAssetPath, TimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetTimeoutAssetPath, TimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClassWithLastTestSkipped"); @@ -121,7 +117,8 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestClass_OnWindows_OnLifeCycleTestClass_WithCooperativeTimeout_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -129,7 +126,7 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClass_WithCooperativeTim return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetCooperativeTimeoutAssetPath, CooperativeTimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetCooperativeTimeoutAssetPath, CooperativeTimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClass"); @@ -146,7 +143,8 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClass_WithCooperativeTim testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped_WithCooperativeTimeout_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -154,7 +152,7 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetCooperativeTimeoutAssetPath, CooperativeTimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetCooperativeTimeoutAssetPath, CooperativeTimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClassWithLastTestSkipped"); @@ -171,7 +169,8 @@ public async Task STATestClass_OnWindows_OnLifeCycleTestClassWithLastTestSkipped testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DerivedSTATestClass_OnWindows_OnTestClassWithClassCleanupEndOfAssembly_ClassCleanupIsMTA(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -179,7 +178,7 @@ public async Task DerivedSTATestClass_OnWindows_OnTestClassWithClassCleanupEndOf return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithClassCleanupEndOfAssembly"); @@ -196,8 +195,7 @@ public async Task DerivedSTATestClass_OnWindows_OnTestClassWithClassCleanupEndOf testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestMethodTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestMethodTests.cs index b1b8b523e1..82875503c0 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestMethodTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/STATestMethodTests.cs @@ -1,27 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class STATestMethodTests : AcceptanceTestBase +[TestClass] +public sealed class STATestMethodTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; private const string AssetName = "STATestMethodProject"; private const string TimeoutAssetName = "TimeoutSTATestMethodProject"; private const string CooperativeTimeoutAssetName = "CooperativeTimeoutSTATestMethodProject"; - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public STATestMethodTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -29,7 +22,7 @@ public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_FixturesAndMethod return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClass"); @@ -46,7 +39,8 @@ public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_FixturesAndMethod testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DerivedSTATestMethod_OnWindows_OnTestClassWithClassCleanupEndOfAssembly(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -54,7 +48,7 @@ public async Task DerivedSTATestMethod_OnWindows_OnTestClassWithClassCleanupEndO return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithClassCleanupEndOfAssembly"); @@ -71,7 +65,8 @@ public async Task DerivedSTATestMethod_OnWindows_OnTestClassWithClassCleanupEndO testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_MethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -79,7 +74,7 @@ public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_MethodsAr return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithMultipleTests"); @@ -99,7 +94,8 @@ public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_MethodsAr testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_WithTimeout_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -107,7 +103,7 @@ public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_WithTimeout_Fixtu return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TimeoutTargetAssetPath, TimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TimeoutTargetAssetPath, TimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClass"); @@ -124,7 +120,8 @@ public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_WithTimeout_Fixtu testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DerivedSTATestMethod_OnWindows_WithTimeout_OnTestClassWithClassCleanupEndOfAssembly(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -132,7 +129,7 @@ public async Task DerivedSTATestMethod_OnWindows_WithTimeout_OnTestClassWithClas return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TimeoutTargetAssetPath, TimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TimeoutTargetAssetPath, TimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithClassCleanupEndOfAssembly"); @@ -149,7 +146,8 @@ public async Task DerivedSTATestMethod_OnWindows_WithTimeout_OnTestClassWithClas testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_WithTimeout_MethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -157,7 +155,7 @@ public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_WithTimeo return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.TimeoutTargetAssetPath, TimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TimeoutTargetAssetPath, TimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithMultipleTests"); @@ -177,7 +175,8 @@ public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_WithTimeo testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_WithCooperativeTimeout_FixturesAndMethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -185,7 +184,7 @@ public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_WithCooperativeTi return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutTargetAssetPath, CooperativeTimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutTargetAssetPath, CooperativeTimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=LifeCycleTestClass"); @@ -202,7 +201,8 @@ public async Task STATestMethod_OnWindows_OnLifeCycleTestClass_WithCooperativeTi testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DerivedSTATestMethod_OnWindows_WithCooperativeTimeout_OnTestClassWithClassCleanupEndOfAssembly(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -210,7 +210,7 @@ public async Task DerivedSTATestMethod_OnWindows_WithCooperativeTimeout_OnTestCl return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutTargetAssetPath, CooperativeTimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutTargetAssetPath, CooperativeTimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithClassCleanupEndOfAssembly"); @@ -227,7 +227,8 @@ public async Task DerivedSTATestMethod_OnWindows_WithCooperativeTimeout_OnTestCl testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_WithCooperativeTimeout_MethodsAreOnExpectedApartmentState(string currentTfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -235,7 +236,7 @@ public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_WithCoope return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.CooperativeTimeoutTargetAssetPath, CooperativeTimeoutAssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutTargetAssetPath, CooperativeTimeoutAssetName, currentTfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath} --filter className=TestClassWithMultipleTests"); @@ -255,8 +256,7 @@ public async Task STATestMethod_OnWindows_OnTestClassWithMultipleTests_WithCoope testHostResult.AssertOutputContains("LifeCycleTestClass.AssemblyCleanup"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs index ef972a347f..8f09d34511 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/SdkTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; using Microsoft.Testing.Platform.Helpers; @@ -11,8 +9,8 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class SdkTests : AcceptanceTestBase +[TestClass] +public sealed class SdkTests : AcceptanceTestBase { private const string AssetName = "MSTestSdk"; @@ -51,17 +49,8 @@ public void TestMethod1() } """; - private readonly AcceptanceFixture _acceptanceFixture; - private readonly TestAssetFixture _testAssetFixture; - - public SdkTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture, TestAssetFixture testAssetFixture) - : base(testExecutionContext) - { - _acceptanceFixture = acceptanceFixture; - _testAssetFixture = testAssetFixture; - } - - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -71,22 +60,24 @@ public async Task RunTests_With_VSTest(string multiTfm, BuildConfiguration build .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", "true")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net8\.0\)"); + compilationResult.AssertOutputMatchesRegex(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net9\.0\)"); #if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net7\.0\)"); - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net6\.0\)"); + compilationResult.AssertOutputMatchesRegex(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net8\.0\)"); + compilationResult.AssertOutputMatchesRegex(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net7\.0\)"); + compilationResult.AssertOutputMatchesRegex(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net6\.0\)"); #endif if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - compilationResult.AssertOutputRegEx(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net462\)"); + compilationResult.AssertOutputMatchesRegex(@"Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: .* [m]?s - MSTestSdk.dll \(net462\)"); } } - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -96,22 +87,24 @@ public async Task RunTests_With_MSTestRunner_DotnetTest(string multiTfm, BuildCo .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); - compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net8\.0|x64\]"); + compilationResult.AssertOutputMatchesRegex(@"Tests succeeded: .* \[net9\.0|x64\]"); #if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS - compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net7\.0|x64\]"); - compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net6\.0|x64\]"); + compilationResult.AssertOutputMatchesRegex(@"Tests succeeded: .* \[net8\.0|x64\]"); + compilationResult.AssertOutputMatchesRegex(@"Tests succeeded: .* \[net7\.0|x64\]"); + compilationResult.AssertOutputMatchesRegex(@"Tests succeeded: .* \[net6\.0|x64\]"); #endif if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - compilationResult.AssertOutputRegEx(@"Tests succeeded: .* \[net462|x64\]"); + compilationResult.AssertOutputMatchesRegex(@"Tests succeeded: .* \[net462|x64\]"); } } - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task RunTests_With_MSTestRunner_Standalone(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -121,7 +114,7 @@ public async Task RunTests_With_MSTestRunner_Standalone(string multiTfm, BuildCo .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -131,7 +124,8 @@ public async Task RunTests_With_MSTestRunner_Standalone(string multiTfm, BuildCo } } - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task RunTests_With_CentralPackageManagement_Standalone(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -141,7 +135,7 @@ public async Task RunTests_With_CentralPackageManagement_Standalone(string multi .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", string.Empty)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -151,48 +145,39 @@ public async Task RunTests_With_CentralPackageManagement_Standalone(string multi } } - public static IEnumerable> RunTests_With_MSTestRunner_Standalone_Plus_Extensions_Data() + public static IEnumerable<(string MultiTfm, BuildConfiguration BuildConfiguration, string MSBuildExtensionEnableFragment, string EnableCommandLineArg, string InvalidCommandLineArg)> RunTests_With_MSTestRunner_Standalone_Plus_Extensions_Data() { - foreach (TestArgumentsEntry<(string MultiTfm, BuildConfiguration BuildConfiguration)> buildConfig in GetBuildMatrixMultiTfmFoldedBuildConfiguration()) + foreach ((string MultiTfm, BuildConfiguration BuildConfiguration) buildConfig in GetBuildMatrixMultiTfmFoldedBuildConfiguration()) { - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string, string)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, "true", "--coverage", - "--crashdump"), - $"multitfm,{buildConfig.Arguments.BuildConfiguration},CodeCoverage"); + "--crashdump"); - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string, string)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, "true", "--retry-failed-tests 3", - "--crashdump"), - $"multitfm,{buildConfig.Arguments.BuildConfiguration},Retry"); + "--crashdump"); - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string, string)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, "true", "--report-trx", - "--crashdump"), - $"multitfm,{buildConfig.Arguments.BuildConfiguration},TrxReport"); + "--crashdump"); - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string, string)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, "true", "--crashdump", - "--hangdump"), - $"multitfm,{buildConfig.Arguments.BuildConfiguration},CrashDump"); + "--hangdump"); - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string, string)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, "true", "--hangdump", - "--crashdump"), - $"multitfm,{buildConfig.Arguments.BuildConfiguration},HangDump"); + "--crashdump"); } } - [ArgumentsProvider(nameof(RunTests_With_MSTestRunner_Standalone_Plus_Extensions_Data))] + [TestMethod] + [DynamicData(nameof(RunTests_With_MSTestRunner_Standalone_Plus_Extensions_Data), DynamicDataSourceType.Method)] public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Extensions(string multiTfm, BuildConfiguration buildConfiguration, string msbuildExtensionEnableFragment, string enableCommandLineArg, @@ -205,7 +190,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Exte .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", msbuildExtensionEnableFragment)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -218,7 +203,8 @@ public async Task RunTests_With_MSTestRunner_Standalone_Selectively_Enabled_Exte } } - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -228,7 +214,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(str .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", "AllMicrosoft")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -238,21 +224,17 @@ public async Task RunTests_With_MSTestRunner_Standalone_EnableAll_Extensions(str } } - public static IEnumerable> RunTests_With_MSTestRunner_Standalone_Default_Extensions_Data() + public static IEnumerable<(string MultiTfm, BuildConfiguration BuildConfiguration, bool EnableDefaultExtensions)> RunTests_With_MSTestRunner_Standalone_Default_Extensions_Data() { - foreach (TestArgumentsEntry<(string MultiTfm, BuildConfiguration BuildConfiguration)> buildConfig in GetBuildMatrixMultiTfmFoldedBuildConfiguration()) + foreach ((string MultiTfm, BuildConfiguration BuildConfiguration) buildConfig in GetBuildMatrixMultiTfmFoldedBuildConfiguration()) { - yield return new TestArgumentsEntry<(string, BuildConfiguration, bool)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, true), - $"enabled,{buildConfig.Arguments.BuildConfiguration},CodeCoverage"); - - yield return new TestArgumentsEntry<(string, BuildConfiguration, bool)>( - (buildConfig.Arguments.MultiTfm, buildConfig.Arguments.BuildConfiguration, false), - $"disabled,{buildConfig.Arguments.BuildConfiguration},CodeCoverage"); + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, true); + yield return new(buildConfig.MultiTfm, buildConfig.BuildConfiguration, false); } } - [ArgumentsProvider(nameof(RunTests_With_MSTestRunner_Standalone_Default_Extensions_Data))] + [TestMethod] + [DynamicData(nameof(RunTests_With_MSTestRunner_Standalone_Default_Extensions_Data), DynamicDataSourceType.Method)] public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extensions(string multiTfm, BuildConfiguration buildConfiguration, bool enableDefaultExtensions) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -262,7 +244,7 @@ public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extension .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", enableDefaultExtensions ? string.Empty : "None")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); foreach (string tfm in multiTfm.Split(";")) { @@ -279,7 +261,8 @@ public async Task RunTests_With_MSTestRunner_Standalone_Enable_Default_Extension } } - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration))] + [TestMethod] + [DynamicData(nameof(GetBuildMatrixMultiTfmFoldedBuildConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] public async Task Invalid_TestingProfile_Name_Should_Fail(string multiTfm, BuildConfiguration buildConfiguration) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -289,11 +272,12 @@ public async Task Invalid_TestingProfile_Name_Should_Fail(string multiTfm, Build .PatchCodeWithReplace("$TargetFramework$", multiTfm) .PatchCodeWithReplace("$ExtraProperties$", "WrongName")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"build -c {buildConfiguration} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); Assert.AreEqual(1, compilationResult.ExitCode); compilationResult.AssertOutputContains("Invalid value for property TestingExtensionsProfile. Valid values are 'Default', 'AllMicrosoft' and 'None'."); } + [TestMethod] public async Task NativeAot_Smoke_Test_Windows() // The native AOT publication is pretty flaky and is often failing on CI with "fatal error LNK1136: invalid or corrupt file", // or sometimes doesn't fail but the native code generation is not done. @@ -313,7 +297,7 @@ public async Task NativeAot_Smoke_Test_Windows() // temporarily set test to be on net9.0 as it's fixing one error that started to happen: error IL3000: System.Net.Quic.MsQuicApi..cctor // see https://github.com/dotnet/sdk/issues/44880. .PatchCodeWithReplace("$TargetFramework$", "net9.0") - .PatchCodeWithReplace("$ExtraProperties$", $""" + .PatchCodeWithReplace("$ExtraProperties$", """ true false """), @@ -321,12 +305,12 @@ public async Task NativeAot_Smoke_Test_Windows() DotnetMuxerResult compilationResult = await DotnetCli.RunAsync( $"publish -r {RID} -f net9.0 {testAsset.TargetAssetPath}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, // We prefer to use the outer retry mechanism as we need some extra checks retryCount: 0, timeoutInSeconds: 180); compilationResult.AssertOutputContains("Generating native code"); - compilationResult.AssertOutputNotContains("warning"); + compilationResult.AssertOutputDoesNotContain("warning"); var testHost = TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, "net9.0", verb: Verb.publish); TestHostResult testHostResult = await testHost.ExecuteAsync(); @@ -335,10 +319,11 @@ public async Task NativeAot_Smoke_Test_Windows() testHostResult.AssertOutputContainsSummary(0, 1, 0); }, times: 15, every: TimeSpan.FromSeconds(5)); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywrightTests(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.PlaywrightProjectPath, TestAssetFixture.PlaywrightProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.PlaywrightProjectPath, TestAssetFixture.PlaywrightProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); // Depending on the machine, the test might fail due to the browser not being installed. @@ -359,16 +344,17 @@ public async Task EnablePlaywrightProperty_WhenUsingRunner_AllowsToRunPlaywright } } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywrightTests(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.PlaywrightProjectPath, TestAssetFixture.PlaywrightProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.PlaywrightProjectPath, TestAssetFixture.PlaywrightProjectName, tfm); string exeOrDllName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? testHost.FullName : testHost.FullName + ".dll"; DotnetMuxerResult dotnetTestResult = await DotnetCli.RunAsync( $"test {exeOrDllName}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false, warnAsError: false, suppressPreviewDotNetMessage: false); @@ -394,22 +380,24 @@ public async Task EnablePlaywrightProperty_WhenUsingVSTest_AllowsToRunPlaywright } } + [TestMethod] public async Task EnableAspireProperty_WhenUsingRunner_AllowsToRunAspireTests() { - var testHost = TestHost.LocateFrom(_testAssetFixture.AspireProjectPath, TestAssetFixture.AspireProjectName, TargetFrameworks.NetCurrent.UidFragment); + var testHost = TestHost.LocateFrom(AssetFixture.AspireProjectPath, TestAssetFixture.AspireProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertOutputContainsSummary(0, 1, 0); } + [TestMethod] public async Task EnableAspireProperty_WhenUsingVSTest_AllowsToRunAspireTests() { - var testHost = TestHost.LocateFrom(_testAssetFixture.AspireProjectPath, TestAssetFixture.AspireProjectName, TargetFrameworks.NetCurrent.UidFragment); + var testHost = TestHost.LocateFrom(AssetFixture.AspireProjectPath, TestAssetFixture.AspireProjectName, TargetFrameworks.NetCurrent); string exeOrDllName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? testHost.FullName : testHost.FullName + ".dll"; DotnetMuxerResult dotnetTestResult = await DotnetCli.RunAsync( $"test {exeOrDllName}", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, warnAsError: false, suppressPreviewDotNetMessage: false); Assert.AreEqual(0, dotnetTestResult.ExitCode); @@ -418,17 +406,18 @@ public async Task EnableAspireProperty_WhenUsingVSTest_AllowsToRunAspireTests() dotnetTestResult.AssertOutputContains("Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1"); } + [TestMethod] public async Task SettingIsTestApplicationToFalseReducesAddedExtensionsAndMakesProjectNotExecutable() { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( AssetName, SingleTestSourceCode .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion) - .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent.UidFragment) + .PatchCodeWithReplace("$TargetFramework$", TargetFrameworks.NetCurrent) .PatchCodeWithReplace("$ExtraProperties$", "false")); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test {testAsset.TargetAssetPath} -bl:{binlogFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"test {testAsset.TargetAssetPath} -bl:{binlogFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.AreEqual(0, compilationResult.ExitCode); @@ -448,9 +437,7 @@ public async Task SettingIsTestApplicationToFalseReducesAddedExtensionsAndMakesP Assert.IsFalse(binLog.FindChildrenRecursive(p => p.Name == "OutputType").Any(p => p.Value == "Exe")); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture _acceptanceFixture) - : TestAssetFixtureBase(_acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string AspireProjectName = "AspireProject"; public const string PlaywrightProjectName = "PlaywrightProject"; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs index 037e12b643..a8a38b1ac9 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/LogsCollector.cs @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using static Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100.TestingPlatformClient; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; -public class LogsCollector : ConcurrentBag; +// Not using ConcurrentBag because it's unordered. +public class LogsCollector : BlockingCollection; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs index d31bf62902..581925c1ab 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/ServerModeTestsBase.cs @@ -1,19 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Globalization; using System.Net; using System.Net.Sockets; -using System.Text; -using System.Text.RegularExpressions; using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; namespace MSTest.Acceptance.IntegrationTests.Messages.V100; -public partial /* for codegen regx */ class ServerModeTestsBase : AcceptanceTestBase +public partial /* for codegen regx */ class ServerModeTestsBase : AcceptanceTestBase + where TFixture : TestAssetFixtureBase, new() { private static readonly string Root = RootFinder.Find(); private static readonly Dictionary DefaultEnvironmentVariables = new() @@ -24,11 +21,6 @@ namespace MSTest.Acceptance.IntegrationTests.Messages.V100; { "DOTNET_MULTILEVEL_LOOKUP", "0" }, }; - protected ServerModeTestsBase(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - protected async Task StartAsServerAndConnectToTheClientAsync(TestHost testHost) { var environmentVariables = new Dictionary(DefaultEnvironmentVariables); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs index ac621b10fd..7b48806dc5 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TelemetryCollector.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; namespace MSTest.Acceptance.IntegrationTests.Messages.V100; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs index b669a13e9b..b49b001157 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/TestNodeUpdateCollector.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; - using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; namespace MSTest.Acceptance.IntegrationTests.Messages.V100; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ClientInfo.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ClientInfo.cs index a4548d891f..5acc6821ed 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ClientInfo.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ClientInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.Json; - using Newtonsoft.Json; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/InitializeRequest.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/InitializeRequest.cs index abf5adb281..f15e94ab37 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/InitializeRequest.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/InitializeRequest.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.Json; - using Newtonsoft.Json; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/RpcListener.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/RpcListener.cs index 6bed732b68..e4eca6facb 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/RpcListener.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/RpcListener.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; internal sealed class ConsoleRpcListener : TraceListener diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ServerInfo.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ServerInfo.cs index ccab026462..e2b0e4a49f 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ServerInfo.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/ServerInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.Json; - using Newtonsoft.Json; namespace Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs index 9e9c68f311..a2585481e5 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerMode/v1.0.0/TestingPlatformClient.cs @@ -1,10 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; -using System.Diagnostics; using System.Net.Sockets; -using System.Text; using MSTest.Acceptance.IntegrationTests.Messages.V100; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs index 9ca1e2813a..154dceb6df 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ServerModeTests.cs @@ -8,18 +8,14 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class ServerModeTests : ServerModeTestsBase +[TestClass] +public sealed class ServerModeTests : ServerModeTestsBase { - private readonly TestAssetFixture _fixture; - - public ServerModeTests(ITestExecutionContext testExecutionContext, TestAssetFixture fixture) - : base(testExecutionContext) => _fixture = fixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DiscoverAndRun(string tfm) { - using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(TestHost.LocateFrom(_fixture.ProjectPath, "MSTestProject", tfm, buildConfiguration: BuildConfiguration.Release)); + using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(TestHost.LocateFrom(AssetFixture.ProjectPath, "MSTestProject", tfm, buildConfiguration: BuildConfiguration.Release)); LogsCollector logs = new(); jsonClient.RegisterLogListener(logs); TelemetryCollector telemetry = new(); @@ -38,19 +34,20 @@ public async Task DiscoverAndRun(string tfm) ResponseListener runListener = await jsonClient.RunTests(Guid.NewGuid(), runCollector.CollectNodeUpdates); await Task.WhenAll(discoveryListener.WaitCompletion(), runListener.WaitCompletion()); - Assert.AreEqual(1, discoveryCollector.TestNodeUpdates.Count(x => x.Node.NodeType == "action"), $"Wrong number of discovery"); - Assert.AreEqual(2, runCollector.TestNodeUpdates.Count, $"Wrong number of updates"); - Assert.IsFalse(logs.IsEmpty, $"Logs are empty"); - Assert.IsFalse(telemetry.IsEmpty, $"telemetry is empty"); + Assert.AreEqual(1, discoveryCollector.TestNodeUpdates.Count(x => x.Node.NodeType == "action"), "Wrong number of discovery"); + Assert.AreEqual(2, runCollector.TestNodeUpdates.Count, "Wrong number of updates"); + Assert.AreNotEqual(0, logs.Count, "Logs are empty"); + Assert.IsFalse(telemetry.IsEmpty, "telemetry is empty"); await jsonClient.Exit(); Assert.AreEqual(0, await jsonClient.WaitServerProcessExit()); Assert.AreEqual(0, jsonClient.ExitCode); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task WhenClientDies_Server_ShouldClose_Gracefully(string tfm) { - using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(TestHost.LocateFrom(_fixture.ProjectPath, "MSTestProject", tfm, buildConfiguration: BuildConfiguration.Release)); + using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(TestHost.LocateFrom(AssetFixture.ProjectPath, "MSTestProject", tfm, buildConfiguration: BuildConfiguration.Release)); LogsCollector logs = new(); jsonClient.RegisterLogListener(logs); TelemetryCollector telemetry = new(); @@ -70,8 +67,7 @@ public async Task WhenClientDies_Server_ShouldClose_Gracefully(string tfm) Assert.AreEqual(3, exitCode); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "MSTestProject"; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs index 152c28dc96..e387366476 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestContextTests.cs @@ -6,17 +6,13 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class TestContextTests : AcceptanceTestBase +[TestClass] +public sealed class TestContextTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public TestContextTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - + [TestMethod] public async Task TestContextsAreCorrectlySet() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextCtor"); // Assert @@ -24,9 +20,10 @@ public async Task TestContextsAreCorrectlySet() testHostResult.AssertOutputContainsSummary(failed: 0, passed: 5, skipped: 0); } + [TestMethod] public async Task TestContext_TestData_PropertyContainsExpectedValue() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextData"); // Assert @@ -34,9 +31,10 @@ public async Task TestContext_TestData_PropertyContainsExpectedValue() testHostResult.AssertOutputContainsSummary(failed: 0, passed: 3, skipped: 0); } + [TestMethod] public async Task TestContext_TestException_PropertyContainsExpectedValue() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextException"); // Assert @@ -46,9 +44,10 @@ public async Task TestContext_TestException_PropertyContainsExpectedValue() testHostResult.AssertOutputContains("Test method TestContextExceptionFailingInTestMethod.TestFailingInTestMethod threw exception:"); } + [TestMethod] public async Task TestContext_TestDisplayName_PropertyContainsExpectedValue() { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent.Arguments); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter ClassName~TestContextDisplayName"); // Assert @@ -56,8 +55,7 @@ public async Task TestContext_TestDisplayName_PropertyContainsExpectedValue() testHostResult.AssertOutputContainsSummary(failed: 0, passed: 4, skipped: 0); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestTestContext"; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs index 8e0c7e53d0..bec1c9dd3a 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestDiscoveryTests.cs @@ -7,21 +7,16 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class TestDiscoveryTests : AcceptanceTestBase +[TestClass] +public class TestDiscoveryTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; private const string AssetName = "TestDiscovery"; - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public TestDiscoveryTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DiscoverTests_FindsAllTests(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests"); @@ -30,10 +25,11 @@ public async Task DiscoverTests_FindsAllTests(string currentTfm) testHostResult.AssertOutputContains("Test2"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DiscoverTests_WithFilter_FindsOnlyFilteredOnes(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --filter Name=Test1"); @@ -42,8 +38,7 @@ public async Task DiscoverTests_WithFilter_FindsOnlyFilteredOnes(string currentT testHostResult.AssertOutputDoesNotContain("Test2"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs index 03ee39c31a..aac2557286 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestFilterTests.cs @@ -7,21 +7,16 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class TestFilterTests : AcceptanceTestBase +[TestClass] +public class TestFilterTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; private const string AssetName = "TestFilter"; - // There's a bug in TAFX where we need to use it at least one time somewhere to use it inside the fixture self (AcceptanceFixture). - public TestFilterTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture, - AcceptanceFixture globalFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task RunWithFilter_UsingTestProperty_FilteredTests(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter tree=one"); @@ -29,10 +24,11 @@ public async Task RunWithFilter_UsingTestProperty_FilteredTests(string currentTf testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task DiscoverTestsWithFilter_UsingTestProperty_FilteredTests(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter tree=one --list-tests"); @@ -43,10 +39,11 @@ public async Task DiscoverTestsWithFilter_UsingTestProperty_FilteredTests(string """); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task UsingTestPropertyForOwnerAndPriorityAndTestCategory_TestsFailed(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter tree!~one"); @@ -60,10 +57,11 @@ failed TestCategoryTest (0ms) """); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task RunWithFilter_UsingTestPropertyForOwner_FilteredButTestsFailed(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter owner=testOwner"); @@ -73,10 +71,11 @@ failed OwnerTest (0ms) """); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task RunWithFilter_UsingTestPropertyForPriorityAndTestCategory_NotFiltered(string currentTfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--filter TestCategory=category|Priority=1"); @@ -84,8 +83,7 @@ public async Task RunWithFilter_UsingTestPropertyForPriorityAndTestCategory_NotF testHostResult.AssertExitCodeIs(8); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public string TargetAssetPath => GetAssetPath(AssetName); diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs index 723ded5cd7..e3c188cd43 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TestRunParametersTests.cs @@ -6,18 +6,14 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class TestRunParametersTests : AcceptanceTestBase +[TestClass] +public sealed class TestRunParametersTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public TestRunParametersTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestRunParameters_WhenProvidingMultipleArgumentsToTheOption(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --test-parameter MyParameter2=MyValue2 MyParameter3=MyValue3"); // Assert @@ -25,10 +21,11 @@ public async Task TestRunParameters_WhenProvidingMultipleArgumentsToTheOption(st testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestRunParameters_WhenProvidingMultipleMultipleTimesTheOptionAndArgument(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--settings my.runsettings --test-parameter MyParameter2=MyValue2 --test-parameter MyParameter3=MyValue3"); // Assert @@ -36,8 +33,7 @@ public async Task TestRunParameters_WhenProvidingMultipleMultipleTimesTheOptionA testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestRunParameters"; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs index 596bbd3325..1c64b26e18 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadContextTests.cs @@ -6,65 +6,67 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class ThreadContextTests : AcceptanceTestBase +[TestClass] +public sealed class ThreadContextTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public ThreadContextTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenCultureIsNotSet_TestMethodFails(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.InitToTestProjectPath, TestAssetFixture.InitToTestProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.InitToTestProjectPath, TestAssetFixture.InitToTestProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertOutputContainsSummary(failed: 1, passed: 0, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenChangedInAssemblyInitialize_IsPassedToTestMethod(string tfm) => await SetCultureInFixtureMethodAndRunTests(tfm, "MSTEST_TEST_SET_CULTURE_ASSEMBLY_INIT"); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenChangedInClassInitialize_IsPassedToTestMethod(string tfm) => await SetCultureInFixtureMethodAndRunTests(tfm, "MSTEST_TEST_SET_CULTURE_CLASS_INIT"); - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenChangedInTestInitialize_IsPassedToTestMethod(string tfm) => await SetCultureInFixtureMethodAndRunTests(tfm, "MSTEST_TEST_SET_CULTURE_TEST_INIT"); - private async Task SetCultureInFixtureMethodAndRunTests(string tfm, string envVarKey) + private static async Task SetCultureInFixtureMethodAndRunTests(string tfm, string envVarKey) { - var testHost = TestHost.LocateFrom(_testAssetFixture.InitToTestProjectPath, TestAssetFixture.InitToTestProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.InitToTestProjectPath, TestAssetFixture.InitToTestProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { [envVarKey] = "true" }); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenUsingSTAThread_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}"); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenUsingSTAThreadAndTimeout_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta-timeout.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -74,10 +76,11 @@ public async Task ThreadingContext_WhenUsingSTAThreadAndTimeout_CurrentCultureFl testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_WhenUsingTimeout_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsProjectPath, TestAssetFixture.CultureFlowsProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "timeout.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -87,29 +90,32 @@ public async Task ThreadingContext_WhenUsingTimeout_CurrentCultureFlowsBetweenMe testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_Inheritance_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 8, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_Inheritance_WhenUsingSTAThread_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}"); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContainsSummary(failed: 0, passed: 8, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_Inheritance_WhenUsingSTAThreadAndTimeout_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta-timeout.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -119,10 +125,11 @@ public async Task ThreadingContext_Inheritance_WhenUsingSTAThreadAndTimeout_Curr testHostResult.AssertOutputContainsSummary(failed: 0, passed: 8, skipped: 0); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task ThreadingContext_Inheritance_WhenUsingTimeout_CurrentCultureFlowsBetweenMethods(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.CultureFlowsInheritanceProjectPath, TestAssetFixture.CultureFlowsInheritanceProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "timeout.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -132,9 +139,7 @@ public async Task ThreadingContext_Inheritance_WhenUsingTimeout_CurrentCultureFl testHostResult.AssertOutputContainsSummary(failed: 0, passed: 8, skipped: 0); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) - : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string InitToTestProjectName = "InitToTestThreadContextProject"; public const string CultureFlowsProjectName = "CultureFlowsThreadContextProject"; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs index b84b167237..b9e91f090c 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ThreadingTests.cs @@ -1,32 +1,27 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Testing.Platform.Acceptance.IntegrationTests; using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class ThreadingTests : AcceptanceTestBase +[TestClass] +public sealed class ThreadingTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public ThreadingTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_WhenMainIsNotSTA_NoRunsettingsProvided_ThreadIsNotSTA(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(0); testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string tfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -34,7 +29,7 @@ public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnW return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -45,7 +40,8 @@ public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnW testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnNonWindows_ThreadIsNotSTAAndWarningIsEmitted(string tfm) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -53,7 +49,7 @@ public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnN return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -65,10 +61,11 @@ public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnN testHostResult.AssertOutputContains("Runsettings entry 'STA' is not supported on non-Windows OSes"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForMTA_ThreadIsNotSTA(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -80,7 +77,8 @@ public async Task TestMethodThreading_WhenMainIsNotSTA_RunsettingsAsksForMTA_Thr testHostResult.AssertOutputDoesNotContain("Runsettings entry 'STA' is not supported on non-Windows OSes"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsProvided_ThreadIsSTA(string tfm) { // Test cannot work on non-Windows OSes as the main method is marked with [STAThread] @@ -89,7 +87,7 @@ public async Task TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsPro return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.STAThreadProjectPath, TestAssetFixture.STAThreadProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.STAThreadProjectPath, TestAssetFixture.STAThreadProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { ["MSTEST_THREAD_STATE_IS_STA"] = "1", @@ -99,7 +97,8 @@ public async Task TestMethodThreading_MainIsSTAThread_OnWindows_NoRunsettingsPro testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForSTA_ThreadIsSTA(string tfm) { // Test cannot work on non-Windows OSes as the main method is marked with [STAThread] @@ -108,7 +107,7 @@ public async Task TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksF return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.STAThreadProjectPath, TestAssetFixture.STAThreadProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.STAThreadProjectPath, TestAssetFixture.STAThreadProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -119,7 +118,8 @@ public async Task TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksF testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksForMTA_ThreadIsMTA(string tfm) { // Test cannot work on non-Windows OSes as the main method is marked with [STAThread] @@ -128,7 +128,7 @@ public async Task TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksF return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.STAThreadProjectPath, TestAssetFixture.STAThreadProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.STAThreadProjectPath, TestAssetFixture.STAThreadProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "mta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -139,7 +139,8 @@ public async Task TestMethodThreading_MainIsSTAThread_OnWindows_RunsettingsAsksF testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string tfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -147,7 +148,7 @@ public async Task LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsA return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.LifecycleAttributesVoidProjectPath, TestAssetFixture.LifecycleAttributesVoidProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.LifecycleAttributesVoidProjectPath, TestAssetFixture.LifecycleAttributesVoidProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -158,7 +159,8 @@ public async Task LifecycleAttributesVoidThreading_WhenMainIsNotSTA_RunsettingsA testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string tfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -166,7 +168,7 @@ public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsA return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.LifecycleAttributesTaskProjectPath, TestAssetFixture.LifecycleAttributesTaskProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.LifecycleAttributesTaskProjectPath, TestAssetFixture.LifecycleAttributesTaskProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -177,7 +179,8 @@ public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsA testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA_With_ParallelAttribute(string tfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -185,7 +188,7 @@ public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsA return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.LifecycleAttributesTaskProjectPath, TestAssetFixture.LifecycleWithParallelAttributesTaskProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.LifecycleAttributesTaskProjectPath, TestAssetFixture.LifecycleWithParallelAttributesTaskProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -196,7 +199,8 @@ public async Task LifecycleAttributesTaskThreading_WhenMainIsNotSTA_RunsettingsA testHostResult.AssertOutputContains("Passed!"); } - [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] public async Task LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_RunsettingsAsksForSTA_OnWindows_ThreadIsSTA(string tfm) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -204,7 +208,7 @@ public async Task LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_Runsett return; } - var testHost = TestHost.LocateFrom(_testAssetFixture.LifecycleAttributesValueTaskProjectPath, TestAssetFixture.LifecycleAttributesValueTaskProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.LifecycleAttributesValueTaskProjectPath, TestAssetFixture.LifecycleAttributesValueTaskProjectName, tfm); string runSettingsFilePath = Path.Combine(testHost.DirectoryName, "sta.runsettings"); TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { @@ -215,9 +219,7 @@ public async Task LifecycleAttributesValueTaskThreading_WhenMainIsNotSTA_Runsett testHostResult.AssertOutputContains("Passed!"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) - : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestThreading"; public const string STAThreadProjectName = "STATestThreading"; diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TimeoutTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TimeoutTests.cs index f2847639e0..28c9eca481 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TimeoutTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/TimeoutTests.cs @@ -7,58 +7,459 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public class TimeoutTests : AcceptanceTestBase +[TestClass] +public class TimeoutTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; + private static readonly Dictionary InfoByKind = new() + { + ["assemblyInit"] = ("TestClass.AssemblyInit", "Assembly initialize", "ASSEMBLYINIT", "AssemblyInitializeTimeout"), + ["assemblyCleanup"] = ("TestClass.AssemblyCleanupMethod", "Assembly cleanup", "ASSEMBLYCLEANUP", "AssemblyCleanupTimeout"), + ["classInit"] = ("TestClass.ClassInit", "Class initialize", "CLASSINIT", "ClassInitializeTimeout"), + ["baseClassInit"] = ("TestClassBase.ClassInitBase", "Class initialize", "BASE_CLASSINIT", "ClassInitializeTimeout"), + ["classCleanup"] = ("TestClass.ClassCleanupMethod", "Class cleanup", "CLASSCLEANUP", "ClassCleanupTimeout"), + ["baseClassCleanup"] = ("TestClassBase.ClassCleanupBase", "Class cleanup", "BASE_CLASSCLEANUP", "ClassCleanupTimeout"), + ["testInit"] = ("TestClass.TestInit", "Test initialize", "TESTINIT", "TestInitializeTimeout"), + ["testCleanup"] = ("TestClass.TestCleanupMethod", "Test cleanup", "TESTCLEANUP", "TestCleanupTimeout"), + }; + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyInit_WhenTestContextCanceled_AssemblyInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestWasCanceledAsync(AssetFixture.CodeWithSixtySecTimeoutAssetPath, TestAssetFixture.CodeWithSixtySecTimeout, + tfm, "TESTCONTEXT_CANCEL_", "assemblyInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyInit_WhenTimeoutExpires_AssemblyInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, + tfm, "LONG_WAIT_", "assemblyInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_AssemblyInitializeExits(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "TIMEOUT_", "assemblyInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInit_WhenTestContextCanceled_ClassInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestWasCanceledAsync(AssetFixture.CodeWithSixtySecTimeoutAssetPath, TestAssetFixture.CodeWithSixtySecTimeout, tfm, + "TESTCONTEXT_CANCEL_", "classInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInit_WhenTimeoutExpires_ClassInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "classInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInit_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "TIMEOUT_", "classInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInitBase_WhenTestContextCanceled_ClassInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestWasCanceledAsync(AssetFixture.CodeWithSixtySecTimeoutAssetPath, TestAssetFixture.CodeWithSixtySecTimeout, tfm, + "TESTCONTEXT_CANCEL_", "baseClassInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInitBase_WhenTimeoutExpires_ClassInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "baseClassInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInitBase_WhenTimeoutExpiresAndTestContextTokenIsUsed_ClassInitializeExits(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "TIMEOUT_", "baseClassInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyInitialize_WhenTimeoutExpires_FromRunSettings_AssemblyInitializeIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "assemblyInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "classInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task BaseClassInitialize_WhenTimeoutExpires_FromRunSettings_ClassInitializeIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "baseClassInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyInitialize_WhenTimeoutExpires_AssemblyInitializeIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "assemblyInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassInitialize_WhenTimeoutExpires_ClassInitializeIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "classInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task BaseClassInitialize_WhenTimeoutExpires_ClassInitializeIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "baseClassInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassCleanupBase_WhenTimeoutExpires_ClassCleanupTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "baseClassCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassCleanup_WhenTimeoutExpires_ClassCleanupTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "classCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "classCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task BaseClassCleanup_WhenTimeoutExpires_FromRunSettings_ClassCleanupIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "baseClassCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task ClassCleanup_WhenTimeoutExpires_ClassCleanupIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "classCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task BaseClassCleanup_WhenTimeoutExpires_ClassCleanupIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "baseClassCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "assemblyCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyCleanup_WhenTimeoutExpires_FromRunSettings_AssemblyCleanupIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "assemblyCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task AssemblyCleanup_WhenTimeoutExpires_AssemblyCleanupIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "assemblyCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestInitialize_WhenTimeoutExpires_TestInitializeTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "testInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestInitialize_WhenTimeoutExpires_FromRunSettings_TestInitializeIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "testInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestInitialize_WhenTimeoutExpires_TestInitializeIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "testInit"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestCleanup_WhenTimeoutExpires_TestCleanupTaskIsCanceled(string tfm) + => await RunAndAssertTestTimedOutAsync(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm, + "LONG_WAIT_", "testCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestCleanup_WhenTimeoutExpires_FromRunSettings_TestCleanupIsCanceled(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 300, false, "testCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task TestCleanup_WhenTimeoutExpires_TestCleanupIsCanceled_AttributeTakesPrecedence(string tfm) + => await RunAndAssertWithRunSettingsAsync(tfm, 25000, true, "testCleanup"); + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenAssemblyInitTimeoutExpires_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["TASKDELAY_ASSEMBLYINIT"] = "1" }); + + testHostResult.AssertOutputContains("AssemblyInit started"); + testHostResult.AssertOutputContains("Assembly initialize method 'TestClass.AssemblyInit' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("AssemblyInit Thread.Sleep completed"); + testHostResult.AssertOutputDoesNotContain("AssemblyInit completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenAssemblyCleanupTimeoutExpires_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["TASKDELAY_ASSEMBLYCLEANUP"] = "1" }); + + testHostResult.AssertOutputContains("AssemblyCleanup started"); + testHostResult.AssertOutputContains("Assembly cleanup method 'TestClass.AssemblyCleanup' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("AssemblyCleanup Thread.Sleep completed"); + testHostResult.AssertOutputDoesNotContain("AssemblyCleanup completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenClassInitTimeoutExpires_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["TASKDELAY_CLASSINIT"] = "1" }); + + testHostResult.AssertOutputContains("ClassInit started"); + testHostResult.AssertOutputContains("Class initialize method 'TestClass.ClassInit' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("ClassInit Thread.Sleep completed"); + testHostResult.AssertOutputDoesNotContain("ClassInit completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenClassCleanupTimeoutExpires_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["TASKDELAY_CLASSCLEANUP"] = "1" }); + + testHostResult.AssertOutputContains("ClassCleanup started"); + testHostResult.AssertOutputContains("Class cleanup method 'TestClass.ClassCleanup' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("ClassCleanup Thread.Sleep completed"); + testHostResult.AssertOutputDoesNotContain("ClassCleanup completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenTestInitTimeoutExpires_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["TASKDELAY_TESTINIT"] = "1" }); + + testHostResult.AssertOutputContains("TestInit started"); + testHostResult.AssertOutputContains("Test initialize method 'TestClass.TestInit' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("TestInit completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenTestCleanupTimeoutExpires_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["TASKDELAY_TESTCLEANUP"] = "1" }); + + testHostResult.AssertOutputContains("TestCleanup started"); + testHostResult.AssertOutputContains("Test cleanup method 'TestClass.TestCleanup' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("TestCleanup completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenAssemblyInitTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["CHECKTOKEN_ASSEMBLYINIT"] = "1" }); + + testHostResult.AssertOutputContains("AssemblyInit started"); + testHostResult.AssertOutputContains("Assembly initialize method 'TestClass.AssemblyInit' timed out after 1000ms"); + testHostResult.AssertOutputContains("AssemblyInit Thread.Sleep completed"); + testHostResult.AssertOutputDoesNotContain("AssemblyInit completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenAssemblyCleanupTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["CHECKTOKEN_ASSEMBLYCLEANUP"] = "1" }); + + testHostResult.AssertOutputContains("AssemblyCleanup started"); + testHostResult.AssertOutputContains("AssemblyCleanup Thread.Sleep completed"); + testHostResult.AssertOutputContains("Assembly cleanup method 'TestClass.AssemblyCleanup' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("AssemblyCleanup completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenClassInitTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["CHECKTOKEN_CLASSINIT"] = "1" }); + + testHostResult.AssertOutputContains("ClassInit started"); + testHostResult.AssertOutputContains("Class initialize method 'TestClass.ClassInit' timed out after 1000ms"); + testHostResult.AssertOutputContains("ClassInit Thread.Sleep completed"); + testHostResult.AssertOutputDoesNotContain("ClassInit completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenClassCleanupTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["CHECKTOKEN_CLASSCLEANUP"] = "1" }); + + testHostResult.AssertOutputContains("ClassCleanup started"); + testHostResult.AssertOutputContains("ClassCleanup Thread.Sleep completed"); + testHostResult.AssertOutputContains("Class cleanup method 'TestClass.ClassCleanup' timed out after 1000ms"); + testHostResult.AssertOutputDoesNotContain("ClassCleanup completed"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenTestInitTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["CHECKTOKEN_TESTINIT"] = "1" }); + + testHostResult.AssertOutputContains("TestInit started"); + testHostResult.AssertOutputDoesNotContain("TestInit completed"); + testHostResult.AssertOutputContains("Test initialize method 'TestClass.TestInit' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeCancellation_WhenTestCleanupTimeoutExpiresAndUserChecksToken_StepThrows(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTimeoutAssetPath, TestAssetFixture.CooperativeTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync( + "--settings my.runsettings", + new() { ["CHECKTOKEN_TESTCLEANUP"] = "1" }); + + testHostResult.AssertOutputContains("TestCleanup started"); + testHostResult.AssertOutputDoesNotContain("TestCleanup completed"); + testHostResult.AssertOutputContains("Test cleanup method 'TestClass.TestCleanup' timed out after 1000ms"); + } + + private static async Task RunAndAssertTestWasCanceledAsync(string rootFolder, string assetName, string tfm, string envVarPrefix, string entryKind) + { + var testHost = TestHost.LocateFrom(rootFolder, assetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { { envVarPrefix + InfoByKind[entryKind].EnvVarSuffix, "1" } }); + testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' was canceled"); + } + + private static async Task RunAndAssertTestTimedOutAsync(string rootFolder, string assetName, string tfm, string envVarPrefix, string entryKind) + { + var testHost = TestHost.LocateFrom(rootFolder, assetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() { { envVarPrefix + InfoByKind[entryKind].EnvVarSuffix, "1" } }); + testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out after 1000ms"); + } + + private static async Task RunAndAssertWithRunSettingsAsync(string tfm, int timeoutValue, bool assertAttributePrecedence, string entryKind) + { + string runSettingsEntry = InfoByKind[entryKind].RunSettingsEntryName; + string runSettings = $""" + + + + + + <{runSettingsEntry}>{timeoutValue} + + +"""; - public TimeoutTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; + // if assertAttributePrecedence is set we will use CodeWithOneSecTimeoutAssetPath + timeoutValue = assertAttributePrecedence ? 1000 : timeoutValue; - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + TestHost testHost = assertAttributePrecedence + ? TestHost.LocateFrom(AssetFixture.CodeWithOneSecTimeoutAssetPath, TestAssetFixture.CodeWithOneSecTimeout, tfm) + : TestHost.LocateFrom(AssetFixture.CodeWithNoTimeoutAssetPath, TestAssetFixture.CodeWithNoTimeout, tfm); + string runSettingsFilePath = Path.Combine(testHost.DirectoryName, $"{Guid.NewGuid():N}.runsettings"); + File.WriteAllText(runSettingsFilePath, runSettings); + + var stopwatch = Stopwatch.StartNew(); + TestHostResult testHostResult = await testHost.ExecuteAsync($"--settings {runSettingsFilePath}", environmentVariables: new() { { $"TIMEOUT_{InfoByKind[entryKind].EnvVarSuffix}", "1" } }); + stopwatch.Stop(); + + if (assertAttributePrecedence) + { + Assert.IsTrue(stopwatch.Elapsed.TotalSeconds < 25); + } + + testHostResult.AssertOutputContains($"{InfoByKind[entryKind].Prefix} method '{InfoByKind[entryKind].MethodFullName}' timed out after {timeoutValue}ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TimeoutWithInvalidArg_WithoutLetterSuffix_OutputInvalidMessage(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TimeoutWithInvalidArg_WithInvalidLetterSuffix_OutputInvalidMessage(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5y"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task TimeoutWithInvalidArg_WithInvalidFormat_OutputInvalidMessage(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5h6m"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task Timeout_WhenTimeoutValueSmallerThanTestDuration_OutputContainsCancelingMessage(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1s"); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); testHostResult.AssertOutputContains("Canceling the test session"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task Timeout_WhenTimeoutValueGreaterThanTestDuration_OutputDoesNotContainCancelingMessage(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 30s"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -66,10 +467,127 @@ public async Task Timeout_WhenTimeoutValueGreaterThanTestDuration_OutputDoesNotC testHostResult.AssertOutputDoesNotContain("Canceling the test session"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task Timeout_WhenMethodTimeoutAndWaitInCtor_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.TestMethodTimeoutAssetPath, TestAssetFixture.TestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_CTOR"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test 'TestMethod' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task Timeout_WhenMethodTimeoutAndWaitInTestInit_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.TestMethodTimeoutAssetPath, TestAssetFixture.TestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_TESTINIT"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test 'TestMethod' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task Timeout_WhenMethodTimeoutAndWaitInTestCleanup_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.TestMethodTimeoutAssetPath, TestAssetFixture.TestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_TESTCLEANUP"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test 'TestMethod' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task Timeout_WhenMethodTimeoutAndWaitInTestMethod_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.TestMethodTimeoutAssetPath, TestAssetFixture.TestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_TEST"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test 'TestMethod' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeTimeout_WhenMethodTimeoutAndWaitInCtor_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTestMethodTimeoutAssetPath, TestAssetFixture.CooperativeTestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_CTOR"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test 'TestMethod' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeTimeout_WhenMethodTimeoutAndWaitInTestInit_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTestMethodTimeoutAssetPath, TestAssetFixture.CooperativeTestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_TESTINIT"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test initialize method 'TimeoutTest.UnitTest1.TestInit' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeTimeout_WhenMethodTimeoutAndWaitInTestCleanup_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTestMethodTimeoutAssetPath, TestAssetFixture.CooperativeTestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_TESTCLEANUP"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test cleanup method 'TimeoutTest.UnitTest1.TestCleanup' timed out after 1000ms"); + } + + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + public async Task CooperativeTimeout_WhenMethodTimeoutAndWaitInTestMethod_TestGetsCanceled(string tfm) + { + var testHost = TestHost.LocateFrom(AssetFixture.CooperativeTestMethodTimeoutAssetPath, TestAssetFixture.CooperativeTestMethodTimeout, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync(environmentVariables: new() + { + ["LONG_WAIT_TEST"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.AtLeastOneTestFailed); + testHostResult.AssertOutputContains("Test 'TestMethod' timed out after 1000ms"); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string AssetName = "TimeoutTest"; + public const string CodeWithOneSecTimeout = nameof(CodeWithOneSecTimeout); + public const string CodeWithSixtySecTimeout = nameof(CodeWithSixtySecTimeout); + public const string CodeWithNoTimeout = nameof(CodeWithNoTimeout); + public const string CooperativeTimeout = nameof(CooperativeTimeout); + public const string TestMethodTimeout = nameof(TestMethodTimeout); + public const string CooperativeTestMethodTimeout = nameof(CooperativeTestMethodTimeout); private const string TestCode = """ #file TimeoutTest.csproj @@ -103,16 +621,407 @@ public void TestA() Thread.Sleep(10000); } } +"""; + + private const string CooperativeTimeoutSourceCode = """ +#file $ProjectName$.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + + PreserveNewest + + + + + +#file my.runsettings + + + false + + + +#file UnitTest1.cs +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[TestClass] +public class TestClass +{ + [Timeout(1000, CooperativeCancellation = true)] + [AssemblyInitialize] + public static async Task AssemblyInit(TestContext testContext) + => await DoWork("ASSEMBLYINIT", "AssemblyInit", testContext); + + [Timeout(1000, CooperativeCancellation = true)] + [AssemblyCleanup] + public static async Task AssemblyCleanup(TestContext testContext) + => await DoWork("ASSEMBLYCLEANUP", "AssemblyCleanup", testContext); + + [Timeout(1000, CooperativeCancellation = true)] + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + => await DoWork("CLASSINIT", "ClassInit", testContext); + + [Timeout(1000, CooperativeCancellation = true)] + [ClassCleanup(ClassCleanupBehavior.EndOfClass)] + public static async Task ClassCleanup(TestContext testContext) + => await DoWork("CLASSCLEANUP", "ClassCleanup", testContext); + + public TestContext TestContext { get; set; } + + [Timeout(1000, CooperativeCancellation = true)] + [TestInitialize] + public async Task TestInit() + => await DoWork("TESTINIT", "TestInit", TestContext); + + [Timeout(1000, CooperativeCancellation = true)] + [TestCleanup] + public async Task TestCleanup() + => await DoWork("TESTCLEANUP", "TestCleanup", TestContext); + + [TestMethod] + public void TestMethod() + { + } + + private static async Task DoWork(string envVarSuffix, string stepName, TestContext testContext) + { + Console.WriteLine($"{stepName} started"); + + if (Environment.GetEnvironmentVariable($"TASKDELAY_{envVarSuffix}") == "1") + { + await Task.Delay(10_000, testContext.CancellationTokenSource.Token); + } + else + { + // We want to wait more than the timeout value to ensure the timeout is hit + await Task.Delay(2_000); + Console.WriteLine($"{stepName} Thread.Sleep completed"); + if (Environment.GetEnvironmentVariable($"CHECKTOKEN_{envVarSuffix}") == "1") + { + testContext.CancellationTokenSource.Token.ThrowIfCancellationRequested(); + } + + } + + Console.WriteLine($"{stepName} completed"); + } +} +"""; + + private const string SourceCode = """ +#file $ProjectName$.csproj + + + + Exe + true + $TargetFrameworks$ + + + + + + + + + +#file UnitTest1.cs + +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +public class TestClassBase +{ + $TimeoutAttribute$ + [ClassInitialize(inheritanceBehavior: InheritanceBehavior.BeforeEachDerivedClass)] + public static async Task ClassInitBase(TestContext testContext) + { + if (Environment.GetEnvironmentVariable("TESTCONTEXT_CANCEL_BASE_CLASSINIT") == "1") + { + testContext.CancellationTokenSource.Cancel(); + await Task.Delay(10_000); + } + else if (Environment.GetEnvironmentVariable("LONG_WAIT_BASE_CLASSINIT") == "1") + { + await Task.Delay(10_000); + } + else if (Environment.GetEnvironmentVariable("TIMEOUT_BASE_CLASSINIT") == "1") + { + await Task.Delay(10_000, testContext.CancellationTokenSource.Token); + } + else + { + await Task.CompletedTask; + } + } + + $TimeoutAttribute$ + [ClassCleanup(inheritanceBehavior: InheritanceBehavior.BeforeEachDerivedClass)] + public static async Task ClassCleanupBase() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_BASE_CLASSCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_BASE_CLASSCLEANUP") == "1") + { + await Task.Delay(10_000); + } + else + { + await Task.CompletedTask; + } + } + +} + +[TestClass] +public class TestClass : TestClassBase +{ + $TimeoutAttribute$ + [AssemblyInitialize] + public static async Task AssemblyInit(TestContext testContext) + { + if (Environment.GetEnvironmentVariable("TESTCONTEXT_CANCEL_ASSEMBLYINIT") == "1") + { + testContext.CancellationTokenSource.Cancel(); + await Task.Delay(10_000); + } + else if (Environment.GetEnvironmentVariable("LONG_WAIT_ASSEMBLYINIT") == "1") + { + await Task.Delay(10_000); + } + else if (Environment.GetEnvironmentVariable("TIMEOUT_ASSEMBLYINIT") == "1") + { + await Task.Delay(60_000, testContext.CancellationTokenSource.Token); + } + else + { + await Task.CompletedTask; + } + } + + $TimeoutAttribute$ + [AssemblyCleanup] + public static async Task AssemblyCleanupMethod() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_ASSEMBLYCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_ASSEMBLYCLEANUP") == "1") + { + await Task.Delay(10_000); + } + else + { + await Task.CompletedTask; + } + } + + $TimeoutAttribute$ + [ClassInitialize] + public static async Task ClassInit(TestContext testContext) + { + if (Environment.GetEnvironmentVariable("TESTCONTEXT_CANCEL_CLASSINIT") == "1") + { + testContext.CancellationTokenSource.Cancel(); + await Task.Delay(10_000); + } + else if (Environment.GetEnvironmentVariable("LONG_WAIT_CLASSINIT") == "1") + { + await Task.Delay(10_000); + } + else if (Environment.GetEnvironmentVariable("TIMEOUT_CLASSINIT") == "1") + { + await Task.Delay(60_000, testContext.CancellationTokenSource.Token); + } + else + { + await Task.CompletedTask; + } + } + + $TimeoutAttribute$ + [ClassCleanup] + public static async Task ClassCleanupMethod() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_CLASSCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_CLASSCLEANUP") == "1") + { + await Task.Delay(10_000); + } + else + { + await Task.CompletedTask; + } + } + + $TimeoutAttribute$ + [TestInitialize] + public async Task TestInit() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_TESTINIT") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_TESTINIT") == "1") + { + await Task.Delay(10_000); + } + else + { + await Task.CompletedTask; + } + } + + $TimeoutAttribute$ + [TestCleanup] + public async Task TestCleanupMethod() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_TESTCLEANUP") == "1" || Environment.GetEnvironmentVariable("TIMEOUT_TESTCLEANUP") == "1") + { + await Task.Delay(10_000); + } + else + { + await Task.CompletedTask; + } + } + + [TestMethod] + public Task Test1() => Task.CompletedTask; +} +"""; + + private const string TestMethodTimeoutCode = """ +#file $ProjectName$.csproj + + + $TargetFrameworks$ + true + Exe + enable + preview + + + + + + +#file UnitTest1.cs +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +namespace TimeoutTest; +[TestClass] +public class UnitTest1 +{ + private readonly TestContext _testContext; + + public UnitTest1(TestContext testContext) + { + _testContext = testContext; + if (Environment.GetEnvironmentVariable("LONG_WAIT_CTOR") == "1") + { + Task.Delay(10_000, _testContext.CancellationTokenSource.Token).Wait(); + } + } + + [TestInitialize] + public async Task TestInit() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_TESTINIT") == "1") + { + await Task.Delay(10_000, _testContext.CancellationTokenSource.Token); + } + } + + [TestCleanup] + public async Task TestCleanup() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_TESTCLEANUP") == "1") + { + await Task.Delay(10_000, _testContext.CancellationTokenSource.Token); + } + } + + [TestMethod] + [Timeout(1000$TimeoutExtraArgs$)] + public async Task TestMethod() + { + if (Environment.GetEnvironmentVariable("LONG_WAIT_TEST") == "1") + { + await Task.Delay(10_000, _testContext.CancellationTokenSource.Token); + } + } +} """; public string NoExtensionTargetAssetPath => GetAssetPath(AssetName); + public string CodeWithOneSecTimeoutAssetPath => GetAssetPath(CodeWithOneSecTimeout); + + public string CodeWithSixtySecTimeoutAssetPath => GetAssetPath(CodeWithSixtySecTimeout); + + public string CodeWithNoTimeoutAssetPath => GetAssetPath(CodeWithNoTimeout); + + public string CooperativeTimeoutAssetPath => GetAssetPath(CooperativeTimeout); + + public string TestMethodTimeoutAssetPath => GetAssetPath(TestMethodTimeout); + + public string CooperativeTestMethodTimeoutAssetPath => GetAssetPath(CooperativeTestMethodTimeout); + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() { yield return (AssetName, AssetName, TestCode .PatchTargetFrameworks(TargetFrameworks.All) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CodeWithNoTimeout, CodeWithNoTimeout, + SourceCode + .PatchCodeWithReplace("$TimeoutAttribute$", string.Empty) + .PatchCodeWithReplace("$ProjectName$", CodeWithNoTimeout) + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CodeWithOneSecTimeout, CodeWithOneSecTimeout, + SourceCode + .PatchCodeWithReplace("$TimeoutAttribute$", "[Timeout(1000)]") + .PatchCodeWithReplace("$ProjectName$", CodeWithOneSecTimeout) + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CodeWithSixtySecTimeout, CodeWithSixtySecTimeout, + SourceCode + .PatchCodeWithReplace("$TimeoutAttribute$", "[Timeout(60000)]") + .PatchCodeWithReplace("$ProjectName$", CodeWithSixtySecTimeout) + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CooperativeTimeout, CooperativeTimeout, + CooperativeTimeoutSourceCode + .PatchCodeWithReplace("$ProjectName$", CooperativeTimeout) + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (TestMethodTimeout, TestMethodTimeout, + TestMethodTimeoutCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$ProjectName$", TestMethodTimeout) + .PatchCodeWithReplace("$TimeoutExtraArgs$", string.Empty) + .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); + + yield return (CooperativeTestMethodTimeout, CooperativeTestMethodTimeout, + TestMethodTimeoutCode + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$ProjectName$", CooperativeTestMethodTimeout) + .PatchCodeWithReplace("$TimeoutExtraArgs$", ", CooperativeCancellation = true") .PatchCodeWithReplace("$MSTestVersion$", MSTestVersion)); } } diff --git a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs index 110cfc716e..acaed7964b 100644 --- a/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs +++ b/test/IntegrationTests/MSTest.Acceptance.IntegrationTests/ValueTaskTests.cs @@ -6,18 +6,14 @@ namespace MSTest.Acceptance.IntegrationTests; -[TestGroup] -public sealed class ValueTaskTests : AcceptanceTestBase +[TestClass] +public sealed class ValueTaskTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public ValueTaskTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [TestMethod] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] public async Task CanUseValueTaskForAllKnownLocations(string tfm) { - var testHost = TestHost.LocateFrom(_testAssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); + var testHost = TestHost.LocateFrom(AssetFixture.ProjectPath, TestAssetFixture.ProjectName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); // Assert @@ -25,8 +21,7 @@ public async Task CanUseValueTaskForAllKnownLocations(string tfm) testHostResult.AssertOutputContainsSummary(failed: 1, passed: 2, skipped: 1); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string ProjectName = "TestValueTask"; diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Extensions/VerifyE2E.cs b/test/IntegrationTests/MSTest.IntegrationTests/Extensions/VerifyE2E.cs index 924ca20614..9d69238a49 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Extensions/VerifyE2E.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Extensions/VerifyE2E.cs @@ -41,13 +41,13 @@ public static void TestsFailed(IEnumerable actual, params string[] e => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Failed, expectedTests, true); public static void ContainsTestsPassed(IEnumerable actual, IEnumerable testCases, IEnumerable expectedTests, MSTestSettings settings = null) - => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Passed, expectedTests, false); + => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Passed, expectedTests); public static void ContainsTestsPassed(IEnumerable actual, params string[] expectedTests) => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Passed, expectedTests); public static void ContainsTestsFailed(IEnumerable actual, IEnumerable testCases, IEnumerable expectedTests, MSTestSettings settings = null) - => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Failed, expectedTests, false); + => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Failed, expectedTests); public static void ContainsTestsFailed(IEnumerable actual, params string[] expectedTests) => ContainsExpectedTestsWithOutcome(actual, TestOutcome.Failed, expectedTests); diff --git a/test/IntegrationTests/MSTest.IntegrationTests/MSTest.IntegrationTests.csproj b/test/IntegrationTests/MSTest.IntegrationTests/MSTest.IntegrationTests.csproj index 43e0e78f28..e95ddb20bb 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/MSTest.IntegrationTests.csproj +++ b/test/IntegrationTests/MSTest.IntegrationTests/MSTest.IntegrationTests.csproj @@ -14,12 +14,15 @@ - + + Analyzer + false + diff --git a/test/IntegrationTests/MSTest.IntegrationTests/OutputTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/OutputTests.cs index fcb07b4ed8..1e8284032d 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/OutputTests.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/OutputTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using FluentAssertions; using Microsoft.MSTestV2.CLIAutomation; @@ -18,7 +16,7 @@ public class OutputTests : CLITestBase public void OutputIsNotMixedWhenAsyncTestsRunInParallel() => ValidateOutputForClass("UnitTest2"); - private void ValidateOutputForClass(string className) + private static void ValidateOutputForClass(string className) { // LogMessageListener uses an implementation of a string writer that captures output per async context. // This allows us to capture output from tasks even when they are running in parallel. diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs index e36a2f78ef..636ac3737e 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Collections.Immutable; + using Microsoft.MSTestV2.CLIAutomation; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; namespace MSTest.IntegrationTests; @@ -21,24 +24,36 @@ public void CustomTestDataSourceTests() string assemblyPath = GetAssetFullPath(TestAssetName); // Act - System.Collections.Immutable.ImmutableArray testCases = DiscoverTests(assemblyPath, "CustomTestDataSourceTestMethod1"); - System.Collections.Immutable.ImmutableArray testResults = RunTests(testCases); + ImmutableArray testCases = DiscoverTests(assemblyPath, "CustomTestDataSourceTestMethod1"); + ImmutableArray testResults = RunTests(testCases); // Assert VerifyE2E.ContainsTestsPassed(testResults, "CustomTestDataSourceTestMethod1 (1,2,3)", "CustomTestDataSourceTestMethod1 (4,5,6)"); } + public void CustomEmptyTestDataSourceTests() + { + // Arrange + string assemblyPath = GetAssetFullPath(TestAssetName); + + // Act + ImmutableArray testCases = DiscoverTests(assemblyPath, "CustomEmptyTestDataSourceTestMethod"); + ImmutableArray testResults = RunTests(testCases); + + // Assert + VerifyE2E.ContainsTestsFailed(testResults, new string[] { null }); + } + public void AssertExtensibilityTests() { // Arrange string assemblyPath = GetAssetFullPath(TestAssetName); // Act - System.Collections.Immutable.ImmutableArray testCases = DiscoverTests(assemblyPath, "FxExtensibilityTestProject.AssertExTest"); - System.Collections.Immutable.ImmutableArray testResults = RunTests(testCases); + ImmutableArray testCases = DiscoverTests(assemblyPath, "FxExtensibilityTestProject.AssertExTest"); + ImmutableArray testResults = RunTests(testCases); // Assert - VerifyE2E.ContainsTestsPassed(testResults, "BasicAssertExtensionTest", "ChainedAssertExtensionTest"); VerifyE2E.ContainsTestsFailed(testResults, "BasicFailingAssertExtensionTest", "ChainedFailingAssertExtensionTest"); } @@ -48,8 +63,8 @@ public void ExecuteCustomTestExtensibilityTests() string assemblyPath = GetAssetFullPath(TestAssetName); // Act - System.Collections.Immutable.ImmutableArray testCases = DiscoverTests(assemblyPath, "(Name~CustomTestMethod1)|(Name~CustomTestClass1)"); - System.Collections.Immutable.ImmutableArray testResults = RunTests(testCases); + ImmutableArray testCases = DiscoverTests(assemblyPath, "(Name~CustomTestMethod1)|(Name~CustomTestClass1)"); + ImmutableArray testResults = RunTests(testCases); // Assert VerifyE2E.ContainsTestsPassed( @@ -75,8 +90,8 @@ public void ExecuteCustomTestExtensibilityWithTestDataTests() string assemblyPath = GetAssetFullPath(TestAssetName); // Act - System.Collections.Immutable.ImmutableArray testCases = DiscoverTests(assemblyPath, "Name~CustomTestMethod2"); - System.Collections.Immutable.ImmutableArray testResults = RunTests(testCases); + ImmutableArray testCases = DiscoverTests(assemblyPath, "Name~CustomTestMethod2"); + ImmutableArray testResults = RunTests(testCases); // Assert VerifyE2E.TestsPassed( @@ -94,4 +109,24 @@ public void ExecuteCustomTestExtensibilityWithTestDataTests() "CustomTestMethod2 (\"C\")", "CustomTestMethod2 (\"C\")"); } + + public void WhenUsingCustomITestDataSourceWithExpansionDisabled_RespectSetting() + { + // Arrange + string assemblyPath = GetAssetFullPath(TestAssetName); + + // Act + ImmutableArray testCases = DiscoverTests(assemblyPath, "CustomDisableExpansionTestDataSourceTestMethod1"); + ImmutableArray testResults = RunTests(testCases); + + // Assert + Verify(testCases.Length == 1); + + VerifyE2E.TestsPassed( + testResults, + "CustomDisableExpansionTestDataSourceTestMethod1 (1,2,3)", + "CustomDisableExpansionTestDataSourceTestMethod1 (4,5,6)"); + + VerifyE2E.TestsFailed(testResults); + } } diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DynamicDataTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DynamicDataTests.cs index 952119a8bb..06d74ed0c7 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DynamicDataTests.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Parameterized tests/DynamicDataTests.cs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Collections.Immutable; + using Microsoft.MSTestV2.CLIAutomation; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; namespace MSTest.IntegrationTests; @@ -15,19 +18,29 @@ public void ExecuteDynamicDataTests() string assemblyPath = GetAssetFullPath(TestAssetName); // Act - System.Collections.Immutable.ImmutableArray testCases = DiscoverTests(assemblyPath); - System.Collections.Immutable.ImmutableArray testResults = RunTests(testCases); + ImmutableArray testCases = DiscoverTests(assemblyPath, testCaseFilter: "ClassName~DynamicDataTests"); + ImmutableArray testResults = RunTests(testCases); // Assert VerifyE2E.TestsPassed( testResults, "DynamicDataTest_SourceProperty (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAuto (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", "Custom DynamicDataTestMethod DynamicDataTest_SourcePropertyOtherType_CustomDisplayName with 2 parameters", "Custom DynamicDataTestMethod DynamicDataTest_SourceMethodOtherType_CustomDisplayName with 2 parameters", "UserDynamicDataTestMethod DynamicDataTest_SourcePropertyOtherType_CustomDisplayNameOtherType with 2 parameters", "Custom DynamicDataTestMethod DynamicDataTest_SourceMethod_CustomDisplayName with 2 parameters", "UserDynamicDataTestMethod DynamicDataTest_SourceMethodOtherType_CustomDisplayNameOtherType with 2 parameters", "DynamicDataTest_SourceMethod (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAuto (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", "UserDynamicDataTestMethod DynamicDataTest_SourcePropertyOtherType_CustomDisplayNameOtherType with 2 parameters", "StackOverflowException_Example (DataSourceTestProject.DynamicDataTests+ExampleTestCase)", "Custom DynamicDataTestMethod DynamicDataTest_SourceProperty_CustomDisplayName with 2 parameters", @@ -43,15 +56,28 @@ public void ExecuteDynamicDataTests() "UserDynamicDataTestMethod DynamicDataTest_SourceProperty_CustomDisplayNameOtherType with 2 parameters", "UserDynamicDataTestMethod DynamicDataTest_SourceProperty_CustomDisplayNameOtherType with 2 parameters", "DynamicDataTest_SourceMethod (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAuto (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", "Custom DynamicDataTestMethod DynamicDataTest_SourcePropertyOtherType_CustomDisplayName with 2 parameters", "UserDynamicDataTestMethod DynamicDataTest_SourceMethodOtherType_CustomDisplayNameOtherType with 2 parameters", "DynamicDataTest_SourceProperty (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAuto (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", "DynamicDataTest_SourcePropertyOtherType (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", "Custom DynamicDataTestMethod DynamicDataTest_SourceMethod_CustomDisplayName with 2 parameters", "MethodWithOverload (\"1\",1)", "MethodWithOverload (\"2\",1)", "MethodWithOverload (1,\"0\")", - "MethodWithOverload (2,\"2\")"); + "MethodWithOverload (2,\"2\")", + "DynamicDataTest_SimpleCollection (0)", + "DynamicDataTest_SimpleCollection (2)", + "DynamicDataTest_SimpleCollection (4)"); VerifyE2E.FailedTestCount(testResults, 0); } @@ -62,8 +88,8 @@ public void ExecuteDynamicDataTestsWithCategoryFilter() string assemblyPath = GetAssetFullPath(TestAssetName); // Act - System.Collections.Immutable.ImmutableArray testCases = DiscoverTests(assemblyPath, "TestCategory~DynamicDataWithCategory"); - System.Collections.Immutable.ImmutableArray testResults = RunTests(testCases); + ImmutableArray testCases = DiscoverTests(assemblyPath, "TestCategory~DynamicDataWithCategory"); + ImmutableArray testResults = RunTests(testCases); // Assert VerifyE2E.ContainsTestsPassed( @@ -73,4 +99,37 @@ public void ExecuteDynamicDataTestsWithCategoryFilter() VerifyE2E.FailedTestCount(testResults, 0); } + + public void ExecuteNonExpandableDynamicDataTests() + { + // Arrange + string assemblyPath = GetAssetFullPath(TestAssetName); + + // Act + ImmutableArray testCases = DiscoverTests(assemblyPath, testCaseFilter: "ClassName~DisableExpansionTests"); + ImmutableArray testResults = RunTests(testCases); + + // Assert + Verify(testCases.Length == 6); + + VerifyE2E.TestsPassed( + testResults, + "TestPropertySourceOnCurrentType (1,a)", + "TestPropertySourceOnCurrentType (2,b)", + "TestPropertySourceOnDifferentType (3,c)", + "TestPropertySourceOnDifferentType (4,d)", + "TestPropertyWithTwoSourcesAndSecondDisablesExpansion (1,a)", + "TestPropertyWithTwoSourcesAndSecondDisablesExpansion (2,b)", + "TestPropertyWithTwoSourcesAndSecondDisablesExpansion (3,c)", + "TestPropertyWithTwoSourcesAndSecondDisablesExpansion (4,d)", + "TestMethodSourceOnDifferentType (3,c)", + "TestMethodSourceOnDifferentType (4,d)", + "TestPropertyWithTwoSourcesAndFirstDisablesExpansion (1,a)", + "TestPropertyWithTwoSourcesAndFirstDisablesExpansion (2,b)", + "TestPropertyWithTwoSourcesAndFirstDisablesExpansion (3,c)", + "TestPropertyWithTwoSourcesAndFirstDisablesExpansion (4,d)", + "TestMethodSourceOnCurrentType (1,a)", + "TestMethodSourceOnCurrentType (2,b)"); + VerifyE2E.FailedTestCount(testResults, 0); + } } diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs index 12e2592fd6..31d9057b6f 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Diagnostics; using DiscoveryAndExecutionTests.Utilities; @@ -21,21 +19,21 @@ namespace Microsoft.MSTestV2.CLIAutomation; public partial class CLITestBase : TestContainer { - internal ImmutableArray DiscoverTests(string assemblyPath, string testCaseFilter = null) + internal static ImmutableArray DiscoverTests(string assemblyPath, string testCaseFilter = null) { var unitTestDiscoverer = new UnitTestDiscoverer(); var logger = new InternalLogger(); var sink = new InternalSink(); - string runSettingXml = GetRunSettingXml(string.Empty); - var context = new InternalDiscoveryContext(runSettingXml, testCaseFilter); + string runSettingsXml = GetRunSettingsXml(string.Empty); + var context = new InternalDiscoveryContext(runSettingsXml, testCaseFilter); unitTestDiscoverer.DiscoverTestsInSource(assemblyPath, logger, sink, context); return sink.DiscoveredTests; } - internal ImmutableArray RunTests(IEnumerable testCases) + internal static ImmutableArray RunTests(IEnumerable testCases) { var testExecutionManager = new TestExecutionManager(); var frameworkHandle = new InternalFrameworkHandle(); diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/TestCaseFilterFactory.cs b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/TestCaseFilterFactory.cs index a662bb0f00..57851c079c 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/TestCaseFilterFactory.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/TestCaseFilterFactory.cs @@ -2,14 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; +using Polyfills; + namespace DiscoveryAndExecutionTests.Utilities; internal static class TestCaseFilterFactory diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs index 0125ea0206..4ef5aaf14c 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.MSTestV2.CLIAutomation; namespace MSTest.VstestConsoleWrapper.IntegrationTests; diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs index 56a788a403..a9a3cde76c 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/FixturesTests.cs @@ -138,7 +138,7 @@ public void RunSingleTest_ClassInitialize_Failure_Runs_AssemblyFixtures() ValidatePassedTests([AssemblyInitialize, AssemblyCleanup, ClassCleanup]); } - private string GetRunSettings(bool fixturesEnabled, bool assemblyInitialize, bool assemblyCleanup, bool classInitialize, bool classCleanup, bool test) + private static string GetRunSettings(bool fixturesEnabled, bool assemblyInitialize, bool assemblyCleanup, bool classInitialize, bool classCleanup, bool test) => $@" diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs index 14ba3ffaf2..44f953da41 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataExtensibilityTests.cs @@ -13,7 +13,7 @@ public void ExecuteTestDataSourceExtensibilityTests() { InvokeVsTestForExecution([TestAssetName]); ValidatePassedTestsContain("CustomTestDataSourceTestMethod1 (1,2,3)", "CustomTestDataSourceTestMethod1 (4,5,6)"); - ValidateFailedTestsContain(false, "FxExtensibilityTestProject.TestDataSourceExTests.CustomTestDataSourceTestMethod1"); + ValidateFailedTestsContain(false, "FxExtensibilityTestProject.TestDataSourceExTests.CustomEmptyTestDataSourceTestMethod"); } public void ExecuteDynamicDataExtensibilityTests() diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs index 71ca40fa64..31a41e6d4a 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.MSTestV2.CLIAutomation; namespace MSTest.VstestConsoleWrapper.IntegrationTests; diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs index 8cebe74926..ab9083a04b 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DynamicDataTests.cs @@ -14,14 +14,34 @@ public void ExecuteDynamicDataTests() // Arrange & Act InvokeVsTestForExecution( [TestAssetName], - testCaseFilter: "DynamicDataTest"); + testCaseFilter: "ClassName=DataSourceTestProject.DynamicDataTests"); // Assert ValidatePassedTests( "DynamicDataTest_SourceMethod (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", "DynamicDataTest_SourceMethod (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAuto (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAuto (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourceMethodAutoShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", "DynamicDataTest_SourceProperty (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", "DynamicDataTest_SourceProperty (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAuto (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAuto (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoFromBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoFromBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoShadowingBase (\"John;Doe\",LibProjectReferencedByDataSourceTest.User)", + "DynamicDataTest_SourcePropertyAutoShadowingBase (\"Jane;Doe\",LibProjectReferencedByDataSourceTest.User)", "Custom DynamicDataTestMethod DynamicDataTest_SourceMethod_CustomDisplayName with 2 parameters", "Custom DynamicDataTestMethod DynamicDataTest_SourceMethod_CustomDisplayName with 2 parameters", "Custom DynamicDataTestMethod DynamicDataTest_SourceProperty_CustomDisplayName with 2 parameters", @@ -48,7 +68,10 @@ public void ExecuteDynamicDataTests() "MethodWithOverload (\"1\",1)", "MethodWithOverload (\"2\",1)", "MethodWithOverload (1,\"0\")", - "MethodWithOverload (2,\"2\")"); + "MethodWithOverload (2,\"2\")", + "DynamicDataTest_SimpleCollection (0)", + "DynamicDataTest_SimpleCollection (2)", + "DynamicDataTest_SimpleCollection (4)"); ValidateFailedTestsCount(0); } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs index 3938ea7cf8..e35a768106 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/AbortionTests.cs @@ -1,24 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class AbortionTests : AcceptanceTestBase +[TestClass] +public class AbortionTests : AcceptanceTestBase { private const string AssetName = "Abort"; - private readonly TestAssetFixture _testAssetFixture; - - public AbortionTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; // We retry because sometime the Canceling the session message is not showing up. - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task AbortWithCTRLPlusC_TestHost_Succeeded(string tfm) { // We expect the same semantic for Linux, the test setup is not cross and we're using specific @@ -28,14 +20,14 @@ public async Task AbortWithCTRLPlusC_TestHost_Succeeded(string tfm) return; } - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.TestSessionAborted); // We check only in netcore for netfx is now showing in CI every time, the same behavior in local something works sometime nope. // Manual test works pretty always as expected, looks like the implementation is different, we care more on .NET Core. - if (TargetFrameworks.Net.Select(x => x.Arguments).Contains(tfm)) + if (TargetFrameworks.Net.Contains(tfm)) { testHostResult.AssertOutputMatchesRegex("Canceling the test session.*"); } @@ -43,8 +35,7 @@ public async Task AbortWithCTRLPlusC_TestHost_Succeeded(string tfm) testHostResult.AssertOutputContainsSummary(failed: 0, passed: 0, skipped: 0, aborted: true); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string Sources = """ #file Abort.csproj @@ -78,11 +69,11 @@ internal sealed class Program public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyAdapter()); + builder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); _ = Task.Run(() => { - DummyAdapter.FireCancel.Wait(); + DummyTestFramework.FireCancel.Wait(); if (!GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0)) { @@ -105,10 +96,10 @@ public enum ConsoleCtrlEvent } -internal class DummyAdapter : ITestFramework, IDataProducer +internal class DummyTestFramework : ITestFramework, IDataProducer { public static readonly ManualResetEventSlim FireCancel = new ManualResetEventSlim(false); - public string Uid => nameof(DummyAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => string.Empty; @@ -118,9 +109,11 @@ internal class DummyAdapter : ITestFramework, IDataProducer public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; - public Task CloseTestSessionAsync(CloseTestSessionContext context) => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); - public Task CreateTestSessionAsync(CreateTestSessionContext context) => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); public async Task ExecuteRequestAsync(ExecuteRequestContext context) { diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs index c6bb94ac4f..6dd94e88c1 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashDumpTests.cs @@ -1,52 +1,60 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public sealed class CrashDumpTests : AcceptanceTestBase +[TestClass] +public sealed class CrashDumpTests : AcceptanceTestBase { - internal static Func RetryPolicy - => ex => ex.ToString().Contains("FAILED No such process") - || ex.ToString().Contains("FAILED 13 (Permission denied)") - || ex.ToString().Contains("Problem suspending threads"); - - private readonly TestAssetFixture _testAssetFixture; - - public CrashDumpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task CrashDump_DefaultSetting_CreateDump(string tfm) { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", tfm); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --results-directory {resultDirectory}"); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); string? dumpFile = Directory.GetFiles(resultDirectory, "CrashDump.dll_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); Assert.IsTrue(dumpFile is not null, $"Dump file not found '{tfm}'\n{testHostResult}'"); } + [TestMethod] public async Task CrashDump_CustomDumpName_CreateDump() { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-filename customdumpname.dmp --results-directory {resultDirectory}"); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); Assert.IsTrue(Directory.GetFiles(resultDirectory, "customdumpname.dmp", SearchOption.AllDirectories).SingleOrDefault() is not null, "Dump file not found"); } - [Arguments("Mini")] - [Arguments("Heap")] - [Arguments("Triage")] - [Arguments("Full")] + [DataRow("Mini")] + [DataRow("Heap")] + [DataRow("Triage")] + [DataRow("Full")] + [TestMethod] public async Task CrashDump_Formats_CreateDump(string format) { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type {format} --results-directory {resultDirectory}"); testHostResult.AssertExitCodeIs(ExitCodes.TestHostProcessExitedNonGracefully); string? dumpFile = Directory.GetFiles(resultDirectory, "CrashDump.dll_*.dmp", SearchOption.AllDirectories).SingleOrDefault(); @@ -54,17 +62,17 @@ public async Task CrashDump_Formats_CreateDump(string format) File.Delete(dumpFile); } + [TestMethod] public async Task CrashDump_InvalidFormat_ShouldFail() { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent.Arguments); + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N")); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync($"--crashdump --crashdump-type invalid --results-directory {resultDirectory}"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("Option '--crashdump-type' has invalid arguments: 'invalid' is not a valid dump type. Valid options are 'Mini', 'Heap', 'Triage' and 'Full'"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string AssetName = "CrashDumpFixture"; @@ -115,22 +123,22 @@ public class Startup public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); builder.AddCrashDumpProvider(); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework +public class DummyTestFramework : ITestFramework { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs index 44bd63c540..2b2da26c79 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CrashPlusHangDumpTests.cs @@ -1,23 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public sealed class CrashPlusHangDumpTests : AcceptanceTestBase +[TestClass] +public sealed class CrashPlusHangDumpTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public CrashPlusHangDumpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - + [TestMethod] public async Task CrashPlusHangDump_InCaseOfCrash_CreateCrashDump() { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashPlusHangDump", TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashPlusHangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 5m --crashdump --results-directory {resultDirectory}", new Dictionary @@ -35,10 +34,17 @@ public async Task CrashPlusHangDump_InCaseOfCrash_CreateCrashDump() Assert.IsFalse(Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_hang.dmp", SearchOption.AllDirectories).Length > 0, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } + [TestMethod] public async Task CrashPlusHangDump_InCaseOfHang_CreateHangDump() { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "CrashPlusHangDump", TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "CrashPlusHangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 8s --crashdump --results-directory {resultDirectory}", new Dictionary @@ -56,10 +62,9 @@ public async Task CrashPlusHangDump_InCaseOfHang_CreateHangDump() Assert.IsTrue(Directory.GetFiles(resultDirectory, "CrashPlusHangDump*_hang.dmp", SearchOption.AllDirectories).Length > 0, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { - private const string AssetName = "TestAssetFixture"; + private const string AssetName = "AssetFixture"; public string TargetAssetPath => GetAssetPath(AssetName); @@ -109,7 +114,7 @@ public class Startup public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); builder.AddCrashDumpProvider(); builder.AddHangDumpProvider(); using ITestApplication app = await builder.BuildAsync(); @@ -117,15 +122,15 @@ public static async Task Main(string[] args) } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs index 4c3920dbfd..9239a51a4a 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/CustomBannerTests.cs @@ -1,34 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class CustomBannerTests : AcceptanceTestBase +[TestClass] +public class CustomBannerTests : AcceptanceTestBase { private const string AssetName = "CustomBannerTest"; - private readonly TestAssetFixture _testAssetFixture; - - public CustomBannerTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task UsingNoBanner_TheBannerDoesNotAppear(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--no-banner"); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -40,10 +35,11 @@ public async Task UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(stri testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -55,18 +51,18 @@ public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear( testHostResult.AssertOutputDoesNotContain(TestAssetFixture.CustomBannerPrefix); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task WithoutUsingNoBanner_TheBannerAppears(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputMatchesRegex($"{TestAssetFixture.CustomBannerPrefix} Platform info: Name: .NET Testing Platform, Version: .+?, Hash: .*?, Date: .+?"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string CustomBannerPrefix = "Custom banner |"; @@ -102,7 +98,7 @@ public static async Task Main(string[] args) ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); builder.RegisterTestFramework( sp => new TestFrameworkCapabilities(new DummyBannerMessageOwnerCapability(sp)), - (_,__) => new DummyTestAdapter()); + (_,__) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } @@ -128,20 +124,20 @@ public DummyBannerMessageOwnerCapability(IServiceProvider serviceProvider) sb.Append($", Hash: {platformInformation.CommitHash}"); sb.Append($", Date: {platformInformation.BuildDate}"); - return Task.FromResult(sb.ToString()); + return Task.FromResult(sb.ToString()); } } #pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -public class DummyTestAdapter : ITestFramework +public class DummyTestFramework : ITestFramework { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs index 4a2fb47dab..d0dae9f7c3 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DiagnosticTests.cs @@ -1,113 +1,112 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.RegularExpressions; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; using Microsoft.Testing.Platform.Configurations; -using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class DiagnosticTests : AcceptanceTestBase +[TestClass] +public class DiagnosticTests : AcceptanceTestBase { private const string AssetName = "DiagnosticTest"; - private readonly TestAssetFixture _testAssetFixture; - - public DiagnosticTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticIsSpecified_ReportIsGeneratedInDefaultLocation(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic"); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticAndOutputFilePrefixAreSpecified_ReportIsGeneratedInDefaultLocation(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"abcd_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic --diagnostic-output-fileprefix abcd"); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, "test1"); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, "test1"); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); Assert.IsTrue(Directory.CreateDirectory(diagPath).Exists); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--diagnostic --diagnostic-output-directory {diagPath}"); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticAndOutputFilePrefixAndOutputDirectoryAreSpecified_ReportIsGeneratedInSpecifiedLocation(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, "test2"); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, "test2"); string diagPathPattern = Path.Combine(diagPath, @"abcde_.*.diag").Replace(@"\", @"\\"); Assert.IsTrue(Directory.CreateDirectory(diagPath).Exists); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--diagnostic --diagnostic-output-fileprefix abcde --diagnostic-output-directory {diagPath}"); await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticOutputFilePrefixButNotDiagnosticIsSpecified_ReportGenerationFails(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-fileprefix cccc"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'--diagnostic-output-fileprefix' requires '--diagnostic' to be provided"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticOutputDirectoryButNotDiagnosticIsSpecified_ReportGenerationFails(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-directory cccc"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'--diagnostic-output-directory' requires '--diagnostic' to be provided"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_WhenDiagnosticFilePrefixAndDiagnosticOutputDirectoryButNotDiagnosticAreSpecified_ReportGenerationFails(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic-output-fileprefix aaaa --diagnostic-output-directory cccc"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.AssertOutputContains("'--diagnostic-output-directory' requires '--diagnostic' to be provided"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_EnableWithEnvironmentVariables_Succeeded(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -118,13 +117,14 @@ public async Task Diag_EnableWithEnvironmentVariables_Succeeded(string tfm) await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -133,16 +133,17 @@ public async Task Diag_EnableWithEnvironmentVariables_Verbosity_Succeeded(string { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC_VERBOSITY, "Trace" }, }); - await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern, "Trace"); + await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"MyPrefix_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -154,13 +155,14 @@ public async Task Diag_EnableWithEnvironmentVariables_CustomPrefix_Succeeded(str await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -172,10 +174,11 @@ public async Task Diag_EnableWithEnvironmentVariables_SynchronousWrite_Succeeded await AssertDiagnosticReportWasGeneratedAsync(testHostResult, diagPathPattern, flushType: "sync"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( "--diagnostic", @@ -183,17 +186,17 @@ public async Task Diag_EnableWithEnvironmentVariables_Disable_Succeeded(string t { { EnvironmentVariableConstants.TESTINGPLATFORM_DIAGNOSTIC, "0" }, }); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotContain("Diagnostic file"); testHostResult = await testHost.ExecuteAsync("--diagnostic"); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputContains("Diagnostic file"); } - private async Task AssertDiagnosticReportWasGeneratedAsync(TestHostResult testHostResult, string diagPathPattern, string level = "Trace", string flushType = "async") + private static async Task AssertDiagnosticReportWasGeneratedAsync(TestHostResult testHostResult, string diagPathPattern, string level = "Trace", string flushType = "async") { - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string outputPattern = $""" Diagnostic file \(level '{level}' with {flushType} flush\): {diagPathPattern} @@ -215,15 +218,14 @@ private async Task AssertDiagnosticReportWasGeneratedAsync(TestHostResul return match.Value; } - private async Task<(bool IsMatch, string Content)> CheckDiagnosticContentsMatchAsync(string path, string pattern) + private static async Task<(bool IsMatch, string Content)> CheckDiagnosticContentsMatchAsync(string path, string pattern) { using var reader = new StreamReader(path); string content = await reader.ReadToEndAsync(); return (Regex.IsMatch(content, pattern), content); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string TestCode = """ #file DiagnosticTest.csproj @@ -237,37 +239,51 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test preview - - - - #file Program.cs -using DiagnosticTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; -#file UnitTest1.cs -namespace DiagnosticTest; - -[TestGroup] -public class UnitTest1 +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,__) => new DummyTestFramework()); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } } -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; +public class DummyTestFramework : ITestFramework +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + return Task.CompletedTask; + } +} """; public string TargetAssetPath => GetAssetPath(AssetName); @@ -277,8 +293,7 @@ public void TestMethod1() 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/EnvironmentVariablesConfigurationProviderTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs index 5b8e45a901..1da20468e9 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/EnvironmentVariablesConfigurationProviderTests.cs @@ -1,31 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public sealed class EnvironmentVariablesConfigurationProviderTests : AcceptanceTestBase +[TestClass] +public sealed class EnvironmentVariablesConfigurationProviderTests : AcceptanceTestBase { private const string AssetName = "EnvironmentVariablesConfigurationProvider"; - private readonly TestAssetFixture _testAssetFixture; - - public EnvironmentVariablesConfigurationProviderTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task SetEnvironmentVariable_ShouldSucceed(string currentTfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.Success); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) - : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string Sources = """ #file EnvironmentVariablesConfigurationProvider.csproj @@ -63,7 +55,7 @@ public class Startup public static async Task Main(string[] args) { var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); - testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); testApplicationBuilder.TestHostControllers.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProviderTestClass()); using ITestApplication app = await testApplicationBuilder.BuildAsync(); return await app.RunAsync(); @@ -97,15 +89,15 @@ public Task ValidateTestHostEnvironmentVariablesAsync(IReadOnl } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs index bdc4d4b550..3ea7ab3952 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExecutionTests.cs @@ -1,123 +1,115 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class ExecutionTests : AcceptanceTestBase +[TestClass] +public class ExecutionTests : AcceptanceTestBase { private const string AssetName = "ExecutionTests"; private const string AssetName2 = "ExecutionTests2"; - private readonly TestAssetFixture _testAssetFixture; - - public ExecutionTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenListTestsIsSpecified_AllTestsAreFound(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests"); testHostResult.AssertExitCodeIs(ExitCodes.Success); const string OutputPattern = """ The following Tests are available: -TestMethod1 -TestMethod2 -TestMethod3 -FilteredOutTest$ +Test1 +Test2$ """; testHostResult.AssertOutputMatchesRegex(OutputPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenOnlyAssetNameIsSpecified_AllTestsAreRun(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.Success); - testHostResult.AssertOutputContainsSummary(failed: 0, passed: 4, skipped: 0); - testHostResult.AssertOutputContains($"! - {_testAssetFixture.TargetAssetPath}"); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 2, skipped: 0); + testHostResult.AssertOutputContains($"! - {AssetFixture.TargetAssetPath}"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenListTestsAndFilterAreSpecified_OnlyFilteredTestsAreFound(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --treenode-filter \"/ExecutionTests/ExecutionTests/UnitTest1/TestMethod*\""); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --treenode-filter \"\""); testHostResult.AssertExitCodeIs(ExitCodes.Success); const string OutputPattern = """ The following Tests are available: -TestMethod1 -TestMethod2 -TestMethod3$ +Test1$ """; testHostResult.AssertOutputMatchesRegex(OutputPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenFilterIsSpecified_OnlyFilteredTestsAreRun(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--treenode-filter \"/ExecutionTests/ExecutionTests/UnitTest1/TestMethod*\""); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--treenode-filter \"\""); testHostResult.AssertExitCodeIs(ExitCodes.Success); - testHostResult.AssertOutputContainsSummary(failed: 0, passed: 3, skipped: 0); - testHostResult.AssertOutputContains($"! - {_testAssetFixture.TargetAssetPath}"); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 1, skipped: 0); + testHostResult.AssertOutputContains($"! - {AssetFixture.TargetAssetPath}"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenMinimumExpectedTestsIsSpecifiedAndEnoughTestsRun_ResultIsOk(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 4"); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 2"); testHostResult.AssertExitCodeIs(ExitCodes.Success); - testHostResult.AssertOutputContainsSummary(failed: 0, passed: 4, skipped: 0); - testHostResult.AssertOutputContains($"! - {_testAssetFixture.TargetAssetPath}"); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 2, skipped: 0); + testHostResult.AssertOutputContains($"! - {AssetFixture.TargetAssetPath}"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenMinimumExpectedTestsIsSpecifiedAndNotEnoughTestsRun_ResultIsNotOk(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 5"); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--minimum-expected-tests 3"); testHostResult.AssertExitCodeIs(ExitCodes.MinimumExpectedTestsPolicyViolation); - testHostResult.AssertOutputContainsSummary(failed: 0, passed: 4, skipped: 0, minimumNumberOfTests: 5); - testHostResult.AssertOutputContains($" - {_testAssetFixture.TargetAssetPath}"); + testHostResult.AssertOutputContainsSummary(failed: 0, passed: 2, skipped: 0, minimumNumberOfTests: 3); + testHostResult.AssertOutputContains($" - {AssetFixture.TargetAssetPath}"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_WhenListTestsAndMinimumExpectedTestsAreSpecified_DiscoveryFails(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); - TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --minimum-expected-tests 4"); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); + TestHostResult testHostResult = await testHost.ExecuteAsync("--list-tests --minimum-expected-tests 2"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); - - const string OutputPattern = "Error: '--list-tests' and '--minimum-expected-tests' are incompatible options"; - Assert.That(testHostResult.StandardOutput.Contains(OutputPattern), $"Output of the test host is:\n{testHostResult}"); + testHostResult.AssertOutputContains("Error: '--list-tests' and '--minimum-expected-tests' are incompatible options"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Exec_Honor_Request_Complete(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath2, AssetName2, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath2, AssetName2, tfm); var stopwatch = Stopwatch.StartNew(); TestHostResult testHostResult = await testHost.ExecuteAsync(); stopwatch.Stop(); @@ -125,8 +117,7 @@ public async Task Exec_Honor_Request_Complete(string tfm) Assert.IsTrue(stopwatch.Elapsed.TotalSeconds > 3); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string TestCode = """ #file ExecutionTests.csproj @@ -140,52 +131,92 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test preview - - - - #file Program.cs -using ExecutionTests; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace ExecutionTests; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.Services; -[TestGroup] -public class UnitTest1 +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + MyExtension myExtension = new(); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,sp) => new DummyTestFramework(sp, myExtension)); + builder.AddTreeNodeFilterService(myExtension); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } +} - public void TestMethod2() - { - Assert.IsTrue(true); - } +public class MyExtension : IExtension +{ + public string Uid => "MyExtension"; + public string Version => "1.0.0"; + public string DisplayName => "My Extension"; + public string Description => "My Extension Description"; + public Task IsEnabledAsync() => Task.FromResult(true); +} - public void TestMethod3() +public class DummyTestFramework : ITestFramework, IDataProducer +{ + private IServiceProvider _sp; + private MyExtension _myExtension; + + public DummyTestFramework(IServiceProvider sp, MyExtension myExtension) { - Assert.IsTrue(true); + _sp = sp; + _myExtension = myExtension; } - public void FilteredOutTest() + public string Uid => _myExtension.Uid; + + public string Version => _myExtension.Version; + + public string DisplayName => _myExtension.DisplayName; + + public string Description => _myExtension.Description; + + public Type[] DataTypesProduced => [typeof(TestNodeUpdateMessage)]; + + public Task IsEnabledAsync() => _myExtension.IsEnabledAsync(); + + 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) { - Assert.IsTrue(true); + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "0", DisplayName = "Test1", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "0", DisplayName = "Test1", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + + if (!_sp.GetCommandLineOptions().TryGetOptionArgumentList("--treenode-filter", out _)) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "Test2", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "Test2", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + } + + context.Complete(); } } - -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; """; private const string TestCode2 = """ @@ -215,13 +246,13 @@ public void FilteredOutTest() using System.Threading.Tasks; ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyAdapter()); +builder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); -internal class DummyAdapter : ITestFramework, IDataProducer +internal class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => string.Empty; @@ -270,14 +301,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/ExitOnProcessExitTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs index 98b741b9e9..b1cdf270aa 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ExitOnProcessExitTests.cs @@ -1,23 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class ExitOnProcessExitTests : AcceptanceTestBase +[TestClass] +public class ExitOnProcessExitTests : AcceptanceTestBase { private const string AssetName = "ExecutionTests"; - private readonly TestAssetFixture _testAssetFixture; - - public ExitOnProcessExitTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public void ExitOnProcessExit_Succeed(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); // Create the mutex name used to wait for the PID file created by the test host. string waitPid = Guid.NewGuid().ToString("N"); @@ -60,8 +55,7 @@ public void ExitOnProcessExit_Succeed(string tfm) } } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string TestCode = """ #file ExecutionTests.csproj @@ -115,21 +109,21 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test else { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs index 2309c7082e..c373ae6ccf 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpOutputTests.cs @@ -1,29 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public sealed class HangDumpOutputTests : AcceptanceTestBase +[TestClass] +public sealed class HangDumpOutputTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public HangDumpOutputTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [Arguments("Mini")] + [DataRow("Mini")] + [TestMethod] public async Task HangDump_Outputs_HangingTests_EvenWhenHangingTestsHaveTheSameDisplayName(string format) { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + // This test makes sure that when tests have the same display name (e.g. like Test1 from both Class1 and Class2) // they will still show up in the hanging tests. This was not the case before when we were just putting them into // a dictionary based on DisplayName. In that case both tests were started at the same time, and only 1 entry was added // to currently executing tests. When first test with name Test1 completed we removed that entry, but Class2.Test1 was still // running. Solution is to use a more unique identifier. - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 8s --hangdump-type {format} --results-directory {resultDirectory} --no-progress", new Dictionary @@ -35,10 +34,9 @@ public async Task HangDump_Outputs_HangingTests_EvenWhenHangingTestsHaveTheSameD testHostResult.AssertOutputContains("Test1"); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { - private const string AssetName = "TestAssetFixture"; + private const string AssetName = "AssetFixture"; public string TargetAssetPath => GetAssetPath(AssetName); @@ -88,22 +86,22 @@ public class Startup public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); builder.AddHangDumpProvider(); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs index 4fe5ac589e..d00609c269 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HangDumpTests.cs @@ -1,24 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public sealed class HangDumpTests : AcceptanceTestBase +[TestClass] +public sealed class HangDumpTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public HangDumpTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task HangDump_DefaultSetting_CreateDump(string tfm) { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), tfm); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", tfm); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDump", tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 8s --results-directory {resultDirectory}", new Dictionary @@ -31,10 +30,17 @@ public async Task HangDump_DefaultSetting_CreateDump(string tfm) Assert.IsTrue(dumpFile is not null, $"Dump file not found '{tfm}'\n{testHostResult}'"); } + [TestMethod] public async Task HangDump_CustomFileName_CreateDump() { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 8s --hangdump-filename myhungdumpfile_%p.dmp --results-directory {resultDirectory}", new Dictionary @@ -47,12 +53,19 @@ public async Task HangDump_CustomFileName_CreateDump() Assert.IsTrue(dumpFile is not null, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } + [TestMethod] public async Task HangDump_PathWithSpaces_CreateDump() { - string resultDir = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDir = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent); string resultDirectory = Path.Combine(resultDir, "directory with spaces"); Directory.CreateDirectory(resultDirectory); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"""--hangdump --hangdump-timeout 8s --hangdump-filename myhungdumpfile_%p.dmp --results-directory "{resultDirectory}" """, new Dictionary @@ -65,14 +78,21 @@ public async Task HangDump_PathWithSpaces_CreateDump() Assert.IsTrue(dumpFile is not null, $"Dump file not found '{TargetFrameworks.NetCurrent}'\n{testHostResult}'"); } - [Arguments("Mini")] - [Arguments("Heap")] - [Arguments("Triage")] - [Arguments("Full")] + [DataRow("Mini")] + [DataRow("Heap")] + [DataRow("Triage")] + [DataRow("Full")] + [TestMethod] public async Task HangDump_Formats_CreateDump(string format) { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), format); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 8s --hangdump-type {format} --results-directory {resultDirectory}", new Dictionary @@ -85,10 +105,11 @@ public async Task HangDump_Formats_CreateDump(string format) Assert.IsTrue(dumpFile is not null, $"Dump file not found '{format}'\n{testHostResult}'"); } + [TestMethod] public async Task HangDump_InvalidFormat_ShouldFail() { - string resultDirectory = Path.Combine(_testAssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent.Arguments); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent.Arguments); + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), TargetFrameworks.NetCurrent); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "HangDump", TargetFrameworks.NetCurrent); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--hangdump --hangdump-timeout 8s --hangdump-type invalid --results-directory {resultDirectory}", new Dictionary @@ -103,10 +124,9 @@ public async Task HangDump_InvalidFormat_ShouldFail() """); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { - private const string AssetName = "TestAssetFixture"; + private const string AssetName = "AssetFixture"; public string TargetAssetPath => GetAssetPath(AssetName); @@ -156,22 +176,22 @@ public class Startup public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); builder.AddHangDumpProvider(); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs index b44f617897..52285ac9dc 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoTests.cs @@ -1,23 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class HelpInfoTests : AcceptanceTestBase +[TestClass] +public class HelpInfoTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public HelpInfoTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Help_WhenNoExtensionRegistered_OutputDefaultHelpContent(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -27,6 +20,8 @@ .NET Testing Platform v* Usage {TestAssetFixture.NoExtensionAssetName}* [option providers] [extension option providers] Execute a .NET Test Application. Options: + --config-file + Specifies a testconfig.json file. --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag @@ -70,17 +65,16 @@ Disable reporting progress to screen. --output Output verbosity when reporting tests. Valid values are 'Normal', 'Detailed'. Default is 'Normal'. - --treenode-filter - Use a tree filter to filter down the tests to execute """; testHostResult.AssertOutputMatchesLines(wildcardMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task HelpShortName_WhenNoExtensionRegistered_OutputDefaultHelpContent(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--?"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -95,12 +89,13 @@ Execute a .NET Test Application. testHostResult.AssertOutputMatchesLines(wildcardMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Help_WhenNoExtensionRegisteredAndUnknownOptionIsSpecified_OutputDefaultHelpContentAndUnknownOption(string tfm) { const string UnknownOption = "aaa"; - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"-{UnknownOption}"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); @@ -116,10 +111,11 @@ Execute a .NET Test Application. testHostResult.AssertOutputMatchesLines(wildcardMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Info_WhenNoExtensionRegistered_OutputDefaultInfoContent(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.NoExtensionAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -130,7 +126,7 @@ .NET Testing Platform v.+ \[.+\] Version: .+ Dynamic Code Supported: True Runtime information: .+ - {(tfm != TargetFrameworks.NetFramework[0].Arguments ? "###SKIP###" : $"Runtime location: .+")} + {(tfm != TargetFrameworks.NetFramework[0] ? "###SKIP###" : "Runtime location: .+")} Test module: .+{TestAssetFixture.NoExtensionAssetName}.* Built-in command line providers: PlatformCommandLineProvider @@ -150,6 +146,10 @@ .NET Testing Platform v.+ \[.+\] Arity: 1 Hidden: True Description: Specify the port of the client\. + --config-file + Arity: 1 + Hidden: False + Description: Specifies a testconfig\.json file\. --diagnostic Arity: 0 Hidden: False @@ -254,15 +254,6 @@ Takes one argument as string in the format \[h\|m\|s\] where 'value' is f Hidden: False Description: Output verbosity when reporting tests. Valid values are 'Normal', 'Detailed'. Default is 'Normal'. - TestingFrameworkExtension - Name: Microsoft Testing Framework - Version: .+ - Description: Microsoft Testing Framework\. This framework allows you to test your code anywhere in any mode \(all OSes, all platforms, all configurations\.\.\.\)\. - Options: - --treenode-filter - Arity: 1 - Hidden: False - Description: Use a tree filter to filter down the tests to execute Registered tools: There are no registered tools\. """; @@ -270,10 +261,11 @@ There are no registered tools\. testHostResult.AssertOutputMatchesRegexLines(regexMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Help_WithAllExtensionsRegistered_OutputFullHelpContent(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -283,6 +275,8 @@ .NET Testing Platform v* Usage {TestAssetFixture.AllExtensionsAssetName}* [option providers] [extension option providers] Execute a .NET Test Application. Options: + --config-file + Specifies a testconfig.json file. --diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[yyMMddHHmmssfff].diag @@ -359,17 +353,16 @@ Output verbosity when reporting tests. Enable generating TRX report --report-trx-filename The name of the generated TRX report - --treenode-filter - Use a tree filter to filter down the tests to execute """; testHostResult.AssertOutputMatchesLines(wildcardPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task HelpShortName_WithAllExtensionsRegistered_OutputFullHelpContent(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("-?"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -384,10 +377,11 @@ Execute a .NET Test Application. testHostResult.AssertOutputMatchesLines(wildcardPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Info_WithAllExtensionsRegistered_OutputFullInfoContent(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.AllExtensionsTargetAssetPath, TestAssetFixture.AllExtensionsAssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--info"); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -398,7 +392,7 @@ .NET Testing Platform v* [*] Version: * Dynamic Code Supported: True Runtime information: * - {(tfm != TargetFrameworks.NetFramework[0].Arguments ? "###SKIP###" : $"Runtime location: *")} + {(tfm != TargetFrameworks.NetFramework[0] ? "###SKIP###" : "Runtime location: *")} Test module: *{TestAssetFixture.AllExtensionsAssetName}* Built-in command line providers: PlatformCommandLineProvider @@ -418,6 +412,10 @@ .NET Testing Platform v* [*] Arity: 1 Hidden: True Description: Specify the port of the client. + --config-file + Arity: 1 + Hidden: False + Description: Specifies a testconfig.json file. --diagnostic Arity: 0 Hidden: False @@ -551,6 +549,15 @@ Default is 30m. Description: Specify the type of the dump. Valid values are 'Mini', 'Heap', 'Triage' (only available in .NET 6+) or 'Full'. Default type is 'Full' + MSBuildCommandLineProvider + Name: MSBuildCommandLineProvider + Version: * + Description: Extension used to pass parameters from MSBuild node and the hosts + Options: + --internal-msbuild-node + Arity: 1 + Hidden: True + Description: Used to pass the MSBuild node handle RetryCommandLineOptionsProvider Name: Retry failed tests Version: * @@ -590,15 +597,6 @@ Default type is 'Full' Hidden: False Description: Output verbosity when reporting tests. Valid values are 'Normal', 'Detailed'. Default is 'Normal'. - TestingFrameworkExtension - Name: Microsoft Testing Framework - Version: * - Description: Microsoft Testing Framework. This framework allows you to test your code anywhere in any mode (all OSes, all platforms, all configurations...). - Options: - --treenode-filter - Arity: 1 - Hidden: False - Description: Use a tree filter to filter down the tests to execute TrxReportGeneratorCommandLine Name: TRX report generator Version: * @@ -637,8 +635,7 @@ Default type is 'Full' testHostResult.AssertOutputMatchesLines(wildcardPattern); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string AllExtensionsAssetName = "AllExtensionsInfoTest"; public const string NoExtensionAssetName = "NoExtensionInfoTest"; @@ -652,49 +649,60 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test enable Exe preview + false - - - - - + - - + + #file Program.cs -using AllExtensionsInfoTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -builder.AddCrashDumpProvider(); -builder.AddHangDumpProvider(); -builder.AddHotReloadProvider(); -builder.AddRetryProvider(); -builder.AddTrxReportProvider(); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace AllExtensionsInfoTest; - -[TestGroup] -public class UnitTest1 +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; + +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,__) => new DummyTestFramework()); + builder.AddSelfRegisteredExtensions(args); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } } -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; +public class DummyTestFramework : ITestFramework +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + return Task.CompletedTask; + } +} """; private const string NoExtensionTestCode = """ @@ -709,37 +717,51 @@ public void TestMethod1() preview - - - - #file Program.cs -using NoExtensionInfoTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; -#file UnitTest1.cs -namespace NoExtensionInfoTest; - -[TestGroup] -public class UnitTest1 +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,__) => new DummyTestFramework()); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } } -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; +public class DummyTestFramework : ITestFramework +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + return Task.CompletedTask; + } +} """; public string NoExtensionTargetAssetPath => GetAssetPath(NoExtensionAssetName); @@ -751,13 +773,11 @@ public void TestMethod1() 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/AcceptanceAssert.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs index 7cb676a209..4831e0ae1e 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceAssert.cs @@ -1,18 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; internal static class AcceptanceAssert { public static void AssertExitCodeIs(this TestHostResult testHostResult, int exitCode, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(exitCode == testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + => Assert.AreEqual(exitCode, testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertExitCodeIsNot(this TestHostResult testHostResult, int exitCode, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(exitCode != testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + => Assert.AreNotEqual(exitCode, testHostResult.ExitCode, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); /// /// Ensure that the output matches the given pattern. The pattern can use `*` to mean any character, it is internally replaced by `.*` and matched as regex. @@ -39,22 +36,15 @@ public static void AssertOutputMatchesLines(this TestHostResult testHostResult, + Regex.Escape(wildcardLines[i]).Replace("\\*", ".*") + "$"; - Assert.That( + Assert.IsTrue( Regex.IsMatch(outputLine, matchingPatternLine, RegexOptions.Singleline), - $"Output on line {j + 1}{Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match pattern{Environment.NewLine}{matchingPatternLine}{Environment.NewLine}Standard output:{Environment.NewLine}{testHostResult.StandardOutput}", - callerMemberName: callerMemberName, - callerFilePath: callerFilePath, - callerLineNumber: callerLineNumber); + $"Output on line {j + 1}{Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match pattern{Environment.NewLine}{matchingPatternLine}{Environment.NewLine}Standard output:{Environment.NewLine}{testHostResult.StandardOutput}{Environment.NewLine}for member '{callerMemberName}' at line {callerLineNumber} of file '{callerFilePath}'."); } else { string expectedLine = wildcardLines[i]; - Assert.That( - string.Equals(outputLine, expectedLine, StringComparison.Ordinal), - $"Output on line {j + 1} (length: {outputLine.Length}){Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match line (length: {expectedLine.Length}){Environment.NewLine}{expectedLine}{Environment.NewLine}Standard output:{Environment.NewLine}{testHostResult.StandardOutput}", - callerMemberName: callerMemberName, - callerFilePath: callerFilePath, - callerLineNumber: callerLineNumber); + Assert.AreEqual(expectedLine, outputLine, StringComparer.Ordinal, + $"Output on line {j + 1} (length: {outputLine.Length}){Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match line (length: {expectedLine.Length}){Environment.NewLine}{expectedLine}{Environment.NewLine}Standard output:{Environment.NewLine}{testHostResult.StandardOutput}{Environment.NewLine}for member '{callerMemberName}' at line {callerLineNumber} of file '{callerFilePath}'."); } j++; @@ -78,39 +68,39 @@ public static void AssertOutputMatchesRegexLines(this TestHostResult testHostRes } string outputLine = testHostResult.StandardOutputLines[j]; - Assert.That( + Assert.IsTrue( Regex.IsMatch(outputLine, patternLines[i], RegexOptions.Singleline), - $"Output on line {j + 1}{Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match pattern{Environment.NewLine}{patternLines[i]}{Environment.NewLine}Standard output:{Environment.NewLine}{testHostResult.StandardOutput}", - callerMemberName: callerMemberName, - callerFilePath: callerFilePath, - callerLineNumber: callerLineNumber); + $"Output on line {j + 1}{Environment.NewLine}{outputLine}{Environment.NewLine}doesn't match pattern{Environment.NewLine}{patternLines[i]}{Environment.NewLine}Standard output:{Environment.NewLine}{testHostResult.StandardOutput}{Environment.NewLine}for member '{callerMemberName}' at line {callerLineNumber} of file '{callerFilePath}'."); j++; } } - public static void AssertOutputMatchesRegex(this TestHostResult testHostResult, string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(Regex.IsMatch(testHostResult.StandardOutput, pattern), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + public static void AssertOutputMatchesRegex(this TestHostResult testHostResult, [StringSyntax("Regex")] string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.IsTrue(Regex.IsMatch(testHostResult.StandardOutput, pattern), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); + + public static void AssertOutputMatchesRegex(this TestHostResult testHostResult, [StringSyntax("Regex")] string pattern, RegexOptions regexOptions, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.IsTrue(Regex.IsMatch(testHostResult.StandardOutput, pattern, regexOptions), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); - public static void AssertOutputDoesNotMatchRegex(this TestHostResult testHostResult, string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(!Regex.IsMatch(testHostResult.StandardOutput, pattern), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + public static void AssertOutputDoesNotMatchRegex(this TestHostResult testHostResult, [StringSyntax("Regex")] string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.IsFalse(Regex.IsMatch(testHostResult.StandardOutput, pattern), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertOutputContains(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(testHostResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + => StringAssert.Contains(testHostResult.StandardOutput, value, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber), StringComparison.Ordinal); public static void AssertOutputContains(this DotnetMuxerResult dotnetMuxerResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(dotnetMuxerResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(dotnetMuxerResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + => StringAssert.Contains(dotnetMuxerResult.StandardOutput, value, GenerateFailedAssertionMessage(dotnetMuxerResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber), StringComparison.Ordinal); - public static void AssertOutputNotContains(this DotnetMuxerResult dotnetMuxerResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(!dotnetMuxerResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(dotnetMuxerResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + public static void AssertOutputDoesNotContain(this DotnetMuxerResult dotnetMuxerResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.IsFalse(dotnetMuxerResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(dotnetMuxerResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); - public static void AssertOutputRegEx(this DotnetMuxerResult dotnetMuxerResult, string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(Regex.IsMatch(dotnetMuxerResult.StandardOutput, pattern), GenerateFailedAssertionMessage(dotnetMuxerResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + public static void AssertOutputMatchesRegex(this DotnetMuxerResult dotnetMuxerResult, [StringSyntax("Regex")] string pattern, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) + => Assert.IsTrue(Regex.IsMatch(dotnetMuxerResult.StandardOutput, pattern), GenerateFailedAssertionMessage(dotnetMuxerResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertOutputDoesNotContain(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(!testHostResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + => Assert.IsFalse(testHostResult.StandardOutput.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber)); public static void AssertStandardErrorContains(this TestHostResult testHostResult, string value, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) - => Assert.That(testHostResult.StandardError.Contains(value, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + => StringAssert.Contains(testHostResult.StandardError, value, GenerateFailedAssertionMessage(testHostResult, callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber), StringComparison.Ordinal); public static void AssertOutputContainsSummary(this TestHostResult testHostResult, int failed, int passed, int skipped, bool? aborted = false, int? minimumNumberOfTests = null, [CallerMemberName] string? callerMemberName = null, [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) { @@ -132,13 +122,13 @@ public static void AssertOutputContainsSummary(this TestHostResult testHostResul succeeded: {passed} skipped: {skipped} """; - Assert.That(testHostResult.StandardOutput.Contains(summaryResult, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); - Assert.That(testHostResult.StandardOutput.Contains(summaryCounts, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult), callerMemberName: callerMemberName, callerFilePath: callerFilePath, callerLineNumber: callerLineNumber); + Assert.IsTrue(testHostResult.StandardOutput.Contains(summaryResult, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult, callerMemberName, callerFilePath, callerLineNumber)); + Assert.IsTrue(testHostResult.StandardOutput.Contains(summaryCounts, StringComparison.Ordinal), GenerateFailedAssertionMessage(testHostResult, callerMemberName, callerFilePath, callerLineNumber)); } - private static string GenerateFailedAssertionMessage(TestHostResult testHostResult) - => $"Output of the test host is:\n{testHostResult}"; + private static string GenerateFailedAssertionMessage(TestHostResult testHostResult, string? callerMemberName, string? callerFilePath, int callerLineNumber, [CallerMemberName] string? assertCallerMemberName = null) + => $"Expression '{assertCallerMemberName}' failed for member '{callerMemberName}' at line {callerLineNumber} of file '{callerFilePath}'. Output of the test host is:{Environment.NewLine}{testHostResult}"; - private static string GenerateFailedAssertionMessage(DotnetMuxerResult dotnetMuxerResult) - => $"Output of the dotnet muxer is:\n{dotnetMuxerResult}"; + private static string GenerateFailedAssertionMessage(DotnetMuxerResult dotnetMuxerResult, string? callerMemberName, string? callerFilePath, int callerLineNumber, [CallerMemberName] string? assertCallerMemberName = null) + => $"Expression '{assertCallerMemberName}' failed for member '{callerMemberName}' at line {callerLineNumber} of file '{callerFilePath}'. Output of the dotnet muxer is:{Environment.NewLine}{dotnetMuxerResult}"; } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceFixture.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceFixture.cs index 007fc22af7..1783ebe6a1 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceFixture.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceFixture.cs @@ -3,11 +3,16 @@ namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestFixture(TestFixtureSharingStrategy.PerTestApplication)] -public sealed class AcceptanceFixture : IDisposable +[TestClass] +public static class AcceptanceFixture { - public TempDirectory NuGetGlobalPackagesFolder { get; } = new(".packages"); + public static TempDirectory NuGetGlobalPackagesFolder { get; private set; } = null!; - public void Dispose() + [AssemblyInitialize] + public static void AssemblyInitialize(TestContext context) + => NuGetGlobalPackagesFolder = new(".packages"); + + [AssemblyCleanup] + public static void AssemblyCleanup() => NuGetGlobalPackagesFolder.Dispose(); } 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 ddaf3bc633..ea57006ec2 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Helpers/AcceptanceTestBase.cs @@ -1,18 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Runtime.InteropServices; -using System.Xml.Linq; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -/// -/// All the properties of this class should be non static. -/// At the moment are static because we need to share them between perclass/id fixtures and -/// it's not supported at the moment. -/// -public abstract class AcceptanceTestBase : TestBase +public abstract class AcceptanceTestBase + where TFixture : ITestAssetFixture, new() { private const string NuGetPackageExtensionName = ".nupkg"; @@ -57,26 +49,22 @@ static AcceptanceTestBase() var cpmPropFileDoc = XDocument.Load(Path.Combine(RootFinder.Find(), "Directory.Packages.props")); MicrosoftNETTestSdkVersion = cpmPropFileDoc.Descendants("MicrosoftNETTestSdkVersion").Single().Value; - var versionsPropFileDoc = XDocument.Load(Path.Combine(RootFinder.Find(), "eng", "Versions.props")); -#if MSTEST_DOWNLOADED - MSTestVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MSTestVersion"); - MicrosoftTestingPlatformVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MSTestVersion"); - MicrosoftTestingEnterpriseExtensionsVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "Microsoft.Testing.Extensions."); - MSTestEngineVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "MSTest.Engine"); -#else + var versionsPropsFileDoc = XDocument.Load(Path.Combine(RootFinder.Find(), "eng", "Versions.props")); MSTestVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "MSTest.TestFramework."); MicrosoftTestingPlatformVersion = ExtractVersionFromPackage(Constants.ArtifactsPackagesShipping, "Microsoft.Testing.Platform."); - MicrosoftTestingEnterpriseExtensionsVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MicrosoftTestingInternalFrameworkVersion"); - MSTestEngineVersion = ExtractVersionFromVersionPropsFile(versionsPropFileDoc, "MSTestEngineVersion"); -#endif + MSTestEngineVersion = ExtractVersionFromXmlFile(versionsPropsFileDoc, "MSTestEngineVersion"); } - protected AcceptanceTestBase(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } + protected static TFixture AssetFixture { get; private set; } = default!; - internal static string RID { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64" : "linux-x64"; + internal static string RID { get; } + = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "win-x64" + : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + ? "linux-x64" + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? "osx-x64" + : throw new NotSupportedException("Current OS is not supported"); public static string MSTestVersion { get; private set; } @@ -86,61 +74,70 @@ protected AcceptanceTestBase(ITestExecutionContext testExecutionContext) 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) + { + AssetFixture = new(); + await AssetFixture.InitializeAsync(); + } + + [ClassCleanup(InheritanceBehavior.BeforeEachDerivedClass)] + [SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Fine in this context")] + public static void ClassCleanup() + => AssetFixture.Dispose(); - internal static IEnumerable> GetBuildMatrixTfmBuildVerbConfiguration() + internal static IEnumerable<(string Tfm, BuildConfiguration BuildConfiguration, Verb Verb)> GetBuildMatrixTfmBuildVerbConfiguration() { - foreach (TestArgumentsEntry tfm in TargetFrameworks.All) + foreach (string tfm in TargetFrameworks.All) { foreach (BuildConfiguration compilationMode in Enum.GetValues()) { foreach (Verb verb in Enum.GetValues()) { - yield return new TestArgumentsEntry<(string Tfm, BuildConfiguration BuildConfiguration, Verb Verb)>((tfm.Arguments, compilationMode, verb), $"{tfm.Arguments},{compilationMode},{verb}"); + yield return new(tfm, compilationMode, verb); } } } } - internal static IEnumerable> GetBuildMatrixTfmBuildConfiguration() + internal static IEnumerable<(string Tfm, BuildConfiguration BuildConfiguration)> GetBuildMatrixTfmBuildConfiguration() { - foreach (TestArgumentsEntry tfm in TargetFrameworks.All) + foreach (string tfm in TargetFrameworks.All) { foreach (BuildConfiguration compilationMode in Enum.GetValues()) { - yield return new TestArgumentsEntry<(string Tfm, BuildConfiguration BuildConfiguration)>((tfm.Arguments, compilationMode), $"{tfm.Arguments},{compilationMode}"); + yield return new(tfm, compilationMode); } } } - internal static IEnumerable> GetBuildMatrixMultiTfmFoldedBuildConfiguration() + internal static IEnumerable<(string MultiTfm, BuildConfiguration BuildConfiguration)> GetBuildMatrixMultiTfmFoldedBuildConfiguration() { foreach (BuildConfiguration compilationMode in Enum.GetValues()) { - yield return new TestArgumentsEntry<(string MultiTfm, BuildConfiguration BuildConfiguration)>((TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode), $"multitfm,{compilationMode}"); + yield return new(TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode); } } - internal static IEnumerable> GetBuildMatrixMultiTfmBuildConfiguration() + internal static IEnumerable<(string MultiTfm, BuildConfiguration BuildConfiguration)> GetBuildMatrixMultiTfmBuildConfiguration() { foreach (BuildConfiguration compilationMode in Enum.GetValues()) { - yield return new TestArgumentsEntry<(string MultiTfm, BuildConfiguration BuildConfiguration)>((TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode), $"{TargetFrameworks.All.ToMSBuildTargetFrameworks()},{compilationMode}"); + yield return new(TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode); } } - internal static IEnumerable> GetBuildMatrixSingleAndMultiTfmBuildConfiguration() + internal static IEnumerable<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)> GetBuildMatrixSingleAndMultiTfmBuildConfiguration() { - foreach (TestArgumentsEntry<(string Tfm, BuildConfiguration BuildConfiguration)> entry in GetBuildMatrixTfmBuildConfiguration()) + foreach ((string Tfm, BuildConfiguration BuildConfiguration) entry in GetBuildMatrixTfmBuildConfiguration()) { - yield return new TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)>( - (entry.Arguments.Tfm, entry.Arguments.BuildConfiguration, false), $"{entry.Arguments.Tfm},{entry.Arguments.BuildConfiguration}"); + yield return new(entry.Tfm, entry.BuildConfiguration, false); } - foreach (TestArgumentsEntry<(string MultiTfm, BuildConfiguration BuildConfiguration)> entry in GetBuildMatrixMultiTfmBuildConfiguration()) + foreach ((string MultiTfm, BuildConfiguration BuildConfiguration) entry in GetBuildMatrixMultiTfmBuildConfiguration()) { - yield return new TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)>( - (entry.Arguments.MultiTfm, entry.Arguments.BuildConfiguration, true), $"multitfm,{entry.Arguments.BuildConfiguration}"); + yield return new(entry.MultiTfm, entry.BuildConfiguration, true); } } @@ -156,13 +153,13 @@ private static string ExtractVersionFromPackage(string rootFolder, string packag // So we need to find a package that contains a number after the prefix. // Ideally, we would want to do a full validation to check this is a nuget version number, but that's too much work for now. matches = matches - // (full path, file name without prefix) - .Select(path => (path, fileName: Path.GetFileName(path)[packagePrefixName.Length..])) - // check if first character of file name without prefix is number - .Where(tuple => int.TryParse(tuple.fileName[0].ToString(), CultureInfo.InvariantCulture, out _)) - // take the full path - .Select(tuple => tuple.path) - .ToArray(); + // (full path, file name without prefix) + .Select(path => (path, fileName: Path.GetFileName(path)[packagePrefixName.Length..])) + // check if first character of file name without prefix is number + .Where(tuple => int.TryParse(tuple.fileName[0].ToString(), CultureInfo.InvariantCulture, out _)) + // take the full path + .Select(tuple => tuple.path) + .ToArray(); } if (matches.Length != 1) @@ -174,7 +171,7 @@ private static string ExtractVersionFromPackage(string rootFolder, string packag return packageFullName.Substring(packagePrefixName.Length, packageFullName.Length - packagePrefixName.Length - NuGetPackageExtensionName.Length); } - private static string ExtractVersionFromVersionPropsFile(XDocument versionPropsXmlDocument, string entryName) + private static string ExtractVersionFromXmlFile(XDocument versionPropsXmlDocument, string entryName) { XElement[] matches = versionPropsXmlDocument.Descendants(entryName).ToArray(); return matches.Length != 1 diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs index b1b27a3e97..50bf5178bf 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/IgnoreExitCodeTests.cs @@ -1,13 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class IgnoreExitCodeTests : AcceptanceTestBase +[TestClass] +public class IgnoreExitCodeTests : AcceptanceTestBase { private const string AssetName = "TestProject"; @@ -43,21 +40,21 @@ public class Startup public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); @@ -83,21 +80,17 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context) } """; - private readonly AcceptanceFixture _acceptanceFixture; - - public IgnoreExitCodeTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - public static IEnumerable> GetBuildMatrix() + public static IEnumerable<(string Tfm, BuildConfiguration BuildConfiguration, string CommandLine, string EnvironmentVariable)> GetBuildMatrix() { - foreach (TestArgumentsEntry<(string Tfm, BuildConfiguration BuildConfiguration)> buildConfig in GetBuildMatrixTfmBuildConfiguration()) + foreach ((string Tfm, BuildConfiguration BuildConfiguration) buildConfig in GetBuildMatrixTfmBuildConfiguration()) { - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string)>((buildConfig.Arguments.Tfm, buildConfig.Arguments.BuildConfiguration, "--ignore-exit-code 2", string.Empty), $"{buildConfig.Arguments.Tfm},{buildConfig.Arguments.BuildConfiguration},CommandLine"); - yield return new TestArgumentsEntry<(string, BuildConfiguration, string, string)>((buildConfig.Arguments.Tfm, buildConfig.Arguments.BuildConfiguration, string.Empty, "2"), $"{buildConfig.Arguments.Tfm},{buildConfig.Arguments.BuildConfiguration},EnvironmentVariable"); + yield return new(buildConfig.Tfm, buildConfig.BuildConfiguration, "--ignore-exit-code 2", string.Empty); + yield return new(buildConfig.Tfm, buildConfig.BuildConfiguration, string.Empty, "2"); } } - [ArgumentsProvider(nameof(GetBuildMatrix))] + [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method)] + [TestMethod] public async Task If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(string tfm, BuildConfiguration buildConfiguration, string commandLine, string environmentVariable) { using TestAsset generator = await TestAsset.GenerateAssetAsync( @@ -107,7 +100,7 @@ public async Task If_IgnoreExitCode_Specified_Should_Return_Success_ExitCode(str .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string assetPath = generator.TargetAssetPath; - string globalPackagesPath = _acceptanceFixture.NuGetGlobalPackagesFolder.Path; + string globalPackagesPath = AcceptanceFixture.NuGetGlobalPackagesFolder.Path; await DotnetCli.RunAsync($"restore -m:1 -nodeReuse:false {assetPath} -r {RID}", globalPackagesPath); await DotnetCli.RunAsync($"build -m:1 -nodeReuse:false {assetPath} -c {buildConfiguration} -r {RID}", globalPackagesPath); var host = TestInfrastructure.TestHost.LocateFrom(assetPath, AssetName, tfm, buildConfiguration: buildConfiguration); 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 8ed4ecc05e..6c80392710 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuild.KnownExtensionRegistration.cs @@ -1,33 +1,27 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; - using SL = Microsoft.Build.Logging.StructuredLogger; namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class MSBuildTests_KnownExtensionRegistration : AcceptanceTestBase +[TestClass] +public class MSBuildTests_KnownExtensionRegistration : AcceptanceTestBase { - private readonly AcceptanceFixture _acceptanceFixture; private const string AssetName = "MSBuildTests"; - public MSBuildTests_KnownExtensionRegistration(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Registered(string tfm, BuildConfiguration compilationMode, Verb verb) { TestAsset testAsset = await TestAsset.GenerateAssetAsync( 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); + 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); var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, AssetName, tfm, rid: RID, verb: verb, buildConfiguration: compilationMode); TestHostResult testHostResult = await testHost.ExecuteAsync("--help"); @@ -55,8 +49,8 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis - DummyAdapter - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + DummyTestFramework + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration @@ -74,10 +68,8 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis - - - - + + @@ -93,17 +85,17 @@ public async Task Microsoft_Testing_Platform_Extensions_ShouldBe_Correctly_Regis namespace MyNamespaceRoot.Level1.Level2; -public static class DummyAdapterRegistration +public static class DummyTestFrameworkRegistration { public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] args) { - testApplicationBuilder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyAdapter()); + testApplicationBuilder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyTestFramework()); } } -internal sealed class DummyAdapter : ITestFramework, IDataProducer +internal sealed class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => string.Empty; diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs index 6db6765313..976b275f43 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.ConfigurationFile.cs @@ -1,17 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class MSBuildTests : AcceptanceTestBase +[TestClass] +public class MSBuildTests : AcceptanceTestBase { - public MSBuildTests(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string tfm, BuildConfiguration compilationMode, Verb verb) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -19,10 +15,9 @@ 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); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, "MSBuildTests", tfm, verb: verb, buildConfiguration: compilationMode); string generatedConfigurationFile = Path.Combine(testHost.DirectoryName, "MSBuildTests.testconfig.json"); @@ -30,7 +25,7 @@ public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string Assert.AreEqual(ConfigurationContent.Trim(), File.ReadAllText(generatedConfigurationFile).Trim()); Assert.IsTrue(compilationResult.StandardOutput.Contains("Microsoft Testing Platform configuration file written")); - compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:normal -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); Assert.IsTrue(File.Exists(generatedConfigurationFile)); Assert.AreEqual(ConfigurationContent.Trim(), File.ReadAllText(generatedConfigurationFile).Trim()); compilationResult.StandardOutput.Contains("Microsoft Testing Platform configuration file written"); @@ -40,7 +35,7 @@ public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string \s*GenerateTestingPlatformConfigurationFile: \s*Skipping target "GenerateTestingPlatformConfigurationFile" because all output files are up\-to\-date with respect to the input files\. """)); - await DotnetCli.RunAsync($"clean -c {compilationMode} -v:normal {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + await DotnetCli.RunAsync($"clean -c {compilationMode} -v:normal {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); // dotnet clean doesn't clean the publish output folder if (verb == Verb.build) @@ -49,7 +44,8 @@ public async Task ConfigFileGeneration_CorrectlyCreateAndCacheAndCleaned(string } } - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task ConfigFileGeneration_NoConfigurationFile_TaskWontRun(string tfm, BuildConfiguration compilationMode, Verb verb) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -57,12 +53,11 @@ 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")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:diagnostic -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -v:diagnostic -nodeReuse:false {testAsset.TargetAssetPath} -c {compilationMode}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); var testHost = TestInfrastructure.TestHost.LocateFrom(testAsset.TargetAssetPath, "MSBuildTests", tfm, verb: verb, buildConfiguration: compilationMode); Assert.IsTrue(compilationResult.StandardOutput.Contains("Target \"GenerateTestingPlatformConfigurationFile\" skipped, due to false condition;")); @@ -92,11 +87,6 @@ public async Task ConfigFileGeneration_NoConfigurationFile_TaskWontRun(string tf - - - - - @@ -104,28 +94,45 @@ public async Task ConfigFileGeneration_NoConfigurationFile_TaskWontRun(string tf $JsonContent$ #file Program.cs -using MSBuildTests; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; -#file UnitTest1.cs -namespace MSBuildTests; - -[TestGroup] -public class UnitTest1 +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,__) => new DummyTestFramework()); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } } -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -"""; +public class DummyTestFramework : ITestFramework +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); - private readonly AcceptanceFixture _acceptanceFixture; + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + context.Complete(); + return Task.CompletedTask; + } +} +"""; } 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 8d1f11f367..80e7b6fb18 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.GenerateEntryPoint.cs @@ -1,23 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - using SL = Microsoft.Build.Logging.StructuredLogger; namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class MSBuildTests_EntryPoint : AcceptanceTestBase +[TestClass] +public class MSBuildTests_EntryPoint : AcceptanceTestBase { private const string AssetName = "MSBuildTests"; - private readonly AcceptanceFixture _acceptanceFixture; - - public MSBuildTests_EntryPoint(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task When_GenerateTestingPlatformEntryPoint_IsFalse_NoEntryPointInjected(string tfm, BuildConfiguration compilationMode, Verb verb) { using TestAsset testAsset = await TestAsset.GenerateAssetAsync( @@ -25,9 +19,9 @@ public async Task When_GenerateTestingPlatformEntryPoint_IsFalse_NoEntryPointInj CSharpSourceCode .PatchCodeWithReplace("$TargetFrameworks$", tfm) .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj ", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"restore -r {RID} {testAsset.TargetAssetPath}{Path.DirectorySeparatorChar}MSBuildTests.csproj ", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); - compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -p:GenerateTestingPlatformEntryPoint=False -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + compilationResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -p:GenerateTestingPlatformEntryPoint=False -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); SL.Build binLog = SL.Serialization.Read(binlogFile); SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); Assert.AreEqual("Target \"_GenerateTestingPlatformEntryPoint\" skipped, due to false condition; ( '$(GenerateTestingPlatformEntryPoint)' == 'True' ) was evaluated as ( 'False' == 'True' ).", ((SL.Message)generateTestingPlatformEntryPoint.Children[0]).Text); @@ -38,7 +32,8 @@ public async Task When_GenerateTestingPlatformEntryPoint_IsFalse_NoEntryPointInj Assert.AreNotEqual(0, compilationResult.ExitCode); } - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task GenerateCSharpEntryPointAndVerifyTheCacheUsage(string tfm, BuildConfiguration compilationMode, Verb verb) => await GenerateAndVerifyLanguageSpecificEntryPoint(nameof(GenerateCSharpEntryPointAndVerifyTheCacheUsage), CSharpSourceCode, "cs", tfm, compilationMode, verb, @"Entrypoint source: @@ -62,7 +57,8 @@ internal sealed class TestingPlatformEntryPoint } }'", "Csc"); - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task GenerateVBEntryPointAndVerifyTheCacheUsage(string tfm, BuildConfiguration compilationMode, Verb verb) => await GenerateAndVerifyLanguageSpecificEntryPoint(nameof(GenerateVBEntryPointAndVerifyTheCacheUsage), VBSourceCode, "vb", tfm, compilationMode, verb, @"Entrypoint source: @@ -89,7 +85,8 @@ End Function End Module'", "Vbc"); - [ArgumentsProvider(nameof(GetBuildMatrixTfmBuildVerbConfiguration))] + [DynamicData(nameof(GetBuildMatrixTfmBuildVerbConfiguration), typeof(AcceptanceTestBase), DynamicDataSourceType.Method)] + [TestMethod] public async Task GenerateFSharpEntryPointAndVerifyTheCacheUsage(string tfm, BuildConfiguration compilationMode, Verb verb) => await GenerateAndVerifyLanguageSpecificEntryPoint(nameof(GenerateFSharpEntryPointAndVerifyTheCacheUsage), FSharpSourceCode, "fs", tfm, compilationMode, verb, @"Entrypoint source: @@ -116,13 +113,11 @@ 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); + 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"); - DotnetMuxerResult buildResult = 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); + DotnetMuxerResult buildResult = await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); SL.Build binLog = SL.Serialization.Read(binlogFile); SL.Target generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive().Single(t => t.Name == "_GenerateTestingPlatformEntryPoint"); SL.Task testingPlatformEntryPoint = generateTestingPlatformEntryPoint.FindChildrenRecursive().Single(t => t.Name == "TestingPlatformEntryPointTask"); @@ -141,7 +136,7 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPoint(string assetName, Assert.IsNotNull(sourceFilePathInObj); File.Delete(binlogFile); - 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); + await DotnetCli.RunAsync($"{(verb == Verb.publish ? $"publish -f {tfm}" : "build")} -c {compilationMode} -r {RID} -nodeReuse:false -bl:{binlogFile} {testAsset.TargetAssetPath} -v:n", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); binLog = SL.Serialization.Read(binlogFile); generateTestingPlatformEntryPoint = binLog.FindChildrenRecursive(t => t.Name == "_GenerateTestingPlatformEntryPoint" && t.Children.Count > 0).Single(); Assert.IsNotNull(generateTestingPlatformEntryPoint.FindChildrenRecursive(m => m.Text.Contains("Skipping target \"_GenerateTestingPlatformEntryPoint\" because all output files are up-to-date with respect to the input files.", StringComparison.OrdinalIgnoreCase)).Single()); @@ -158,15 +153,15 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPoint(string assetName, - DummyAdapter - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + DummyTestFramework + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration - DummyAdapter2 - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2 + DummyTestFramework2 + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration2 @@ -181,7 +176,6 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPoint(string assetName, - @@ -195,15 +189,15 @@ private async Task GenerateAndVerifyLanguageSpecificEntryPoint(string assetName, namespace MyNamespaceRoot.Level1.Level2; -public static class DummyAdapterRegistration +public static class DummyTestFrameworkRegistration { public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] args) { - testApplicationBuilder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyAdapter()); + testApplicationBuilder.RegisterTestFramework(_ => new Capabilities(), (_, __) => new DummyTestFramework()); } } -public static class DummyAdapterRegistration2 +public static class DummyTestFrameworkRegistration2 { public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] args) { @@ -211,9 +205,9 @@ public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, } } -internal sealed class DummyAdapter : ITestFramework, IDataProducer +internal sealed class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => string.Empty; @@ -249,15 +243,15 @@ internal sealed class Capabilities : ITestFrameworkCapabilities - DummyAdapter - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + DummyTestFramework + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration - DummyAdapter2 - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2 + DummyTestFramework2 + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration2 @@ -269,7 +263,6 @@ internal sealed class Capabilities : ITestFrameworkCapabilities - @@ -284,18 +277,18 @@ Imports Microsoft.Testing.Platform.Extensions Imports Microsoft.Testing.Platform Namespace MyNamespaceRoot.Level1.Level2 - Public Module DummyAdapterRegistration + Public Module DummyTestFrameworkRegistration Public Sub AddExtensions(builder As ITestApplicationBuilder, args As String()) - builder.RegisterTestFramework(Function() New Capabilities(), Function(cap, services) New DummyAdapter()) + builder.RegisterTestFramework(Function() New Capabilities(), Function(cap, services) New DummyTestFramework()) End Sub End Module - Public Module DummyAdapterRegistration2 + Public Module DummyTestFrameworkRegistration2 Public Sub AddExtensions(builder As ITestApplicationBuilder, args As String()) End Sub End Module - Class DummyAdapter + Class DummyTestFramework Implements ITestFramework Implements IDataProducer @@ -375,15 +368,15 @@ End Namespace - DummyAdapter - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration + DummyTestFramework + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration - DummyAdapter2 - MyNamespaceRoot.Level1.Level2.DummyAdapterRegistration2 + DummyTestFramework2 + MyNamespaceRoot.Level1.Level2.DummyTestFrameworkRegistration2 @@ -419,11 +412,11 @@ type Capabilities () = interface ITestFrameworkCapabilities with member _.Capabilities = [||] -type DummyAdapter() = +type DummyTestFramework() = let dataProducer = { new IDataProducer with member _.DataTypesProduced = [| typedefof |] - member _.Uid = nameof(DummyAdapter) + member _.Uid = nameof(DummyTestFramework) member _.Version = "" member _.DisplayName = "" member _.Description = "" @@ -432,7 +425,7 @@ member _.IsEnabledAsync() = Task.FromResult true } interface ITestFramework with - member _.Uid = nameof(DummyAdapter) + member _.Uid = nameof(DummyTestFramework) member _.Version = "" member _.DisplayName = "" member _.Description = "" @@ -449,11 +442,11 @@ member _.IsEnabledAsync() = Task.FromResult true context.Complete() } -module DummyAdapterRegistration = +module DummyTestFrameworkRegistration = let AddExtensions (testApplicationBuilder : ITestApplicationBuilder, args: string[]) = - testApplicationBuilder.RegisterTestFramework((fun _ -> Capabilities()), (fun _ _ -> DummyAdapter())) |> ignore + testApplicationBuilder.RegisterTestFramework((fun _ -> Capabilities()), (fun _ _ -> DummyTestFramework())) |> ignore -module DummyAdapterRegistration2 = +module DummyTestFrameworkRegistration2 = let AddExtensions (_, _) = () """; diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs index e004a8ced4..f4afa0443f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Solution.cs @@ -1,41 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class MSBuildTests_Solution : AcceptanceTestBase +[TestClass] +public class MSBuildTests_Solution : AcceptanceTestBase { - private readonly AcceptanceFixture _acceptanceFixture; private const string AssetName = "MSTestProject"; - static MSBuildTests_Solution() - { - string dotnetMuxerSDK = Directory.GetDirectories(Path.Combine(RootFinder.Find(), ".dotnet", "sdk")).OrderByDescending(x => x).First(); - File.WriteAllText(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.CrossTargeting.targets"), MicrosoftCommonCrossTargeting); - if (!File.Exists(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.Test.targets"))) - { - File.WriteAllText(Path.Combine(dotnetMuxerSDK, "Microsoft.Common.Test.targets"), MicrosoftCommonTesttargets); - } - } - - public MSBuildTests_Solution(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - - private void CheckPatch() - { - // https://github.com/dotnet/sdk/issues/37712 - if (DateTime.UtcNow.Date > new DateTime(2024, 12, 1)) - { - throw new InvalidOperationException("Check if we can remove the patch!"); - } - } - - internal static IEnumerable> GetBuildMatrix() + internal static IEnumerable<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm, string Command)> GetBuildMatrix() { - foreach (TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm)> entry in GetBuildMatrixSingleAndMultiTfmBuildConfiguration()) + foreach ((string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm) entry in GetBuildMatrixSingleAndMultiTfmBuildConfiguration()) { foreach (string command in new string[] { @@ -43,28 +18,23 @@ private void CheckPatch() "test --no-restore", }) { - yield return new TestArgumentsEntry<(string SingleTfmOrMultiTfm, BuildConfiguration BuildConfiguration, bool IsMultiTfm, string Command)>( - (entry.Arguments.SingleTfmOrMultiTfm, entry.Arguments.BuildConfiguration, entry.Arguments.IsMultiTfm, command), $"{(entry.Arguments.IsMultiTfm ? "multitfm" : entry.Arguments.SingleTfmOrMultiTfm)},{entry.Arguments.BuildConfiguration},{command}"); + yield return new(entry.SingleTfmOrMultiTfm, entry.BuildConfiguration, entry.IsMultiTfm, command); } } } - [ArgumentsProvider(nameof(GetBuildMatrix))] + [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method)] + [TestMethod] public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_Tests(string singleTfmOrMultiTfm, BuildConfiguration _, bool isMultiTfm, string command) { - CheckPatch(); - using TestAsset generator = await TestAsset.GenerateAssetAsync( AssetName, SourceCode .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{singleTfmOrMultiTfm}" : $"{singleTfmOrMultiTfm}") - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .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()); - string unitTestSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "UnitTest1.cs", SearchOption.AllDirectories).Single()); - string usingsSourceContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "Usings.cs", SearchOption.AllDirectories).Single()); string nugetConfigContent = File.ReadAllText(Directory.GetFiles(generator.TargetAssetPath, "NuGet.config", SearchOption.AllDirectories).Single()); // Create a solution with 3 projects @@ -77,32 +47,30 @@ public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_ CSharpProject project = solution.CreateCSharpProject($"TestProject{i}", isMultiTfm ? singleTfmOrMultiTfm.Split(';') : [singleTfmOrMultiTfm]); File.WriteAllText(project.ProjectFile, projectContent); project.AddOrUpdateFileContent("Program.cs", programSourceContent.PatchCodeWithReplace("$ProjectName$", $"TestProject{i}")); - project.AddOrUpdateFileContent("UnitTest1.cs", unitTestSourceContent); - project.AddOrUpdateFileContent("Usings.cs", usingsSourceContent); CSharpProject project2 = solution.CreateCSharpProject($"Project{i}", isMultiTfm ? singleTfmOrMultiTfm.Split(';') : [singleTfmOrMultiTfm]); project.AddProjectReference(project2.ProjectFile); } // Build the solution - DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); - restoreResult.AssertOutputNotContains("An approximate best match of"); - DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -nodeReuse:false {solution.SolutionFile}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + DotnetMuxerResult restoreResult = await DotnetCli.RunAsync($"restore -nodeReuse:false {solution.SolutionFile} --configfile {nugetFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); + restoreResult.AssertOutputDoesNotContain("An approximate best match of"); + DotnetMuxerResult testResult = await DotnetCli.RunAsync($"{command} -nodeReuse:false {solution.SolutionFile}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path); if (isMultiTfm) { foreach (string tfm in singleTfmOrMultiTfm.Split(';')) { - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject0\..*' \[{tfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject1\..*' \[{tfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject2\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject0\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject1\..*' \[{tfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject2\..*' \[{tfm}\|x64\]"); } } else { - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject0\..*' \[{singleTfmOrMultiTfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject1\..*' \[{singleTfmOrMultiTfm}\|x64\]"); - testResult.AssertOutputRegEx($@"Tests succeeded: '.*TestProject2\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject0\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject1\..*' \[{singleTfmOrMultiTfm}\|x64\]"); + testResult.AssertOutputMatchesRegex($@"Tests succeeded: '.*TestProject2\..*' \[{singleTfmOrMultiTfm}\|x64\]"); } } @@ -122,248 +90,61 @@ public async Task MSBuildTests_UseMSBuildTestInfrastructure_Should_Run_Solution_ - - - - #file Program.cs -using MSBuildTests; -using $ProjectName$; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -builder.AddMSBuild(); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace MSBuildTests; - -[TestGroup] -public class UnitTest1 +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.MSBuild; +using Microsoft.Testing.Platform.Services; + +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,__) => new DummyTestFramework()); + builder.AddMSBuild(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } } -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Platform.MSBuild; -"""; - - private const string MicrosoftCommonCrossTargeting = """ - - - - true - true - - - - - - - - - - <_ThisProjectBuildMetadata Include="$(MSBuildProjectFullPath)"> - @(_TargetFrameworkInfo) - @(_TargetFrameworkInfo->'%(TargetFrameworkMonikers)') - @(_TargetFrameworkInfo->'%(TargetPlatformMonikers)') - $(_AdditionalPropertiesFromProject) - false - @(_TargetFrameworkInfo->'%(IsRidAgnostic)') - - - false - $(Platform) - $(Platforms) - - - - - - <_TargetFramework Include="$(TargetFrameworks)" /> - - <_TargetFrameworkNormalized Include="@(_TargetFramework->Trim()->Distinct())" /> - <_InnerBuildProjects Include="$(MSBuildProjectFile)"> - TargetFramework=%(_TargetFrameworkNormalized.Identity) - - - - - - - - - - - - true - - - - - - - - - - - - - Build - - - - - - - - - - - - $([MSBuild]::IsRunningFromVisualStudio()) - $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets - $(MSBuildToolsPath)\NuGet.targets - - - - - - true - - - - - true - - - - true - - - - <_DirectoryBuildTargetsFile Condition="'$(_DirectoryBuildTargetsFile)' == ''">Directory.Build.targets - <_DirectoryBuildTargetsBasePath Condition="'$(_DirectoryBuildTargetsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildTargetsFile)')) - $([System.IO.Path]::Combine('$(_DirectoryBuildTargetsBasePath)', '$(_DirectoryBuildTargetsFile)')) - - - - false - - - -"""; + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "Test1", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "Test1", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); - private const string MicrosoftCommonTesttargets = """ - - - + context.Complete(); + } +} """; } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs index fa4a775ae4..2941e1c4e8 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MSBuildTests.Test.cs @@ -1,43 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class MSBuildTests_Test : AcceptanceTestBase +[TestClass] +public class MSBuildTests_Test : AcceptanceTestBase { private const string AssetName = "MSBuildTests"; - private readonly AcceptanceFixture _acceptanceFixture; - - public MSBuildTests_Test(ITestExecutionContext testExecutionContext, AcceptanceFixture acceptanceFixture) - : base(testExecutionContext) => _acceptanceFixture = acceptanceFixture; - internal static TestArgumentsEntry<(string BuildCommand, string TargetFramework, BuildConfiguration BuildConfiguration, bool TestSucceeded)> FormatBuildMatrixEntry(TestArgumentsContext ctx) - { - var entry = ((string, string, BuildConfiguration, bool))ctx.Arguments; - return new TestArgumentsEntry<(string, string, BuildConfiguration, bool)>(entry, $"{entry.Item1},{(TargetFrameworks.All.ToMSBuildTargetFrameworks() == entry.Item2 ? "multitfm" : entry.Item2)},{entry.Item3},{(entry.Item4 ? "Succeeded" : "Failed")}"); - } + public static string? FormatBuildMatrixEntry(MethodInfo method, object?[]? data) + => $"{data![0]},{(Equals(TargetFrameworks.All.ToMSBuildTargetFrameworks(), data[1]) ? "multitfm" : data[1])},{data[2]},{((bool)data[3]! ? "Succeeded" : "Failed")}"; internal static IEnumerable<(string BuildCommand, string TargetFramework, BuildConfiguration BuildConfiguration, bool TestSucceeded)> GetBuildMatrix() { - foreach (TestArgumentsEntry tfm in TargetFrameworks.All) + foreach (string tfm in TargetFrameworks.All) { - foreach (BuildConfiguration compilationMode in Enum.GetValues()) + foreach ((string buildCommand, _, BuildConfiguration buildConfiguration, bool testSucceeded) in GetBuildMatrixMultiTfm()) { - foreach (bool testSucceeded in new bool[] { true, false }) - { - foreach (string buildCommand in new string[] - { - "build -t:Test", - "test -p:TestingPlatformDotnetTestSupport=True", - }) - { - yield return (buildCommand, tfm.Arguments, compilationMode, testSucceeded); - } - } + yield return (buildCommand, tfm, buildConfiguration, testSucceeded); } } } @@ -48,25 +28,21 @@ public MSBuildTests_Test(ITestExecutionContext testExecutionContext, AcceptanceF { foreach (bool testSucceeded in new bool[] { true, false }) { - foreach (string buildCommand in new string[] - { - "build -t:Test", - "test -p:TestingPlatformDotnetTestSupport=True", - }) - { - yield return (buildCommand, TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode, testSucceeded); - } + yield return ("build -t:Test", TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode, testSucceeded); + yield return ("test -p:TestingPlatformDotnetTestSupport=True", TargetFrameworks.All.ToMSBuildTargetFrameworks(), compilationMode, testSucceeded); } } } - [ArgumentsProvider(nameof(GetBuildMatrix), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(FormatBuildMatrixEntry))] + [TestMethod] public async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail_SingleTfm(string testCommand, string tfm, BuildConfiguration compilationMode, bool testSucceeded) => await InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(testCommand, tfm, false, [tfm], compilationMode, testSucceeded); - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfm), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + [DynamicData(nameof(GetBuildMatrixMultiTfm), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(FormatBuildMatrixEntry))] + [TestMethod] public async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail_MultiTfm(string testCommand, string multiTfm, BuildConfiguration compilationMode, bool testSucceeded) - => await InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(testCommand, multiTfm, true, TargetFrameworks.All.Select(x => x.Arguments).ToArray(), compilationMode, testSucceeded); + => await InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(testCommand, multiTfm, true, TargetFrameworks.All, compilationMode, testSucceeded); private async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Showing_Error_Detail(string testCommand, string tfm, bool isMultiTfm, string[] tfmsToAssert, BuildConfiguration compilationMode, bool testSucceeded) { @@ -76,11 +52,10 @@ private async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Sho .PatchCodeWithReplace("$PlatformTarget$", "x64") .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); string testResultFolder = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N")); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformCommandLineArguments=\"--results-directory %22{testResultFolder}%22\" -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformCommandLineArguments=\"--results-directory %22{testResultFolder}%22\" -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); foreach (string tfmToAssert in tfmsToAssert) { @@ -88,13 +63,15 @@ private async Task InvokeTestingPlatform_Target_Should_Execute_Tests_Without_Sho } } - [ArgumentsProvider(nameof(GetBuildMatrix), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(FormatBuildMatrixEntry))] + [TestMethod] public async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_SingleTfm(string testCommand, string tfm, BuildConfiguration compilationMode, bool testSucceeded) => await InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(testCommand, tfm, false, [tfm], compilationMode, testSucceeded); - [ArgumentsProvider(nameof(GetBuildMatrixMultiTfm), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + [DynamicData(nameof(GetBuildMatrixMultiTfm), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(FormatBuildMatrixEntry))] + [TestMethod] public async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_MultiTfm(string testCommand, string multiTfm, BuildConfiguration compilationMode, bool testSucceeded) - => await InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(testCommand, multiTfm, true, TargetFrameworks.All.Select(x => x.Arguments).ToArray(), compilationMode, testSucceeded); + => await InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(testCommand, multiTfm, true, TargetFrameworks.All, compilationMode, testSucceeded); private async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_And_Execute_Passing_Test_And_Pass_TheRun_Detail(string testCommand, string tfm, bool isMultiTfm, string[] tfmsToAssert, BuildConfiguration compilationMode, bool testSucceeded) { @@ -104,18 +81,17 @@ private async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_An .PatchCodeWithReplace("$PlatformTarget$", "x64") .PatchCodeWithReplace("$TargetFrameworks$", isMultiTfm ? $"{tfm}" : $"{tfm}") .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); string testResultFolder = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N")); DotnetMuxerResult compilationResult = testCommand.StartsWith("test", StringComparison.OrdinalIgnoreCase) ? await DotnetCli.RunAsync( - $"{testCommand} -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\" -- --treenode-filter /*/*/*/TestMethod1 --results-directory \"{testResultFolder}\"", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path) + $"{testCommand} -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\" -- --treenode-filter --results-directory \"{testResultFolder}\"", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path) : await DotnetCli.RunAsync( - $"{testCommand} -p:TestingPlatformCommandLineArguments=\"--treenode-filter /*/*/*/TestMethod1 --results-directory \"{testResultFolder}\"\" -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path); + $"{testCommand} -p:TestingPlatformCommandLineArguments=\"--treenode-filter --results-directory \"{testResultFolder}\"\" -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path); foreach (string tfmToAssert in tfmsToAssert) { @@ -123,6 +99,7 @@ private async Task InvokeTestingPlatform_Target_Should_Build_Without_Warnings_An } } + [TestMethod] public async Task Invoke_DotnetTest_With_Arch_Switch_x86_Should_Work() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -141,24 +118,61 @@ public async Task Invoke_DotnetTest_With_Arch_Switch_x86_Should_Work() AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", string.Empty) - .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent.Arguments}") + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); await DotnetCli.RunAsync( $"test --arch x86 -p:TestingPlatformDotnetTestSupport=True -p:Configuration=Release -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, environmentVariables: dotnetRootX86, failIfReturnValueIsNotZero: false); - string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net8.0_x86.log", SearchOption.AllDirectories).Single(); + string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net9.0_x86.log", SearchOption.AllDirectories).Single(); Assert.IsTrue(File.Exists(outputFileLog), $"Expected file '{outputFileLog}'"); string logFileContent = File.ReadAllText(outputFileLog); Assert.IsTrue(Regex.IsMatch(logFileContent, ".*win-x86.*"), logFileContent); Assert.IsTrue(Regex.IsMatch(logFileContent, @"\.dotnet\\x86\\dotnet\.exe"), logFileContent); } + [TestMethod] + public async Task Invoke_DotnetTest_With_Incompatible_Arch() + { + Architecture currentArchitecture = RuntimeInformation.ProcessArchitecture; + string incompatibleArchitecture = currentArchitecture switch + { + Architecture.X86 or Architecture.X64 => "arm64", + _ => "x64", + }; + + TestAsset testAsset = await TestAsset.GenerateAssetAsync( + AssetName, + SourceCode + .PatchCodeWithReplace("$PlatformTarget$", string.Empty) + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") + .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + DotnetMuxerResult result = await DotnetCli.RunAsync( + $"test --arch {incompatibleArchitecture} -p:TestingPlatformDotnetTestSupport=True \"{testAsset.TargetAssetPath}\"", + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, + failIfReturnValueIsNotZero: false); + // The output looks like: + /* + D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\tidOn\.packages\microsoft.testing.platform.msbuild\1.5.0-ci\buildMultiTargeting\Microsoft.Testing.Platform.MSBuild.targets(320,5): error : Could not find 'dotnet.exe' host for the 'arm64' architecture. [D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\vf8vR\MSBuildTests\MSBuild Tests.csproj] + D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\tidOn\.packages\microsoft.testing.platform.msbuild\1.5.0-ci\buildMultiTargeting\Microsoft.Testing.Platform.MSBuild.targets(320,5): error : [D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\vf8vR\MSBuildTests\MSBuild Tests.csproj] + D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\tidOn\.packages\microsoft.testing.platform.msbuild\1.5.0-ci\buildMultiTargeting\Microsoft.Testing.Platform.MSBuild.targets(320,5): error : You can resolve the problem by installing the 'arm64' .NET. [D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\vf8vR\MSBuildTests\MSBuild Tests.csproj] + D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\tidOn\.packages\microsoft.testing.platform.msbuild\1.5.0-ci\buildMultiTargeting\Microsoft.Testing.Platform.MSBuild.targets(320,5): error : [D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\vf8vR\MSBuildTests\MSBuild Tests.csproj] + D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\tidOn\.packages\microsoft.testing.platform.msbuild\1.5.0-ci\buildMultiTargeting\Microsoft.Testing.Platform.MSBuild.targets(320,5): error : The specified framework can be found at: [D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\vf8vR\MSBuildTests\MSBuild Tests.csproj] + D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\tidOn\.packages\microsoft.testing.platform.msbuild\1.5.0-ci\buildMultiTargeting\Microsoft.Testing.Platform.MSBuild.targets(320,5): error : - https://aka.ms/dotnet-download [D:\a\_work\1\s\artifacts\tmp\Debug\testsuite\vf8vR\MSBuildTests\MSBuild Tests.csproj] + */ + // Assert each error line separately for simplicity. + result.AssertOutputContains($"Could not find '{(OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet")}' host for the '{incompatibleArchitecture}' architecture."); + result.AssertOutputContains($"You can resolve the problem by installing the '{incompatibleArchitecture}' .NET."); + result.AssertOutputContains("The specified framework can be found at:"); + result.AssertOutputContains(" - https://aka.ms/dotnet-download"); + } + + [TestMethod] public async Task Invoke_DotnetTest_With_DOTNET_HOST_PATH_Should_Work() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -177,18 +191,17 @@ public async Task Invoke_DotnetTest_With_DOTNET_HOST_PATH_Should_Work() AssetName, SourceCode .PatchCodeWithReplace("$PlatformTarget$", string.Empty) - .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent.Arguments}") + .PatchCodeWithReplace("$TargetFrameworks$", $"{TargetFrameworks.NetCurrent}") .PatchCodeWithReplace("$AssertValue$", bool.TrueString.ToLowerInvariant()) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); await DotnetCli.RunAsync( $"test -p:TestingPlatformDotnetTestSupport=True -p:Configuration=Release -p:nodeReuse=false -bl:{binlogFile} \"{testAsset.TargetAssetPath}\"", - _acceptanceFixture.NuGetGlobalPackagesFolder.Path, + AcceptanceFixture.NuGetGlobalPackagesFolder.Path, environmentVariables: dotnetHostPathEnvVar, failIfReturnValueIsNotZero: false); - string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net8.0_x64.log", SearchOption.AllDirectories).Single(); + string outputFileLog = Directory.GetFiles(testAsset.TargetAssetPath, "MSBuild Tests_net9.0_x64.log", SearchOption.AllDirectories).Single(); Assert.IsTrue(File.Exists(outputFileLog), $"Expected file '{outputFileLog}'"); string logFileContent = File.ReadAllText(outputFileLog); Assert.IsTrue(Regex.IsMatch(logFileContent, @"\.dotnet\\dotnet\.exe"), logFileContent); @@ -211,9 +224,11 @@ private static void CommonAssert(DotnetMuxerResult compilationResult, string tfm Assert.IsFalse(string.IsNullOrEmpty(File.ReadAllText(outputFileLog)), $"Content of file '{File.ReadAllText(outputFileLog)}'"); } - // We avoid to test the multi-tfm because it's already tested with the above tests and we don't want to have too heavy testing, msbuild is pretty heavy (a lot of processes started due to the no 'nodereuse') and makes tests flaky. + // We avoid to test the multi-tfm because it's already tested with the above tests and we don't want to have too heavy testing, + // msbuild is pretty heavy (a lot of processes started due to the no 'nodereuse') and makes tests flaky. // We test two functionality for the same reason, we don't want to load too much the CI only for UX reasons. - [ArgumentsProvider(nameof(GetBuildMatrix), TestArgumentsEntryProviderMethodName = nameof(FormatBuildMatrixEntry))] + [DynamicData(nameof(GetBuildMatrix), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(FormatBuildMatrixEntry))] + [TestMethod] public async Task InvokeTestingPlatform_Target_Showing_Error_And_Do_Not_Capture_The_Output_SingleTfm(string testCommand, string tfm, BuildConfiguration compilationMode, bool testSucceeded) { // We test only failed but we don't want to have too much argument provider overload. @@ -228,13 +243,13 @@ public async Task InvokeTestingPlatform_Target_Showing_Error_And_Do_Not_Capture_ .PatchCodeWithReplace("$PlatformTarget$", "x64") .PatchCodeWithReplace("$TargetFrameworks$", $"{tfm}") .PatchCodeWithReplace("$AssertValue$", testSucceeded.ToString().ToLowerInvariant()) - .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion) - .PatchCodeWithReplace("$MicrosoftTestingEnterpriseExtensionsVersion$", MicrosoftTestingEnterpriseExtensionsVersion)); + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); string binlogFile = Path.Combine(testAsset.TargetAssetPath, Guid.NewGuid().ToString("N"), "msbuild.binlog"); - DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformShowTestsFailure=True -p:TestingPlatformCaptureOutput=False -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} {testAsset.TargetAssetPath}", _acceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); - Assert.Contains("error test failed: TestMethod2 (", compilationResult.StandardOutput); - Assert.Contains("Assert.IsTrue: Expected 'true', but got 'false'.", compilationResult.StandardOutput); - Assert.Contains(".NET Testing Platform", compilationResult.StandardOutput); + DotnetMuxerResult compilationResult = await DotnetCli.RunAsync($"{testCommand} -p:TestingPlatformShowTestsFailure=True -p:TestingPlatformCaptureOutput=False -p:Configuration={compilationMode} -p:nodeReuse=false -bl:{binlogFile} {testAsset.TargetAssetPath}", AcceptanceFixture.NuGetGlobalPackagesFolder.Path, failIfReturnValueIsNotZero: false); + + compilationResult.AssertOutputContains("error test failed: Test2 ("); + compilationResult.AssertOutputContains("FAILED: Expected 'true', but got 'false'."); + compilationResult.AssertOutputContains(".NET Testing Platform"); } private const string SourceCode = """ @@ -253,42 +268,93 @@ public async Task InvokeTestingPlatform_Target_Showing_Error_And_Do_Not_Capture_ - - - - #file Program.cs -using MSBuildTests; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new MSBuild_Tests.SourceGeneratedTestNodesBuilder()); -builder.AddMSBuild(); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace MSBuildTests; - -[TestGroup] -public class UnitTest1 +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; +using Microsoft.Testing.Platform.MSBuild; +using Microsoft.Testing.Platform.Services; + +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + MyExtension myExtension = new(); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,sp) => new DummyTestFramework(sp, myExtension)); + builder.AddTreeNodeFilterService(myExtension); + builder.AddMSBuild(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } +} + +public class MyExtension : IExtension +{ + public string Uid => "MyExtension"; + public string Version => "1.0.0"; + public string DisplayName => "My Extension"; + public string Description => "My Extension Description"; + public Task IsEnabledAsync() => Task.FromResult(true); +} - public void TestMethod2() +public class DummyTestFramework : ITestFramework, IDataProducer +{ + private IServiceProvider _sp; + private MyExtension _myExtension; + + public DummyTestFramework(IServiceProvider sp, MyExtension myExtension) { - Assert.IsTrue($AssertValue$); + _sp = sp; + _myExtension = myExtension; } -} -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Platform.MSBuild; + public string Uid => _myExtension.Uid; + + public string Version => _myExtension.Version; + + public string DisplayName => _myExtension.DisplayName; + + public string Description => _myExtension.Description; + + public Type[] DataTypesProduced => [typeof(TestNodeUpdateMessage)]; + + public Task IsEnabledAsync() => _myExtension.IsEnabledAsync(); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode { Uid = "1", DisplayName = "Test1", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode { Uid = "1", DisplayName = "Test1", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + + if (!_sp.GetCommandLineOptions().TryGetOptionArgumentList("--treenode-filter", out _)) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode { Uid = "2", DisplayName = "Test2", Properties = new(DiscoveredTestNodeStateProperty.CachedInstance) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode { Uid = "2", DisplayName = "Test2", Properties = new($AssertValue$ ? PassedTestNodeStateProperty.CachedInstance : new FailedTestNodeStateProperty("FAILED: Expected 'true', but got 'false'.")) })); + } + + context.Complete(); + } +} """; } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs new file mode 100644 index 0000000000..44ebf81d34 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/MaxFailedTestsExtensionTests.cs @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestClass] +public class MaxFailedTestsExtensionTests : AcceptanceTestBase +{ + private const string AssetName = nameof(MaxFailedTestsExtensionTests); + + [TestMethod] + public async Task TestMaxFailedTestsShouldCallStopTestExecutionAsync() + { + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--maximum-failed-tests 2"); + + testHostResult.AssertExitCodeIs(ExitCodes.TestExecutionStoppedForMaxFailedTests); + + testHostResult.AssertOutputContains("Test session is aborting due to reaching failures ('2') specified by the '--maximum-failed-tests' option."); + testHostResult.AssertOutputContainsSummary(failed: 3, passed: 3, skipped: 0); + } + + [TestMethod] + public async Task WhenCapabilityIsMissingShouldFail() + { + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, TargetFrameworks.NetCurrent); + TestHostResult testHostResult = await testHost.ExecuteAsync("--maximum-failed-tests 2", environmentVariables: new() + { + ["DO_NOT_ADD_CAPABILITY"] = "1", + }); + + testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); + testHostResult.AssertOutputContains("The current test framework does not implement 'IGracefulStopTestExecutionCapability' which is required for '--maximum-failed-tests' feature."); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string Sources = """ +#file MaxFailedTestsExtensionTests.csproj + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + +#file Program.cs +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.Threading; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Helpers; + +internal sealed class Program +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + var testFramework = new DummyTestFramework(); + builder.RegisterTestFramework(_ => new Capabilities(), (_, __) => testFramework); + +#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. + builder.AddMaximumFailedTestsService(testFramework); +#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. + + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +internal class DummyTestFramework : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => string.Empty; + + public string DisplayName => string.Empty; + + public string Description => string.Empty; + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CloseTestSessionAsync(CloseTestSessionContext context) => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + // First fail. + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "1", DisplayName = "Test1", Properties = new(new FailedTestNodeStateProperty()) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "2", DisplayName = "Test2", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "3", DisplayName = "Test3", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "4", DisplayName = "Test4", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + + if (GracefulStop.Instance.IsStopRequested) throw new InvalidOperationException("Unexpected stop request!"); + + // Second fail. + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "5", DisplayName = "Test5", Properties = new(new FailedTestNodeStateProperty()) })); + + await GracefulStop.Instance.TCS.Task; + + if (!GracefulStop.Instance.IsStopRequested) throw new InvalidOperationException("Expected stop request!"); + + // Third fail. + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "6", DisplayName = "Test6", Properties = new(new FailedTestNodeStateProperty()) })); + + context.Complete(); + } + + public Task IsEnabledAsync() => Task.FromResult(true); +} + +internal class Capabilities : ITestFrameworkCapabilities +{ + IReadOnlyCollection ICapabilities.Capabilities + { + get + { + if (Environment.GetEnvironmentVariable("DO_NOT_ADD_CAPABILITY") == "1") + { + return []; + } + + return [GracefulStop.Instance]; + } + } +} + +#pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +internal sealed class GracefulStop : IGracefulStopTestExecutionCapability +#pragma warning restore TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +{ + private GracefulStop() + { + } + + public static GracefulStop Instance { get; } = new(); + + public TaskCompletionSource TCS { get; } = new(); + + public bool IsStopRequested { get; private set; } + + public Task StopTestExecutionAsync(CancellationToken cancellationToken) + { + IsStopRequested = true; + TCS.SetResult(); + return Task.CompletedTask; + } +} + +"""; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.NetCurrent) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + } +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj index 7e0e340d94..747774663a 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests.csproj @@ -1,43 +1,28 @@ - + $(NetCurrent) $(TestRunnerAdditionalArguments) --retry-failed-tests 3 false + true $(DefineConstants);SKIP_INTERMEDIATE_TARGET_FRAMEWORKS Exe - true - - + - - - - + - + + + - - - - - - - - - - - - - diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NoBannerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NoBannerTests.cs index 599edb8d81..9e7308c844 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NoBannerTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/NoBannerTests.cs @@ -1,35 +1,30 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class NoBannerTests : AcceptanceTestBase +[TestClass] +public class NoBannerTests : AcceptanceTestBase { private const string AssetName = "NoBannerTest"; - private readonly TestAssetFixture _testAssetFixture; private readonly string _bannerRegexMatchPattern = @".NET Testing Platform v.+ \[.+\]"; - public NoBannerTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task UsingNoBanner_TheBannerDoesNotAppear(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--no-banner"); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputDoesNotMatchRegex(_bannerRegexMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -41,10 +36,11 @@ public async Task UsingNoBanner_InTheEnvironmentVars_TheBannerDoesNotAppear(stri testHostResult.AssertOutputDoesNotMatchRegex(_bannerRegexMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( null, new Dictionary @@ -56,18 +52,18 @@ public async Task UsingDotnetNoLogo_InTheEnvironmentVars_TheBannerDoesNotAppear( testHostResult.AssertOutputDoesNotMatchRegex(_bannerRegexMatchPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task WithoutUsingNoBanner_TheBannerAppears(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); testHostResult.AssertOutputMatchesRegex(_bannerRegexMatchPattern); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string NoBannerTestCode = """ #file NoBannerTest.csproj @@ -96,21 +92,21 @@ public class Program public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework +public class DummyTestFramework : ITestFramework { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs index f78a15125c..5463488c7f 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/Program.cs @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.Testing.Extensions; -using Microsoft.Testing.Internal.Framework.Configurations; -using Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] // Opt-out telemetry Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); @@ -15,7 +14,7 @@ ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new SourceGeneratedTestNodesBuilder()); +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif 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 2610486891..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": "--treenode-filter /*/*/*/**", + "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..9a3440cf71 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/RetryFailedTestsTests.cs @@ -0,0 +1,374 @@ +#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! -"); + } + } + + [TestMethod] + // We use crash dump, not supported in NetFramework at the moment + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + public async Task RetryFailedTests_MoveFiles_Succeeds(string tfm) + { + // TODO: Crash dump is not working properly on macos, so we skip the test for now + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return; + } + + 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/ServerLoggingTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerLoggingTests.cs new file mode 100644 index 0000000000..6b01f26fb0 --- /dev/null +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/ServerLoggingTests.cs @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100; + +using MSTest.Acceptance.IntegrationTests.Messages.V100; + +namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; + +[TestClass] +public sealed partial class ServerLoggingTests : ServerModeTestsBase +{ + [TestMethod] + public async Task RunningInServerJsonRpcModeShouldHaveOutputDeviceLogsPushedToTestExplorer() + { + string tfm = TargetFrameworks.NetCurrent; + string resultDirectory = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"), tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "ServerLoggingTests", tfm); + using TestingPlatformClient jsonClient = await StartAsServerAndConnectToTheClientAsync(testHost); + LogsCollector logs = new(); + jsonClient.RegisterLogListener(logs); + + InitializeResponse initializeResponseArgs = await jsonClient.Initialize(); + + TestNodeUpdateCollector discoveryCollector = new(); + ResponseListener discoveryListener = await jsonClient.DiscoverTests(Guid.NewGuid(), discoveryCollector.CollectNodeUpdates); + + TestNodeUpdateCollector runCollector = new(); + ResponseListener runListener = await jsonClient.RunTests(Guid.NewGuid(), runCollector.CollectNodeUpdates); + + await Task.WhenAll(discoveryListener.WaitCompletion(), runListener.WaitCompletion()); + Assert.IsFalse(logs.Count == 0, "Logs are empty"); + string logsString = string.Join(Environment.NewLine, logs.Select(l => l.ToString())); + string logPath = LogFilePathRegex().Match(logsString).Groups[1].Value; + string port = PortRegex().Match(logsString).Groups[1].Value; + + Assert.AreEqual( + $$""" + Log { LogLevel = Information, Message = Connecting to client host '127.0.0.1' port '{{port}}' } + Log { LogLevel = Trace, Message = Starting test session. The log file path is '{{logPath}}'. } + Log { LogLevel = Error, Message = System.Exception: This is an exception output } + Log { LogLevel = Information, Message = This is a red output with padding set to 3 } + Log { LogLevel = Information, Message = This is a yellow output with padding set to 2 } + Log { LogLevel = Information, Message = This is a blue output with padding set to 1 } + Log { LogLevel = Information, Message = This is normal text output. } + Log { LogLevel = Trace, Message = Finished test session. } + Log { LogLevel = Trace, Message = Starting test session. The log file path is '{{logPath}}'. } + Log { LogLevel = Error, Message = System.Exception: This is an exception output } + Log { LogLevel = Information, Message = This is a red output with padding set to 3 } + Log { LogLevel = Information, Message = This is a yellow output with padding set to 2 } + Log { LogLevel = Information, Message = This is a blue output with padding set to 1 } + Log { LogLevel = Information, Message = This is normal text output. } + Log { LogLevel = Trace, Message = Finished test session. } + """, logsString); + } + + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) + { + private const string AssetName = "AssetFixture"; + + public string TargetAssetPath => GetAssetPath(AssetName); + + public override IEnumerable<(string ID, string Name, string Code)> GetAssetsToGenerate() + { + yield return (AssetName, AssetName, + Sources + .PatchTargetFrameworks(TargetFrameworks.All) + .PatchCodeWithReplace("$MicrosoftTestingPlatformVersion$", MicrosoftTestingPlatformVersion)); + } + + private const string Sources = """ +#file ServerLoggingTests.csproj + + + + $TargetFrameworks$ + Exe + true + enable + preview + + + + + + + +#file Program.cs + +using System; +using System.Threading.Tasks; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.OutputDevice; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.OutputDevice; +using Microsoft.Testing.Platform.Services; + +public class Startup +{ + public static async Task Main(string[] args) + { + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, serviceProvider) => new DummyTestFramework(serviceProvider)); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} + +public class DummyTestFramework : ITestFramework, IDataProducer, IOutputDeviceDataProducer +{ + private readonly IOutputDevice _outputDevice; + + public DummyTestFramework(IServiceProvider serviceProvider) + { + _outputDevice = serviceProvider.GetOutputDevice(); + } + + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Type[] DataTypesProduced => new[] { typeof(TestNodeUpdateMessage) }; + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await _outputDevice.DisplayAsync(this, new ExceptionOutputDeviceData(new Exception("This is an exception output"))); + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("This is a red output with padding set to 3") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Red }, + Padding = 3, + }); + + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("This is a yellow output with padding set to 2") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Yellow }, + Padding = 2, + }); + + await _outputDevice.DisplayAsync(this, new FormattedTextOutputDeviceData("This is a blue output with padding set to 1") + { + ForegroundColor = new SystemConsoleColor() { ConsoleColor = ConsoleColor.Blue }, + Padding = 1, + }); + + await _outputDevice.DisplayAsync(this, new TextOutputDeviceData("This is normal text output.")); + context.Complete(); + } +} +"""; + } + + [GeneratedRegex("Connecting to client host '127.0.0.1' port '(\\d+)'")] + private static partial Regex PortRegex(); + + [GeneratedRegex("The log file path is '(.+?)'")] + private static partial Regex LogFilePathRegex(); +} diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs index 48cafde0c2..53abc787a6 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TelemetryTests.cs @@ -1,31 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.RegularExpressions; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; using Microsoft.Testing.Platform.Configurations; -using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class TelemetryTests : AcceptanceTestBase +[TestClass] +public class TelemetryTests : AcceptanceTestBase { private const string AssetName = "TelemetryTest"; - private readonly TestAssetFixture _testAssetFixture; - - public TelemetryTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Telemetry_ByDefault_TelemetryIsEnabled(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", disableTelemetry: false); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -40,13 +32,14 @@ public async Task Telemetry_ByDefault_TelemetryIsEnabled(string tfm) await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_TelemetryIsDisabled(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( "--diagnostic", new Dictionary @@ -67,13 +60,14 @@ public async Task Telemetry_WhenOptingOutTelemetry_WithEnvironmentVariable_Telem await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVariable_TelemetryIsDisabled(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( "--diagnostic", new Dictionary @@ -94,13 +88,14 @@ public async Task Telemetry_WhenOptingOutTelemetry_With_DOTNET_CLI_EnvironmentVa await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOptions_TelemetryIsDisabled(string tfm) { - string diagPath = Path.Combine(_testAssetFixture.TargetAssetPathWithDisableTelemetry, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); + string diagPath = Path.Combine(AssetFixture.TargetAssetPathWithDisableTelemetry, "bin", "Release", tfm, AggregatedConfiguration.DefaultTestResultFolderName); string diagPathPattern = Path.Combine(diagPath, @"log_.*.diag").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPathWithDisableTelemetry, AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithDisableTelemetry, AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--diagnostic", disableTelemetry: false); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -115,7 +110,7 @@ public async Task Telemetry_WhenEnableTelemetryIsFalse_WithTestApplicationOption await AssertDiagnosticReportAsync(testHostResult, diagPathPattern, diagContentsPattern); } - private async Task AssertDiagnosticReportAsync(TestHostResult testHostResult, string diagPathPattern, string diagContentsPattern, string level = "Trace", string flushType = "async") + private static async Task AssertDiagnosticReportAsync(TestHostResult testHostResult, string diagPathPattern, string diagContentsPattern, string level = "Trace", string flushType = "async") { testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -131,15 +126,14 @@ private async Task AssertDiagnosticReportAsync(TestHostResult testHostRe return match.Value; } - private async Task<(bool IsMatch, string Content)> CheckDiagnosticContentsMatchAsync(string path, string pattern) + private static async Task<(bool IsMatch, string Content)> CheckDiagnosticContentsMatchAsync(string path, string pattern) { using var reader = new StreamReader(path); string content = await reader.ReadToEndAsync(); return (Regex.IsMatch(content, pattern), content); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string WithTelemetry = nameof(WithTelemetry); private const string WithoutTelemetry = nameof(WithoutTelemetry); @@ -171,21 +165,21 @@ public class Program public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args$TelemetryArg$); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework +public class DummyTestFramework : ITestFramework { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs index a2bcd24716..ea7644e892 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TestHostProcessLifetimeHandlerTests.cs @@ -1,34 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public sealed class TestHostProcessLifetimeHandlerTests : AcceptanceTestBase +[TestClass] +public sealed class TestHostProcessLifetimeHandlerTests : AcceptanceTestBase { private const string AssetName = "TestHostProcessLifetimeHandler"; - private readonly TestAssetFixture _testAssetFixture; - - public TestHostProcessLifetimeHandlerTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task All_Interface_Methods_ShouldBe_Invoked(string currentTfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, AssetName, currentTfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, AssetName, currentTfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.Success); - Assert.AreEqual(File.ReadAllText(Path.Combine(testHost.DirectoryName, "BeforeTestHostProcessStartAsync.txt")), "TestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync"); - Assert.AreEqual(File.ReadAllText(Path.Combine(testHost.DirectoryName, "OnTestHostProcessStartedAsync.txt")), "TestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync"); - Assert.AreEqual(File.ReadAllText(Path.Combine(testHost.DirectoryName, "OnTestHostProcessExitedAsync.txt")), "TestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync"); + Assert.AreEqual("TestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync", File.ReadAllText(Path.Combine(testHost.DirectoryName, "BeforeTestHostProcessStartAsync.txt"))); + Assert.AreEqual("TestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync", File.ReadAllText(Path.Combine(testHost.DirectoryName, "OnTestHostProcessStartedAsync.txt"))); + Assert.AreEqual("TestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync", File.ReadAllText(Path.Combine(testHost.DirectoryName, "OnTestHostProcessExitedAsync.txt"))); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) - : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string Sources = """ #file TestHostProcessLifetimeHandler.csproj @@ -66,7 +58,7 @@ public class Startup public static async Task Main(string[] args) { var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); - testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); testApplicationBuilder.TestHostControllers.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler()); using ITestApplication app = await testApplicationBuilder.BuildAsync(); return await app.RunAsync(); @@ -107,15 +99,15 @@ public Task OnTestHostProcessStartedAsync(ITestHostProcessInformation testHostPr } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs index 273938bc3c..0e0bdc1db7 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TimeoutTests.cs @@ -1,97 +1,95 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class TimeoutTests : AcceptanceTestBase +[TestClass] +public class TimeoutTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public TimeoutTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithInvalidArg_WithoutLetterSuffix_OutputInvalidMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.StandardError.Contains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithInvalidArg_WithInvalidLetterSuffix_OutputInvalidMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5y"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.StandardError.Contains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithInvalidArg_WithInvalidFormat_OutputInvalidMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 5h6m"); testHostResult.AssertExitCodeIs(ExitCodes.InvalidCommandLine); testHostResult.StandardError.Contains("'timeout' option should have one argument as string in the format [h|m|s] where 'value' is float"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithValidArg_WithTestTimeOut_OutputContainsCancelingMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1s"); testHostResult.AssertExitCodeIsNot(ExitCodes.Success); testHostResult.StandardOutput.Contains("Canceling the test session"); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithValidArg_WithSecondAsSuffix_WithTestNotTimeOut_OutputDoesNotContainCancelingMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 12.5s"); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string output = testHostResult.StandardOutput; Assert.IsFalse(output.Contains("Canceling the test session")); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithValidArg_WithMinuteAsSuffix_WithTestNotTimeOut_OutputDoesNotContainCancelingMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1m"); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string output = testHostResult.StandardOutput; Assert.IsFalse(output.Contains("Canceling the test session")); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task TimeoutWithValidArg_WithHourAsSuffix_WithTestNotTimeOut_OutputDoesNotContainCancelingMessage(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.NoExtensionTargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--timeout 1h"); - testHostResult.AssertExitCodeIs(ExitCodes.Success); + testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); string output = testHostResult.StandardOutput; Assert.IsFalse(output.Contains("Canceling the test session")); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string AssetName = "TimeoutTest"; @@ -107,38 +105,52 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test preview - - - - #file Program.cs -using TimeoutTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); - -#file UnitTest1.cs -namespace TimeoutTest; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.TestFramework; +using Microsoft.Testing.Platform.Services; -[TestGroup] -public class UnitTest1 +public class Program { - public void TestMethod1() + public static async Task Main(string[] args) { - Assert.IsTrue(true); - Thread.Sleep(10000); + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + builder.RegisterTestFramework( + sp => new TestFrameworkCapabilities(), + (_,__) => new DummyTestFramework()); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); } } -#file Usings.cs -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; +public class DummyTestFramework : ITestFramework +{ + public string Uid => nameof(DummyTestFramework); + + public string Version => "2.0.0"; + + public string DisplayName => nameof(DummyTestFramework); + + public string Description => nameof(DummyTestFramework); + + public Task IsEnabledAsync() => Task.FromResult(true); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + public Task ExecuteRequestAsync(ExecuteRequestContext context) + { + Thread.Sleep(10000); + context.Complete(); + return Task.CompletedTask; + } +} """; public string NoExtensionTargetAssetPath => GetAssetPath(AssetName); @@ -148,8 +160,7 @@ public void TestMethod1() 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 ec20efca39..f2f1a5ddca 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs @@ -1,25 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.RegularExpressions; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class TrxTests : AcceptanceTestBase +[TestClass] +public class TrxTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public TrxTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync(); testHostResult.AssertExitCodeIs(ExitCodes.Success); @@ -31,24 +22,32 @@ public async Task Trx_WhenReportTrxIsNotSpecified_TrxReportIsNotGenerated(string testHostResult.AssertOutputDoesNotMatchRegex(outputPattern); } - [ArgumentsProvider(nameof(TargetFrameworks.All), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLocation(string tfm) { - string testResultsPath = Path.Combine(_testAssetFixture.TargetAssetPath, "bin", "Release", tfm, "TestResults"); + string testResultsPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, "TestResults"); string trxPathPattern = Path.Combine(testResultsPath, ".*.trx").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx"); // number of test is the third param because we have two different test code with different number of tests. await AssertTrxReportWasGeneratedAsync(testHostResult, trxPathPattern, 1); } - [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string tfm) { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // TODO: Investigate failures on macos + return; + } + string fileName = Guid.NewGuid().ToString("N"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( $"--crashdump --report-trx --report-trx-filename {fileName}.trx", new() { { "CRASHPROCESS", "1" } }); @@ -57,15 +56,16 @@ public async Task Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string tfm) string trxFile = Directory.GetFiles(testHost.DirectoryName, $"{fileName}.trx", SearchOption.AllDirectories).Single(); string trxContent = File.ReadAllText(trxFile); - Assert.That(Regex.IsMatch(trxContent, @"Test host process pid: .* crashed\."), trxContent); - Assert.That(trxContent.Contains(""""""), trxContent); + Assert.IsTrue(Regex.IsMatch(trxContent, @"Test host process pid: .* crashed\."), trxContent); + StringAssert.Contains(trxContent, """""", trxContent); } - [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string tfm) { string fileName = Guid.NewGuid().ToString("N"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx"); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -75,33 +75,34 @@ public async Task Trx_WhenSkipTest_ItAppearsAsExpectedInsideTheTrx(string tfm) string trxContent = File.ReadAllText(trxFile); // check if the tests have been added to Results, TestDefinitions, TestEntries and ResultSummary. - Assert.That(trxContent.Contains(@""""), trxContent); - Assert.That(trxContent.Contains(""""""), trxContent); + StringAssert.Contains(trxContent, @"""", trxContent); + StringAssert.Contains(trxContent, """""", trxContent); } - [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Trx_WhenTheTestNameHasInvalidXmlChar_TheTrxCreatedSuccessfully(string tfm) { - string testResultsPath = Path.Combine(_testAssetFixture.TargetAssetPathWithDataRow, "bin", "Release", tfm, "TestResults"); + string testResultsPath = Path.Combine(AssetFixture.TargetAssetPathWithDataRow, "bin", "Release", tfm, "TestResults"); string trxPathPattern = Path.Combine(testResultsPath, ".*.trx").Replace(@"\", @"\\"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPathWithDataRow, TestAssetFixture.AssetNameUsingMSTest, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithDataRow, TestAssetFixture.AssetNameUsingMSTest, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync("--report-trx"); // number of test is the third param because we have two different test code with different number of tests. await AssertTrxReportWasGeneratedAsync(testHostResult, trxPathPattern, 2); } - [ArgumentsProvider(nameof(TargetFrameworks.Net), typeof(TargetFrameworks))] + [DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))] + [TestMethod] public async Task Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(string tfm) { string fileName = Guid.NewGuid().ToString("N"); - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPathWithSkippedTest, TestAssetFixture.AssetNameUsingMSTest, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync($"--report-trx --report-trx-filename {fileName}.trx"); testHostResult.AssertExitCodeIs(ExitCodes.ZeroTests); @@ -115,48 +116,52 @@ public async Task Trx_UsingDataDriven_CreatesUnitTestTagForEachOneInsideTheTrx(s \s* CheckTrxContentsMatchAsync(string path, string pattern) + private static async Task CheckTrxContentsMatchAsync(string path, string pattern) { using StreamReader reader = new(path); return Regex.IsMatch(await reader.ReadToEndAsync(), pattern); } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { public const string AssetName = "TrxTest"; public const string AssetNameUsingMSTest = "TrxTestUsingMSTest"; @@ -211,45 +215,73 @@ public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : Test - - - - - #file Program.cs -using TrxTest; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); -builder.AddCrashDumpProvider(); -builder.AddTrxReportProvider(); -using ITestApplication app = await builder.BuildAsync(); -return await app.RunAsync(); +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(); + using ITestApplication app = await builder.BuildAsync(); + return await app.RunAsync(); + } +} -#file UnitTest1.cs -namespace TrxTest; +public class TrxReportCapability : ITrxReportCapability +{ + bool ITrxReportCapability.IsSupported { get; } = true; + void ITrxReportCapability.Enable() + { + } +} -[TestGroup] -public class UnitTest1 +public class DummyTestFramework : ITestFramework, IDataProducer { - public void TestMethod1() + 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) { if (Environment.GetEnvironmentVariable("CRASHPROCESS") == "1") { Environment.FailFast("CRASHPROCESS"); } - Assert.IsTrue(true); + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, + new TestNode() { Uid = "0", DisplayName = "Test", Properties = new(PassedTestNodeStateProperty.CachedInstance) })); + context.Complete(); } } - -#file Usings.cs -global using System; -global using Microsoft.Testing.Platform.Builder; -global using Microsoft.Testing.Internal.Framework; -global using Microsoft.Testing.Extensions; """; private const string MSTestCode = """ @@ -263,6 +295,7 @@ public void TestMethod1() true preview true + false @@ -311,8 +344,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/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs index 209cd2005f..bf567aee73 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/UnhandledExceptionPolicyTests.cs @@ -1,21 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - -using Microsoft.Testing.Platform.Acceptance.IntegrationTests.Helpers; -using Microsoft.Testing.Platform.Helpers; - namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests; -[TestGroup] -public class UnhandledExceptionPolicyTests : AcceptanceTestBase +[TestClass] +public class UnhandledExceptionPolicyTests : AcceptanceTestBase { - private readonly TestAssetFixture _testAssetFixture; - - public UnhandledExceptionPolicyTests(ITestExecutionContext testExecutionContext, TestAssetFixture testAssetFixture) - : base(testExecutionContext) => _testAssetFixture = testAssetFixture; - public enum Mode { Enabled, @@ -25,22 +15,23 @@ public enum Mode Default, } - internal static IEnumerable> ModeProvider() + internal static IEnumerable<(Mode Mode, string Arguments)> ModeProvider() { - foreach (TestArgumentsEntry tfm in TargetFrameworks.All) + foreach (string tfm in TargetFrameworks.All) { - yield return new TestArgumentsEntry<(Mode, string)>((Mode.Enabled, tfm.Arguments), $"Enabled - {tfm.Arguments}"); - yield return new TestArgumentsEntry<(Mode, string)>((Mode.Disabled, tfm.Arguments), $"Disabled - {tfm.Arguments}"); - yield return new TestArgumentsEntry<(Mode, string)>((Mode.DisabledByEnvironmentVariable, tfm.Arguments), $"DisabledByEnvironmentVariable - {tfm.Arguments}"); - yield return new TestArgumentsEntry<(Mode, string)>((Mode.EnabledByEnvironmentVariable, tfm.Arguments), $"EnabledByEnvironmentVariable - {tfm.Arguments}"); - yield return new TestArgumentsEntry<(Mode, string)>((Mode.Default, tfm.Arguments), $"Default - ({tfm.Arguments})"); + yield return new(Mode.Enabled, tfm); + yield return new(Mode.Disabled, tfm); + yield return new(Mode.DisabledByEnvironmentVariable, tfm); + yield return new(Mode.EnabledByEnvironmentVariable, tfm); + yield return new(Mode.Default, tfm); } } - [ArgumentsProvider(nameof(ModeProvider))] + [DynamicData(nameof(ModeProvider), DynamicDataSourceType.Method)] + [TestMethod] public async Task UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_ShouldCrashProcessIfEnabled(Mode mode, string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "UnhandledExceptionPolicyTests", tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "UnhandledExceptionPolicyTests", tfm); using TempDirectory clone = new(); await clone.CopyDirectoryAsync(testHost.DirectoryName, clone.Path, retainAttributes: !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); testHost = TestInfrastructure.TestHost.LocateFrom(clone.Path, "UnhandledExceptionPolicyTests"); @@ -93,10 +84,11 @@ public async Task UnhandledExceptionPolicy_ConfigFile_UnobservedTaskException_Sh } } - [ArgumentsProvider(nameof(ModeProvider))] + [DynamicData(nameof(ModeProvider), DynamicDataSourceType.Method)] + [TestMethod] public async Task UnhandledExceptionPolicy_EnvironmentVariable_UnhandledException_ShouldCrashProcessIfEnabled(Mode mode, string tfm) { - var testHost = TestInfrastructure.TestHost.LocateFrom(_testAssetFixture.TargetAssetPath, "UnhandledExceptionPolicyTests", tfm); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, "UnhandledExceptionPolicyTests", tfm); using TempDirectory clone = new(); await clone.CopyDirectoryAsync(testHost.DirectoryName, clone.Path, retainAttributes: !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); testHost = TestInfrastructure.TestHost.LocateFrom(clone.Path, "UnhandledExceptionPolicyTests"); @@ -151,8 +143,7 @@ public async Task UnhandledExceptionPolicy_EnvironmentVariable_UnhandledExceptio } } - [TestFixture(TestFixtureSharingStrategy.PerTestGroup)] - public sealed class TestAssetFixture(AcceptanceFixture acceptanceFixture) : TestAssetFixtureBase(acceptanceFixture.NuGetGlobalPackagesFolder) + public sealed class TestAssetFixture() : TestAssetFixtureBase(AcceptanceFixture.NuGetGlobalPackagesFolder) { private const string AssetName = "UnhandledExceptionPolicyTests"; @@ -208,21 +199,21 @@ public class Startup public static async Task Main(string[] args) { ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); - builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestAdapter()); + builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_,__) => new DummyTestFramework()); using ITestApplication app = await builder.BuildAsync(); return await app.RunAsync(); } } -public class DummyTestAdapter : ITestFramework, IDataProducer +public class DummyTestFramework : ITestFramework, IDataProducer { - public string Uid => nameof(DummyTestAdapter); + public string Uid => nameof(DummyTestFramework); public string Version => "2.0.0"; - public string DisplayName => nameof(DummyTestAdapter); + public string DisplayName => nameof(DummyTestFramework); - public string Description => nameof(DummyTestAdapter); + public string Description => nameof(DummyTestFramework); public Task IsEnabledAsync() => Task.FromResult(true); diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs index b49d6d337b..51ed1bd2b4 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; -using System.Xml; - using FluentAssertions; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -24,7 +21,7 @@ public class DesktopTestSourceHostTests : TestContainer public void ParentDomainShouldHonorSearchDirectoriesSpecifiedInRunsettings() { string sampleProjectDirPath = Path.GetDirectoryName(GetTestAssemblyPath("SampleProjectForAssemblyResolution")); - string runSettingXml = + string runSettingsXml = $""" @@ -42,7 +39,7 @@ public void ParentDomainShouldHonorSearchDirectoriesSpecifiedInRunsettings() _testSourceHost = new TestSourceHost( GetTestAssemblyPath("DesktopTestProjectx86Debug"), - GetMockedIRunSettings(runSettingXml).Object, + GetMockedIRunSettings(runSettingsXml).Object, null); _testSourceHost.SetupHost(); @@ -55,7 +52,7 @@ public void ChildDomainResolutionPathsShouldHaveSearchDirectoriesSpecifiedInRuns { string sampleProjectPath = GetTestAssemblyPath("SampleProjectForAssemblyResolution"); string sampleProjectDirPath = Path.GetDirectoryName(sampleProjectPath); - string runSettingXml = + string runSettingsXml = $""" @@ -73,7 +70,7 @@ public void ChildDomainResolutionPathsShouldHaveSearchDirectoriesSpecifiedInRuns _testSourceHost = new TestSourceHost( GetTestAssemblyPath("DesktopTestProjectx86Debug"), - GetMockedIRunSettings(runSettingXml).Object, + GetMockedIRunSettings(runSettingsXml).Object, null); _testSourceHost.SetupHost(); @@ -82,7 +79,7 @@ public void ChildDomainResolutionPathsShouldHaveSearchDirectoriesSpecifiedInRuns // Creating instance of SampleProjectForAssemblyResolution should not throw. // It is present in specified in runsettings - AppDomainUtilities.CreateInstance(_testSourceHost.AppDomain, type, null); + AppDomainUtilities.CreateInstance(_testSourceHost.AppDomain!, type, null); } public void DisposeShouldUnloadChildAppDomain() @@ -130,12 +127,12 @@ private static string GetTestAssemblyPath(string assetName) return testAssetPath; } - private static Mock GetMockedIRunSettings(string runSettingXml) + private static Mock GetMockedIRunSettings(string runSettingsXml) { var mockRunSettings = new Mock(); - mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingXml); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); - StringReader stringReader = new(runSettingXml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); MSTestSettingsProvider mstestSettingsProvider = new(); reader.ReadToFollowing("MSTestV2"); diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj index 6a81eaca13..be8d574df6 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj @@ -8,6 +8,10 @@ + + Analyzer + false + diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs index a77daa42fd..7fc3fc6a6b 100644 --- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs +++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/ReflectionUtilityTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Reflection; - using FluentAssertions; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; @@ -41,7 +38,7 @@ public ReflectionUtilityTests() public void GetCustomAttributesShouldReturnAllAttributes() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestBaseClass").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestBaseClass").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, false); @@ -54,7 +51,7 @@ public void GetCustomAttributesShouldReturnAllAttributes() public void GetCustomAttributesShouldReturnAllAttributesIgnoringBaseInheritance() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, false); @@ -67,7 +64,7 @@ public void GetCustomAttributesShouldReturnAllAttributesIgnoringBaseInheritance( public void GetCustomAttributesShouldReturnAllAttributesWithBaseInheritance() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, true); @@ -120,7 +117,7 @@ public void GetCustomAttributesOnTypeShouldReturnAllAttributesWithBaseInheritanc public void GetSpecificCustomAttributesShouldReturnAllAttributes() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestBaseClass").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestBaseClass").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, typeof(TestCategoryAttribute), false); @@ -133,7 +130,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributes() public void GetSpecificCustomAttributesShouldReturnAllAttributesIgnoringBaseInheritance() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, typeof(TestCategoryAttribute), false); @@ -147,7 +144,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIgnoringBaseInhe public void GetSpecificCustomAttributesShouldReturnAllAttributesWithBaseInheritance() { MethodInfo methodInfo = - _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1"); + _testAsset.GetType("TestProjectForDiscovery.AttributeTestClass").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, typeof(TestCategoryAttribute), true); @@ -160,7 +157,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesWithBaseInherita public void GetCustomAttributesShouldReturnAllAttributesIncludingUserDefinedAttributes() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClassWithCustomAttributes").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClassWithCustomAttributes").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, null, true); @@ -173,7 +170,7 @@ public void GetCustomAttributesShouldReturnAllAttributesIncludingUserDefinedAttr public void GetSpecificCustomAttributesShouldReturnAllAttributesIncludingUserDefinedAttributes() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClassWithCustomAttributes").GetMethod("DummyVTestMethod1"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClassWithCustomAttributes").GetMethod("DummyVTestMethod1")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, typeof(TestPropertyAttribute), true); @@ -186,7 +183,7 @@ public void GetSpecificCustomAttributesShouldReturnAllAttributesIncludingUserDef public void GetSpecificCustomAttributesShouldReturnArrayAttributesAsWell() { - MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClassWithCustomAttributes").GetMethod("DummyTestMethod2"); + MethodInfo methodInfo = _testAsset.GetType("TestProjectForDiscovery.AttributeTestClassWithCustomAttributes").GetMethod("DummyTestMethod2")!; IReadOnlyList attributes = ReflectionUtility.GetCustomAttributes(methodInfo, typeof(CategoryArrayAttribute), true); @@ -249,7 +246,7 @@ public void GetSpecificCustomAttributesOnAssemblyShouldReturnAllAttributes() GetAttributeValuePairs(attributes).Should().Equal(expectedAttributes); } - private Assembly ReflectionOnlyOnResolve(object sender, ResolveEventArgs args) + private static Assembly ReflectionOnlyOnResolve(object sender, ResolveEventArgs args) { string assemblyNameToLoad = AppDomain.CurrentDomain.ApplyPolicy(args.Name); diff --git a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_DerivedClass.cs b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_DerivedClass.cs index 08cbb82f2e..3bb8e29f39 100644 --- a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_DerivedClass.cs +++ b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_DerivedClass.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace DataRowTestProject; diff --git a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Enums.cs b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Enums.cs index b3ce205f65..24cdff7f1e 100644 --- a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Enums.cs +++ b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_Enums.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace DataRowTestProject; diff --git a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs index b26bc5a3a3..ac37d83d00 100644 --- a/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs +++ b/test/IntegrationTests/TestAssets/DataRowTestProject/DataRowTests_OverriddenGetDisplayName.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace DataRowAttributeTestProject; diff --git a/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs b/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs index aa2f02e272..bef73871c7 100644 --- a/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs +++ b/test/IntegrationTests/TestAssets/DiscoverInternalsProject/UnitTest1.cs @@ -56,7 +56,7 @@ internal sealed class SerializableInternalType; internal class DynamicDataTest { [DataTestMethod] - [DynamicData(nameof(DynamicData), DynamicDataSourceType.Property)] + [DynamicData(nameof(DynamicData))] internal void DynamicDataTestMethod(SerializableInternalType serializableInternalType) { } diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs index 98eda7ca9a..555530f5c6 100644 --- a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DataProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using LibProjectReferencedByDataSourceTest; namespace DynamicDataTestProject; diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs new file mode 100644 index 0000000000..c22a047e3f --- /dev/null +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DisableExpansionTests.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DynamicDataTestProject; + +[TestClass] +public sealed class DisableExpansionTests +{ + [TestMethod] + [DynamicData(nameof(PropertySource), UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + public void TestPropertySourceOnCurrentType(int a, string s) + { + } + + [TestMethod] + [DynamicData(nameof(MethodSource), DynamicDataSourceType.Method, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + public void TestMethodSourceOnCurrentType(int a, string s) + { + } + + [TestMethod] + [DynamicData(nameof(PropertySource), typeof(DataSourceHelper), UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + public void TestPropertySourceOnDifferentType(int a, string s) + { + } + + [TestMethod] + [DynamicData(nameof(MethodSource), typeof(DataSourceHelper), DynamicDataSourceType.Method, UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + public void TestMethodSourceOnDifferentType(int a, string s) + { + } + + [TestMethod] + [DynamicData(nameof(PropertySource), UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + [DynamicData(nameof(PropertySource), typeof(DataSourceHelper))] + public void TestPropertyWithTwoSourcesAndFirstDisablesExpansion(int a, string s) + { + } + + [TestMethod] + [DynamicData(nameof(PropertySource))] + [DynamicData(nameof(PropertySource), typeof(DataSourceHelper), UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)] + public void TestPropertyWithTwoSourcesAndSecondDisablesExpansion(int a, string s) + { + } + + private static IEnumerable PropertySource => MethodSource(); + + private static IEnumerable MethodSource() + { + yield return new object[] { 1, "a" }; + yield return new object[] { 2, "b" }; + } +} + +public class DataSourceHelper +{ + public static IEnumerable PropertySource => MethodSource(); + + public static IEnumerable MethodSource() + { + yield return new object[] { 3, "c" }; + yield return new object[] { 4, "d" }; + } +} diff --git a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs index f52f036ab7..2bd7372e62 100644 --- a/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs +++ b/test/IntegrationTests/TestAssets/DynamicDataTestProject/DynamicDataTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using DynamicDataTestProject; using LibProjectReferencedByDataSourceTest; @@ -14,25 +12,120 @@ namespace DataSourceTestProject; +public abstract class DynamicDataTestsBase +{ + public static IEnumerable GetDataFromBase() + { + yield return + [ + "John;Doe", + new User() + { + FirstName = "John", + LastName = "Doe", + } + ]; + + yield return + [ + "Jane;Doe", + new User() + { + FirstName = "Jane", + LastName = "Doe", + } + ]; + } + + public static IEnumerable DataFromBase + { + get + { + yield return + [ + "John;Doe", + new User() + { + FirstName = "John", + LastName = "Doe", + } + ]; + + yield return + [ + "Jane;Doe", + new User() + { + FirstName = "Jane", + LastName = "Doe", + } + ]; + } + } + + public static IEnumerable DataShadowingBase => throw new NotImplementedException(); + + public static IEnumerable GetDataShadowingBase() => throw new NotImplementedException(); +} + [TestClass] -public class DynamicDataTests +public class DynamicDataTests : DynamicDataTestsBase { [DataTestMethod] [DynamicData(nameof(GetParseUserData), DynamicDataSourceType.Method)] public void DynamicDataTest_SourceMethod(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + [DataTestMethod] + [DynamicData(nameof(GetDataFromBase), DynamicDataSourceType.Method)] + public void DynamicDataTest_SourceMethodFromBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(GetDataShadowingBase), DynamicDataSourceType.Method)] + public void DynamicDataTest_SourceMethodShadowingBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(GetParseUserData))] + public void DynamicDataTest_SourceMethodAuto(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(GetDataFromBase))] + public void DynamicDataTest_SourceMethodAutoFromBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(GetDataShadowingBase))] + public void DynamicDataTest_SourceMethodAutoShadowingBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + [DataTestMethod] [DynamicData(nameof(ParseUserData), DynamicDataSourceType.Property)] public void DynamicDataTest_SourceProperty(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + [DataTestMethod] + [DynamicData(nameof(DataFromBase), DynamicDataSourceType.Property)] + public void DynamicDataTest_SourcePropertyFromBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(DataShadowingBase), DynamicDataSourceType.Property)] + public void DynamicDataTest_SourcePropertyShadowingBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(ParseUserData))] + public void DynamicDataTest_SourcePropertyAuto(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(DataFromBase))] + public void DynamicDataTest_SourcePropertyAutoFromBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + + [DataTestMethod] + [DynamicData(nameof(DataShadowingBase))] + public void DynamicDataTest_SourcePropertyAutoShadowingBase(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); + [DataTestMethod] [DynamicData(nameof(GetParseUserData), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))] public void DynamicDataTest_SourceMethod_CustomDisplayName(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); [DataTestMethod] - [DynamicData(nameof(ParseUserData), DynamicDataSourceType.Property, - DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))] + [DynamicData(nameof(ParseUserData), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))] public void DynamicDataTest_SourceProperty_CustomDisplayName(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); [DataTestMethod] @@ -41,8 +134,7 @@ public class DynamicDataTests public void DynamicDataTest_SourceMethod_CustomDisplayNameOtherType(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); // todo [DataTestMethod] - [DynamicData(nameof(ParseUserData), DynamicDataSourceType.Property, - DynamicDataDisplayName = nameof(DataProvider.GetUserDynamicDataDisplayName), DynamicDataDisplayNameDeclaringType = typeof(DataProvider))] + [DynamicData(nameof(ParseUserData), DynamicDataDisplayName = nameof(DataProvider.GetUserDynamicDataDisplayName), DynamicDataDisplayNameDeclaringType = typeof(DataProvider))] public void DynamicDataTest_SourceProperty_CustomDisplayNameOtherType(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); // todo [DataTestMethod] @@ -50,7 +142,7 @@ public class DynamicDataTests public void DynamicDataTest_SourceMethodOtherType(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); [DataTestMethod] - [DynamicData(nameof(DataProvider.UserDataAndExceptedParsedUser), typeof(DataProvider), DynamicDataSourceType.Property)] + [DynamicData(nameof(DataProvider.UserDataAndExceptedParsedUser), typeof(DataProvider))] public void DynamicDataTest_SourcePropertyOtherType(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); [DataTestMethod] @@ -59,7 +151,7 @@ public class DynamicDataTests public void DynamicDataTest_SourceMethodOtherType_CustomDisplayName(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); [DataTestMethod] - [DynamicData(nameof(DataProvider.UserDataAndExceptedParsedUser), typeof(DataProvider), DynamicDataSourceType.Property, + [DynamicData(nameof(DataProvider.UserDataAndExceptedParsedUser), typeof(DataProvider), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))] public void DynamicDataTest_SourcePropertyOtherType_CustomDisplayName(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); @@ -69,7 +161,7 @@ public class DynamicDataTests public void DynamicDataTest_SourceMethodOtherType_CustomDisplayNameOtherType(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); [DataTestMethod] - [DynamicData(nameof(DataProvider.UserDataAndExceptedParsedUser), typeof(DataProvider), DynamicDataSourceType.Property, + [DynamicData(nameof(DataProvider.UserDataAndExceptedParsedUser), typeof(DataProvider), DynamicDataDisplayName = nameof(DataProvider.GetUserDynamicDataDisplayName), DynamicDataDisplayNameDeclaringType = typeof(DataProvider))] public void DynamicDataTest_SourcePropertyOtherType_CustomDisplayNameOtherType(string userData, User expectedUser) => ParseAndAssert(userData, expectedUser); @@ -94,6 +186,11 @@ public void MethodWithOverload(int x, string y) { } + [TestMethod] + [DynamicData(nameof(SimpleCollection))] + public void DynamicDataTest_SimpleCollection(int value) + => Assert.AreEqual(0, value % 2); + private static void ParseAndAssert(string userData, User expectedUser) { // Prepare @@ -108,54 +205,13 @@ private static void ParseAndAssert(string userData, User expectedUser) Assert.AreEqual(user.LastName, expectedUser.LastName); } - public static IEnumerable GetParseUserData() - { - yield return - [ - "John;Doe", - new User() - { - FirstName = "John", - LastName = "Doe", - } - ]; + public static new IEnumerable GetDataShadowingBase() => GetDataFromBase(); - yield return - [ - "Jane;Doe", - new User() - { - FirstName = "Jane", - LastName = "Doe", - } - ]; - } + public static IEnumerable GetParseUserData() => GetDataFromBase(); - public static IEnumerable ParseUserData - { - get - { - yield return - [ - "John;Doe", - new User() - { - FirstName = "John", - LastName = "Doe", - } - ]; + public static IEnumerable ParseUserData => DataFromBase; - yield return - [ - "Jane;Doe", - new User() - { - FirstName = "Jane", - LastName = "Doe", - } - ]; - } - } + public static new IEnumerable DataShadowingBase => DataFromBase; public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data) => $"Custom DynamicDataTestMethod {methodInfo.Name} with {data.Length} parameters"; @@ -206,4 +262,14 @@ private static IEnumerable Int32AndString() yield return new object[] { 1, "0" }; yield return new object[] { 2, "2" }; } + + private static IEnumerable SimpleCollection + { + get + { + yield return 0; + yield return 2; + yield return 4; + } + } } diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/AssertExTest.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/AssertExTest.cs index d48bd50e3d..37e0f8709f 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/AssertExTest.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/AssertExTest.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.CompilerServices; - using Microsoft.VisualStudio.TestTools.UnitTesting; using MSTest.Extensibility.Samples; diff --git a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs index b9a171c4b1..17e0c80ab6 100644 --- a/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs +++ b/test/IntegrationTests/TestAssets/FxExtensibilityTestProject/TestDataSourceExTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace FxExtensibilityTestProject; @@ -13,13 +10,27 @@ public class TestDataSourceExTests { [TestMethod] [CustomTestDataSource] - [CustomEmptyTestDataSource] public void CustomTestDataSourceTestMethod1(int a, int b, int c) { Assert.AreEqual(1, a % 3); Assert.AreEqual(2, b % 3); Assert.AreEqual(0, c % 3); } + + [TestMethod] + [CustomDisableExpansionTestDataSource] + public void CustomDisableExpansionTestDataSourceTestMethod1(int a, int b, int c) + { + } + + [TestMethod] + [CustomEmptyTestDataSource] + public void CustomEmptyTestDataSourceTestMethod(int a, int b, int c) + { + Assert.AreEqual(1, a % 3); + Assert.AreEqual(2, b % 3); + Assert.AreEqual(0, c % 3); + } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] @@ -37,3 +48,13 @@ public class CustomEmptyTestDataSourceAttribute : Attribute, ITestDataSource public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data)) : null; } + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class CustomDisableExpansionTestDataSourceAttribute : Attribute, ITestDataSource, ITestDataSourceUnfoldingCapability +{ + public TestDataSourceUnfoldingStrategy UnfoldingStrategy => TestDataSourceUnfoldingStrategy.Fold; + + public IEnumerable GetData(MethodInfo methodInfo) => [[1, 2, 3], [4, 5, 6]]; + + public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data)) : null; +} diff --git a/test/IntegrationTests/TestAssets/HierarchyProject/ClassWithNoNamespace.cs b/test/IntegrationTests/TestAssets/HierarchyProject/ClassWithNoNamespace.cs index 0e6cf14e52..c83e67d045 100644 --- a/test/IntegrationTests/TestAssets/HierarchyProject/ClassWithNoNamespace.cs +++ b/test/IntegrationTests/TestAssets/HierarchyProject/ClassWithNoNamespace.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/test/IntegrationTests/TestAssets/OutputTestProject/Assembly.cs b/test/IntegrationTests/TestAssets/OutputTestProject/Assembly.cs index 5a096fb3d6..1b0e0b0cb6 100644 --- a/test/IntegrationTests/TestAssets/OutputTestProject/Assembly.cs +++ b/test/IntegrationTests/TestAssets/OutputTestProject/Assembly.cs @@ -3,5 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + [assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] [assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] diff --git a/test/IntegrationTests/TestAssets/OutputTestProject/OutputTestProject.csproj b/test/IntegrationTests/TestAssets/OutputTestProject/OutputTestProject.csproj index a7ac324043..fd8f8d9281 100644 --- a/test/IntegrationTests/TestAssets/OutputTestProject/OutputTestProject.csproj +++ b/test/IntegrationTests/TestAssets/OutputTestProject/OutputTestProject.csproj @@ -7,4 +7,5 @@ + diff --git a/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest1.cs b/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest1.cs index 247876bdea..db917d4c97 100644 --- a/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest1.cs +++ b/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest1.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace OutputTestProject; diff --git a/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest2.cs b/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest2.cs index 3d7198907a..2d3d87ed80 100644 --- a/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest2.cs +++ b/test/IntegrationTests/TestAssets/OutputTestProject/UnitTest2.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace OutputTestProject; diff --git a/test/IntegrationTests/TestAssets/ParallelTestClass/Constants.cs b/test/IntegrationTests/TestAssets/ParallelTestClass/Constants.cs index 8f7f36363f..25055c42e0 100644 --- a/test/IntegrationTests/TestAssets/ParallelTestClass/Constants.cs +++ b/test/IntegrationTests/TestAssets/ParallelTestClass/Constants.cs @@ -4,6 +4,8 @@ // Parallel configuration using Microsoft.VisualStudio.TestTools.UnitTesting; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + [assembly: Parallelize(Workers = 2, Scope = ExecutionScope.ClassLevel)] namespace ParallelClassesTestProject; diff --git a/test/IntegrationTests/TestAssets/ParallelTestMethods/Constants.cs b/test/IntegrationTests/TestAssets/ParallelTestMethods/Constants.cs index b14568538e..8aa87bbf69 100644 --- a/test/IntegrationTests/TestAssets/ParallelTestMethods/Constants.cs +++ b/test/IntegrationTests/TestAssets/ParallelTestMethods/Constants.cs @@ -4,6 +4,8 @@ // Parallel configuration using Microsoft.VisualStudio.TestTools.UnitTesting; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + [assembly: Parallelize(Workers = 2, Scope = ExecutionScope.MethodLevel)] namespace ParallelMethodsTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleAssemblyInitializeAndCleanup.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleAssemblyInitializeAndCleanup.cs index 072bcf6331..c2a6b40a74 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleAssemblyInitializeAndCleanup.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleAssemblyInitializeAndCleanup.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanup.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanup.cs index a9cada8692..b191f7c8d1 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanup.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanup.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssembly.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssembly.cs index ee640bb493..6d584cdb5a 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssembly.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssembly.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs index ae8988aeef..0e45d85674 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndNone.cs index 504b64beec..98055d013c 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfAssemblyAndNone.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClass.cs index 20f9522f73..eb43aa7ac3 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClass.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.cs index d806abf283..5d9bff160e 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndBeforeEachDerivedClass.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndNone.cs index fb41374bd0..3962444cde 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassCleanupEndOfClassAndNone.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.cs index be26e3774a..3027b699ce 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupBeforeEachDerivedClass.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupNone.cs index c3d5f362da..98db71eb24 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeAndCleanupNone.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs index 228a1fbe5d..e72ec2729d 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs index 17c99946f9..dbbd039655 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs index efe957ff83..7e1a4d3eed 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndBeforeEachDerivedClass.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.cs index 591159cb6c..7d99058862 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfAssemblyAndNone.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.cs index 2c621aea2e..b4147a157c 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndBeforeEachDerivedClass.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndNone.cs index 9767ec81d6..12648249fc 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassCleanupEndOfClassAndNone.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.cs index e239ea472e..5669769de1 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupBeforeEachDerivedClass.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupNone.cs index c4c53901fa..11749707a8 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeAndCleanupNone.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs index c97009c8be..3f83272a39 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeBeforeEachDerivedClassAndClassCleanupNone.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs index 9087f8b2f0..fcb2b08bed 100644 --- a/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs +++ b/test/IntegrationTests/TestAssets/SuiteLifeCycleTestProject/LifeCycleDerivedClassInitializeNoneAndClassCleanupBeforeEachDerivedClass.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SuiteLifeCycleTestProject; diff --git a/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs b/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs index 7ef1d44620..351e04be54 100644 --- a/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs +++ b/test/IntegrationTests/TestAssets/TestIdProject.DefaultStrategy/TestIdCases.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; #if LEGACY_TEST_ID diff --git a/test/IntegrationTests/TestAssets/TestProject/Properties/AssemblyInfo.cs b/test/IntegrationTests/TestAssets/TestProject/Properties/AssemblyInfo.cs index d413c515f1..e73e5926fa 100644 --- a/test/IntegrationTests/TestAssets/TestProject/Properties/AssemblyInfo.cs +++ b/test/IntegrationTests/TestAssets/TestProject/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.VisualStudio.TestTools.UnitTesting; [assembly: TestCategory("a1")] diff --git a/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj b/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj index fba56e108c..eb2306c412 100644 --- a/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj +++ b/test/Performance/MSTest.Performance.Runner/MSTest.Performance.Runner.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable diff --git a/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs b/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs index de01ee3219..bcc2153539 100644 --- a/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs +++ b/test/Performance/MSTest.Performance.Runner/PipelinesRunner.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - using Microsoft.Extensions.FileSystemGlobbing; namespace MSTest.Performance.Runner; @@ -43,9 +41,9 @@ public int Run(string pipelineNameFilter, IDictionary? parameter { "PipelineName", pipeline.PipelineName }, }; - foreach (KeyValuePair item in parametersBag) + foreach ((string key, object value) in parametersBag) { - pipelinePropertyBag.Add(item.Key, item.Value); + pipelinePropertyBag.Add(key, value); } pipeline.UpdatePropertyBag?.Invoke(pipelinePropertyBag); diff --git a/test/Performance/MSTest.Performance.Runner/Program.cs b/test/Performance/MSTest.Performance.Runner/Program.cs index a5e94f2991..3237deeb73 100644 --- a/test/Performance/MSTest.Performance.Runner/Program.cs +++ b/test/Performance/MSTest.Performance.Runner/Program.cs @@ -2,13 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.CommandLine; -using System.Runtime.InteropServices; using Microsoft.Testing.TestInfrastructure; using MSTest.Performance.Runner.Steps; using DotnetMuxer = MSTest.Performance.Runner.Steps.DotnetMuxer; +using ExecutionScope = MSTest.Performance.Runner.Steps.ExecutionScope; namespace MSTest.Performance.Runner; @@ -22,7 +22,7 @@ public static Task Main(string[] args) Console.WriteLine("Microsoft (R) MSTest Performance Profiler Command Line Tool"); var rootCommand = new RootCommand("MSTest Performance Profiler Command Line Tool"); - var pipelineNameFilter = new Option(name: "--pipelineNameFilter", description: "Globbing filter for the pipeline name to execute.", getDefaultValue: () => string.Empty); + var pipelineNameFilter = new Option(name: "--pipelineNameFilter", description: "Globing filter for the pipeline name to execute.", getDefaultValue: () => string.Empty); var executeTests = new Command("execute", "Execute the performance scenarios.") { pipelineNameFilter, @@ -41,7 +41,7 @@ private static int Pipelines(string pipelineNameFilter) pipelineRunner.AddPipeline("Default", "Scenario1_PerfView", [OSPlatform.Windows], parametersBag => Pipeline - .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) + .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net9.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new PerfviewRunner(" /BufferSizeMB:1024 ", "Scenario1_PerfView.zip", includeScenario: true)) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) @@ -49,7 +49,7 @@ private static int Pipelines(string pipelineNameFilter) pipelineRunner.AddPipeline("Default", "Scenario1_DotnetTrace", [OSPlatform.Windows], parametersBag => Pipeline - .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) + .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net9.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new DotnetTrace("--profile cpu-sampling", "DotnetTrace_CPU_Sampling.zip")) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) @@ -58,14 +58,14 @@ private static int Pipelines(string pipelineNameFilter) // C:\Program Files\Microsoft Visual Studio\2022\Preview\Team Tools\DiagnosticsHub\Collector\AgentConfigs pipelineRunner.AddPipeline("Default", "Scenario1_DotNetObjectAllocBase", [OSPlatform.Windows], parametersBag => Pipeline - .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) + .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net9.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new VSDiagnostics("DotNetObjectAllocLow.json", "Scenario1_DotNetObjectAllocBase.zip")) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) .NextStep(() => new CleanupDisposable())); pipelineRunner.AddPipeline("Default", "Scenario1_CpuUsageLow", [OSPlatform.Windows], parametersBag => Pipeline - .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) + .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net9.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new VSDiagnostics("CpuUsageHigh.json", "Scenario1_CpuUsageLow.zip")) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) @@ -73,7 +73,7 @@ private static int Pipelines(string pipelineNameFilter) pipelineRunner.AddPipeline("Default", "Scenario1_ConcurrencyVisualizer", [OSPlatform.Windows], parametersBag => Pipeline - .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) + .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net9.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new ConcurrencyVisualizer("Scenario1_ConcurrencyVisualizer.zip")) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) @@ -81,7 +81,7 @@ private static int Pipelines(string pipelineNameFilter) pipelineRunner.AddPipeline("Default", "Scenario1_PlainProcess", [OSPlatform.Windows], parametersBag => Pipeline - .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net8.0", executionScope: ExecutionScope.MethodLevel), parametersBag) + .FirstStep(() => new Scenario1(numberOfClass: 100, methodsPerClass: 100, tfm: "net9.0", executionScope: ExecutionScope.MethodLevel), parametersBag) .NextStep(() => new DotnetMuxer(BuildConfiguration.Debug)) .NextStep(() => new PlainProcess("Scenario1_PlainProcess.zip")) .NextStep(() => new MoveFiles("*.zip", Path.Combine(Directory.GetCurrentDirectory(), "Results"))) diff --git a/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs b/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs index 33768cd2dd..749bfe6c3c 100644 --- a/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs +++ b/test/Performance/MSTest.Performance.Runner/Scenarios/Scenario1.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; -using System.Xml.Linq; - using Microsoft.Testing.TestInfrastructure; namespace MSTest.Performance.Runner.Steps; @@ -106,7 +102,7 @@ public class UnitTest{{i}} addPublicFeeds: true); context.AddDisposable(generator); - return new SingleProject(["net8.0"], generator, nameof(Scenario1)); + return new SingleProject(["net9.0"], generator, nameof(Scenario1)); } private static string ExtractVersionFromPackage(string rootFolder, string packagePrefixName) diff --git a/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs b/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs index 5d0b04ed23..f92666e68f 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/ConcurrencyVisualizer.cs @@ -1,11 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; using System.IO.Compression; -using System.Runtime.InteropServices; -using System.Text; namespace MSTest.Performance.Runner.Steps; diff --git a/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs b/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs index 70d7b95ceb..5742d70b20 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/DotnetTrace.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; using System.IO.Compression; -using System.Runtime.InteropServices; using Microsoft.Testing.TestInfrastructure; diff --git a/test/Performance/MSTest.Performance.Runner/Steps/Files.cs b/test/Performance/MSTest.Performance.Runner/Steps/Files.cs index ef9db35e37..f8e722e908 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/Files.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/Files.cs @@ -3,4 +3,4 @@ namespace MSTest.Performance.Runner.Steps; -internal record Files(string[] FilesCollection) : IPayload; +internal sealed record Files(string[] FilesCollection) : IPayload; diff --git a/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs b/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs index d45e8b6d17..a0d46e9866 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/PerfviewRunner.cs @@ -1,11 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; using System.IO.Compression; -using System.Runtime.InteropServices; -using System.Text; namespace MSTest.Performance.Runner.Steps; @@ -107,7 +103,7 @@ public async Task ExecuteAsync(BuildArtifact payload, IContext context) return new Files([sample]); } - private async Task PerfviewExecutable() + private static async Task PerfviewExecutable() { string localPath = Path.Combine(Path.GetTempPath(), "PerfView", "PerfView.exe"); Directory.CreateDirectory(Path.GetDirectoryName(localPath)!); diff --git a/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs b/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs index 6589d9f0d0..98c3708508 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/PlainProcess.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; using System.IO.Compression; using System.Text.Json; diff --git a/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs b/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs index ea821ecd39..6eb5ee9441 100644 --- a/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs +++ b/test/Performance/MSTest.Performance.Runner/Steps/VSDiagnostics.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; using System.IO.Compression; -using System.Runtime.InteropServices; namespace MSTest.Performance.Runner.Steps; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs index ad6c0b52bb..44aba361df 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyCleanupShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class AssemblyCleanupShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class AssemblyCleanupShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenAssemblyCleanupIsPublic_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public static void AssemblyCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyCleanIsInsideAGenericClass_Diagnostic() { string code = """ @@ -49,6 +51,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenAssemblyCleanupIsNotOrdinary_Diagnostic() { string code = """ @@ -70,6 +73,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenAssemblyCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ @@ -90,6 +94,7 @@ public static void AssemblyCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyCleanupIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() { string code = """ @@ -128,10 +133,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenAssemblyCleanupIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -166,6 +172,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyCleanupIsGeneric_Diagnostic() { string code = """ @@ -200,6 +207,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyCleanupIsNotStatic_Diagnostic() { string code = """ @@ -234,6 +242,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyCleanupHasParameters_Diagnostic() { string code = """ @@ -243,31 +252,16 @@ public async Task WhenAssemblyCleanupHasParameters_Diagnostic() public class MyTestClass { [AssemblyCleanup] - public static void {|#0:AssemblyCleanup|}(TestContext testContext) + public static void AssemblyCleanup(TestContext testContext) { } } """; - string fixedCode = """ - using Microsoft.VisualStudio.TestTools.UnitTesting; - - [TestClass] - public class MyTestClass - { - [AssemblyCleanup] - public static void AssemblyCleanup() - { - } - } - """; - - await VerifyCS.VerifyCodeFixAsync( - code, - VerifyCS.Diagnostic().WithLocation(0).WithArguments("AssemblyCleanup"), - fixedCode); + await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssemblyCleanupReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -375,6 +369,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyCleanupReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -406,6 +401,7 @@ public static ValueTask AssemblyCleanup2() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyCleanupIsAsyncVoid_Diagnostic() { string code = """ @@ -444,6 +440,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenMultipleViolations_TheyAllGetFixed() { string code = """ @@ -482,6 +479,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyCleanupIsNotOnClass_Diagnostic() { string code = """ @@ -499,6 +497,7 @@ public struct MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyCleanupIsOnClassNotMarkedWithTestClass_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs index b6d2a58b3f..e7ffa9a0fe 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssemblyInitializeShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class AssemblyInitializeShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class AssemblyInitializeShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenAssemblyInitializeIsPublic_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public static void AssemblyInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ @@ -48,6 +50,7 @@ public static void AssemblyInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyInitializeIsInsideAGenericClass_Diagnostic() { string code = """ @@ -69,6 +72,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenAssemblyInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() { string code = """ @@ -107,10 +111,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenAssemblyInitializeIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -145,6 +150,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyInitializeIsNotOrdinary_Diagnostic() { string code = """ @@ -166,6 +172,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenAssemblyInitializeIsGeneric_Diagnostic() { string code = """ @@ -200,6 +207,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyInitializeIsNotStatic_Diagnostic() { string code = """ @@ -234,6 +242,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyInitializeDoesNotHaveParameters_Diagnostic() { string code = """ @@ -268,6 +277,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyInitializeReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -371,6 +381,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyInitializeReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -402,6 +413,7 @@ public static ValueTask AssemblyInitialize2(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyInitializeIsAsyncVoid_Diagnostic() { string code = """ @@ -440,6 +452,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenMultipleViolations_TheyAllGetFixed() { string code = """ @@ -478,6 +491,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenAssemblyInitializeIsNotOnClass_Diagnostic() { string code = """ @@ -495,6 +509,7 @@ public struct MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssemblyInitializeIsOnClassNotMarkedWithTestClass_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs index f045776241..67f245a0d1 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldAvoidConditionalAccessAnalyzerTests.cs @@ -3,13 +3,14 @@ using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.AssertionArgsShouldAvoidConditionalAccessFixer>; namespace MSTest.Analyzers.UnitTests; -[TestGroup] -public sealed class AssertionArgsShouldAvoidConditionalAccessAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class AssertionArgsShouldAvoidConditionalAccessAnalyzerTests { + [TestMethod] public async Task WhenUsingConditionalsAccess_In_Assert_Equal() { string code = """ @@ -96,9 +97,109 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + #nullable enable + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + public List? T { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + A? a = new A(); + A b = new A(); + string? s = ""; + Assert.IsNotNull(s); + Assert.AreEqual(s.Length, 32); + Assert.AreEqual(((s.Length)), 32); + Assert.AreEqual(s.Length, s.Length); + Assert.IsNotNull(a); + Assert.IsNotNull(a.S); + Assert.AreEqual(a.S.Length, 32); + Assert.IsNotNull(b.S); + Assert.AreEqual(b.S.Length, 32); + Assert.IsNotNull(a.T); + Assert.IsNotNull(a.T[3]); + Assert.AreEqual(a.T[3].Length, 32); + Assert.IsNotNull(b.T); + Assert.IsNotNull(b.T[3]); + Assert.AreEqual(b.T[3].Length, 32); + + Assert.AreNotEqual(s.Length, 32); + Assert.AreNotEqual(((s.Length)), 32); + Assert.AreNotEqual(s.Length, s.Length); + Assert.AreNotEqual(a.S.Length, 32); + Assert.AreNotEqual(b.S.Length, 32); + Assert.AreNotEqual(a.T[3].Length, 32); + Assert.AreNotEqual(b.T[3].Length, 32); + + Assert.AreSame(s.Length, 32); + Assert.AreSame(((s.Length)), 32); + Assert.AreSame(s.Length, s.Length); + Assert.AreSame(a.S.Length, 32); + Assert.AreSame(b.S.Length, 32); + Assert.AreSame(a.T[3].Length, 32); + Assert.AreSame(b.T[3].Length, 32); + + Assert.AreNotSame(s.Length, 32); + Assert.AreNotSame(((s.Length)), 32); + Assert.AreNotSame(s.Length, s.Length); + Assert.AreNotSame(a.S.Length, 32); + Assert.AreNotSame(b.S.Length, 32); + Assert.AreNotSame(a.T[3].Length, 32); + Assert.AreNotSame(b.T[3].Length, 32); + } + + [TestMethod] + public void Compliant() + { + string? s = ""; + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(s); + Assert.IsNotNull(a); + Assert.IsNotNull(a.S); + Assert.IsNotNull(a.T); + Assert.IsNotNull(b); + Assert.IsNotNull(b.S); + Assert.IsNotNull(b.T); + Assert.AreEqual(s.Length, 32); + Assert.AreEqual(((s.Length)), 32); + Assert.AreEqual(s.Length, s.Length); + Assert.AreEqual(a.S.Length, 32); + Assert.AreEqual(b.S.Length, 32); + Assert.AreEqual(a.T[3].Length, 32); + Assert.AreEqual(b.T[3].Length, 32); + Assert.AreNotEqual(s.Length, 32); + Assert.AreNotEqual(((s.Length)), 32); + Assert.AreNotEqual(s.Length, s.Length); + Assert.AreNotEqual(a.S.Length, 32); + Assert.AreNotEqual(b.S.Length, 32); + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = fixedCode, + NumberOfFixAllIterations = 3, + NumberOfFixAllInDocumentIterations = 3, + NumberOfFixAllInProjectIterations = 3, + NumberOfIncrementalIterations = 48, + }.RunAsync(); } + [TestMethod] public async Task WhenUsingConditionalsAccess_In_Assert_Boolean() { string code = """ @@ -155,9 +256,75 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + #nullable enable + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + A? a = new A(); + A b = new A(); + string? s = ""; + Assert.IsNotNull(s); + Assert.IsTrue(s.Length > 32); + Assert.IsTrue((s.Length> 32)); + Assert.IsNotNull(a); + Assert.IsNotNull(a.S); + Assert.IsTrue(a.S.Length > 32); + Assert.IsNotNull(b.S); + Assert.IsTrue(b.S.Length > 32); + Assert.IsFalse(s.Length > 32); + Assert.IsFalse((s.Length > 32)); + Assert.IsFalse(a.S.Length > 32); + Assert.IsFalse(b.S.Length > 32); + } + + [TestMethod] + public void Compliant() + { + string? s = ""; + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(s); + Assert.IsNotNull(a); + Assert.IsNotNull(a.S); + Assert.IsNotNull(b); + Assert.IsNotNull(b.S); + Assert.IsTrue(s.Length > 32); + Assert.IsTrue((s.Length > 32)); + Assert.IsTrue(a.S.Length > 32); + Assert.IsTrue(b.S.Length > 32); + Assert.IsFalse(s.Length > 32); + Assert.IsFalse((s.Length > 32)); + Assert.IsFalse(a.S.Length > 32); + Assert.IsFalse(b.S.Length > 32); + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = fixedCode, + NumberOfFixAllIterations = 2, + NumberOfFixAllInDocumentIterations = 2, + NumberOfFixAllInProjectIterations = 2, + NumberOfIncrementalIterations = 10, + }.RunAsync(); } + [TestMethod] public async Task WhenUsingConditionalsAccess_In_NullAssertions_Gives_NoWarning() { string code = """ @@ -184,9 +351,10 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenUsingConditionalsAccess_In_CollectionAssert() { string code = """ @@ -255,9 +423,84 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + #nullable enable + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public List? S { get; set; } + public Type? T { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + A? a = new A(); + A b = new A(); + Assert.IsNotNull(a); + CollectionAssert.AreEqual(a.S, b.S); + CollectionAssert.AreEqual(a.S, a.S); + CollectionAssert.AreEqual(b.S, a.S); + CollectionAssert.AreNotEqual(a.S, b.S); + CollectionAssert.AreNotEqual(a.S, a.S); + CollectionAssert.AreNotEqual(b.S, a.S); + CollectionAssert.Contains(a.S, a); + CollectionAssert.Contains(b.S, a.S); + CollectionAssert.Contains(a.S, a.S); + CollectionAssert.DoesNotContain(a.S, a); + CollectionAssert.DoesNotContain(b.S, a.S); + CollectionAssert.DoesNotContain(a.S, a.S); + CollectionAssert.AllItemsAreNotNull(a.S); + CollectionAssert.AllItemsAreUnique(a.S); + CollectionAssert.IsSubsetOf(a.S, b.S); + CollectionAssert.IsSubsetOf(a.S, a.S); + CollectionAssert.IsSubsetOf(b.S, a.S); + CollectionAssert.IsNotSubsetOf(a.S, b.S); + CollectionAssert.IsNotSubsetOf(a.S, a.S); + CollectionAssert.IsNotSubsetOf(b.S, a.S); + CollectionAssert.AllItemsAreInstancesOfType(a.S, b.T); + CollectionAssert.AllItemsAreInstancesOfType(a.S, a.T); + CollectionAssert.AllItemsAreInstancesOfType(b.S, a.T); + } + + [TestMethod] + public void Compliant() + { + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(a); + CollectionAssert.AreEqual(a.S, b.S); + CollectionAssert.AreNotEqual(a.S, b.S); + CollectionAssert.Contains(a.S, a); + CollectionAssert.DoesNotContain(a.S, a); + CollectionAssert.AllItemsAreNotNull(a.S); + CollectionAssert.AllItemsAreUnique(a.S); + CollectionAssert.IsSubsetOf(a.S, b.S); + CollectionAssert.IsNotSubsetOf(a.S, b.S); + CollectionAssert.AllItemsAreInstancesOfType(a.S, a.T); + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = fixedCode, + NumberOfFixAllIterations = 2, + NumberOfFixAllInDocumentIterations = 2, + NumberOfFixAllInProjectIterations = 2, + NumberOfIncrementalIterations = 30, + }.RunAsync(); } + [TestMethod] public async Task WhenUsingConditionalsAccess_In_StringAssert() { string code = """ @@ -328,6 +571,190 @@ public void Compliant() } """; - await VerifyCS.VerifyAnalyzerAsync(code); + string fixedCode = """ + #nullable enable + using System.Text.RegularExpressions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + public Regex? R { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + string s = ""; + Regex r = new Regex(""); + A? a = new A(); + A b = new A(); + Assert.IsNotNull(a); + StringAssert.Contains(a.S, s); + StringAssert.Contains(a.S, a.S); + StringAssert.Contains(b.S, a.S); + StringAssert.StartsWith(a.S, s); + StringAssert.StartsWith(a.S, a.S); + StringAssert.StartsWith(s, a.S); + StringAssert.EndsWith(a.S, s); + StringAssert.EndsWith(a.S, a.S); + StringAssert.EndsWith(s, a.S); + StringAssert.Matches(a.S, r); + StringAssert.Matches(a.S, a.R); + StringAssert.Matches(s, a.R); + StringAssert.DoesNotMatch(a.S, r); + StringAssert.DoesNotMatch(a.S, a.R); + StringAssert.DoesNotMatch(s, a.R); + } + + [TestMethod] + public void Compliant() + { + string s = ""; + Regex r = new Regex(""); + A? a = new A(); + A b = new A(); + + Assert.IsNotNull(a); + StringAssert.Contains(a.S, s); + StringAssert.Contains(a.S, a.S); + StringAssert.Contains(b.S, a.S); + StringAssert.StartsWith(a.S, s); + StringAssert.StartsWith(a.S, a.S); + StringAssert.StartsWith(s, a.S); + StringAssert.EndsWith(a.S, s); + StringAssert.EndsWith(a.S, a.S); + StringAssert.EndsWith(s, a.S); + StringAssert.Matches(a.S, r); + StringAssert.Matches(a.S, a.R); + StringAssert.Matches(s, a.R); + StringAssert.DoesNotMatch(a.S, r); + StringAssert.DoesNotMatch(a.S, a.R); + StringAssert.DoesNotMatch(s, a.R); + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = fixedCode, + NumberOfFixAllIterations = 2, + NumberOfFixAllInDocumentIterations = 2, + NumberOfFixAllInProjectIterations = 2, + NumberOfIncrementalIterations = 20, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenExpressionBody() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class MyClass + { + public MyClass A { get; set; } + public MyClass B { get; set; } + public MyClass C { get; set; } + public MyClass D { get; set; } + public MyClass E { get; set; } + public MyClass F { get; set; } + + public bool MyBool { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void Case1() // NOTE: No easy way for us to fix this properly + => [|Assert.IsTrue(new MyClass().A?.B.C?.D.E.MyBool)|]; + + [TestMethod] + public void Case2() // NOTE: No easy way for us to fix this properly + => [|Assert.IsTrue(new MyClass().A?.B.C.D.E.MyBool)|]; + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class MyClass + { + public MyClass A { get; set; } + public MyClass B { get; set; } + public MyClass C { get; set; } + public MyClass D { get; set; } + public MyClass E { get; set; } + public MyClass F { get; set; } + + public bool MyBool { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void Case1() // NOTE: No easy way for us to fix this properly + { + Assert.IsNotNull(new MyClass().A); + Assert.IsNotNull(new MyClass().A.B.C); + Assert.IsTrue(new MyClass().A.B.C.D.E.MyBool); + } + + [TestMethod] + public void Case2() // NOTE: No easy way for us to fix this properly + { + Assert.IsNotNull(new MyClass().A); + Assert.IsTrue(new MyClass().A.B.C.D.E.MyBool); + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = fixedCode, + NumberOfFixAllIterations = 2, + NumberOfFixAllInDocumentIterations = 2, + NumberOfFixAllInProjectIterations = 2, + NumberOfIncrementalIterations = 3, + }.RunAsync(); + } + + [TestMethod] + public async Task WhenUsingConditionalsAccess_In_Message_NoDiagnostic() + { + string code = """ + #nullable enable + using System.Text.RegularExpressions; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + public class A + { + public string? S { get; set; } + public Regex? R { get; set; } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void Compliant() + { + Assert.AreEqual(new object(), new object(), new A().S?.Length.ToString()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs index 413be9c844..7be33c066d 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.UnitTests; -[TestGroup] -public sealed class AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class AssertionArgsShouldBePassedInCorrectOrderAnalyzerTests { + [TestMethod] public async Task WhenUsingLiterals() { string code = """ @@ -161,6 +162,29 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] + public async Task WhenBothAreLiterals_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void NonCompliant() + { + Assert.AreEqual(0, 0); + Assert.AreEqual(0, 1); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] public async Task LiteralUsingNamedArgument() { string code = """ @@ -292,6 +316,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task ConstantValue() { string code = """ @@ -437,6 +462,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task ActualAsLocalVariableOrNot() { string code = """ @@ -501,6 +527,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task ActualOrExpectedPrefix() { string code = """ @@ -702,6 +729,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task MethodCalls() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertAreSameWithValueTypesAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertAreSameWithValueTypesAnalyzerTests.cs new file mode 100644 index 0000000000..c5910bc905 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertAreSameWithValueTypesAnalyzerTests.cs @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer, + MSTest.Analyzers.AvoidAssertAreSameWithValueTypesFixer>; + +namespace MSTest.Analyzers.UnitTests; + +[TestClass] +public sealed class AvoidAssertAreSameWithValueTypesAnalyzerTests +{ + [TestMethod] + public async Task UseAssertAreSameWithValueTypes_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + // Both are value types + [|Assert.AreSame(0, 0)|]; + [|Assert.AreSame(0, 0, "Message")|]; + [|Assert.AreSame(0, 0, "Message {0}", "Arg")|]; + [|Assert.AreSame(message: "Message", expected: 0, actual: 0)|]; + + [|Assert.AreSame(0, 0)|]; + [|Assert.AreSame(0, 0, "Message")|]; + [|Assert.AreSame(0, 0, "Message {0}", "Arg")|]; + [|Assert.AreSame(message: "Message", expected: 0, actual: 0)|]; + + // Expected is value type. This is always-failing assert. + [|Assert.AreSame("0", 0)|]; + [|Assert.AreSame("0", 0, "Message")|]; + [|Assert.AreSame("0", 0, "Message {0}", "Arg")|]; + [|Assert.AreSame(message: "Message", expected: "0", actual: 0)|]; + + // Actual is value type. This is always-failing assert. + [|Assert.AreSame(0, "0")|]; + [|Assert.AreSame(0, "0", "Message")|]; + [|Assert.AreSame(0, "0", "Message {0}", "Arg")|]; + [|Assert.AreSame(message: "Message", expected: 0, actual: "0")|]; + + // Both are reference types. No diagnostic. + Assert.AreSame("0", "0"); + Assert.AreSame("0", "0", "Message"); + Assert.AreSame("0", "0", "Message {0}", "Arg"); + Assert.AreSame(message: "Message", expected: "0", actual: "0"); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + // Both are value types + Assert.AreEqual(0, 0); + Assert.AreEqual(0, 0, "Message"); + Assert.AreEqual(0, 0, "Message {0}", "Arg"); + Assert.AreEqual(message: "Message", expected: 0, actual: 0); + + Assert.AreEqual(0, 0); + Assert.AreEqual(0, 0, "Message"); + Assert.AreEqual(0, 0, "Message {0}", "Arg"); + Assert.AreEqual(message: "Message", expected: 0, actual: 0); + + // Expected is value type. This is always-failing assert. + Assert.AreEqual("0", 0); + Assert.AreEqual("0", 0, "Message"); + Assert.AreEqual("0", 0, "Message {0}", "Arg"); + Assert.AreEqual(message: "Message", expected: "0", actual: 0); + + // Actual is value type. This is always-failing assert. + Assert.AreEqual(0, "0"); + Assert.AreEqual(0, "0", "Message"); + Assert.AreEqual(0, "0", "Message {0}", "Arg"); + Assert.AreEqual(message: "Message", expected: 0, actual: "0"); + + // Both are reference types. No diagnostic. + Assert.AreSame("0", "0"); + Assert.AreSame("0", "0", "Message"); + Assert.AreSame("0", "0", "Message {0}", "Arg"); + Assert.AreSame(message: "Message", expected: "0", actual: "0"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task UseAssertAreNotSameWithValueTypes_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + // Both are value types + [|Assert.AreNotSame(0, 1)|]; + [|Assert.AreNotSame(0, 1, "Message")|]; + [|Assert.AreNotSame(0, 1, "Message {0}", "Arg")|]; + [|Assert.AreNotSame(message: "Message", notExpected: 0, actual: 1)|]; + + [|Assert.AreNotSame(0, 1)|]; + [|Assert.AreNotSame(0, 1, "Message")|]; + [|Assert.AreNotSame(0, 1, "Message {0}", "Arg")|]; + [|Assert.AreNotSame(message: "Message", notExpected: 0, actual: 1)|]; + + // Expected is value type. This is always-failing assert. + [|Assert.AreNotSame("0", 1)|]; + [|Assert.AreNotSame("0", 1, "Message")|]; + [|Assert.AreNotSame("0", 1, "Message {0}", "Arg")|]; + [|Assert.AreNotSame(message: "Message", notExpected: "0", actual: 1)|]; + + // Actual is value type. This is always-failing assert. + [|Assert.AreNotSame(0, "1")|]; + [|Assert.AreNotSame(0, "1", "Message")|]; + [|Assert.AreNotSame(0, "1", "Message {0}", "Arg")|]; + [|Assert.AreNotSame(message: "Message", notExpected: 0, actual: "1")|]; + + // Both are reference types. No diagnostic. + Assert.AreNotSame("0", "1"); + Assert.AreNotSame("0", "1", "Message"); + Assert.AreNotSame("0", "1", "Message {0}", "Arg"); + Assert.AreNotSame(message: "Message", notExpected: "0", actual: "1"); + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + // Both are value types + Assert.AreNotEqual(0, 1); + Assert.AreNotEqual(0, 1, "Message"); + Assert.AreNotEqual(0, 1, "Message {0}", "Arg"); + Assert.AreNotEqual(message: "Message", notExpected: 0, actual: 1); + + Assert.AreNotEqual(0, 1); + Assert.AreNotEqual(0, 1, "Message"); + Assert.AreNotEqual(0, 1, "Message {0}", "Arg"); + Assert.AreNotEqual(message: "Message", notExpected: 0, actual: 1); + + // Expected is value type. This is always-failing assert. + Assert.AreNotEqual("0", 1); + Assert.AreNotEqual("0", 1, "Message"); + Assert.AreNotEqual("0", 1, "Message {0}", "Arg"); + Assert.AreNotEqual(message: "Message", notExpected: "0", actual: 1); + + // Actual is value type. This is always-failing assert. + Assert.AreNotEqual(0, "1"); + Assert.AreNotEqual(0, "1", "Message"); + Assert.AreNotEqual(0, "1", "Message {0}", "Arg"); + Assert.AreNotEqual(message: "Message", notExpected: 0, actual: "1"); + + // Both are reference types. No diagnostic. + Assert.AreNotSame("0", "1"); + Assert.AreNotSame("0", "1", "Message"); + Assert.AreNotSame("0", "1", "Message {0}", "Arg"); + Assert.AreNotSame(message: "Message", notExpected: "0", actual: "1"); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs index 15e99f9f67..ddc024ba3e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidExpectedExceptionAttributeAnalyzerTests.cs @@ -9,9 +9,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class AvoidExpectedExceptionAttributeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class AvoidExpectedExceptionAttributeAnalyzerTests { + [TestMethod] public async Task WhenUsed_Diagnostic() { string code = """ @@ -78,15 +79,13 @@ public void TestMethod2() { } - [ExpectedException(typeof(System.Exception), AllowDerivedTypes = true)] [TestMethod] - public void [|TestMethod3|]() + public void TestMethod3() { } - [ExpectedException(typeof(System.Exception), "Some message", AllowDerivedTypes = true)] [TestMethod] - public void [|TestMethod4|]() + public void TestMethod4() { } @@ -107,11 +106,9 @@ public void TestMethod2() { fixedCode, }, - // ExpectedException with AllowDerivedTypes = True cannot be simply converted - // to Assert.ThrowsException as the semantics are different (same for custom attributes that may have some special semantics). - // For now, the user needs to manually fix this to use Assert.ThrowsException and specify the actual (exact) exception type. + // The codefix cannot fix MyExpectedException because it cannot detect the exception type. + // For now, the user needs to manually fix this. // We *could* provide a codefix that uses Assert.ThrowsException but that's most likely going to be wrong. - // If the user explicitly has AllowDerivedTypes, it's likely because he doesn't specify the exact exception type. // NOTE: For fixed state, the default is MarkupMode.IgnoreFixable, so we set // to Allow as we still have expected errors after applying the codefix. MarkupHandling = MarkupMode.Allow, @@ -121,6 +118,7 @@ public void TestMethod2() await test.RunAsync(CancellationToken.None); } + [TestMethod] public async Task When_Statement_Block_Diagnostic() { string code = """ @@ -136,6 +134,13 @@ public class TestClass { Console.WriteLine("Hello, world!"); } + + [ExpectedException(typeof(System.Exception), AllowDerivedTypes = true)] + [TestMethod] + public void [|TestMethod2|]() + { + Console.WriteLine("Hello, world!"); + } } """; @@ -149,7 +154,13 @@ public class TestClass [TestMethod] public void TestMethod() { - Assert.ThrowsException(() => Console.WriteLine("Hello, world!")); + Assert.ThrowsExactly(() => Console.WriteLine("Hello, world!")); + } + + [TestMethod] + public void TestMethod2() + { + Assert.Throws(() => Console.WriteLine("Hello, world!")); } } """; @@ -157,6 +168,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_Statement_ExpressionBody_Diagnostic() { string code = """ @@ -182,13 +194,14 @@ public class TestClass { [TestMethod] public void TestMethod() - => Assert.ThrowsException(() => Console.WriteLine("Hello, world!")); + => Assert.ThrowsExactly(() => Console.WriteLine("Hello, world!")); } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_Expression_Block_Diagnostic() { string code = """ @@ -221,7 +234,7 @@ public class TestClass [TestMethod] public void TestMethod() { - Assert.ThrowsException(() => GetNumber()); + Assert.ThrowsExactly(() => GetNumber()); } } """; @@ -229,6 +242,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_Expression_ExpressionBody_Diagnostic() { string code = """ @@ -257,13 +271,14 @@ public class TestClass private int GetNumber() => 0; [TestMethod] public void TestMethod() - => Assert.ThrowsException(() => GetNumber()); + => Assert.ThrowsExactly(() => GetNumber()); } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_Async_Block_Diagnostic() { string code = """ @@ -280,6 +295,13 @@ public class TestClass { await Task.Delay(0); } + + [ExpectedException(typeof(System.Exception), AllowDerivedTypes = true)] + [TestMethod] + public async Task [|TestMethod2|]() + { + await Task.Delay(0); + } } """; @@ -294,7 +316,13 @@ public class TestClass [TestMethod] public async Task TestMethod() { - await Assert.ThrowsExceptionAsync(async () => await Task.Delay(0)); + await Assert.ThrowsExactlyAsync(async () => await Task.Delay(0)); + } + + [TestMethod] + public async Task TestMethod2() + { + await Assert.ThrowsAsync(async () => await Task.Delay(0)); } } """; @@ -302,6 +330,7 @@ public async Task TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_Async_ExpressionBody_Diagnostic() { string code = """ @@ -329,13 +358,14 @@ public class TestClass { [TestMethod] public async Task TestMethod() - => await Assert.ThrowsExceptionAsync(async () => await Task.Delay(0)); + => await Assert.ThrowsExactlyAsync(async () => await Task.Delay(0)); } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_TestMethodIsAsyncButLastStatementIsSynchronous_Diagnostic() { string code = """ @@ -370,7 +400,7 @@ public class TestClass public async Task TestMethod() { await Task.Delay(0); - Assert.ThrowsException(() => M()); + Assert.ThrowsExactly(() => M()); } private static void M() => throw new Exception(); @@ -380,6 +410,7 @@ public async Task TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task When_LastStatementHasDeepAwait_Diagnostic() { string code = """ @@ -419,11 +450,10 @@ public class TestClass public async Task TestMethod() { Console.WriteLine("Hello, world!"); - await Assert.ThrowsExceptionAsync(async () => - // In ideal world, it's best if the codefix can separate await M() to a - // variable, then only wrap M(someVariable) in Assert.ThrowsException - // Let's also have this comment serve as a test for trivia ;) - M(await M())); + // In ideal world, it's best if the codefix can separate await M() to a + // variable, then only wrap M(someVariable) in Assert.ThrowsException + // Let's also have this comment serve as a test for trivia ;) + await Assert.ThrowsExactlyAsync(async () => M(await M())); } private static Task M() => Task.FromResult(0); @@ -434,4 +464,380 @@ await Assert.ThrowsExceptionAsync(async () => await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + + [TestMethod] + public async Task When_BlockEndsWithLocalFunctions_Should_ConsiderPreviousStatements() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + M1(); + M2(); + + void M1() { } + void M2() { } + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + M1(); + Assert.ThrowsExactly(() => M2()); + + void M1() { } + void M2() { } + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithLocalVariableDeclaration_Should_NotCrash() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + var x = Console.ReadLine(); + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + Assert.ThrowsExactly(() => Console.ReadLine()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithAssignment_Should_NotCrash() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + object x; + x = Console.ReadLine(); + } + + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod2|]() + { + _ = Console.ReadLine(); + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + object x; + Assert.ThrowsExactly(() => x = Console.ReadLine()); + } + + [TestMethod] + public void TestMethod2() + { + Assert.ThrowsExactly(() => _ = Console.ReadLine()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithNestedBlocks_Should_NotCrash() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + object x; + + { + { + } + { + x = Console.ReadLine(); + } + { + } + } + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + object x; + + { + { + } + { + Assert.ThrowsExactly(() => x = Console.ReadLine()); + } + { + } + } + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithEmptyStatement_Should_BeIgnored() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + Console.WriteLine();; + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + Assert.ThrowsExactly(() => Console.WriteLine()); ; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithForEach_Should_NotCrash() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + foreach (var x in new[] { 1, 2, 3 }) + { + Console.WriteLine(x); + } + } + } + """; + + // TODO: Formatting is so broken here. + // See https://github.com/microsoft/testfx/issues/4570 + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + Assert.ThrowsExactly(() => + { + foreach (var x in new[] { 1, 2, 3 }) + { + Console.WriteLine(x); + } + }); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithThrowStatement_Should_NotBeWrappedInBlock() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + throw new Exception(); + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + Assert.ThrowsExactly(() => throw new Exception()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task When_BlockEndsWithNestedLockStatements_Should_NotCrash() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [ExpectedException(typeof(System.Exception))] + [TestMethod] + public void [|TestMethod|]() + { + object x = new(); + lock (x) + { + lock (x) + { + } + lock (x) + { + Console.WriteLine(); + } + lock (x) + { + } + } + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + public void TestMethod() + { + object x = new(); + lock (x) + { + lock (x) + { + } + lock (x) + { + Assert.ThrowsExactly(() => Console.WriteLine()); + } + lock (x) + { + } + } + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs index 7fee442876..bf5c6d78d3 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassCleanupShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class ClassCleanupShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class ClassCleanupShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenClassCleanupIsPublic_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsGenericWithInheritanceModeSet_NoDiagnostic() { string code = """ @@ -46,6 +48,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsGenericWithInheritanceModeSetToNone_Diagnostic() { string code = """ @@ -67,6 +70,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassCleanupIsGenericWithoutSettingInheritanceMode_Diagnostic() { string code = """ @@ -88,6 +92,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassCleanupIsNotOrdinary_Diagnostic() { string code = """ @@ -109,6 +114,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ @@ -129,6 +135,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() { string code = """ @@ -167,10 +174,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenClassCleanupIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -205,6 +213,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassCleanupIsGeneric_Diagnostic() { string code = """ @@ -239,6 +248,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassCleanupIsNotStatic_Diagnostic() { string code = """ @@ -273,7 +283,8 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - public async Task WhenClassCleanupHasParameters_Diagnostic() + [TestMethod] + public async Task WhenClassCleanupHasTestContextParameter_NoDiagnostic() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -282,31 +293,16 @@ public async Task WhenClassCleanupHasParameters_Diagnostic() public class MyTestClass { [ClassCleanup] - public static void {|#0:ClassCleanup|}(TestContext testContext) + public static void ClassCleanup(TestContext testContext) { } } """; - string fixedCode = """ - using Microsoft.VisualStudio.TestTools.UnitTesting; - - [TestClass] - public class MyTestClass - { - [ClassCleanup] - public static void ClassCleanup() - { - } - } - """; - - await VerifyCS.VerifyCodeFixAsync( - code, - VerifyCS.Diagnostic().WithLocation(0).WithArguments("ClassCleanup"), - fixedCode); + await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenClassCleanupReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -382,6 +378,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassCleanupReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -413,6 +410,7 @@ public static ValueTask ClassCleanup2() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsAsyncVoid_Diagnostic() { string code = """ @@ -451,6 +449,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenMultipleViolations_TheyAllGetFixed() { string code = """ @@ -489,6 +488,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassCleanupIsNotOnClass_Diagnostic() { string code = """ @@ -506,6 +506,7 @@ public struct MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnSealedClassNotMarkedWithTestClass_Diagnostic() { string code = """ @@ -523,6 +524,7 @@ public sealed class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() { string code = """ @@ -540,6 +542,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnAbstractClassNotMarkedWithTestClass_AndWithInheritanceBehavior_NoDiagnostic() { string code = """ @@ -557,6 +560,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnAbstractClassMarkedWithTestClass_AndWithInheritanceBehavior_NoDiagnostic() { string code = """ @@ -575,6 +579,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsAbstractClassNotMarkedWithTestClass_AndWithoutInheritanceBehavior_Diagnostic() { string code = """ @@ -592,6 +597,7 @@ public abstract class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnAbstractClassMarkedWithTestClass_AndWithoutInheritanceBehavior_Diagnostic() { string code = """ @@ -610,6 +616,7 @@ public abstract class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnAbstractClassMarkedWithTestClass_AndWithInheritanceBehaviorNone_Diagnostic() { string code = """ @@ -628,6 +635,7 @@ public abstract class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnSealedClassMarkedWithTestClass_AndWithInheritanceBehavior_Diagnostic() { string code = """ @@ -646,6 +654,7 @@ public sealed class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnSealedClassMarkedWithTestClass_AndWithInheritanceBehaviorNone_NoDiagnostic() { string code = """ @@ -664,6 +673,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassCleanupIsOnSealedClassMarkedWithTestClass_WithDefaultInheritanceBehavior_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs index 171807825d..55cc137e6f 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ClassInitializeShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class ClassInitializeShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class ClassInitializeShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenClassInitializeIsPublic_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsGenericWithInheritanceModeSet_NoDiagnostic() { string code = """ @@ -46,6 +48,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsGenericWithInheritanceModeSetToNone_Diagnostic() { string code = """ @@ -67,6 +70,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassInitializeIsGenericWithoutSettingInheritanceMode_Diagnostic() { string code = """ @@ -88,6 +92,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ @@ -108,6 +113,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() { string code = """ @@ -148,10 +154,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenClassInitializeIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -186,6 +193,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassInitializeIsNotOrdinary_Diagnostic() { string code = """ @@ -207,6 +215,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassInitializeIsGeneric_Diagnostic() { string code = """ @@ -241,6 +250,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassInitializeIsNotStatic_Diagnostic() { string code = """ @@ -275,6 +285,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassInitializeDoesNotHaveParameters_Diagnostic() { string code = """ @@ -309,6 +320,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassInitializeReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -384,6 +396,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassInitializeReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -415,6 +428,7 @@ public static ValueTask ClassInitialize2(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsAsyncVoid_Diagnostic() { string code = """ @@ -453,6 +467,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenMultipleViolations_TheyAllGetFixed() { string code = """ @@ -491,6 +506,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassInitializeIsNotOnClass_Diagnostic() { string code = """ @@ -508,6 +524,7 @@ public struct MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnSealedClassNotMarkedWithTestClass_Diagnostic() { string code = """ @@ -525,6 +542,7 @@ public sealed class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() { string code = """ @@ -542,6 +560,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnAbstractClassNotMarkedWithTestClass_AndWithInheritanceBehavior_NoDiagnostic() { string code = """ @@ -559,6 +578,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnAbstractClassMarkedWithTestClass_AndWithInheritanceBehavior_NoDiagnostic() { string code = """ @@ -577,6 +597,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsAbstractClassNotMarkedWithTestClass_AndWithoutInheritanceBehavior_Diagnostic() { string code = """ @@ -594,6 +615,7 @@ public abstract class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnAbstractClassMarkedWithTestClass_AndWithoutInheritanceBehavior_Diagnostic() { string code = """ @@ -612,6 +634,7 @@ public abstract class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnAbstractClassMarkedWithTestClass_AndWithInheritanceBehaviorNone_Diagnostic() { string code = """ @@ -630,6 +653,7 @@ public abstract class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnSealedClassMarkedWithTestClass_AndWithInheritanceBehavior_Diagnostic() { string code = """ @@ -648,6 +672,7 @@ public sealed class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnSealedClassMarkedWithTestClass_AndWithInheritanceBehaviorNone_NoDiagnostic() { string code = """ @@ -666,6 +691,7 @@ public static void ClassInitialize(TestContext testContext) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassInitializeIsOnSealedClassMarkedWithTestClass_AndDefaultInheritanceBehavior_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs index c273069028..37e2ced325 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class DataRowShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class DataRowShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgument_NoDiagnostic() { string code = """ @@ -29,6 +30,7 @@ public void TestMethod1(int a) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndWithDataTestMethodAttribute_NoDiagnostic() { string code = """ @@ -48,6 +50,7 @@ public void TestMethod1(int a) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndWithDerivedTestMethodAttribute_NoDiagnostic() { string code = """ @@ -71,6 +74,7 @@ public void TestMethod1(int a) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithThreeArguments_NoDiagnostic() { string code = """ @@ -90,6 +94,7 @@ public void TestMethod1(int a, int b, int c) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasParamsArgument_NoDiagnostic() { string code = """ @@ -109,6 +114,7 @@ public void TestMethod1(params object[] o) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasArrayArgument_NoDiagnostic() { string code = """ @@ -128,6 +134,7 @@ public void TestMethod1(object[] o) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowPassesOneItemAndParameterExpectsArray_Diagnostic() { string code = """ @@ -151,6 +158,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments((0, 0))); } + [TestMethod] public async Task WhenDataRowHasThreeArgumentsAndMethodHasAnIntegerAndAnArrayArgument_Diagnostic() { string code = """ @@ -174,6 +182,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments(3, 2)); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndMethodHasAPrimitiveTypeAndAParamsArgument_NoDiagnostic() { string code = """ @@ -193,6 +202,7 @@ public void TestMethod1(int a, params object[] o) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasAPrimitiveTypeAndAParamsArgument_NoDiagnostic() { string code = """ @@ -212,6 +222,7 @@ public void TestMethod1(int a, params object[] o) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndMethodHasAPrimitiveTypeAndAParamsStringArgument_NoDiagnostic() { string code = """ @@ -231,6 +242,7 @@ public void TestMethod1(int a, params string[] o) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndMethodHasAPrimitiveTypeAndADefaultArgument_NoDiagnostic() { string code = """ @@ -252,6 +264,7 @@ public void TestMethod1(int a, object?[]? o = null) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndIntegersAreAssignableToDoubles_NoDiagnostic() { string code = """ @@ -271,6 +284,7 @@ public void TestMethod1(double d) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndCharsAreAssignableToIntegers_NoDiagnostic() { string code = """ @@ -290,6 +304,7 @@ public void TestMethod1(int i) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowIsCorrectlyDefinedWithOneArgumentAndNullsAreAssignableToIntegers_NoDiagnostic() { string code = """ @@ -309,6 +324,7 @@ public void TestMethod1(int i) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataRowHasOneNullArgumentAndMethodHasNoArguments_Diagnostic() { string code = """ @@ -332,6 +348,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments(1, 0)); } + [TestMethod] public async Task WhenDataRowIsNotSetOnATestMethod_Diagnostic() { string code = """ @@ -351,6 +368,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code, VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.DataRowOnTestMethodRule).WithLocation(0)); } + [TestMethod] public async Task WhenDataRowHasNoArgsButMethodHasOneArgument_Diagnostic() { string code = """ @@ -375,6 +393,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments(0, 1)); } + [TestMethod] public async Task WhenDataRowHasArgumentMismatchWithTestMethod_Diagnostic() { string code = """ @@ -399,6 +418,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments(3, 4)); } + [TestMethod] public async Task WhenDataRowHasArgumentMismatchWithTestMethod2_Diagnostic() { string code = """ @@ -423,6 +443,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments(3, 2)); } + [TestMethod] public async Task WhenDataRowHasArgumentMismatchWithTestMethod3_Diagnostic() { string code = """ @@ -447,6 +468,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments(3, 2)); } + [TestMethod] public async Task WhenDataRowHasTypeMismatchWithTestMethod_Diagnostic() { string code = """ @@ -471,6 +493,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments((2, 2))); } + [TestMethod] public async Task DefaultArguments() { string code = """ @@ -508,6 +531,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentCountMismatchRule).WithLocation(2).WithArguments(1, 5)); } + [TestMethod] public async Task Testfx_2606_NullArgumentForArray() { string code = """ @@ -540,6 +564,7 @@ public void TestSomething( await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task Issue2856_ArraysInDataRow_NoDiagnostic() { string code = """ @@ -560,4 +585,71 @@ public void ItemsTest(int[] input) await VerifyCS.VerifyAnalyzerAsync(code); } + + [TestMethod] + public async Task WhenMethodIsGeneric() + { + string code = """ + using System; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class TestClass + { + [TestMethod] + [DataRow(0)] // This is an unfortunate false negative that will blow up at runtime. + public void AMethodWithBadConstraints(T p) where T : IDisposable + => Assert.Fail($"Test method 'AMethodWithBadConstraints' did run with T type being '{typeof(T)}'."); + + [TestMethod] + [DataRow((byte)1)] + [DataRow((int)2)] + [DataRow("Hello world")] + [{|#0:DataRow(null)|}] + public void ParameterizedMethodSimple(T parameter) + => Assert.Fail($"Test method 'ParameterizedMethodSimple' did run with parameter '{parameter?.ToString() ?? ""}' and type '{typeof(T)}'."); + + [TestMethod] + [{|#1:DataRow((byte)1, "Hello world", (int)2, 3)|}] + [DataRow(null, "Hello world", "Hello again", 3)] + [{|#2:DataRow("Hello hello", "Hello world", null, null)|}] + [{|#3:{|#4:DataRow(null, null, null, null)|}|}] + public void ParameterizedMethodTwoGenericParametersAndFourMethodParameters(T2 p1, string p2, T2 p3, T1 p4) + => Assert.Fail($"Test method 'ParameterizedMethodTwoGenericParametersAndFourMethodParameters' did run with parameters '{p1?.ToString() ?? ""}', '{p2 ?? ""}', '{p3?.ToString() ?? ""}', '{p4?.ToString() ?? ""}' and generic types '{typeof(T1)}', '{typeof(T2)}'."); + + [TestMethod] + [{|#5:DataRow((byte)1)|}] + [{|#6:DataRow((byte)1, 2)|}] + [{|#7:DataRow("Hello world")|}] + [{|#8:DataRow(null)|}] + [{|#9:DataRow(null, "Hello world")|}] + public void ParameterizedMethodSimpleParams(params T[] parameter) + => Assert.Fail($"Test method 'ParameterizedMethodSimple' did run with parameter '{string.Join(",", parameter)}' and type '{typeof(T)}'."); + } + """; + + await VerifyCS.VerifyAnalyzerAsync( + code, + // /0/Test0.cs(17,6): warning MSTEST0014: The type of the generic parameter 'T' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(0).WithArguments("T"), + // /0/Test0.cs(22,6): warning MSTEST0014: Found two conflicting types for generic parameter 'T2'. The conflicting types are 'Byte' and 'Int32'. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentConflictingTypesRule).WithLocation(1).WithArguments("T2", "Byte", "Int32"), + // /0/Test0.cs(24,6): warning MSTEST0014: The type of the generic parameter 'T1' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(2).WithArguments("T1"), + // /0/Test0.cs(25,6): warning MSTEST0014: The type of the generic parameter 'T1' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(3).WithArguments("T1"), + // /0/Test0.cs(25,6): warning MSTEST0014: The type of the generic parameter 'T2' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(4).WithArguments("T2"), + // /0/Test0.cs(30,6): warning MSTEST0014: The type of the generic parameter 'T' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(5).WithArguments("T"), + // /0/Test0.cs(31,6): warning MSTEST0014: The type of the generic parameter 'T' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(6).WithArguments("T"), + // /0/Test0.cs(32,6): warning MSTEST0014: The type of the generic parameter 'T' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(7).WithArguments("T"), + // /0/Test0.cs(33,6): warning MSTEST0014: The type of the generic parameter 'T' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(8).WithArguments("T"), + // /0/Test0.cs(34,6): warning MSTEST0014: The type of the generic parameter 'T' could not be inferred. + VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.GenericTypeArgumentNotResolvedRule).WithLocation(9).WithArguments("T")); + } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs index efcad90561..f4e145e21e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotNegateBooleanAssertionAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class DoNotNegateBooleanAssertionAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class DoNotNegateBooleanAssertionAnalyzerTests { + [TestMethod] public async Task WhenAssertionIsNotNegated_NoDiagnostic() { string code = """ @@ -41,6 +42,7 @@ public void TestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssertionIsNegated_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs index 5cb352dbce..f0c3590176 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class DoNotStoreStaticTestContextAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class DoNotStoreStaticTestContextAnalyzerTests { + [TestMethod] public async Task WhenAssemblyInitializeOrClassInitialize_Diagnostic() { string code = """ @@ -87,6 +88,7 @@ public static ValueTask ClassInit(TestContext tc) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenOtherTestContext_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs index a4196dc747..8b471471e3 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseShadowingAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class DoNotUseShadowingAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class DoNotUseShadowingAnalyzerTests { + [TestMethod] public async Task WhenTestClassHaveShadowingMethod_Diagnostic() { string code = """ @@ -29,6 +30,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButDifferentParameters_NoDiagnostic() { string code = """ @@ -48,6 +50,7 @@ public void Method() { } await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButBothArePrivate_NoDiagnostic() { string code = """ @@ -67,6 +70,7 @@ private void Method() { } await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButBaseMethodIsPrivate_NoDiagnostic() { string code = """ @@ -86,6 +90,7 @@ public void Method() { } await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButDerivedMethodIsPrivate_Diagnostic() { string code = """ @@ -105,6 +110,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButDerivedMethodIsProtected_AndBaseMethodIsInternal_Diagnostic() { string code = """ @@ -124,6 +130,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSamePropertyAsBaseClassMethod__ButBasePropertyIsProtected_AndDerivedPropertyIsInternal_Diagnostic() { string code = """ @@ -143,6 +150,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSamePropertyAsBaseClassMethod_ButBothArePrivate_NoDiagnostic() { string code = """ @@ -162,6 +170,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSamePropertyAsBaseClassMethod_ButBasePropertyIsPrivate_NoDiagnostic() { string code = """ @@ -181,6 +190,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSamePropertyAsBaseClassMethod_ButDerivedPropertyIsPrivate_Diagnostic() { string code = """ @@ -200,6 +210,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_ButOneIsGeneric_NoDiagnostic() { string code = """ @@ -231,6 +242,7 @@ public class SomeType await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveSameMethodAsBaseClassMethod_WithParameters_Diagnostic() { string code = """ @@ -250,6 +262,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveOverrideProperty_NoDiagnostic() { string code = """ @@ -269,6 +282,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassOverrideMethodFromBaseClass_NoDiagnostic() { string code = """ @@ -288,6 +302,7 @@ public override void Method() { } await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveShadowingProperty_Diagnostic() { string code = """ @@ -307,6 +322,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveShadowingMethod_WithoutNewModifier_Diagnostic() { string code = """ @@ -326,6 +342,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassHaveShadowingProperty_WithoutNewModifier_Diagnostic() { string code = """ @@ -345,6 +362,7 @@ public class DerivedClass : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassAndBaseClassHaveStaticCtor_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs index de809f42f0..e5c2dd3cd7 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotUseSystemDescriptionAttributeAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class DoNotUseSystemDescriptionAttributeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class DoNotUseSystemDescriptionAttributeAnalyzerTests { + [TestMethod] public async Task WhenTestMethodHasMicrosoftDescriptionAttribute_NoDiagnostic() { string code = """ @@ -29,6 +30,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestMethodHasSystemDescriptionAttribute_Diagnostic() { string code = """ @@ -48,6 +50,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenMethodWithoutTestMethodAttribute_HasSystemDescriptionAttribute_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs index b15c8c2c9d..5cc4064392 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DynamicDataShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class DynamicDataShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class DynamicDataShouldBeValidAnalyzerTests { + [TestMethod] public async Task ValidUsages_NoDiagnostic() { string code = """ @@ -22,49 +23,97 @@ public class MyTestClass { [DynamicData("Data")] [TestMethod] - public void TestMethod1(object[] o) + public void TestMethod1Auto(object[] o) + { + } + + [DynamicData("Data", DynamicDataSourceType.Property)] + [TestMethod] + public void TestMethod1Property(object[] o) { } [DynamicData("SomeData", typeof(SomeClass))] [TestMethod] - public void TestMethod2(object[] o) + public void TestMethod2Auto(object[] o) + { + } + + [DynamicData("SomeData", typeof(SomeClass), DynamicDataSourceType.Property)] + [TestMethod] + public void TestMethod2Property(object[] o) { } [DynamicData(dynamicDataSourceName: "Data")] [TestMethod] - public void TestMethod3(object[] o) + public void TestMethod3Auto(object[] o) + { + } + + [DynamicData(dynamicDataSourceName: "Data", DynamicDataSourceType.Property)] + [TestMethod] + public void TestMethod3Property(object[] o) { } [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeData")] [TestMethod] - public void TestMethod4(object[] o) + public void TestMethod4Auto(object[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeData", dynamicDataSourceType: DynamicDataSourceType.Property)] + [TestMethod] + public void TestMethod4Property(object[] o) { } [DynamicData("GetData", DynamicDataSourceType.Method)] [TestMethod] - public void TestMethod11(object[] o) + public void TestMethod11Method(object[] o) + { + } + + [DynamicData("GetData")] + [TestMethod] + public void TestMethod11Auto(object[] o) { } [DynamicData("GetSomeData", typeof(SomeClass), DynamicDataSourceType.Method)] [TestMethod] - public void TestMethod12(object[] o) + public void TestMethod12Method(object[] o) + { + } + + [DynamicData("GetSomeData", typeof(SomeClass))] + [TestMethod] + public void TestMethod12Auto(object[] o) { } [DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetData")] [TestMethod] - public void TestMethod13(object[] o) + public void TestMethod13Method(object[] o) + { + } + + [DynamicData(dynamicDataSourceType: DynamicDataSourceType.AutoDetect, dynamicDataSourceName: "GetData")] + [TestMethod] + public void TestMethod13Auto(object[] o) { } [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeData")] [TestMethod] - public void TestMethod14(object[] o) + public void TestMethod14Method(object[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.AutoDetect, dynamicDataSourceName: "GetSomeData")] + [TestMethod] + public void TestMethod14Auto(object[] o) { } @@ -260,16 +309,116 @@ public void TestMethod414(MyTestClass[] testClasses) { } + [DynamicData("Data")] + [TestMethod] + public void TestMethod501(object[] o) + { + } + + [DynamicData("SomeData", typeof(SomeClass))] + [TestMethod] + public void TestMethod502(object[] o) + { + } + + [DynamicData(dynamicDataSourceName: "Data")] + [TestMethod] + public void TestMethod503(object[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeData")] + [TestMethod] + public void TestMethod504(object[] o) + { + } + + [DynamicData("GetData", DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod511(object[] o) + { + } + + [DynamicData("GetSomeData", typeof(SomeClass), DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod512(object[] o) + { + } + + [DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetData")] + [TestMethod] + public void TestMethod513(object[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeData")] + [TestMethod] + public void TestMethod514(object[] o) + { + } + + [DynamicData("DataArray")] + [TestMethod] + public void TestMethod601(MyTestClass[] o) + { + } + + [DynamicData("SomeDataArray", typeof(SomeClass))] + [TestMethod] + public void TestMethod602(MyTestClass[] o) + { + } + + [DynamicData(dynamicDataSourceName: "DataArray")] + [TestMethod] + public void TestMethod603(MyTestClass[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataArray")] + [TestMethod] + public void TestMethod604(MyTestClass[] o) + { + } + + [DynamicData("GetDataArray", DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod611(MyTestClass[] o) + { + } + + [DynamicData("GetSomeDataArray", typeof(SomeClass), DynamicDataSourceType.Method)] + [TestMethod] + public void TestMethod612(MyTestClass[] o) + { + } + + [DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetDataArray")] + [TestMethod] + public void TestMethod613(MyTestClass[] o) + { + } + + [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeDataArray")] + [TestMethod] + public void TestMethod614(MyTestClass[] o) + { + } + public static IEnumerable Data => new List(); public static IEnumerable> DataTuple => new List>(); public static IEnumerable<(int, string)> DataValueTuple => new List<(int, string)>(); public static MyTestClass[][] DataJaggedArray => System.Array.Empty(); public static IEnumerable DataNonObjectTypeArray => new List(); + public static IEnumerable DataObject => new List(); + public static MyTestClass[] DataArray => System.Array.Empty(); public static IEnumerable GetData() => new List(); public static IEnumerable> GetDataTuple() => new List>(); public static IEnumerable<(int, string)> GetDataValueTuple() => new List<(int, string)>(); public static MyTestClass[][] GetDataJaggedArray() => System.Array.Empty(); public static IEnumerable GetDataNonObjectTypeArray() => new List(); + public static IEnumerable GetDataObject() => new List(); + public static MyTestClass[] GetDataArray() => System.Array.Empty(); } public class SomeClass @@ -279,17 +428,22 @@ public class SomeClass public static IEnumerable<(int, string)> SomeDataValueTuple => new List<(int, string)>(); public static MyTestClass[][] SomeDataJaggedArray => System.Array.Empty(); public static IEnumerable SomeDataNonObjectTypeArray => new List(); + public static IEnumerable SomeDataObject => new List(); + public static MyTestClass[] SomeDataArray => System.Array.Empty(); public static IEnumerable GetSomeData() => new List(); public static IEnumerable> GetSomeDataTuple() => new List>(); public static IEnumerable<(int, string)> GetSomeDataValueTuple() => new List<(int, string)>(); public static MyTestClass[][] GetSomeDataJaggedArray() => System.Array.Empty(); public static IEnumerable GetSomeDataNonObjectTypeArray() => new List(); + public static IEnumerable GetSomeDataObject() => new List(); + public static MyTestClass[] GetSomeDataArray() => System.Array.Empty(); } """; await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDataSourceMemberDoesNotExist_Diagnostic() { string code = """ @@ -364,6 +518,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberNotFoundRule).WithLocation(7).WithArguments("SomeClass", "MemberNotFound")); } + [TestMethod] public async Task WhenAppliedToNonTestMethod_Diagnostic() { string code = """ @@ -372,22 +527,22 @@ public async Task WhenAppliedToNonTestMethod_Diagnostic() [TestClass] public class MyTestClass { - [DynamicData("SomeProperty")] + [{|#4:DynamicData("SomeProperty")|}] public void {|#0:TestMethod1|}(object[] o) { } - [DynamicData("SomeProperty", typeof(SomeClass))] + [{|#5:DynamicData("SomeProperty", typeof(SomeClass))|}] public void {|#1:TestMethod2|}(object[] o) { } - [DynamicData(dynamicDataSourceName: "SomeProperty")] + [{|#6:DynamicData(dynamicDataSourceName: "SomeProperty")|}] public void {|#2:TestMethod3|}(object[] o) { } - [DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeProperty")] + [{|#7:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeProperty")|}] public void {|#3:TestMethod4|}(object[] o) { } @@ -400,12 +555,25 @@ public class SomeClass await VerifyCS.VerifyAnalyzerAsync( code, - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithLocation(0), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithLocation(1), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithLocation(2), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithLocation(3)); + // /0/Test0.cs(6,6): warning MSTEST0018: '[DynamicData]' member 'MyTestClass.SomeProperty' cannot be found + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberNotFoundRule).WithSpan(6, 6, 6, 33).WithArguments("MyTestClass", "SomeProperty"), + // /0/Test0.cs(7,17): warning MSTEST0018: '[DynamicData]' should only be set on a test method + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithSpan(7, 17, 7, 28), + // /0/Test0.cs(11,6): warning MSTEST0018: '[DynamicData]' member 'SomeClass.SomeProperty' cannot be found + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberNotFoundRule).WithSpan(11, 6, 11, 52).WithArguments("SomeClass", "SomeProperty"), + // /0/Test0.cs(12,17): warning MSTEST0018: '[DynamicData]' should only be set on a test method + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithSpan(12, 17, 12, 28), + // /0/Test0.cs(16,6): warning MSTEST0018: '[DynamicData]' member 'MyTestClass.SomeProperty' cannot be found + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberNotFoundRule).WithSpan(16, 6, 16, 56).WithArguments("MyTestClass", "SomeProperty"), + // /0/Test0.cs(17,17): warning MSTEST0018: '[DynamicData]' should only be set on a test method + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithSpan(17, 17, 17, 28), + // /0/Test0.cs(21,6): warning MSTEST0018: '[DynamicData]' member 'SomeClass.SomeProperty' cannot be found + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberNotFoundRule).WithSpan(21, 6, 21, 101).WithArguments("SomeClass", "SomeProperty"), + // /0/Test0.cs(22,17): warning MSTEST0018: '[DynamicData]' should only be set on a test method + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.NotTestMethodRule).WithSpan(22, 17, 22, 28)); } + [TestMethod] public async Task WhenDataSourceMemberFoundMultipleTimes_Diagnostic() { string code = """ @@ -439,6 +607,18 @@ public void TestMethod4(object[] o) { } + [{|#4:DynamicData("GetData", DynamicDataSourceType.AutoDetect)|}] + [TestMethod] + public void TestMethod5(object[] o) + { + } + + [{|#5:DynamicData("GetData")|}] + [TestMethod] + public void TestMethod6(object[] o) + { + } + public static IEnumerable GetData() => new List(); public static IEnumerable GetData(int i) => new List(); } @@ -455,9 +635,12 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(0).WithArguments("MyTestClass", "GetData"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(1).WithArguments("SomeClass", "GetSomeData"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(2).WithArguments("MyTestClass", "GetData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(3).WithArguments("SomeClass", "GetSomeData")); + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(3).WithArguments("SomeClass", "GetSomeData"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(4).WithArguments("MyTestClass", "GetData"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.FoundTooManyMembersRule).WithLocation(5).WithArguments("MyTestClass", "GetData")); } + [TestMethod] public async Task WhenMemberKindIsMixedUp_Diagnostic() { string code = """ @@ -467,25 +650,25 @@ public async Task WhenMemberKindIsMixedUp_Diagnostic() [TestClass] public class MyTestClass { - [{|#0:DynamicData("GetData")|}] + [{|#0:DynamicData("GetData", DynamicDataSourceType.Property)|}] [TestMethod] public void TestMethod1(object[] o) { } - [{|#1:DynamicData("GetSomeData", typeof(SomeClass))|}] + [{|#1:DynamicData("GetSomeData", typeof(SomeClass), DynamicDataSourceType.Property)|}] [TestMethod] public void TestMethod2(object[] o) { } - [{|#2:DynamicData(dynamicDataSourceName: "GetData")|}] + [{|#2:DynamicData(dynamicDataSourceName: "GetData", DynamicDataSourceType.Property)|}] [TestMethod] public void TestMethod3(object[] o) { } - [{|#3:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "GetSomeData")|}] + [{|#3:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "GetSomeData", dynamicDataSourceType: DynamicDataSourceType.Property)|}] [TestMethod] public void TestMethod4(object[] o) { @@ -515,8 +698,33 @@ public void TestMethod14(object[] o) { } + [{|#8:DynamicData(nameof(DataField), DynamicDataSourceType.Method)|}] + [TestMethod] + public void TestMethod15(object[] o) + { + } + + [{|#9:DynamicData(nameof(DataField), DynamicDataSourceType.Property)|}] + [TestMethod] + public void TestMethod16(object[] o) + { + } + + [{|#10:DynamicData(nameof(DataField), DynamicDataSourceType.AutoDetect)|}] + [TestMethod] + public void TestMethod17(object[] o) + { + } + + [{|#11:DynamicData(nameof(DataField))|}] + [TestMethod] + public void TestMethod18(object[] o) + { + } + public static IEnumerable Data => new List(); public static IEnumerable GetData() => new List(); + public static IEnumerable DataField = new List(); } public class SomeClass @@ -535,12 +743,18 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(4).WithArguments("MyTestClass", "Data"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(5).WithArguments("SomeClass", "SomeData"), VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(6).WithArguments("MyTestClass", "Data"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(7).WithArguments("SomeClass", "SomeData")); + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypePropertyRule).WithLocation(7).WithArguments("SomeClass", "SomeData"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(8).WithArguments("MyTestClass", "DataField"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(9).WithArguments("MyTestClass", "DataField"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(10).WithArguments("MyTestClass", "DataField"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.SourceTypeNotPropertyOrMethodRule).WithLocation(11).WithArguments("MyTestClass", "DataField")); } - public async Task WhenDataSourceReturnTypeIsInvalid_Diagnostic() + [TestMethod] + public async Task MemberIsNotStatic_Diagnostic() { string code = """ + using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -553,173 +767,107 @@ public void TestMethod1(object[] o) { } - [{|#1:DynamicData("SomeData", typeof(SomeClass))|}] + [{|#1:DynamicData("GetData", DynamicDataSourceType.Method)|}] [TestMethod] public void TestMethod2(object[] o) { } - [{|#2:DynamicData(dynamicDataSourceName: "Data")|}] - [TestMethod] - public void TestMethod3(object[] o) - { - } - - [{|#3:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeData")|}] - [TestMethod] - public void TestMethod4(object[] o) - { - } - - [{|#4:DynamicData("GetData", DynamicDataSourceType.Method)|}] - [TestMethod] - public void TestMethod5(object[] o) - { - } - - [{|#5:DynamicData("GetSomeData", typeof(SomeClass), DynamicDataSourceType.Method)|}] - [TestMethod] - public void TestMethod6(object[] o) - { - } - - [{|#6:DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetData")|}] - [TestMethod] - public void TestMethod7(object[] o) - { - } - - [{|#7:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeData")|}] - [TestMethod] - public void TestMethod8(object[] o) - { - } - - [{|#8:DynamicData("DataArray")|}] - [TestMethod] - public void TestMethod9(MyTestClass[] o) - { - } - - [{|#9:DynamicData("SomeDataArray", typeof(SomeClass))|}] - [TestMethod] - public void TestMethod10(MyTestClass[] o) - { - } - - [{|#10:DynamicData(dynamicDataSourceName: "DataArray")|}] - [TestMethod] - public void TestMethod11(MyTestClass[] o) - { - } - - [{|#11:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceName: "SomeDataArray")|}] - [TestMethod] - public void TestMethod12(MyTestClass[] o) - { - } + public IEnumerable Data => new List(); + public IEnumerable GetData() => new List(); + } + """; - [{|#12:DynamicData("GetDataArray", DynamicDataSourceType.Method)|}] - [TestMethod] - public void TestMethod13(MyTestClass[] o) - { - } + await VerifyCS.VerifyAnalyzerAsync( + code, + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(0).WithArguments("MyTestClass", "Data"), + VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(1).WithArguments("MyTestClass", "GetData")); + } - [{|#13:DynamicData("GetSomeDataArray", typeof(SomeClass), DynamicDataSourceType.Method)|}] - [TestMethod] - public void TestMethod14(MyTestClass[] o) - { - } + [TestMethod] + public async Task MemberIsNotPublic_NoDiagnostic() + { + string code = """ + using System; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestTools.UnitTesting; - [{|#14:DynamicData(dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetDataArray")|}] + [TestClass] + public class MyTestClass + { + [DynamicData("Data")] [TestMethod] - public void TestMethod15(MyTestClass[] o) + public void TestMethod1(object[] o) { } - [{|#15:DynamicData(dynamicDataDeclaringType: typeof(SomeClass), dynamicDataSourceType: DynamicDataSourceType.Method, dynamicDataSourceName: "GetSomeDataArray")|}] + [DynamicData("GetData", DynamicDataSourceType.Method)] [TestMethod] - public void TestMethod16(MyTestClass[] o) + public void TestMethod2(object[] o) { } - public static IEnumerable Data => new List(); - public static MyTestClass[] DataArray => System.Array.Empty(); - public static IEnumerable GetData() => new List(); - public static MyTestClass[] GetDataArray() => System.Array.Empty(); - } - - public class SomeClass - { - public static IEnumerable SomeData => new List(); - public static MyTestClass[] SomeDataArray => System.Array.Empty(); - public static IEnumerable GetSomeData() => new List(); - public static MyTestClass[] GetSomeDataArray() => System.Array.Empty(); + private static IEnumerable Data => new List(); + private static IEnumerable GetData() => new List(); } """; - await VerifyCS.VerifyAnalyzerAsync( - code, - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(0).WithArguments("MyTestClass", "Data"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(1).WithArguments("SomeClass", "SomeData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(2).WithArguments("MyTestClass", "Data"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(3).WithArguments("SomeClass", "SomeData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(4).WithArguments("MyTestClass", "GetData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(5).WithArguments("SomeClass", "GetSomeData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(6).WithArguments("MyTestClass", "GetData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(7).WithArguments("SomeClass", "GetSomeData"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(8).WithArguments("MyTestClass", "DataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(9).WithArguments("SomeClass", "SomeDataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(10).WithArguments("MyTestClass", "DataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(11).WithArguments("SomeClass", "SomeDataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(12).WithArguments("MyTestClass", "GetDataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(13).WithArguments("SomeClass", "GetSomeDataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(14).WithArguments("MyTestClass", "GetDataArray"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberTypeRule).WithLocation(15).WithArguments("SomeClass", "GetSomeDataArray")); + await VerifyCS.VerifyAnalyzerAsync(code); } - public async Task MemberIsNotStatic_Diagnostic() + [TestMethod] + public async Task MemberIsShadowingBase_NoDiagnostic() { string code = """ using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; + public abstract class MyTestClassBase + { + public static IEnumerable Data => new List(); + public static IEnumerable GetData() => new List(); + } + [TestClass] - public class MyTestClass + public class MyTestClass : MyTestClassBase { - [{|#0:DynamicData("Data")|}] + [DynamicData("Data")] [TestMethod] public void TestMethod1(object[] o) { } - [{|#1:DynamicData("GetData", DynamicDataSourceType.Method)|}] + [DynamicData("GetData", DynamicDataSourceType.Method)] [TestMethod] public void TestMethod2(object[] o) { } - public IEnumerable Data => new List(); - public IEnumerable GetData() => new List(); + public static IEnumerable Data => new List(); + public static IEnumerable GetData() => new List(); } """; - await VerifyCS.VerifyAnalyzerAsync( - code, - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(0).WithArguments("MyTestClass", "Data"), - VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(1).WithArguments("MyTestClass", "GetData")); + await VerifyCS.VerifyAnalyzerAsync(code); } - public async Task MemberIsNotPublic_NoDiagnostic() + [TestMethod] + public async Task MemberIsFromBase_NoDiagnostic() { string code = """ using System; using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; + public abstract class MyTestClassBase + { + public static IEnumerable Data => new List(); + public static IEnumerable GetData() => new List(); + } + [TestClass] - public class MyTestClass + public class MyTestClass : MyTestClassBase { [DynamicData("Data")] [TestMethod] @@ -732,15 +880,13 @@ public void TestMethod1(object[] o) public void TestMethod2(object[] o) { } - - private static IEnumerable Data => new List(); - private static IEnumerable GetData() => new List(); } """; await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task MethodHasParameters_Diagnostic() { string code = """ @@ -774,6 +920,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(1).WithArguments("MyTestClass", "GetData2")); } + [TestMethod] public async Task MethodIsGeneric_Diagnostic() { string code = """ @@ -799,6 +946,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DataMemberSignatureRule).WithLocation(0).WithArguments("MyTestClass", "GetData")); } + [TestMethod] public async Task WhenDisplayMemberIsValid_NoDiagnostic() { string code = """ @@ -873,6 +1021,7 @@ public class SomeClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenDisplayMemberIsNotFound_Diagnostic() { string code = """ @@ -924,6 +1073,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.MemberNotFoundRule).WithLocation(3).WithArguments("SomeClass", "MemberNotFound")); } + [TestMethod] public async Task WhenDisplayMemberIsNotPublic_Diagnostic() { string code = """ @@ -979,6 +1129,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DisplayMethodSignatureRule).WithLocation(3).WithArguments("SomeClass", "GetSomeDisplayName")); } + [TestMethod] public async Task WhenDisplayMemberIsNotStatic_Diagnostic() { string code = """ @@ -1034,6 +1185,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DisplayMethodSignatureRule).WithLocation(3).WithArguments("SomeClass", "GetSomeDisplayName")); } + [TestMethod] public async Task WhenDisplayMemberDoesNotReturnString_Diagnostic() { string code = """ @@ -1089,6 +1241,7 @@ await VerifyCS.VerifyAnalyzerAsync( VerifyCS.Diagnostic(DynamicDataShouldBeValidAnalyzer.DisplayMethodSignatureRule).WithLocation(3).WithArguments("SomeClass", "GetSomeDisplayName")); } + [TestMethod] public async Task WhenDisplayMemberInvalidParameters_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj index b9dbd809a1..d525f44713 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/MSTest.Analyzers.UnitTests.csproj @@ -1,11 +1,16 @@ - + net6.0 false + true true true - true + + + + + true @@ -17,6 +22,10 @@ + + + + @@ -24,8 +33,8 @@ - + diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs index 12c094c2c4..23d263569a 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/NonNullableReferenceNotInitializedSuppressorTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,9 +12,10 @@ namespace MSTest.Analyzers.UnitTests; -[TestGroup] -public sealed class NonNullableReferenceNotInitializedSuppressorTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class NonNullableReferenceNotInitializedSuppressorTests { + [TestMethod] public async Task TestContextPropertyOnTestClass_DiagnosticIsSuppressed() { string code = @" @@ -43,6 +43,7 @@ public class SomeClass }.RunAsync(); } + [TestMethod] public async Task TestContextPropertyOnNonTestClass_DiagnosticIsNotSuppressed() { string code = @" diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs index f6a9729152..69f3b533a6 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PreferAssertFailOverAlwaysFalseConditionsAnalyzerTests { + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterIsNullable_NoDiagnostic() { string code = """ @@ -30,6 +31,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterIsNotNullable_Diagnostic() { string code = """ @@ -64,6 +66,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterAsPropertySymbolIsNotNullable_Diagnostic() { string code = """ @@ -99,6 +102,7 @@ public void Test() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterAsPropertySymbolIsNullable_NoDiagnostic() { string code = """ @@ -120,6 +124,7 @@ public void Test() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_Diagnostic() { string code = """ @@ -163,6 +168,7 @@ public class ObjectClass await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_WithoutEnableNullable_NoDiagnostic() { string code = """ @@ -187,6 +193,7 @@ public class ObjectClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenIsNullAssertion_ValueParameterAsReferenceObjectIsNullable_NoDiagnostic() { string code = """ @@ -213,6 +220,7 @@ public class ObjectClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedTrue_NoDiagnostic() { string code = """ @@ -232,6 +240,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedTrue_WithMessage_NoDiagnostic() { string code = """ @@ -251,6 +260,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedTrue_WithMessageFirst_NoDiagnostic() { string code = """ @@ -270,6 +280,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedFalse_Diagnostic() { string code = """ @@ -302,6 +313,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedFalse_WithMessage_Diagnostic() { string code = """ @@ -326,13 +338,52 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail("message"); + } + } + """; + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueIsPassedFalse_WithMessageAndArgsAsParams_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + [|Assert.IsTrue(false, "message", "1")|]; + [|Assert.IsTrue(false, "message", "1", "2")|]; + [|Assert.IsTrue(false, "message", new object[] { "1", "2" })|]; + [|Assert.IsTrue(message: "message", parameters: new object[] { "1", "2" }, condition: false)|]; + } + } + """; + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + Assert.Fail("message", "1"); + Assert.Fail("message", "1", "2"); + Assert.Fail("message", new object[] { "1", "2" }); + Assert.Fail(message: "message", parameters: new object[] { "1", "2" }); } } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedFalse_WithMessageFirst_Diagnostic() { string code = """ @@ -357,13 +408,14 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedUnknown_NoDiagnostic() { string code = """ @@ -385,6 +437,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -406,6 +459,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -427,6 +481,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedTrue_Diagnostic() { string code = """ @@ -459,6 +514,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedTrue_WithMessage_Diagnostic() { string code = """ @@ -483,7 +539,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail("message"); } } """; @@ -491,6 +547,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedTrue_WithMessageFirst_Diagnostic() { string code = """ @@ -515,7 +572,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; @@ -523,6 +580,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedFalse_NoDiagnostic() { string code = """ @@ -542,6 +600,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedFalse_WithMessage_NoDiagnostic() { string code = """ @@ -561,6 +620,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedFalse_WithMessageFirst_NoDiagnostic() { string code = """ @@ -580,6 +640,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedUnknown_NoDiagnostic() { string code = """ @@ -601,6 +662,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -622,6 +684,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -643,6 +706,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNotNullIsPassedNull_Diagnostic() { string code = """ @@ -675,6 +739,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsNotNullIsPassedNull_WithMessage_Diagnostic() { string code = """ @@ -699,7 +764,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail("message"); } } """; @@ -707,6 +772,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsNotNullIsPassedNull_WithMessageFirst_Diagnostic() { string code = """ @@ -731,7 +797,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; @@ -739,6 +805,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertIsNotNullIsPassedUnknown_NoDiagnostic() { string code = """ @@ -760,6 +827,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNotNullIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -781,6 +849,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNotNullIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -802,6 +871,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_NoDiagnostic() { string code = """ @@ -821,6 +891,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_WithMessage_NoDiagnostic() { string code = """ @@ -840,6 +911,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_WithMessageFirst_NoDiagnostic() { string code = """ @@ -859,6 +931,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_WithMessageSecond_NoDiagnostic() { string code = """ @@ -878,6 +951,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_Diagnostic() { string code = """ @@ -910,6 +984,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessage_Diagnostic() { string code = """ @@ -934,7 +1009,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail("message"); } } """; @@ -942,6 +1017,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessageFirst_Diagnostic() { string code = """ @@ -966,7 +1042,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; @@ -974,6 +1050,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessageSecond_Diagnostic() { string code = """ @@ -998,7 +1075,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; @@ -1006,6 +1083,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_NoDiagnostic() { string code = """ @@ -1027,6 +1105,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -1048,6 +1127,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -1069,6 +1149,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() { string code = """ @@ -1090,6 +1171,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_Diagnostic() { string code = """ @@ -1122,6 +1204,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessage_Diagnostic() { string code = """ @@ -1146,7 +1229,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail("message"); } } """; @@ -1154,6 +1237,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessageFirst_Diagnostic() { string code = """ @@ -1178,7 +1262,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; @@ -1186,6 +1270,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessageSecond_Diagnostic() { string code = """ @@ -1210,7 +1295,7 @@ public class MyTestClass [TestMethod] public void TestMethod() { - Assert.Fail(); + Assert.Fail(message: "message"); } } """; @@ -1218,6 +1303,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_NoDiagnostic() { string code = """ @@ -1237,6 +1323,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessage_NoDiagnostic() { string code = """ @@ -1256,6 +1343,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessageFirst_NoDiagnostic() { string code = """ @@ -1275,6 +1363,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessageSecond_NoDiagnostic() { string code = """ @@ -1294,6 +1383,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_NoDiagnostic() { string code = """ @@ -1315,6 +1405,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -1336,6 +1427,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -1357,6 +1449,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs index 4bead3d728..f6335eedb7 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class PreferConstructorOverTestInitializeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PreferConstructorOverTestInitializeAnalyzerTests { + [TestMethod] public async Task WhenTestClassHasCtor_NoDiagnostic() { string code = """ @@ -27,6 +28,7 @@ public MyTestClass() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestClassHasTestInitialize_Diagnostic() { string code = """ @@ -56,6 +58,7 @@ public MyTestClass() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestInitializeAsync_NoDiagnostic() { string code = """ @@ -76,6 +79,7 @@ public Task MyTestInit() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestClassHasTestInitializeAndCtor_Diagnostic() { string code = """ @@ -110,6 +114,7 @@ public MyTestClass() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestInitializeAndCtorWithBody_Diagnostic() { string code = """ @@ -150,6 +155,7 @@ public MyTestClass() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestInitializeAndCtorWithBothHavingBody_Diagnostic() { string code = """ @@ -196,6 +202,7 @@ public MyTestClass() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestInitializeAndTwoCtor_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs index 4c93ee48f4..65bb4d83c5 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class PreferDisposeOverTestCleanupAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PreferDisposeOverTestCleanupAnalyzerTests { + [TestMethod] public async Task WhenTestClassHasDispose_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public void Dispose() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestClassHasDisposeAsync_NoDiagnostic() { string code = """ @@ -48,6 +50,7 @@ public ValueTask DisposeAsync() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestClassHasTestCleanup_Diagnostic() { string code = """ @@ -79,6 +82,7 @@ public void Dispose() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestCleanup_WithAnotherBaseClass_Diagnostic() { string code = """ @@ -113,6 +117,7 @@ public void Dispose() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestCleanup_AndHasDispose_Diagnostic() { string code = """ @@ -151,6 +156,7 @@ public void Dispose() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestCleanup_AndHasDisposeInAnotherPartial_Diagnostic() { // This scenario is currently broken. The test is to document the current behavior @@ -201,6 +207,7 @@ public partial class MyTestClass : IDisposable await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestCleanupTask_Diagnostic() { string code = """ @@ -223,6 +230,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestClassHasTestCleanupValueTask_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs index 278c0a417f..fcee06522e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestCleanupOverDisposeAnalyzerTests.cs @@ -7,9 +7,28 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class PreferTestCleanupOverDisposeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PreferTestCleanupOverDisposeAnalyzerTests { + [TestMethod] + public async Task WhenNonTestClassHasDispose_NoDiagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class MyNonTestClass : IDisposable + { + public void Dispose() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] public async Task WhenTestClassHasDispose_Diagnostic() { string code = """ @@ -49,6 +68,7 @@ public interface IMyInterface { } await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasDisposeAsync_Diagnostic() { string code = """ @@ -84,6 +104,7 @@ public ValueTask TestCleanup() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasTestCleanup_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs index 2b033a31e9..ed62792b41 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferTestInitializeOverConstructorAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class PreferTestInitializeOverConstructorAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PreferTestInitializeOverConstructorAnalyzerTests { + [TestMethod] public async Task WhenTestClassHasCtor_Diagnostic() { string code = """ @@ -39,6 +40,7 @@ public void TestInitialize() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHas_TwoCtorandExsitesTestInitialize_Diagnostic() { string code = """ @@ -97,6 +99,7 @@ public MyTestClass(int i) await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClass_WithLocalTestInitializeAttribute_Diagnostic() { string code = """ @@ -151,6 +154,7 @@ public void TestInitialize() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestClassHasImplicitCtor_NoDiagnostic() { string code = """ @@ -165,6 +169,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestClassHasParameterizedCtor_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs index 040e4af637..6ad08378a2 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Program.cs @@ -1,15 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.Testing.Extensions; -using Microsoft.Testing.Internal.Framework.Configurations; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] // DebuggerUtility.AttachVSToCurrentProcess(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new MSTest.Analyzers.UnitTests.SourceGeneratedTestNodesBuilder()); +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json b/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json index d7b9c2736a..485ee85ee1 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "MSTest.Analyzers.UnitTests": { "commandName": "Project", - "commandLineArgs": "--treenode-filter /*/*/*/**" + "commandLineArgs": "" } } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs index da6fa23d27..2a7548dbdd 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicMethodShouldBeTestMethodAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class PublicMethodShouldBeTestMethodAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PublicMethodShouldBeTestMethodAnalyzerTests { + [TestMethod] public async Task WhenMethodIsPrivate_NoDiagnostic() { string code = """ @@ -27,6 +28,7 @@ private void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPublicButWithInvalidTestMethodSignature_NoDiagnostic() { string code = """ @@ -44,6 +46,7 @@ public static void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPublicAndMarkedAsTestMethod_NoDiagnostic() { string code = """ @@ -62,6 +65,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPrivateButNotInsideTestClass_NoDiagnostic() { string code = """ @@ -78,6 +82,7 @@ private void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPublicAndMarkedAsDerivedTestMethodAttribute_NoDiagnostic() { string code = """ @@ -99,6 +104,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPublicAndNotMarkedAsTestMethodWithInheritanceFromBaseTestClass_NoDiagnostic() { string code = """ @@ -120,6 +126,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPrivateAndNotMarkedAsTestMethodWithInheritedTestClassAttribute_NoDiagnostic() { string code = """ @@ -141,6 +148,7 @@ private void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPublicAndNotMarkedAsTestMethod_Diagnostic() { string code = """ @@ -171,6 +179,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenMethodIsPublicAndNotMarkedAsTestMethodWithInheritedTestClassAttribute_Diagnostic() { string code = """ @@ -208,6 +217,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenMethodIsPublicAndMarkedAsTestInitialize_NoDiagnostic() { string code = """ @@ -225,6 +235,7 @@ public void TestInitialize() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenMethodIsPublicAndMarkedAsTestCleanup_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs index f9d13370ab..5aa41648b9 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PublicTypeShouldBeTestClassAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.UnitTests; -[TestGroup] -public sealed class PublicTypeShouldBeTestClassAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class PublicTypeShouldBeTestClassAnalyzerTests { + [TestMethod] public async Task WhenClassIsPublicAndNotTestClass_Diagnostic() { string code = """ @@ -34,6 +35,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsPublicAndNotTestClassAndHaveAnotherAttribute_Diagnostic() { string code = """ @@ -60,6 +62,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsPublicAndNotClass_NoDiagnostic() { string code = """ @@ -79,6 +82,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassIsPublicAndAbstract_NoDiagnostic() { string code = """ @@ -94,6 +98,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenClassIsPublicAndStatic_NoDiagnostic() { string code = """ @@ -109,6 +114,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenTypeIsNotPublicAndNotTestClass_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs index 3116986593..a4d68420e0 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/ReviewAlwaysTrueAssertConditionAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class ReviewAlwaysTrueAssertConditionAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class ReviewAlwaysTrueAssertConditionAnalyzerTests { + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterIsNotNullable_NoDiagnostic() { string code = """ @@ -30,6 +31,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterIsNotNullable_Diagnostic() { string code = """ @@ -50,6 +52,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterAsPropertySymbolIsNotNullable_Diagnostic() { string code = """ @@ -71,6 +74,7 @@ public void Test() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterAsPropertySymbolIsNullable_NoDiagnostic() { string code = """ @@ -92,6 +96,7 @@ public void Test() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_Diagnostic() { string code = """ @@ -117,6 +122,7 @@ public class ObjectClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_WithoutEnableNullable_NoDiagnostic() { string code = """ @@ -141,6 +147,7 @@ public class ObjectClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenIsNotNullAssertion_ValueParameterAsReferenceObjectIsNotNullable_NoDiagnostic() { string code = """ @@ -167,6 +174,7 @@ public class ObjectClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedTrue_NoDiagnostic() { string code = """ @@ -186,6 +194,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedTrue_WithMessage_NoDiagnostic() { string code = """ @@ -205,6 +214,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedTrue_WithMessageFirst_NoDiagnostic() { string code = """ @@ -224,6 +234,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedFalse_Diagnostic() { string code = """ @@ -243,6 +254,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedFalse_WithMessage_Diagnostic() { string code = """ @@ -262,6 +274,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedFalse_WithMessageFirst_Diagnostic() { string code = """ @@ -281,6 +294,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedUnknown_NoDiagnostic() { string code = """ @@ -302,6 +316,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -323,6 +338,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsFalseIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -344,6 +360,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedTrue_Diagnostic() { string code = """ @@ -363,6 +380,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedTrue_WithMessage_Diagnostic() { string code = """ @@ -382,6 +400,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedTrue_WithMessageFirst_Diagnostic() { string code = """ @@ -401,6 +420,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedFalse_NoDiagnostic() { string code = """ @@ -420,6 +440,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedFalse_WithMessage_NoDiagnostic() { string code = """ @@ -439,6 +460,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedFalse_WithMessageFirst_NoDiagnostic() { string code = """ @@ -458,6 +480,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedUnknown_NoDiagnostic() { string code = """ @@ -479,6 +502,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -500,6 +524,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsTrueIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -521,6 +546,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNullIsPassedNull_Diagnostic() { string code = """ @@ -540,6 +566,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNullIsPassedNull_WithMessage_Diagnostic() { string code = """ @@ -559,6 +586,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNullIsPassedNull_WithMessageFirst_Diagnostic() { string code = """ @@ -578,6 +606,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNullIsPassedUnknown_NoDiagnostic() { string code = """ @@ -599,6 +628,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNullIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -620,6 +650,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertIsNullIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -641,6 +672,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_NoDiagnostic() { string code = """ @@ -660,6 +692,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessage_NoDiagnostic() { string code = """ @@ -679,6 +712,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessageFirst_NoDiagnostic() { string code = """ @@ -698,6 +732,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedEqual_WithMessageSecond_NoDiagnostic() { string code = """ @@ -717,6 +752,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_Diagnostic() { string code = """ @@ -736,6 +772,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessage_Diagnostic() { string code = """ @@ -755,6 +792,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessageFirst_Diagnostic() { string code = """ @@ -774,6 +812,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedNonEqual_WithMessageSecond_Diagnostic() { string code = """ @@ -793,6 +832,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_NoDiagnostic() { string code = """ @@ -814,6 +854,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -835,6 +876,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -856,6 +898,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreNotEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() { string code = """ @@ -877,6 +920,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_Diagnostic() { string code = """ @@ -896,6 +940,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_WithMessage_Diagnostic() { string code = """ @@ -915,6 +960,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_WithMessageFirst_Diagnostic() { string code = """ @@ -934,6 +980,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedEqual_WithMessageSecond_Diagnostic() { string code = """ @@ -953,6 +1000,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_NoDiagnostic() { string code = """ @@ -972,6 +1020,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessage_NoDiagnostic() { string code = """ @@ -991,6 +1040,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessageFirst_NoDiagnostic() { string code = """ @@ -1010,6 +1060,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedNonEqual_WithMessageSecond_NoDiagnostic() { string code = """ @@ -1029,6 +1080,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_NoDiagnostic() { string code = """ @@ -1050,6 +1102,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_WithMessage_NoDiagnostic() { string code = """ @@ -1071,6 +1124,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_WithMessageFirst_NoDiagnostic() { string code = """ @@ -1092,6 +1146,7 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenAssertAreEqualIsPassedUnknown_WithMessageSecond_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs index 6e10dd3640..82c94cc4f3 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestClassShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestClassShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenClassIsPublicAndTestClass_NoDiagnostic() { string code = """ @@ -24,6 +25,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenClassIsInternalAndTestClass_Diagnostic() { string code = """ @@ -52,8 +54,9 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("private")] - [Arguments("internal")] + [DataRow("private")] + [DataRow("internal")] + [TestMethod] public async Task WhenClassIsInnerAndNotPublicTestClass_Diagnostic(string accessibility) { string code = $$""" @@ -68,7 +71,8 @@ public class OuterClass } """; - string fixedCode = $$""" + string fixedCode = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; public class OuterClass @@ -88,6 +92,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsInternalAndNotTestClass_NoDiagnostic() { string code = """ @@ -101,6 +106,7 @@ internal class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenClassIsPublicAndTestClassAsInnerOfInternalClass_Diagnostic() { string code = """ @@ -122,6 +128,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments("MyTestClass")); } + [TestMethod] public async Task WhenClassIsGeneric_NoDiagnostic() { string code = """ @@ -136,6 +143,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenDiscoverInternalsAndTypeIsInternal_NoDiagnostic() { string code = """ @@ -152,6 +160,7 @@ internal class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenDiscoverInternalsAndTypeIsPrivate_Diagnostic() { string code = """ @@ -190,6 +199,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsStaticAndEmpty_NoDiagnostic() { string code = """ @@ -204,6 +214,7 @@ public static class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenClassIsStaticAndContainsAssemblyInitialize_NoDiagnostic() { string code = """ @@ -222,6 +233,7 @@ public static void AssemblyInit(TestContext context) await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenClassIsStaticAndContainsAssemblyCleanup_NoDiagnostic() { string code = """ @@ -240,6 +252,7 @@ public static void AssemblyCleanup() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenClassIsStaticAndContainsClassInitialize_Diagnostic() { string code = """ @@ -276,6 +289,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsStaticAndContainsClassCleanup_Diagnostic() { string code = """ @@ -310,6 +324,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsStaticAndContainsTestInitialize_Diagnostic() { string code = """ @@ -345,6 +360,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsStaticAndContainsTestCleanup_Diagnostic() { string code = """ @@ -380,6 +396,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenClassIsStaticAndContainsTestMethod_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs index 495665896d..46bff4564b 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestClassShouldHaveTestMethodAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestClassShouldHaveTestMethodAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestClassShouldHaveTestMethodAnalyzerTests { + [TestMethod] public async Task WhenTestClassHasTestMethod_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenStaticTestClassWithAssemblyCleanup_DoesNotHaveTestMethod_NoDiagnostic() { string code = """ @@ -46,6 +48,7 @@ public static void AssemblyCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenStaticTestClassWithAssemblyInitialization_DoesNotHaveTestMethod_NoDiagnostic() { string code = """ @@ -64,6 +67,7 @@ public static void AssemblyInitialize(TestContext context) await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassDoesNotHaveTestMethod_Diagnostic() { string code = """ @@ -82,6 +86,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments("MyTestClass")); } + [TestMethod] public async Task WhenStaticTestClassWithoutAssemblyAttributes_DoesNotHaveTestMethod_Diagnostic() { string code = """ @@ -99,6 +104,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments("MyTestClass")); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromAbstractClassHasTestMethod_NoDiagnostic() { string code = """ @@ -118,6 +124,7 @@ public class Derived : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromClassHasTestMethod_NoDiagnostic() { string code = """ @@ -137,6 +144,7 @@ public class Derived : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromTestClassHasTestMethod_NoDiagnostic() { string code = """ @@ -157,6 +165,7 @@ public class Derived : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromAbstractTestClassHasTestMethod_NoDiagnostic() { string code = """ @@ -177,6 +186,7 @@ public class Derived : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromBaseBaseClassHasTestMethod_NoDiagnostic() { string code = """ @@ -200,6 +210,7 @@ public class Derived : BaseClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromClassDoesNotHaveTestMethod_Diagnostic() { string code = """ @@ -221,6 +232,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments("Derived")); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromClassHasAssemblyInitialize_Diagnostic() { string code = """ @@ -246,6 +258,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments("Derived")); } + [TestMethod] public async Task WhenTestClassWithoutAssemblyAttributesAndTestMethod_InheritsFromBaseBaseClassHasAssemblyCleanup_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs index b30e073f5a..cdfea41d81 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestCleanupShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestCleanupShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestCleanupShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenTestCleanupIsPublic_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public void TestCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestCleanupIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ @@ -48,6 +50,7 @@ public void TestCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestCleanupIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() { string code = """ @@ -88,10 +91,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenTestCleanupIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -128,6 +132,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupIsNotOrdinary_Diagnostic() { string code = """ @@ -149,6 +154,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenTestCleanupIsAbstract_Diagnostic() { string code = """ @@ -181,6 +187,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupIsGeneric_Diagnostic() { string code = """ @@ -215,6 +222,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupIsStatic_Diagnostic() { string code = """ @@ -249,6 +257,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupHasParameters_Diagnostic() { string code = """ @@ -283,6 +292,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -358,6 +368,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -389,6 +400,7 @@ public ValueTask TestCleanup2() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestCleanupIsAsyncVoid_Diagnostic() { string code = """ @@ -427,6 +439,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenMultipleViolations_TheyAllGetFixed() { string code = """ @@ -465,6 +478,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestCleanupIsNotOnClass_Diagnostic() { string code = """ @@ -482,6 +496,7 @@ public struct MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestCleanupIsOnSealedClassNotMarkedWithTestClass_Diagnostic() { string code = """ @@ -499,6 +514,7 @@ public sealed class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestCleanupIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() { string code = """ @@ -516,6 +532,7 @@ public void TestCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestCleanupIsOnGenericClass_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs index 141b0f0202..acd349abd4 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestContextShouldBeValidAnalyzerTests.cs @@ -7,25 +7,26 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestContextShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestContextShouldBeValidAnalyzerTests { - [Arguments("TestContext", "private")] - [Arguments("TestContext", "public")] - [Arguments("TestContext", "internal")] - [Arguments("TestContext", "protected")] - [Arguments("testcontext", "private")] - [Arguments("testcontext", "public")] - [Arguments("testcontext", "internal")] - [Arguments("testcontext", "protected")] - [Arguments("TESTCONTEXT", "private")] - [Arguments("TESTCONTEXT", "public")] - [Arguments("TESTCONTEXT", "internal")] - [Arguments("TESTCONTEXT", "protected")] - [Arguments("TeStCoNtExT", "private")] - [Arguments("TeStCoNtExT", "public")] - [Arguments("TeStCoNtExT", "internal")] - [Arguments("TeStCoNtExT", "protected")] + [DataRow("TestContext", "private")] + [DataRow("TestContext", "public")] + [DataRow("TestContext", "internal")] + [DataRow("TestContext", "protected")] + [DataRow("testcontext", "private")] + [DataRow("testcontext", "public")] + [DataRow("testcontext", "internal")] + [DataRow("testcontext", "protected")] + [DataRow("TESTCONTEXT", "private")] + [DataRow("TESTCONTEXT", "public")] + [DataRow("TESTCONTEXT", "internal")] + [DataRow("TESTCONTEXT", "protected")] + [DataRow("TeStCoNtExT", "private")] + [DataRow("TeStCoNtExT", "public")] + [DataRow("TeStCoNtExT", "internal")] + [DataRow("TeStCoNtExT", "protected")] + [TestMethod] public async Task WhenTestContextCaseInsensitiveIsField_Diagnostic(string fieldName, string accessibility) { string code = $$""" @@ -37,7 +38,8 @@ public class MyTestClass {{accessibility}} TestContext {|#0:{{fieldName}}|}; } """; - string fixedCode = $$""" + string fixedCode = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -52,22 +54,23 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("TestContext", "private")] - [Arguments("TestContext", "public")] - [Arguments("TestContext", "internal")] - [Arguments("TestContext", "protected")] - [Arguments("testcontext", "private")] - [Arguments("testcontext", "public")] - [Arguments("testcontext", "internal")] - [Arguments("testcontext", "protected")] - [Arguments("TESTCONTEXT", "private")] - [Arguments("TESTCONTEXT", "public")] - [Arguments("TESTCONTEXT", "internal")] - [Arguments("TESTCONTEXT", "protected")] - [Arguments("TeStCoNtExT", "private")] - [Arguments("TeStCoNtExT", "public")] - [Arguments("TeStCoNtExT", "internal")] - [Arguments("TeStCoNtExT", "protected")] + [DataRow("TestContext", "private")] + [DataRow("TestContext", "public")] + [DataRow("TestContext", "internal")] + [DataRow("TestContext", "protected")] + [DataRow("testcontext", "private")] + [DataRow("testcontext", "public")] + [DataRow("testcontext", "internal")] + [DataRow("testcontext", "protected")] + [DataRow("TESTCONTEXT", "private")] + [DataRow("TESTCONTEXT", "public")] + [DataRow("TESTCONTEXT", "internal")] + [DataRow("TESTCONTEXT", "protected")] + [DataRow("TeStCoNtExT", "private")] + [DataRow("TeStCoNtExT", "public")] + [DataRow("TeStCoNtExT", "internal")] + [DataRow("TeStCoNtExT", "protected")] + [TestMethod] public async Task WhenTestContextCaseInsensitiveIsField_AssignedInConstructor_NoDiagnostic(string fieldName, string accessibility) { string code = $$""" @@ -88,14 +91,15 @@ public MyTestClass(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } - [Arguments("TestContext", "private")] - [Arguments("TestContext", "internal")] - [Arguments("testcontext", "private")] - [Arguments("testcontext", "internal")] - [Arguments("TESTCONTEXT", "private")] - [Arguments("TESTCONTEXT", "internal")] - [Arguments("TeStCoNtExT", "private")] - [Arguments("TeStCoNtExT", "internal")] + [DataRow("TestContext", "private")] + [DataRow("TestContext", "internal")] + [DataRow("testcontext", "private")] + [DataRow("testcontext", "internal")] + [DataRow("TESTCONTEXT", "private")] + [DataRow("TESTCONTEXT", "internal")] + [DataRow("TeStCoNtExT", "private")] + [DataRow("TeStCoNtExT", "internal")] + [TestMethod] public async Task WhenTestContextPropertyIsPrivateOrInternal_Diagnostic(string propertyName, string accessibility) { string code = $$""" @@ -107,7 +111,8 @@ public class MyTestClass {{accessibility}} TestContext {|#0:{{propertyName}}|} { get; set; } } """; - string fixedCode = $$""" + string fixedCode = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -123,14 +128,15 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("TestContext", "private")] - [Arguments("TestContext", "internal")] - [Arguments("testcontext", "private")] - [Arguments("testcontext", "internal")] - [Arguments("TESTCONTEXT", "private")] - [Arguments("TESTCONTEXT", "internal")] - [Arguments("TeStCoNtExT", "private")] - [Arguments("TeStCoNtExT", "internal")] + [DataRow("TestContext", "private")] + [DataRow("TestContext", "internal")] + [DataRow("testcontext", "private")] + [DataRow("testcontext", "internal")] + [DataRow("TESTCONTEXT", "private")] + [DataRow("TESTCONTEXT", "internal")] + [DataRow("TeStCoNtExT", "private")] + [DataRow("TeStCoNtExT", "internal")] + [TestMethod] public async Task WhenTestContextPropertyIsPrivateOrInternal_AssignedInConstructor_NoDiagnostic(string propertyName, string accessibility) { string code = $$""" @@ -151,8 +157,9 @@ public MyTestClass(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } - [Arguments(true)] - [Arguments(false)] + [DataRow(true)] + [DataRow(false)] + [TestMethod] public async Task WhenTestContextPropertyIsValid_NoDiagnostic(bool discoverInternals) { string code = $$""" @@ -170,6 +177,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenDiscoverInternalsTestContextPropertyIsPrivate_Diagnostic() { string code = """ @@ -202,6 +210,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenDiscoverInternalsTestContextPropertyIsPrivate_AssignedInConstructor_NoDiagnostic() { string code = """ @@ -224,6 +233,7 @@ public MyTestClass(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenDiscoverInternalsTestContextPropertyIsInternal_Diagnostic() { string code = """ @@ -253,6 +263,7 @@ public class MyTestClass await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenDiscoverInternalsTestContextPropertyIsInternal_AssignedInConstructor_NoDiagnostic() { string code = """ @@ -275,9 +286,11 @@ public MyTestClass(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestContextPropertyIsStatic_Diagnostic() { - string code = $$""" + string code = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -286,7 +299,8 @@ public class MyTestClass public static TestContext {|#0:TestContext|} { get; set; } } """; - string fixedCode = $$""" + string fixedCode = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -303,9 +317,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestContextPropertyIsReadonly_Diagnostic() { - string code = $$""" + string code = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -314,7 +330,8 @@ public class MyTestClass public TestContext {|#0:TestContext|} { get; } } """; - string fixedCode = $$""" + string fixedCode = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -331,9 +348,42 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] + public async Task WhenTestContextPropertyIsNotCasedCorrectly_Diagnostic() + { + string code = + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public TestContext {|#0:testContext|} { get; set; } + } + """; + string fixedCode = + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public TestContext TestContext { get; set; } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic(TestContextShouldBeValidAnalyzer.TestContextShouldBeValidRule) + .WithLocation(0), + fixedCode); + } + + [TestMethod] public async Task WhenTestContextPropertyIsReadonly_AssignedInConstructor_NoDiagnostic() { - string code = $$""" + string code = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -343,7 +393,6 @@ public MyTestClass(TestContext testContext) { TestContext = testContext; } - public TestContext TestContext { get; } } """; @@ -351,22 +400,45 @@ public MyTestClass(TestContext testContext) await VerifyCS.VerifyCodeFixAsync(code, code); } - [Arguments("TestContext", "private")] - [Arguments("TestContext", "public")] - [Arguments("TestContext", "internal")] - [Arguments("TestContext", "protected")] - [Arguments("testcontext", "private")] - [Arguments("testcontext", "public")] - [Arguments("testcontext", "internal")] - [Arguments("testcontext", "protected")] - [Arguments("TESTCONTEXT", "private")] - [Arguments("TESTCONTEXT", "public")] - [Arguments("TESTCONTEXT", "internal")] - [Arguments("TESTCONTEXT", "protected")] - [Arguments("TeStCoNtExT", "private")] - [Arguments("TeStCoNtExT", "public")] - [Arguments("TeStCoNtExT", "internal")] - [Arguments("TeStCoNtExT", "protected")] + [TestMethod] + public async Task WhenTestContextPropertyIsReadonly_AssignedInConstructorViaField_NoDiagnostic() + { + string code = + """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private readonly TestContext _testContext; + public MyTestClass(TestContext testContext) + { + _testContext = testContext; + } + public TestContext TestContext => _testContext; + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [DataRow("TestContext", "private")] + [DataRow("TestContext", "public")] + [DataRow("TestContext", "internal")] + [DataRow("TestContext", "protected")] + [DataRow("testcontext", "private")] + [DataRow("testcontext", "public")] + [DataRow("testcontext", "internal")] + [DataRow("testcontext", "protected")] + [DataRow("TESTCONTEXT", "private")] + [DataRow("TESTCONTEXT", "public")] + [DataRow("TESTCONTEXT", "internal")] + [DataRow("TESTCONTEXT", "protected")] + [DataRow("TeStCoNtExT", "private")] + [DataRow("TeStCoNtExT", "public")] + [DataRow("TeStCoNtExT", "internal")] + [DataRow("TeStCoNtExT", "protected")] + [TestMethod] public async Task WhenTestContextIsFieldNotOnTestClass_NoDiagnostic(string fieldName, string accessibility) { string code = $$""" diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs index e46e21c15e..d8eb9cbe5e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestInitializeShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestInitializeShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestInitializeShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenTestInitializeIsPublic_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public void TestInitialize() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestInitializeIsNotOrdinary_Diagnostic() { string code = """ @@ -49,6 +51,7 @@ await VerifyCS.VerifyCodeFixAsync( code); } + [TestMethod] public async Task WhenTestInitializeIsPublic_InsideInternalClassWithDiscoverInternals_NoDiagnostic() { string code = """ @@ -69,6 +72,7 @@ public void TestInitialize() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestInitializeIsInternal_InsidePublicClassWithDiscoverInternals_Diagnostic() { string code = """ @@ -107,10 +111,11 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenTestInitializeIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -147,6 +152,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeIsAbstract_Diagnostic() { string code = """ @@ -179,6 +185,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeIsGeneric_Diagnostic() { string code = """ @@ -213,6 +220,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeIsStatic_Diagnostic() { string code = """ @@ -247,6 +255,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeHasParameters_Diagnostic() { string code = """ @@ -281,6 +290,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -356,6 +366,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -387,6 +398,7 @@ public ValueTask TestInitialize2() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestInitializeIsAsyncVoid_Diagnostic() { string code = """ @@ -425,6 +437,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenMultipleViolations_TheyAllGetFixed() { string code = """ @@ -463,6 +476,7 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] public async Task WhenTestInitializeIsNotOnClass_Diagnostic() { string code = """ @@ -480,6 +494,7 @@ public struct MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestInitializeIsOnSealedClassNotMarkedWithTestClass_Diagnostic() { string code = """ @@ -497,6 +512,7 @@ public sealed class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestInitializeIsOnNonSealedClassNotMarkedWithTestClass_NoDiagnostic() { string code = """ @@ -514,6 +530,7 @@ public void TestInitialize() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestInitializeIsOnGenericClass_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs index a6b598bec1..75ba66adbb 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldBeValidAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestMethodShouldBeValidAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestMethodShouldBeValidAnalyzerTests { + [TestMethod] public async Task WhenTestMethodIsPublic_NoDiagnostic() { string code = """ @@ -28,10 +29,11 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } - [Arguments("protected")] - [Arguments("internal")] - [Arguments("internal protected")] - [Arguments("private")] + [DataRow("protected")] + [DataRow("internal")] + [DataRow("internal protected")] + [DataRow("private")] + [TestMethod] public async Task WhenTestMethodIsNotPublic_Diagnostic(string accessibility) { string code = $$""" @@ -47,7 +49,8 @@ public class MyTestClass } """; - string fixedCode = $$""" + string fixedCode = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -63,9 +66,11 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenMethodIsNotPublicAndNotTestMethod_NoDiagnostic() { - string code = $$""" + string code = + """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -92,6 +97,7 @@ internal void InternalMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestMethodIsStatic_Diagnostic() { string code = """ @@ -123,6 +129,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestMethodIsAbstract_Diagnostic() { string code = """ @@ -152,6 +159,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestMethodIsGeneric_Diagnostic() { string code = """ @@ -164,6 +172,12 @@ public class MyTestClass public void [|MyTestMethod|]() { } + + [TestMethod] + [DataRow(0)] + public void MyTestMethod(T t) + { + } } """; @@ -177,12 +191,19 @@ public class MyTestClass public void MyTestMethod() { } + + [TestMethod] + [DataRow(0)] + public void MyTestMethod(T t) + { + } } """; await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestMethodIsNotOrdinary_Diagnostic() { string code = """ @@ -201,6 +222,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestMethodReturnTypeIsNotValid_Diagnostic() { string code = """ @@ -268,6 +290,7 @@ public void MyTestMethod3() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestMethodReturnTypeIsValid_NoDiagnostic() { string code = """ @@ -299,6 +322,7 @@ public ValueTask MyTestMethod2() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestMethodIsAsyncVoid_Diagnostic() { string code = """ @@ -332,6 +356,7 @@ public async Task MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + [TestMethod] public async Task WhenTestMethodIsInternalAndDiscoverInternals_NoDiagnostic() { string code = """ @@ -371,6 +396,7 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + [TestMethod] public async Task WhenTestMethodIsPrivateAndDiscoverInternals_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs index 7deb038f4a..9c66e4a8f0 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TestMethodShouldNotBeIgnoredAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TestMethodShouldNotBeIgnoredAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TestMethodShouldNotBeIgnoredAnalyzerTests { + [TestMethod] public async Task WhenTestMethodIsNotIgnored_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingIgnoreWithoutTestMethod_NoDiagnostic() { string code = """ @@ -46,6 +48,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestMethodIsIgnored_Diagnostic() { string code = """ @@ -69,6 +72,7 @@ await VerifyCS.VerifyAnalyzerAsync( .WithArguments("MyTestMethod")); } + [TestMethod] public async Task WhenDerivedTestMethodAttributeIsIgnored_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs index a743894786..816b3d6653 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/TypeContainingTestMethodShouldBeATestClassAnalyzer.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class TypeContainingTestMethodShouldBeATestClassAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TypeContainingTestMethodShouldBeATestClassAnalyzerTests { + [TestMethod] public async Task WhenTestClassHasTestMethod_NoDiagnostic() { string code = """ @@ -28,6 +29,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassWithoutTestAttribute_HaveTestMethod_Diagnostic() { string code = """ @@ -43,6 +45,7 @@ public void TestMethod1() {} await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassWithoutTestAttribute_AndWithoutTestMethods_InheritTestClassWithTestMethods_Diagnostic() { string code = """ @@ -71,6 +74,7 @@ public void TestMethod2() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassWithoutTestAttribute_AndWithTestMethods_InheritTestClass_Diagnostic() { string code = """ @@ -99,6 +103,7 @@ public void TestMethod2() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenInheritedTestClassAttribute_HasInheritedTestMethodAttribute_NoDiagnostic() { string code = """ @@ -125,6 +130,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassWithoutTestAttribute_HasInheritedTestMethodAttribute_Diagnostic() { string code = """ @@ -146,6 +152,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAbstractClassWithoutTestAttribute_HaveTestMethod_NoDiagnostic() { string code = """ @@ -163,6 +170,7 @@ public void TestMethod1() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenClassHasTestInitializeAndThenTestMethod_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs index c9a55fb143..069d599c3e 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestFixtureMethodSuppressorTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,9 +12,10 @@ namespace MSTest.Analyzers.UnitTests; -[TestGroup] -public sealed class UseAsyncSuffixTestFixtureMethodSuppressorTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class UseAsyncSuffixTestFixtureMethodSuppressorTests { + [TestMethod] public async Task AsyncTestFixtureMethodsWithoutSuffix_DiagnosticIsSuppressed() { string code = @" @@ -57,6 +57,7 @@ public class SomeClass }.RunAsync(); } + [TestMethod] public async Task AsyncTestMethodWithSuffix_NoDiagnostic() { string code = """ @@ -113,7 +114,7 @@ public override void Initialize(AnalysisContext context) context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); } - private void AnalyzeSymbol(SymbolAnalysisContext context) + private static void AnalyzeSymbol(SymbolAnalysisContext context) { var method = (IMethodSymbol)context.Symbol; if (method.Name.EndsWith("Async", StringComparison.Ordinal)) diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs index de624c2e06..ba566c293c 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAsyncSuffixTestMethodSuppressorTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,9 +12,10 @@ namespace MSTest.Analyzers.UnitTests; -[TestGroup] -public sealed class UseAsyncSuffixTestMethodSuppressorTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class UseAsyncSuffixTestMethodSuppressorTests { + [TestMethod] public async Task AsyncTestMethodWithoutSuffix_DiagnosticIsSuppressed() { string code = @@ -45,6 +45,7 @@ public class SomeClass }.RunAsync(); } + [TestMethod] public async Task AsyncDataTestMethodWithoutSuffix_DiagnosticIsSuppressed() { string code = @@ -73,6 +74,7 @@ public class SomeClass }.RunAsync(); } + [TestMethod] public async Task AsyncTestMethodWithSuffix_NoDiagnostic() { string code = @@ -115,7 +117,7 @@ public override void Initialize(AnalysisContext context) context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); } - private void AnalyzeSymbol(SymbolAnalysisContext context) + private static void AnalyzeSymbol(SymbolAnalysisContext context) { var method = (IMethodSymbol)context.Symbol; if (method.Name.EndsWith("Async", StringComparison.Ordinal)) diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs index 17f3b8a420..f7353183ad 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseAttributeOnTestMethodAnalyzerTests.cs @@ -9,8 +9,8 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class UseAttributeOnTestMethodAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class UseAttributeOnTestMethodAnalyzerTests { private static readonly List<(DiagnosticDescriptor Rule, string AttributeUsageExample)> RuleUsageExamples = [ @@ -35,11 +35,11 @@ protected override void Verify(System.Exception exception) { } internal static IEnumerable<(DiagnosticDescriptor Rule, string AttributeUsageExample)> GetAttributeUsageExampleAndRuleTuples() => RuleUsageExamples.Select(tuple => (tuple.Rule, tuple.AttributeUsageExample)); - internal static IEnumerable GetAttributeUsageExamples() - => RuleUsageExamples.Select(tuple => tuple.AttributeUsageExample); + internal static IEnumerable GetAttributeUsageExamples() + => RuleUsageExamples.Select(tuple => new object[] { tuple.AttributeUsageExample }); // This generates all possible combinations of any two tuples (Rule, AttributeUsageExample) with the exception of the - // combaination where the two tuples are equal. The result is flattened in a new tuple created from the elements of the + // combination where the two tuples are equal. The result is flattened in a new tuple created from the elements of the // previous two tuples. internal static IEnumerable<(DiagnosticDescriptor Rule1, string AttributeUsageExample1, DiagnosticDescriptor Rule2, string AttributeUsageExample2)> GetAttributeUsageExampleAndRuleTuplesForTwoAttributes() => RuleUsageExamples @@ -47,7 +47,8 @@ internal static IEnumerable GetAttributeUsageExamples() .Where(tuples => !tuples.tuple1.AttributeUsageExample.Equals(tuples.tuple2.AttributeUsageExample, StringComparison.Ordinal)) .Select(tuples => (tuples.tuple1.Rule, tuples.tuple1.AttributeUsageExample, tuples.tuple2.Rule, tuples.tuple2.AttributeUsageExample)); - [ArgumentsProvider(nameof(GetAttributeUsageExamples))] + [DynamicData(nameof(GetAttributeUsageExamples), DynamicDataSourceType.Method)] + [TestMethod] public async Task WhenMethodIsMarkedWithTestMethodAndTestAttributes_NoDiagnosticAsync(string attributeUsageExample) { string code = $$""" @@ -69,7 +70,8 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } - [ArgumentsProvider(nameof(GetAttributeUsageExampleAndRuleTuples))] + [DynamicData(nameof(GetAttributeUsageExampleAndRuleTuples), DynamicDataSourceType.Method)] + [TestMethod] public async Task WhenMethodIsMarkedWithTestAttributeButNotWithTestMethod_DiagnosticAsync(DiagnosticDescriptor rule, string attributeUsageExample) { string code = $$""" @@ -106,7 +108,8 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, VerifyCS.Diagnostic(rule).WithLocation(0), fixedCode); } - [ArgumentsProvider(nameof(GetAttributeUsageExampleAndRuleTuplesForTwoAttributes))] + [DynamicData(nameof(GetAttributeUsageExampleAndRuleTuplesForTwoAttributes), DynamicDataSourceType.Method)] + [TestMethod] public async Task WhenMethodIsMarkedWithMultipleTestAttributesButNotWithTestMethod_DiagnosticOnEachAttributeAsync( DiagnosticDescriptor rule1, string attributeUsageExample1, @@ -149,7 +152,8 @@ public void TestMethod() await VerifyCS.VerifyCodeFixAsync(code, new[] { VerifyCS.Diagnostic(rule1).WithLocation(0), VerifyCS.Diagnostic(rule2).WithLocation(1) }, fixedCode); } - [ArgumentsProvider(nameof(GetAttributeUsageExamples))] + [DynamicData(nameof(GetAttributeUsageExamples), DynamicDataSourceType.Method)] + [TestMethod] public async Task WhenMethodIsMarkedWithTestAttributeAndCustomTestMethod_NoDiagnosticAsync(string attributeUsageExample) { string code = $$""" diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseClassCleanupBehaviorEndOfClassAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseClassCleanupBehaviorEndOfClassAnalyzerTests.cs index 531bf1886c..6406ceff68 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseClassCleanupBehaviorEndOfClassAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseClassCleanupBehaviorEndOfClassAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class UseClassCleanupBehaviorEndOfClassAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class UseClassCleanupBehaviorEndOfClassAnalyzerTests { + [TestMethod] public async Task UsingClassCleanup_WithoutCleanupBehaviorEndOfClass_AndNotInsideTestClass_NoDiagnostic() { string code = """ @@ -27,6 +28,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithoutCleanupBehaviorEndOfClass_AndInsideTestClass_Diagnostic() { string code = """ @@ -45,6 +47,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithCleanupBehaviorEndOfClass_NoDiagnostic() { string code = """ @@ -63,6 +66,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithCleanupBehaviorEndOfAssembly_Diagnostic() { string code = """ @@ -81,6 +85,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithoutCleanupBehavior_Diagnostic() { string code = """ @@ -99,6 +104,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithoutCleanupBehaviorAndWithInheritanceBehavior_Diagnostic() { string code = """ @@ -117,6 +123,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithCleanupBehaviorEndOFAssemblyAndWithInheritanceBehavior_Diagnostic() { string code = """ @@ -135,6 +142,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_WithCleanupBehaviorEndOFClassAndWithInheritanceBehavior_NoDiagnostic() { string code = """ @@ -153,6 +161,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfClassBehavior_NoDiagnostic() { string code = """ @@ -172,6 +181,7 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfAsseblyBehavior_Diagnostic() { string code = """ @@ -191,6 +201,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfClassBehavior_WithCleanupBehaviorEndOfAssembly_Diagnostic() { string code = """ @@ -210,6 +221,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfAssemblyBehavior_WithCleanupBehaviorEndOfClass_NoDiagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseDeploymentItemWithTestMethodOrTestClassAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseDeploymentItemWithTestMethodOrTestClassAnalyzerTests.cs index 228f7c2f41..e0fa42c540 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseDeploymentItemWithTestMethodOrTestClassAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseDeploymentItemWithTestMethodOrTestClassAnalyzerTests.cs @@ -7,9 +7,10 @@ namespace MSTest.Analyzers.Test; -[TestGroup] -public sealed class UseDeploymentItemWithTestMethodOrTestClassAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class UseDeploymentItemWithTestMethodOrTestClassAnalyzerTests { + [TestMethod] public async Task WhenTestClassHasDeploymentItem_NoDiagnostic() { string code = """ @@ -25,6 +26,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenTestMethodHasDeploymentItem_NoDiagnostic() { string code = """ @@ -44,6 +46,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenInheritedTestClassAttributeHasDeploymentItem_NoDiagnostic() { string code = """ @@ -62,6 +65,7 @@ public class MyTestClass await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenInheritedTestMethodAttributeHasDeploymentItem_NoDiagnostic() { string code = """ @@ -84,6 +88,22 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] + public async Task WhenAnAbstractClassHasDeploymentItem_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [DeploymentItem("")] + public abstract class MyTestClass + { + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] public async Task WhenAClassHasDeploymentItem_Diagnostic() { string code = """ @@ -98,6 +118,7 @@ public class [|MyTestClass|] await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAMethodHasDeploymentItem_Diagnostic() { string code = """ diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseNewerAssertThrowsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseNewerAssertThrowsAnalyzerTests.cs new file mode 100644 index 0000000000..1bc0223a6b --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseNewerAssertThrowsAnalyzerTests.cs @@ -0,0 +1,298 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.UseNewerAssertThrowsAnalyzer, + MSTest.Analyzers.UseNewerAssertThrowsFixer>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class UseNewerAssertThrowsAnalyzerTests +{ + [TestMethod] + public async Task WhenAssertThrowsException_Diagnostic() + { + string code = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // action only overload + [|Assert.ThrowsException(() => Console.WriteLine())|]; + [|Assert.ThrowsException(action: () => Console.WriteLine())|]; + [|Assert.ThrowsExceptionAsync(() => Task.CompletedTask)|]; + [|Assert.ThrowsExceptionAsync(action: () => Task.CompletedTask)|]; + + // action and message overload + [|Assert.ThrowsException(() => Console.WriteLine(), "Message")|]; + [|Assert.ThrowsException(action: () => Console.WriteLine(), message: "Message")|]; + [|Assert.ThrowsException(message: "Message", action: () => Console.WriteLine())|]; + [|Assert.ThrowsException(action: () => Console.WriteLine(), "Message")|]; + [|Assert.ThrowsException(() => Console.WriteLine(), message: "Message")|]; + [|Assert.ThrowsExceptionAsync(() => Task.CompletedTask, "Message")|]; + [|Assert.ThrowsExceptionAsync(action: () => Task.CompletedTask, message: "Message")|]; + [|Assert.ThrowsExceptionAsync(message: "Message", action: () => Task.CompletedTask)|]; + [|Assert.ThrowsExceptionAsync(action: () => Task.CompletedTask, "Message")|]; + [|Assert.ThrowsExceptionAsync(() => Task.CompletedTask, message: "Message")|]; + + // action, message, and parameters overload + [|Assert.ThrowsException(() => Console.WriteLine(), "Message", "A", "B", "C")|]; + [|Assert.ThrowsException(() => Console.WriteLine(), "Message", parameters: new object[] { "A", "B", "C" })|]; + [|Assert.ThrowsExceptionAsync(() => Task.CompletedTask, "Message", "A", "B", "C")|]; + [|Assert.ThrowsExceptionAsync(() => Task.CompletedTask, "Message", parameters: new object[] { "A", "B", "C" })|]; + } + } + """; + + string fixedCode = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + // action only overload + Assert.ThrowsExactly(() => Console.WriteLine()); + Assert.ThrowsExactly(action: () => Console.WriteLine()); + Assert.ThrowsExactlyAsync(() => Task.CompletedTask); + Assert.ThrowsExactlyAsync(action: () => Task.CompletedTask); + + // action and message overload + Assert.ThrowsExactly(() => Console.WriteLine(), "Message"); + Assert.ThrowsExactly(action: () => Console.WriteLine(), message: "Message"); + Assert.ThrowsExactly(message: "Message", action: () => Console.WriteLine()); + Assert.ThrowsExactly(action: () => Console.WriteLine(), "Message"); + Assert.ThrowsExactly(() => Console.WriteLine(), message: "Message"); + Assert.ThrowsExactlyAsync(() => Task.CompletedTask, "Message"); + Assert.ThrowsExactlyAsync(action: () => Task.CompletedTask, message: "Message"); + Assert.ThrowsExactlyAsync(message: "Message", action: () => Task.CompletedTask); + Assert.ThrowsExactlyAsync(action: () => Task.CompletedTask, "Message"); + Assert.ThrowsExactlyAsync(() => Task.CompletedTask, message: "Message"); + + // action, message, and parameters overload + Assert.ThrowsExactly(() => Console.WriteLine(), "Message", "A", "B", "C"); + Assert.ThrowsExactly(() => Console.WriteLine(), "Message", messageArgs: new object[] { "A", "B", "C" }); + Assert.ThrowsExactlyAsync(() => Task.CompletedTask, "Message", "A", "B", "C"); + Assert.ThrowsExactlyAsync(() => Task.CompletedTask, "Message", messageArgs: new object[] { "A", "B", "C" }); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenAssertThrowsExceptionFuncOverloadExpressionBody_Diagnostic() + { + string code = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.ThrowsException(() => 5)|]; + } + } + """; + + // NOTE: The discard is needed to avoid CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement + // This is because ThrowsException has a Func overload that is being used in the original code. + // But ThrowsExactly only has an Action overload. + string fixedCode = """ + using System; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.ThrowsExactly(() => _ = 5); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenAssertThrowsExceptionFuncOverloadComplexBody_Diagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Assert.ThrowsException(() => + { + Console.WriteLine(); + Func x = () => + { + int LocalFunction() + { + // This shouldn't be touched. + return 0; + } + + // This shouldn't be touched. + return LocalFunction(); + }; + + if (true) + { + return 1; + } + else if (true) + return 2; + + return 3; + })|]; + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.ThrowsExactly(() => + { + Console.WriteLine(); + Func x = () => + { + int LocalFunction() + { + // This shouldn't be touched. + return 0; + } + + // This shouldn't be touched. + return LocalFunction(); + }; + + if (true) + { + _ = 1; + return; + } + else if (true) + { + _ = 2; + return; + } + + _ = 3; + return; + }); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenAssertThrowsExceptionFuncOverloadVariable_Diagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Func action = () => _ = 5; + [|Assert.ThrowsException(action)|]; + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Func action = () => _ = 5; + Assert.ThrowsExactly(() => _ = action()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [TestMethod] + public async Task WhenAssertThrowsExceptionFuncOverloadBinaryExpression_Diagnostic() + { + string code = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Func action = () => _ = 5; + [|Assert.ThrowsException(action + action)|]; + } + } + """; + + string fixedCode = """ + using System; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Func action = () => _ = 5; + Assert.ThrowsExactly(() => _ = (action + action)()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs index ddc0b5b5b8..8872cb744b 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseParallelizeAttributeAnalyzerTests.cs @@ -1,19 +1,45 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; + using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.UseParallelizeAttributeAnalyzer, Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; namespace MSTest.Analyzers.Test; -[TestGroup] -public class UseParallelizeAttributeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public class UseParallelizeAttributeAnalyzerTests { - public async Task WhenNoAttributeSpecified_Diagnostic() => await VerifyCS.VerifyAnalyzerAsync( - string.Empty, - VerifyCS.Diagnostic(UseParallelizeAttributeAnalyzer.Rule).WithNoLocation()); + private static async Task VerifyAsync(string code, bool includeTestAdapter, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestCode = code, + }; + + if (includeTestAdapter) + { + // NOTE: Test constructor already adds TestFramework refs. + test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(MSTestExecutor).Assembly.Location)); + } + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + [TestMethod] + public async Task WhenNoAttributeSpecified_TestAdapterNotReferenced_NoDiagnostic() + => await VerifyAsync(string.Empty, includeTestAdapter: false); + + [TestMethod] + public async Task WhenNoAttributeSpecified_TestAdapterReferenced_Diagnostic() + => await VerifyAsync(string.Empty, includeTestAdapter: true, VerifyCS.Diagnostic(UseParallelizeAttributeAnalyzer.Rule).WithNoLocation()); + [TestMethod] public async Task WhenParallelizeAttributeSet_NoDiagnostic() { string code = """ @@ -22,9 +48,11 @@ public async Task WhenParallelizeAttributeSet_NoDiagnostic() [assembly: Parallelize(Workers = 2, Scope = ExecutionScope.MethodLevel)] """; - await VerifyCS.VerifyAnalyzerAsync(code); + await VerifyAsync(code, includeTestAdapter: true); + await VerifyAsync(code, includeTestAdapter: false); } + [TestMethod] public async Task WhenDoNotParallelizeAttributeSet_NoDiagnostic() { string code = """ @@ -33,6 +61,7 @@ public async Task WhenDoNotParallelizeAttributeSet_NoDiagnostic() [assembly: DoNotParallelize] """; - await VerifyCS.VerifyAnalyzerAsync(code); + await VerifyAsync(code, includeTestAdapter: true); + await VerifyAsync(code, includeTestAdapter: false); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs index a1238fd7a2..1676550103 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs @@ -3,14 +3,23 @@ using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< MSTest.Analyzers.UseProperAssertMethodsAnalyzer, - Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + MSTest.Analyzers.UseProperAssertMethodsFixer>; namespace MSTest.Analyzers.Test; // NOTE: tests in this class are intentionally not using the [|...|] markup syntax so that we test the arguments -[TestGroup] -public sealed class UseProperAssertMethodsAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class UseProperAssertMethodsAnalyzerTests { + private const string SomeClassWithUserDefinedEqualityOperators = """ + public class SomeClass + { + public static bool operator ==(SomeClass x, SomeClass y) => true; + public static bool operator !=(SomeClass x, SomeClass y) => false; + } + """; + + [TestMethod] public async Task WhenAssertIsTrueWithEqualsNullArgument() { string code = """ @@ -22,19 +31,584 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.IsTrue(x == null)|}; + object x = new object(); + {|#0:Assert.IsTrue(x == null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithEqualsNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsTrue(x == null); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithIsNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsTrue(x is null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithIsNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + {|#0:Assert.IsTrue(x is null)|}; + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + string fixedCode = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsNull(x); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithNotEqualsNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsTrue(x != null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNotNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithNotEqualsNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsTrue(x != null); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithIsNotNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsTrue(x is not null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNotNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsTrueWithIsNotNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + {|#0:Assert.IsTrue(x is not null)|}; + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + string fixedCode = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsNotNull(x); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "IsTrue"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithEqualsNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsFalse(x == null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNotNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithEqualsNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsFalse(x == null); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithIsNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsFalse(x is null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNotNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithIsNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + {|#0:Assert.IsFalse(x is null)|}; + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + string fixedCode = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsNotNull(x); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithNotEqualsNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsFalse(x != null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithNotEqualsNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsFalse(x != null); + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithIsNotNullArgument() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + {|#0:Assert.IsFalse(x is not null)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNull(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "IsFalse"), + fixedCode); + } + + [TestMethod] + public async Task WhenAssertIsFalseWithIsNotNullArgumentAndUserDefinedOperator() + { + string code = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + {|#0:Assert.IsFalse(x is not null)|}; + } + } + + {{SomeClassWithUserDefinedEqualityOperators}} + """; + + string fixedCode = $$""" + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + SomeClass x = new SomeClass(); + Assert.IsNull(x); } } + + {{SomeClassWithUserDefinedEqualityOperators}} """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsTrue' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNull", "IsTrue")); + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "IsFalse"), + fixedCode); } - public async Task WhenAssertIsTrueWithIsNullArgument() + [TestMethod] + public async Task WhenAssertIsTrueAndArgumentIsEquality() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -46,20 +620,39 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - {|#0:Assert.IsTrue(x is null)|}; + object y = new object(); + {|#0:Assert.IsTrue(x == y)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + object y = new object(); + Assert.AreEqual(y, x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsTrue' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNull", "IsTrue")); + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreEqual' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreEqual", "IsTrue"), + fixedCode); } - public async Task WhenAssertIsTrueWithNotEqualsNullArgument() + [TestMethod] + public async Task WhenAssertIsTrueAndArgumentIsEqualityAndUserDefinedOperator() { - string code = """ + string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -68,19 +661,20 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.IsTrue(x != null)|}; + SomeClass x = new SomeClass(); + SomeClass y = new SomeClass(); + Assert.IsTrue(x == y); } } + + {{SomeClassWithUserDefinedEqualityOperators}} """; - await VerifyCS.VerifyAnalyzerAsync( - code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsTrue' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNotNull", "IsTrue")); + await VerifyCS.VerifyCodeFixAsync(code, code); } - public async Task WhenAssertIsTrueWithIsNotNullArgument() + [TestMethod] + public async Task WhenAssertIsTrueAndArgumentIsInequality() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -92,20 +686,39 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - {|#0:Assert.IsTrue(x is not null)|}; + object y = new object(); + {|#0:Assert.IsTrue(x != y)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + object y = new object(); + Assert.AreNotEqual(y, x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsTrue' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNotNull", "IsTrue")); + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreNotEqual' instead of 'Assert.IsTrue' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreNotEqual", "IsTrue"), + fixedCode); } - public async Task WhenAssertIsFalseWithEqualsNullArgument() + [TestMethod] + public async Task WhenAssertIsTrueAndArgumentIsInequalityAndUserDefinedOperator() { - string code = """ + string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -114,19 +727,20 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.IsFalse(x == null)|}; + SomeClass x = new SomeClass(); + SomeClass y = new SomeClass(); + Assert.IsTrue(x != y); } } + + {{SomeClassWithUserDefinedEqualityOperators}} """; - await VerifyCS.VerifyAnalyzerAsync( - code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsFalse' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNotNull", "IsFalse")); + await VerifyCS.VerifyCodeFixAsync(code, code); } - public async Task WhenAssertIsFalseWithIsNullArgument() + [TestMethod] + public async Task WhenAssertIsFalseAndArgumentIsEquality() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -138,20 +752,39 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - {|#0:Assert.IsFalse(x is null)|}; + object y = new object(); + {|#0:Assert.IsFalse(x == y)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + object y = new object(); + Assert.AreNotEqual(y, x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.IsFalse' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNotNull", "IsFalse")); + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreNotEqual' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreNotEqual", "IsFalse"), + fixedCode); } - public async Task WhenAssertIsFalseWithNotEqualsNullArgument() + [TestMethod] + public async Task WhenAssertIsFalseAndArgumentIsEqualityAndUserDefinedOperator() { - string code = """ + string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -160,19 +793,20 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.IsFalse(x != null)|}; + SomeClass x = new SomeClass(); + SomeClass y = new SomeClass(); + Assert.IsFalse(x == y); } } + + {{SomeClassWithUserDefinedEqualityOperators}} """; - await VerifyCS.VerifyAnalyzerAsync( - code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsFalse' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNull", "IsFalse")); + await VerifyCS.VerifyCodeFixAsync(code, code); } - public async Task WhenAssertIsFalseWithIsNotNullArgument() + [TestMethod] + public async Task WhenAssertIsFalseAndArgumentIsInequality() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -184,20 +818,39 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - {|#0:Assert.IsFalse(x is not null)|}; + object y = new object(); + {|#0:Assert.IsFalse(x != y)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + object y = new object(); + Assert.AreEqual(y, x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.IsFalse' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNull", "IsFalse")); + // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreEqual' instead of 'Assert.IsFalse' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("AreEqual", "IsFalse"), + fixedCode); } - public async Task WhenAssertIsTrueAndArgumentIsEquality() + [TestMethod] + public async Task WhenAssertIsFalseAndArgumentIsInequalityAndUserDefinedOperator() { - string code = """ + string code = $$""" using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -206,20 +859,20 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - object y = new object(); - {|#0:Assert.IsTrue(x == y)|}; + SomeClass x = new SomeClass(); + SomeClass y = new SomeClass(); + Assert.IsFalse(x != y); } } + + {{SomeClassWithUserDefinedEqualityOperators}} """; - await VerifyCS.VerifyAnalyzerAsync( - code, - // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreEqual' instead of 'Assert.IsTrue' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("AreEqual", "IsTrue")); + await VerifyCS.VerifyCodeFixAsync(code, code); } - public async Task WhenAssertIsTrueAndArgumentIsInequality() + [TestMethod] + public async Task WhenAssertAreEqualAndExpectedIsNull() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -231,19 +884,35 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - object y = new object(); - {|#0:Assert.IsTrue(x != y)|}; + {|#0:Assert.AreEqual(null, x)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNull(x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreNotEqual' instead of 'Assert.IsTrue' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("AreNotEqual", "IsTrue")); + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNull", "AreEqual"), + fixedCode); } - public async Task WhenAssertIsFalseAndArgumentIsEquality() + [TestMethod] + public async Task WhenAssertAreNotEqualAndExpectedIsNull() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -255,19 +924,35 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - object y = new object(); - {|#0:Assert.IsFalse(x == y)|}; + {|#0:Assert.AreNotEqual(null, x)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsNotNull(x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreNotEqual' instead of 'Assert.IsFalse' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("AreNotEqual", "IsFalse")); + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.AreNotEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsNotNull", "AreNotEqual"), + fixedCode); } - public async Task WhenAssertIsFalseAndArgumentIsInequality() + [TestMethod] + public async Task WhenAssertAreEqualAndExpectedIsTrue() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -279,19 +964,35 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); - object y = new object(); - {|#0:Assert.IsFalse(x != y)|}; + {|#0:Assert.AreEqual(true, x)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsTrue((bool?)x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(11,9): info MSTEST0037: Use 'Assert.AreEqual' instead of 'Assert.IsFalse' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("AreEqual", "IsFalse")); + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsTrue' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsTrue", "AreEqual"), + fixedCode); } - public async Task WhenAssertAreEqualAndExpectedIsNull() + [TestMethod] + public async Task WhenAssertAreEqualAndExpectedIsTrue_CastNotAddedWhenTypeIsBool() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -302,19 +1003,36 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.AreEqual(null, x)|}; + bool x = false; + {|#0:Assert.AreEqual(true, x)|}; + } + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + bool x = false; + Assert.IsTrue(x); } } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNull' instead of 'Assert.AreEqual' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNull", "AreEqual")); + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsTrue' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsTrue", "AreEqual"), + fixedCode); } - public async Task WhenAssertAreNotEqualAndExpectedIsNull() + [TestMethod] + public async Task WhenAssertAreEqualAndExpectedIsTrue_CastNotAddedWhenTypeIsNullableBool() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -325,19 +1043,36 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.AreNotEqual(null, x)|}; + bool? x = false; + {|#0:Assert.AreEqual(true, x)|}; } } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + bool? x = false; + Assert.IsTrue(x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, - // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsNotNull' instead of 'Assert.AreNotEqual' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsNotNull", "AreNotEqual")); + // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsTrue' instead of 'Assert.AreEqual' + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsTrue", "AreEqual"), + fixedCode); } - public async Task WhenAssertAreEqualAndExpectedIsTrue() + [TestMethod] + public async Task WhenAssertAreEqualAndExpectedIsTrue_CastShouldBeAddedWithParentheses() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -348,18 +1083,45 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - object x = new object(); - {|#0:Assert.AreEqual(true, x)|}; + {|#0:Assert.AreEqual(true, new C() + new C())|}; + } + } + + public class C + { + public static object operator +(C c1, C c2) + => true; + } + """; + + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Assert.IsTrue((bool?)(new C() + new C())); } } + + public class C + { + public static object operator +(C c1, C c2) + => true; + } """; - await VerifyCS.VerifyAnalyzerAsync( + await VerifyCS.VerifyCodeFixAsync( code, // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsTrue' instead of 'Assert.AreEqual' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsTrue", "AreEqual")); + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsTrue", "AreEqual"), + fixedCode); } + [TestMethod] public async Task WhenAssertAreNotEqualAndExpectedIsTrue() { string code = """ @@ -372,6 +1134,9 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); + // Note: Assert.IsFalse(x) has different semantics. So no diagnostic. + // We currently don't produce a diagnostic even if the type of 'x' is boolean. + // But we could special case that. Assert.AreNotEqual(true, x); } } @@ -380,6 +1145,7 @@ public void MyTestMethod() await VerifyCS.VerifyAnalyzerAsync(code); } + [TestMethod] public async Task WhenAssertAreEqualAndExpectedIsFalse() { string code = """ @@ -397,12 +1163,29 @@ public void MyTestMethod() } """; - await VerifyCS.VerifyAnalyzerAsync( + string fixedCode = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + object x = new object(); + Assert.IsFalse((bool?)x); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( code, // /0/Test0.cs(10,9): info MSTEST0037: Use 'Assert.IsFalse' instead of 'Assert.AreEqual' - VerifyCS.Diagnostic().WithLocation(0).WithArguments("IsFalse", "AreEqual")); + VerifyCS.DiagnosticIgnoringAdditionalLocations().WithLocation(0).WithArguments("IsFalse", "AreEqual"), + fixedCode); } + [TestMethod] public async Task WhenAssertAreNotEqualAndExpectedIsFalse() { string code = """ @@ -415,6 +1198,9 @@ public class MyTestClass public void MyTestMethod() { object x = new object(); + // Note: Assert.IsTrue(x) has different semantics. So no diagnostic. + // We currently don't produce a diagnostic even if the type of 'x' is boolean. + // But we could special case that. Assert.AreNotEqual(false, x); } } diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs index 9db21ba08b..3fc45b7658 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.VisualStudio.TestTools.UnitTesting; using TestContext = Microsoft.VisualStudio.TestTools.UnitTesting.TestContext; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs index 54e1de1980..16ff04edf8 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Testing; @@ -19,6 +17,10 @@ public static partial class CSharpCodeFixVerifier public static DiagnosticResult Diagnostic() => CSharpCodeFixVerifier.Diagnostic(); + /// + public static DiagnosticResult DiagnosticIgnoringAdditionalLocations() + => CSharpCodeFixVerifier.Diagnostic().WithOptions(DiagnosticOptions.IgnoreAdditionalLocations); + /// public static DiagnosticResult Diagnostic(string diagnosticId) => CSharpCodeFixVerifier.Diagnostic(diagnosticId); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs index 7846e846d7..d721d4c831 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/AssemblyResolverTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; @@ -206,7 +204,7 @@ public TestableAssemblyResolver(IList directories) public Func, string, bool, Assembly> SearchAssemblySetter { get; internal set; } - protected override bool DoesDirectoryExist(string path) => DoesDirectoryExistSetter == null ? base.DoesDirectoryExist(path) : DoesDirectoryExistSetter(path); + protected override bool DoesDirectoryExist(string path) => DoesDirectoryExistSetter?.Invoke(path) ?? base.DoesDirectoryExist(path); protected override string[] GetDirectories(string path) => GetDirectoriesSetter == null ? base.GetDirectories(path) : GetDirectoriesSetter(path); @@ -214,7 +212,7 @@ protected override Assembly SearchAssembly(List searchDirectorypaths, st ? base.SearchAssembly(searchDirectorypaths, name, isReflectionOnly) : SearchAssemblySetter(searchDirectorypaths, name, isReflectionOnly); - protected override bool DoesFileExist(string filePath) => DoesFileExistSetter == null ? base.DoesFileExist(filePath) : DoesFileExistSetter(filePath); + protected override bool DoesFileExist(string filePath) => DoesFileExistSetter?.Invoke(filePath) ?? base.DoesFileExist(filePath); protected override Assembly LoadAssemblyFrom(string path) => LoadAssemblyFromSetter == null ? base.LoadAssemblyFrom(path) : LoadAssemblyFromSetter(path); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs index 2776f4c331..508a713858 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/AssemblyLoadWorkerTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs index 89d7fd16d8..34b48cb028 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Deployment/DeploymentItemTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj index 576e802897..404e5c61dc 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj @@ -25,6 +25,10 @@ + + Analyzer + false + @@ -35,12 +39,9 @@ - + - - - - + Always diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Properties/AssemblyInfo.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Properties/AssemblyInfo.cs index 612ad5f14c..9c4a2e3eb1 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Properties/AssemblyInfo.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - #if NET462 using MSTestAdapter.PlatformServices.UnitTests.Utilities; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopFileOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopFileOperationsTests.cs index 37f25a76f5..5870fb2ee9 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopFileOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopFileOperationsTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs index 877075b280..c6edd36a0a 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopReflectionOperationsTests.cs @@ -2,9 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 - -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using MSTestAdapter.PlatformServices.UnitTests.Utilities; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs index 2da4d888eb..269952ba91 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs index 2fbaf9315a..32463c805f 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; using System.Security.Policy; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; @@ -124,7 +123,7 @@ public void SetupHostShouldHaveParentDomainsAppBaseSetToTestSourceLocation() { // Arrange DummyClass dummyClass = new(); - string runSettingxml = + string runSettingsXml = """ @@ -135,7 +134,7 @@ public void SetupHostShouldHaveParentDomainsAppBaseSetToTestSourceLocation() string location = typeof(TestSourceHost).Assembly.Location; var mockRunSettings = new Mock(); - mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); TestSourceHost sourceHost = new(location, mockRunSettings.Object, null); @@ -198,7 +197,7 @@ public void NoAppDomainShouldGetCreatedWhenDisableAppDomainIsSetToTrue() { // Arrange DummyClass dummyClass = new(); - string runSettingxml = + string runSettingsXml = @" True @@ -207,7 +206,7 @@ public void NoAppDomainShouldGetCreatedWhenDisableAppDomainIsSetToTrue() string location = typeof(TestSourceHost).Assembly.Location; var mockRunSettings = new Mock(); - mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); Mock testSourceHost = new(location, mockRunSettings.Object, null) { CallBase = true }; @@ -227,7 +226,7 @@ public void AppDomainShouldGetCreatedWhenDisableAppDomainIsSetToFalse() { // Arrange DummyClass dummyClass = new(); - string runSettingxml = + string runSettingsXml = """ @@ -238,7 +237,7 @@ public void AppDomainShouldGetCreatedWhenDisableAppDomainIsSetToFalse() string location = typeof(TestSourceHost).Assembly.Location; var mockRunSettings = new Mock(); - mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); Mock testSourceHost = new(location, mockRunSettings.Object, null) { CallBase = true }; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceTests.cs index 310a7d7e07..b9ec70e054 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs index 2d8df6545a..b9d863ba6d 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml; - -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; @@ -318,16 +316,16 @@ public void ToSettings_ShouldInitializeDeploymentAndAssemblyResolutionSettingsCo { // Arrange var configDictionary = new Dictionary - { - { "mstest:deployment:enabled", "true" }, - { "mstest:deployment:deployTestSourceDependencies", "true" }, - { "mstest:deployment:deleteDeploymentDirectoryAfterTestRunIsComplete", "false" }, - { "mstest:assemblyResolution:0:path", "C:\\project\\dependencies" }, - { "mstest:assemblyResolution:0:includeSubDirectories", "true" }, - { "mstest:assemblyResolution:1:path", "C:\\project\\libs" }, - { "mstest:assemblyResolution:1:includeSubDirectories", "false" }, - { "mstest:assemblyResolution:2:path", "C:\\project\\plugins" }, - }; + { + { "mstest:deployment:enabled", "true" }, + { "mstest:deployment:deployTestSourceDependencies", "true" }, + { "mstest:deployment:deleteDeploymentDirectoryAfterTestRunIsComplete", "false" }, + { "mstest:assemblyResolution:0:path", "C:\\project\\dependencies" }, + { "mstest:assemblyResolution:0:includeSubDirectories", "true" }, + { "mstest:assemblyResolution:1:path", "C:\\project\\libs" }, + { "mstest:assemblyResolution:1:includeSubDirectories", "false" }, + { "mstest:assemblyResolution:2:path", "C:\\project\\plugins" }, + }; var mockConfig = new Mock(); mockConfig.Setup(config => config[It.IsAny()]) @@ -385,7 +383,7 @@ public TestableMSTestAdapterSettings() public Func ExpandEnvironmentVariablesSetter { get; set; } - protected override bool DoesDirectoryExist(string path) => DoesDirectoryExistSetter == null ? base.DoesDirectoryExist(path) : DoesDirectoryExistSetter(path); + protected override bool DoesDirectoryExist(string path) => DoesDirectoryExistSetter?.Invoke(path) ?? base.DoesDirectoryExist(path); protected override string ExpandEnvironmentVariables(string path) => ExpandEnvironmentVariablesSetter == null ? base.ExpandEnvironmentVariables(path) : ExpandEnvironmentVariablesSetter(path); } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs index 5eb74aa7cf..44397350b5 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestSettingsProviderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; @@ -42,13 +40,13 @@ public void SettingsShouldReturnDefaultSettingsIfNotInitialized() public void SettingsShouldReturnInitializedSettings() { - string runSettingxml = + string runSettingsXml = """ False """; - StringReader stringReader = new(runSettingxml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); _settingsProvider.Load(reader); @@ -60,13 +58,13 @@ public void LoadShouldThrowIfReaderIsNull() => public void LoadShouldReadAndFillInSettings() { - string runSettingxml = + string runSettingsXml = """ False """; - StringReader stringReader = new(runSettingxml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); reader.Read(); _settingsProvider.Load(reader); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs index 6ea1f07e05..64a5aefc89 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs index b2a1ec1e32..fdac028df8 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs @@ -7,6 +7,8 @@ #endif using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -389,4 +391,21 @@ public void GetResultFilesShouldReturnListOfAddedResultFiles() Verify(resultFiles.Contains("C:\\files\\myfile2.txt")); } #endif + + public void DisplayMessageShouldForwardToIMessageLogger() + { + var messageLoggerMock = new Mock(MockBehavior.Strict); + + messageLoggerMock + .Setup(l => l.SendMessage(It.IsAny(), It.IsAny())); + + _testContextImplementation = new TestContextImplementation(_testMethod.Object, new ThreadSafeStringWriter(null, "test"), _properties, messageLoggerMock.Object); + _testContextImplementation.DisplayMessage(MessageLevel.Informational, "InfoMessage"); + _testContextImplementation.DisplayMessage(MessageLevel.Warning, "WarningMessage"); + _testContextImplementation.DisplayMessage(MessageLevel.Error, "ErrorMessage"); + + messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Informational, "InfoMessage"), Times.Once); + messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Warning, "WarningMessage"), Times.Once); + messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Error, "ErrorMessage"), Times.Once); + } } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs index f77edd4e1b..5615c73684 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; -using System.Xml; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; @@ -109,9 +106,9 @@ public void CleanupShouldNotDeleteDirectoriesIfRunDirectoriesIsNull() public void CleanupShouldNotDeleteDirectoriesIfRunSettingsSpecifiesSo() { - string runSettingXml = + string runSettingsXml = "False"; - StringReader stringReader = new(runSettingXml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); MSTestSettingsProvider mstestSettingsProvider = new(); mstestSettingsProvider.Load(reader); @@ -191,9 +188,9 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseButHasDeployme new DeploymentUtility(), _mockFileUtility.Object); - string runSettingXml = + string runSettingsXml = "False"; - StringReader stringReader = new(runSettingXml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); MSTestSettingsProvider mstestSettingsProvider = new(); mstestSettingsProvider.Load(reader); @@ -214,9 +211,9 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseAndHasNoDeploy new DeploymentUtility(), _mockFileUtility.Object); - string runSettingXml = + string runSettingsXml = "False"; - StringReader stringReader = new(runSettingXml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); MSTestSettingsProvider mstestSettingsProvider = new(); mstestSettingsProvider.Load(reader); @@ -237,9 +234,9 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToTrueButHasNoDeploym new DeploymentUtility(), _mockFileUtility.Object); - string runSettingXml = + string runSettingsXml = "True"; - StringReader stringReader = new(runSettingXml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); MSTestSettingsProvider mstestSettingsProvider = new(); mstestSettingsProvider.Load(reader); @@ -267,9 +264,9 @@ internal void DeployShouldReturnTrueWhenDeploymentEnabledSetToTrueAndHasDeployme new DeploymentUtility(), _mockFileUtility.Object); - string runSettingXml = + string runSettingsXml = "True"; - StringReader stringReader = new(runSettingXml); + StringReader stringReader = new(runSettingsXml); var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); MSTestSettingsProvider mstestSettingsProvider = new(); mstestSettingsProvider.Load(reader); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs index 69ae52a851..b7d18b5576 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ThreadSafeStringWriterTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Globalization; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerManagerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerManagerTests.cs index d95cadf0fa..50334027ac 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerManagerTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !WIN_UI -using System.Diagnostics; -using System.Text; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerTests.cs index f97695a13a..1e93748a6f 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TraceListenerTests.cs @@ -3,8 +3,6 @@ #if !WIN_UI -using System.Text; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs index 0456a3cba3..20414f8607 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/AppDomainUtilitiesTests.cs @@ -2,9 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 - -using System.Xml; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using TestFramework.ForTestingMSTest; @@ -54,7 +51,7 @@ public void SetConfigurationFileShouldSetOMRedirectionIfConfigFileIsPresent() """; byte[] observedConfigBytes = setup.GetConfigurationBytes(); - string observedXml = System.Text.Encoding.UTF8.GetString(observedConfigBytes); + string observedXml = Encoding.UTF8.GetString(observedConfigBytes); Verify(SanitizeString(observedXml).Contains(SanitizeString(expectedRedir)), "Config must have OM redirection"); diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs index ae47587921..09fcd7bd52 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs index fe8a664887..c5819693df 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DesktopReflectionUtilityTests.cs @@ -2,9 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Collections; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs index 31909ed3e4..57bf7763de 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/VSInstallationUtilitiesTests.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NET462 -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/XmlUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/XmlUtilitiesTests.cs index f6f2d13165..42c9135a83 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/XmlUtilitiesTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/XmlUtilitiesTests.cs @@ -3,9 +3,6 @@ #if NET462 -using System.Reflection; -using System.Xml; - using TestFramework.ForTestingMSTest; using static MSTestAdapter.PlatformServices.UnitTests.Utilities.AppDomainUtilitiesTests; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs index 5f64b0a4ef..ef07e223fd 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13DeploymentItemUtilityTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; @@ -389,7 +386,7 @@ public void IsValidDeploymentItemShouldReturnTrueForAValidDeploymentItem() { Verify(DeploymentItemUtility.IsValidDeploymentItem(_defaultDeploymentItemPath, _defaultDeploymentItemOutputDirectory, out string warning)); - Verify(string.Empty.Equals(warning, StringComparison.Ordinal)); + Verify(warning is null); } #endregion diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs index 027e30af77..3174bf5115 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/ns13ReflectionUtilityTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ns10TestSourceTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ns10TestSourceTests.cs index 9884032ded..95ceea6783 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ns10TestSourceTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ns10TestSourceTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs index c34c2d834f..61ea77d64a 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorTests.cs @@ -2,10 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.ObjectModel; -using System.Globalization; -using System.Reflection; -using System.Text; -using System.Xml; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; @@ -26,7 +22,7 @@ public class AssemblyEnumeratorTests : TestContainer private readonly AssemblyEnumerator _assemblyEnumerator; private readonly TestablePlatformServiceProvider _testablePlatformServiceProvider; - private ICollection _warnings; + private readonly List _warnings; public AssemblyEnumeratorTests() { @@ -71,10 +67,9 @@ public void ConstructorShouldPopulateSettings() }); var mockMessageLogger = new Mock(); var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, mockMessageLogger.Object); - var assemblyEnumerator = new AssemblyEnumerator(adapterSettings) - { - RunSettingsXml = runSettingsXml, - }; + + // Constructor has the side effect of populating the passed settings to MSTestSettings.CurrentSettings + _ = new AssemblyEnumerator(adapterSettings); Verify(MSTestSettings.CurrentSettings.ForcedLegacyMode); Verify(MSTestSettings.CurrentSettings.TestSettingsFile == "DummyPath\\TestSettings1.testsettings"); @@ -101,7 +96,7 @@ public void GetTypesShouldReturnSetOfDefinedTypes() TypeInfo[] expectedTypes = [typeof(DummyTestClass).GetTypeInfo(), typeof(DummyTestClass).GetTypeInfo()]; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Returns(expectedTypes); + mockAssembly.Setup(a => a.GetTypes()).Returns(expectedTypes); IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); Verify(expectedTypes.SequenceEqual(types)); @@ -123,7 +118,7 @@ public void GetTypesShouldReturnReflectionTypeLoadExceptionTypesOnException() var reflectedTypes = new Type[] { typeof(DummyTestClass) }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(reflectedTypes, null)); + mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(reflectedTypes, null)); IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, string.Empty, _warnings); @@ -137,8 +132,8 @@ public void GetTypesShouldLogWarningsWhenReflectionFailsWithLoaderExceptions() var exceptions = new Exception[] { new("DummyLoaderException") }; // Setup mocks - mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(null, exceptions)); - mockAssembly.Setup(a => a.DefinedTypes).Throws(new ReflectionTypeLoadException(null, exceptions)); + mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions)); + mockAssembly.Setup(a => a.GetTypes()).Throws(new ReflectionTypeLoadException(null, exceptions)); IReadOnlyList types = AssemblyEnumerator.GetTypes(mockAssembly.Object, "DummyAssembly", _warnings); @@ -226,7 +221,7 @@ public void EnumerateAssemblyShouldReturnEmptyListWhenNoDeclaredTypes() _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - Verify(_assemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings).Count == 0); + Verify(_assemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings).Count == 0); } public void EnumerateAssemblyShouldReturnEmptyListWhenNoTestElementsInAType() @@ -237,14 +232,14 @@ public void EnumerateAssemblyShouldReturnEmptyListWhenNoTestElementsInAType() // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) - .Returns((ICollection)null); + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)) + .Returns((List)null); - Verify(_assemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings).Count == 0); + Verify(_assemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings).Count == 0); } public void EnumerateAssemblyShouldReturnTestElementsForAType() @@ -256,14 +251,14 @@ public void EnumerateAssemblyShouldReturnTestElementsForAType() // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) - .Returns(new Collection { unitTestElement }); + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)) + .Returns(new List { unitTestElement }); - ICollection testElements = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings); + ICollection testElements = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings); Verify(new Collection { unitTestElement }.SequenceEqual(testElements)); } @@ -273,19 +268,19 @@ public void EnumerateAssemblyShouldReturnMoreThanOneTestElementForAType() Mock mockAssembly = CreateMockTestableAssembly(); var testableAssemblyEnumerator = new TestableAssemblyEnumerator(); var unitTestElement = new UnitTestElement(new TestMethod("DummyMethod", "DummyClass", "DummyAssembly", false)); - var expectedTestElements = new Collection { unitTestElement, unitTestElement }; + var expectedTestElements = new List { unitTestElement, unitTestElement }; // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)) .Returns(expectedTestElements); - ICollection testElements = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings); + ICollection testElements = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings); Verify(expectedTestElements.SequenceEqual(testElements)); } @@ -295,19 +290,19 @@ public void EnumerateAssemblyShouldReturnMoreThanOneTestElementForMoreThanOneTyp Mock mockAssembly = CreateMockTestableAssembly(); var testableAssemblyEnumerator = new TestableAssemblyEnumerator(); var unitTestElement = new UnitTestElement(new TestMethod("DummyMethod", "DummyClass", "DummyAssembly", false)); - var expectedTestElements = new Collection { unitTestElement, unitTestElement }; + var expectedTestElements = new List { unitTestElement, unitTestElement }; // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass), typeof(DummyTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(DummyTestClass).GetTypeInfo(), typeof(DummyTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)) + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)) .Returns(expectedTestElements); - ICollection testElements = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings); + ICollection testElements = testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings); expectedTestElements.Add(unitTestElement); expectedTestElements.Add(unitTestElement); @@ -318,18 +313,18 @@ public void EnumerateAssemblyShouldNotLogWarningsIfNonePresent() { Mock mockAssembly = CreateMockTestableAssembly(); var testableAssemblyEnumerator = new TestableAssemblyEnumerator(); - ICollection warningsFromTypeEnumerator = []; + List warningsFromTypeEnumerator = []; // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(InternalTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(InternalTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out warningsFromTypeEnumerator)); + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(warningsFromTypeEnumerator)); - testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings); + testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings); Verify(_warnings.Count == 0); } @@ -337,7 +332,7 @@ public void EnumerateAssemblyShouldLogWarningsIfPresent() { Mock mockAssembly = CreateMockTestableAssembly(); var testableAssemblyEnumerator = new TestableAssemblyEnumerator(); - ICollection warningsFromTypeEnumerator = new Collection + var warningsFromTypeEnumerator = new List { "DummyWarning", }; @@ -345,15 +340,16 @@ public void EnumerateAssemblyShouldLogWarningsIfPresent() // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(InternalTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(InternalTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out warningsFromTypeEnumerator)); + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)) + .Callback(() => _warnings.AddRange(warningsFromTypeEnumerator)); - testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings); + testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings); - Verify(warningsFromTypeEnumerator.ToList().SequenceEqual(_warnings)); + Verify(warningsFromTypeEnumerator.SequenceEqual(_warnings)); } public void EnumerateAssemblyShouldHandleExceptionsWhileEnumeratingAType() @@ -365,13 +361,13 @@ public void EnumerateAssemblyShouldHandleExceptionsWhileEnumeratingAType() // Setup mocks mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(InternalTestClass)]); - mockAssembly.Setup(a => a.DefinedTypes) + mockAssembly.Setup(a => a.GetTypes()) .Returns([typeof(InternalTestClass).GetTypeInfo()]); _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("DummyAssembly", false)) .Returns(mockAssembly.Object); - testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(out _warnings)).Throws(exception); + testableAssemblyEnumerator.MockTypeEnumerator.Setup(te => te.Enumerate(_warnings)).Throws(exception); - testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", out _warnings); + testableAssemblyEnumerator.EnumerateAssembly("DummyAssembly", null, _warnings); Verify(_warnings.ToList().Contains( string.Format( @@ -396,7 +392,15 @@ private static Mock CreateMockTestableAssembly() mockAssembly .Setup(a => a.GetCustomAttributes( + typeof(TestDataSourceOptionsAttribute), + true)) + .Returns(Array.Empty()); + + mockAssembly + .Setup(a => a.GetCustomAttributes( +#pragma warning disable CS0618 // Type or member is obsolete typeof(TestDataSourceDiscoveryAttribute), +#pragma warning restore CS0618 // Type or member is obsolete true)) .Returns(Array.Empty()); @@ -429,14 +433,12 @@ internal TestableAssemblyEnumerator() reflectHelper.Object, typeValidator.Object, testMethodValidator.Object, - TestDataSourceDiscoveryOption.DuringExecution, TestIdGenerationStrategy.FullyQualified); } internal Mock MockTypeEnumerator { get; set; } - internal override TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, - TestDataSourceDiscoveryOption discoveryOption, TestIdGenerationStrategy testIdGenerationStrategy) + internal override TypeEnumerator GetTypeEnumerator(Type type, string assemblyFileName, bool discoverInternals, TestIdGenerationStrategy testIdGenerationStrategy) => MockTypeEnumerator.Object; } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs index 8b3b433213..13e34b7e83 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/AssemblyEnumeratorWrapperTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; @@ -21,7 +18,7 @@ public class AssemblyEnumeratorWrapperTests : TestContainer private readonly AssemblyEnumeratorWrapper _testableAssemblyEnumeratorWrapper; private readonly TestablePlatformServiceProvider _testablePlatformServiceProvider; - private ICollection _warnings; + private List _warnings; public AssemblyEnumeratorWrapperTests() { diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs index e7c3a59f05..ef2d8b35b7 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TestMethodValidatorTests.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -43,6 +38,8 @@ public void IsValidTestMethodShouldReturnFalseForMethodsWithoutATestMethodAttrib Verify(!_testMethodValidator.IsValidTestMethod(_mockMethodInfo.Object, _type, _warnings)); } + // TODO: Fix this test. It should be returning true, but we get false for a different reason (IsPublic is false) + // https://github.com/microsoft/testfx/issues/4207 public void IsValidTestMethodShouldReturnFalseForGenericTestMethodDefinitions() { SetupTestMethod(); @@ -53,18 +50,20 @@ public void IsValidTestMethodShouldReturnFalseForGenericTestMethodDefinitions() Verify(!_testMethodValidator.IsValidTestMethod(_mockMethodInfo.Object, _type, _warnings)); } - public void IsValidTestMethodShouldReportWarningsForGenericTestMethodDefinitions() + // TODO: Fix this test. It should be returning true, but we get false for a different reason (IsPublic is false) + // https://github.com/microsoft/testfx/issues/4207 + public void IsValidTestMethodShouldNotReportWarningsForGenericTestMethodDefinitions() { SetupTestMethod(); _mockMethodInfo.Setup(mi => mi.IsGenericMethodDefinition).Returns(true); _mockMethodInfo.Setup(mi => mi.DeclaringType.FullName).Returns("DummyTestClass"); _mockMethodInfo.Setup(mi => mi.Name).Returns("DummyTestMethod"); - + _mockMethodInfo.Setup(mi => mi.Attributes).Returns(MethodAttributes.Public); + _mockMethodInfo.Setup(mi => mi.ReturnType).Returns(typeof(void)); _testMethodValidator.IsValidTestMethod(_mockMethodInfo.Object, _type, _warnings); - Verify(_warnings.Count == 1); - Verify(_warnings.Contains(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, "DummyTestClass", "DummyTestMethod"))); + Verify(_warnings.Count == 0); } public void IsValidTestMethodShouldReturnFalseForNonPublicMethods() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs new file mode 100644 index 0000000000..99d9772523 --- /dev/null +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.MockedMethodInfoWithExtraAttributes.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if !NET6_0_OR_GREATER +using Polyfills; +#endif + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery; + +public partial class TypeEnumeratorTests +{ + private sealed class MockedMethodInfoWithExtraAttributes : MethodInfo + { + private readonly MethodInfo _original; + private readonly Attribute[] _extraAttributes; + + public MockedMethodInfoWithExtraAttributes(MethodInfo original, params Attribute[] extraAttributes) + { + _original = original; + _extraAttributes = extraAttributes; + } + + public override ICustomAttributeProvider ReturnTypeCustomAttributes => _original.ReturnTypeCustomAttributes; + + public override RuntimeMethodHandle MethodHandle => _original.MethodHandle; + + public override MethodAttributes Attributes => _original.Attributes; + + public override string Name => _original.Name; + + public override Type DeclaringType => _original.DeclaringType; + + public override Type ReflectedType => _original.ReflectedType; + + public override IEnumerable CustomAttributes => _original.CustomAttributes; + + public override int MetadataToken => _original.MetadataToken; + + public override Module Module => _original.Module; + + public override MethodImplAttributes MethodImplementationFlags => _original.MethodImplementationFlags; + + public override CallingConventions CallingConvention => _original.CallingConvention; + + public override bool IsGenericMethodDefinition => _original.IsGenericMethodDefinition; + + public override bool ContainsGenericParameters => _original.ContainsGenericParameters; + + public override bool IsGenericMethod => _original.IsGenericMethod; + + public override bool IsSecurityCritical => _original.IsSecurityCritical; + + public override bool IsSecuritySafeCritical => _original.IsSecuritySafeCritical; + + public override bool IsSecurityTransparent => _original.IsSecurityTransparent; + + public override MemberTypes MemberType => _original.MemberType; + + public override Type ReturnType => _original.ReturnType; + + public override ParameterInfo ReturnParameter => _original.ReturnParameter; + + public override Delegate CreateDelegate(Type delegateType) => _original.CreateDelegate(delegateType); + + public override Delegate CreateDelegate(Type delegateType, object target) => _original.CreateDelegate(delegateType, target); + + public override MethodInfo GetBaseDefinition() => _original.GetBaseDefinition(); + + public override object[] GetCustomAttributes(bool inherit) => _original.GetCustomAttributes().Concat(_extraAttributes).ToArray(); + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => _original.GetCustomAttributes().Concat(_extraAttributes.Where(a => a.GetType().IsAssignableTo(attributeType))).ToArray(); + + public override IList GetCustomAttributesData() => _original.GetCustomAttributesData(); + + public override Type[] GetGenericArguments() => _original.GetGenericArguments(); + + public override MethodInfo GetGenericMethodDefinition() => _original.GetGenericMethodDefinition(); + + public override MethodBody GetMethodBody() => _original.GetMethodBody(); + + public override MethodImplAttributes GetMethodImplementationFlags() => _original.GetMethodImplementationFlags(); + + public override ParameterInfo[] GetParameters() => _original.GetParameters(); + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + => _original.Invoke(obj, invokeAttr, binder, parameters, culture); + + public override bool IsDefined(Type attributeType, bool inherit) + => _original.IsDefined(attributeType, inherit) || _extraAttributes.Any(a => a.GetType().IsAssignableTo(attributeType)); + + public override MethodInfo MakeGenericMethod(params Type[] typeArguments) => _original.MakeGenericMethod(typeArguments); + + public override string ToString() => _original.ToString(); + } +} diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs index e3706ea127..5921ef972e 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeEnumeratorTests.cs @@ -1,14 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; -using System.Runtime.CompilerServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; -using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -19,7 +15,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery; -public class TypeEnumeratorTests : TestContainer +public partial class TypeEnumeratorTests : TestContainer { private readonly Mock _mockReflectHelper; private readonly Mock _mockTestMethodValidator; @@ -27,11 +23,15 @@ public class TypeEnumeratorTests : TestContainer private readonly TestablePlatformServiceProvider _testablePlatformServiceProvider; private readonly Mock _mockMessageLogger; - private ICollection _warnings; + private readonly List _warnings; public TypeEnumeratorTests() { - _mockReflectHelper = new Mock(); + _mockReflectHelper = new Mock() + { + CallBase = true, + }; + _mockTypeValidator = new Mock(MockBehavior.Default, _mockReflectHelper.Object); _mockTestMethodValidator = new Mock(MockBehavior.Default, _mockReflectHelper.Object); _warnings = new List(); @@ -55,7 +55,7 @@ protected override void Dispose(bool disposing) public void EnumerateShouldReturnNullIfTypeIsNotValid() { TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(IDummyInterface), string.Empty); - Verify(typeEnumerator.Enumerate(out _warnings) is null); + Verify(typeEnumerator.Enumerate(_warnings) is null); } public void EnumerateShouldReturnEmptyCollectionWhenNoValidTestMethodsExist() @@ -63,7 +63,7 @@ public void EnumerateShouldReturnEmptyCollectionWhenNoValidTestMethodsExist() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: false, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), string.Empty); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); Verify(tests.Count == 0); @@ -78,7 +78,7 @@ public void GetTestsShouldReturnDeclaredTestMethods() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyBaseTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -91,7 +91,7 @@ public void GetTestsShouldReturnBaseTestMethodsInSameAssembly() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -122,7 +122,7 @@ public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByDefault() TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -152,7 +152,7 @@ public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByConfiguratio SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -183,7 +183,7 @@ public void GetTestsShouldNotReturnBaseTestMethodsFromAnotherAssemblyByConfigura SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: false); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -197,7 +197,7 @@ public void GetTestsShouldNotReturnHiddenTestMethods() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyHidingTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -216,7 +216,7 @@ public void GetTestsShouldReturnOverriddenTestMethods() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyOverridingTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -239,7 +239,7 @@ public void GetTestsShouldNotReturnHiddenTestMethodsFromAnyLevel() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummySecondHidingTestClass), Assembly.GetExecutingAssembly().FullName); - ICollection tests = typeEnumerator.Enumerate(out _warnings); + ICollection tests = typeEnumerator.Enumerate(_warnings); Verify(tests is not null); @@ -291,34 +291,14 @@ public void GetTestFromMethodShouldInitializeAsyncTypeNameCorrectly() Verify(expectedAsyncTaskName == testElement.AsyncTypeName); } - public void GetTestFromMethodShouldSetIgnoredPropertyToFalseIfNotSetOnTestClassAndTestMethod() - { - SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); - TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); - MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - - // Setup mocks - _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(typeof(DummyTestClass), false)).Returns(false); - _mockReflectHelper.Setup( - rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)).Returns(false); - - MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); - - Verify(testElement is not null); - Verify(!testElement.Ignored); - } - public void GetTestFromMethodShouldSetTestCategory() { SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new TestCategoryAttribute("foo"), new TestCategoryAttribute("bar")); string[] testCategories = ["foo", "bar"]; - // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetTestCategories(methodInfo, typeof(DummyTestClass))).Returns(testCategories); - MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); Verify(testElement is not null); @@ -330,9 +310,7 @@ public void GetTestFromMethodShouldSetDoNotParallelize() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - - // Setup mocks - _mockReflectHelper.Setup(rh => rh.IsDerivedAttributeDefined(It.IsAny(), true)).Returns(true); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new DoNotParallelizeAttribute()); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -345,15 +323,20 @@ public void GetTestFromMethodShouldFillTraitsWithTestProperties() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - var testProperties = new List { new("foo", "bar"), new("fooprime", "barprime") }; - - // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetTestPropertiesAsTraits(methodInfo)).Returns(testProperties); + methodInfo = new MockedMethodInfoWithExtraAttributes( + methodInfo, + new TestMethodAttribute(), + new TestPropertyAttribute("foo", "bar"), + new TestPropertyAttribute("fooprime", "barprime")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); Verify(testElement is not null); - Verify(testProperties.SequenceEqual(testElement.Traits)); + Verify(testElement.Traits.Length == 2); + Verify(testElement.Traits[0].Name == "foo"); + Verify(testElement.Traits[0].Value == "bar"); + Verify(testElement.Traits[1].Name == "fooprime"); + Verify(testElement.Traits[1].Value == "barprime"); } public void GetTestFromMethodShouldFillTraitsWithTestOwnerPropertyIfPresent() @@ -361,18 +344,23 @@ public void GetTestFromMethodShouldFillTraitsWithTestOwnerPropertyIfPresent() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - var testProperties = new List { new("foo", "bar"), new("fooprime", "barprime") }; - var ownerTrait = new Trait("owner", "mike"); - - // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetTestPropertiesAsTraits(methodInfo)).Returns(testProperties); - _mockReflectHelper.Setup(rh => rh.GetTestOwnerAsTraits(methodInfo)).Returns(ownerTrait); + methodInfo = new MockedMethodInfoWithExtraAttributes( + methodInfo, + new TestMethodAttribute(), + new TestPropertyAttribute("foo", "bar"), + new TestPropertyAttribute("fooprime", "barprime"), + new OwnerAttribute("mike")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); Verify(testElement is not null); - testProperties.Add(ownerTrait); - Verify(testProperties.SequenceEqual(testElement.Traits)); + Verify(testElement.Traits.Length == 3); + Verify(testElement.Traits[0].Name == "foo"); + Verify(testElement.Traits[0].Value == "bar"); + Verify(testElement.Traits[1].Name == "fooprime"); + Verify(testElement.Traits[1].Value == "barprime"); + Verify(testElement.Traits[2].Name == "Owner"); + Verify(testElement.Traits[2].Value == "mike"); } public void GetTestFromMethodShouldFillTraitsWithTestPriorityPropertyIfPresent() @@ -380,19 +368,18 @@ public void GetTestFromMethodShouldFillTraitsWithTestPriorityPropertyIfPresent() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - var testProperties = new List { new("foo", "bar"), new("fooprime", "barprime") }; - var priorityTrait = new Trait("Priority", "1"); - - // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetTestPropertiesAsTraits(methodInfo)).Returns(testProperties); - _mockReflectHelper.Setup(rh => rh.GetPriority(methodInfo)).Returns(1); - _mockReflectHelper.Setup(rh => rh.GetTestPriorityAsTraits(1)).Returns(priorityTrait); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new TestPropertyAttribute("foo", "bar"), new TestPropertyAttribute("fooprime", "barprime"), new PriorityAttribute(1)); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); Verify(testElement is not null); - testProperties.Add(priorityTrait); - Verify(testProperties.SequenceEqual(testElement.Traits)); + Verify(testElement.Traits.Length == 3); + Verify(testElement.Traits[0].Name == "foo"); + Verify(testElement.Traits[0].Value == "bar"); + Verify(testElement.Traits[1].Name == "fooprime"); + Verify(testElement.Traits[1].Value == "barprime"); + Verify(testElement.Traits[2].Name == "Priority"); + Verify(testElement.Traits[2].Value == "1"); } public void GetTestFromMethodShouldSetPriority() @@ -400,9 +387,7 @@ public void GetTestFromMethodShouldSetPriority() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - - // Setup mocks - _mockReflectHelper.Setup(rh => rh.GetPriority(methodInfo)).Returns(1); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new PriorityAttribute(1)); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -415,7 +400,7 @@ public void GetTestFromMethodShouldSetDescription() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new DescriptionAttribute("Dummy description")); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new DescriptionAttribute("Dummy description")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -427,7 +412,7 @@ public void GetTestFromMethodShouldSetWorkItemIds() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetDerivedAttributes(methodInfo, true)).Returns([new(123), new(345)]); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new WorkItemAttribute(123), new WorkItemAttribute(345)); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -439,7 +424,6 @@ public void GetTestFromMethodShouldSetWorkItemIdsToNullIfNotAny() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetDerivedAttributes(methodInfo, true)).Returns([]); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -451,7 +435,7 @@ public void GetTestFromMethodShouldSetCssIteration() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new CssIterationAttribute("234")); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new CssIterationAttribute("234")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -463,7 +447,7 @@ public void GetTestFromMethodShouldSetCssProjectStructure() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod("MethodWithVoidReturnType"); - _mockReflectHelper.Setup(rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new CssProjectStructureAttribute("ProjectStructure123")); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new CssProjectStructureAttribute("ProjectStructure123")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -528,10 +512,7 @@ public void GetTestFromMethodShouldSetDisplayNameToTestMethodNameIfDisplayNameIs SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.MethodWithVoidReturnType)); - - // Setup mocks to behave like we have [TestMethod] attribute on the method - _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(It.IsAny(), false)).Returns(new TestMethodAttribute()); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new TestMethodAttribute()); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -544,10 +525,7 @@ public void GetTestFromMethodShouldSetDisplayNameFromTestMethodAttribute() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.MethodWithVoidReturnType)); - - // Setup mocks to behave like we have [TestMethod("Test method display name.")] attribute on the method - _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new TestMethodAttribute("Test method display name.")); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new TestMethodAttribute("Test method display name.")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -560,10 +538,7 @@ public void GetTestFromMethodShouldSetDisplayNameFromDataTestMethodAttribute() SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true); TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyTestClass), "DummyAssemblyName"); MethodInfo methodInfo = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.MethodWithVoidReturnType)); - - // Setup mocks to behave like we have [DataTestMethod("Test method display name.")] attribute on the method - _mockReflectHelper.Setup( - rh => rh.GetFirstDerivedAttributeOrDefault(methodInfo, true)).Returns(new DataTestMethodAttribute("Test method display name.")); + methodInfo = new MockedMethodInfoWithExtraAttributes(methodInfo, new DataTestMethodAttribute("Test method display name.")); MSTest.TestAdapter.ObjectModel.UnitTestElement testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, _warnings); @@ -577,7 +552,7 @@ public void GetTestFromMethodShouldSetDisplayNameFromDataTestMethodAttribute() private void SetupTestClassAndTestMethods(bool isValidTestClass, bool isValidTestMethod, bool isMethodFromSameAssembly) { - _mockTypeValidator.Setup(tv => tv.IsValidTestClass(It.IsAny(), It.IsAny>())) + _mockTypeValidator.Setup(tv => tv.IsValidTestClass(It.IsAny(), It.IsAny>())) .Returns(isValidTestClass); _mockTestMethodValidator.Setup( tmv => tmv.IsValidTestMethod(It.IsAny(), It.IsAny(), It.IsAny>())).Returns(isValidTestMethod); @@ -585,16 +560,13 @@ private void SetupTestClassAndTestMethods(bool isValidTestClass, bool isValidTes rh => rh.IsMethodDeclaredInSameAssemblyAsType(It.IsAny(), It.IsAny())).Returns(isMethodFromSameAssembly); } - private TypeEnumerator GetTypeEnumeratorInstance(Type type, string assemblyName, - TestDataSourceDiscoveryOption discoveryOption = TestDataSourceDiscoveryOption.DuringExecution, - TestIdGenerationStrategy idGenerationStrategy = TestIdGenerationStrategy.FullyQualified) + private TypeEnumerator GetTypeEnumeratorInstance(Type type, string assemblyName, TestIdGenerationStrategy idGenerationStrategy = TestIdGenerationStrategy.FullyQualified) => new( type, assemblyName, _mockReflectHelper.Object, _mockTypeValidator.Object, _mockTestMethodValidator.Object, - discoveryOption, idGenerationStrategy); #endregion diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs index c08376ec22..68687c1bd7 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/TypeValidatorTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs index 2941a271e0..24905444fa 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Discovery/UnitTestDiscovererTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; @@ -369,7 +367,7 @@ private void SetupNavigation(string source, UnitTestElement test, string classNa SetTestCaseNavigationData(testCase1, testNavigationData.FileName, testNavigationData.MinLineNumber); } - private void SetTestCaseNavigationData(TestCase testCase, string fileName, int lineNumber) + private static void SetTestCaseNavigationData(TestCase testCase, string fileName, int lineNumber) { testCase.LineNumber = lineNumber; testCase.CodeFilePath = fileName; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs index 60162cf013..5993212f62 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/DynamicDataAttributeTests.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -29,12 +25,12 @@ public DynamicDataAttributeTests() DynamicDataAttribute.TestIdGenerationStrategy = TestIdGenerationStrategy.FullyQualified; } - public void GetDataShouldThrowExceptionIfInvalidPropertyNameIsSpecifiedOrPropertyDoesNotExist() => - VerifyThrows(() => - { - _dynamicDataAttribute = new DynamicDataAttribute("ABC"); - _dynamicDataAttribute.GetData(_testMethodInfo); - }); + public void GetDataShouldThrowExceptionIfInvalidPropertyNameIsSpecifiedOrPropertyDoesNotExist() + { + _dynamicDataAttribute = new DynamicDataAttribute("ABC"); + InvalidOperationException ex = VerifyThrows(() => _dynamicDataAttribute.GetData(_testMethodInfo)); + Verify(ex.Message == string.Format(CultureInfo.InvariantCulture, Resource.DynamicDataSourceShouldExistAndBeValid, "ABC", _testMethodInfo.DeclaringType.FullName)); + } public void GetDataShouldReadDataFromProperty() { @@ -240,11 +236,10 @@ public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems() Verify(displayName == "TestMethod1 ([[\"a\",\"b\",\"c\"],[\"d\",\"e\",\"f\"],[\"gh\",\"ij\",\"kl\"]],['m','n','o'],[[\"1\",\"2\",\"3\"],[\"4\",\"5\",\"6\"],[\"7\",\"8\",\"9\"]])"); } -#if NETCOREAPP public void DynamicDataSource_WithTuple_Works() { MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property); + var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData)); dynamicDataAttribute.GetData(testMethodInfo); dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); @@ -254,7 +249,7 @@ public void DynamicDataSource_WithTuple_Works() public void DynamicDataSource_WithValueTuple_Works() { MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property); + var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTuple), typeof(TestClassTupleData)); dynamicDataAttribute.GetData(testMethodInfo); dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); @@ -264,44 +259,12 @@ public void DynamicDataSource_WithValueTuple_Works() public void DynamicDataSource_WithValueTupleWithTupleSyntax_Works() { MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Property); + var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData)); dynamicDataAttribute.GetData(testMethodInfo); dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method); dynamicDataAttribute.GetData(testMethodInfo); } -#else - public void DynamicDataSource_WithTuple_Throws() - { - MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property); - - VerifyThrows(() => dynamicDataAttribute.GetData(testMethodInfo)); - - dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); - VerifyThrows(() => dynamicDataAttribute.GetData(testMethodInfo)); - } - - public void DynamicDataSource_WithValueTuple_Throws() - { - MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property); - VerifyThrows(() => dynamicDataAttribute.GetData(testMethodInfo)); - - dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method); - VerifyThrows(() => dynamicDataAttribute.GetData(testMethodInfo)); - } - - public void DynamicDataSource_WithValueTupleWithTupleSyntax_Throws() - { - MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple)); - var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Property); - VerifyThrows(() => dynamicDataAttribute.GetData(testMethodInfo)); - - dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method); - VerifyThrows(() => dynamicDataAttribute.GetData(testMethodInfo)); - } -#endif } /// diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs index 223668be9e..64fa22ef13 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/ClassCleanupManagerTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; @@ -35,7 +33,7 @@ public void AssemblyCleanupRunsAfterAllTestsFinishEvenIfWeScheduleTheSameTestMul var classCleanupManager = new ClassCleanupManager(testsToRun, ClassCleanupBehavior.EndOfClass, ClassCleanupBehavior.EndOfClass, reflectHelper); - TestClassInfo testClassInfo = new(typeof(ClassCleanupManagerTests), null, true, null, null, null) + TestClassInfo testClassInfo = new(typeof(ClassCleanupManagerTests), null, true, null, null) { // This needs to be set, to allow running class cleanup. ClassCleanupMethod = classCleanupMethodInfo, diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs index 31f33aca5e..792eaf50e1 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblyInfoTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs index 9abcca60a1..95afcb4469 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestAssemblySettingsProviderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; @@ -101,7 +99,7 @@ public void GetSettingsShouldSetParallelScopeToClassLevelByDefault() MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = TestAssemblySettingsProvider.GetSettings("Foo"); // Assert. - Verify(settings.Scope == UTF.ExecutionScope.ClassLevel); + Verify(settings.Scope == ExecutionScope.ClassLevel); } public void GetSettingsShouldSetParallelScope() @@ -114,13 +112,13 @@ public void GetSettingsShouldSetParallelScope() _testablePlatformServiceProvider .MockReflectionOperations .Setup(ro => ro.GetCustomAttributes(It.IsAny(), typeof(UTF.ParallelizeAttribute))) - .Returns([new UTF.ParallelizeAttribute { Scope = UTF.ExecutionScope.MethodLevel }]); + .Returns([new UTF.ParallelizeAttribute { Scope = ExecutionScope.MethodLevel }]); // Act. MSTest.TestAdapter.ObjectModel.TestAssemblySettings settings = TestAssemblySettingsProvider.GetSettings("Foo"); // Assert. - Verify(settings.Scope == UTF.ExecutionScope.MethodLevel); + Verify(settings.Scope == ExecutionScope.MethodLevel); } public void GetSettingsShouldSetCanParallelizeAssemblyToTrueByDefault() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs index c4840468df..99c1297130 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestClassInfoTests.cs @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Moq; @@ -22,8 +21,6 @@ public class TestClassInfoTests : TestContainer private readonly ConstructorInfo _testClassConstructor; - private readonly PropertyInfo _testContextProperty; - private readonly UTF.TestClassAttribute _testClassAttribute; private readonly TestAssemblyInfo _testAssemblyInfo; @@ -36,7 +33,6 @@ public TestClassInfoTests() { _testClassType = typeof(DummyTestClass); _testClassConstructor = _testClassType.GetConstructors().First(); - _testContextProperty = _testClassType.GetProperties().First(); _testClassAttribute = (UTF.TestClassAttribute)_testClassType.GetCustomAttributes().First(); _testAssemblyInfo = new TestAssemblyInfo(_testClassType.Assembly); @@ -44,7 +40,6 @@ public TestClassInfoTests() _testClassType, _testClassConstructor, true, - _testContextProperty, _testClassAttribute, _testAssemblyInfo); @@ -69,7 +64,7 @@ public TestClassInfoTests() public void TestClassInfoConstructorGetsTheConstructorInfoForTestClass() => Verify(_testClassConstructor == _testClassInfo.Constructor); - public void TestClassInfoTestContextPropertyGetsAReferenceToTheTestContextDefinedInTestClass() => Verify(_testContextProperty == _testClassInfo.TestContextProperty); + public void TestClassInfoTestContextPropertyGetsAReferenceToTheTestContextDefinedInTestClass() => Verify(_testClassInfo.TestContextProperty == _testClassType.GetProperty("TestContext")); public void TestClassInfoParentGetsAReferenceToTheParentAssemblyForTheTestClass() => Verify(_testAssemblyInfo == _testClassInfo.Parent); @@ -103,7 +98,7 @@ public void TestClassInfoClassCleanupMethodShouldNotInvokeWhenNoTestClassInitial _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod("ClassCleanupMethod"); _testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("ClassInitializeMethod"); - _testClassInfo.ExecuteClassCleanup(); // call cleanup without calling init + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // call cleanup without calling init Verify(classCleanupCallCount == 0); } @@ -116,7 +111,7 @@ public void TestClassInfoClassCleanupMethodShouldInvokeWhenTestClassInitializedI _testClassInfo.ClassInitializeMethod = typeof(DummyTestClass).GetMethod("ClassInitializeMethod"); _testClassInfo.RunClassInitialize(_testContext); - _testClassInfo.ExecuteClassCleanup(); // call cleanup without calling init + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // call cleanup without calling init Verify(classCleanupCallCount == 1); } @@ -130,7 +125,7 @@ public void TestClassInfoClassCleanupMethodShouldInvokeBaseClassCleanupMethodWhe _testClassInfo.BaseClassCleanupMethods.Add(typeof(DummyBaseTestClass).GetMethod("CleanupClassMethod")); _testClassInfo.RunClassInitialize(_testContext); - _testClassInfo.ExecuteClassCleanup(); + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); Verify(classCleanupCallCount == 1); } @@ -419,7 +414,8 @@ public void RunClassCleanupShouldInvokeIfClassCleanupMethod() _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.ClassCleanupMethod)); // Act - _testClassInfo.ExecuteClassCleanup(); + _testClassInfo.RunClassInitialize(null); + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(classCleanupCallCount == 1); @@ -433,7 +429,7 @@ public void RunClassCleanupShouldNotInvokeIfClassCleanupIsNull() _testClassInfo.ClassCleanupMethod = null; // Act - _testClassInfo.ExecuteClassCleanup(); + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(classCleanupCallCount == 0); @@ -446,7 +442,8 @@ public void RunClassCleanupShouldReturnAssertFailureExceptionDetails() _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.ClassCleanupMethod)); // Act - Exception classCleanupException = VerifyThrows(_testClassInfo.ExecuteClassCleanup); + _testClassInfo.RunClassInitialize(null); + Exception classCleanupException = VerifyThrows(() => _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()))); // Assert Verify(classCleanupException.Message.StartsWith("Class Cleanup method DummyTestClass.ClassCleanupMethod failed.", StringComparison.Ordinal)); @@ -461,7 +458,8 @@ public void RunClassCleanupShouldReturnAssertInconclusiveExceptionDetails() _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.ClassCleanupMethod)); // Act - Exception classCleanupException = VerifyThrows(_testClassInfo.ExecuteClassCleanup); + _testClassInfo.RunClassInitialize(null); + Exception classCleanupException = VerifyThrows(() => _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()))); // Assert Verify(classCleanupException.Message.StartsWith("Class Cleanup method DummyTestClass.ClassCleanupMethod failed.", StringComparison.Ordinal)); @@ -476,7 +474,8 @@ public void RunClassCleanupShouldReturnExceptionDetailsOfNonAssertExceptions() _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.ClassCleanupMethod)); // Act - Exception classCleanupException = VerifyThrows(_testClassInfo.ExecuteClassCleanup); + _testClassInfo.RunClassInitialize(null); + Exception classCleanupException = VerifyThrows(() => _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()))); // Assert Verify(classCleanupException.Message.StartsWith("Class Cleanup method DummyTestClass.ClassCleanupMethod failed.", StringComparison.Ordinal)); @@ -493,7 +492,8 @@ public void RunBaseClassCleanupWithNoDerivedClassCleanupShouldReturnExceptionDet _testClassInfo.BaseClassCleanupMethods.Add(baseClassCleanupMethod); // Act - Exception classCleanupException = VerifyThrows(_testClassInfo.ExecuteClassCleanup); + _testClassInfo.RunClassInitialize(null); + Exception classCleanupException = VerifyThrows(() => _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()))); // Assert Verify(classCleanupException.Message.StartsWith("Class Cleanup method DummyBaseTestClass.CleanupClassMethod failed.", StringComparison.Ordinal)); @@ -511,10 +511,26 @@ public void RunBaseClassCleanupEvenIfThereIsNoDerivedClassCleanup() _testClassInfo.BaseClassCleanupMethods.Add(baseClassCleanupMethod); // Act - _testClassInfo.ExecuteClassCleanup(); + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); // Assert Verify(_testClassInfo.HasExecutableCleanupMethod); + Verify(classCleanupCallCount == 0, "DummyBaseTestClass.CleanupClassMethod call count"); + + // Act 2 + _testClassInfo.RunClassInitialize(null); + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); + + // Assert 2 + Verify(_testClassInfo.HasExecutableCleanupMethod); + Verify(_testClassInfo.IsClassInitializeExecuted); + Verify(classCleanupCallCount == 1, "DummyBaseTestClass.CleanupClassMethod call count"); + + // Act 3 + _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary())); + + // Assert 3 + Verify(_testClassInfo.HasExecutableCleanupMethod); Verify(classCleanupCallCount == 1, "DummyBaseTestClass.CleanupClassMethod call count"); } @@ -526,7 +542,8 @@ public void RunClassCleanupShouldThrowTheInnerMostExceptionWhenThereAreMultipleN DummyTestClass.ClassCleanupMethodBody = FailingStaticHelper.DoWork; _testClassInfo.ClassCleanupMethod = typeof(DummyTestClass).GetMethod("ClassCleanupMethod"); - Exception classCleanupException = VerifyThrows(_testClassInfo.ExecuteClassCleanup); + _testClassInfo.RunClassInitialize(null); + Exception classCleanupException = VerifyThrows(() => _testClassInfo.ExecuteClassCleanup(new TestContextImplementation(null, new StringWriter(), new Dictionary()))); Verify(classCleanupException.Message.StartsWith("Class Cleanup method DummyTestClass.ClassCleanupMethod failed. Error Message: System.InvalidOperationException: I fail..", StringComparison.Ordinal)); Verify(classCleanupException.Message.Contains("at Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestClassInfoTests.FailingStaticHelper..cctor()")); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs index 046f7d8ed8..f00010e440 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -126,7 +123,7 @@ public void RunTestsForTestWithFilterShouldSendResultsForFilteredTests() public void RunTestsForIgnoredTestShouldSendResultsMarkingIgnoredTestsAsSkipped() { - TestCase testCase = GetTestCase(typeof(DummyTestClass), "IgnoredTest", ignore: true); + TestCase testCase = GetTestCase(typeof(DummyTestClass), "IgnoredTest"); TestCase[] tests = [testCase]; _testExecutionManager.RunTests(tests, _runContext, _frameworkHandle, _cancellationToken); @@ -817,14 +814,11 @@ private void RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState() #region private methods - private static TestCase GetTestCase(Type typeOfClass, string testName, bool ignore = false) + private static TestCase GetTestCase(Type typeOfClass, string testName) { MethodInfo methodInfo = typeOfClass.GetMethod(testName); var testMethod = new TestMethod(methodInfo.Name, typeOfClass.FullName, Assembly.GetExecutingAssembly().Location, isAsync: false); - UnitTestElement element = new(testMethod) - { - Ignored = ignore, - }; + UnitTestElement element = new(testMethod); return element.ToTestCase(); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs index 91cce933f9..5be66c1c2d 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodFilterTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -60,7 +58,7 @@ public void PropertyProviderValueForInvalidTestCaseReturnsNull() public void PropertyProviderValueForInvalidPropertyNameReturnsNull() { Type type = typeof(DummyTestClassWithTestMethods); - string fullName = $"{type.FullName}.{"TestMethod"}"; + string fullName = $"{type.FullName}.TestMethod"; TestCase testCase = new(fullName, MSTest.TestAdapter.Constants.ExecutorUri, Assembly.GetExecutingAssembly().FullName); object result = _testMethodFilter.PropertyValueProvider(testCase, null); @@ -70,7 +68,7 @@ public void PropertyProviderValueForInvalidPropertyNameReturnsNull() public void PropertyProviderValueForSupportedPropertyNameWhichIsNotSetReturnsNull() { Type type = typeof(DummyTestClassWithTestMethods); - string fullName = $"{type.FullName}.{"TestMethod"}"; + string fullName = $"{type.FullName}.TestMethod"; TestCase testCase = new(fullName, MSTest.TestAdapter.Constants.ExecutorUri, Assembly.GetExecutingAssembly().FullName); object result = _testMethodFilter.PropertyValueProvider(testCase, "Priority"); @@ -80,7 +78,7 @@ public void PropertyProviderValueForSupportedPropertyNameWhichIsNotSetReturnsNul public void PropertyProviderValueForValidTestAndSupportedPropertyNameReturnsValue() { Type type = typeof(DummyTestClassWithTestMethods); - string fullName = $"{type.FullName}.{"TestMethod"}"; + string fullName = $"{type.FullName}.TestMethod"; TestCase testCase = new(fullName, MSTest.TestAdapter.Constants.ExecutorUri, Assembly.GetExecutingAssembly().FullName); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs index 70b8453cac..400d28d913 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodInfoTests.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; @@ -35,8 +31,6 @@ public class TestMethodInfoTests : TestContainer private readonly UTF.TestMethodAttribute _testMethodAttribute; - private readonly PropertyInfo _testContextProperty; - private readonly TestAssemblyInfo _testAssemblyInfo; private readonly ConstructorInfo _constructorInfo; @@ -55,14 +49,13 @@ public TestMethodInfoTests() _methodInfo = typeof(DummyTestClass).GetMethods().Single(m => m.Name.Equals("DummyTestMethod", StringComparison.Ordinal)); _classAttribute = new UTF.TestClassAttribute(); _testMethodAttribute = new UTF.TestMethodAttribute(); - _testContextProperty = typeof(DummyTestClass).GetProperty("TestContext"); _testAssemblyInfo = new TestAssemblyInfo(typeof(DummyTestClass).Assembly); var testMethod = new TestMethod("dummyTestName", "dummyClassName", "dummyAssemblyName", false); _testContextImplementation = new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()); - _testClassInfo = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _testContextProperty, _classAttribute, _testAssemblyInfo); + _testClassInfo = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _classAttribute, _testAssemblyInfo); _expectedException = new UTF.ExpectedExceptionAttribute(typeof(DivideByZeroException)); - _testMethodOptions = new TestMethodOptions(TimeoutInfo.FromTimeout(3600 * 1000), null, _testContextImplementation, false, _testMethodAttribute); + _testMethodOptions = new TestMethodOptions(TimeoutInfo.FromTimeout(3600 * 1000), _testContextImplementation, false, _testMethodAttribute); _testMethodInfo = new TestMethodInfo( _methodInfo, @@ -268,7 +261,7 @@ public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrow public void TestMethodInfoInvokeShouldSetErrorMessageIfTestClassConstructorThrowsWithoutInnerException() { ConstructorInfo ctorInfo = typeof(DummyTestClassWithParameterizedCtor).GetConstructors().Single(); - var testClass = new TestClassInfo(typeof(DummyTestClassWithParameterizedCtor), ctorInfo, true, _testContextProperty, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClassWithParameterizedCtor), ctorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(_methodInfo, testClass, _testMethodOptions); UTF.TestResult result = method.Invoke(null); @@ -298,7 +291,7 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstru public void TestMethodInfoInvokeShouldSetStackTraceInformationIfTestClassConstructorThrowsWithoutInnerException() { ConstructorInfo ctorInfo = typeof(DummyTestClassWithParameterizedCtor).GetConstructors().Single(); - var testClass = new TestClassInfo(typeof(DummyTestClassWithParameterizedCtor), ctorInfo, true, _testContextProperty, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClassWithParameterizedCtor), ctorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(_methodInfo, testClass, _testMethodOptions); var exception = method.Invoke(null).TestFailureException as TestFailedException; @@ -327,7 +320,7 @@ public void TestMethodInfoInvokeShouldSetResultFilesIfTestContextHasAttachments( public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContext_SetsItToTestContext() { ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(UTFExtension.TestContext)]); - var testClassInfo = new TestClassInfo(typeof(DummyTestClass), ctorInfo, false, _testContextProperty, _classAttribute, _testAssemblyInfo); + var testClassInfo = new TestClassInfo(typeof(DummyTestClass), ctorInfo, false, _classAttribute, _testAssemblyInfo); var testMethodInfo = new TestMethodInfo(_methodInfo, testClassInfo, _testMethodOptions); UTF.TestResult result = testMethodInfo.Invoke(null); @@ -341,7 +334,7 @@ public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContext_SetsIt public void TestMethodInfoInvokeShouldNotThrowIfTestContextIsNotPresent() { - var testClass = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, null, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(_methodInfo, testClass, _testMethodOptions); UTF.TestResult result; @@ -353,8 +346,7 @@ public void TestMethodInfoInvokeShouldNotThrowIfTestContextIsNotPresent() public void TestMethodInfoInvokeShouldNotThrowIfTestContextDoesNotHaveASetter() { - PropertyInfo testContext = typeof(DummyTestClassWithTestContextWithoutSetter).GetProperties().Single(); - var testClass = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, testContext, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClass), _constructorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(_methodInfo, testClass, _testMethodOptions); UTF.TestResult result; @@ -414,7 +406,7 @@ public void TestMethodInfoInvokeShouldSetStackTraceInformationIfSetTestContextTh public void TestMethodInfoInvoke_WhenCtorHasOneParameterOfTypeTestContextAndTestContextProperty_InitializeBothTestContexts() { ConstructorInfo ctorInfo = typeof(DummyTestClass).GetConstructor([typeof(UTFExtension.TestContext)]); - var testClassInfo = new TestClassInfo(typeof(DummyTestClass), ctorInfo, false, _testContextProperty, _classAttribute, _testAssemblyInfo); + var testClassInfo = new TestClassInfo(typeof(DummyTestClass), ctorInfo, false, _classAttribute, _testAssemblyInfo); var testMethodInfo = new TestMethodInfo(_methodInfo, testClassInfo, _testMethodOptions); UTFExtension.TestContext testContext = null; DummyTestClass.TestContextSetterBody = context => testContext = context as UTFExtension.TestContext; @@ -505,7 +497,7 @@ public void TestMethodInfoInvokeWhenTestThrowsReturnsExpectedResult() _testClassInfo.TestInitializeMethod.Name, "System.ArgumentException: Some exception message ---> System.InvalidOperationException: Inner exception message"); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; // Act. UTF.TestResult result = testMethodInfo.Invoke(null); @@ -530,7 +522,7 @@ public void TestInitialize_WhenTestReturnsTaskFromException_DisplayProperExcepti // Arrange. DummyTestClass.TestInitializeMethodBodyAsync = async classInstance => await Task.FromException(new Exception("Outer", new InvalidOperationException("Inner"))); _testClassInfo.TestInitializeMethod = typeof(DummyTestClass).GetMethod("DummyTestInitializeMethodAsync"); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; // Act. UTF.TestResult result = testMethodInfo.Invoke(null); @@ -566,7 +558,7 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertFailReturnsExpectedResult() _testClassInfo.TestInitializeMethod.Name, "Assert.Fail failed. dummyFailMessage"); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; // Act. UTF.TestResult result = testMethodInfo.Invoke(null); @@ -597,7 +589,7 @@ public void TestMethodInfoInvokeWhenTestThrowsAssertInconclusiveReturnsExpectedR _testClassInfo.TestInitializeMethod.Name, "Assert.Inconclusive failed. dummyFailMessage"); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; // Act. UTF.TestResult result = testMethodInfo.Invoke(null); @@ -886,7 +878,7 @@ public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClass() bool disposeCalled = false; DummyTestClassWithDisposable.DisposeMethodBody = () => disposeCalled = true; ConstructorInfo ctorInfo = typeof(DummyTestClassWithDisposable).GetConstructor([])!; - var testClass = new TestClassInfo(typeof(DummyTestClassWithDisposable), ctorInfo, true, null, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClassWithDisposable), ctorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(typeof(DummyTestClassWithDisposable).GetMethod("DummyTestMethod"), testClass, _testMethodOptions); method.Invoke(null); @@ -901,7 +893,7 @@ public void TestMethodInfoInvoke_WhenTestClassIsAsyncDisposable_ShouldDisposeAsy bool asyncDisposeCalled = false; DummyTestClassWithAsyncDisposable.DisposeAsyncMethodBody = () => asyncDisposeCalled = true; ConstructorInfo ctorInfo = typeof(DummyTestClassWithAsyncDisposable).GetConstructor([])!; - var testClass = new TestClassInfo(typeof(DummyTestClassWithAsyncDisposable), ctorInfo, true, null, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClassWithAsyncDisposable), ctorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(typeof(DummyTestClassWithAsyncDisposable).GetMethod("DummyTestMethod"), testClass, _testMethodOptions); // Act @@ -922,7 +914,7 @@ public void TestMethodInfoInvoke_WhenTestClassIsDisposableAndAsyncDisposable_Sho DummyTestClassWithAsyncDisposableAndDisposable.DisposeAsyncMethodBody = () => disposeAsyncCalledOrder = ++order; ConstructorInfo ctorInfo = typeof(DummyTestClassWithAsyncDisposableAndDisposable).GetConstructor([])!; - var testClass = new TestClassInfo(typeof(DummyTestClassWithAsyncDisposableAndDisposable), ctorInfo, true, null, _classAttribute, _testAssemblyInfo); + var testClass = new TestClassInfo(typeof(DummyTestClassWithAsyncDisposableAndDisposable), ctorInfo, true, _classAttribute, _testAssemblyInfo); var method = new TestMethodInfo(typeof(DummyTestClassWithAsyncDisposableAndDisposable).GetMethod("DummyTestMethod"), testClass, _testMethodOptions); // Act @@ -940,7 +932,7 @@ public void TestMethodInfoInvokeShouldCallDisposeForDisposableTestClassIfTestCle DummyTestClassWithDisposable.DisposeMethodBody = () => disposeCalled = true; DummyTestClassWithDisposable.DummyTestCleanupMethodBody = classInstance => throw new NotImplementedException(); ConstructorInfo ctorInfo = typeof(DummyTestClassWithDisposable).GetConstructor([])!; - var testClass = new TestClassInfo(typeof(DummyTestClassWithDisposable), ctorInfo, true, null, _classAttribute, _testAssemblyInfo) + var testClass = new TestClassInfo(typeof(DummyTestClassWithDisposable), ctorInfo, true, _classAttribute, _testAssemblyInfo) { TestCleanupMethod = typeof(DummyTestClassWithDisposable).GetMethod("DummyTestCleanupMethod"), }; @@ -1007,7 +999,7 @@ public void TestMethodInfoInvokeShouldNotCallTestCleanupIfClassSetContextThrows( public void TestMethodInfoInvokeShouldSetResultAsPassedIfExpectedExceptionIsThrown() { DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; UTF.TestResult result = testMethodInfo.Invoke(null); @@ -1017,7 +1009,7 @@ public void TestMethodInfoInvokeShouldSetResultAsPassedIfExpectedExceptionIsThro public void TestMethodInfoInvokeShouldSetResultAsFailedIfExceptionDifferentFromExpectedExceptionIsThrown() { DummyTestClass.TestMethodBody = o => throw new IndexOutOfRangeException(); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; UTF.TestResult result = testMethodInfo.Invoke(null); @@ -1030,7 +1022,7 @@ public void TestMethodInfoInvokeShouldSetResultAsFailedIfExceptionDifferentFromE public void TestMethodInfoInvokeShouldSetResultAsFailedWhenExceptionIsExpectedButIsNotThrown() { DummyTestClass.TestMethodBody = o => { return; }; - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; UTF.TestResult result = testMethodInfo.Invoke(null); Verify(result.Outcome == UTF.UnitTestOutcome.Failed); string message = "Test method did not throw expected exception System.DivideByZeroException."; @@ -1040,7 +1032,7 @@ public void TestMethodInfoInvokeShouldSetResultAsFailedWhenExceptionIsExpectedBu public void TestMethodInfoInvokeShouldSetResultAsInconclusiveWhenExceptionIsAssertInconclusiveException() { DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; UTF.TestResult result = testMethodInfo.Invoke(null); Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); string message = "Exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException' was thrown."; @@ -1059,7 +1051,7 @@ public void TestMethodInfoInvokeShouldSetTestOutcomeBeforeTestCleanup() } }; _testClassInfo.TestCleanupMethod = typeof(DummyTestClass).GetMethod("DummyTestCleanupMethod"); - var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions with { ExpectedException = _expectedException }); + var testMethodInfo = new TestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions) { ExpectedException = _expectedException }; UTF.TestResult result = testMethodInfo.Invoke(null); @@ -1072,7 +1064,10 @@ public void HandleMethodExceptionShouldInvokeVerifyOfCustomExpectedException() var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = customExpectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = customExpectedException, + }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); UTF.TestResult result = method.Invoke(null); @@ -1086,7 +1081,10 @@ public void HandleMethodExceptionShouldSetOutcomeAsFailedIfVerifyOfExpectedExcep var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = customExpectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = customExpectedException, + }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); UTF.TestResult result = method.Invoke(null); @@ -1100,7 +1098,10 @@ public void HandleMethodExceptionShouldSetOutcomeAsInconclusiveIfVerifyOfExpecte var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = customExpectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = customExpectedException, + }; DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); UTF.TestResult result = method.Invoke(null); @@ -1115,7 +1116,10 @@ public void HandleMethodExceptionShouldInvokeVerifyOfDerivedCustomExpectedExcept var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = derivedCustomExpectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = derivedCustomExpectedException, + }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); UTF.TestResult result = method.Invoke(null); @@ -1132,7 +1136,10 @@ public void VerifyShouldNotThrowIfThrownExceptionCanBeAssignedToExpectedExceptio var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = expectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = expectedException, + }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); UTF.TestResult result = method.Invoke(null); @@ -1148,7 +1155,10 @@ public void VerifyShouldThrowExceptionIfThrownExceptionCannotBeAssignedToExpecte var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = expectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = expectedException, + }; DummyTestClass.TestMethodBody = o => throw new ArgumentNullException(); UTF.TestResult result = method.Invoke(null); @@ -1167,7 +1177,10 @@ public void VerifyShouldRethrowExceptionIfThrownExceptionIsAssertFailedException var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = expectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = expectedException, + }; DummyTestClass.TestMethodBody = o => throw new UTF.AssertFailedException(); UTF.TestResult result = method.Invoke(null); @@ -1185,7 +1198,10 @@ public void VerifyShouldRethrowExceptionIfThrownExceptionIsAssertInconclusiveExc var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = expectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = expectedException, + }; DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); UTF.TestResult result = method.Invoke(null); @@ -1200,7 +1216,10 @@ public void VerifyShouldThrowIfThrownExceptionIsNotSameAsExpectedException() var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = expectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = expectedException, + }; DummyTestClass.TestMethodBody = o => throw new DivideByZeroException(); UTF.TestResult result = method.Invoke(null); @@ -1216,7 +1235,10 @@ public void VerifyShouldRethrowIfThrownExceptionIsAssertExceptionWhichIsNotSameA var method = new TestMethodInfo( _methodInfo, _testClassInfo, - _testMethodOptions with { ExpectedException = expectedException, TimeoutInfo = TimeoutInfo.FromTimeout(0) }); + _testMethodOptions with { TimeoutInfo = TimeoutInfo.FromTimeout(0) }) + { + ExpectedException = expectedException, + }; DummyTestClass.TestMethodBody = o => throw new UTF.AssertInconclusiveException(); UTF.TestResult result = method.Invoke(null); @@ -1225,6 +1247,67 @@ public void VerifyShouldRethrowIfThrownExceptionIsAssertExceptionWhichIsNotSameA Verify(result.Outcome == UTF.UnitTestOutcome.Inconclusive); } + public void ResolveExpectedExceptionShouldThrowWhenAttributeIsDefinedTwice_DifferentConcreteType() + { + MethodInfo testMethodInfo = typeof(DummyTestClassForExpectedException).GetMethod(nameof(DummyTestClassForExpectedException.DummyTestMethod1)); + TestClassInfo classInfo = new( + typeof(DummyTestClassForExpectedException), + typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty()), + isParameterlessConstructor: true, + new UTF.TestClassAttribute(), + new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); + + TypeInspectionException ex = UTF.Assert.ThrowsException(() => new TestMethodInfo(testMethodInfo, classInfo, _testMethodOptions)); + UTF.Assert.AreEqual("The test method Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests+DummyTestClassForExpectedException.DummyTestMethod1 has multiple attributes derived from ExpectedExceptionBaseAttribute defined on it. Only one such attribute is allowed.", ex.Message); + } + + public void ResolveExpectedExceptionShouldThrowWhenAttributeIsDefinedTwice_SameConcreteType() + { + MethodInfo testMethodInfo = typeof(DummyTestClassForExpectedException).GetMethod(nameof(DummyTestClassForExpectedException.DummyTestMethod1)); + TestClassInfo classInfo = new( + typeof(DummyTestClassForExpectedException), + typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty()), + isParameterlessConstructor: true, + new UTF.TestClassAttribute(), + new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); + + TypeInspectionException ex = UTF.Assert.ThrowsException(() => new TestMethodInfo(testMethodInfo, classInfo, _testMethodOptions)); + UTF.Assert.AreEqual("The test method Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution.TestMethodInfoTests+DummyTestClassForExpectedException.DummyTestMethod1 has multiple attributes derived from ExpectedExceptionBaseAttribute defined on it. Only one such attribute is allowed.", ex.Message); + } + + public void ResolveExpectedExceptionHelperShouldReturnExpectedExceptionAttributeIfPresent() + { + Type type = typeof(DummyTestClassForExpectedException); + MethodInfo methodInfo = type.GetMethod(nameof(DummyTestClassForExpectedException.TestMethodWithExpectedException)); + TestClassInfo classInfo = new( + typeof(DummyTestClassForExpectedException), + typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty()), + isParameterlessConstructor: true, + new UTF.TestClassAttribute(), + new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); + + var testMethodInfo = new TestMethodInfo(methodInfo, classInfo, _testMethodOptions); + + Verify(testMethodInfo.ExpectedException is not null); + Verify(((UTF.ExpectedExceptionAttribute)testMethodInfo.ExpectedException).ExceptionType == typeof(DivideByZeroException)); + } + + public void ResolveExpectedExceptionHelperShouldReturnNullIfExpectedExceptionAttributeIsNotPresent() + { + Type type = typeof(DummyTestClassForExpectedException); + MethodInfo methodInfo = type.GetMethod(nameof(DummyTestClassForExpectedException.TestMethodWithoutExpectedException)); + TestClassInfo classInfo = new( + typeof(DummyTestClassForExpectedException), + typeof(DummyTestClassForExpectedException).GetConstructor(Array.Empty()), + isParameterlessConstructor: true, + new UTF.TestClassAttribute(), + new TestAssemblyInfo(typeof(DummyTestClassForExpectedException).Assembly)); + + var testMethodInfo = new TestMethodInfo(methodInfo, classInfo, _testMethodOptions); + + Verify(testMethodInfo.ExpectedException is null); + } + #endregion #region TestMethod invoke setup order @@ -1276,7 +1359,7 @@ public void TestMethodInfoInvokeShouldReturnTestFailureOnTimeout() UTF.TestResult result = method.Invoke(null); Verify(result.Outcome == UTF.UnitTestOutcome.Timeout); - Verify(result.TestFailureException.Message.Contains("exceeded execution timeout period")); + Verify(result.TestFailureException.Message.Equals("Test 'DummyTestMethod' timed out after 1ms", StringComparison.Ordinal)); }); } @@ -1303,7 +1386,7 @@ public void TestMethodInfoInvokeShouldCancelTokenSourceOnTimeout() UTF.TestResult result = method.Invoke(null); Verify(result.Outcome == UTF.UnitTestOutcome.Timeout); - Verify(result.TestFailureException.Message.Contains("exceeded execution timeout period")); + Verify(result.TestFailureException.Message.Equals("Test 'DummyTestMethod' timed out after 1ms", StringComparison.Ordinal)); Verify(_testContextImplementation.CancellationTokenSource.IsCancellationRequested, "Not canceled.."); }); } @@ -1333,7 +1416,7 @@ public void TestMethodInfoInvokeShouldFailOnTokenSourceCancellation() UTF.TestResult result = method.Invoke(null); Verify(result.Outcome == UTF.UnitTestOutcome.Timeout); - Verify(result.TestFailureException.Message.Contains("execution has been aborted")); + Verify(result.TestFailureException.Message.Equals("Test 'DummyTestMethod' was canceled", StringComparison.Ordinal)); Verify(_testContextImplementation.CancellationTokenSource.IsCancellationRequested, "Not canceled.."); }); } @@ -1448,7 +1531,7 @@ public void ResolveArgumentsShouldReturnPopulatedParamsWithAllProvided() #region helper methods - private void RunWithTestablePlatformService(TestablePlatformServiceProvider testablePlatformServiceProvider, Action action) + private static void RunWithTestablePlatformService(TestablePlatformServiceProvider testablePlatformServiceProvider, Action action) { try { @@ -1625,6 +1708,42 @@ protected internal override void Verify(Exception exception) #endregion + public class DummyTestClassForExpectedException + { + private class MyExpectedException1Attribute : UTF.ExpectedExceptionBaseAttribute + { + protected internal override void Verify(Exception exception) => throw new NotImplementedException(); + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class MyExpectedException2Attribute : UTF.ExpectedExceptionBaseAttribute + { + protected internal override void Verify(Exception exception) => throw new NotImplementedException(); + } + + [UTF.ExpectedException(typeof(Exception))] + [MyExpectedException1] + + public void DummyTestMethod1() + { + } + + [MyExpectedException2] + [MyExpectedException2] + public void DummyTestMethod2() + { + } + + [UTF.ExpectedException(typeof(DivideByZeroException))] + public void TestMethodWithExpectedException() + { + } + + public void TestMethodWithoutExpectedException() + { + } + } + #if NET6_0_OR_GREATER public class DummyTestClassWithAsyncDisposable : IAsyncDisposable { diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs index 7041365e7d..c11710a862 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Text; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -20,7 +15,6 @@ using AdapterTestOutcome = Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome; using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; -using UTFExtension = Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; @@ -28,7 +22,7 @@ public class TestMethodRunnerTests : TestContainer { private readonly MethodInfo _methodInfo; - private readonly UTF.TestMethodAttribute _testMethodAttribute; + private readonly TestMethodAttribute _testMethodAttribute; private readonly TestContextImplementation _testContextImplementation; @@ -43,13 +37,13 @@ public class TestMethodRunnerTests : TestContainer public TestMethodRunnerTests() { _methodInfo = typeof(DummyTestClass).GetMethods().Single(m => m.Name.Equals("DummyTestMethod", StringComparison.Ordinal)); - _testMethodAttribute = new UTF.TestMethodAttribute(); + _testMethodAttribute = new TestMethodAttribute(); _testMethod = new TestMethod("dummyTestName", "dummyClassName", "dummyAssemblyName", false); _testContextImplementation = new TestContextImplementation(_testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()); _testClassInfo = GetTestClassInfo(); - _testMethodOptions = new TestMethodOptions(TimeoutInfo.FromTimeout(200), null, _testContextImplementation, false, _testMethodAttribute); + _testMethodOptions = new TestMethodOptions(TimeoutInfo.FromTimeout(200), _testContextImplementation, false, _testMethodAttribute); // Reset test hooks DummyTestClass.TestConstructorMethodBody = () => { }; @@ -69,10 +63,9 @@ public TestMethodRunnerTests() private static TestClassInfo GetTestClassInfo() { ConstructorInfo constructorInfo = typeof(T).GetConstructor([])!; - PropertyInfo testContextProperty = typeof(T).GetProperty("TestContext"); - var classAttribute = new UTF.TestClassAttribute(); + var classAttribute = new TestClassAttribute(); var testAssemblyInfo = new TestAssemblyInfo(typeof(T).Assembly); - return new TestClassInfo(typeof(T), constructorInfo, isParameterlessConstructor: true, testContextProperty, classAttribute, testAssemblyInfo); + return new TestClassInfo(typeof(T), constructorInfo, isParameterlessConstructor: true, classAttribute, testAssemblyInfo); } protected override void Dispose(bool disposing) @@ -96,7 +89,7 @@ public void ExecuteForTestThrowingExceptionShouldReturnUnitTestResultWithFailedO public void ExecuteForPassingTestShouldReturnUnitTestResultWithPassedOutcome() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); UnitTestResult[] results = testMethodRunner.Execute(string.Empty, string.Empty, string.Empty, string.Empty); @@ -105,7 +98,7 @@ public void ExecuteForPassingTestShouldReturnUnitTestResultWithPassedOutcome() public void ExecuteShouldNotFillInDebugAndTraceLogsIfDebugTraceDisabled() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); StringWriter writer = new(new StringBuilder("DummyTrace")); @@ -125,7 +118,7 @@ public void ExecuteShouldNotFillInDebugAndTraceLogsFromRunningTestMethod() () => { writer.Write("InTestMethod"); - return new UTF.TestResult() + return new TestResult() { Outcome = UTF.UnitTestOutcome.Passed, }; @@ -150,14 +143,14 @@ public void RunTestMethodForTestThrowingExceptionShouldReturnUnitTestResultWithF public void RunTestMethodForMultipleResultsReturnMultipleResults() { - var testMethodAttributeMock = new Mock(); - testMethodAttributeMock.Setup(_ => _.Execute(It.IsAny())).Returns( + var testMethodAttributeMock = new Mock(); + testMethodAttributeMock.Setup(_ => _.Execute(It.IsAny())).Returns( [ - new UTF.TestResult { Outcome = UTF.UnitTestOutcome.Passed }, - new UTF.TestResult { Outcome = UTF.UnitTestOutcome.Failed }, + new TestResult { Outcome = UTF.UnitTestOutcome.Passed }, + new TestResult { Outcome = UTF.UnitTestOutcome.Failed }, ]); - var localTestMethodOptions = new TestMethodOptions(TimeoutInfo.FromTimeout(200), null, _testContextImplementation, false, testMethodAttributeMock.Object); + var localTestMethodOptions = new TestMethodOptions(TimeoutInfo.FromTimeout(200), _testContextImplementation, false, testMethodAttributeMock.Object); var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, localTestMethodOptions, null); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); @@ -171,7 +164,7 @@ public void RunTestMethodForMultipleResultsReturnMultipleResults() public void RunTestMethodForPassingTestThrowingExceptionShouldReturnUnitTestResultWithPassedOutcome() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); UnitTestResult[] results = testMethodRunner.Execute(string.Empty, string.Empty, string.Empty, string.Empty); @@ -180,7 +173,7 @@ public void RunTestMethodForPassingTestThrowingExceptionShouldReturnUnitTestResu public void RunTestMethodForFailingTestThrowingExceptionShouldReturnUnitTestResultWithFailedOutcome() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Failed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Failed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); UnitTestResult[] results = testMethodRunner.Execute(string.Empty, string.Empty, string.Empty, string.Empty); @@ -189,7 +182,7 @@ public void RunTestMethodForFailingTestThrowingExceptionShouldReturnUnitTestResu public void RunTestMethodShouldGiveTestResultAsPassedWhenTestMethodPasses() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); UnitTestResult[] results = testMethodRunner.RunTestMethod(); @@ -200,7 +193,7 @@ public void RunTestMethodShouldGiveTestResultAsPassedWhenTestMethodPasses() public void RunTestMethodShouldGiveTestResultAsFailedWhenTestMethodFails() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Failed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Failed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); UnitTestResult[] results = testMethodRunner.RunTestMethod(); @@ -211,10 +204,10 @@ public void RunTestMethodShouldGiveTestResultAsFailedWhenTestMethodFails() public void RunTestMethodShouldRunDataDrivenTestsWhenDataIsProvidedUsingDataSourceAttribute() { - var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); + var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new TestResult() { Outcome = UTF.UnitTestOutcome.Passed }); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); - UTF.DataSourceAttribute dataSourceAttribute = new("DummyConnectionString", "DummyTableName"); + DataSourceAttribute dataSourceAttribute = new("DummyConnectionString", "DummyTableName"); var attributes = new Attribute[] { dataSourceAttribute }; @@ -234,7 +227,7 @@ public void RunTestMethodShouldRunDataDrivenTestsWhenDataIsProvidedUsingDataSour public void RunTestMethodShouldRunDataDrivenTestsWhenDataIsProvidedUsingDataRowAttribute() { - UTF.TestResult testResult = new() + TestResult testResult = new() { Outcome = UTF.UnitTestOutcome.Inconclusive, }; @@ -244,7 +237,7 @@ public void RunTestMethodShouldRunDataDrivenTestsWhenDataIsProvidedUsingDataRowA int dummyIntData = 2; string dummyStringData = "DummyString"; - UTF.DataRowAttribute dataRowAttribute = new( + DataRowAttribute dataRowAttribute = new( dummyIntData, dummyStringData); @@ -262,7 +255,7 @@ public void RunTestMethodShouldSetDataRowIndexForDataDrivenTestsWhenDataIsProvid var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult()); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); - UTF.DataSourceAttribute dataSourceAttribute = new("DummyConnectionString", "DummyTableName"); + DataSourceAttribute dataSourceAttribute = new("DummyConnectionString", "DummyTableName"); var attributes = new Attribute[] { dataSourceAttribute }; @@ -283,10 +276,10 @@ public void RunTestMethodShouldRunOnlyDataSourceTestsWhenBothDataSourceAndDataRo var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => new UTF.TestResult()); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); - UTF.DataSourceAttribute dataSourceAttribute = new("DummyConnectionString", "DummyTableName"); + DataSourceAttribute dataSourceAttribute = new("DummyConnectionString", "DummyTableName"); int dummyIntData = 2; string dummyStringData = "DummyString"; - UTF.DataRowAttribute dataRowAttribute = new( + DataRowAttribute dataRowAttribute = new( dummyIntData, dummyStringData); @@ -306,13 +299,13 @@ public void RunTestMethodShouldRunOnlyDataSourceTestsWhenBothDataSourceAndDataRo public void RunTestMethodShouldFillInDisplayNameWithDataRowDisplayNameIfProvidedForDataDrivenTests() { - UTF.TestResult testResult = new(); + TestResult testResult = new(); var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => testResult); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); int dummyIntData = 2; string dummyStringData = "DummyString"; - UTF.DataRowAttribute dataRowAttribute = new(dummyIntData, dummyStringData) + DataRowAttribute dataRowAttribute = new(dummyIntData, dummyStringData) { DisplayName = "DataRowTestDisplayName", }; @@ -330,13 +323,13 @@ public void RunTestMethodShouldFillInDisplayNameWithDataRowDisplayNameIfProvided public void RunTestMethodShouldFillInDisplayNameWithDataRowArgumentsIfNoDisplayNameIsProvidedForDataDrivenTests() { - UTF.TestResult testResult = new(); + TestResult testResult = new(); var testMethodInfo = new TestableTestMethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, () => testResult); var testMethodRunner = new TestMethodRunner(testMethodInfo, _testMethod, _testContextImplementation); int dummyIntData = 2; string dummyStringData = "DummyString"; - UTF.DataRowAttribute dataRowAttribute = new( + DataRowAttribute dataRowAttribute = new( dummyIntData, dummyStringData); @@ -353,7 +346,7 @@ public void RunTestMethodShouldFillInDisplayNameWithDataRowArgumentsIfNoDisplayN public void RunTestMethodShouldSetResultFilesIfPresentForDataDrivenTests() { - UTF.TestResult testResult = new() + TestResult testResult = new() { ResultFiles = new List() { "C:\\temp.txt" }, }; @@ -363,8 +356,8 @@ public void RunTestMethodShouldSetResultFilesIfPresentForDataDrivenTests() int dummyIntData1 = 1; int dummyIntData2 = 2; - UTF.DataRowAttribute dataRowAttribute1 = new(dummyIntData1); - UTF.DataRowAttribute dataRowAttribute2 = new(dummyIntData2); + DataRowAttribute dataRowAttribute1 = new(dummyIntData1); + DataRowAttribute dataRowAttribute2 = new(dummyIntData2); var attributes = new Attribute[] { dataRowAttribute1, dataRowAttribute2 }; @@ -429,7 +422,7 @@ private void RunTestMethodWithEmptyDataSourceShouldFailIfConsiderEmptyDataSource #region Test data [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Use through reflection")] - private static void InitMethodThrowingException(UTFExtension.TestContext tc) + private static void InitMethodThrowingException(TestContext tc) // TODO: Fix exception type #pragma warning disable CA2208 // Instantiate argument exceptions correctly => throw new ArgumentException(); @@ -437,12 +430,12 @@ private static void InitMethodThrowingException(UTFExtension.TestContext tc) public class TestableTestMethodInfo : TestMethodInfo { - private readonly Func _invokeTest; + private readonly Func _invokeTest; internal TestableTestMethodInfo(MethodInfo testMethod, TestClassInfo parent, TestMethodOptions testMethodOptions, Func invoke) : base(testMethod, parent, testMethodOptions) => _invokeTest = invoke; - public override UTF.TestResult Invoke(object[] arguments) => + public override TestResult Invoke(object[] arguments) => // Ignore args for now _invokeTest(); } @@ -470,20 +463,20 @@ public class DummyTestClass : DummyTestClassBase public static Func DummyAsyncTestMethodBody { get; set; } - public static Action AssemblyInitializeMethodBody { get; set; } + public static Action AssemblyInitializeMethodBody { get; set; } - public static Action ClassInitializeMethodBody { get; set; } + public static Action ClassInitializeMethodBody { get; set; } - public UTFExtension.TestContext TestContext + public TestContext TestContext { get => throw new NotImplementedException(); set => TestContextSetterBody(value); } - public static void DummyAssemblyInit(UTFExtension.TestContext tc) => AssemblyInitializeMethodBody(tc); + public static void DummyAssemblyInit(TestContext tc) => AssemblyInitializeMethodBody(tc); - public static void DummyClassInit(UTFExtension.TestContext tc) => ClassInitializeMethodBody(tc); + public static void DummyClassInit(TestContext tc) => ClassInitializeMethodBody(tc); public void DummyTestInitializeMethod() => TestInitializeMethodBody(this); @@ -505,7 +498,7 @@ public DummyTestClassWithParameterizedCtor(int x) public class DummyTestClassWithTestContextWithoutSetter { - public UTFExtension.TestContext TestContext { get; } + public TestContext TestContext { get; } } public class DummyTestClassEmptyDataSource diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestPropertyAttributeTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestPropertyAttributeTests.cs new file mode 100644 index 0000000000..36e5736182 --- /dev/null +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestPropertyAttributeTests.cs @@ -0,0 +1,204 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; +using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +using TestFramework.ForTestingMSTest; + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; + +public class TestPropertyAttributeTests : TestContainer +{ + private readonly TypeCache _typeCache; + + public TestPropertyAttributeTests() + { + _typeCache = new TypeCache(new ReflectHelper()); + var testablePlatformServiceProvider = new TestablePlatformServiceProvider(); + testablePlatformServiceProvider.MockFileOperations.Setup(x => x.LoadAssembly(It.IsAny(), It.IsAny())).Returns(GetType().Assembly); + PlatformServiceProvider.Instance = testablePlatformServiceProvider; + + ReflectHelper.Instance.ClearCache(); + } + + protected override void Dispose(bool disposing) + { + if (!IsDisposed) + { + base.Dispose(disposing); + PlatformServiceProvider.Instance = null; + MSTestSettings.Reset(); + } + } + + #region GetTestMethodInfo tests + + public void GetTestMethodInfoShouldAddPropertiesFromContainingClassCorrectly() + { + string className = typeof(DummyTestClassBase).FullName; + var testMethod = new TestMethod(nameof(DummyTestClassBase.VirtualTestMethodInBaseAndDerived), className, typeof(DummyTestClassBase).Assembly.GetName().Name, isAsync: false); + + var testContext = new TestContextImplementation(testMethod, new StringWriter(), new Dictionary()); + + _ = _typeCache.GetTestMethodInfo( + testMethod, + testContext, + false); + + Assert.IsTrue(testContext.TryGetPropertyValue("TestMethodKeyFromBase", out object value1)); + Assert.AreEqual("TestMethodValueFromBase", value1); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey1", out object value2)); + Assert.AreEqual("DummyTestClassBaseValue1", value2); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey2", out object value3)); + Assert.AreEqual("DummyTestClassBaseValue2", value3); + + TestPlatform.ObjectModel.Trait[] traits = ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassBase).GetMethod(nameof(DummyTestClassBase.VirtualTestMethodInBaseAndDerived))).ToArray(); + Assert.AreEqual(3, traits.Length); + Assert.AreEqual("TestMethodKeyFromBase", traits[0].Name); + Assert.AreEqual("TestMethodValueFromBase", traits[0].Value); + Assert.AreEqual("DummyTestClassBaseKey1", traits[1].Name); + Assert.AreEqual("DummyTestClassBaseValue1", traits[1].Value); + Assert.AreEqual("DummyTestClassBaseKey2", traits[2].Name); + Assert.AreEqual("DummyTestClassBaseValue2", traits[2].Value); + } + + public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClassesAndOverriddenMethodsCorrectly_OverriddenIsTestMethod() + { + string className = typeof(DummyTestClassDerived).FullName; + var testMethod = new TestMethod(nameof(DummyTestClassDerived.VirtualTestMethodInBaseAndDerived), className, typeof(DummyTestClassBase).Assembly.GetName().Name, isAsync: false); + + var testContext = new TestContextImplementation(testMethod, new StringWriter(), new Dictionary()); + + _ = _typeCache.GetTestMethodInfo( + testMethod, + testContext, + false); + + Assert.IsTrue(testContext.TryGetPropertyValue("DerivedMethod1Key", out object value1)); + Assert.AreEqual("DerivedMethod1Value", value1); + + Assert.IsTrue(testContext.TryGetPropertyValue("TestMethodKeyFromBase", out object value2)); + Assert.AreEqual("TestMethodValueFromBase", value2); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassDerivedKey1", out object value3)); + Assert.AreEqual("DummyTestClassValue1", value3); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassDerivedKey2", out object value4)); + Assert.AreEqual("DummyTestClassValue2", value4); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey1", out object value5)); + Assert.AreEqual("DummyTestClassBaseValue1", value5); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey2", out object value6)); + Assert.AreEqual("DummyTestClassBaseValue2", value6); + + TestPlatform.ObjectModel.Trait[] traits = ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInBaseAndDerived))).ToArray(); + Assert.AreEqual(6, traits.Length); + Assert.AreEqual("DerivedMethod1Key", traits[0].Name); + Assert.AreEqual("DerivedMethod1Value", traits[0].Value); + Assert.AreEqual("TestMethodKeyFromBase", traits[1].Name); + Assert.AreEqual("TestMethodValueFromBase", traits[1].Value); + Assert.AreEqual("DummyTestClassDerivedKey1", traits[2].Name); + Assert.AreEqual("DummyTestClassValue1", traits[2].Value); + Assert.AreEqual("DummyTestClassDerivedKey2", traits[3].Name); + Assert.AreEqual("DummyTestClassValue2", traits[3].Value); + Assert.AreEqual("DummyTestClassBaseKey1", traits[4].Name); + Assert.AreEqual("DummyTestClassBaseValue1", traits[4].Value); + Assert.AreEqual("DummyTestClassBaseKey2", traits[5].Name); + Assert.AreEqual("DummyTestClassBaseValue2", traits[5].Value); + } + + public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClassesAndOverriddenMethodsCorrectly_OverriddenIsNotTestMethod() + { + string className = typeof(DummyTestClassDerived).FullName; + var testMethod = new TestMethod(nameof(DummyTestClassDerived.VirtualTestMethodInDerivedButNotTestMethodInBase), className, typeof(DummyTestClassBase).Assembly.GetName().Name, isAsync: false); + + var testContext = new TestContextImplementation(testMethod, new StringWriter(), new Dictionary()); + + _ = _typeCache.GetTestMethodInfo( + testMethod, + testContext, + false); + + Assert.IsTrue(testContext.TryGetPropertyValue("DerivedMethod2Key", out object value1)); + Assert.AreEqual("DerivedMethod2Value", value1); + + Assert.IsTrue(testContext.TryGetPropertyValue("NonTestMethodKeyFromBase", out object value2)); + Assert.AreEqual("NonTestMethodValueFromBase", value2); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassDerivedKey1", out object value3)); + Assert.AreEqual("DummyTestClassValue1", value3); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassDerivedKey2", out object value4)); + Assert.AreEqual("DummyTestClassValue2", value4); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey1", out object value5)); + Assert.AreEqual("DummyTestClassBaseValue1", value5); + + Assert.IsTrue(testContext.TryGetPropertyValue("DummyTestClassBaseKey2", out object value6)); + Assert.AreEqual("DummyTestClassBaseValue2", value6); + + TestPlatform.ObjectModel.Trait[] traits = ReflectHelper.Instance.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInDerivedButNotTestMethodInBase))).ToArray(); + Assert.AreEqual(6, traits.Length); + Assert.AreEqual("DerivedMethod2Key", traits[0].Name); + Assert.AreEqual("DerivedMethod2Value", traits[0].Value); + Assert.AreEqual("NonTestMethodKeyFromBase", traits[1].Name); + Assert.AreEqual("NonTestMethodValueFromBase", traits[1].Value); + Assert.AreEqual("DummyTestClassDerivedKey1", traits[2].Name); + Assert.AreEqual("DummyTestClassValue1", traits[2].Value); + Assert.AreEqual("DummyTestClassDerivedKey2", traits[3].Name); + Assert.AreEqual("DummyTestClassValue2", traits[3].Value); + Assert.AreEqual("DummyTestClassBaseKey1", traits[4].Name); + Assert.AreEqual("DummyTestClassBaseValue1", traits[4].Value); + Assert.AreEqual("DummyTestClassBaseKey2", traits[5].Name); + Assert.AreEqual("DummyTestClassBaseValue2", traits[5].Value); + } + + #endregion + #region dummy implementations + + [TestClass] + [TestProperty("DummyTestClassBaseKey1", "DummyTestClassBaseValue1")] + [TestProperty("DummyTestClassBaseKey2", "DummyTestClassBaseValue2")] + internal class DummyTestClassBase + { + public TestContext TestContext { get; set; } + + [TestMethod] + [TestProperty("TestMethodKeyFromBase", "TestMethodValueFromBase")] + public virtual void VirtualTestMethodInBaseAndDerived() + { + } + + [TestProperty("NonTestMethodKeyFromBase", "NonTestMethodValueFromBase")] + public virtual void VirtualTestMethodInDerivedButNotTestMethodInBase() + { + } + } + + [TestClass] + [TestProperty("DummyTestClassDerivedKey1", "DummyTestClassValue1")] + [TestProperty("DummyTestClassDerivedKey2", "DummyTestClassValue2")] + internal class DummyTestClassDerived : DummyTestClassBase + { + [TestProperty("DerivedMethod1Key", "DerivedMethod1Value")] + [TestMethod] + public override void VirtualTestMethodInBaseAndDerived() => base.VirtualTestMethodInBaseAndDerived(); + + [TestProperty("DerivedMethod2Key", "DerivedMethod2Value")] + [TestMethod] + public override void VirtualTestMethodInDerivedButNotTestMethodInBase() => base.VirtualTestMethodInDerivedButNotTestMethodInBase(); + } + + #endregion +} diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs index ffbc102efc..3e0bd1ccb3 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TypeCacheTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -236,7 +233,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInitializeAttribute() var testMethod = new TestMethod("TestInit", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); @@ -256,7 +253,7 @@ public void GetTestMethodInfoShouldCacheAssemblyCleanupAttribute() var testMethod = new TestMethod("TestCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); @@ -276,7 +273,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInitAndCleanupAttribute() var testMethod = new TestMethod("TestInitOrCleanup", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); @@ -299,7 +296,7 @@ public void GetTestMethodInfoShouldThrowIfAssemblyInitHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); @@ -329,7 +326,7 @@ public void GetTestMethodInfoShouldThrowIfAssemblyCleanupHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); @@ -360,7 +357,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInfoInstanceAndReuseTheCache() var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _typeCache.GetTestMethodInfo( testMethod, @@ -372,7 +369,7 @@ public void GetTestMethodInfoShouldCacheAssemblyInfoInstanceAndReuseTheCache() new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), false); - _mockReflectHelper.Verify(rh => rh.IsDerivedAttributeDefined(type, true), Times.Once); + _mockReflectHelper.Verify(rh => rh.IsDerivedAttributeDefined(type, false), Times.Once); Verify(_typeCache.AssemblyInfoCache.Count == 1); } @@ -477,7 +474,7 @@ public void GetTestMethodInfoShouldCacheBaseClassCleanupAttributes() var testMethod = new TestMethod("TestMethod", type.FullName, "A", false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(baseType.GetMethod("AssemblyCleanup"), false)).Returns(true); _mockReflectHelper.Setup( @@ -526,7 +523,7 @@ public void GetTestMethodInfoShouldCacheBaseClassInitAndCleanupAttributes() MethodInfo baseCleanupMethod = baseType.GetMethod("ClassCleanup"); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(baseInitializeMethod, false)).Returns(true); @@ -626,7 +623,7 @@ public void GetTestMethodInfoShouldThrowIfClassInitHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyInit"), false)).Returns(true); @@ -656,7 +653,7 @@ public void GetTestMethodInfoShouldThrowIfClassCleanupHasIncorrectSignature() var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); @@ -726,7 +723,7 @@ public void GetTestMethodInfoShouldThrowIfTestInitOrCleanupHasIncorrectSignature var testMethod = new TestMethod("M", type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("TestInit"), false)).Returns(true); @@ -1031,6 +1028,9 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForMethodsAdornedWithADer public void GetTestMethodInfoShouldSetTestContextWithCustomProperty() { + // Not using _typeCache here which uses a mocked ReflectHelper which doesn't work well with this test. + // Setting up the mock feels unnecessary when the original production implementation can work just fine. + var typeCache = new TypeCache(new ReflectHelper()); Type type = typeof(DummyTestClassWithTestMethods); MethodInfo methodInfo = type.GetMethod("TestMethodWithCustomProperty"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); @@ -1039,7 +1039,7 @@ public void GetTestMethodInfoShouldSetTestContextWithCustomProperty() new ThreadSafeStringWriter(null, "test"), new Dictionary()); - _typeCache.GetTestMethodInfo(testMethod, testContext, false); + typeCache.GetTestMethodInfo(testMethod, testContext, false); KeyValuePair customProperty = ((IDictionary)testContext.Properties).FirstOrDefault(p => p.Key.Equals("WhoAmI", StringComparison.Ordinal)); Verify((object)customProperty is not null); @@ -1048,6 +1048,9 @@ public void GetTestMethodInfoShouldSetTestContextWithCustomProperty() public void GetTestMethodInfoShouldReportWarningIfCustomPropertyHasSameNameAsPredefinedProperties() { + // Not using _typeCache here which uses a mocked ReflectHelper which doesn't work well with this test. + // Setting up the mock feels unnecessary when the original production implementation can work just fine. + var typeCache = new TypeCache(new ReflectHelper()); Type type = typeof(DummyTestClassWithTestMethods); MethodInfo methodInfo = type.GetMethod("TestMethodWithOwnerAsCustomProperty"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); @@ -1056,7 +1059,7 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyHasSameNameAsPre new ThreadSafeStringWriter(null, "test"), new Dictionary()); - TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo(testMethod, testContext, false); + TestMethodInfo testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext, false); Verify(testMethodInfo is not null); string expectedMessage = string.Format( @@ -1070,6 +1073,9 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyHasSameNameAsPre public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsEmpty() { + // Not using _typeCache here which uses a mocked ReflectHelper which doesn't work well with this test. + // Setting up the mock feels unnecessary when the original production implementation can work just fine. + var typeCache = new TypeCache(new ReflectHelper()); Type type = typeof(DummyTestClassWithTestMethods); MethodInfo methodInfo = type.GetMethod("TestMethodWithEmptyCustomPropertyName"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); @@ -1078,7 +1084,7 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsEmpty() new ThreadSafeStringWriter(null, "test"), new Dictionary()); - TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo(testMethod, testContext, false); + TestMethodInfo testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext, false); Verify(testMethodInfo is not null); string expectedMessage = string.Format( @@ -1091,6 +1097,9 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsEmpty() public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsNull() { + // Not using _typeCache here which uses a mocked ReflectHelper which doesn't work well with this test. + // Setting up the mock feels unnecessary when the original production implementation can work just fine. + var typeCache = new TypeCache(new ReflectHelper()); Type type = typeof(DummyTestClassWithTestMethods); MethodInfo methodInfo = type.GetMethod("TestMethodWithNullCustomPropertyName"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); @@ -1099,7 +1108,7 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsNull() new ThreadSafeStringWriter(null, "test"), new Dictionary()); - TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo(testMethod, testContext, false); + TestMethodInfo testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext, false); Verify(testMethodInfo is not null); string expectedMessage = string.Format( @@ -1112,6 +1121,9 @@ public void GetTestMethodInfoShouldReportWarningIfCustomPropertyNameIsNull() public void GetTestMethodInfoShouldNotAddDuplicateTestPropertiesToTestContext() { + // Not using _typeCache here which uses a mocked ReflectHelper which doesn't work well with this test. + // Setting up the mock feels unnecessary when the original production implementation can work just fine. + var typeCache = new TypeCache(new ReflectHelper()); Type type = typeof(DummyTestClassWithTestMethods); MethodInfo methodInfo = type.GetMethod("TestMethodWithDuplicateCustomPropertyNames"); var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); @@ -1120,7 +1132,7 @@ public void GetTestMethodInfoShouldNotAddDuplicateTestPropertiesToTestContext() new ThreadSafeStringWriter(null, "test"), new Dictionary()); - TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo(testMethod, testContext, false); + TestMethodInfo testMethodInfo = typeCache.GetTestMethodInfo(testMethod, testContext, false); Verify(testMethodInfo is not null); @@ -1265,7 +1277,7 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnEmptyListWhe var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(false); @@ -1287,7 +1299,7 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnAssemblyInfo var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); _mockReflectHelper.Setup( - rh => rh.IsDerivedAttributeDefined(type, true)).Returns(true); + rh => rh.IsDerivedAttributeDefined(type, false)).Returns(true); _mockReflectHelper.Setup( rh => rh.IsNonDerivedAttributeDefined(type.GetMethod("AssemblyCleanup"), false)).Returns(true); @@ -1307,44 +1319,6 @@ public void AssemblyInfoListWithExecutableCleanupMethodsShouldReturnAssemblyInfo #region ResolveExpectedExceptionHelper tests - public void ResolveExpectedExceptionHelperShouldReturnExpectedExceptionAttributeIfPresent() - { - Type type = typeof(DummyTestClassWithTestMethods); - MethodInfo methodInfo = type.GetMethod("TestMethodWithExpectedException"); - var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)); - - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) - .Returns(true); - _mockReflectHelper.Setup(rh => rh.ResolveExpectedExceptionHelper(methodInfo, testMethod)).Returns(expectedException); - - TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( - testMethod, - new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), - false); - - Verify(expectedException == testMethodInfo.TestMethodOptions.ExpectedException); - } - - public void ResolveExpectedExceptionHelperShouldReturnNullIfExpectedExceptionAttributeIsNotPresent() - { - Type type = typeof(DummyTestClassWithTestMethods); - MethodInfo methodInfo = type.GetMethod("TestMethod"); - var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false); - - _mockReflectHelper.Setup(rh => rh.IsNonDerivedAttributeDefined(methodInfo, false)) - .Returns(true); - - TestMethodInfo testMethodInfo = _typeCache.GetTestMethodInfo( - testMethod, - new TestContextImplementation(testMethod, new ThreadSafeStringWriter(null, "test"), new Dictionary()), - false); - - ExpectedExceptionAttribute expectedException = new(typeof(DivideByZeroException)); - - Verify(testMethodInfo.TestMethodOptions.ExpectedException is null); - } - public void ResolveExpectedExceptionHelperShouldThrowIfMultipleExpectedExceptionAttributesArePresent() { Type type = typeof(DummyTestClassWithTestMethods); @@ -1533,6 +1507,7 @@ private DummyTestClassWithNoDefaultConstructor(int a) } } + [DummyTestClass] private class DummyTestClassWithIncorrectTestContextType { // This is TP.TF type. @@ -1544,6 +1519,7 @@ private class DummyTestClassWithTestContextProperty : DummyTestClassWithIncorrec public new string TestContext { get; set; } } + [DummyTestClass] private class DummyTestClassWithMultipleTestContextProperties : DummyTestClassWithTestContextProperty; [DummyTestClass] diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs index 387ac1ebad..8e02319239 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestResultTest.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs index 186ecb58de..52524eabf9 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/UnitTestRunnerTests.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; -using System.Xml; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; @@ -85,12 +81,12 @@ public void ConstructorShouldPopulateSettings() #region RunSingleTest tests public void RunSingleTestShouldThrowIfTestMethodIsNull() => - VerifyThrows(() => _unitTestRunner.RunSingleTest(null, null)); + VerifyThrows(() => _unitTestRunner.RunSingleTest(null, null, null)); public void RunSingleTestShouldThrowIfTestRunParametersIsNull() { var testMethod = new TestMethod("M", "C", "A", isAsync: false); - VerifyThrows(() => _unitTestRunner.RunSingleTest(testMethod, null)); + VerifyThrows(() => _unitTestRunner.RunSingleTest(testMethod, null, null)); } public void RunSingleTestShouldReturnTestResultIndicateATestNotFoundIfTestMethodCannotBeFound() @@ -100,7 +96,7 @@ public void RunSingleTestShouldReturnTestResultIndicateATestNotFoundIfTestMethod _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -117,7 +113,7 @@ public void RunSingleTestShouldReturnTestResultIndicatingNotRunnableTestIfTestMe _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); string expectedMessage = string.Format( CultureInfo.InvariantCulture, @@ -140,7 +136,7 @@ public void ExecuteShouldSkipTestAndFillInClassIgnoreMessageIfIgnoreAttributeIsP _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -157,7 +153,7 @@ public void ExecuteShouldSkipTestAndSkipFillingIgnoreMessageIfIgnoreAttributeIsP _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -174,7 +170,7 @@ public void ExecuteShouldSkipTestAndFillInMethodIgnoreMessageIfIgnoreAttributeIs _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -191,7 +187,7 @@ public void ExecuteShouldSkipTestAndSkipFillingIgnoreMessageIfIgnoreAttributeIsP _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -208,7 +204,7 @@ public void ExecuteShouldSkipTestAndFillInClassIgnoreMessageIfIgnoreAttributeIsP _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -225,7 +221,7 @@ public void ExecuteShouldSkipTestAndFillInMethodIgnoreMessageIfIgnoreAttributeIs _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -241,7 +237,7 @@ public void RunSingleTestShouldReturnTestResultIndicatingFailureIfThereIsAnyType _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); string expectedMessage = string.Format( CultureInfo.InvariantCulture, @@ -264,7 +260,7 @@ public void RunSingleTestShouldReturnTestResultsForAPassingTestMethod() _testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.LoadAssembly("A", It.IsAny())) .Returns(Assembly.GetExecutingAssembly()); - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -282,7 +278,7 @@ public void RunSingleTestShouldSetTestsAsInProgressInTestContext() .Returns(Assembly.GetExecutingAssembly()); // Asserting in the test method execution flow itself. - UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + UnitTestResult[] results = _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(results is not null); Verify(results.Length == 1); @@ -308,7 +304,7 @@ public void RunSingleTestShouldCallAssemblyInitializeAndClassInitializeMethodsIn DummyTestClassWithInitializeMethods.AssemblyInitializeMethodBody = () => validator <<= 2; DummyTestClassWithInitializeMethods.ClassInitializeMethodBody = () => validator >>= 2; - _unitTestRunner.RunSingleTest(testMethod, _testRunParameters); + _unitTestRunner.RunSingleTest(testMethod, _testRunParameters, null); Verify(validator == 1); } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs index 79391029ec..4c37529ebf 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/ExceptionExtensionsTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs index 9a0fe37271..254f7e0e35 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/MethodInfoExtensionsTests.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs index 35d5e51709..9e2843c535 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/TestCaseExtensionsTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs index 852811883e..77c056e763 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Extensions/UnitTestOutcomeExtensionsTests.cs @@ -62,25 +62,25 @@ public void ToUnitTestResultsForUnknownTestResultsConvertsToErrorUnitTestResults public void GetMoreImportantOutcomeShouldReturnFailIfTwoOutcomesAreFailedAndInconclusive() { - UTF.UnitTestOutcome resultOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed, UTF.UnitTestOutcome.Inconclusive); + UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Failed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Inconclusive); Verify(resultOutcome == UTF.UnitTestOutcome.Failed); } public void GetMoreImportantOutcomeShouldReturnInconclusiveIfTwoOutcomesArePassedAndInconclusive() { - UTF.UnitTestOutcome resultOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(UTF.UnitTestOutcome.Passed, UTF.UnitTestOutcome.Inconclusive); + UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Passed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Inconclusive); Verify(resultOutcome == UTF.UnitTestOutcome.Inconclusive); } public void GetMoreImportantOutcomeShouldReturnFailedIfTwoOutcomesArePassedAndFailed() { - UTF.UnitTestOutcome resultOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(UTF.UnitTestOutcome.Passed, UTF.UnitTestOutcome.Failed); + UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Passed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed); Verify(resultOutcome == UTF.UnitTestOutcome.Failed); } public void GetMoreImportantOutcomeShouldReturnFailedIfBothOutcomesAreFailed() { - UTF.UnitTestOutcome resultOutcome = UnitTestOutcomeExtensions.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed, UTF.UnitTestOutcome.Failed); + UTF.UnitTestOutcome resultOutcome = UTF.UnitTestOutcome.Failed.GetMoreImportantOutcome(UTF.UnitTestOutcome.Failed); Verify(resultOutcome == UTF.UnitTestOutcome.Failed); } } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DictionaryHelperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DictionaryHelperTests.cs index 6e7db6c92e..0872333d45 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DictionaryHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/DictionaryHelperTests.cs @@ -15,7 +15,7 @@ public void ConcatenatingDictionariesReturnsEmptyDictionaryWhenBothSidesAreNullO var overwrite = new Dictionary(); - IDictionary actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + IDictionary actual = source.ConcatWithOverwrites(overwrite); var expected = new Dictionary(); actual.ToList().Sort(); @@ -33,10 +33,10 @@ public void ConcatenatingDictionariesReturnsSourceSideWhenOverwriteIsNullOrEmpty Dictionary overwrite = null; - IDictionary actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + IDictionary actual = source.ConcatWithOverwrites(overwrite); - IOrderedEnumerable> sortedActual = from entry in actual orderby entry.Key ascending select entry; - IOrderedEnumerable> sortedSource = from entry in source orderby entry.Key ascending select entry; + IOrderedEnumerable> sortedActual = from entry in actual orderby entry.Key select entry; + IOrderedEnumerable> sortedSource = from entry in source orderby entry.Key select entry; Verify(sortedActual.SequenceEqual(sortedSource)); } @@ -50,10 +50,10 @@ public void ConcatenatingDictionariesReturnsOverwriteSideWhenSourceIsNullOrEmpty ["bbb"] = "overwrite", }; - IDictionary actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + IDictionary actual = source.ConcatWithOverwrites(overwrite); - IOrderedEnumerable> sortedActual = from entry in actual orderby entry.Key ascending select entry; - IOrderedEnumerable> sortedOverwrite = from entry in overwrite orderby entry.Key ascending select entry; + IOrderedEnumerable> sortedActual = from entry in actual orderby entry.Key select entry; + IOrderedEnumerable> sortedOverwrite = from entry in overwrite orderby entry.Key select entry; Verify(sortedActual.SequenceEqual(sortedOverwrite)); } @@ -71,7 +71,7 @@ public void ConcatenatingDictionariesShouldMergeThemAndTakeDuplicateKeysFromOver ["ccc"] = "overwrite", }; - IDictionary actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + IDictionary actual = source.ConcatWithOverwrites(overwrite); var expected = new Dictionary { // this is only present in source, take it @@ -84,8 +84,8 @@ public void ConcatenatingDictionariesShouldMergeThemAndTakeDuplicateKeysFromOver ["bbb"] = "overwrite", }; - IOrderedEnumerable> sortedActual = from entry in actual orderby entry.Key ascending select entry; - IOrderedEnumerable> sortedExpected = from entry in expected orderby entry.Key ascending select entry; + IOrderedEnumerable> sortedActual = from entry in actual orderby entry.Key select entry; + IOrderedEnumerable> sortedExpected = from entry in expected orderby entry.Key select entry; Verify(sortedActual.SequenceEqual(sortedExpected)); } } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs index 37d3610eac..54a001d1e8 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/ReflectHelperTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs index 7be7fa6d9a..6c4ff49376 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs @@ -19,7 +19,7 @@ public class UnitTestOutcomeHelperTests : TestContainer public UnitTestOutcomeHelperTests() { - string runSettingxml = + string runSettingsXml = """ @@ -27,7 +27,7 @@ public UnitTestOutcomeHelperTests() """; var mockMessageLogger = new Mock(); - _adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, mockMessageLogger.Object); + _adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, mockMessageLogger.Object); } public void UniTestHelperToTestOutcomeForUnitTestOutcomePassedShouldReturnTestOutcomePassed() diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj b/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj index 46dc781e59..9bf62ef237 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestAdapter.UnitTests.csproj @@ -22,11 +22,20 @@ + + Analyzer + false + + + + + + diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs index de5c7be8bc..70a1335c79 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestDiscovererTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs index 4407e86d75..68628e0119 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestExecutorTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; @@ -43,7 +41,7 @@ public void RunTestsShouldNotExecuteTestsIfTestSettingsIsGiven() { var testCase = new TestCase("DummyName", new Uri("executor://MSTestAdapter/v2"), Assembly.GetExecutingAssembly().Location); TestCase[] tests = [testCase]; - string runSettingxml = + string runSettingsXml = """ @@ -54,7 +52,7 @@ public void RunTestsShouldNotExecuteTestsIfTestSettingsIsGiven() """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); _mstestExecutor.RunTests(tests, _mockRunContext.Object, _mockFrameworkHandle.Object); // Test should not start if TestSettings is given. @@ -65,7 +63,7 @@ public void RunTestsShouldReportErrorAndBailOutOnSettingsException() { var testCase = new TestCase("DummyName", new Uri("executor://MSTestAdapter/v2"), Assembly.GetExecutingAssembly().Location); TestCase[] tests = [testCase]; - string runSettingxml = + string runSettingsXml = """ @@ -76,7 +74,7 @@ public void RunTestsShouldReportErrorAndBailOutOnSettingsException() """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); // Act. _mstestExecutor.RunTests(tests, _mockRunContext.Object, _mockFrameworkHandle.Object); @@ -89,7 +87,7 @@ public void RunTestsShouldReportErrorAndBailOutOnSettingsException() public void RunTestsWithSourcesShouldNotExecuteTestsIfTestSettingsIsGiven() { var sources = new List { Assembly.GetExecutingAssembly().Location }; - string runSettingxml = + string runSettingsXml = """ @@ -100,7 +98,7 @@ public void RunTestsWithSourcesShouldNotExecuteTestsIfTestSettingsIsGiven() """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); // Test should not start if TestSettings is given. @@ -110,7 +108,7 @@ public void RunTestsWithSourcesShouldNotExecuteTestsIfTestSettingsIsGiven() public void RunTestsWithSourcesShouldReportErrorAndBailOutOnSettingsException() { var sources = new List { Assembly.GetExecutingAssembly().Location }; - string runSettingxml = + string runSettingsXml = """ @@ -121,7 +119,7 @@ public void RunTestsWithSourcesShouldReportErrorAndBailOutOnSettingsException() """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); // Act. _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); @@ -134,13 +132,13 @@ public void RunTestsWithSourcesShouldReportErrorAndBailOutOnSettingsException() public void RunTestsWithSourcesShouldSetDefaultCollectSourceInformationAsTrue() { var sources = new List { Assembly.GetExecutingAssembly().Location }; - string runSettingxml = + string runSettingsXml = """ """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); Verify(MSTestSettings.RunConfigurationSettings.CollectSourceInformation); @@ -149,7 +147,7 @@ public void RunTestsWithSourcesShouldSetDefaultCollectSourceInformationAsTrue() public void RunTestsWithSourcesShouldSetCollectSourceInformationAsFalseIfSpecifiedInRunSettings() { var sources = new List { Assembly.GetExecutingAssembly().Location }; - string runSettingxml = + string runSettingsXml = """ @@ -158,7 +156,7 @@ public void RunTestsWithSourcesShouldSetCollectSourceInformationAsFalseIfSpecifi """; _mockRunContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); _mstestExecutor.RunTests(sources, _mockRunContext.Object, _mockFrameworkHandle.Object); Verify(!MSTestSettings.RunConfigurationSettings.CollectSourceInformation); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs index 7e0eab35dc..09b54289d7 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/MSTestSettingsTests.cs @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml; - -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -15,8 +13,6 @@ using TestFramework.ForTestingMSTest; -using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; - namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests; public class MSTestSettingsTests : TestContainer @@ -48,7 +44,7 @@ protected override void Dispose(bool disposing) public void MapInconclusiveToFailedIsByDefaultFalseWhenNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -56,14 +52,14 @@ public void MapInconclusiveToFailedIsByDefaultFalseWhenNotSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(!adapterSettings.MapInconclusiveToFailed); } public void MapNotRunnableToFailedIsByDefaultTrueWhenNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -71,14 +67,14 @@ public void MapNotRunnableToFailedIsByDefaultTrueWhenNotSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.MapNotRunnableToFailed); } public void MapInconclusiveToFailedShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -87,14 +83,14 @@ public void MapInconclusiveToFailedShouldBeConsumedFromRunSettingsWhenSpecified( """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.MapInconclusiveToFailed); } public void RunSettings_WithInvalidValues_GettingAWarningForEachInvalidSetting() { - string runSettingxml = + string runSettingsXml = """ @@ -120,7 +116,7 @@ public void RunSettings_WithInvalidValues_GettingAWarningForEachInvalidSetting() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); _mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.Exactly(18)); _mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '3' for runsettings entry 'CooperativeCancellationTimeout', setting will be ignored."), Times.Once); @@ -145,7 +141,7 @@ public void RunSettings_WithInvalidValues_GettingAWarningForEachInvalidSetting() public void MapNotRunnableToFailedShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -154,14 +150,14 @@ public void MapNotRunnableToFailedShouldBeConsumedFromRunSettingsWhenSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.MapNotRunnableToFailed); } public void ForcedLegacyModeIsByDefaultFalseWhenNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -169,14 +165,14 @@ public void ForcedLegacyModeIsByDefaultFalseWhenNotSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); Verify(!adapterSettings.ForcedLegacyMode); } public void ForcedLegacyModeShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -185,14 +181,14 @@ public void ForcedLegacyModeShouldBeConsumedFromRunSettingsWhenSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); Verify(adapterSettings.ForcedLegacyMode); } public void TestSettingsFileIsByDefaultNullWhenNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -200,14 +196,14 @@ public void TestSettingsFileIsByDefaultNullWhenNotSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); Verify(adapterSettings.TestSettingsFile is null); } public void TestSettingsFileShouldNotBeNullWhenSpecifiedInRunSettings() { - string runSettingxml = + string runSettingsXml = """ @@ -216,14 +212,14 @@ public void TestSettingsFileShouldNotBeNullWhenSpecifiedInRunSettings() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); Verify(adapterSettings.TestSettingsFile is not null); } public void EnableBaseClassTestMethodsFromOtherAssembliesIsByDefaulTrueWhenNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -231,14 +227,14 @@ public void EnableBaseClassTestMethodsFromOtherAssembliesIsByDefaulTrueWhenNotSp """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.EnableBaseClassTestMethodsFromOtherAssemblies); } public void EnableBaseClassTestMethodsFromOtherAssembliesShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -247,14 +243,14 @@ public void EnableBaseClassTestMethodsFromOtherAssembliesShouldBeConsumedFromRun """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.EnableBaseClassTestMethodsFromOtherAssemblies); } public void CaptureDebugTracesShouldBeTrueByDefault() { - string runSettingxml = + string runSettingsXml = """ @@ -262,14 +258,14 @@ public void CaptureDebugTracesShouldBeTrueByDefault() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.CaptureDebugTraces); } public void CaptureDebugTracesShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -278,14 +274,14 @@ public void CaptureDebugTracesShouldBeConsumedFromRunSettingsWhenSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(!adapterSettings.CaptureDebugTraces); } public void TestTimeoutShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -294,14 +290,14 @@ public void TestTimeoutShouldBeConsumedFromRunSettingsWhenSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TestTimeout == 4000); } public void TestTimeoutShouldBeSetToZeroIfNotSpecifiedInRunSettings() { - string runSettingxml = + string runSettingsXml = """ @@ -309,14 +305,14 @@ public void TestTimeoutShouldBeSetToZeroIfNotSpecifiedInRunSettings() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TestTimeout == 0); } public void TreatClassCleanupWarningsAsErrorsShouldBeFalseByDefault() { - string runSettingxml = + string runSettingsXml = """ @@ -324,14 +320,14 @@ public void TreatClassCleanupWarningsAsErrorsShouldBeFalseByDefault() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(!adapterSettings.TreatClassAndAssemblyCleanupWarningsAsErrors); } public void TreatClassCleanupWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -340,14 +336,14 @@ public void TreatClassCleanupWarningsAsErrorsShouldBeConsumedFromRunSettingsWhen """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TreatClassAndAssemblyCleanupWarningsAsErrors); } public void TreatDiscoveryWarningsAsErrorsShouldBeFalseByDefault() { - string runSettingxml = + string runSettingsXml = """ @@ -355,14 +351,14 @@ public void TreatDiscoveryWarningsAsErrorsShouldBeFalseByDefault() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(!adapterSettings.TreatDiscoveryWarningsAsErrors); } public void TreatDiscoveryWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -371,14 +367,14 @@ public void TreatDiscoveryWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpe """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TreatDiscoveryWarningsAsErrors); } public void ParallelizationSettingsShouldNotBeSetByDefault() { - string runSettingxml = + string runSettingsXml = """ @@ -386,7 +382,7 @@ public void ParallelizationSettingsShouldNotBeSetByDefault() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(!adapterSettings.ParallelizationWorkers.HasValue); Verify(!adapterSettings.ParallelizationScope.HasValue); @@ -394,7 +390,7 @@ public void ParallelizationSettingsShouldNotBeSetByDefault() public void GetSettingsShouldThrowIfParallelizationWorkersIsNotInt() { - string runSettingxml = + string runSettingsXml = """ @@ -405,14 +401,14 @@ public void GetSettingsShouldThrowIfParallelizationWorkersIsNotInt() """; - AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); + AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); Verify(exception.Message.Contains("Invalid value 'GoneFishing' specified for 'Workers'. The value should be a non-negative integer.")); } public void GetSettingsShouldThrowIfParallelizationWorkersIsNegative() { - string runSettingxml = + string runSettingsXml = """ @@ -423,13 +419,13 @@ public void GetSettingsShouldThrowIfParallelizationWorkersIsNegative() """; - AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); + AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); Verify(exception.Message.Contains("Invalid value '-1' specified for 'Workers'. The value should be a non-negative integer.")); } public void ParallelizationWorkersShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -440,14 +436,14 @@ public void ParallelizationWorkersShouldBeConsumedFromRunSettingsWhenSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.ParallelizationWorkers == 2); } public void ParallelizationWorkersShouldBeSetToProcessorCountWhenSetToZero() { - string runSettingxml = + string runSettingsXml = """ @@ -458,14 +454,14 @@ public void ParallelizationWorkersShouldBeSetToProcessorCountWhenSetToZero() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(Environment.ProcessorCount == adapterSettings.ParallelizationWorkers); } public void ParallelizationSettingsShouldBeSetToDefaultsWhenNotSet() { - string runSettingxml = + string runSettingsXml = """ @@ -475,15 +471,15 @@ public void ParallelizationSettingsShouldBeSetToDefaultsWhenNotSet() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(Environment.ProcessorCount == adapterSettings.ParallelizationWorkers); - Verify(adapterSettings.ParallelizationScope == UTF.ExecutionScope.ClassLevel); + Verify(adapterSettings.ParallelizationScope == ExecutionScope.ClassLevel); } public void ParallelizationSettingsShouldBeSetToDefaultsOnAnEmptyParalleizeSetting() { - string runSettingxml = + string runSettingsXml = """ @@ -492,15 +488,15 @@ public void ParallelizationSettingsShouldBeSetToDefaultsOnAnEmptyParalleizeSetti """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(Environment.ProcessorCount == adapterSettings.ParallelizationWorkers); - Verify(adapterSettings.ParallelizationScope == UTF.ExecutionScope.ClassLevel); + Verify(adapterSettings.ParallelizationScope == ExecutionScope.ClassLevel); } public void ParallelizationSettingsShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -512,15 +508,15 @@ public void ParallelizationSettingsShouldBeConsumedFromRunSettingsWhenSpecified( """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.ParallelizationWorkers == 127); - Verify(adapterSettings.ParallelizationScope == UTF.ExecutionScope.MethodLevel); + Verify(adapterSettings.ParallelizationScope == ExecutionScope.MethodLevel); } public void GetSettingsShouldThrowIfParallelizationScopeIsNotValid() { - string runSettingxml = + string runSettingsXml = """ @@ -531,13 +527,13 @@ public void GetSettingsShouldThrowIfParallelizationScopeIsNotValid() """; - AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); + AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); Verify(exception.Message.Contains("Invalid value 'JustParallelizeWillYou' specified for 'Scope'. Supported scopes are ClassLevel, MethodLevel.")); } public void ParallelizationScopeShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -548,14 +544,14 @@ public void ParallelizationScopeShouldBeConsumedFromRunSettingsWhenSpecified() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); - Verify(adapterSettings.ParallelizationScope == UTF.ExecutionScope.MethodLevel); + Verify(adapterSettings.ParallelizationScope == ExecutionScope.MethodLevel); } public void GetSettingsShouldThrowWhenParallelizeHasInvalidElements() { - string runSettingxml = + string runSettingsXml = """ @@ -566,13 +562,13 @@ public void GetSettingsShouldThrowWhenParallelizeHasInvalidElements() """; - AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); + AdapterSettingsException exception = VerifyThrows(() => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)); Verify(exception.Message.Contains("Invalid settings 'Parallelize'. Unexpected XmlElement: 'Hola'.")); } public void GetSettingsShouldBeAbleToReadAfterParallelizationSettings() { - string runSettingxml = + string runSettingsXml = """ @@ -583,14 +579,14 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettings() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TestSettingsFile is not null); } public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsWithData() { - string runSettingxml = + string runSettingsXml = """ @@ -603,16 +599,16 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsWithData() """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TestSettingsFile is not null); Verify(adapterSettings.ParallelizationWorkers == 127); - Verify(adapterSettings.ParallelizationScope == UTF.ExecutionScope.MethodLevel); + Verify(adapterSettings.ParallelizationScope == ExecutionScope.MethodLevel); } public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsOnEmptyParallelizationNode() { - string runSettingxml = + string runSettingsXml = """ @@ -622,21 +618,21 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsOnEmptyPara """; - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object); Verify(adapterSettings.TestSettingsFile is not null); } public void DisableParallelizationShouldBeFalseByDefault() { - string runSettingxml = + string runSettingsXml = """ """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); Verify(!MSTestSettings.CurrentSettings.DisableParallelization); @@ -645,7 +641,7 @@ public void DisableParallelizationShouldBeFalseByDefault() public void DisableParallelizationShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -655,7 +651,7 @@ public void DisableParallelizationShouldBeConsumedFromRunSettingsWhenSpecified() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); Verify(MSTestSettings.CurrentSettings.DisableParallelization); @@ -663,7 +659,7 @@ public void DisableParallelizationShouldBeConsumedFromRunSettingsWhenSpecified() public void DisableParallelization_WithInvalidValue_GettingAWarning() { - string runSettingxml = + string runSettingsXml = """ @@ -673,7 +669,7 @@ public void DisableParallelization_WithInvalidValue_GettingAWarning() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); _mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '3' for runsettings entry 'DisableParallelization', setting will be ignored."), Times.Once); } @@ -684,7 +680,7 @@ public void DisableParallelization_WithInvalidValue_GettingAWarning() public void GetSettingsShouldProbePlatformSpecificSettingsAlso() { - string runSettingxml = + string runSettingsXml = """ @@ -703,13 +699,13 @@ public void GetSettingsShouldProbePlatformSpecificSettingsAlso() } }); - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); _testablePlatformServiceProvider.MockSettingsProvider.Verify(sp => sp.Load(It.IsAny()), Times.Once); } public void GetSettingsShouldOnlyPassTheElementSubTreeToPlatformService() { - string runSettingxml = + string runSettingsXml = """ @@ -732,13 +728,13 @@ public void GetSettingsShouldOnlyPassTheElementSubTreeToPlatformService() } }); - MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); Verify(expectedrunSettingxml == observedxml); } public void GetSettingsShouldBeAbleToReadSettingsAfterThePlatformServiceReadsItsSettings() { - string runSettingxml = + string runSettingsXml = """ @@ -782,7 +778,7 @@ public void GetSettingsShouldBeAbleToReadSettingsAfterThePlatformServiceReadsIts } }); - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); // Assert. Verify(dummyPlatformSpecificSetting); @@ -793,7 +789,7 @@ public void GetSettingsShouldBeAbleToReadSettingsAfterThePlatformServiceReadsIts public void GetSettingsShouldBeAbleToReadSettingsIfThePlatformServiceDoesNotUnderstandASetting() { - string runSettingxml = + string runSettingsXml = """ @@ -840,7 +836,7 @@ public void GetSettingsShouldBeAbleToReadSettingsIfThePlatformServiceDoesNotUnde } }); - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); // Assert. Verify(dummyPlatformSpecificSetting); @@ -853,7 +849,7 @@ public void GetSettingsShouldBeAbleToReadSettingsIfThePlatformServiceDoesNotUnde public void GetSettingsShouldOnlyReadTheAdapterSection() { - string runSettingxml = + string runSettingsXml = """ @@ -894,7 +890,7 @@ public void GetSettingsShouldOnlyReadTheAdapterSection() } }); - MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); // Assert. Verify(!outOfScopeCall); @@ -902,7 +898,7 @@ public void GetSettingsShouldOnlyReadTheAdapterSection() public void GetSettingsShouldWorkIfThereAreCommentsInTheXML() { - string runSettingxml = + string runSettingsXml = """ @@ -952,7 +948,7 @@ public void GetSettingsShouldWorkIfThereAreCommentsInTheXML() } }); - var adapterSettings = MSTestSettings.GetSettings(runSettingxml, MSTestSettings.SettingsName, _mockMessageLogger.Object); + var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object); // Assert. Verify(dummyPlatformSpecificSetting); @@ -979,7 +975,7 @@ public void CurrentSettingShouldReturnDefaultSettingsIfNotSet() public void CurrentSettingShouldReturnCachedLoadedSettings() { - string runSettingxml = + string runSettingsXml = """ @@ -989,7 +985,7 @@ public void CurrentSettingShouldReturnCachedLoadedSettings() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); MSTestSettings adapterSettings = MSTestSettings.CurrentSettings; @@ -1074,7 +1070,7 @@ public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsXmlIsE public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -1084,7 +1080,7 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); MSTestSettings adapterSettings = MSTestSettings.CurrentSettings; @@ -1097,7 +1093,7 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() public void PopulateSettingsShouldInitializeSettingsFromMSTestSection() { - string runSettingxml = + string runSettingsXml = """ @@ -1111,7 +1107,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestSection() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); MSTestSettings adapterSettings = MSTestSettings.CurrentSettings; @@ -1127,7 +1123,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestSection() public void PopulateSettingsShouldInitializeSettingsFromMSTestV2Section() { - string runSettingxml = + string runSettingsXml = """ @@ -1141,7 +1137,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestV2Section() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); MSTestSettings adapterSettings = MSTestSettings.CurrentSettings; @@ -1157,7 +1153,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestV2Section() public void PopulateSettingsShouldInitializeSettingsFromMSTestV2OverMSTestV1Section() { - string runSettingxml = + string runSettingsXml = """ @@ -1174,7 +1170,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestV2OverMSTestV1Sect """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); MSTestSettings adapterSettings = MSTestSettings.CurrentSettings; @@ -1201,7 +1197,7 @@ public void IsLegacyScenarioReturnsFalseWhenDiscoveryContextIsNull() public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToFalse() { - string runSettingxml = + string runSettingsXml = """ @@ -1211,14 +1207,14 @@ public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToFalse() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); Verify(!MSTestSettings.IsLegacyScenario(_mockMessageLogger.Object)); } public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToTrue() { - string runSettingxml = + string runSettingsXml = """ @@ -1227,14 +1223,14 @@ public void IsLegacyScenarioReturnsFalseWhenForcedLegacyModeIsSetToTrue() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); Verify(!MSTestSettings.IsLegacyScenario(_mockMessageLogger.Object)); } public void IsLegacyScenarioReturnsTrueWhenTestSettingsFileIsGiven() { - string runSettingxml = + string runSettingsXml = """ @@ -1243,14 +1239,14 @@ public void IsLegacyScenarioReturnsTrueWhenTestSettingsFileIsGiven() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); Verify(MSTestSettings.IsLegacyScenario(_mockMessageLogger.Object)); } public void LegacyScenariosNotSupportedWarningIsPrintedWhenVsmdiFileIsGiven() { - string runSettingxml = + string runSettingsXml = """ @@ -1259,7 +1255,7 @@ public void LegacyScenariosNotSupportedWarningIsPrintedWhenVsmdiFileIsGiven() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); Verify(MSTestSettings.IsLegacyScenario(_mockMessageLogger.Object)); _mockMessageLogger.Verify(logger => logger.SendMessage(TestMessageLevel.Warning, Resource.LegacyScenariosNotSupportedWarning), Times.Once); @@ -1374,5 +1370,25 @@ public void ConfigJson_WithValidValues_ValuesAreSetCorrectly() Verify(settings.ParallelizationWorkers == 4); Verify(settings.ParallelizationScope == ExecutionScope.ClassLevel); } + + [TestMethod] + public void ConfigJson_WithValidValues_MethodScope() + { + // Arrange - setting up valid configuration values + var configDictionary = new Dictionary { { "mstest:parallelism:scope", "method" } }; + + var mockConfig = new Mock(); + mockConfig.Setup(config => config[It.IsAny()]) + .Returns((string key) => configDictionary.TryGetValue(key, out string value) ? value : null); + + var settings = new MSTestSettings(); + + // Act + MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings); + + // Assert + Verify(settings.ParallelizationScope == ExecutionScope.MethodLevel); + } + #endregion } diff --git a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs index 7f80016c8e..2261c73862 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/ObjectModel/UnitTestResultTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs index 4aa2b30fce..9f85231bbf 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/PlatformServiceProviderTests.cs @@ -64,7 +64,7 @@ public void GetTestContextShouldReturnAValidTestContext() testMethod.Setup(tm => tm.Name).Returns("M"); // Act. - PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod.Object, writer, properties); + PlatformServices.Interface.ITestContext testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod.Object, writer, properties, null, default); // Assert. Verify(testContext.Context.FullyQualifiedTestClassName == "A.C.M"); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs index 355ee93e9d..25ef9f3978 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/RunConfigurationSettingsTests.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Platform.Configurations; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; +using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; @@ -42,7 +42,7 @@ protected override void Dispose(bool disposing) public void CollectSourceInformationIsByDefaultTrueWhenNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -50,13 +50,13 @@ public void CollectSourceInformationIsByDefaultTrueWhenNotSpecified() """; - var configurationSettings = RunConfigurationSettings.GetSettings(runSettingxml, RunConfigurationSettings.SettingsName); + var configurationSettings = RunConfigurationSettings.GetSettings(runSettingsXml, RunConfigurationSettings.SettingsName); Verify(configurationSettings.CollectSourceInformation); } public void CollectSourceInformationShouldBeConsumedFromRunSettingsWhenSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -67,7 +67,7 @@ public void CollectSourceInformationShouldBeConsumedFromRunSettingsWhenSpecified """; - var configurationSettings = RunConfigurationSettings.GetSettings(runSettingxml, RunConfigurationSettings.SettingsName); + var configurationSettings = RunConfigurationSettings.GetSettings(runSettingsXml, RunConfigurationSettings.SettingsName); Verify(!configurationSettings.CollectSourceInformation); } @@ -117,7 +117,7 @@ public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsXmlIsE public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() { - string runSettingxml = + string runSettingsXml = """ @@ -127,7 +127,7 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings; @@ -139,7 +139,7 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified() public void PopulateSettingsShouldInitializeSettingsFromRunConfigurationSection() { - string runSettingxml = + string runSettingsXml = """ @@ -150,7 +150,7 @@ public void PopulateSettingsShouldInitializeSettingsFromRunConfigurationSection( """; _mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object); - _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + _mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml); MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null); RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings; diff --git a/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs b/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs index afec0277f4..5f9bec282d 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Moq; +using UTF = Microsoft.VisualStudio.TestTools.UnitTesting; + namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; internal class TestablePlatformServiceProvider : IPlatformServiceProvider @@ -26,7 +27,7 @@ public TestablePlatformServiceProvider() MockTraceListener = new Mock(); MockTraceListenerManager = new Mock(); MockThreadOperations = new Mock(); - TestTools.UnitTesting.DynamicDataProvider.Instance = SourceGeneratorToggle.UseSourceGenerator + UTF.DynamicDataProvider.Instance = SourceGeneratorToggle.UseSourceGenerator ? new SourceGeneratedDynamicDataOperations() : new DynamicDataOperations(); } @@ -125,7 +126,14 @@ public IReflectionOperations2 ReflectionOperations public TestRunCancellationToken TestRunCancellationToken { get; set; } - public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties) => new TestContextImplementation(testMethod, writer, properties); + public bool IsGracefulStopRequested { get; set; } + + public ITestContext GetTestContext(ITestMethod testMethod, StringWriter writer, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome) + { + var testContextImpl = new TestContextImplementation(testMethod, writer, properties, messageLogger); + testContextImpl.SetOutcome(outcome); + return testContextImpl; + } public ITestSourceHost CreateTestSourceHost(string source, TestPlatform.ObjectModel.Adapter.IRunSettings runSettings, TestPlatform.ObjectModel.Adapter.IFrameworkHandle frameworkHandle) => MockTestSourceHost.Object; diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs index 2d025fb0ae..41643a7bf0 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppInsightsProviderTests.cs @@ -12,14 +12,10 @@ namespace Microsoft.Testing.Extensions.UnitTests; -[TestGroup] -public sealed class AppInsightsProviderTests : TestBase +[TestClass] +public sealed class AppInsightsProviderTests { - public AppInsightsProviderTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void Platform_CancellationToken_Cancellation_Should_Exit_Gracefully() { Mock environment = new(); @@ -84,9 +80,10 @@ public void Platform_CancellationToken_Cancellation_Should_Exit_Gracefully() #endif // We expect to not consume the second event because we exit the inner loop for the cancellation token - Assert.IsTrue(events.Single() == "Sample"); + Assert.AreEqual("Sample", events.Single()); } + [TestMethod] public void Timeout_During_Dispose_Should_Exit_Gracefully() { Mock environment = new(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs index a858eccdf2..4f2b1640f2 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/CrashDumpTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics; using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Extensions.UnitTests.Helpers; @@ -10,13 +8,14 @@ namespace Microsoft.Testing.Extensions.UnitTests; -[TestGroup] -public class CrashDumpTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class CrashDumpTests { - [Arguments("Mini")] - [Arguments("Heap")] - [Arguments("Triage")] - [Arguments("Full")] + [TestMethod] + [DataRow("Mini")] + [DataRow("Heap")] + [DataRow("Triage")] + [DataRow("Full")] public async Task IsValid_If_CrashDumpType_Has_CorrectValue(string crashDumpType) { var provider = new CrashDumpCommandLineProvider(); @@ -27,6 +26,7 @@ public async Task IsValid_If_CrashDumpType_Has_CorrectValue(string crashDumpType Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } + [TestMethod] public async Task IsInvValid_If_CrashDumpType_Has_IncorrectValue() { var provider = new CrashDumpCommandLineProvider(); @@ -37,6 +37,7 @@ public async Task IsInvValid_If_CrashDumpType_Has_IncorrectValue() Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, CrashDumpResources.CrashDumpTypeOptionInvalidType, "invalid"), validateOptionsResult.ErrorMessage); } + [TestMethod] public async Task CrashDump_CommandLineOptions_Are_AlwaysValid() { var provider = new CrashDumpCommandLineProvider(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs index c3c239e37f..461e4a861d 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HangDumpTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.Diagnostics; using Microsoft.Testing.Extensions.Diagnostics.Resources; using Microsoft.Testing.Extensions.UnitTests.Helpers; @@ -14,8 +12,8 @@ namespace Microsoft.Testing.Extensions.UnitTests; -[TestGroup] -public class HangDumpTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class HangDumpTests { private HangDumpCommandLineProvider GetProvider() { @@ -27,6 +25,7 @@ private HangDumpCommandLineProvider GetProvider() "suffix")); } + [TestMethod] public async Task IsValid_If_Timeout_Value_Has_CorrectValue() { HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); @@ -37,6 +36,7 @@ public async Task IsValid_If_Timeout_Value_Has_CorrectValue() Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } + [TestMethod] public async Task IsInvalid_If_Timeout_Value_Has_IncorrectValue() { HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); @@ -47,12 +47,13 @@ public async Task IsInvalid_If_Timeout_Value_Has_IncorrectValue() Assert.AreEqual(ExtensionResources.HangDumpTimeoutOptionInvalidArgument, validateOptionsResult.ErrorMessage); } + [TestMethod] #if NETCOREAPP - [Arguments("Triage")] + [DataRow("Triage")] #endif - [Arguments("Mini")] - [Arguments("Heap")] - [Arguments("Full")] + [DataRow("Mini")] + [DataRow("Heap")] + [DataRow("Full")] public async Task IsValid_If_HangDumpType_Has_CorrectValue(string dumpType) { HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); @@ -63,6 +64,7 @@ public async Task IsValid_If_HangDumpType_Has_CorrectValue(string dumpType) Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } + [TestMethod] public async Task IsInvalid_If_HangDumpType_Has_IncorrectValue() { HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); @@ -73,9 +75,10 @@ public async Task IsInvalid_If_HangDumpType_Has_IncorrectValue() Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, ExtensionResources.HangDumpTypeOptionInvalidType, "invalid"), validateOptionsResult.ErrorMessage); } - [Arguments(HangDumpCommandLineProvider.HangDumpFileNameOptionName)] - [Arguments(HangDumpCommandLineProvider.HangDumpTimeoutOptionName)] - [Arguments(HangDumpCommandLineProvider.HangDumpTypeOptionName)] + [TestMethod] + [DataRow(HangDumpCommandLineProvider.HangDumpFileNameOptionName)] + [DataRow(HangDumpCommandLineProvider.HangDumpTimeoutOptionName)] + [DataRow(HangDumpCommandLineProvider.HangDumpTypeOptionName)] public async Task Missing_HangDumpMainOption_ShouldReturn_IsInvalid(string hangDumpArgument) { HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); @@ -86,12 +89,13 @@ public async Task Missing_HangDumpMainOption_ShouldReturn_IsInvalid(string hangD ValidationResult validateOptionsResult = await hangDumpCommandLineProvider.ValidateCommandLineOptionsAsync(new TestCommandLineOptions(options)); Assert.IsFalse(validateOptionsResult.IsValid); - Assert.AreEqual(validateOptionsResult.ErrorMessage, "You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line"); + Assert.AreEqual("You specified one or more hang dump parameters but did not enable it, add --hangdump to the command line", validateOptionsResult.ErrorMessage); } - [Arguments(HangDumpCommandLineProvider.HangDumpFileNameOptionName)] - [Arguments(HangDumpCommandLineProvider.HangDumpTimeoutOptionName)] - [Arguments(HangDumpCommandLineProvider.HangDumpTypeOptionName)] + [TestMethod] + [DataRow(HangDumpCommandLineProvider.HangDumpFileNameOptionName)] + [DataRow(HangDumpCommandLineProvider.HangDumpTimeoutOptionName)] + [DataRow(HangDumpCommandLineProvider.HangDumpTypeOptionName)] public async Task If_HangDumpMainOption_IsSpecified_ShouldReturn_IsValid(string hangDumpArgument) { HangDumpCommandLineProvider hangDumpCommandLineProvider = GetProvider(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs index d999659995..00fabfa6d4 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Helpers/TestCommandLineOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.CommandLine; namespace Microsoft.Testing.Extensions.UnitTests.Helpers; 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 1acf72929b..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 @@ -3,8 +3,7 @@ $(MicrosoftTestingTargetFrameworks);net462 false - Exe - true + true @@ -14,6 +13,9 @@ TargetFramework=netstandard2.0 + + TargetFramework=netstandard2.0 + diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs index 637390b157..c731857d74 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Program.cs @@ -3,8 +3,14 @@ using Microsoft.Testing.Extensions; +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] + ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new Microsoft.Testing.Extensions.UnitTests.SourceGeneratedTestNodesBuilder()); + +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json index ebf3f0f011..7195225907 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Microsoft.Testing.Extensions.UnitTests": { "commandName": "Project", - "commandLineArgs": "--treenode-filter /*/*/*/**" + "commandLineArgs": "" } } } 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..53f3b8f3d3 --- /dev/null +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/RetryTests.cs @@ -0,0 +1,113 @@ +#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 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)); + } +} diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs index ce0d3c10ca..1a4160d843 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxCompareToolCommandLineTests.cs @@ -1,19 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.TrxReport.Abstractions; using Microsoft.Testing.Extensions.UnitTests.Helpers; using Microsoft.Testing.Platform.Extensions.CommandLine; namespace Microsoft.Testing.Extensions.UnitTests; -[TestGroup] -public class TrxCompareToolCommandLineTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TrxCompareToolCommandLineTests { - [Arguments(TrxCompareToolCommandLine.BaselineTrxOptionName)] - [Arguments(TrxCompareToolCommandLine.TrxToCompareOptionName)] + [TestMethod] + [DataRow(TrxCompareToolCommandLine.BaselineTrxOptionName)] + [DataRow(TrxCompareToolCommandLine.TrxToCompareOptionName)] public async Task IsValid_When_Correct_TrxFile_IsProvided_For_Options(string optionName) { var provider = new TrxCompareToolCommandLine(new TestExtension()); @@ -27,10 +26,11 @@ public async Task IsValid_When_Correct_TrxFile_IsProvided_For_Options(string opt File.Delete(filename); } - [Arguments(TrxCompareToolCommandLine.BaselineTrxOptionName, false)] - [Arguments(TrxCompareToolCommandLine.BaselineTrxOptionName, true)] - [Arguments(TrxCompareToolCommandLine.TrxToCompareOptionName, false)] - [Arguments(TrxCompareToolCommandLine.TrxToCompareOptionName, true)] + [TestMethod] + [DataRow(TrxCompareToolCommandLine.BaselineTrxOptionName, false)] + [DataRow(TrxCompareToolCommandLine.BaselineTrxOptionName, true)] + [DataRow(TrxCompareToolCommandLine.TrxToCompareOptionName, false)] + [DataRow(TrxCompareToolCommandLine.TrxToCompareOptionName, true)] public async Task IsInvalid_When_Incorrect_TrxFile_IsProvided_For_Options(string optionName, bool isTrxFile) { var provider = new TrxCompareToolCommandLine(new TestExtension()); @@ -42,6 +42,7 @@ public async Task IsInvalid_When_Incorrect_TrxFile_IsProvided_For_Options(string Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, TestReports.Resources.ExtensionResources.TrxComparerToolOptionExpectsSingleArgument, optionName), validateOptionsResult.ErrorMessage); } + [TestMethod] public async Task IsValid_If_Both_TrxOptions_Are_Provided() { var provider = new TrxCompareToolCommandLine(new TestExtension()); @@ -56,8 +57,9 @@ public async Task IsValid_If_Both_TrxOptions_Are_Provided() Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } - [Arguments(true, false)] - [Arguments(false, true)] + [TestMethod] + [DataRow(true, false)] + [DataRow(false, true)] public async Task IsInvalid_If_Any_TrxOptions_Is_Missing(bool isBaseLineSet, bool isToCompareSet) { var provider = new TrxCompareToolCommandLine(new TestExtension()); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs index 22d7e747f8..11f6621ed1 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxReportGeneratorCommandLineTests.cs @@ -7,9 +7,10 @@ namespace Microsoft.Testing.Extensions.UnitTests; -[TestGroup] -public class TrxReportGeneratorCommandLineTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class TrxReportGeneratorCommandLineTests { + [TestMethod] public async Task IsValid_If_TrxFile_And_Only_TargetFilename_Is_Provided() { var provider = new TrxReportGeneratorCommandLine(); @@ -21,8 +22,9 @@ public async Task IsValid_If_TrxFile_And_Only_TargetFilename_Is_Provided() Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } - [Arguments(false, false)] - [Arguments(true, true)] + [TestMethod] + [DataRow(false, false)] + [DataRow(true, true)] public async Task IsInvalid_If_TrxFile_And_Only_TargetFilename_Are_Not_Provided(bool isTrxFile, bool hasDirectory) { var provider = new TrxReportGeneratorCommandLine(); @@ -39,8 +41,9 @@ public async Task IsInvalid_If_TrxFile_And_Only_TargetFilename_Are_Not_Provided( Assert.AreEqual(isTrxFile ? TestReports.Resources.ExtensionResources.TrxReportFileNameShouldNotContainPath : TestReports.Resources.ExtensionResources.TrxReportFileNameExtensionIsNotTrx, validateOptionsResult.ErrorMessage); } - [Arguments(false, false, true)] - [Arguments(true, true, false)] + [TestMethod] + [DataRow(false, false, true)] + [DataRow(true, true, false)] public async Task IsValid_When_TrxReport_TrxReportFile_Is_Provided_And_DiscoverTests_Not_Provided(bool isFileNameSet, bool isTrxSet, bool isDiscoverTestsSet) { var provider = new TrxReportGeneratorCommandLine(); @@ -65,8 +68,9 @@ public async Task IsValid_When_TrxReport_TrxReportFile_Is_Provided_And_DiscoverT Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } - [Arguments(true, false, false)] - [Arguments(true, true, true)] + [TestMethod] + [DataRow(true, false, false)] + [DataRow(true, true, true)] public async Task IsInvalid_When_TrxReport_TrxReportFile_Is_Provided_And_DiscoverTests_Provided(bool isFileNameSet, bool isTrxSet, bool isDiscoverTestsSet) { var provider = new TrxReportGeneratorCommandLine(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs index e411f49abc..23b29e8e5a 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text.RegularExpressions; -using System.Xml.Linq; - using Microsoft.Testing.Extensions.TrxReport.Abstractions; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; @@ -20,8 +17,8 @@ namespace Microsoft.Testing.Extensions.UnitTests; -[TestGroup] -public class TrxTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public class TrxTests { private readonly Mock _environmentMock = new(); private readonly Mock _commandLineOptionsMock = new(); @@ -33,6 +30,7 @@ public class TrxTests(ITestExecutionContext testExecutionContext) : TestBase(tes private readonly Dictionary> _artifactsByTestNode = new(); private readonly Dictionary> _artifactsByExtension = new(); + [TestMethod] public async Task TrxReportEngine_GenerateReportAsyncWithNullAdapterSupportTrxCapability_TrxDoesNotContainClassName() { // Arrange @@ -51,6 +49,7 @@ public async Task TrxReportEngine_GenerateReportAsyncWithNullAdapterSupportTrxCa Assert.IsFalse(trxContent.Contains(@"className=")); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsyncWithNotExecutedTests_TrxExecutedTestsCountHasIt() { // Arrange @@ -69,6 +68,7 @@ public async Task TrxReportEngine_GenerateReportAsyncWithNotExecutedTests_TrxExe Assert.IsTrue(trxContent.Contains(@"notExecuted=""1""")); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsyncWithTimeoutTests_TrxTimeoutTestsCountHasIt() { // Arrange @@ -87,6 +87,7 @@ public async Task TrxReportEngine_GenerateReportAsyncWithTimeoutTests_TrxTimeout Assert.IsTrue(trxContent.Contains(@"timeout=""1""")); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithArgumentTrxReportFileName_FileIsCorrectlyGenerated() { // Arrange @@ -105,6 +106,7 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArgumentTrxReportFileN AssertTrxOutcome(xml, "Completed"); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithInvalidArgumentValueForTrxReportFileName_FileIsGeneratedWithNormalizedName() { // Arrange @@ -123,6 +125,7 @@ public async Task TrxReportEngine_GenerateReportAsync_WithInvalidArgumentValueFo AssertTrxOutcome(xml, "Completed"); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithTestHostCrash_ResultSummaryOutcomeIsFailed() { // Arrange @@ -139,6 +142,7 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestHostCrash_ResultSu AssertTrxOutcome(xml, "Failed"); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithTestSkipped_ResultSummaryOutcomeIsCompleted() { // Arrange @@ -155,6 +159,7 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestSkipped_ResultSumm AssertTrxOutcome(xml, "Completed"); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithStandardErrorTrxMessage_TrxContainsStdErr() { // Arrange @@ -184,9 +189,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithStandar "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStandardErrorTrxMessage_TrxContainsStdOut() { // Arrange @@ -211,9 +217,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStan "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStandardErrorTrxMessage_TrxContainsErrorInfo() { // Arrange @@ -241,9 +248,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithTestFailed_WithoutStan "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_PassedTestWithTestCategory_TrxContainsTestCategory() { // Arrange @@ -267,9 +275,10 @@ public async Task TrxReportEngine_GenerateReportAsync_PassedTestWithTestCategory "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_FailedTestWithTestCategory_TrxContainsTestCategory() { // Arrange @@ -293,9 +302,10 @@ public async Task TrxReportEngine_GenerateReportAsync_FailedTestWithTestCategory "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithAdapterSupportTrxCapability_TrxContainsClassName() { // Arrange @@ -314,9 +324,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithAdapterSupportTrxCapab XDocument xml = GetTrxContent(memoryStream); AssertTrxOutcome(xml, "Completed"); string trxContent = xml.ToString(); - Assert.That(trxContent.Contains(@"className=""TrxFullyQualifiedTypeName"), trxContent); + Assert.IsTrue(trxContent.Contains(@"className=""TrxFullyQualifiedTypeName"), trxContent); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByTestNode_TrxContainsResultFile() { // Arrange @@ -340,9 +351,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByTestNode_Tr "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByExtension_TrxContainsCollectorDataEntries() { // Arrange @@ -372,9 +384,10 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByExtension_T "; - Assert.That(Regex.IsMatch(trxContent, trxContentsPattern)); + Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern)); } + [TestMethod] public async Task TrxReportEngine_GenerateReportAsync_FileAlreadyExists_WillRetry() { // Arrange @@ -422,7 +435,6 @@ private static void AssertTrxOutcome(XDocument xml, string expectedOutcome) Assert.IsNotNull(resultSummary); XAttribute? outcome = resultSummary.FirstAttribute; Assert.IsNotNull(outcome); - Assert.IsNotNull(outcome.Value); Assert.IsTrue(outcome.Value.Equals(expectedOutcome, StringComparison.Ordinal)); } diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs index 03f91ef640..1609ade663 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/RunSettingsCommandLineOptionsProviderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers; @@ -13,10 +11,10 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.CommandLine; -[TestGroup] -public sealed class RunSettingsCommandLineOptionsProviderTests(ITestExecutionContext testExecutionContext) - : TestBase(testExecutionContext) +[TestClass] +public sealed class RunSettingsCommandLineOptionsProviderTests { + [TestMethod] public async Task RunSettingsOption_WhenFileDoesNotExist_IsNotValid() { // Arrange @@ -35,6 +33,7 @@ public async Task RunSettingsOption_WhenFileDoesNotExist_IsNotValid() Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RunsettingsFileDoesNotExist, filePath), result.ErrorMessage); } + [TestMethod] public async Task RunSettingsOption_WhenFileCannotBeOpen_IsNotValid() { // Arrange @@ -54,6 +53,7 @@ public async Task RunSettingsOption_WhenFileCannotBeOpen_IsNotValid() Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, ExtensionResources.RunsettingsFileCannotBeRead, filePath), result.ErrorMessage); } + [TestMethod] public async Task RunSettingsOption_WhenFileExistsAndCanBeOpen_IsValid() { // Arrange diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs index 7677aef16d..c97d84f8ae 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/CommandLine/TestRunParameterCommandLineOptionsProviderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Extensions.VSTestBridge.Resources; using Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers; @@ -10,10 +8,10 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.CommandLine; -[TestGroup] -public sealed class TestRunParameterCommandLineOptionsProviderTests(ITestExecutionContext testExecutionContext) - : TestBase(testExecutionContext) +[TestClass] +public sealed class TestRunParameterCommandLineOptionsProviderTests { + [TestMethod] public async Task TestRunParameterOption_WhenArgumentDoesNotContainEqual_IsNotValid() { // Arrange @@ -28,6 +26,7 @@ public async Task TestRunParameterOption_WhenArgumentDoesNotContainEqual_IsNotVa Assert.AreEqual(string.Format(CultureInfo.CurrentCulture, ExtensionResources.TestRunParameterOptionArgumentIsNotParameter, "something"), result.ErrorMessage); } + [TestMethod] public async Task TestRunParameterOption_WhenArgumentContainsEqual_IsValid() { // Arrange diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj index fa5aae3fee..b7ade1f8e0 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests.csproj @@ -3,7 +3,7 @@ $(TargetFrameworks);net462 false - true + true Exe diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs index ab1f69cd40..9e0d20d5d9 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/ObjectModelConvertersTests.cs @@ -10,19 +10,17 @@ using Microsoft.Testing.Platform.TestHost; using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult; + namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.ObjectModel; -[TestGroup] -public sealed class ObjectModelConvertersTests : TestBase +[TestClass] +public sealed class ObjectModelConvertersTests { private static readonly IClientInfo TestClient = new ClientInfoService("UnitTest", string.Empty); private static readonly IClientInfo VSTestClient = new ClientInfoService(WellKnownClients.VisualStudio, string.Empty); - public ObjectModelConvertersTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void ToTestNode_WhenTestCaseHasDisplayName_TestNodeDisplayNameUsesIt() { TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs") @@ -34,6 +32,7 @@ public void ToTestNode_WhenTestCaseHasDisplayName_TestNodeDisplayNameUsesIt() Assert.AreEqual("MyDisplayName", testNode.DisplayName); } + [TestMethod] public void ToTestNode_WhenTestCaseHasNoDisplayName_TestNodeDisplayNameUsesIt() { TestCase testCase = new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs"); @@ -42,6 +41,7 @@ public void ToTestNode_WhenTestCaseHasNoDisplayName_TestNodeDisplayNameUsesIt() Assert.AreEqual("SomeFqn", testNode.DisplayName); } + [TestMethod] public void ToTestNode_WhenTestResultHasCodeFilePath_SetsTestFileLocationProperty() { TestResult testResult = new(new("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs") @@ -52,6 +52,7 @@ public void ToTestNode_WhenTestResultHasCodeFilePath_SetsTestFileLocationPropert Assert.AreEqual("FilePath", testNode.Properties.Single().FilePath); } + [TestMethod] public void ToTestNode_WhenTestResultOutcomeIsFailed_TestNodePropertiesContainFailedTestNodeStateProperty() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -63,12 +64,13 @@ public void ToTestNode_WhenTestResultOutcomeIsFailed_TestNodePropertiesContainFa var testNode = testResult.ToTestNode(false, TestClient); FailedTestNodeStateProperty[] failedTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(failedTestNodeStateProperties.Length == 1); + Assert.AreEqual(1, failedTestNodeStateProperties.Length); Assert.IsTrue(failedTestNodeStateProperties[0].Exception is VSTestException); Assert.AreEqual(testResult.ErrorStackTrace, failedTestNodeStateProperties[0].Exception!.StackTrace); Assert.AreEqual(testResult.ErrorMessage, failedTestNodeStateProperties[0].Exception!.Message); } + [TestMethod] public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestProperty_TestNodePropertiesContainTheCategoryInTraits() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); @@ -78,12 +80,13 @@ public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestProperty var testNode = testResult.ToTestNode(false, VSTestClient); SerializableNamedKeyValuePairsStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(errorTestNodeStateProperties.Length == 1); - Assert.IsTrue(errorTestNodeStateProperties[0].Name == "traits"); - Assert.IsTrue(errorTestNodeStateProperties[0].Pairs.Length == 1); - Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Key == "category1"); + Assert.AreEqual(1, errorTestNodeStateProperties.Length); + Assert.AreEqual("traits", errorTestNodeStateProperties[0].Name); + Assert.AreEqual(1, errorTestNodeStateProperties[0].Pairs.Length); + Assert.AreEqual("category1", errorTestNodeStateProperties[0].Pairs[0].Key); } + [TestMethod] public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestPropertyWithTrxEnabled_TestNodePropertiesContainTrxCategoriesProperty() { TestResult testResult = new(new TestCase("assembly.class.SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); @@ -93,11 +96,12 @@ public void ToTestNode_WhenTestResultHasMSTestDiscovererTestCategoryTestProperty var testNode = testResult.ToTestNode(true, VSTestClient); TrxCategoriesProperty[] trxCategoriesProperty = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(trxCategoriesProperty.Length == 1); - Assert.IsTrue(trxCategoriesProperty[0].Categories.Length == 1); - Assert.AreEqual(trxCategoriesProperty[0].Categories[0], "category1"); + Assert.AreEqual(1, trxCategoriesProperty.Length); + Assert.AreEqual(1, trxCategoriesProperty[0].Categories.Length); + Assert.AreEqual("category1", trxCategoriesProperty[0].Categories[0]); } + [TestMethod] public void ToTestNode_WhenTestResultHasTestCaseHierarchyTestProperty_TestNodePropertiesContainItInSerializableNamedArrayStringProperty() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); @@ -107,13 +111,14 @@ public void ToTestNode_WhenTestResultHasTestCaseHierarchyTestProperty_TestNodePr var testNode = testResult.ToTestNode(false, VSTestClient); SerializableNamedArrayStringProperty[] trxCategoriesProperty = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(trxCategoriesProperty.Length == 1); - Assert.AreEqual(trxCategoriesProperty[0].Values[0], "assembly"); - Assert.AreEqual(trxCategoriesProperty[0].Values[1], "class"); - Assert.AreEqual(trxCategoriesProperty[0].Values[2], "category"); - Assert.AreEqual(trxCategoriesProperty[0].Values[3], "test"); + Assert.AreEqual(1, trxCategoriesProperty.Length); + Assert.AreEqual("assembly", trxCategoriesProperty[0].Values[0]); + Assert.AreEqual("class", trxCategoriesProperty[0].Values[1]); + Assert.AreEqual("category", trxCategoriesProperty[0].Values[2]); + Assert.AreEqual("test", trxCategoriesProperty[0].Values[3]); } + [TestMethod] public void ToTestNode_WhenTestResultHasOriginalExecutorUriProperty_TestNodePropertiesContainItInSerializableKeyValuePairStringProperty() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")); @@ -125,11 +130,12 @@ public void ToTestNode_WhenTestResultHasOriginalExecutorUriProperty_TestNodeProp var testNode = testResult.ToTestNode(false, VSTestClient); SerializableKeyValuePairStringProperty[] serializableKeyValuePairStringProperty = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(serializableKeyValuePairStringProperty.Length == 3); - Assert.AreEqual(serializableKeyValuePairStringProperty[0].Key, VSTestTestNodeProperties.OriginalExecutorUriPropertyName); - Assert.AreEqual(serializableKeyValuePairStringProperty[0].Value, "https://vs.com/"); + Assert.AreEqual(3, serializableKeyValuePairStringProperty.Length); + Assert.AreEqual(VSTestTestNodeProperties.OriginalExecutorUriPropertyName, serializableKeyValuePairStringProperty[0].Key); + Assert.AreEqual("https://vs.com/", serializableKeyValuePairStringProperty[0].Value); } + [TestMethod] public void ToTestNode_WhenTestResultHasFullyQualifiedTypeAndTrxEnabled_TestNodeHasFullyQualifiedTypeName() { TestResult testResult = new(new TestCase("assembly.class.test", new("executor://uri", UriKind.Absolute), "source.cs")); @@ -140,15 +146,17 @@ public void ToTestNode_WhenTestResultHasFullyQualifiedTypeAndTrxEnabled_TestNode Assert.AreEqual("assembly.class", testNode.Properties.Single().FullyQualifiedTypeName); } + [TestMethod] public void ToTestNode_WhenTestResultHasNoFullyQualifiedTypeAndTrxEnabled_Throws() { TestResult testResult = new(new TestCase("test", new("executor://uri", UriKind.Absolute), "source.cs")); - string errorMessage = Assert.Throws(() => testResult.ToTestNode(true, TestClient)).Message; + string errorMessage = Assert.ThrowsException(() => testResult.ToTestNode(true, TestClient)).Message; Assert.IsTrue(errorMessage.Contains("Unable to parse fully qualified type name from test case: ")); } + [TestMethod] public void ToTestNode_FromTestResult_TestNodePropertiesContainCorrectTimingProperty() { var startTime = new DateTime(1996, 8, 22, 20, 30, 5); @@ -167,6 +175,7 @@ public void ToTestNode_FromTestResult_TestNodePropertiesContainCorrectTimingProp Assert.AreEqual(testNode.Properties.OfType()[0], testResultTimingProperty); } + [TestMethod] public void ToTestNode_WhenTestResultOutcomeIsNotFoundWithoutSetErrorMessage_TestNodePropertiesContainErrorTestNodeStatePropertyWithDefaultErrorMessage() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -177,12 +186,13 @@ public void ToTestNode_WhenTestResultOutcomeIsNotFoundWithoutSetErrorMessage_Tes var testNode = testResult.ToTestNode(false, TestClient); ErrorTestNodeStateProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(errorTestNodeStateProperties.Length == 1); + Assert.AreEqual(1, errorTestNodeStateProperties.Length); Assert.IsTrue(errorTestNodeStateProperties[0].Exception is VSTestException); Assert.AreEqual(testResult.ErrorStackTrace, errorTestNodeStateProperties[0].Exception!.StackTrace); Assert.IsTrue(errorTestNodeStateProperties[0].Exception!.Message.Contains("Not found")); } + [TestMethod] public void ToTestNode_WhenTestResultOutcomeIsSkipped_TestNodePropertiesContainSkippedTestNodeStateProperty() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -192,9 +202,10 @@ public void ToTestNode_WhenTestResultOutcomeIsSkipped_TestNodePropertiesContainS var testNode = testResult.ToTestNode(false, TestClient); SkippedTestNodeStateProperty[] skipTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(skipTestNodeStateProperties.Length == 1); + Assert.AreEqual(1, skipTestNodeStateProperties.Length); } + [TestMethod] public void ToTestNode_WhenTestResultOutcomeIsNone_TestNodePropertiesContainSkippedTestNodeStateProperty() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -204,9 +215,10 @@ public void ToTestNode_WhenTestResultOutcomeIsNone_TestNodePropertiesContainSkip var testNode = testResult.ToTestNode(false, TestClient); SkippedTestNodeStateProperty[] skipTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(skipTestNodeStateProperties.Length == 1); + Assert.AreEqual(1, skipTestNodeStateProperties.Length); } + [TestMethod] public void ToTestNode_WhenTestResultOutcomeIsPassed_TestNodePropertiesContainPassedTestNodeStateProperty() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -216,9 +228,10 @@ public void ToTestNode_WhenTestResultOutcomeIsPassed_TestNodePropertiesContainPa var testNode = testResult.ToTestNode(false, TestClient); PassedTestNodeStateProperty[] passedTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(passedTestNodeStateProperties.Length == 1); + Assert.AreEqual(1, passedTestNodeStateProperties.Length); } + [TestMethod] public void ToTestNode_WhenTestResultHasUidAndDisplayNameWithWellKnownClient_TestNodePropertiesContainSerializableKeyValuePairStringPropertyTwice() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -229,12 +242,13 @@ public void ToTestNode_WhenTestResultHasUidAndDisplayNameWithWellKnownClient_Tes var testNode = testResult.ToTestNode(false, VSTestClient); SerializableKeyValuePairStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(errorTestNodeStateProperties.Length == 2, "Expected 2 SerializableKeyValuePairStringProperty"); - Assert.IsTrue(errorTestNodeStateProperties[0].Key == "vstest.TestCase.Id"); - Assert.IsTrue(errorTestNodeStateProperties[1].Key == "vstest.TestCase.FullyQualifiedName"); - Assert.IsTrue(errorTestNodeStateProperties[1].Value == "SomeFqn"); + Assert.AreEqual(2, errorTestNodeStateProperties.Length, "Expected 2 SerializableKeyValuePairStringProperty"); + Assert.AreEqual("vstest.TestCase.Id", errorTestNodeStateProperties[0].Key); + Assert.AreEqual("vstest.TestCase.FullyQualifiedName", errorTestNodeStateProperties[1].Key); + Assert.AreEqual("SomeFqn", errorTestNodeStateProperties[1].Value); } + [TestMethod] public void ToTestNode_WhenTestResultHasTraits_TestNodePropertiesContainIt() { TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) @@ -246,10 +260,50 @@ public void ToTestNode_WhenTestResultHasTraits_TestNodePropertiesContainIt() var testNode = testResult.ToTestNode(false, VSTestClient); SerializableNamedKeyValuePairsStringProperty[] errorTestNodeStateProperties = testNode.Properties.OfType().ToArray(); - Assert.IsTrue(errorTestNodeStateProperties.Length == 1); - Assert.IsTrue(errorTestNodeStateProperties[0].Name == "traits"); - Assert.IsTrue(errorTestNodeStateProperties[0].Pairs.Length == 1); - Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Key == "key"); - Assert.IsTrue(errorTestNodeStateProperties[0].Pairs[0].Value == "value"); + Assert.AreEqual(1, errorTestNodeStateProperties.Length); + Assert.AreEqual("traits", errorTestNodeStateProperties[0].Name); + Assert.AreEqual(1, errorTestNodeStateProperties[0].Pairs.Length); + Assert.AreEqual("key", errorTestNodeStateProperties[0].Pairs[0].Key); + Assert.AreEqual("value", errorTestNodeStateProperties[0].Pairs[0].Value); + } + + [TestMethod] + public void ToTestNode_WhenTestResultHasMultipleStandardOutputMessages_TestNodePropertiesHasASingleOne() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + DisplayName = "TestName", + Messages = + { + new TestResultMessage(TestResultMessage.StandardOutCategory, "message1"), + new TestResultMessage(TestResultMessage.StandardOutCategory, "message2"), + }, + }; + + var testNode = testResult.ToTestNode(false, VSTestClient); + + StandardOutputProperty[] standardOutputProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(standardOutputProperties.Length == 1); + Assert.AreEqual($"message1{Environment.NewLine}message2", standardOutputProperties[0].StandardOutput); + } + + [TestMethod] + public void ToTestNode_WhenTestResultHasMultipleStandardErrorMessages_TestNodePropertiesHasASingleOne() + { + TestResult testResult = new(new TestCase("SomeFqn", new("executor://uri", UriKind.Absolute), "source.cs")) + { + DisplayName = "TestName", + Messages = + { + new TestResultMessage(TestResultMessage.StandardErrorCategory, "message1"), + new TestResultMessage(TestResultMessage.StandardErrorCategory, "message2"), + }, + }; + + var testNode = testResult.ToTestNode(false, VSTestClient); + + StandardErrorProperty[] standardErrorProperties = testNode.Properties.OfType().ToArray(); + Assert.IsTrue(standardErrorProperties.Length == 1); + Assert.AreEqual($"message1{Environment.NewLine}message2", standardErrorProperties[0].StandardError); } } diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs index 543e1012bb..58bcb780a3 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunContextAdapterTests.cs @@ -9,21 +9,17 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.ObjectModel; -[TestGroup] -public class RunContextAdapterTests : TestBase +[TestClass] +public class RunContextAdapterTests { private readonly Mock _commandLineOptions = new(); private readonly Mock _runSettings = new(); - public RunContextAdapterTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void TestRunDirectory_IsNotNull_If_ResultsDirectory_Is_Provided() { string runSettings = -$""" +""" /PlatformResultDirectoryFromFile @@ -38,10 +34,11 @@ public void TestRunDirectory_IsNotNull_If_ResultsDirectory_Is_Provided() Assert.IsNotNull(runContextAdapter.RunSettings); } + [TestMethod] public void TestRunDirectory_IsNull_If_ResultsDirectory_IsNot_Provided() { string runSettings = -$""" +""" diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs index 224ed9acf9..92c52dbde1 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/ObjectModel/RunSettingsPatcherTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml.Linq; -using System.Xml.XPath; - using Microsoft.Testing.Extensions.VSTestBridge.CommandLine; using Microsoft.Testing.Extensions.VSTestBridge.ObjectModel; using Microsoft.Testing.Platform.CommandLine; @@ -14,12 +11,13 @@ namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.ObjectModel; -[TestGroup] -public class RunSettingsPatcherTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public class RunSettingsPatcherTests { private readonly Mock _configuration = new(); private readonly Mock _commandLineOptions = new(); + [TestMethod] public void Patch_WhenNoRunSettingsProvided_CreateRunSettingsWithResultsDirectoryElement() { _configuration.Setup(x => x[PlatformConfigurationConstants.PlatformResultDirectory]).Returns("/PlatformResultDirectory"); @@ -30,6 +28,7 @@ public void Patch_WhenNoRunSettingsProvided_CreateRunSettingsWithResultsDirector runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/ResultsDirectory")!.Value); } + [TestMethod] public void Patch_WithRunSettingsProvidedButMissingResultsDirectory_AddsElement() { string runSettings = """ @@ -49,10 +48,11 @@ public void Patch_WithRunSettingsProvidedButMissingResultsDirectory_AddsElement( Assert.IsTrue(bool.Parse(runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/Canary")!.Value)); } + [TestMethod] public void Patch_WithRunSettingsContainingResultsDirectory_EntryIsNotOverridden() { string runSettings = -$""" +""" true @@ -69,6 +69,7 @@ public void Patch_WithRunSettingsContainingResultsDirectory_EntryIsNotOverridden Assert.IsTrue(bool.Parse(runSettingsDocument.XPathSelectElement("RunSettings/RunConfiguration/Canary")!.Value)); } + [TestMethod] public void Patch_WhenRunSettingsExists_MergesParameters() { string runSettings = """ @@ -94,14 +95,15 @@ public void Patch_WhenRunSettingsExists_MergesParameters() _commandLineOptions.Object); XElement[] testRunParameters = runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter").ToArray(); - Assert.AreEqual(testRunParameters[0].Attribute("name")!.Value, "key1"); - Assert.AreEqual(testRunParameters[0].Attribute("value")!.Value, "value1"); - Assert.AreEqual(testRunParameters[1].Attribute("name")!.Value, "key2"); - Assert.AreEqual(testRunParameters[1].Attribute("value")!.Value, "updated-value"); - Assert.AreEqual(testRunParameters[2].Attribute("name")!.Value, "key3"); - Assert.AreEqual(testRunParameters[2].Attribute("value")!.Value, "value3"); + Assert.AreEqual("key1", testRunParameters[0].Attribute("name")!.Value); + Assert.AreEqual("value1", testRunParameters[0].Attribute("value")!.Value); + Assert.AreEqual("key2", testRunParameters[1].Attribute("name")!.Value); + Assert.AreEqual("updated-value", testRunParameters[1].Attribute("value")!.Value); + Assert.AreEqual("key3", testRunParameters[2].Attribute("name")!.Value); + Assert.AreEqual("value3", testRunParameters[2].Attribute("value")!.Value); } + [TestMethod] public void Patch_WhenRunSettingsDoesNotExist_AddParameters() { string[]? arguments; @@ -117,9 +119,9 @@ public void Patch_WhenRunSettingsDoesNotExist_AddParameters() _commandLineOptions.Object); XElement[] testRunParameters = runSettingsDocument.XPathSelectElements("RunSettings/TestRunParameters/Parameter").ToArray(); - Assert.AreEqual(testRunParameters[0].Attribute("name")!.Value, "key1"); - Assert.AreEqual(testRunParameters[0].Attribute("value")!.Value, "value1"); - Assert.AreEqual(testRunParameters[1].Attribute("name")!.Value, "key2"); - Assert.AreEqual(testRunParameters[1].Attribute("value")!.Value, "value2"); + Assert.AreEqual("key1", testRunParameters[0].Attribute("name")!.Value); + Assert.AreEqual("value1", testRunParameters[0].Attribute("value")!.Value); + Assert.AreEqual("key2", testRunParameters[1].Attribute("name")!.Value); + Assert.AreEqual("value2", testRunParameters[1].Attribute("value")!.Value); } } diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs index 0b997618f7..5af7b8a583 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Program.cs @@ -2,15 +2,20 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Testing.Extensions; -using Microsoft.Testing.Extensions.VSTestBridge.UnitTests; -ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new SourceGeneratedTestNodesBuilder()); +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] #if NETCOREAPP Console.WriteLine("Dynamic code supported: " + System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported); #endif +ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); + +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); + #if !NATIVE_AOT #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json index ea079cb247..23c4781b41 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json +++ b/test/UnitTests/Microsoft.Testing.Extensions.VSTestBridge.UnitTests/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Microsoft.Testing.Extensions.VSTestBridge.UnitTests": { "commandName": "Project", - "commandLineArgs": "--treenode-filter /*/*/*/**", + "commandLineArgs": "", "environmentVariables": { } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs index 503931e844..5bc558d71c 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/MSBuildTests.cs @@ -1,41 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; - using Microsoft.Build.Framework; -#if NET8_0_OR_GREATER + using Moq; -#endif namespace Microsoft.Testing.Platform.MSBuild.UnitTests; -[TestGroup] -public class MSBuildTests : TestBase +[TestClass] +public sealed class MSBuildTests { -#if NET8_0_OR_GREATER private readonly Mock _buildEngine; private readonly List _errors; -#endif - public MSBuildTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public MSBuildTests() { -#if NET8_0_OR_GREATER _buildEngine = new Mock(); _errors = new List(); _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); -#endif } + [TestMethod] public void Verify_Correct_Registration_Order_For_WellKnown_Extensions() { -#if !NET8_0_OR_GREATER - // On netfx, net6.0, and net7.0 this is failing with: - // Could not load file or assembly 'Microsoft.Build.Framework, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified. - // This is because the NuGet Package is "compatible" with netstandard2.0, so it can be installed everywhere, but it restores dlls only into specific (new) versions of .NET Framework and .NET. - return; -#else InMemoryFileSystem inMemoryFileSystem = new(); TestingPlatformEntryPointTask testingPlatformEntryPoint = new(inMemoryFileSystem) { @@ -69,7 +56,6 @@ internal sealed class TestingPlatformEntryPoint """; Assert.AreEqual(expectedSourceOrder, inMemoryFileSystem.Files["obj/entryPointFile"]); -#endif } private sealed class InMemoryFileSystem : IFileSystem diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj index 87bee4bfd3..d5a6455762 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests.csproj @@ -1,9 +1,9 @@ - $(MicrosoftTestingTargetFrameworks);net462 + net8.0;net9.0 false - true + true Exe diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs index 45d399bd11..c05ca317bd 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Program.cs @@ -3,9 +3,12 @@ using Microsoft.Testing.Extensions; +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] + // DebuggerUtility.AttachVSToCurrentProcess(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new Microsoft.Testing.Platform.MSBuild.UnitTests.SourceGeneratedTestNodesBuilder()); +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif diff --git a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs index a91def1ab6..10fe4382b8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.MSBuild.UnitTests/Usings.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -global using Microsoft.Testing.Internal.Framework; global using Microsoft.Testing.Platform.Builder; global using Microsoft.Testing.Platform.Extensions; global using Microsoft.Testing.TestInfrastructure; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs index 33921a8451..5cada34ede 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/ArgumentArityTests.cs @@ -10,8 +10,8 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class ArgumentArityTests : TestBase +[TestClass] +public sealed class ArgumentArityTests { private readonly ICommandLineOptionsProvider[] _systemCommandLineOptionsProviders = [ @@ -23,11 +23,7 @@ public class ArgumentArityTests : TestBase new ExtensionCommandLineProviderMockOptionsWithDifferentArity() ]; - public ArgumentArityTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public async Task ParseAndValidate_WhenOptionWithArityZeroIsCalledWithOneArgument_ReturnsFalse() { // Arrange @@ -43,6 +39,7 @@ public async Task ParseAndValidate_WhenOptionWithArityZeroIsCalledWithOneArgumen Assert.AreEqual("Option '--zeroArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects no arguments", result.ErrorMessage, StringComparer.Ordinal); } + [TestMethod] public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithTwoArguments_ReturnsFalse() { // Arrange @@ -58,6 +55,7 @@ public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithTwoA Assert.AreEqual("Option '--exactlyOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at most 1 arguments", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithoutArguments_ReturnsFalse() { // Arrange @@ -73,6 +71,7 @@ public async Task ParseAndValidate_WhenOptionWithArityExactlyOneIsCalledWithoutA Assert.AreEqual("Option '--exactlyOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at least 1 arguments", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidate_WhenOptionWithArityZeroOrOneIsCalledWithTwoArguments_ReturnsFalse() { // Arrange @@ -88,6 +87,7 @@ public async Task ParseAndValidate_WhenOptionWithArityZeroOrOneIsCalledWithTwoAr Assert.AreEqual("Option '--zeroOrOneArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at most 1 arguments", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidate_WhenOptionWithArityOneOrMoreIsCalledWithoutArguments_ReturnsFalse() { // Arrange @@ -103,6 +103,7 @@ public async Task ParseAndValidate_WhenOptionWithArityOneOrMoreIsCalledWithoutAr Assert.AreEqual("Option '--oneOrMoreArgumentsOption' from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) expects at least 1 arguments", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidate_WhenOptionsGetsTheExpectedNumberOfArguments_ReturnsTrue() { // Arrange diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs index b7544c39ee..c192ec482f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineHandlerTests.cs @@ -12,8 +12,8 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class CommandLineHandlerTests : TestBase +[TestClass] +public sealed class CommandLineHandlerTests { private readonly Mock _outputDisplayMock = new(); private readonly Mock _testApplicationModuleInfoMock = new(); @@ -25,11 +25,7 @@ public class CommandLineHandlerTests : TestBase private readonly ICommandLineOptionsProvider[] _extensionCommandLineOptionsProviders = []; - public CommandLineHandlerTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public async Task ParseAndValidateAsync_InvalidCommandLineArguments_ReturnsFalse() { // Arrange @@ -42,10 +38,11 @@ public async Task ParseAndValidateAsync_InvalidCommandLineArguments_ReturnsFalse // Assert Assert.IsFalse(result.IsValid); - Assert.Contains("Invalid command line arguments:", result.ErrorMessage); - Assert.Contains("Unexpected argument 'a'", result.ErrorMessage); + StringAssert.Contains(result.ErrorMessage, "Invalid command line arguments:"); + StringAssert.Contains(result.ErrorMessage, "Unexpected argument 'a'"); } + [TestMethod] public async Task ParseAndValidateAsync_EmptyCommandLineArguments_ReturnsTrue() { // Arrange @@ -60,6 +57,7 @@ public async Task ParseAndValidateAsync_EmptyCommandLineArguments_ReturnsTrue() Assert.IsTrue(result.IsValid); } + [TestMethod] public async Task ParseAndValidateAsync_DuplicateOption_ReturnsFalse() { // Arrange @@ -77,9 +75,10 @@ public async Task ParseAndValidateAsync_DuplicateOption_ReturnsFalse() // Assert Assert.IsFalse(result.IsValid); - Assert.Contains("Option '--userOption' is declared by multiple extensions: 'Microsoft Testing Platform command line provider', 'Microsoft Testing Platform command line provider'", result.ErrorMessage); + StringAssert.Contains(result.ErrorMessage, "Option '--userOption' is declared by multiple extensions: 'Microsoft Testing Platform command line provider', 'Microsoft Testing Platform command line provider'"); } + [TestMethod] public async Task ParseAndValidateAsync_InvalidOption_ReturnsFalse() { // Arrange @@ -95,6 +94,7 @@ public async Task ParseAndValidateAsync_InvalidOption_ReturnsFalse() Assert.AreEqual("Option '--diagnostic-verbosity' has invalid arguments: '--diagnostic-verbosity' expects a single level argument ('Trace', 'Debug', 'Information', 'Warning', 'Error', or 'Critical')", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidateAsync_InvalidArgumentArity_ReturnsFalse() { // Arrange @@ -110,6 +110,7 @@ public async Task ParseAndValidateAsync_InvalidArgumentArity_ReturnsFalse() Assert.AreEqual("Option '--help' from provider 'Platform command line provider' (UID: PlatformCommandLineProvider) expects no arguments", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidateAsync_ReservedOptions_ReturnsFalse() { // Arrange @@ -129,6 +130,7 @@ public async Task ParseAndValidateAsync_ReservedOptions_ReturnsFalse() Assert.AreEqual("Option '--help' is reserved and cannot be used by providers: 'help'", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidateAsync_ReservedOptionsPrefix_ReturnsFalse() { // Arrange @@ -148,6 +150,7 @@ public async Task ParseAndValidateAsync_ReservedOptionsPrefix_ReturnsFalse() Assert.AreEqual("Option `--internal-customextension` from provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider) is using the reserved prefix '--internal'", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidateAsync_UnknownOption_ReturnsFalse() { // Arrange @@ -168,6 +171,7 @@ public async Task ParseAndValidateAsync_UnknownOption_ReturnsFalse() Assert.AreEqual("Unknown option '--x'", result.ErrorMessage); } + [TestMethod] public async Task ParseAndValidateAsync_InvalidValidConfiguration_ReturnsFalse() { // Arrange @@ -187,6 +191,7 @@ public async Task ParseAndValidateAsync_InvalidValidConfiguration_ReturnsFalse() Assert.AreEqual("Invalid configuration for provider 'Microsoft Testing Platform command line provider' (UID: PlatformCommandLineProvider). Error: Invalid configuration errorMessage", result.ErrorMessage); } + [TestMethod] public void IsHelpInvoked_HelpOptionSet_ReturnsTrue() { // Arrange @@ -204,6 +209,7 @@ public void IsHelpInvoked_HelpOptionSet_ReturnsTrue() _outputDisplayMock.Verify(o => o.DisplayBannerAsync(It.IsAny()), Times.Never); } + [TestMethod] public void IsInfoInvoked_InfoOptionSet_ReturnsTrue() { // Arrange @@ -221,6 +227,7 @@ public void IsInfoInvoked_InfoOptionSet_ReturnsTrue() _outputDisplayMock.Verify(o => o.DisplayBannerAsync(It.IsAny()), Times.Never); } + [TestMethod] public void IsVersionInvoked_VersionOptionSet_ReturnsTrue() { // Arrange @@ -238,6 +245,7 @@ public void IsVersionInvoked_VersionOptionSet_ReturnsTrue() _outputDisplayMock.Verify(o => o.DisplayBannerAsync(It.IsAny()), Times.Never); } + [TestMethod] public void GetOptionValue_OptionExists_ReturnsOptionValue() { // Arrange @@ -251,12 +259,13 @@ public void GetOptionValue_OptionExists_ReturnsOptionValue() // Assert Assert.IsTrue(result); - Assert.IsFalse(optionValue is null); + Assert.IsNotNull(optionValue); Assert.AreEqual(optionValue?.Length, 2); Assert.AreEqual("value1", optionValue?[0]); Assert.AreEqual("value2", optionValue?[1]); } + [TestMethod] public void GetOptionValue_OptionDoesNotExist_ReturnsNull() { // Arrange @@ -278,7 +287,7 @@ public void GetOptionValue_OptionDoesNotExist_ReturnsNull() // Assert Assert.IsFalse(result); - Assert.IsTrue(optionValue is null); + Assert.IsNull(optionValue); } private sealed class ExtensionCommandLineProviderMockReservedOptions : ICommandLineOptionsProvider diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs index bd1f756bb7..07468dc56c 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/CommandLineTests.cs @@ -7,13 +7,13 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class CommandLineTests : TestBase +[TestClass] +public sealed class CommandLineTests { // The test method ParserTests is parameterized and one of the parameter needs to be CommandLineParseResult. // The test method has to be public to be run, but CommandLineParseResult is internal. // So, we introduce this wrapper to be used instead so that the test method can be made public. - public class CommandLineParseResultWrapper + public sealed class CommandLineParseResultWrapper { internal CommandLineParseResultWrapper(string? toolName, IReadOnlyList options, IReadOnlyList errors) => Result = new CommandLineParseResult(toolName, options, errors); @@ -21,12 +21,8 @@ internal CommandLineParseResultWrapper(string? toolName, IReadOnlyList ParserTestDataFormat(TestArgumentsContext ctx) + public static string ParserTestDataFormat(MethodInfo methodInfo, object?[]? data) { - (int TestNum, string[] Args, (string RspFileName, string RspFileContent)[]? RspFiles, CommandLineParseResultWrapper ParseResult) item = ((int, string[], (string, string)[], CommandLineParseResultWrapper))ctx.Arguments; + (int testNum, string[] args, (string RspFileName, string RspFileContent)[]? rspFiles, CommandLineParseResultWrapper parseResult) = ((int)data![0]!, (string[])data[1]!, ((string, string)[])data[2]!, (CommandLineParseResultWrapper)data[3]!); - return item.TestNum == 13 - ? new(item, $"\"--option1\", $@\" \"\" \\{{Environment.NewLine}} \"\" \" {item.TestNum}") - : new(item, $"{item.Args.Aggregate((a, b) => $"{a} {b}")} {item.TestNum}"); + return testNum == 13 + ? $"\"--option1\", $@\" \"\" \\{{Environment.NewLine}} \"\" \" {testNum}" + : $"{args.Aggregate((a, b) => $"{a} {b}")} {testNum}"; } internal static IEnumerable<(int TestNum, string[] Args, (string RspFileName, string RspFileContent)[]? RspFiles, CommandLineParseResultWrapper ParseResult)> ParserTestsData() @@ -204,6 +200,7 @@ public void ParserTests(int testNum, string[] args, (string RspFileName, string }.ToArray(), [])); } + [TestMethod] public void CommandLineOptionWithNumber_IsSupported() { _ = new CommandLineOption("123", "sample", ArgumentArity.ZeroOrOne, false); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs index b108c5ed59..4ea02fad22 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/PlatformCommandLineProviderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Extensions.CommandLine; using Microsoft.Testing.Platform.Resources; @@ -10,20 +8,16 @@ namespace Microsoft.Testing.Platform.UnitTests.CommandLine; -[TestGroup] -public class PlatformCommandLineProviderTests : TestBase +[TestClass] +public sealed class PlatformCommandLineProviderTests { - public PlatformCommandLineProviderTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - - [Arguments("Trace")] - [Arguments("Debug")] - [Arguments("Information")] - [Arguments("Warning")] - [Arguments("Error")] - [Arguments("Critical")] + [TestMethod] + [DataRow("Trace")] + [DataRow("Debug")] + [DataRow("Information")] + [DataRow("Warning")] + [DataRow("Error")] + [DataRow("Critical")] public async Task IsValid_If_Verbosity_Has_CorrectValue(string dumpType) { var provider = new PlatformCommandLineProvider(); @@ -34,6 +28,7 @@ public async Task IsValid_If_Verbosity_Has_CorrectValue(string dumpType) Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } + [TestMethod] public async Task IsInvalid_If_Verbosity_Has_IncorrectValue() { var provider = new PlatformCommandLineProvider(); @@ -44,6 +39,7 @@ public async Task IsInvalid_If_Verbosity_Has_IncorrectValue() Assert.AreEqual(PlatformResources.PlatformCommandLineDiagnosticOptionExpectsSingleArgumentErrorMessage, validateOptionsResult.ErrorMessage); } + [TestMethod] public async Task IsValid_If_ClientPort_Is_Integer() { var provider = new PlatformCommandLineProvider(); @@ -54,8 +50,9 @@ public async Task IsValid_If_ClientPort_Is_Integer() Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } - [Arguments("32.32")] - [Arguments("invalid")] + [TestMethod] + [DataRow("32.32")] + [DataRow("invalid")] public async Task IsInvalid_If_ClientPort_Is_Not_Integer(string clientPort) { var provider = new PlatformCommandLineProvider(); @@ -66,6 +63,7 @@ public async Task IsInvalid_If_ClientPort_Is_Not_Integer(string clientPort) Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLinePortOptionSingleArgument, PlatformCommandLineProvider.ClientPortOptionKey), validateOptionsResult.ErrorMessage); } + [TestMethod] public async Task IsValid_If_ExitOnProcessExit_Is_Integer() { var provider = new PlatformCommandLineProvider(); @@ -76,8 +74,9 @@ public async Task IsValid_If_ExitOnProcessExit_Is_Integer() Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } - [Arguments("32.32")] - [Arguments("invalid")] + [TestMethod] + [DataRow("32.32")] + [DataRow("invalid")] public async Task IsInvalid_If_ExitOnProcessExit_Is_Not_Integer(string pid) { var provider = new PlatformCommandLineProvider(); @@ -88,6 +87,7 @@ public async Task IsInvalid_If_ExitOnProcessExit_Is_Not_Integer(string pid) Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineExitOnProcessExitSingleArgument, PlatformCommandLineProvider.ExitOnProcessExitOptionKey), validateOptionsResult.ErrorMessage); } + [TestMethod] public async Task IsValid_If_Diagnostics_Provided_With_Other_Diagnostics_Provided() { var provider = new PlatformCommandLineProvider(); @@ -103,6 +103,7 @@ public async Task IsValid_If_Diagnostics_Provided_With_Other_Diagnostics_Provide Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } + [TestMethod] public async Task IsValid_When_NoOptionSpecified() { var provider = new PlatformCommandLineProvider(); @@ -112,9 +113,10 @@ public async Task IsValid_When_NoOptionSpecified() Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } - [Arguments(PlatformCommandLineProvider.DiagnosticOutputDirectoryOptionKey)] - [Arguments(PlatformCommandLineProvider.DiagnosticOutputFilePrefixOptionKey)] + [DataRow(PlatformCommandLineProvider.DiagnosticOutputDirectoryOptionKey)] + [DataRow(PlatformCommandLineProvider.DiagnosticOutputFilePrefixOptionKey)] + [TestMethod] public async Task IsNotValid_If_Diagnostics_Missing_When_OthersDiagnostics_Provided(string optionName) { var provider = new PlatformCommandLineProvider(); @@ -128,9 +130,10 @@ public async Task IsNotValid_If_Diagnostics_Missing_When_OthersDiagnostics_Provi Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, PlatformResources.PlatformCommandLineDiagnosticOptionIsMissing, optionName), validateOptionsResult.ErrorMessage); } - [Arguments(true, false)] - [Arguments(false, true)] - [Arguments(false, false)] + [TestMethod] + [DataRow(true, false)] + [DataRow(false, true)] + [DataRow(false, false)] public async Task IsValid_When_Both_DiscoverTests_MinimumExpectedTests_NotProvided(bool discoverTestsSet, bool minimumExpectedTestsSet) { var provider = new PlatformCommandLineProvider(); @@ -150,6 +153,7 @@ public async Task IsValid_When_Both_DiscoverTests_MinimumExpectedTests_NotProvid Assert.IsTrue(string.IsNullOrEmpty(validateOptionsResult.ErrorMessage)); } + [TestMethod] public async Task IsInvalid_When_Both_DiscoverTests_MinimumExpectedTests_Provided() { var provider = new PlatformCommandLineProvider(); @@ -164,6 +168,7 @@ public async Task IsInvalid_When_Both_DiscoverTests_MinimumExpectedTests_Provide Assert.AreEqual(PlatformResources.PlatformCommandLineMinimumExpectedTestsIncompatibleDiscoverTests, validateOptionsResult.ErrorMessage); } + [TestMethod] public async Task IsNotValid_If_ExitOnProcess_Not_Running() { var provider = new PlatformCommandLineProvider(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs index 2b865e70f2..873a57df4f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/CommandLine/TreeNodeFilterCommandLineOptionsProviderTests.cs @@ -7,14 +7,10 @@ namespace Microsoft.Testing.Platform.UnitTests.CommandLine; -[TestGroup] -public class TreeNodeFilterCommandLineOptionsProviderTests : TestBase +[TestClass] +public sealed class TreeNodeFilterCommandLineOptionsProviderTests { - public TreeNodeFilterCommandLineOptionsProviderTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public async Task TreenodeFilter_AlwaysValid() { var provider = new TreeNodeFilterCommandLineOptionsProvider(new TestExtension()); @@ -24,6 +20,7 @@ public async Task TreenodeFilter_AlwaysValid() Assert.IsTrue(validateOptionsResult.IsValid); } + [TestMethod] public async Task CommandLineOptions_AlwaysValid() { var provider = new TreeNodeFilterCommandLineOptionsProvider(new TestExtension()); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs index 7d41698231..77373844a9 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/AggregatedConfigurationTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Helpers; @@ -13,21 +11,24 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class AggregatedConfigurationTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class AggregatedConfigurationTests { private const string ExpectedPath = "a/b/c"; private readonly Mock _testApplicationModuleInfoMock = new(); private readonly Mock _fileSystemMock = new(); - [Arguments(PlatformConfigurationConstants.PlatformResultDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] - public void IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string key) => Assert.IsNull(new AggregatedConfiguration([], _testApplicationModuleInfoMock.Object, _fileSystemMock.Object)[key]); - - [Arguments(PlatformConfigurationConstants.PlatformResultDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] + [TestMethod] + [DataRow(PlatformConfigurationConstants.PlatformResultDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] + public void IndexerTest_DirectoryNotSetAndNoConfigurationProviders_DirectoryIsNull(string key) + => Assert.IsNull(new AggregatedConfiguration([], _testApplicationModuleInfoMock.Object, _fileSystemMock.Object)[key]); + + [TestMethod] + [DataRow(PlatformConfigurationConstants.PlatformResultDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] public void IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNull(string key) { Mock mockProvider = new(); @@ -36,15 +37,17 @@ public void IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_Director Assert.IsNull(aggregatedConfiguration[key]); } - [Arguments(PlatformConfigurationConstants.PlatformResultDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] + [TestMethod] + [DataRow(PlatformConfigurationConstants.PlatformResultDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] public void IndexerTest_DirectoryNotSetButConfigurationProvidersPresent_DirectoryIsNotNull(string key) { AggregatedConfiguration aggregatedConfiguration = new([new FakeConfigurationProvider(ExpectedPath)], _testApplicationModuleInfoMock.Object, _fileSystemMock.Object); Assert.AreEqual(ExpectedPath, aggregatedConfiguration[key]); } + [TestMethod] public void IndexerTest_ResultDirectorySet_DirectoryIsNotNull() { AggregatedConfiguration aggregatedConfiguration = new([], _testApplicationModuleInfoMock.Object, _fileSystemMock.Object); @@ -53,6 +56,7 @@ public void IndexerTest_ResultDirectorySet_DirectoryIsNotNull() Assert.AreEqual(ExpectedPath, aggregatedConfiguration[PlatformConfigurationConstants.PlatformResultDirectory]); } + [TestMethod] public void IndexerTest_CurrentWorkingDirectorySet_DirectoryIsNotNull() { AggregatedConfiguration aggregatedConfiguration = new([], _testApplicationModuleInfoMock.Object, _fileSystemMock.Object); @@ -61,6 +65,7 @@ public void IndexerTest_CurrentWorkingDirectorySet_DirectoryIsNotNull() Assert.AreEqual(ExpectedPath, aggregatedConfiguration[PlatformConfigurationConstants.PlatformCurrentWorkingDirectory]); } + [TestMethod] public void IndexerTest_TestHostWorkingDirectorySet_DirectoryIsNotNull() { AggregatedConfiguration aggregatedConfiguration = new([], _testApplicationModuleInfoMock.Object, _fileSystemMock.Object); @@ -69,6 +74,7 @@ public void IndexerTest_TestHostWorkingDirectorySet_DirectoryIsNotNull() Assert.AreEqual(ExpectedPath, aggregatedConfiguration[PlatformConfigurationConstants.PlatformTestHostWorkingDirectory]); } + [TestMethod] public async ValueTask CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDirectoryFromCommandLineProvider() { Mock mockTestApplicationModuleInfo = new(); @@ -90,6 +96,7 @@ await aggregatedConfiguration.CheckTestResultsDirectoryOverrideAndCreateItAsync( Assert.AreEqual("a" + Path.DirectorySeparatorChar + "b", aggregatedConfiguration[PlatformConfigurationConstants.PlatformCurrentWorkingDirectory]); } + [TestMethod] public async ValueTask CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDirectoryFromStore() { Mock mockTestApplicationModuleInfo = new(); @@ -111,6 +118,7 @@ public async ValueTask CheckTestResultsDirectoryOverrideAndCreateItAsync_Results Assert.AreEqual("a" + Path.DirectorySeparatorChar + "b", aggregatedConfiguration[PlatformConfigurationConstants.PlatformCurrentWorkingDirectory]); } + [TestMethod] public async ValueTask CheckTestResultsDirectoryOverrideAndCreateItAsync_ResultsDirectoryIsNull_GetDefaultDirectory() { Mock mockTestApplicationModuleInfo = new(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs index ead1f42c3d..5e2b7f22c2 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationExtensionsTests.cs @@ -7,15 +7,10 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class ConfigurationExtensionsTests : TestBase +[TestClass] +public sealed class ConfigurationExtensionsTests { - public ConfigurationExtensionsTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - - private string GetActualValueFromConfiguration(IConfiguration configuration, string key) => key switch + private static string GetActualValueFromConfiguration(IConfiguration configuration, string key) => key switch { PlatformConfigurationConstants.PlatformResultDirectory => configuration.GetTestResultDirectory(), PlatformConfigurationConstants.PlatformCurrentWorkingDirectory => configuration.GetCurrentWorkingDirectory(), @@ -23,9 +18,10 @@ public ConfigurationExtensionsTests(ITestExecutionContext testExecutionContext) _ => throw new ArgumentException("Unsupported key."), }; - [Arguments(PlatformConfigurationConstants.PlatformResultDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] + [TestMethod] + [DataRow(PlatformConfigurationConstants.PlatformResultDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] public void ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string key) { string expectedPath = Path.Combine("a", "b", "c"); @@ -38,9 +34,10 @@ public void ConfigurationExtensions_TestedMethod_ReturnsExpectedPath(string key) Assert.AreEqual(expectedPath, GetActualValueFromConfiguration(configuration.Object, key)); } - [Arguments(PlatformConfigurationConstants.PlatformResultDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] - [Arguments(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] + [TestMethod] + [DataRow(PlatformConfigurationConstants.PlatformResultDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformCurrentWorkingDirectory)] + [DataRow(PlatformConfigurationConstants.PlatformTestHostWorkingDirectory)] public void ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(string key) { Mock configuration = new(); @@ -48,6 +45,6 @@ public void ConfigurationExtensions_TestedMethod_ThrowsArgumentNullException(str .Setup(configuration => configuration[key]) .Returns(value: null); - Assert.Throws(() => GetActualValueFromConfiguration(configuration.Object, key)); + Assert.ThrowsException(() => GetActualValueFromConfiguration(configuration.Object, key)); } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs index 8b516c7269..5d73e93c9b 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/ConfigurationManagerTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Helpers; @@ -13,19 +11,19 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class ConfigurationManagerTests : TestBase +[TestClass] +public sealed class ConfigurationManagerTests { private readonly ServiceProvider _serviceProvider; - public ConfigurationManagerTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public ConfigurationManagerTests() { _serviceProvider = new(); _serviceProvider.AddService(new SystemFileSystem()); } - [ArgumentsProvider(nameof(GetConfigurationValueFromJsonData))] + [TestMethod] + [DynamicData(nameof(GetConfigurationValueFromJsonData), DynamicDataSourceType.Method)] public async ValueTask GetConfigurationValueFromJson(string jsonFileConfig, string key, string? result) { Mock fileSystem = new(); @@ -56,6 +54,7 @@ public async ValueTask GetConfigurationValueFromJson(string jsonFileConfig, stri yield return ("{\"platformOptions\": { \"Array\" : [ {\"Key\" : \"Value\"} , {\"Key\" : 3} ] } }", "platformOptions:Array:1:Key", "3"); } + [TestMethod] public async ValueTask InvalidJson_Fail() { Mock fileSystem = new(); @@ -65,10 +64,11 @@ public async ValueTask InvalidJson_Fail() ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo); configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); - await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); + await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); } - [ArgumentsProvider(nameof(GetConfigurationValueFromJsonData))] + [TestMethod] + [DynamicData(nameof(GetConfigurationValueFromJsonData), DynamicDataSourceType.Method)] public async ValueTask GetConfigurationValueFromJsonWithFileLoggerProvider(string jsonFileConfig, string key, string? result) { byte[] bytes = Encoding.UTF8.GetBytes(jsonFileConfig); @@ -97,6 +97,7 @@ public async ValueTask GetConfigurationValueFromJsonWithFileLoggerProvider(strin loggerMock.Verify(x => x.LogAsync(LogLevel.Trace, It.IsAny(), null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask BuildAsync_EmptyConfigurationSources_ThrowsException() { CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); @@ -104,6 +105,7 @@ public async ValueTask BuildAsync_EmptyConfigurationSources_ThrowsException() await Assert.ThrowsAsync(() => configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), Array.Empty()))); } + [TestMethod] public async ValueTask BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsException() { Mock mockConfigurationSource = new(); @@ -118,6 +120,7 @@ public async ValueTask BuildAsync_ConfigurationSourcesNotEnabledAsync_ThrowsExce mockConfigurationSource.Verify(x => x.IsEnabledAsync(), Times.Once); } + [TestMethod] public async ValueTask BuildAsync_ConfigurationSourceIsAsyncInitializableExtension_InitializeAsyncIsCalled() { Mock mockConfigurationProvider = new(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs index e1cad5c9e3..484bbc5890 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/CountDownEventTests.cs @@ -5,14 +5,10 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class CountDownEventTests : TestBase +[TestClass] +public sealed class CountDownEventTests { - public CountDownEventTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public async Task CountDownEvent_WaitAsync_Succeeded() { CountdownEvent countdownEvent = new(3); @@ -36,6 +32,7 @@ public async Task CountDownEvent_WaitAsync_Succeeded() Assert.IsTrue(await waiter1); } + [TestMethod] public async Task CountDownEvent_WaitAsyncCanceled_Succeeded() { CountdownEvent countdownEvent = new(1); @@ -46,6 +43,7 @@ public async Task CountDownEvent_WaitAsyncCanceled_Succeeded() await Assert.ThrowsAsync(async () => await waiter); } + [TestMethod] public async Task CountDownEvent_WaitAsyncCanceledByTimeout_Succeeded() { CountdownEvent countdownEvent = new(1); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs index bba20c5da7..d6d0daaa03 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/SystemAsyncMonitorTests.cs @@ -1,20 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.Testing.Platform.Helpers; namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class SystemAsyncMonitorTests : TestBase +[TestClass] +public sealed class SystemAsyncMonitorTests { - public SystemAsyncMonitorTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public async Task AsyncMonitor_ShouldCorrectlyLock() { var asyncSystemMonitor = (SystemAsyncMonitor)new SystemMonitorAsyncFactory().Create(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs index d857372c67..e1c27f56f8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TaskExtensionsTests.cs @@ -5,27 +5,29 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class TaskExtensionsTests : TestBase +[TestClass] +public sealed class TaskExtensionsTests { - public TaskExtensionsTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - - public async Task TimeoutAfterAsync_Succeeds() => await Assert.ThrowsAsync(async () - => await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync(TimeSpan.FromSeconds(2))); + [TestMethod] + public async Task TimeoutAfterAsync_Succeeds() + => await Assert.ThrowsAsync(async () => + await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync(TimeSpan.FromSeconds(2))); - public async Task TimeoutAfterAsync_CancellationToken_Succeeds() => await Assert.ThrowsAsync(async () => - await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync( - TimeSpan.FromSeconds(30), - new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token)); + [TestMethod] + public async Task TimeoutAfterAsync_CancellationToken_Succeeds() + => await Assert.ThrowsAsync(async () => + await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync( + TimeSpan.FromSeconds(30), + new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token)); - public async Task TimeoutAfterAsync_CancellationTokenNone_Succeeds() => await Assert.ThrowsAsync(async () => - await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync( - TimeSpan.FromSeconds(2), - CancellationToken.None)); + [TestMethod] + public async Task TimeoutAfterAsync_CancellationTokenNone_Succeeds() + => await Assert.ThrowsAsync(async () => + await Task.Delay(TimeSpan.FromSeconds(60)).TimeoutAfterAsync( + TimeSpan.FromSeconds(2), + CancellationToken.None)); + [TestMethod] public async Task CancellationAsync_Cancellation_Succeeds() { CancellationTokenSource cancellationTokenSource = new(); @@ -38,6 +40,7 @@ public async Task CancellationAsync_Cancellation_Succeeds() Assert.AreEqual(cancelToken, exception.CancellationToken); } + [TestMethod] public async Task CancellationAsync_CancellationWithArgument_Succeeds() { CancellationTokenSource cancellationTokenSource = new(); @@ -50,6 +53,7 @@ public async Task CancellationAsync_CancellationWithArgument_Succeeds() Assert.AreEqual(cancelToken, exception.CancellationToken); } + [TestMethod] public async Task CancellationAsync_NonCanceled_Succeeds() { CancellationTokenSource cancellationTokenSource = new(); @@ -57,54 +61,59 @@ public async Task CancellationAsync_NonCanceled_Succeeds() await Task.Delay(TimeSpan.FromSeconds(1), cancelToken).WithCancellationAsync(cancelToken); } + [TestMethod] public async Task CancellationAsync_NonCanceledWithArgument_Succeeds() { CancellationTokenSource cancellationTokenSource = new(); Assert.AreEqual("Hello", await DoSomething().WithCancellationAsync(cancellationTokenSource.Token)); } - public async Task CancellationAsync_ObserveException_Succeeds() => await RetryHelper.RetryAsync( - async () => - { - ManualResetEvent waitException = new(false); - await Assert.ThrowsAsync(async () - => await Task.Run(async () => - { - await Task.Delay(TimeSpan.FromSeconds(10)); - waitException.Set(); - throw new InvalidOperationException(); - }).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); + [TestMethod] + public async Task CancellationAsync_ObserveException_Succeeds() + => await RetryHelper.RetryAsync( + async () => + { + ManualResetEvent waitException = new(false); + await Assert.ThrowsAsync(async () + => await Task.Run(async () => + { + await Task.Delay(TimeSpan.FromSeconds(10)); + waitException.Set(); + throw new InvalidOperationException(); + }).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); - waitException.WaitOne(); - await Task.Delay(TimeSpan.FromSeconds(4)); - }, 3, TimeSpan.FromSeconds(3), _ => true); + waitException.WaitOne(); + await Task.Delay(TimeSpan.FromSeconds(4)); + }, 3, TimeSpan.FromSeconds(3), _ => true); - public async Task CancellationAsyncWithReturnValue_ObserveException_Succeeds() => await RetryHelper.RetryAsync( - async () => - { - ManualResetEvent waitException = new(false); - await Assert.ThrowsAsync(async () - => await Task.Run(async () => + [TestMethod] + public async Task CancellationAsyncWithReturnValue_ObserveException_Succeeds() + => await RetryHelper.RetryAsync( + async () => + { + ManualResetEvent waitException = new(false); + await Assert.ThrowsAsync(async () + => await Task.Run(async () => + { + await Task.Delay(TimeSpan.FromSeconds(10)); + try + { + return 2; + } + finally { - await Task.Delay(TimeSpan.FromSeconds(10)); - try - { - return 2; - } - finally - { - waitException.Set(); + waitException.Set(); #pragma warning disable CA2219 // Do not raise exceptions in finally clauses - throw new InvalidOperationException(); + throw new InvalidOperationException(); #pragma warning restore CA2219 // Do not raise exceptions in finally clauses - } - }).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); + } + }).WithCancellationAsync(new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token)); - waitException.WaitOne(); - await Task.Delay(TimeSpan.FromSeconds(4)); - }, 3, TimeSpan.FromSeconds(3), _ => true); + waitException.WaitOne(); + await Task.Delay(TimeSpan.FromSeconds(4)); + }, 3, TimeSpan.FromSeconds(3), _ => true); - private async Task DoSomething() + private static async Task DoSomething() { await Task.Delay(TimeSpan.FromSeconds(1)); return "Hello"; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs index 2020e0fbd4..f58fedb461 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Helpers/TestCommandLineOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.CommandLine; namespace Microsoft.Testing.Platform.UnitTests.Helpers; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs index 591542bc78..fea23a67d7 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs @@ -11,14 +11,15 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class IPCTests : TestBase +[TestClass] +public sealed class IPCTests { - private readonly ITestExecutionContext _testExecutionContext; + private readonly TestContext _testContext; - public IPCTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) => _testExecutionContext = testExecutionContext; + public IPCTests(TestContext testContext) + => _testContext = testContext; + [TestMethod] public async Task SingleConnectionNamedPipeServer_MultipleConnection_Fails() { PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); @@ -39,9 +40,9 @@ public async Task SingleConnectionNamedPipeServer_MultipleConnection_Fails() new SystemEnvironment(), new Mock().Object, new SystemTask(), - _testExecutionContext.CancellationToken); + _testContext.CancellationTokenSource.Token); - await singleConnectionNamedPipeServer.WaitConnectionAsync(_testExecutionContext.CancellationToken); + await singleConnectionNamedPipeServer.WaitConnectionAsync(_testContext.CancellationTokenSource.Token); openedPipe.Add(singleConnectionNamedPipeServer); } } @@ -53,7 +54,7 @@ public async Task SingleConnectionNamedPipeServer_MultipleConnection_Fails() }); NamedPipeClient namedPipeClient1 = new(pipeNameDescription.Name); - await namedPipeClient1.ConnectAsync(_testExecutionContext.CancellationToken); + await namedPipeClient1.ConnectAsync(_testContext.CancellationTokenSource.Token); waitException.Wait(); Assert.AreEqual(1, openedPipe.Count); @@ -82,6 +83,7 @@ public async Task SingleConnectionNamedPipeServer_MultipleConnection_Fails() pipeNameDescription.Dispose(); } + [TestMethod] public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded() { Queue receivedMessages = new(); @@ -103,9 +105,9 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ manualResetEventSlim.Set(); break; } - catch (OperationCanceledException ct) when (ct.CancellationToken == _testExecutionContext.CancellationToken) + catch (OperationCanceledException ct) when (ct.CancellationToken == _testContext.CancellationTokenSource.Token) { - throw new OperationCanceledException("SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded cancellation during connect, _testExecutionContext.CancellationToken"); + throw new OperationCanceledException("SingleConnectionNamedPipeServer_RequestReplySerialization_Succeeded cancellation during connect, testContext.CancellationTokenSource.Token"); } catch (OperationCanceledException) { @@ -166,7 +168,8 @@ public async Task SingleConnectionNamedPipeServer_RequestReplySerialization_Succ pipeNameDescription.Dispose(); } - public async Task ConnectionNamedPipeServer_MultipleConnection_Succeded() + [TestMethod] + public async Task ConnectionNamedPipeServer_MultipleConnection_Succeeds() { PipeNameDescription pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); @@ -174,17 +177,16 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeded() for (int i = 0; i < 3; i++) { pipes.Add(new( - pipeNameDescription, - async _ => await Task.FromResult(VoidResponse.CachedInstance), - new SystemEnvironment(), - new Mock().Object, - new SystemTask(), - maxNumberOfServerInstances: 3, - _testExecutionContext.CancellationToken)); + pipeNameDescription, + async _ => await Task.FromResult(VoidResponse.CachedInstance), + new SystemEnvironment(), + new Mock().Object, + new SystemTask(), + maxNumberOfServerInstances: 3, + _testContext.CancellationTokenSource.Token)); } -#pragma warning disable CA1806 // Do not ignore method results - IOException exception = Assert.Throws(() => + IOException exception = Assert.ThrowsException(() => new NamedPipeServer( pipeNameDescription, async _ => await Task.FromResult(VoidResponse.CachedInstance), @@ -192,9 +194,8 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeded() new Mock().Object, new SystemTask(), maxNumberOfServerInstances: 3, - _testExecutionContext.CancellationToken)); - Assert.Contains("All pipe instances are busy.", exception.Message); -#pragma warning restore CA1806 // Do not ignore method results + _testContext.CancellationTokenSource.Token)); + StringAssert.Contains(exception.Message, "All pipe instances are busy."); List waitConnectionTask = new(); int connectionCompleted = 0; @@ -202,7 +203,7 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeded() { waitConnectionTask.Add(Task.Run(async () => { - await namedPipeServer.WaitConnectionAsync(_testExecutionContext.CancellationToken); + await namedPipeServer.WaitConnectionAsync(_testContext.CancellationTokenSource.Token); Interlocked.Increment(ref connectionCompleted); })); } @@ -212,7 +213,7 @@ public async Task ConnectionNamedPipeServer_MultipleConnection_Succeded() { NamedPipeClient namedPipeClient = new(pipeNameDescription.Name); connectedClients.Add(namedPipeClient); - await namedPipeClient.ConnectAsync(_testExecutionContext.CancellationToken); + await namedPipeClient.ConnectAsync(_testContext.CancellationTokenSource.Token); } await Task.WhenAll(waitConnectionTask.ToArray()); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs index 9928d95bff..aae141f46a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs @@ -6,14 +6,10 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class ProtocolTests : TestBase +[TestClass] +public sealed class ProtocolTests { - public ProtocolTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void TestResultMessagesSerializeDeserialize() { var success = new SuccessfulTestResultMessage("uid", "displayName", 1, 100, "reason", "standardOutput", "errorOutput", "sessionUid"); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs index b26ea68c65..aaeb9dc6d0 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/FileLoggerTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; @@ -11,8 +8,8 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class FileLoggerTests : TestBase, IDisposable +[TestClass] +public sealed class FileLoggerTests : IDisposable { private const string LogFolder = "aaa"; private const string LogPrefix = "bbb"; @@ -31,10 +28,9 @@ public class FileLoggerTests : TestBase, IDisposable private readonly Mock _mockFileSystem = new(); private readonly Mock _mockStream = new(); private readonly Mock _mockFileStreamFactory = new(); - private readonly MemoryStream _memoryStream; + private readonly CustomMemoryStream _memoryStream; - public FileLoggerTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public FileLoggerTests() { _mockStream.Setup(x => x.Dispose()); #if NETCOREAPP @@ -42,15 +38,16 @@ public FileLoggerTests(ITestExecutionContext testExecutionContext) #endif _mockStream.Setup(x => x.Name).Returns(FileName); - _memoryStream = new MemoryStream(); + _memoryStream = new CustomMemoryStream(); _mockStream.Setup(x => x.Stream).Returns(_memoryStream); } + [TestMethod] public void Write_IfMalformedUTF8_ShouldNotCrash() { using TempDirectory tempDirectory = new(nameof(Write_IfMalformedUTF8_ShouldNotCrash)); using FileLogger fileLogger = new( - new FileLoggerOptions(tempDirectory.Path, "Test", fileName: null, true), + new FileLoggerOptions(tempDirectory.Path, "Test", fileName: null), LogLevel.Trace, new SystemClock(), new SystemTask(), @@ -60,6 +57,7 @@ public void Write_IfMalformedUTF8_ShouldNotCrash() fileLogger.Log(LogLevel.Trace, "\uD886", null, LoggingExtensions.Formatter, "Category"); } + [TestMethod] public void FileLogger_NullFileSyncFlush_FileStreamCreated() { // First return is to compute the expected file name. It's ok that first time is greater @@ -98,6 +96,7 @@ public void FileLogger_NullFileSyncFlush_FileStreamCreated() Assert.AreEqual(expectedFileName, fileLoggerName); } + [TestMethod] public void FileLogger_NullFileSyncFlush_FileStreamCreationThrows() { // First return is to compute the expected file name. It's ok that first time is greater @@ -111,7 +110,7 @@ public void FileLogger_NullFileSyncFlush_FileStreamCreationThrows() .Throws() .Returns(_mockStream.Object); - Assert.Throws(() => _ = new FileLogger( + Assert.ThrowsException(() => _ = new FileLogger( new(LogFolder, LogPrefix, fileName: null, syncFlush: true), LogLevel.Trace, _mockClock.Object, @@ -121,10 +120,11 @@ public void FileLogger_NullFileSyncFlush_FileStreamCreationThrows() _mockFileStreamFactory.Object)); } - [Arguments(true, true)] - [Arguments(true, false)] - [Arguments(false, true)] - [Arguments(false, false)] + [DataRow(true, true)] + [DataRow(true, false)] + [DataRow(false, true)] + [DataRow(false, false)] + [TestMethod] public void FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool syncFlush, bool fileExists) { string expectedPath = Path.Combine(LogFolder, FileName); @@ -152,7 +152,8 @@ public void FileLogger_ValidFileName_FileStreamCreatedSuccessfully(bool syncFlus Assert.AreEqual(FileName, fileLoggerName); } - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] + [TestMethod] + [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers), DynamicDataSourceType.Method)] public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(LogLevel defaultLogLevel, LogLevel currentLogLevel) { _mockFileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); @@ -172,12 +173,17 @@ public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt if (LogTestHelpers.IsLogEnabled(defaultLogLevel, currentLogLevel)) { - if (_memoryStream.Length == 0) + await _memoryStream.FlushAsync(); + int iteration = 0; + while (_memoryStream.Length == 0 && iteration < 10) { - await Task.Delay(1000); + iteration++; + await Task.Delay(200); } await _memoryStream.FlushAsync(); + + _mockConsole.Verify(x => x.WriteLine(It.IsAny()), Times.Never); Assert.AreEqual($"[00:00:00.000 Test - {currentLogLevel}] Message{Environment.NewLine}", Encoding.Default.GetString(_memoryStream.ToArray())); } else @@ -186,31 +192,30 @@ public async Task Log_WhenSyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt } } - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] - public async Task Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(LogLevel defaultLogLevel, LogLevel currentLogLevel) + [TestMethod] + [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + public void Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsIt(LogLevel defaultLogLevel, LogLevel currentLogLevel) { _mockFileSystem.Setup(x => x.Exists(It.IsAny())).Returns(false); _mockFileStreamFactory .Setup(x => x.Create(It.IsAny(), FileMode.CreateNew, FileAccess.Write, FileShare.Read)) .Returns(_mockStream.Object); - using FileLogger fileLogger = new( + // Ensures that the async flush is completed before the file is read + using (FileLogger fileLogger = new( new(LogFolder, LogPrefix, fileName: FileName, syncFlush: false), defaultLogLevel, _mockClock.Object, new SystemTask(), _mockConsole.Object, _mockFileSystem.Object, - _mockFileStreamFactory.Object); - fileLogger.Log(currentLogLevel, Message, null, Formatter, Category); + _mockFileStreamFactory.Object)) + { + fileLogger.Log(currentLogLevel, Message, null, Formatter, Category); + } if (LogTestHelpers.IsLogEnabled(defaultLogLevel, currentLogLevel)) { - if (_memoryStream.Length == 0) - { - await Task.Delay(1000); - } - Assert.AreEqual($"0001-01-01T00:00:00.0000000+00:00 Test {currentLogLevel.ToString().ToUpperInvariant()} Message{Environment.NewLine}", Encoding.Default.GetString(_memoryStream.ToArray())); } else @@ -219,5 +224,25 @@ public async Task Log_WhenAsyncFlush_StreamWriterIsCalledOnlyWhenLogLevelAllowsI } } - public void Dispose() => _memoryStream.Dispose(); + void IDisposable.Dispose() + => _memoryStream.Dispose(); + + private sealed class CustomMemoryStream : MemoryStream + { + private bool _shouldDispose; + + [SuppressMessage("Usage", "CA2215:Dispose methods should call base class dispose", Justification = "Don't dispose")] + protected override void Dispose(bool disposing) + { + if (_shouldDispose) + { + base.Dispose(disposing); + } + else + { + _shouldDispose = true; + return; + } + } + } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs index e0dc034294..571f312dff 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LogTestHelpers.cs @@ -16,6 +16,9 @@ public static IEnumerable GetLogLevels() => typeof(LogLevel).GetEnumValues().Cast(); #endif + public static IEnumerable GetLogLevelsForDynamicData() + => GetLogLevels().Select(x => new object[] { x }); + public static IEnumerable<(LogLevel DefaultLevel, LogLevel CurrentLevel)> GetLogLevelCombinations() { List<(LogLevel, LogLevel)> logLevelCombinations = new(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs index 8322b1c830..d257ecf3d4 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerFactoryTests.cs @@ -8,16 +8,15 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class LoggerFactoryTests : TestBase +[TestClass] +public sealed class LoggerFactoryTests { private readonly Mock _mockLogger = new(); private readonly Mock _mockMonitor = new(); private readonly Mock _mockLoggerProvider = new(); private readonly ILoggerProvider[] _loggerProviders; - public LoggerFactoryTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public LoggerFactoryTests() { _mockMonitor.Setup(x => x.Lock(It.IsAny())).Returns(new Mock().Object); _mockLoggerProvider.Setup(x => x.CreateLogger(It.IsAny())).Returns(_mockLogger.Object); @@ -28,6 +27,7 @@ public LoggerFactoryTests(ITestExecutionContext testExecutionContext) ]; } + [TestMethod] public void LoggerFactory_LoggerCreatedOnlyOnce() { using LoggerFactory loggerFactory = new(_loggerProviders, LogLevel.Information, _mockMonitor.Object); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs index d4be4a9dc2..5818e30477 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggerTests.cs @@ -1,16 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; - using Microsoft.Testing.Platform.Logging; using Moq; namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class LoggerTests : TestBase +[TestClass] +public sealed class LoggerTests { private static readonly Func Formatter = (state, exception) => @@ -20,8 +18,7 @@ public class LoggerTests : TestBase private readonly Exception _exception = new("TestException"); private readonly Mock _mockLogger = new(); - public LoggerTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public LoggerTests() { _mockLogger.Setup(x => x.Log(It.IsAny(), It.IsAny(), It.IsAny(), Formatter)); _mockLogger.Setup(x => x.LogAsync(It.IsAny(), It.IsAny(), It.IsAny(), Formatter)); @@ -38,14 +35,16 @@ private Logger CreateLogger(LogLevel logLevel) return new Logger(mockLoggerFactory.Object); } - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] + [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + [TestMethod] public void Logger_CheckEnabled(LogLevel defaultLogLevel, LogLevel currentLogLevel) { Logger logger = CreateLogger(defaultLogLevel); Assert.AreEqual(logger.IsEnabled(currentLogLevel), LogTestHelpers.IsLogEnabled(defaultLogLevel, currentLogLevel)); } - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] + [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + [TestMethod] public void Logger_Log_FormattedStringIsCorrect(LogLevel defaultLogLevel, LogLevel currentLogLevel) { Logger logger = CreateLogger(defaultLogLevel); @@ -56,7 +55,8 @@ public void Logger_Log_FormattedStringIsCorrect(LogLevel defaultLogLevel, LogLev LogTestHelpers.GetExpectedLogCallTimes(defaultLogLevel, currentLogLevel)); } - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] + [DynamicData(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + [TestMethod] public async ValueTask Logger_LogAsync_FormattedStringIsCorrect(LogLevel defaultLogLevel, LogLevel currentLogLevel) { Logger logger = CreateLogger(defaultLogLevel); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs index ac6b5d61bc..194d2c5e80 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/LoggingExtensionsTests.cs @@ -7,8 +7,8 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class LoggingExtensionsTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class LoggingExtensionsTest { private const string Message = "Test"; private readonly Exception _exception = new("TestException"); @@ -16,6 +16,7 @@ public class LoggingExtensionsTests(ITestExecutionContext testExecutionContext) // Use strict mock to ensure that only the expected methods are called private readonly Mock _mockLogger = new(MockBehavior.Strict); + [TestMethod] public void LoggerExtensions_LogTrace_CallsLogWithLogLevelTrace() { _mockLogger.Setup(x => x.Log(LogLevel.Trace, Message, null, LoggingExtensions.Formatter)); @@ -23,6 +24,7 @@ public void LoggerExtensions_LogTrace_CallsLogWithLogLevelTrace() _mockLogger.Verify(x => x.Log(LogLevel.Trace, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogDebug_CallsLogWithLogLevelDebug() { _mockLogger.Setup(x => x.Log(LogLevel.Debug, Message, null, LoggingExtensions.Formatter)); @@ -30,6 +32,7 @@ public void LoggerExtensions_LogDebug_CallsLogWithLogLevelDebug() _mockLogger.Verify(x => x.Log(LogLevel.Debug, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogInformation_CallsLogWithLogLevelInformation() { _mockLogger.Setup(x => x.Log(LogLevel.Information, Message, null, LoggingExtensions.Formatter)); @@ -37,6 +40,7 @@ public void LoggerExtensions_LogInformation_CallsLogWithLogLevelInformation() _mockLogger.Verify(x => x.Log(LogLevel.Information, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogWarning_CallsLogWithLogLevelWarning() { _mockLogger.Setup(x => x.Log(LogLevel.Warning, Message, null, LoggingExtensions.Formatter)); @@ -44,6 +48,7 @@ public void LoggerExtensions_LogWarning_CallsLogWithLogLevelWarning() _mockLogger.Verify(x => x.Log(LogLevel.Warning, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogError_CallsLogWithLogLevelError() { _mockLogger.Setup(x => x.Log(LogLevel.Error, Message, null, LoggingExtensions.Formatter)); @@ -51,6 +56,7 @@ public void LoggerExtensions_LogError_CallsLogWithLogLevelError() _mockLogger.Verify(x => x.Log(LogLevel.Error, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndMessageAndException() { _mockLogger.Setup(x => x.Log(LogLevel.Error, Message, _exception, LoggingExtensions.Formatter)); @@ -58,6 +64,7 @@ public void LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndMessageAndExce _mockLogger.Verify(x => x.Log(LogLevel.Error, Message, _exception, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndException() { _mockLogger.Setup(x => x.Log(LogLevel.Error, _exception.ToString(), null, LoggingExtensions.Formatter)); @@ -65,6 +72,7 @@ public void LoggerExtensions_LogError_CallsLogWithLogLevelErrorAndException() _mockLogger.Verify(x => x.Log(LogLevel.Error, _exception.ToString(), null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public void LoggerExtensions_LogCritical_CallsLogWithLogLevelCritical() { _mockLogger.Setup(x => x.Log(LogLevel.Critical, Message, null, LoggingExtensions.Formatter)); @@ -72,6 +80,7 @@ public void LoggerExtensions_LogCritical_CallsLogWithLogLevelCritical() _mockLogger.Verify(x => x.Log(LogLevel.Critical, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogTraceAsync_CallsLogAsyncWithLogLevelTrace() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Trace, Message, null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -79,6 +88,7 @@ public async ValueTask LoggerExtensions_LogTraceAsync_CallsLogAsyncWithLogLevelT _mockLogger.Verify(x => x.LogAsync(LogLevel.Trace, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogDebugAsync_CallsLogAsyncWithLogLevelDebug() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Debug, Message, null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -86,6 +96,7 @@ public async ValueTask LoggerExtensions_LogDebugAsync_CallsLogAsyncWithLogLevelD _mockLogger.Verify(x => x.LogAsync(LogLevel.Debug, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogInformationAsync_CallsLogAsyncWithLogLevelInformation() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Information, Message, null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -93,6 +104,7 @@ public async ValueTask LoggerExtensions_LogInformationAsync_CallsLogAsyncWithLog _mockLogger.Verify(x => x.LogAsync(LogLevel.Information, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogWarningAsync_CallsLogAsyncWithLogLevelWarning() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Warning, Message, null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -100,6 +112,7 @@ public async ValueTask LoggerExtensions_LogWarningAsync_CallsLogAsyncWithLogLeve _mockLogger.Verify(x => x.LogAsync(LogLevel.Warning, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelError() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Error, Message, null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -107,6 +120,7 @@ public async ValueTask LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelE _mockLogger.Verify(x => x.LogAsync(LogLevel.Error, Message, null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelErrorAndMessageAndException() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Error, Message, _exception, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -114,6 +128,7 @@ public async ValueTask LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelE _mockLogger.Verify(x => x.LogAsync(LogLevel.Error, Message, _exception, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelErrorAndException() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Error, _exception.ToString(), null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); @@ -121,6 +136,7 @@ public async ValueTask LoggerExtensions_LogErrorAsync_CallsLogAsyncWithLogLevelE _mockLogger.Verify(x => x.LogAsync(LogLevel.Error, _exception.ToString(), null, LoggingExtensions.Formatter), Times.Once); } + [TestMethod] public async ValueTask LoggerExtensions_LogCriticalAsync_CallsLogAsyncWithLogLevelCritical() { _mockLogger.Setup(x => x.LogAsync(LogLevel.Critical, Message, null, LoggingExtensions.Formatter)).Returns(Task.CompletedTask); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs index 1db2c78371..687b8fcfe8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/NopLoggerTests.cs @@ -5,8 +5,8 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class NopLoggerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +[TestClass] +public sealed class NopLoggerTests { private static readonly Func Formatter = (state, exception) => @@ -21,18 +21,21 @@ public class NopLoggerTests(ITestExecutionContext testExecutionContext) : TestBa private static int s_formatterCalls; - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevels), typeof(LogTestHelpers))] + [DynamicData(nameof(LogTestHelpers.GetLogLevelsForDynamicData), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + [TestMethod] public void NopLogger_CheckDisabled(LogLevel logLevel) => Assert.IsFalse(_nopLogger.IsEnabled(logLevel)); - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevels), typeof(LogTestHelpers))] + [DynamicData(nameof(LogTestHelpers.GetLogLevelsForDynamicData), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + [TestMethod] public void NopLogger_Log_NoFormatterCalls(LogLevel logLevel) { _nopLogger.Log(logLevel, Message, _exception, Formatter); Assert.AreEqual(0, s_formatterCalls); } - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevels), typeof(LogTestHelpers))] + [DynamicData(nameof(LogTestHelpers.GetLogLevelsForDynamicData), typeof(LogTestHelpers), DynamicDataSourceType.Method)] + [TestMethod] public async ValueTask NopLogger_LogAsync_NoFormatterCalls(LogLevel logLevel) { await _nopLogger.LogAsync(logLevel, Message, _exception, Formatter); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs deleted file mode 100644 index 10be06a371..0000000000 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Logging/ServerLoggerForwarderTests.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Globalization; - -using Microsoft.Testing.Platform.Extensions.Messages; -using Microsoft.Testing.Platform.Helpers; -using Microsoft.Testing.Platform.Hosts; -using Microsoft.Testing.Platform.Logging; -using Microsoft.Testing.Platform.Services; - -using Moq; - -namespace Microsoft.Testing.Platform.UnitTests; - -[TestGroup] -public class ServerLoggerForwarderTests : TestBase -{ - private const string Message = "Dummy"; - - private static readonly Func Formatter = - (state, exception) => - string.Format(CultureInfo.InvariantCulture, "{0}{1}", state, exception is not null ? $" -- {exception}" : string.Empty); - - private readonly Mock _mockServerTestHost = new(); - private readonly ServiceProvider _serviceProvider = new(); - - public ServerLoggerForwarderTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - _mockServerTestHost.Setup(x => x.PushDataAsync(It.IsAny())).Returns(Task.CompletedTask); - _serviceProvider.AddService(new SystemTask()); - _serviceProvider.AddService(_mockServerTestHost.Object); - } - - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] - public void ServerLoggerForwarder_Log(LogLevel defaultLevel, LogLevel currentLevel) - { - using (ServerLoggerForwarder serverLoggerForwarder = new(defaultLevel, new SystemTask(), _mockServerTestHost.Object)) - { - serverLoggerForwarder.Log(currentLevel, Message, null, Formatter); - } - - _mockServerTestHost.Verify(x => x.PushDataAsync(It.IsAny()), LogTestHelpers.GetExpectedLogCallTimes(defaultLevel, currentLevel)); - } - - [ArgumentsProvider(nameof(LogTestHelpers.GetLogLevelCombinations), typeof(LogTestHelpers))] - public async Task ServerLoggerForwarder_LogAsync(LogLevel defaultLevel, LogLevel currentLevel) - { - using (ServerLoggerForwarder serverLoggerForwarder = new(defaultLevel, new SystemTask(), _mockServerTestHost.Object)) - { - await serverLoggerForwarder.LogAsync(currentLevel, Message, null, Formatter); - } - - _mockServerTestHost.Verify(x => x.PushDataAsync(It.IsAny()), LogTestHelpers.GetExpectedLogCallTimes(defaultLevel, currentLevel)); - } -} diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs index 94af4c458e..2d6ba5076e 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.cs @@ -10,17 +10,13 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class AsynchronousMessageBusTests : TestBase +[TestClass] +public sealed class AsynchronousMessageBusTests { - public AsynchronousMessageBusTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public async Task UnexpectedTypePublished_ShouldFail() { - MessageBusProxy proxy = new(); + using MessageBusProxy proxy = new(); InvalidTypePublished consumer = new(proxy); AsynchronousMessageBus asynchronousMessageBus = new( [consumer], @@ -37,9 +33,10 @@ public async Task UnexpectedTypePublished_ShouldFail() await Assert.ThrowsAsync(proxy.DrainDataAsync); } + [TestMethod] public async Task DrainDataAsync_Loop_ShouldFail() { - MessageBusProxy proxy = new(); + using MessageBusProxy proxy = new(); LoopConsumerA consumerA = new(proxy); ConsumerB consumerB = new(proxy); AsynchronousMessageBus asynchronousMessageBus = new( @@ -59,7 +56,7 @@ public async Task DrainDataAsync_Loop_ShouldFail() } catch (InvalidOperationException ex) { - Assert.That(ex.Message.Contains("Publisher/Consumer loop detected during the drain after")); + StringAssert.Contains(ex.Message, "Publisher/Consumer loop detected during the drain after"); } // Prevent loop to continue @@ -67,9 +64,10 @@ public async Task DrainDataAsync_Loop_ShouldFail() consumerB.StopConsume(); } + [TestMethod] public async Task MessageBus_WhenConsumerProducesAndConsumesTheSameType_ShouldNotConsumeWhatProducedByItself() { - MessageBusProxy proxy = new(); + using MessageBusProxy proxy = new(); Consumer consumerA = new(proxy, "consumerA"); Consumer consumerB = new(proxy, "consumerB"); AsynchronousMessageBus asynchronousMessageBus = new( @@ -90,13 +88,14 @@ public async Task MessageBus_WhenConsumerProducesAndConsumesTheSameType_ShouldNo await proxy.DrainDataAsync(); // assert - Assert.AreEqual(consumerA.ConsumedData.Count, 1); - Assert.AreEqual(consumerA.ConsumedData[0], consumerBData); + Assert.AreEqual(1, consumerA.ConsumedData.Count); + Assert.AreEqual(consumerBData, consumerA.ConsumedData[0]); - Assert.AreEqual(consumerB.ConsumedData.Count, 1); - Assert.AreEqual(consumerB.ConsumedData[0], consumerAData); + Assert.AreEqual(1, consumerB.ConsumedData.Count); + Assert.AreEqual(consumerAData, consumerB.ConsumedData[0]); } + [TestMethod] public async Task Consumers_ConsumeData_ShouldNotMissAnyPayload() { int totalConsumers = Environment.ProcessorCount; diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs index 0dfb5466ae..12f4ad6b23 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/PropertyBagTests.cs @@ -5,14 +5,10 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class PropertyBagTests : TestBase +[TestClass] +public sealed class PropertyBagTests { - public PropertyBagTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void Ctors_CorrectlyInit() { PropertyBag property = new([new DummyProperty(), PassedTestNodeStateProperty.CachedInstance]); @@ -24,18 +20,21 @@ public void Ctors_CorrectlyInit() Assert.IsNotNull(property._property); } + [TestMethod] public void Ctors_With_WrongInit_ShouldFail() { - Assert.Throws(() => _ = new PropertyBag([new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance])); - Assert.Throws(() => _ = new PropertyBag(new IProperty[] { new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance }.AsEnumerable())); + Assert.ThrowsException(() => _ = new PropertyBag([new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance])); + Assert.ThrowsException(() => _ = new PropertyBag(new IProperty[] { new DummyProperty(), PassedTestNodeStateProperty.CachedInstance, PassedTestNodeStateProperty.CachedInstance }.AsEnumerable())); } + [TestMethod] public void Counts_ShouldBe_Correct() { PropertyBag property = new([new DummyProperty(), new DummyProperty(), PassedTestNodeStateProperty.CachedInstance]); Assert.AreEqual(3, property.Count); } + [TestMethod] public void AddGet_Of_TestNodeStateProperty_Succed() { PropertyBag property = new(); @@ -51,21 +50,24 @@ public void AddGet_Of_TestNodeStateProperty_Succed() Assert.AreEqual(1, property.OfType().Length); } + [TestMethod] public void Add_Of_TestNodeStateProperty_More_Than_One_Time_Fail() { PropertyBag property = new(); property.Add(PassedTestNodeStateProperty.CachedInstance); - Assert.Throws(() => property.Add(PassedTestNodeStateProperty.CachedInstance)); + Assert.ThrowsException(() => property.Add(PassedTestNodeStateProperty.CachedInstance)); } + [TestMethod] public void Add_Same_Instance_More_Times_Fail() { PropertyBag property = new(); DummyProperty dummyProperty = new(); property.Add(dummyProperty); - Assert.Throws(() => property.Add(dummyProperty)); + Assert.ThrowsException(() => property.Add(dummyProperty)); } + [TestMethod] public void Any_Should_Return_CorrectBoolean() { PropertyBag property = new(); @@ -77,6 +79,7 @@ public void Any_Should_Return_CorrectBoolean() Assert.IsTrue(property.Any()); } + [TestMethod] public void SingleOrDefault_Should_Return_CorrectObject() { PropertyBag property = new(); @@ -89,9 +92,10 @@ public void SingleOrDefault_Should_Return_CorrectObject() Assert.IsNull(property.SingleOrDefault()); property.Add(new DummyProperty()); - Assert.Throws(() => property.SingleOrDefault()); + Assert.ThrowsException(property.SingleOrDefault); } + [TestMethod] public void Single_Should_Return_CorrectObject() { PropertyBag property = new(); @@ -101,12 +105,13 @@ public void Single_Should_Return_CorrectObject() Assert.AreEqual(PassedTestNodeStateProperty.CachedInstance, property.Single()); Assert.AreEqual(prop, property.Single()); - Assert.Throws(() => property.Single()); + Assert.ThrowsException(property.Single); property.Add(new DummyProperty()); - Assert.Throws(() => property.Single()); + Assert.ThrowsException(property.Single); } + [TestMethod] public void OfType_Should_Return_CorrectObject() { PropertyBag property = new(); @@ -118,6 +123,7 @@ public void OfType_Should_Return_CorrectObject() Assert.AreEqual(2, property.OfType().Length); } + [TestMethod] public void AsEnumerable_Should_Return_CorrectItems() { PropertyBag property = new(); @@ -135,7 +141,7 @@ public void AsEnumerable_Should_Return_CorrectItems() list.Remove(prop); } - Assert.IsEmpty(list); + Assert.AreEqual(0, list.Count); list = property.AsEnumerable().ToList(); foreach (IProperty prop in property) @@ -143,16 +149,17 @@ public void AsEnumerable_Should_Return_CorrectItems() list.Remove(prop); } - Assert.IsEmpty(list); + Assert.AreEqual(0, list.Count); } + [TestMethod] public void EmptyProperties_Should_NotFail() { PropertyBag property = new(); Assert.AreEqual(0, property.Count); Assert.IsFalse(property.Any()); Assert.IsNull(property.SingleOrDefault()); - Assert.Throws(() => property.Single()); + Assert.ThrowsException(property.Single); Assert.AreEqual(0, property.OfType().Length); Assert.AreEqual(0, property.AsEnumerable().Count()); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs index 10d6c1827e..6a57d3b664 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/TestNodeUidTests.cs @@ -1,47 +1,39 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using FrameworkTestNodeUid = Microsoft.Testing.Internal.Framework.TestNodeUid; - using TestNodeUid = Microsoft.Testing.Platform.Extensions.Messages.TestNodeUid; namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class TestNodeUidTests : TestBase +[TestClass] +public sealed class TestNodeUidTests { - public TestNodeUidTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void TestNodeUid_EqualityChecks_ShouldWorkAsExpected() { TestNodeUid testNodeUid = "TestNodeUid"; string testNodeUidString = testNodeUid; Assert.AreEqual(new TestNodeUid("TestNodeUid"), testNodeUid); - Assert.IsTrue(new FrameworkTestNodeUid("TestNodeUid") == "TestNodeUid"); - Assert.IsTrue(testNodeUid == "TestNodeUid"); - Assert.IsTrue(testNodeUid != "TestNodeUid2"); - Assert.IsTrue(testNodeUidString == testNodeUid); + Assert.AreEqual("TestNodeUid", testNodeUid); + Assert.AreNotEqual("TestNodeUid2", testNodeUid); + Assert.AreEqual(testNodeUidString, testNodeUid); } + [TestMethod] public void TestNodeUid_NullValue_ShouldFail() { -#pragma warning disable CS0219 // Variable is assigned but its value is never used // This won't fail, TestNodeUid is a record (not a record struct) and assigning null is a compile time check // not a runtime check. No construction of the object is happening, so we won't throw. // Assert.Throw(() => { TestNodeUid testNode = null!; }); // Implicit conversion from a null, empty or whitespace string should throw. - Assert.Throws(() => { TestNodeUid testNode = (string)null!; }); - Assert.Throws(() => { TestNodeUid testNode = string.Empty; }); - Assert.Throws(() => { TestNodeUid testNode = " "; }); + Assert.ThrowsException(() => { TestNodeUid testNode = (string)null!; }); + Assert.ThrowsException(() => { TestNodeUid testNode = string.Empty; }); + Assert.ThrowsException(() => { TestNodeUid testNode = " "; }); // Providing null, empty, or whitespace id should throw. - Assert.Throws(() => { TestNodeUid testNode = new(null!); }); - Assert.Throws(() => { TestNodeUid testNode = new(string.Empty); }); - Assert.Throws(() => { TestNodeUid testNode = new(" "); }); -#pragma warning restore CS0219 // Variable is assigned but its value is never used + Assert.ThrowsException(() => { TestNodeUid testNode = new(null!); }); + Assert.ThrowsException(() => { TestNodeUid testNode = new(string.Empty); }); + Assert.ThrowsException(() => { TestNodeUid testNode = new(" "); }); } } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj index 5ff2e65fc9..770d8063a6 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Microsoft.Testing.Platform.UnitTests.csproj @@ -1,9 +1,9 @@ - + $(MicrosoftTestingTargetFrameworks);net462 false - true + true Exe @@ -21,10 +21,6 @@ - - - - diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TargetFrameworkParserTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TargetFrameworkParserTests.cs index 1f3ac7a5b2..d597da8847 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TargetFrameworkParserTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TargetFrameworkParserTests.cs @@ -5,37 +5,35 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class TargetFrameworkParserTests : TestBase +[TestClass] +public sealed class TargetFrameworkParserTests { - public TargetFrameworkParserTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - // known 2 digit versions - [Arguments(".NET Framework 4.7.0", "net47")] - [Arguments(".NET Framework 4.8.0", "net48")] + [DataRow(".NET Framework 4.7.0", "net47")] + [DataRow(".NET Framework 4.8.0", "net48")] // known 3 digit versions - [Arguments(".NET Framework 4.6.2", "net462")] - [Arguments(".NET Framework 4.7.1", "net471")] - [Arguments(".NET Framework 4.7.2", "net472")] - [Arguments(".NET Framework 4.8.1", "net481")] + [DataRow(".NET Framework 4.6.2", "net462")] + [DataRow(".NET Framework 4.7.1", "net471")] + [DataRow(".NET Framework 4.7.2", "net472")] + [DataRow(".NET Framework 4.8.1", "net481")] // other - [Arguments(".NET Framework 4.6.3", "net46")] - [Arguments(".NET Framework 4.8.9", "net48")] + [DataRow(".NET Framework 4.6.3", "net46")] + [DataRow(".NET Framework 4.8.9", "net48")] + [TestMethod] public void ParseNETFramework(string longName, string expectedShortName) => Assert.AreEqual(expectedShortName, TargetFrameworkParser.GetShortTargetFramework(longName)); - [Arguments(".NET Core 3.1.0", "netcoreapp3.1")] + [DataRow(".NET Core 3.1.0", "netcoreapp3.1")] + [TestMethod] public void ParseNETCore(string longName, string expectedShortName) => Assert.AreEqual(expectedShortName, TargetFrameworkParser.GetShortTargetFramework(longName)); - [Arguments(".NET 6.0.0", "net6.0")] - [Arguments(".NET 8.0.0", "net8.0")] - [Arguments(".NET 10.0.0", "net10.0")] + [DataRow(".NET 6.0.0", "net6.0")] + [DataRow(".NET 8.0.0", "net8.0")] + [DataRow(".NET 10.0.0", "net10.0")] + [TestMethod] public void ParseNET(string longName, string expectedShortName) => Assert.AreEqual(expectedShortName, TargetFrameworkParser.GetShortTargetFramework(longName)); } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs index 58ad5b0aad..6692bf7fd5 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/Terminal/TerminalTestReporterTests.cs @@ -1,22 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; -using System.Text; - using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.OutputDevice.Terminal; namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class TerminalTestReporterTests : TestBase +[TestClass] +public sealed class TerminalTestReporterTests { - public TerminalTestReporterTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void AppendStackFrameFormatsStackTraceLineCorrectly() { var terminal = new StringBuilderTerminal(); @@ -34,14 +27,43 @@ public void AppendStackFrameFormatsStackTraceLineCorrectly() TerminalTestReporter.AppendStackFrame(terminal, firstStackTraceLine); #if NETCOREAPP - Assert.Contains(" at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly() in ", terminal.Output); + StringAssert.Contains(terminal.Output, " at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly() in "); #else - Assert.Contains(" at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly()", terminal.Output); + StringAssert.Contains(terminal.Output, " at Microsoft.Testing.Platform.UnitTests.TerminalTestReporterTests.AppendStackFrameFormatsStackTraceLineCorrectly()"); #endif // Line number without the respective file - Assert.That(!terminal.Output.ToString().Contains(" :0")); + Assert.IsFalse(terminal.Output.Contains(" :0")); + } + + // Code with line when we have symbols + [DataRow( + " at TestingPlatformEntryPoint.Main(String[]) in /_/TUnit.TestProject/obj/Release/net8.0/osx-x64/TestPlatformEntryPoint.cs:line 16", + " at TestingPlatformEntryPoint.Main(String[]) in /_/TUnit.TestProject/obj/Release/net8.0/osx-x64/TestPlatformEntryPoint.cs:16")] + // code without line when we don't have symbols + [DataRow( + " at TestingPlatformEntryPoint.
(String[])", + " at TestingPlatformEntryPoint.
(String[])")] + // stack trace when published as NativeAOT + [DataRow( + " at BenchmarkTest.ExceptionThrower.d__2.MoveNext() + 0x9d", + " at BenchmarkTest.ExceptionThrower.d__2.MoveNext() + 0x9d")] + // spanners that we want to keep, to not lose information + [DataRow( + "--- End of stack trace from previous location ---", + " --- End of stack trace from previous location ---")] + [TestMethod] + public void StackTraceRegexCapturesLines(string stackTraceLine, string expected) + { + var terminal = new StringBuilderTerminal(); + TerminalTestReporter.AppendStackFrame(terminal, stackTraceLine); + + // We add newline after every, but it is hard to put it in the attribute. + expected += Environment.NewLine; + + Assert.AreEqual(expected, terminal.Output); } + [TestMethod] public void OutputFormattingIsCorrect() { var stringBuilderConsole = new StringBuilderConsole(); @@ -72,16 +94,16 @@ public void OutputFormattingIsCorrect() string standardOutput = "Hello!"; string errorOutput = "Oh no!"; - terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); // timed out + canceled + failed should all report as failed in summary - terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "TimedoutTest1", "TimedoutTest1", TestOutcome.Timeout, TimeSpan.FromSeconds(10), errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "CanceledTest1", "CanceledTest1", TestOutcome.Canceled, TimeSpan.FromSeconds(10), errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); - terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "FailedTest1", "FailedTest1", TestOutcome.Fail, TimeSpan.FromSeconds(10), errorMessage: "Tests failed", exception: new StackTraceException(@$" at FailingTest() in {folder}codefile.cs:line 10"), expected: "ABC", actual: "DEF", standardOutput, errorOutput); terminalReporter.ArtifactAdded(outOfProcess: true, assembly, targetFramework, architecture, executionId: null, testName: null, @$"{folder}artifact1.txt"); terminalReporter.ArtifactAdded(outOfProcess: false, assembly, targetFramework, architecture, executionId: null, testName: null, @$"{folder}artifact2.txt"); @@ -140,12 +162,143 @@ Oh no! Assert.AreEqual(expected, ShowEscape(output)); } + [TestMethod] + public void OutputProgressFrameIsCorrect() + { + var stringBuilderConsole = new StringBuilderConsole(); + var stopwatchFactory = new StopwatchFactory(); + var terminalReporter = new TerminalTestReporter(stringBuilderConsole, new TerminalTestReporterOptions + { + ShowPassedTests = () => true, + UseAnsi = true, + ForceAnsi = true, + + ShowActiveTests = true, + ShowAssembly = false, + ShowAssemblyStartAndComplete = false, + ShowProgress = () => true, + }) + { + CreateStopwatch = stopwatchFactory.CreateStopwatch, + }; + + var startHandle = new AutoResetEvent(initialState: false); + var stopHandle = new AutoResetEvent(initialState: false); + + // Note: Ensure that we disable the timer updates, so that we can have deterministic output. + terminalReporter.OnProgressStartUpdate += (sender, args) => startHandle.WaitOne(); + terminalReporter.OnProgressStopUpdate += (sender, args) => stopHandle.Set(); + + DateTimeOffset startTime = DateTimeOffset.MinValue; + DateTimeOffset endTime = DateTimeOffset.MaxValue; + terminalReporter.TestExecutionStarted(startTime, 1, isDiscovery: false); + + string targetFramework = "net8.0"; + string architecture = "x64"; + string assembly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\assembly.dll" : "/mnt/work/assembly.dll"; + string folder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work\" : "/mnt/work/"; + string folderNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:\work" : "/mnt/work"; + string folderLink = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work/" : "mnt/work/"; + string folderLinkNoSlash = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"C:/work" : "mnt/work"; + + terminalReporter.AssemblyRunStarted(assembly, targetFramework, architecture, executionId: null); + string standardOutput = "Hello!"; + string errorOutput = "Oh no!"; + + // Note: Add 1ms to make the order of the progress frame deterministic. + // Otherwise all tests that run for 1m31s could show in any order. + terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "PassedTest1", displayName: "PassedTest1", executionId: null); + stopwatchFactory.AddTime(TimeSpan.FromMilliseconds(1)); + terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "SkippedTest1", displayName: "SkippedTest1", executionId: null); + stopwatchFactory.AddTime(TimeSpan.FromMilliseconds(1)); + terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "InProgressTest1", displayName: "InProgressTest1", executionId: null); + stopwatchFactory.AddTime(TimeSpan.FromMinutes(1)); + terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "InProgressTest2", displayName: "InProgressTest2", executionId: null); + stopwatchFactory.AddTime(TimeSpan.FromSeconds(30)); + terminalReporter.TestInProgress(assembly, targetFramework, architecture, testNodeUid: "InProgressTest3", displayName: "InProgressTest3", executionId: null); + stopwatchFactory.AddTime(TimeSpan.FromSeconds(1)); + + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "PassedTest1", "PassedTest1", TestOutcome.Passed, TimeSpan.FromSeconds(10), + errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + terminalReporter.TestCompleted(assembly, targetFramework, architecture, executionId: null, testNodeUid: "SkippedTest1", "SkippedTest1", TestOutcome.Skipped, TimeSpan.FromSeconds(10), + errorMessage: null, exception: null, expected: null, actual: null, standardOutput, errorOutput); + + string output = stringBuilderConsole.Output; + startHandle.Set(); + stopHandle.WaitOne(); + + // Note: On MacOS the busy indicator is not rendered. + bool useBusyIndicator = !RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + string busyIndicatorString = useBusyIndicator ? "â›]9;4;3;â›\\" : string.Empty; + + // Note: The progress is drawn after each completed event. + string expected = $""" + {busyIndicatorString}â›[?25lâ›[92mpassedâ›[m PassedTest1â›[90m â›[90m(10s 000ms)â›[m + â›[90m Standard output + Hello! + Error output + Oh no! + â›[m + [â›[92m✓1â›[m/â›[91mx0â›[m/â›[93m↓0â›[m] assembly.dll (net8.0|x64)â›[2147483640G(1m 31s) + SkippedTest1â›[2147483640G(1m 31s) + InProgressTest1â›[2147483640G(1m 31s) + InProgressTest2â›[2147483643G(31s) + InProgressTest3â›[2147483644G(1s) + â›[7F + â›[Jâ›[93mskippedâ›[m SkippedTest1â›[90m â›[90m(10s 000ms)â›[m + â›[90m Standard output + Hello! + Error output + Oh no! + â›[m + [â›[92m✓1â›[m/â›[91mx0â›[m/â›[93m↓1â›[m] assembly.dll (net8.0|x64)â›[2147483640G(1m 31s) + InProgressTest1â›[2147483640G(1m 31s) + InProgressTest2â›[2147483643G(31s) + InProgressTest3â›[2147483644G(1s) + + """; + + Assert.AreEqual(expected, ShowEscape(output)); + } + private static string? ShowEscape(string? text) { string visibleEsc = "\x241b"; return text?.Replace(AnsiCodes.Esc, visibleEsc); } + internal sealed class StopwatchFactory + { + private TimeSpan _currentTime = TimeSpan.Zero; + + public void AddTime(TimeSpan time) => _currentTime += time; + + public IStopwatch CreateStopwatch() => new MockStopwatch(this, _currentTime); + + internal sealed class MockStopwatch : IStopwatch + { + private readonly StopwatchFactory _factory; + + public MockStopwatch(StopwatchFactory factory, TimeSpan startTime) + { + _factory = factory; + StartTime = startTime; + } + + private TimeSpan StartTime { get; } + + public TimeSpan Elapsed => _factory._currentTime - StartTime; + + public void Start() + { + } + + public void Stop() + { + } + } + } + internal class StringBuilderConsole : IConsole { private readonly StringBuilder _output = new(); @@ -154,7 +307,7 @@ internal class StringBuilderConsole : IConsole public int BufferWidth => int.MinValue; - public bool IsOutputRedirected => throw new NotImplementedException(); + public bool IsOutputRedirected => false; public string Output => _output.ToString(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs index 3c5b97f952..f8a7bf63f0 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Program.cs @@ -1,10 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - using Microsoft.Testing.Extensions; -using Microsoft.Testing.Internal.Framework.Configurations; + +using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] +[assembly: ClassCleanupExecution(ClassCleanupBehavior.EndOfClass)] // Opt-out telemetry Environment.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", "1"); @@ -12,7 +14,8 @@ // DebuggerUtility.AttachVSToCurrentProcess(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); -builder.AddTestFramework(new TestFrameworkConfiguration(Debugger.IsAttached ? 1 : Environment.ProcessorCount), new Microsoft.Testing.Platform.UnitTests.SourceGeneratedTestNodesBuilder()); +builder.AddMSTest(() => [Assembly.GetEntryAssembly()!]); + #if ENABLE_CODECOVERAGE builder.AddCodeCoverageProvider(); #endif diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Properties/launchSettings.json b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Properties/launchSettings.json index f90979f3cd..3fcf329603 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Properties/launchSettings.json +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Microsoft.Testing.Platform.UnitTests": { "commandName": "Project", - "commandLineArgs": "--treenode-filter /*/*/*/**", + "commandLineArgs": "", "environmentVariables": { //"TESTINGPLATFORM_HOTRELOAD_ENABLED": "1" } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs index 49523583b0..f587f02457 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Requests/TreeNodeFilterTests.cs @@ -7,14 +7,10 @@ #pragma warning disable TPEXP // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class TreeNodeFilterTests : TestBase +[TestClass] +public sealed class TreeNodeFilterTests { - public TreeNodeFilterTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void MatchAllFilter_MatchesAnyPath() { TreeNodeFilter filter = new("/**"); @@ -22,16 +18,20 @@ public void MatchAllFilter_MatchesAnyPath() Assert.IsTrue(filter.MatchesFilter("/Path/Of/The/Test", new PropertyBag())); } + [TestMethod] public void MatchAllFilter_MatchesSubpaths() { TreeNodeFilter filter = new("/Path/**"); Assert.IsTrue(filter.MatchesFilter("/Path/Of/The/Test", new PropertyBag())); } - public void MatchAllFilter_Invalid() => Assert.Throws(() => _ = new TreeNodeFilter("/A(&B)")); + [TestMethod] + public void MatchAllFilter_Invalid() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/A(&B)")); - public void MatchAllFilter_DoNotAllowInMiddleOfFilter() => Assert.Throws(() => _ = new TreeNodeFilter("/**/Path")); + [TestMethod] + public void MatchAllFilter_DoNotAllowInMiddleOfFilter() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/**/Path")); + [TestMethod] public void MatchWildcard_MatchesSubstrings() { TreeNodeFilter filter = new("/*.UnitTests"); @@ -40,6 +40,7 @@ public void MatchWildcard_MatchesSubstrings() Assert.IsFalse(filter.MatchesFilter("/ProjectB.FunctionalTests", new PropertyBag())); } + [TestMethod] public void EscapeSequences_SupportsWildcard() { TreeNodeFilter filter = new("/*.\\*UnitTests"); @@ -48,6 +49,7 @@ public void EscapeSequences_SupportsWildcard() Assert.IsFalse(filter.MatchesFilter("/ProjectB.AUnitTests", new PropertyBag())); } + [TestMethod] public void EscapeSequences_SupportsParentheses() { TreeNodeFilter filter = new("/*.\\(UnitTests\\)"); @@ -56,8 +58,10 @@ public void EscapeSequences_SupportsParentheses() Assert.IsFalse(filter.MatchesFilter("/ProjectB.(UnitTests", new PropertyBag())); } - public void EscapeSequences_ThrowsIfLastCharIsAnEscapeChar() => Assert.Throws(() => _ = new TreeNodeFilter("/*.\\(UnitTests\\)\\")); + [TestMethod] + public void EscapeSequences_ThrowsIfLastCharIsAnEscapeChar() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/*.\\(UnitTests\\)\\")); + [TestMethod] public void OrExpression_WorksForLiteralStrings() { TreeNodeFilter filter = new("/A|B"); @@ -66,6 +70,7 @@ public void OrExpression_WorksForLiteralStrings() Assert.IsFalse(filter.MatchesFilter("/C", new PropertyBag())); } + [TestMethod] public void AndExpression() { TreeNodeFilter filter = new("/(*.UnitTests)&(*ProjectB*)"); @@ -75,6 +80,7 @@ public void AndExpression() Assert.IsFalse(filter.MatchesFilter("/ProjectC.UnitTests.SomeExtension", new PropertyBag())); } + [TestMethod] public void Parentheses_EnsuresOrdering() { TreeNodeFilter filter = new("/((*.UnitTests)&(*ProjectB*))|C"); @@ -86,8 +92,11 @@ public void Parentheses_EnsuresOrdering() Assert.IsFalse(filter.MatchesFilter("/C.UnitTests", new PropertyBag())); } - public void Parenthesis_DisallowSeparatorInside() => Assert.Throws(() => _ = new TreeNodeFilter("/(A/B)")); + [TestMethod] + public void Parenthesis_DisallowSeparatorInside() + => Assert.ThrowsException(() => new TreeNodeFilter("/(A/B)")); + [TestMethod] public void Parameters_PropertyCheck() { TreeNodeFilter filter = new("/*.UnitTests[Tag=Fast]"); @@ -96,18 +105,63 @@ public void Parameters_PropertyCheck() Assert.IsFalse(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag())); } - public void Parameters_DisallowAtStart() => Assert.Throws(() => _ = new TreeNodeFilter("/[Tag=Fast]")); + [TestMethod] + public void Parameters_NegatedPropertyCheck() + { + TreeNodeFilter filter = new("/*.UnitTests[Tag!=Fast]"); + Assert.IsFalse(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Fast")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Slow")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag())); + } + + [TestMethod] + public void Parameters_NegatedPropertyCheckWithMatchAllFilter() + { + TreeNodeFilter filter = new("/**[Tag!=Fast]"); + Assert.IsFalse(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Fast")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Slow")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag())); + } + + [TestMethod] + public void Parameters_NegatedPropertyCheckCombinedWithAnd() + { + TreeNodeFilter filter = new("/*.UnitTests[(Tag!=Fast)&(Tag!=Slow)]"); + Assert.IsFalse(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Fast")))); + Assert.IsFalse(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Slow")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag())); + } + + [TestMethod] + public void Parameters_NegatedPropertyCheckCombinedWithOr() + { + TreeNodeFilter filter = new("/*.UnitTests[(Tag!=Fast)|(Tag!=Slow)]"); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Fast")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag(new KeyValuePairStringProperty("Tag", "Slow")))); + Assert.IsTrue(filter.MatchesFilter("/ProjectB.UnitTests", new PropertyBag())); + } + + [TestMethod] + public void Parameters_DisallowAtStart() + => Assert.ThrowsException(() => _ = new TreeNodeFilter("/[Tag=Fast]")); - public void Parameters_DisallowEmpty() => Assert.Throws(() => _ = new TreeNodeFilter("/Path[]")); + [TestMethod] + public void Parameters_DisallowEmpty() + => Assert.ThrowsException(() => _ = new TreeNodeFilter("/Path[]")); - public void Parameters_DisallowMultiple() => Assert.Throws(() => _ = new TreeNodeFilter("/Path[Prop=2][Prop=B]")); + [TestMethod] + public void Parameters_DisallowMultiple() + => Assert.ThrowsException(() => _ = new TreeNodeFilter("/Path[Prop=2][Prop=B]")); - public void Parameters_DisallowNested() => Assert.Throws(() => _ = new TreeNodeFilter("/Path[X=[Y=1]]")); + [TestMethod] + public void Parameters_DisallowNested() + => Assert.ThrowsException(() => _ = new TreeNodeFilter("/Path[X=[Y=1]]")); - [Arguments("/A/B", "/A/B", true)] - [Arguments("/A/B", "/A%2FB", false)] - [Arguments("/A%2FB", "/A/B", false)] - [Arguments("/A%2FB", "/A%2FB", true)] + [DataRow("/A/B", "/A/B", true)] + [DataRow("/A/B", "/A%2FB", false)] + [DataRow("/A%2FB", "/A/B", false)] + [DataRow("/A%2FB", "/A%2FB", true)] + [TestMethod] public void TestNodeFilterNeedsUrlEncodingOfSlashes(string filter, string nodePath, bool isMatched) { TreeNodeFilter filterInstance = new(filter); @@ -123,14 +177,15 @@ public void TestNodeFilterNeedsUrlEncodingOfSlashes(string filter, string nodePa } } - [Arguments("/A/B[ValueWithSlash=Some/thing]", "/A/B", true)] - [Arguments("/A/B[ValueWithSlash=Some%2Fthing]", "/A/B", false)] - [Arguments("/A/B[Other/thing=KeyWithSlash]", "/A/B", true)] - [Arguments("/A/B[Other%2Fthing=KeyWithSlash]", "/A/B", false)] - [Arguments("/A%2FB[ValueWithSlash=Some/thing]", "/A%2FB", true)] - [Arguments("/A%2FB[ValueWithSlash=Some%2Fthing]", "/A%2FB", false)] - [Arguments("/A%2FB[Other/thing=KeyWithSlash]", "/A%2FB", true)] - [Arguments("/A%2FB[Other%2Fthing=KeyWithSlash]", "/A%2FB", false)] + [DataRow("/A/B[ValueWithSlash=Some/thing]", "/A/B", true)] + [DataRow("/A/B[ValueWithSlash=Some%2Fthing]", "/A/B", false)] + [DataRow("/A/B[Other/thing=KeyWithSlash]", "/A/B", true)] + [DataRow("/A/B[Other%2Fthing=KeyWithSlash]", "/A/B", false)] + [DataRow("/A%2FB[ValueWithSlash=Some/thing]", "/A%2FB", true)] + [DataRow("/A%2FB[ValueWithSlash=Some%2Fthing]", "/A%2FB", false)] + [DataRow("/A%2FB[Other/thing=KeyWithSlash]", "/A%2FB", true)] + [DataRow("/A%2FB[Other%2Fthing=KeyWithSlash]", "/A%2FB", false)] + [TestMethod] public void PropertiesDoNotNeedUrlEncodingOfSlashes(string filter, string nodePath, bool isMatched) { TreeNodeFilter filterInstance = new(filter); @@ -148,4 +203,23 @@ public void PropertiesDoNotNeedUrlEncodingOfSlashes(string filter, string nodePa Assert.IsFalse(filterInstance.MatchesFilter(nodePath, nodeProperties)); } } + + [TestMethod] + public void MatchAllFilterWithPropertyExpression() + { + TreeNodeFilter filter = new("/**[A=B]"); + Assert.IsTrue(filter.MatchesFilter("/A/B/C/D", new PropertyBag(new KeyValuePairStringProperty("A", "B")))); + Assert.IsFalse(filter.MatchesFilter("/A/B/C/D", new PropertyBag(new KeyValuePairStringProperty("A", "C")))); + } + + [TestMethod] + public void MatchAllFilterSubpathWithPropertyExpression() + { + TreeNodeFilter filter = new("/A/**[A=B]"); + Assert.IsTrue(filter.MatchesFilter("/A/B/C/D", new PropertyBag(new KeyValuePairStringProperty("A", "B")))); + Assert.IsFalse(filter.MatchesFilter("/B/A/C/D", new PropertyBag(new KeyValuePairStringProperty("A", "B")))); + } + + [TestMethod] + public void MatchAllFilterWithPropertyExpression_DoNotAllowInMiddleOfFilter() => Assert.ThrowsException(() => _ = new TreeNodeFilter("/**/Path[A=B]")); } diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs index 67cdcf857f..3e96e5919a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/FormatterUtilitiesTests.cs @@ -11,19 +11,23 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class FormatterUtilitiesTests : TestBase +[TestClass] +public sealed class FormatterUtilitiesTests { private readonly IMessageFormatter _formatter = FormatterUtilities.CreateFormatter(); - public FormatterUtilitiesTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) => + public static IEnumerable SerializerTypesForDynamicData + => SerializerUtilities.SerializerTypes.Select(x => new object[] { x }); + + public FormatterUtilitiesTests() + => #if NETCOREAPP Assert.AreEqual("System.Text.Json", _formatter.Id); #else Assert.AreEqual("Jsonite", _formatter.Id); #endif + [TestMethod] public void CanDeserializeTaskResponse() { #pragma warning disable SA1009 // Closing parenthesis should be spaced correctly @@ -44,11 +48,11 @@ public void CanDeserializeTaskResponse() var response = (ResponseMessage)msg; Assert.AreEqual(1, response.Id); - Assert.AreEqual(null, response.Result); + Assert.IsNull(response.Result); } - [ArgumentsProvider(memberName: nameof(SerializerUtilities.SerializerTypes), memberType: typeof(SerializerUtilities), - TestArgumentsEntryProviderMethodName = nameof(FormatSerializerTypes))] + [DynamicData(nameof(SerializerTypesForDynamicData), DynamicDataDisplayName = nameof(FormatSerializerTypes))] + [TestMethod] public async Task SerializeDeserialize_Succeed(Type type) { object instanceToSerialize = CreateInstance(type); @@ -71,8 +75,9 @@ public async Task SerializeDeserialize_Succeed(Type type) static bool HasCustomDeserializeAssert(Type type) => type == typeof(TestNode); } - [Arguments(typeof(DiscoverRequestArgs))] - [Arguments(typeof(RunRequestArgs))] + [DataRow(typeof(DiscoverRequestArgs))] + [DataRow(typeof(RunRequestArgs))] + [TestMethod] public void DeserializeSpecificTypes(Type type) { string json = CreateSerializedInstance(type); @@ -93,12 +98,12 @@ public void DeserializeSpecificTypes(Type type) } else { - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } - private void AssertRequestArgs(Type type, TRequestArgs actualRequest, TRequestArgs expectedRequest) + private static void AssertRequestArgs(Type type, TRequestArgs actualRequest, TRequestArgs expectedRequest) where TRequestArgs : RequestArgsBase { Assert.AreEqual(expectedRequest.RunId, actualRequest.RunId); @@ -127,7 +132,8 @@ private static void CustomAssert(Type type, object instanceDeserialized, object } } - internal static TestArgumentsEntry FormatSerializerTypes(TestArgumentsContext testArgumentsContext) => new((Type)testArgumentsContext.Arguments, ((Type)testArgumentsContext.Arguments).Name); + public static string? FormatSerializerTypes(MethodInfo methodInfo, object?[]? data) + => (data?[0] as Type)?.Name; private static void AssertSerialize(Type type, string instanceSerialized) { diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs index bae1ad7d57..cfa9e68ac5 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsonTests.cs @@ -9,13 +9,12 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class JsonTests : TestBase +[TestClass] +public sealed class JsonTests { private readonly Json _json; - public JsonTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public JsonTests() { Dictionary serializers = new(); Dictionary deserializers = new(); @@ -36,6 +35,7 @@ public JsonTests(ITestExecutionContext testExecutionContext) _json = new Json(serializers, deserializers); } + [TestMethod] public async Task Serialize_TestNodeAsync() { // Arrange @@ -45,7 +45,7 @@ public async Task Serialize_TestNodeAsync() { DisplayName = "test", Properties = bag, - Uid = new Extensions.Messages.TestNodeUid("11111"), + Uid = new TestNodeUid("11111"), }; // Act @@ -55,6 +55,7 @@ public async Task Serialize_TestNodeAsync() Assert.AreEqual("""{"uid":"11111","display-name":"test","hello":"my friend","node-type":"group"}""", actual); } + [TestMethod] public async Task Serialize_Array() { // Arrange @@ -67,6 +68,7 @@ public async Task Serialize_Array() Assert.AreEqual("[1,2,3]", actual); } + [TestMethod] public async Task Serialize_DateTimeOffset() { // Arrange @@ -79,6 +81,7 @@ public async Task Serialize_DateTimeOffset() Assert.AreEqual("2023-01-01T01:01:01.0010000+00:00", actual.Trim('"')); } + [TestMethod] public async Task Serialize_ArrayOfObjects() { // Arrange @@ -113,6 +116,7 @@ public async Task Serialize_ArrayOfObjects() Assert.AreEqual("""[{"name":"Thomas","children":[{"name":"Ruth","children":null}]},[2],3]""", actual); } + [TestMethod] public void DeserializePerson() { // Arrange @@ -137,6 +141,7 @@ public void DeserializePerson() Assert.IsNull(actual.Children![0].Children); } + [TestMethod] public void DeserializePersonList() { // Arrange diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs index cee4678ebf..853256dc7f 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/JsoniteTests.cs @@ -3,14 +3,10 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class JsoniteTests : TestBase +[TestClass] +public sealed class JsoniteTests { - public JsoniteTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void Serialize_DateTimeOffset() { #if !NETCOREAPP diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs index 71054ee0d7..e1703f6e8d 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerDataConsumerServiceTests.cs @@ -15,8 +15,8 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class ServerDataConsumerServiceTests : TestBase, IAsyncCleanable, IDisposable +[TestClass] +public sealed class ServerDataConsumerServiceTests : IDisposable { private readonly PerRequestServerDataConsumer _service; private readonly ServiceProvider _serviceProvider = new(); @@ -25,13 +25,13 @@ public sealed class ServerDataConsumerServiceTests : TestBase, IAsyncCleanable, private readonly Guid _runId = Guid.NewGuid(); - public ServerDataConsumerServiceTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public ServerDataConsumerServiceTests() { _serviceProvider.TryAddService(new PerRequestTestSessionContext(CancellationToken.None, CancellationToken.None)); _service = new PerRequestServerDataConsumer(_serviceProvider, _serverTestHost.Object, _runId, _task.Object); } + [TestMethod] public async Task ConsumeAsync_WithSessionFileArtifact() { SessionFileArtifact sessionFileArtifact = new(new SessionUid("1"), new FileInfo("file"), "name", "description"); @@ -55,6 +55,7 @@ public async Task ConsumeAsync_WithSessionFileArtifact() Assert.AreEqual(expected[0].Description, actual[0].Description); } + [TestMethod] public async Task ConsumeAsync_WithTestNodeUpdatedMessage() { TestNodeUpdateMessage testNode = new(new SessionUid("1"), new TestNode { Uid = new TestNodeUid("test()"), DisplayName = string.Empty }); @@ -66,6 +67,7 @@ public async Task ConsumeAsync_WithTestNodeUpdatedMessage() Assert.AreEqual(0, actual.Count); } + [TestMethod] public void PopulateTestNodeStatistics_WithMissingNodeType() { TestNodeUpdateMessage testNode = new(new SessionUid("1"), new TestNode { Uid = new TestNodeUid("test()"), DisplayName = string.Empty }); @@ -73,11 +75,12 @@ public void PopulateTestNodeStatistics_WithMissingNodeType() _service.PopulateTestNodeStatistics(testNode); TestNodeStatistics statistics = _service.GetTestNodeStatistics(); - Assert.AreEqual(statistics.TotalDiscoveredTests, 0); - Assert.AreEqual(statistics.TotalPassedTests, 0); - Assert.AreEqual(statistics.TotalFailedTests, 0); + Assert.AreEqual(0, statistics.TotalDiscoveredTests); + Assert.AreEqual(0, statistics.TotalPassedTests); + Assert.AreEqual(0, statistics.TotalFailedTests); } + [TestMethod] public void PopulateTestNodeStatistics_WithDuplicateDiscoveredEvents() { PropertyBag properties = new( @@ -89,11 +92,12 @@ public void PopulateTestNodeStatistics_WithDuplicateDiscoveredEvents() _service.PopulateTestNodeStatistics(testNode); TestNodeStatistics statistics = _service.GetTestNodeStatistics(); - Assert.AreEqual(statistics.TotalDiscoveredTests, 1); - Assert.AreEqual(statistics.TotalPassedTests, 0); - Assert.AreEqual(statistics.TotalFailedTests, 0); + Assert.AreEqual(1, statistics.TotalDiscoveredTests); + Assert.AreEqual(0, statistics.TotalPassedTests); + Assert.AreEqual(0, statistics.TotalFailedTests); } + [TestMethod] public void PopulateTestNodeStatistics_WithDiscoveredAndPassedEventsForSameUid() { PropertyBag properties = new( @@ -110,11 +114,12 @@ public void PopulateTestNodeStatistics_WithDiscoveredAndPassedEventsForSameUid() _service.PopulateTestNodeStatistics(otherTestNode); TestNodeStatistics statistics = _service.GetTestNodeStatistics(); - Assert.AreEqual(statistics.TotalDiscoveredTests, 1); - Assert.AreEqual(statistics.TotalPassedTests, 1); - Assert.AreEqual(statistics.TotalFailedTests, 0); + Assert.AreEqual(1, statistics.TotalDiscoveredTests); + Assert.AreEqual(1, statistics.TotalPassedTests); + Assert.AreEqual(0, statistics.TotalFailedTests); } + [TestMethod] public void PopulateTestNodeStatistics_WithDuplicatePassedEvents() { PropertyBag properties = new(PassedTestNodeStateProperty.CachedInstance); @@ -125,13 +130,14 @@ public void PopulateTestNodeStatistics_WithDuplicatePassedEvents() _service.PopulateTestNodeStatistics(testNode); TestNodeStatistics statistics = _service.GetTestNodeStatistics(); - Assert.AreEqual(statistics.TotalDiscoveredTests, 0); - Assert.AreEqual(statistics.TotalPassedTests, 1); - Assert.AreEqual(statistics.TotalFailedTests, 0); - Assert.AreEqual(statistics.TotalPassedRetries, 1); - Assert.AreEqual(statistics.TotalFailedRetries, 0); + Assert.AreEqual(0, statistics.TotalDiscoveredTests); + Assert.AreEqual(1, statistics.TotalPassedTests); + Assert.AreEqual(0, statistics.TotalFailedTests); + Assert.AreEqual(1, statistics.TotalPassedRetries); + Assert.AreEqual(0, statistics.TotalFailedRetries); } + [TestMethod] public void PopulateTestNodeStatistics_WithEventsForSameUid() { PropertyBag properties = new( @@ -148,13 +154,14 @@ public void PopulateTestNodeStatistics_WithEventsForSameUid() _service.PopulateTestNodeStatistics(otherTestNode); TestNodeStatistics statistics = _service.GetTestNodeStatistics(); - Assert.AreEqual(statistics.TotalDiscoveredTests, 0); - Assert.AreEqual(statistics.TotalPassedTests, 1); - Assert.AreEqual(statistics.TotalFailedTests, 0); - Assert.AreEqual(statistics.TotalPassedRetries, 1); - Assert.AreEqual(statistics.TotalFailedRetries, 0); + Assert.AreEqual(0, statistics.TotalDiscoveredTests); + Assert.AreEqual(1, statistics.TotalPassedTests); + Assert.AreEqual(0, statistics.TotalFailedTests); + Assert.AreEqual(1, statistics.TotalPassedRetries); + Assert.AreEqual(0, statistics.TotalFailedRetries); } + [TestMethod] public void PopulateTestNodeStatistics_WithEventsForDifferentUids() { PropertyBag properties = new( @@ -171,14 +178,15 @@ public void PopulateTestNodeStatistics_WithEventsForDifferentUids() _service.PopulateTestNodeStatistics(otherTestNode); TestNodeStatistics statistics = _service.GetTestNodeStatistics(); - Assert.AreEqual(statistics.TotalDiscoveredTests, 0); - Assert.AreEqual(statistics.TotalPassedTests, 1); - Assert.AreEqual(statistics.TotalFailedTests, 1); - Assert.AreEqual(statistics.TotalPassedRetries, 0); - Assert.AreEqual(statistics.TotalFailedRetries, 0); + Assert.AreEqual(0, statistics.TotalDiscoveredTests); + Assert.AreEqual(1, statistics.TotalPassedTests); + Assert.AreEqual(1, statistics.TotalFailedTests); + Assert.AreEqual(0, statistics.TotalPassedRetries); + Assert.AreEqual(0, statistics.TotalFailedRetries); } - public async Task CleanupAsync(CleanupContext context) + [TestCleanup] + public async Task CleanupAsync() { if (_service.GetIdleUpdateTaskAsync() is { } idleUpdateTaskAsync) { @@ -186,7 +194,7 @@ public async Task CleanupAsync(CleanupContext context) } } - public void Dispose() => _service.Dispose(); + void IDisposable.Dispose() => _service.Dispose(); private sealed class DataProducer : IDataProducer { diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs index ba16dad5dc..e2e876c54a 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/ServerMode/ServerTests.cs @@ -3,7 +3,6 @@ using System.Net; using System.Net.Sockets; -using System.Text; using Microsoft.Testing.Platform.Capabilities; using Microsoft.Testing.Platform.Capabilities.TestFramework; @@ -15,11 +14,10 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public class ServerTests : TestBase +[TestClass] +public sealed class ServerTests { - public ServerTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public ServerTests() { if (IsHotReloadEnabled(new SystemEnvironment())) { @@ -30,6 +28,7 @@ public ServerTests(ITestExecutionContext testExecutionContext) private static bool IsHotReloadEnabled(SystemEnvironment environment) => environment.GetEnvironmentVariable(EnvironmentVariableConstants.DOTNET_WATCH) == "1" || environment.GetEnvironmentVariable(EnvironmentVariableConstants.TESTINGPLATFORM_HOTRELOAD_ENABLED) == "1"; + [TestMethod] public async Task ServerCanBeStartedAndAborted_TcpIp() => await RetryHelper.RetryAsync( async () => { @@ -51,11 +50,12 @@ public async Task ServerCanBeStartedAndAborted_TcpIp() => await RetryHelper.Retr Assert.AreEqual(ExitCodes.TestSessionAborted, await serverTask); }, 3, TimeSpan.FromSeconds(10)); + [TestMethod] public async Task ServerCanInitialize() { using var server = TcpServer.Create(); - string[] args = ["--no-banner", $"--server", "--client-port", $"{server.Port}", "--internal-testingplatform-skipbuildercheck"]; + string[] args = ["--no-banner", "--server", "--client-port", $"{server.Port}", "--internal-testingplatform-skipbuildercheck"]; TestApplicationHooks testApplicationHooks = new(); ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); builder.TestHost.AddTestApplicationLifecycleCallbacks(_ => testApplicationHooks); @@ -126,6 +126,7 @@ await WriteMessageAsync( Assert.AreEqual(0, result); } + [TestMethod] public async Task DiscoveryRequestCanBeCanceled() { using var server = TcpServer.Create(); @@ -133,7 +134,7 @@ public async Task DiscoveryRequestCanBeCanceled() TaskCompletionSource discoveryStartedTaskCompletionSource = new(); TaskCompletionSource discoveryCanceledTaskCompletionSource = new(); - string[] args = ["--no-banner", $"--server", "--client-port", $"{server.Port}", "--internal-testingplatform-skipbuildercheck"]; + string[] args = ["--no-banner", "--server", "--client-port", $"{server.Port}", "--internal-testingplatform-skipbuildercheck"]; ITestApplicationBuilder builder = await TestApplication.CreateBuilderAsync(args); builder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, __) => new MockTestAdapter { @@ -244,7 +245,7 @@ public async Task DiscoveryRequestCanBeCanceled() private static async Task WriteMessageAsync(StreamWriter writer, string message) { await writer.WriteLineAsync($"Content-Length: {message.Length}"); - await writer.WriteLineAsync($"Content-Type: application/testingplatform"); + await writer.WriteLineAsync("Content-Type: application/testingplatform"); await writer.WriteLineAsync(); await writer.WriteAsync(message); await writer.FlushAsync(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs index 2053169c0d..1ad71b8e80 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/ServiceProviderTests.cs @@ -11,16 +11,12 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class ServiceProviderTests : TestBase +[TestClass] +public sealed class ServiceProviderTests { private readonly ServiceProvider _serviceProvider = new(); - public ServiceProviderTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - + [TestMethod] public void GetService_InternalExtension_ShouldNotReturn() { _serviceProvider.AddService(new TestHostProcessLifetimeHandler()); @@ -39,6 +35,7 @@ public void GetService_InternalExtension_ShouldNotReturn() Assert.IsNull(_serviceProvider.GetService()); } + [TestMethod] public void GetServiceInternal_InternalExtension_ShouldReturn() { _serviceProvider.AddService(new TestHostProcessLifetimeHandler()); @@ -57,6 +54,7 @@ public void GetServiceInternal_InternalExtension_ShouldReturn() Assert.IsNotNull(_serviceProvider.GetServiceInternal()); } + [TestMethod] public void Clone_WithoutFilter_Succeeded() { _serviceProvider.AddService(new TestHostProcessLifetimeHandler()); @@ -74,6 +72,7 @@ public void Clone_WithoutFilter_Succeeded() } } + [TestMethod] public void Clone_WithFilter_Succeeded() { _serviceProvider.AddService(new TestHostProcessLifetimeHandler()); @@ -88,29 +87,37 @@ public void Clone_WithFilter_Succeeded() Assert.AreEqual(_serviceProvider.Services.ToArray()[0].GetType(), typeof(TestHostProcessLifetimeHandler)); } - public void AddService_TestFramework_ShouldFail() => _ = Assert.Throws(() => _serviceProvider.AddService(new TestFramework())); + [TestMethod] + public void AddService_TestFramework_ShouldFail() + => Assert.ThrowsException(() => _serviceProvider.AddService(new TestFramework())); - public void TryAddService_TestFramework_ShouldFail() => _ = Assert.Throws(() => _serviceProvider.TryAddService(new TestFramework())); + [TestMethod] + public void TryAddService_TestFramework_ShouldFail() + => Assert.ThrowsException(() => _serviceProvider.TryAddService(new TestFramework())); + [TestMethod] public void AddService_TestFramework_ShouldNotFail() { _serviceProvider.AllowTestAdapterFrameworkRegistration = true; _serviceProvider.AddService(new TestFramework()); } + [TestMethod] public void TryAddService_TestFramework_ShouldNotFail() { _serviceProvider.AllowTestAdapterFrameworkRegistration = true; _serviceProvider.TryAddService(new TestFramework()); } + [TestMethod] public void AddService_SameInstance_ShouldFail() { TestHostProcessLifetimeHandler instance = new(); _serviceProvider.AddService(instance); - _ = Assert.Throws(() => _serviceProvider.AddService(instance)); + _ = Assert.ThrowsException(() => _serviceProvider.AddService(instance)); } + [TestMethod] public void AddService_SameInstance_ShouldNotFail() { TestHostProcessLifetimeHandler instance = new(); @@ -118,13 +125,15 @@ public void AddService_SameInstance_ShouldNotFail() _serviceProvider.AddService(instance, throwIfSameInstanceExit: false); } + [TestMethod] public void AddServices_SameInstance_ShouldFail() { TestHostProcessLifetimeHandler instance = new(); _serviceProvider.AddServices([instance]); - _ = Assert.Throws(() => _serviceProvider.AddServices([instance])); + _ = Assert.ThrowsException(() => _serviceProvider.AddServices([instance])); } + [TestMethod] public void AddServices_SameInstance_ShouldNotFail() { TestHostProcessLifetimeHandler instance = new(); @@ -132,6 +141,7 @@ public void AddServices_SameInstance_ShouldNotFail() _serviceProvider.AddServices([instance], throwIfSameInstanceExit: false); } + [TestMethod] public void TryAddService_SameInstance_ShouldReturnFalse() { TestHostProcessLifetimeHandler instance = new(); @@ -139,6 +149,7 @@ public void TryAddService_SameInstance_ShouldReturnFalse() Assert.IsFalse(_serviceProvider.TryAddService(instance)); } + [TestMethod] public void GetServicesInternal_ExtensionMethod_InternalExtension_ShouldReturn() { _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); @@ -147,6 +158,7 @@ public void GetServicesInternal_ExtensionMethod_InternalExtension_ShouldReturn() Assert.AreEqual(2, _serviceProvider.GetServicesInternal().Count()); } + [TestMethod] public void GetServicesInternal_InternalExtension_ShouldNotReturn() { _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); @@ -155,6 +167,7 @@ public void GetServicesInternal_InternalExtension_ShouldNotReturn() Assert.AreEqual(0, _serviceProvider.GetServicesInternal(typeof(ITestApplicationLifecycleCallbacks), stopAtFirst: false, skipInternalOnlyExtensions: true).Count()); } + [TestMethod] public void GetServicesInternal_InternalExtension_ShouldReturn() { _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); @@ -163,6 +176,7 @@ public void GetServicesInternal_InternalExtension_ShouldReturn() Assert.AreEqual(2, _serviceProvider.GetServicesInternal(typeof(ITestApplicationLifecycleCallbacks), stopAtFirst: false, skipInternalOnlyExtensions: false).Count()); } + [TestMethod] public void GetServicesInternal_InternalExtension_FirstOnly_ShouldReturnOne() { _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); @@ -171,6 +185,7 @@ public void GetServicesInternal_InternalExtension_FirstOnly_ShouldReturnOne() Assert.AreEqual(1, _serviceProvider.GetServicesInternal(typeof(ITestApplicationLifecycleCallbacks), stopAtFirst: true, skipInternalOnlyExtensions: false).Count()); } + [TestMethod] public void GetServiceInternal_InternalExtension_ShouldReturnOne() { _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); @@ -179,7 +194,8 @@ public void GetServiceInternal_InternalExtension_ShouldReturnOne() Assert.IsNotNull(_serviceProvider.GetServiceInternal(typeof(ITestApplicationLifecycleCallbacks), skipInternalOnlyExtensions: false)); } - public void GetServiceInternal_InternalExtension_SkipInternalOnlyExtensios_ShouldReturnNull() + [TestMethod] + public void GetServiceInternal_InternalExtension_SkipInternalOnlyExtensions_ShouldReturnNull() { _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); _serviceProvider.AddService(new TestApplicationLifecycleCallbacks()); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs index 6964e6aa45..f16518ac06 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Services/TestApplicationResultTests.cs @@ -11,24 +11,20 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class TestApplicationResultTests : TestBase +[TestClass] +public sealed class TestApplicationResultTests { private readonly TestApplicationResult _testApplicationResult - = new(new Mock().Object, new Mock().Object, new Mock().Object, new Mock().Object); - - public TestApplicationResultTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } + = new(new Mock().Object, new Mock().Object, new Mock().Object, new Mock().Object); + [TestMethod] public async Task GetProcessExitCodeAsync_If_All_Skipped_Returns_ZeroTestsRan() { await _testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(SkippedTestNodeStateProperty.CachedInstance), }), CancellationToken.None); @@ -36,13 +32,14 @@ public async Task GetProcessExitCodeAsync_If_All_Skipped_Returns_ZeroTestsRan() Assert.AreEqual(ExitCodes.ZeroTests, _testApplicationResult.GetProcessExitCode()); } + [TestMethod] public async Task GetProcessExitCodeAsync_If_No_Tests_Ran_Returns_ZeroTestsRan() { await _testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(), }), CancellationToken.None); @@ -50,14 +47,15 @@ public async Task GetProcessExitCodeAsync_If_No_Tests_Ran_Returns_ZeroTestsRan() Assert.AreEqual(ExitCodes.ZeroTests, _testApplicationResult.GetProcessExitCode()); } - [ArgumentsProvider(nameof(FailedState))] + [TestMethod] + [DynamicData(nameof(FailedState), DynamicDataSourceType.Method)] public async Task GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTestFailed(TestNodeStateProperty testNodeStateProperty) { await _testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(testNodeStateProperty), }), CancellationToken.None); @@ -65,24 +63,27 @@ public async Task GetProcessExitCodeAsync_If_Failed_Tests_Returns_AtLeastOneTest Assert.AreEqual(ExitCodes.AtLeastOneTestFailed, _testApplicationResult.GetProcessExitCode()); } + [TestMethod] public async Task GetProcessExitCodeAsync_If_Canceled_Returns_TestSessionAborted() { Mock testApplicationCancellationTokenSource = new(); + // CTS should not be created in SetupGet so that the mocked ITestApplicationCancellationTokenSource returns the same instance on every access + // which is the case in the real production implementation. + CancellationTokenSource cancellationTokenSource = new(); testApplicationCancellationTokenSource.SetupGet(x => x.CancellationToken).Returns(() => { - CancellationTokenSource cancellationTokenSource = new(); cancellationTokenSource.Cancel(); return cancellationTokenSource.Token; }); TestApplicationResult testApplicationResult - = new(new Mock().Object, testApplicationCancellationTokenSource.Object, new Mock().Object, new Mock().Object); + = new(new Mock().Object, new Mock().Object, new Mock().Object, new StopPoliciesService(testApplicationCancellationTokenSource.Object)); await testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(), }), CancellationToken.None); @@ -90,14 +91,15 @@ TestApplicationResult testApplicationResult Assert.AreEqual(ExitCodes.TestSessionAborted, testApplicationResult.GetProcessExitCode()); } + [TestMethod] public async Task GetProcessExitCodeAsync_If_TestAdapter_Returns_TestAdapterTestSessionFailure() { await _testApplicationResult.SetTestAdapterTestSessionFailureAsync("Adapter error"); await _testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(PassedTestNodeStateProperty.CachedInstance), }), CancellationToken.None); @@ -105,27 +107,29 @@ public async Task GetProcessExitCodeAsync_If_TestAdapter_Returns_TestAdapterTest Assert.AreEqual(ExitCodes.TestAdapterTestSessionFailure, _testApplicationResult.GetProcessExitCode()); } + [TestMethod] public async Task GetProcessExitCodeAsync_If_MinimumExpectedTests_Violated_Returns_MinimumExpectedTestsPolicyViolation() { TestApplicationResult testApplicationResult - = new(new Mock().Object, new Mock().Object, - new CommandLineOption(PlatformCommandLineProvider.MinimumExpectedTestsOptionKey, ["2"]), - new Mock().Object); + = new( + new Mock().Object, + new CommandLineOption(PlatformCommandLineProvider.MinimumExpectedTestsOptionKey, ["2"]), + new Mock().Object, new Mock().Object); await testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(PassedTestNodeStateProperty.CachedInstance), }), CancellationToken.None); await testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(InProgressTestNodeStateProperty.CachedInstance), }), CancellationToken.None); @@ -133,36 +137,40 @@ TestApplicationResult testApplicationResult Assert.AreEqual(ExitCodes.MinimumExpectedTestsPolicyViolation, testApplicationResult.GetProcessExitCode()); } + [TestMethod] public async Task GetProcessExitCodeAsync_OnDiscovery_No_Tests_Discovered_Returns_ZeroTests() { TestApplicationResult testApplicationResult - = new(new Mock().Object, new Mock().Object, - new CommandLineOption(PlatformCommandLineProvider.DiscoverTestsOptionKey, []), - new Mock().Object); + = new( + new Mock().Object, + new CommandLineOption(PlatformCommandLineProvider.DiscoverTestsOptionKey, []), + new Mock().Object, new Mock().Object); await testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", }), CancellationToken.None); Assert.AreEqual(ExitCodes.ZeroTests, testApplicationResult.GetProcessExitCode()); } + [TestMethod] public async Task GetProcessExitCodeAsync_OnDiscovery_Some_Tests_Discovered_Returns_Success() { TestApplicationResult testApplicationResult - = new(new Mock().Object, new Mock().Object, - new CommandLineOption(PlatformCommandLineProvider.DiscoverTestsOptionKey, []), - new Mock().Object); + = new( + new Mock().Object, + new CommandLineOption(PlatformCommandLineProvider.DiscoverTestsOptionKey, []), + new Mock().Object, new Mock().Object); await testApplicationResult.ConsumeAsync(new DummyProducer(), new TestNodeUpdateMessage( default, - new Extensions.Messages.TestNode + new TestNode { - Uid = new Extensions.Messages.TestNodeUid("id"), + Uid = new TestNodeUid("id"), DisplayName = "DisplayName", Properties = new PropertyBag(DiscoveredTestNodeStateProperty.CachedInstance), }), CancellationToken.None); @@ -170,17 +178,18 @@ TestApplicationResult testApplicationResult Assert.AreEqual(ExitCodes.Success, testApplicationResult.GetProcessExitCode()); } - [Arguments("8", ExitCodes.Success)] - [Arguments("8;2", ExitCodes.Success)] - [Arguments("8;", ExitCodes.Success)] - [Arguments("8;2;", ExitCodes.Success)] - [Arguments("5", ExitCodes.ZeroTests)] - [Arguments("5;7", ExitCodes.ZeroTests)] - [Arguments("5;", ExitCodes.ZeroTests)] - [Arguments("5;7;", ExitCodes.ZeroTests)] - [Arguments(";", ExitCodes.ZeroTests)] - [Arguments(null, ExitCodes.ZeroTests)] - [Arguments("", ExitCodes.ZeroTests)] + [DataRow("8", ExitCodes.Success)] + [DataRow("8;2", ExitCodes.Success)] + [DataRow("8;", ExitCodes.Success)] + [DataRow("8;2;", ExitCodes.Success)] + [DataRow("5", ExitCodes.ZeroTests)] + [DataRow("5;7", ExitCodes.ZeroTests)] + [DataRow("5;", ExitCodes.ZeroTests)] + [DataRow("5;7;", ExitCodes.ZeroTests)] + [DataRow(";", ExitCodes.ZeroTests)] + [DataRow(null, ExitCodes.ZeroTests)] + [DataRow("", ExitCodes.ZeroTests)] + [TestMethod] public void GetProcessExitCodeAsync_IgnoreExitCodes(string argument, int expectedExitCode) { Mock environment = new(); @@ -188,24 +197,27 @@ public void GetProcessExitCodeAsync_IgnoreExitCodes(string argument, int expecte foreach (TestApplicationResult testApplicationResult in new TestApplicationResult[] { - new(new Mock().Object, new Mock().Object, + new( + new Mock().Object, new CommandLineOption(PlatformCommandLineProvider.IgnoreExitCodeOptionKey, argument is null ? [] : [argument]), - new Mock().Object), - new(new Mock().Object, new Mock().Object, + new Mock().Object, new Mock().Object), + new( + new Mock().Object, new Mock().Object, - environment.Object), + environment.Object, + new Mock().Object), }) { Assert.AreEqual(expectedExitCode, testApplicationResult.GetProcessExitCode()); } } - internal static IEnumerable FailedState() + internal static IEnumerable FailedState() { - yield return new FailedTestNodeStateProperty(); - yield return new ErrorTestNodeStateProperty(); - yield return new CancelledTestNodeStateProperty(); - yield return new TimeoutTestNodeStateProperty(); + yield return new[] { new FailedTestNodeStateProperty() }; + yield return new[] { new ErrorTestNodeStateProperty() }; + yield return new[] { new CancelledTestNodeStateProperty() }; + yield return new[] { new TimeoutTestNodeStateProperty() }; } private sealed class CommandLineOption : ICommandLineOptions diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs index dfee39ce9f..8b754df5a8 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/ServerTelemetryTests.cs @@ -9,15 +9,16 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class ServerTelemetryTests : TestBase +[TestClass] +public sealed class ServerTelemetryTests { private readonly ServerTelemetry _serverTelemetry; private readonly Mock _serverTestHost = new(); - public ServerTelemetryTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) => _serverTelemetry = new(_serverTestHost.Object); + public ServerTelemetryTests() + => _serverTelemetry = new(_serverTestHost.Object); + [TestMethod] public async Task LogEvent_ForDiscovery() { Dictionary metadata = new() @@ -30,6 +31,7 @@ public async Task LogEvent_ForDiscovery() _serverTestHost.Verify(s => s.SendTelemetryEventUpdateAsync(new TelemetryEventArgs(TelemetryEvents.TestsDiscoveryEventName, metadata))); } + [TestMethod] public async Task LogEvent_ForRun() { Dictionary metadata = new() diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs index d66cd424f7..475b900da2 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/Telemetry/TelemetryManagerTests.cs @@ -13,23 +13,19 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class TelemetryManagerTests : TestBase +[TestClass] +public sealed class TelemetryManagerTests { - public TelemetryManagerTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) - { - } - // When set to 1 or true it should suppress the message. - [Arguments(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER, "1")] - [Arguments(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER, "true")] - [Arguments(EnvironmentVariableConstants.DOTNET_NOLOGO, "1")] - [Arguments(EnvironmentVariableConstants.DOTNET_NOLOGO, "true")] + [DataRow(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER, "1")] + [DataRow(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER, "true")] + [DataRow(EnvironmentVariableConstants.DOTNET_NOLOGO, "1")] + [DataRow(EnvironmentVariableConstants.DOTNET_NOLOGO, "true")] // When set to 0 it should write the message. - [Arguments(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER, "0")] - [Arguments(EnvironmentVariableConstants.DOTNET_NOLOGO, "0")] + [DataRow(EnvironmentVariableConstants.TESTINGPLATFORM_NOBANNER, "0")] + [DataRow(EnvironmentVariableConstants.DOTNET_NOLOGO, "0")] + [TestMethod] public async Task TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(string variable, string value) { // Arrange @@ -75,14 +71,15 @@ public async Task TelemetryManager_UsingNoLogoShouldSuppressTelemetryMessage(str } // When set to 1 or true it should suppress the message. - [Arguments(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1")] - [Arguments(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "true")] - [Arguments(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "1")] - [Arguments(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "true")] + [DataRow(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "1")] + [DataRow(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "true")] + [DataRow(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "1")] + [DataRow(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "true")] // When set to 0 it should write the message. - [Arguments(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "0")] - [Arguments(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "0")] + [DataRow(EnvironmentVariableConstants.TESTINGPLATFORM_TELEMETRY_OPTOUT, "0")] + [DataRow(EnvironmentVariableConstants.DOTNET_CLI_TELEMETRY_OPTOUT, "0")] + [TestMethod] public async Task TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(string variable, string value) { // Arrange @@ -131,6 +128,7 @@ public async Task TelemetryManager_UsingTelemetryOptOutShouldDisableTelemetry(st } } + [TestMethod] public async Task TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNoticeOnSubsequentRuns() { // Arrange @@ -193,6 +191,7 @@ public async Task TelemetryManager_SentinelIsWrittenPerUserAndAvoidsShowingNotic fileSystemMock.Verify(f => f.NewFileStream(path, It.IsAny(), It.IsAny()), Times.Never); } + [TestMethod] public async Task TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessage() { // Arrange @@ -262,6 +261,7 @@ public async Task TelemetryManager_SentinelIsWrittenOnlyWhenUserWouldSeeTheMessa fileSystemMock.Verify(f => f.NewFileStream(path, It.IsAny(), It.IsAny()), Times.Once); } + [TestMethod] public async Task TelemetryManager_UsingNoBannerCommandLine_ShouldSuppressTelemetryMessage() { TestApplicationOptions options = new(); diff --git a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs index 0d58477da0..27fde2ee59 100644 --- a/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs +++ b/test/UnitTests/Microsoft.Testing.Platform.UnitTests/TestApplicationBuilderTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions.Messages; using Microsoft.Testing.Platform.Extensions.TestHost; @@ -14,13 +12,12 @@ namespace Microsoft.Testing.Platform.UnitTests; -[TestGroup] -public sealed class TestApplicationBuilderTests : TestBase +[TestClass] +public sealed class TestApplicationBuilderTests { private readonly ServiceProvider _serviceProvider = new(); - public TestApplicationBuilderTests(ITestExecutionContext testExecutionContext) - : base(testExecutionContext) + public TestApplicationBuilderTests() { CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); AggregatedConfiguration configuration = new([], testApplicationModuleInfo, new SystemFileSystem()); @@ -29,55 +26,61 @@ public TestApplicationBuilderTests(ITestExecutionContext testExecutionContext) _serviceProvider.AddService(configuration); } + [TestMethod] public async Task TestApplicationLifecycleCallbacks_DuplicatedId_ShouldFail() { TestHostManager testHostManager = new(); testHostManager.AddTestApplicationLifecycleCallbacks(_ => new ApplicationLifecycleCallbacks("duplicatedId")); testHostManager.AddTestApplicationLifecycleCallbacks(_ => new ApplicationLifecycleCallbacks("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostManager.BuildTestApplicationLifecycleCallbackAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildTestApplicationLifecycleCallbackAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(ApplicationLifecycleCallbacks).ToString())); } + [TestMethod] public async Task DataConsumer_DuplicatedId_ShouldFail() { TestHostManager testHostManager = new(); testHostManager.AddDataConsumer(_ => new Consumer("duplicatedId")); testHostManager.AddDataConsumer(_ => new Consumer("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(Consumer).ToString())); } + [TestMethod] public async Task DataConsumer_DuplicatedIdWithCompositeFactory_ShouldFail() { TestHostManager testHostManager = new(); CompositeExtensionFactory compositeExtensionFactory = new(() => new Consumer("duplicatedId")); testHostManager.AddDataConsumer(_ => new Consumer("duplicatedId")); testHostManager.AddDataConsumer(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildDataConsumersAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(Consumer).ToString())); } + [TestMethod] public async Task TestSessionLifetimeHandle_DuplicatedId_ShouldFail() { TestHostManager testHostManager = new(); testHostManager.AddTestSessionLifetimeHandle(_ => new TestSessionLifetimeHandler("duplicatedId")); testHostManager.AddTestSessionLifetimeHandle(_ => new TestSessionLifetimeHandler("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestSessionLifetimeHandler).ToString())); } + [TestMethod] public async Task TestSessionLifetimeHandle_DuplicatedIdWithCompositeFactory_ShouldFail() { TestHostManager testHostManager = new(); CompositeExtensionFactory compositeExtensionFactory = new(() => new TestSessionLifetimeHandler("duplicatedId")); testHostManager.AddTestSessionLifetimeHandle(_ => new TestSessionLifetimeHandler("duplicatedId")); testHostManager.AddTestSessionLifetimeHandle(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostManager.BuildTestSessionLifetimeHandleAsync(_serviceProvider, [])); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestSessionLifetimeHandler).ToString())); } - [Arguments(true)] - [Arguments(false)] + [DataRow(true)] + [DataRow(false)] + [TestMethod] public async Task TestHost_ComposeFactory_ShouldSucceed(bool withParameter) { TestHostManager testHostManager = new(); @@ -96,46 +99,51 @@ public async Task TestHost_ComposeFactory_ShouldSucceed(bool withParameter) Assert.AreEqual(compositeExtensions[0].GetInstance(), sessionLifetimeHandle[0]); } + [TestMethod] public async Task TestHostControllerEnvironmentVariableProvider_DuplicatedId_ShouldFail() { TestHostControllersManager testHostControllerManager = new(); testHostControllerManager.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProvider("duplicatedId")); testHostControllerManager.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProvider("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostEnvironmentVariableProvider).ToString())); } + [TestMethod] public async Task TestHostControllerEnvironmentVariableProvider_DuplicatedIdWithCompositeFactory_ShouldFail() { TestHostControllersManager testHostControllerManager = new(); CompositeExtensionFactory compositeExtensionFactory = new(() => new TestHostEnvironmentVariableProvider("duplicatedId")); testHostControllerManager.AddEnvironmentVariableProvider(_ => new TestHostEnvironmentVariableProvider("duplicatedId")); testHostControllerManager.AddEnvironmentVariableProvider(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostEnvironmentVariableProvider).ToString())); } + [TestMethod] public async Task TestHostControllerProcessLifetimeHandler_DuplicatedId_ShouldFail() { TestHostControllersManager testHostControllerManager = new(); testHostControllerManager.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler("duplicatedId")); testHostControllerManager.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler("duplicatedId")); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostProcessLifetimeHandler).ToString())); } + [TestMethod] public async Task TestHostControllerProcessLifetimeHandler_DuplicatedIdWithCompositeFactory_ShouldFail() { TestHostControllersManager testHostControllerManager = new(); CompositeExtensionFactory compositeExtensionFactory = new(() => new TestHostProcessLifetimeHandler("duplicatedId")); testHostControllerManager.AddProcessLifetimeHandler(_ => new TestHostProcessLifetimeHandler("duplicatedId")); testHostControllerManager.AddProcessLifetimeHandler(compositeExtensionFactory); - InvalidOperationException invalidOperationException = await Assert.ThrowsAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); + InvalidOperationException invalidOperationException = await Assert.ThrowsExceptionAsync(() => testHostControllerManager.BuildAsync(_serviceProvider)); Assert.IsTrue(invalidOperationException.Message.Contains("duplicatedId") && invalidOperationException.Message.Contains(typeof(TestHostProcessLifetimeHandler).ToString())); } - [Arguments(true)] - [Arguments(false)] + [DataRow(true)] + [DataRow(false)] + [TestMethod] public async Task TestHostController_ComposeFactory_ShouldSucceed(bool withParameter) { TestHostControllersManager testHostControllerManager = new(); @@ -154,15 +162,16 @@ public async Task TestHostController_ComposeFactory_ShouldSucceed(bool withParam Assert.AreEqual(((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance(), configuration.EnvironmentVariableProviders[0]); } - [Arguments(true)] - [Arguments(false)] + [DataRow(true)] + [DataRow(false)] + [TestMethod] public void ComposeFactory_InvalidComposition_ShouldFail(bool withParameter) { CompositeExtensionFactory compositeExtensionFactory = withParameter ? new CompositeExtensionFactory(sp => new InvalidComposition(sp)) : new CompositeExtensionFactory(() => new InvalidComposition()); - InvalidOperationException invalidOperationException = Assert.Throws(() => ((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance()); + InvalidOperationException invalidOperationException = Assert.ThrowsException(() => ((ICompositeExtensionFactory)compositeExtensionFactory).GetInstance()); Assert.AreEqual(CompositeExtensionFactory.ValidateCompositionErrorMessage, invalidOperationException.Message); } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs index 1f2be37f6d..793c189caa 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreEqualTests.cs @@ -3,9 +3,6 @@ #nullable enable -using System.Diagnostics.CodeAnalysis; -using System.Globalization; - using Microsoft.VisualStudio.TestTools.UnitTesting; using TestFramework.ForTestingMSTest; @@ -129,7 +126,8 @@ public void AreEqual_WithEnglishCultureAndDoesNotIgnoreCase_Throws() var englishCulture = new CultureInfo("en-EN"); // Won't ignore case. - VerifyThrows(() => Assert.AreEqual(expected, actual, false, englishCulture)); + Exception ex = VerifyThrows(() => Assert.AreEqual(expected, actual, false, englishCulture)); + Verify(ex.Message == "Assert.AreEqual failed. Expected:. Case is different for actual value:. "); } public void AreEqual_WithTurkishCultureAndDoesNotIgnoreCase_Throws() @@ -293,6 +291,830 @@ public void AreEqualUsingDynamicsDoesNotFail() #pragma warning restore IDE0004 + public void GenericAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(o, o, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task GenericAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(0, 1, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected:<0>. Actual:<1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void GenericAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(0, 1, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task GenericAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(0, 0, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected any value except:<0>. Actual:<0>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void FloatAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1.0f, 1.1f, delta: 0.2f, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task FloatAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0f, 1.1f, 0.001f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void FloatAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1.0f, 1.1f, 0.001f, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task FloatAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0f, 1.1f, 0.2f, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DecimalAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1.0m, 1.1m, delta: 0.2m, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DecimalAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0m, 1.1m, 0.001m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DecimalAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1.0m, 1.1m, 0.001m, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DecimalAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0m, 1.1m, 0.2m, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1.0> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void LongAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1L, 2L, delta: 1L, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task LongAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1L, 2L, 0L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void LongAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1L, 2L, 0L, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task LongAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1L, 2L, 1L, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <1> between expected value <1> and actual value <2>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DoubleAreEqual_InterpolatedString_EqualValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreEqual(1.0d, 1.1d, delta: 0.2d, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DoubleAreEqual_InterpolatedString_DifferentValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreEqual(1.0d, 1.1d, 0.001d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreEqual failed. Expected a difference no greater than <0.001> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void DoubleAreNotEqual_InterpolatedString_DifferentValues_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotEqual(1.0d, 1.1d, 0.001d, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task DoubleAreNotEqual_InterpolatedString_SameValues_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotEqual(1.0d, 1.1d, 0.2d, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotEqual failed. Expected a difference greater than <0.2> between expected value <1> and actual value <1.1>. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 2.0f, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 1.0f, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 1.0f, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, float.NaN, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, float.NaN, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 2.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 1.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 1.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, float.NaN, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, float.NaN, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 2.0f, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, 1.0f, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 1.0f, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, float.NaN, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0f, float.NaN, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(5.0f, 2.0f, 2.0f)); // difference is 3. Delta is 2 + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value <2>. "); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaNegative_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(2.0f, 5.0f, 2.0f)); // difference is -3. Delta is 2 + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <2> and actual value <5>. "); + } + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldPass() + => Assert.AreEqual(5.0f, 4.0f, 2.0f); // difference is 1. Delta is 2 + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaNegative_DeltaIsNumeric_ShouldFail() + => Assert.AreEqual(4.0f, 5.0f, 2.0f); // difference is -1. Delta is 2 + + public void FloatAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(5.0f, float.NaN, 2.0f)); + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value . "); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(float.NaN, 5.0f, 2.0f)); + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value and actual value <5>. "); + } + + public void FloatAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldPass() + => Assert.AreEqual(float.NaN, float.NaN, 2.0f); + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 2.0f, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 1.0f, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, 1.0f, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, float.NaN, float.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 2.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 1.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, 1.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, float.NaN, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 2.0f, -1.0f)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, 1.0f, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, 1.0f, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0f, float.NaN, float.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldPass() + => Assert.AreNotEqual(5.0f, 2.0f, 2.0f); // difference is 3. Delta is 2 + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaNegative_DeltaIsNumeric_ShouldPass() + => Assert.AreNotEqual(2.0f, 5.0f, 2.0f); // difference is -3. Delta is 2 + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(5.0f, 4.0f, 2.0f)); // difference is 1. Delta is 2 + Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <5> and actual value <4>. "); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaNegative_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(4.0f, 5.0f, 2.0f)); // difference is -1. Delta is 2 + Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <4> and actual value <5>. "); + } + + public void FloatAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldPass() => Assert.AreNotEqual(5.0f, float.NaN, 2.0f); + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_ShouldPass() + => Assert.AreNotEqual(float.NaN, 5.0f, 2.0f); + + public void FloatAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(float.NaN, float.NaN, 2.0f)); + Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value and actual value . "); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 2.0d, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 1.0d, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 1.0d, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, double.NaN, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, double.NaN, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 2.0d, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 1.0d, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 1.0d, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, double.NaN, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, double.NaN, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 2.0d, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, 1.0d, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 1.0d, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, double.NaN, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(1.0d, double.NaN, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(5.0d, 2.0d, 2.0d)); // difference is 3. Delta is 2 + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value <2>. "); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaNegative_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(2.0d, 5.0d, 2.0d)); // difference is -3. Delta is 2 + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <2> and actual value <5>. "); + } + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldPass() + => Assert.AreEqual(5.0d, 4.0d, 2.0d); // difference is 1. Delta is 2 + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaNegative_DeltaIsNumeric_ShouldFail() + => Assert.AreEqual(4.0d, 5.0d, 2.0d); // difference is -1. Delta is 2 + + public void DoubleAreEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(5.0d, double.NaN, 2.0d)); + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value <5> and actual value . "); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreEqual(double.NaN, 5.0d, 2.0d)); + Verify(ex.Message == "Assert.AreEqual failed. Expected a difference no greater than <2> between expected value and actual value <5>. "); + } + + public void DoubleAreEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldPass() + => Assert.AreEqual(double.NaN, double.NaN, 2.0d); + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 2.0d, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 1.0d, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, 1.0d, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNaN_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, double.NaN, double.NaN)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 2.0d, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 1.0d, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, 1.0d, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegative_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, double.NaN, -1.0d)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualNotEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 2.0d, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualEquals_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, 1.0d, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, 1.0d, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNegativeInf_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(1.0d, double.NaN, double.NegativeInfinity)); + Verify(ex.Message is """ + Specified argument was out of the range of valid values. + Parameter name: delta + """ or "Specified argument was out of the range of valid values. (Parameter 'delta')"); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaPositive_DeltaIsNumeric_ShouldPass() + => Assert.AreNotEqual(5.0d, 2.0d, 2.0d); // difference is 3. Delta is 2 + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceGreaterThanDeltaNegative_DeltaIsNumeric_ShouldPass() + => Assert.AreNotEqual(2.0d, 5.0d, 2.0d); // difference is -3. Delta is 2 + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaPositive_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(5.0d, 4.0d, 2.0d)); // difference is 1. Delta is 2 + Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <5> and actual value <4>. "); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNumeric_ExpectedAndActualDifferenceLessThanDeltaNegative_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(4.0d, 5.0d, 2.0d)); // difference is -1. Delta is 2 + Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value <4> and actual value <5>. "); + } + + public void DoubleAreNotEqual_ExpectedIsNumeric_ActualIsNaN_DeltaIsNumeric_ShouldPass() => Assert.AreNotEqual(5.0d, double.NaN, 2.0d); + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNumeric_DeltaIsNumeric_ShouldPass() + => Assert.AreNotEqual(double.NaN, 5.0d, 2.0d); + + public void DoubleAreNotEqual_ExpectedIsNaN_ActualIsNaN_DeltaIsNumeric_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreNotEqual(double.NaN, double.NaN, 2.0d)); + Verify(ex.Message == "Assert.AreNotEqual failed. Expected a difference greater than <2> between expected value and actual value . "); + } + private CultureInfo? GetCultureInfo() => CultureInfo.CurrentCulture; private class TypeOverridesEquals diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs new file mode 100644 index 0000000000..eaced7c334 --- /dev/null +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.AreSame.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#nullable enable + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; + +public partial class AssertTests +{ + public void AreSame_PassSameObject_ShouldPass() + { + object o = new(); + Assert.AreSame(o, o); + } + + public void AreSame_PassDifferentObject_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object())); + Verify(ex.Message == "Assert.AreSame failed. "); + } + + public void AreSame_StringMessage_PassSameObject_ShouldPass() + { + object o = new(); + Assert.AreSame(o, o, "User-provided message"); + } + + public void AreSame_StringMessage_PassDifferentObject_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object(), "User-provided message")); + Verify(ex.Message == "Assert.AreSame failed. User-provided message"); + } + + public void AreSame_InterpolatedString_PassSameObject_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreSame(o, o, $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task AreSame_InterpolatedString_PassDifferentObject_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreSame(new object(), new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void AreSame_MessageArgs_PassSameObject_ShouldPass() + { + object o = new(); + Assert.AreSame(o, o, "User-provided message: {0}", new object().GetType()); + } + + public void AreSame_MessageArgs_PassDifferentObject_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.AreSame(new object(), new object(), "User-provided message: System.Object type: {0}", new object().GetType())); + Verify(ex.Message == "Assert.AreSame failed. User-provided message: System.Object type: System.Object"); + } + + public void AreNotSame_PassDifferentObject_ShouldPass() + => Assert.AreNotSame(new object(), new object()); + + public void AreSame_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1)); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). "); + } + + public void AreSame_StringMessage_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, "User-provided message")); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message"); + } + + public void AreSame_InterpolatedString_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, $"User-provided message {new object().GetType()}")); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); + } + + public void AreSame_MessageArgs_BothAreValueTypes_ShouldFailWithSpecializedMessage() + { + Exception ex = VerifyThrows(() => Assert.AreSame(1, 1, "User-provided message {0}", new object().GetType())); + Verify(ex.Message == "Assert.AreSame failed. Do not pass value types to AreSame(). Values converted to Object will never be the same. Consider using AreEqual(). User-provided message System.Object"); + } + + public void AreNotSame_PassSameObject_ShouldFail() + { + object o = new(); + Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o)); + Verify(ex.Message == "Assert.AreNotSame failed. "); + } + + public void AreNotSame_StringMessage_PassDifferentObject_ShouldPass() + => Assert.AreNotSame(new object(), new object(), "User-provided message"); + + public void AreNotSame_StringMessage_PassSameObject_ShouldFail() + { + object o = new(); + Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o, "User-provided message")); + Verify(ex.Message == "Assert.AreNotSame failed. User-provided message"); + } + + public void AreNotSame_InterpolatedString_PassDifferentObject_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.AreNotSame(new object(), new object(), $"User-provided message: {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task AreNotSame_InterpolatedString_PassSameObject_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.AreNotSame(o, o, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.AreNotSame failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void AreNotSame_MessageArgs_PassDifferentObject_ShouldPass() + => Assert.AreNotSame(new object(), new object(), "User-provided message: {0}", new object().GetType()); + + public void AreNotSame_MessageArgs_PassSameObject_ShouldFail() + { + object o = new(); + Exception ex = VerifyThrows(() => Assert.AreNotSame(o, o, "User-provided message: System.Object type: {0}", new object().GetType())); + Verify(ex.Message == "Assert.AreNotSame failed. User-provided message: System.Object type: System.Object"); + } +} diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs index cde2af40ff..d03c7555c6 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.InconclusiveTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs index b532333130..4ec85f34d3 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsInstanceOfTypeTests.cs @@ -3,8 +3,6 @@ #nullable enable -using System.Diagnostics.CodeAnalysis; - using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; @@ -12,16 +10,81 @@ namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; [SuppressMessage("Usage", "CA2263:Prefer generic overload when type is known", Justification = "We want to test also the non-generic API")] public partial class AssertTests { - public void InstanceOfTypeShouldFailWhenValueIsNull() => - VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests))); + public void InstanceOfTypeShouldFailWhenValueIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests))); + Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + } + + public void InstanceOfTypeShouldFailWhenTypeIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + } - public void InstanceOfTypeShouldFailWhenTypeIsNull() => - VerifyThrows(() => Assert.IsInstanceOfType(5, null)); + public void InstanceOfTypeShouldFailWhenTypeIsMismatched() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string))); + Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + } public void InstanceOfTypeShouldPassOnSameInstance() => Assert.IsInstanceOfType(5, typeof(int)); public void InstanceOfTypeShouldPassOnHigherInstance() => Assert.IsInstanceOfType(5, typeof(object)); + public void InstanceOfType_WithStringMessage_ShouldFailWhenValueIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null, typeof(AssertTests), "User-provided message")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message"); + } + + public void InstanceOfType_WithStringMessage_ShouldFailWhenTypeIsNull() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null, "User-provided message")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message"); + } + + public void InstanceOfType_WithStringMessage_ShouldFailWhenTypeIsMismatched() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string), "User-provided message")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message Expected type:. Actual type:."); + } + + public void InstanceOfType_WithStringMessage_ShouldPassWhenTypeIsCorrect() + => Assert.IsInstanceOfType(5, typeof(int), "User-provided message"); + + public async Task InstanceOfType_WithInterpolatedString_ShouldFailWhenValueIsNull() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsInstanceOfType(null, typeof(AssertTests), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsInstanceOfType failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void InstanceOfType_WithInterpolatedString_ShouldFailWhenTypeIsNull() + { + DummyClassTrackingToStringCalls o = new(); + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, null, $"User-provided message {o}")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls"); + Verify(o.WasToStringCalled); + } + + public void InstanceOfType_WithInterpolatedString_ShouldFailWhenTypeIsMismatched() + { + DummyClassTrackingToStringCalls o = new(); + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, typeof(string), $"User-provided message {o}")); + Verify(ex.Message == "Assert.IsInstanceOfType failed. User-provided message DummyClassTrackingToStringCalls Expected type:. Actual type:."); + Verify(o.WasToStringCalled); + } + + public void InstanceOfType_WithInterpolatedString_ShouldPassWhenTypeIsCorrect() + { + DummyClassTrackingToStringCalls o = new(); + Assert.IsInstanceOfType(5, typeof(int), $"User-provided message {o}"); + Verify(!o.WasToStringCalled); + } + public void InstanceNotOfTypeShouldFailWhenValueIsNull() => Assert.IsNotInstanceOfType(null, typeof(object)); public void InstanceNotOfTypeShouldFailWhenTypeIsNull() => @@ -32,8 +95,17 @@ public void InstanceNotOfTypeShouldFailWhenTypeIsNull() => public void InstanceNotOfTypeShouldPassOnSubInstance() => Assert.IsNotInstanceOfType(new object(), typeof(int)); [TestMethod] - public void IsInstanceOfTypeUsingGenericType_WhenValueIsNull_Fails() => - VerifyThrows(() => Assert.IsInstanceOfType(null)); + public void IsInstanceOfTypeUsingGenericType_WhenValueIsNull_Fails() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(null)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. "); + } + + public void IsInstanceOfTypeUsingGenericType_WhenTypeMismatch_Fails() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + } [TestMethod] public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenValueIsNull_Fails() @@ -43,6 +115,12 @@ public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenValueIsNull_Fai Verify(assertTests is null); } + public void IsInstanceOfTypeUsingGenericTypeWithOutParameter_WhenTypeMismatch_Fails() + { + Exception ex = VerifyThrows(() => Assert.IsInstanceOfType(5, out _)); + Verify(ex.Message == "Assert.IsInstanceOfType failed. Expected type:. Actual type:."); + } + [TestMethod] public void IsInstanceOfTypeUsingGenericType_OnSameInstance_DoesNotThrow() => Assert.IsInstanceOfType(5); diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs index bec3b88580..15eb6d2249 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsNull.cs @@ -7,10 +7,53 @@ using TestFramework.ForTestingMSTest; -namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests.Assertions; +namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests : TestContainer { + public void IsNull_PassNull_ShouldPass() + => Assert.IsNull(null); + + public void IsNull_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNull(new object())); + Verify(ex.Message == "Assert.IsNull failed. "); + } + + public void IsNull_StringMessage_PassNull_ShouldPass() + => Assert.IsNull(null, "User-provided message"); + + public void IsNull_StringMessage_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNull(new object(), "User-provided message")); + Verify(ex.Message == "Assert.IsNull failed. User-provided message"); + } + + public void IsNull_InterpolatedString_PassNull_ShouldPass() + { + DummyClassTrackingToStringCalls o = new(); + Assert.IsNull(null, $"User-provided message {o}"); + Verify(!o.WasToStringCalled); + } + + public async Task IsNull_InterpolatedString_PassNonNull_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsNull(new object(), $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void IsNull_MessageFormat_PassNull_ShouldPass() + => Assert.IsNull(null, "User-provided message {0}", new object().GetType()); + + public void IsNull_MessageFormat_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNull(new object(), "User-provided message {0}", new object().GetType())); + Verify(ex.Message == "Assert.IsNull failed. User-provided message System.Object"); + } + public void IsNotNull_WhenNonNullNullableValue_DoesNotThrowAndLearnNotNull() { object? obj = GetObj(); @@ -25,6 +68,15 @@ public void IsNotNull_WhenNonNullNullableValueAndMessage_DoesNotThrowAndLearnNot _ = obj.ToString(); // No potential NRE warning } + public void IsNotNull_WhenNonNullNullableValueAndInterpolatedStringMessage_DoesNotThrowAndLearnNotNull() + { + object? obj = GetObj(); + DummyClassTrackingToStringCalls o = new(); + Assert.IsNotNull(obj, $"my message {o}"); + Verify(!o.WasToStringCalled); + _ = obj.ToString(); // No potential NRE warning + } + public void IsNotNull_WhenNonNullNullableValueAndCompositeMessage_DoesNotThrowAndLearnNotNull() { object? obj = GetObj(); @@ -32,5 +84,30 @@ public void IsNotNull_WhenNonNullNullableValueAndCompositeMessage_DoesNotThrowAn _ = obj.ToString(); // No potential NRE warning } - private object? GetObj() => new(); + public void IsNotNull_PassNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNotNull(null)); + Verify(ex.Message == "Assert.IsNotNull failed. "); + } + + public void IsNotNull_StringMessage_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNotNull(null, "User-provided message")); + Verify(ex.Message == "Assert.IsNotNull failed. User-provided message"); + } + + public async Task IsNotNull_InterpolatedString_PassNonNull_ShouldFail() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsNotNull(null, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsNotNull failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + Verify(o.WasToStringCalled); + } + + public void IsNotNull_MessageFormat_PassNonNull_ShouldFail() + { + Exception ex = VerifyThrows(() => Assert.IsNotNull(null, "User-provided message {0}", new object().GetType())); + Verify(ex.Message == "Assert.IsNotNull failed. User-provided message System.Object"); + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs index 437a0919f9..3cf8d1ea23 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.IsTrueTests.cs @@ -7,18 +7,247 @@ namespace Microsoft.VisualStudio.TestPlatform.TestFramework.UnitTests; public partial class AssertTests { - public void IsFalseNullableBooleansShouldFailWithNull() + public void IsFalseNullableBooleanShouldFailWithNull() { bool? nullBool = null; Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool)); - Verify(ex.Message.Contains("Assert.IsFalse failed")); + Verify(ex.Message == "Assert.IsFalse failed. "); } - public void IsTrueNullableBooleansShouldFailWithNull() + public void IsFalseNullableBooleanShouldFailWithTrue() + { + bool? nullBool = true; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool)); + Verify(ex.Message == "Assert.IsFalse failed. "); + } + + public void IsFalseNullableBooleanShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool); + } + + public void IsFalseBooleanShouldFailWithTrue() + { + Exception ex = VerifyThrows(() => Assert.IsFalse(true)); + Verify(ex.Message == "Assert.IsFalse failed. "); + } + + public void IsFalseBooleanShouldNotFailWithFalse() + => Assert.IsFalse(false); + + public void IsFalseNullableBooleanStringMessageShouldFailWithNull() { bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + } + public void IsFalseNullableBooleanStringMessageShouldFailWithTrue() + { + bool? nullBool = true; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + } + + public void IsFalseNullableBooleanStringMessageShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool, "User-provided message"); + } + + public void IsFalseBooleanStringMessageShouldFailWithTrue() + { + Exception ex = VerifyThrows(() => Assert.IsFalse(true, "User-provided message")); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message"); + } + + public void IsFalseBooleanStringMessageShouldNotFailWithFalse() + => Assert.IsFalse(false, "User-provided message"); + + public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithNull() + { + bool? nullBool = null; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public async Task IsFalseNullableBooleanInterpolatedStringMessageShouldFailWithTrue() + { + bool? nullBool = true; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsFalseNullableBooleanInterpolatedStringMessageShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool, $"User-provided message. Input: {nullBool}"); + } + + public async Task IsFalseBooleanInterpolatedStringMessageShouldFailWithTrue() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsFalse(true, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsFalse failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsFalseBooleanInterpolatedStringMessageShouldNotFailWithFalse() + => Assert.IsFalse(false, $"User-provided message. Input: {false}"); + + public void IsFalseNullableBooleanMessageArgsShouldFailWithNull() + { + bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: "); + } + + public void IsFalseNullableBooleanMessageArgsShouldFailWithTrue() + { + bool? nullBool = true; + Exception ex = VerifyThrows(() => Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: True"); + } + + public void IsFalseNullableBooleanMessageArgsShouldNotFailWithFalse() + { + bool? nullBool = false; + Assert.IsFalse(nullBool, "User-provided message. Input: {0}", nullBool); + } + + public void IsFalseBooleanMessageArgsShouldFailWithTrue() + { + Exception ex = VerifyThrows(() => Assert.IsFalse(true, "User-provided message. Input: {0}", true)); + Verify(ex.Message == "Assert.IsFalse failed. User-provided message. Input: True"); + } + + public void IsFalseBooleanMessageArgsShouldNotFailWithFalse() + => Assert.IsFalse(false, "User-provided message. Input: {0}", false); + + public void IsTrueNullableBooleanShouldFailWithNull() + { + bool? nullBool = null; Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool)); - Verify(ex.Message.Contains("Assert.IsTrue failed")); + Verify(ex.Message == "Assert.IsTrue failed. "); } + + public void IsTrueNullableBooleanShouldFailWithFalse() + { + bool? nullBool = false; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool)); + Verify(ex.Message == "Assert.IsTrue failed. "); + } + + public void IsTrueNullableBooleanShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool); + } + + public void IsTrueBooleanShouldFailWithFalse() + { + Exception ex = VerifyThrows(() => Assert.IsTrue(false)); + Verify(ex.Message == "Assert.IsTrue failed. "); + } + + public void IsTrueBooleanShouldNotFailWithTrue() + => Assert.IsTrue(true); + + public void IsTrueNullableBooleanStringMessageShouldFailWithNull() + { + bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + } + + public void IsTrueNullableBooleanStringMessageShouldFailWithFalse() + { + bool? nullBool = false; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message")); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + } + + public void IsTrueNullableBooleanStringMessageShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool, "User-provided message"); + } + + public void IsTrueBooleanStringMessageShouldFailWithFalse() + { + Exception ex = VerifyThrows(() => Assert.IsTrue(false, "User-provided message")); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message"); + } + + public void IsTrueBooleanStringMessageShouldNotFailWithTrue() + => Assert.IsTrue(true, "User-provided message"); + + public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithNull() + { + bool? nullBool = null; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public async Task IsTrueNullableBooleanInterpolatedStringMessageShouldFailWithFalse() + { + bool? nullBool = false; + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(nullBool, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsTrueNullableBooleanInterpolatedStringMessageShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool, $"User-provided message. Input: {nullBool}"); + } + + public async Task IsTrueBooleanInterpolatedStringMessageShouldFailWithFalse() + { + DummyClassTrackingToStringCalls o = new(); + DateTime dateTime = DateTime.Now; + Exception ex = await VerifyThrowsAsync(async () => Assert.IsTrue(false, $"User-provided message. {o}, {o,35}, {await GetHelloStringAsync()}, {new DummyIFormattable()}, {dateTime:tt}, {dateTime,5:tt}")); + Verify(ex.Message == $"Assert.IsTrue failed. User-provided message. DummyClassTrackingToStringCalls, DummyClassTrackingToStringCalls, Hello, DummyIFormattable.ToString(), {string.Format(null, "{0:tt}", dateTime)}, {string.Format(null, "{0,5:tt}", dateTime)}"); + } + + public void IsTrueBooleanInterpolatedStringMessageShouldNotFailWithTrue() + => Assert.IsTrue(true, $"User-provided message. Input: {true}"); + + public void IsTrueNullableBooleanMessageArgsShouldFailWithNull() + { + bool? nullBool = null; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: "); + } + + public void IsTrueNullableBooleanMessageArgsShouldFailWithFalse() + { + bool? nullBool = false; + Exception ex = VerifyThrows(() => Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool)); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: False"); + } + + public void IsTrueNullableBooleanMessageArgsShouldNotFailWithTrue() + { + bool? nullBool = true; + Assert.IsTrue(nullBool, "User-provided message. Input: {0}", nullBool); + } + + public void IsTrueBooleanMessageArgsShouldFailWithFalse() + { + Exception ex = VerifyThrows(() => Assert.IsTrue(false, "User-provided message. Input: {0}", false)); + Verify(ex.Message == "Assert.IsTrue failed. User-provided message. Input: False"); + } + + public void IsTrueBooleanMessageArgsShouldNotFailWithTrue() + => Assert.IsTrue(true, "User-provided message. Input: {0}", true); } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs index 459c84c165..66202bd2da 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.cs @@ -72,7 +72,7 @@ public void ThrowsExceptionAsyncShouldThrowAssertionOnNoException() Verify(innerException is not null); Verify(typeof(AssertFailedException) == innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. ", StringComparison.Ordinal)); + Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. ", StringComparison.Ordinal)); } public void ThrowsExceptionAsyncShouldThrowAssertionOnWrongException() @@ -89,7 +89,7 @@ public void ThrowsExceptionAsyncShouldThrowAssertionOnWrongException() Verify(innerException is not null); Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsException failed. Expected exception type:. Actual exception type:. ", StringComparison.Ordinal)); + Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. ", StringComparison.Ordinal)); } public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnNoException() @@ -103,7 +103,7 @@ public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnNoException() Verify(innerException is not null); Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. The world is not on fire.", StringComparison.Ordinal)); + Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. The world is not on fire.", StringComparison.Ordinal)); } public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnWrongException() @@ -121,7 +121,7 @@ public void ThrowsExceptionAsyncWithMessageShouldThrowAssertionOnWrongException( Verify(innerException is not null); Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsException failed. Expected exception type:. Actual exception type:. Happily ever after.", StringComparison.Ordinal)); + Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. Happily ever after.", StringComparison.Ordinal)); } public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowOnNullAction() @@ -170,7 +170,7 @@ public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnNoExce Verify(innerException is not null); Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsException failed. Expected exception type: but no exception was thrown. The world is not on fire ta.da-123.", StringComparison.Ordinal)); + Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type: but no exception was thrown. The world is not on fire ta.da-123.", StringComparison.Ordinal)); } public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnWrongException() @@ -190,7 +190,225 @@ public void ThrowsExceptionAsyncWithMessageAndParamsShouldThrowAssertionOnWrongE Verify(innerException is not null); Assert.AreEqual(typeof(AssertFailedException), innerException.GetType()); - Verify(innerException.Message.Equals("Assert.ThrowsException failed. Expected exception type:. Actual exception type:. Happily ever after. The End.", StringComparison.Ordinal)); + Verify(innerException.Message.Equals("Assert.ThrowsExceptionAsync failed. Expected exception type:. Actual exception type:. Happily ever after. The End.", StringComparison.Ordinal)); } #endregion + + public void Throws_WhenExceptionIsDerivedFromExpectedType_ShouldNotThrow() + => Assert.Throws(() => throw new ArgumentNullException()); + + public void Throws_WhenExceptionIsNotExpectedType_ShouldThrow() + { + static void Action() => Assert.Throws(() => throw new Exception()); + Exception ex = VerifyThrows(Action); + Verify(ex is AssertFailedException); + } + + public void ThrowsExactly_WhenExceptionIsDerivedFromExpectedType_ShouldThrow() + { + static void Action() => Assert.ThrowsExactly(() => throw new ArgumentNullException()); + Exception ex = VerifyThrows(Action); + Verify(ex is AssertFailedException); + } + + public void ThrowsExactly_WhenExceptionExpectedType_ShouldNotThrow() + => Assert.ThrowsExactly(() => throw new ArgumentNullException()); + + public async Task ThrowsAsync_WhenExceptionIsDerivedFromExpectedType_ShouldNotThrow() + => await Assert.ThrowsAsync(() => throw new ArgumentNullException()); + + public void ThrowsAsync_WhenExceptionIsNotExpectedType_ShouldThrow() + { + Task t = Assert.ThrowsAsync(() => throw new Exception()); + Exception ex = VerifyThrows(t.Wait); + Assert.IsInstanceOfType(ex.InnerException, out AssertFailedException assertFailedException); + Assert.AreEqual("Assert.ThrowsAsync failed. Expected exception type:. Actual exception type:. ", assertFailedException.Message); + } + + public void ThrowsExactlyAsync_WhenExceptionIsDerivedFromExpectedType_ShouldThrow() + { + Task t = Assert.ThrowsExactlyAsync(() => throw new ArgumentNullException()); + Exception ex = VerifyThrows(t.Wait); + Assert.IsInstanceOfType(ex.InnerException, out AssertFailedException assertFailedException); + Assert.AreEqual("Assert.ThrowsExactlyAsync failed. Expected exception type:. Actual exception type:. ", assertFailedException.Message); + } + + public async Task ThrowsExactlyAsync_WhenExceptionExpectedType_ShouldNotThrow() + => await Assert.ThrowsExactlyAsync(() => throw new ArgumentNullException()); + + public void Throws_WithMessageBuilder_Passes() + { + bool wasBuilderCalled = false; + Assert.Throws(() => throw new ArgumentNullException(), messageBuilder: _ => + { + wasBuilderCalled = true; + return "message constructed via builder."; + }); + + Verify(!wasBuilderCalled); + } + + public void Throws_WithMessageBuilder_FailsBecauseNoException() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = VerifyThrows(() => Assert.Throws(() => { }, messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is null); + Verify(assertFailedEx.Message == "Assert.Throws failed. Expected exception type: but no exception was thrown. message constructed via builder."); + } + + public void Throws_WithMessageBuilder_FailsBecauseTypeMismatch() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = VerifyThrows(() => Assert.Throws(() => throw new ArgumentOutOfRangeException("MyParamNameHere"), messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); + Verify(assertFailedEx.Message == "Assert.Throws failed. Expected exception type:. Actual exception type:. message constructed via builder."); + } + + public void ThrowsExactly_WithMessageBuilder_Passes() + { + bool wasBuilderCalled = false; + Assert.ThrowsExactly(() => throw new ArgumentNullException(), messageBuilder: _ => + { + wasBuilderCalled = true; + return "message constructed via builder."; + }); + + Verify(!wasBuilderCalled); + } + + public void ThrowsExactly_WithMessageBuilder_FailsBecauseNoException() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = VerifyThrows(() => Assert.ThrowsExactly(() => { }, messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is null); + Verify(assertFailedEx.Message == "Assert.ThrowsExactly failed. Expected exception type: but no exception was thrown. message constructed via builder."); + } + + public void ThrowsExactly_WithMessageBuilder_FailsBecauseTypeMismatch() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = VerifyThrows(() => Assert.ThrowsExactly(() => throw new ArgumentOutOfRangeException("MyParamNameHere"), messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); + Verify(assertFailedEx.Message == "Assert.ThrowsExactly failed. Expected exception type:. Actual exception type:. message constructed via builder."); + } + + public async Task ThrowsAsync_WithMessageBuilder_Passes() + { + bool wasBuilderCalled = false; + await Assert.ThrowsAsync(() => Task.FromException(new ArgumentNullException()), messageBuilder: _ => + { + wasBuilderCalled = true; + return "message constructed via builder."; + }); + + Verify(!wasBuilderCalled); + } + + public async Task ThrowsAsync_WithMessageBuilder_FailsBecauseNoException() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsAsync(() => Task.CompletedTask, messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is null); + Verify(assertFailedEx.Message == "Assert.ThrowsAsync failed. Expected exception type: but no exception was thrown. message constructed via builder."); + } + + public async Task ThrowsAsync_WithMessageBuilder_FailsBecauseTypeMismatch() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsAsync(() => Task.FromException(new ArgumentOutOfRangeException("MyParamNameHere")), messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); + Verify(assertFailedEx.Message == "Assert.ThrowsAsync failed. Expected exception type:. Actual exception type:. message constructed via builder."); + } + + public async Task ThrowsExactlyAsync_WithMessageBuilder_Passes() + { + bool wasBuilderCalled = false; + await Assert.ThrowsExactlyAsync(() => Task.FromException(new ArgumentNullException()), messageBuilder: _ => + { + wasBuilderCalled = true; + return "message constructed via builder."; + }); + + Verify(!wasBuilderCalled); + } + + public async Task ThrowsExactlyAsync_WithMessageBuilder_FailsBecauseNoException() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsExactlyAsync(() => Task.CompletedTask, messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is null); + Verify(assertFailedEx.Message == "Assert.ThrowsExactlyAsync failed. Expected exception type: but no exception was thrown. message constructed via builder."); + } + + public async Task ThrowsExactlyAsync_WithMessageBuilder_FailsBecauseTypeMismatch() + { + bool wasBuilderCalled = false; + Exception exceptionPassedToBuilder = null; + AssertFailedException assertFailedEx = await VerifyThrowsAsync(async () => await Assert.ThrowsExactlyAsync(() => Task.FromException(new ArgumentOutOfRangeException("MyParamNameHere")), messageBuilder: ex => + { + wasBuilderCalled = true; + exceptionPassedToBuilder = ex; + return "message constructed via builder."; + })); + + Verify(wasBuilderCalled); + Verify(exceptionPassedToBuilder is ArgumentOutOfRangeException { ParamName: "MyParamNameHere" }); + Verify(assertFailedEx.Message == "Assert.ThrowsExactlyAsync failed. Expected exception type:. Actual exception type:. message constructed via builder."); + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs index 4d1f5a12c3..49cdeb4897 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.cs @@ -10,7 +10,7 @@ public partial class AssertTests #region That tests public void ThatShouldReturnAnInstanceOfAssert() => Verify(Assert.That is not null); - public void ThatShouldCacheAssertInstance() => Verify(object.ReferenceEquals(Assert.That, Assert.That)); + public void ThatShouldCacheAssertInstance() => Verify(ReferenceEquals(Assert.That, Assert.That)); #endregion #region ReplaceNullChars tests @@ -39,4 +39,24 @@ public void BuildUserMessageDoesNotThrowWhenMessageContainsInvalidStringFormatCo Verify(message == "{"); } #endregion + + private static Task GetHelloStringAsync() + => Task.FromResult("Hello"); + + private sealed class DummyClassTrackingToStringCalls + { + public bool WasToStringCalled { get; private set; } + + public override string ToString() + { + WasToStringCalled = true; + return nameof(DummyClassTrackingToStringCalls); + } + } + + private sealed class DummyIFormattable : IFormattable + { + public string ToString(string format, IFormatProvider formatProvider) + => "DummyIFormattable.ToString()"; + } } diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs index 908a0d6ec4..9c96249a35 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/CollectionAssertTests.cs @@ -3,7 +3,6 @@ #nullable enable -using System.Collections; using System.Collections.ObjectModel; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs b/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs index 21ef26548d..f910b5acd1 100644 --- a/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Assertions/StringAssertTests.cs @@ -3,9 +3,6 @@ #nullable enable -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; - using Microsoft.VisualStudio.TestTools.UnitTesting; using TestFramework.ForTestingMSTest; diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs index b585158ef6..26657b16c4 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/DataRowAttributeTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Reflection; - using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; diff --git a/test/Utilities/Automation.CLI/CLITestBase.common.cs b/test/Utilities/Automation.CLI/CLITestBase.common.cs index 35426ea724..fdb82562ff 100644 --- a/test/Utilities/Automation.CLI/CLITestBase.common.cs +++ b/test/Utilities/Automation.CLI/CLITestBase.common.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml; - using FluentAssertions; using TestFramework.ForTestingMSTest; @@ -18,10 +16,8 @@ public partial class CLITestBase : TestContainer "Release"; #endif -#pragma warning disable IDE0051 // Remove unused private members - private const string TestPlatformCLIPackageName = "Microsoft.TestPlatform"; -#pragma warning restore IDE0051 // Remove unused private members private const string DefaultTargetFramework = "net462"; + internal const string TestPlatformCLIPackageName = "Microsoft.TestPlatform"; protected static XmlDocument ReadCPMFile() { @@ -39,14 +35,14 @@ protected static XmlDocument ReadCPMFile() protected static string GetTestPlatformVersion() { XmlDocument cpmXml = ReadCPMFile(); - XmlNode testSdkVersion = cpmXml.DocumentElement.SelectSingleNode($"PropertyGroup/MicrosoftNETTestSdkVersion"); + XmlNode testSdkVersion = cpmXml.DocumentElement.SelectSingleNode("PropertyGroup/MicrosoftNETTestSdkVersion"); return testSdkVersion.InnerText; } protected static string GetArtifactsBinFolderPath() { - string assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location; + string assemblyLocation = Assembly.GetExecutingAssembly().Location; string artifactsBinFolder = Path.GetFullPath(Path.Combine(assemblyLocation, @"..\..\..\..")); Directory.Exists(artifactsBinFolder).Should().BeTrue(); @@ -56,7 +52,7 @@ protected static string GetArtifactsBinFolderPath() protected static string GetArtifactsTestResultsFolderPath() { - string assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location; + string assemblyLocation = Assembly.GetExecutingAssembly().Location; string artifactsFolder = Path.GetFullPath(Path.Combine(assemblyLocation, @"..\..\..\..\..")); Directory.Exists(artifactsFolder).Should().BeTrue(); @@ -78,13 +74,10 @@ protected static string GetAssetFullPath(string assetName, string configuration } /// - /// Gets the RunSettingXml having testadapterpath filled in specified by argument. - /// Inserts testAdapterPath in existing runSetting if not present already, + /// Gets the RunSettingXml with the TestAdapterPath inserted, /// or generates new runSettings with testAdapterPath if runSettings is Empty. /// - /// RunSettings provided for discovery/execution. - /// RunSettingXml as string. - protected static string GetRunSettingXml(string settingsXml) + protected static string GetRunSettingsXml(string settingsXml) { if (string.IsNullOrEmpty(settingsXml)) { diff --git a/test/Utilities/Automation.CLI/CLITestBase.e2e.cs b/test/Utilities/Automation.CLI/CLITestBase.e2e.cs index c1a92af1cb..ebbdedaae4 100644 --- a/test/Utilities/Automation.CLI/CLITestBase.e2e.cs +++ b/test/Utilities/Automation.CLI/CLITestBase.e2e.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.CompilerServices; - using FluentAssertions; using Microsoft.TestPlatform.VsTestConsole.TranslationLayer; @@ -35,9 +33,9 @@ public void InvokeVsTestForDiscovery(string[] sources, string runSettings = "", ExpandTestSourcePaths(sources, targetFramework); _discoveryEventsHandler = new DiscoveryEventsHandler(); - string runSettingXml = GetRunSettingXml(runSettings); + string runSettingsXml = GetRunSettingsXml(runSettings); - s_vsTestConsoleWrapper.DiscoverTests(sources, runSettingXml, _discoveryEventsHandler); + s_vsTestConsoleWrapper.DiscoverTests(sources, runSettingsXml, _discoveryEventsHandler); } /// @@ -51,9 +49,9 @@ public void InvokeVsTestForExecution(string[] sources, string runSettings = "", ExpandTestSourcePaths(sources, targetFramework); RunEventsHandler = new RunEventsHandler(); - string runSettingXml = GetRunSettingXml(runSettings); + string runSettingsXml = GetRunSettingsXml(runSettings); - s_vsTestConsoleWrapper.RunTests(sources, runSettingXml, new TestPlatformOptions { TestCaseFilter = testCaseFilter }, RunEventsHandler); + s_vsTestConsoleWrapper.RunTests(sources, runSettingsXml, new TestPlatformOptions { TestCaseFilter = testCaseFilter }, RunEventsHandler); if (RunEventsHandler.Errors.Count != 0) { throw new Exception($"Run failed with {RunEventsHandler.Errors.Count} errors:{Environment.NewLine}{string.Join(Environment.NewLine, RunEventsHandler.Errors)}"); @@ -81,7 +79,7 @@ public static string GetNugetPackageFolder() /// Gets the path to vstest.console.exe. /// /// Full path to vstest.console.exe. - public string GetConsoleRunnerPath() + public static string GetConsoleRunnerPath() { string testPlatformNuGetPackageFolder = Path.Combine( GetNugetPackageFolder(), diff --git a/test/Utilities/Automation.CLI/DiscoveryEventsHandler.cs b/test/Utilities/Automation.CLI/DiscoveryEventsHandler.cs index e21e5553ec..6c1b1a7cbf 100644 --- a/test/Utilities/Automation.CLI/DiscoveryEventsHandler.cs +++ b/test/Utilities/Automation.CLI/DiscoveryEventsHandler.cs @@ -4,7 +4,6 @@ #nullable enable using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; diff --git a/test/Utilities/Automation.CLI/Properties/AssemblyInfo.cs b/test/Utilities/Automation.CLI/Properties/AssemblyInfo.cs index a730c9f39e..918aafdae2 100644 --- a/test/Utilities/Automation.CLI/Properties/AssemblyInfo.cs +++ b/test/Utilities/Automation.CLI/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. diff --git a/test/Utilities/Automation.CLI/RunConfiguration.cs b/test/Utilities/Automation.CLI/RunConfiguration.cs index edde844f5d..9bb1c434cf 100644 --- a/test/Utilities/Automation.CLI/RunConfiguration.cs +++ b/test/Utilities/Automation.CLI/RunConfiguration.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml; - namespace Microsoft.MSTestV2.CLIAutomation; /// diff --git a/test/Utilities/Automation.CLI/XmlRunSettingsUtilities.cs b/test/Utilities/Automation.CLI/XmlRunSettingsUtilities.cs index 2fd036c6c8..857fead94a 100644 --- a/test/Utilities/Automation.CLI/XmlRunSettingsUtilities.cs +++ b/test/Utilities/Automation.CLI/XmlRunSettingsUtilities.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Xml; - namespace Microsoft.MSTestV2.CLIAutomation; public static class XmlRunSettingsUtilities diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs index 73693a1896..baad841ddc 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/CommandLine.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.Testing.TestInfrastructure; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/Constants.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/Constants.cs index a4489f8b11..67b83f1b4f 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/Constants.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/Constants.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - namespace Microsoft.Testing.TestInfrastructure; public static class Constants diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs index ddb5ed161b..2a7839e498 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DebuggerUtility.cs @@ -4,12 +4,6 @@ #pragma warning disable CA1837 // Use 'Environment.ProcessId' #pragma warning disable CA1416 // Validate platform compatibility -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; namespace Microsoft.Testing.TestInfrastructure; @@ -26,7 +20,7 @@ private static bool AttachVSToProcess(int? pid, int? vsPid, bool enableLog = fal { if (pid == null) { - Trace($"FAIL: Pid is null.", enabled: enableLog); + Trace("FAIL: Pid is null.", enabled: enableLog); return false; } @@ -42,7 +36,7 @@ private static bool AttachVSToProcess(int? pid, int? vsPid, bool enableLog = fal return true; } - Trace($"Parent VS not found, finding the first VS that started.", enabled: enableLog); + Trace("Parent VS not found, finding the first VS that started.", enabled: enableLog); var firstVs = Process.GetProcesses() .Where(p => p.ProcessName == "devenv") .Select(p => @@ -104,21 +98,21 @@ private static bool AttachVs(Process vs, int pid, bool enableLog = false) Marshal.ThrowExceptionForHR(r); if (bindCtx == null) { - Trace($"BindCtx is null. Cannot attach VS.", enabled: enableLog); + Trace("BindCtx is null. Cannot attach VS.", enabled: enableLog); return false; } bindCtx.GetRunningObjectTable(out runningObjectTable); if (runningObjectTable == null) { - Trace($"RunningObjectTable is null. Cannot attach VS.", enabled: enableLog); + Trace("RunningObjectTable is null. Cannot attach VS.", enabled: enableLog); return false; } runningObjectTable.EnumRunning(out enumMoniker); if (enumMoniker == null) { - Trace($"EnumMoniker is null. Cannot attach VS.", enabled: enableLog); + Trace("EnumMoniker is null. Cannot attach VS.", enabled: enableLog); return false; } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs index be08498905..7e41079af4 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetCli.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; - using Polly; using Polly.Contrib.WaitAndRetry; @@ -83,7 +80,11 @@ public static async Task RunAsync( } } - environmentVariables.Add(key!, entry.Value!.ToString()!); + // We use TryAdd to let tests "overwrite" existing environment variables. + // Consider that the given dictionary has "TESTINGPLATFORM_UI_LANGUAGE" as a key. + // And also Environment.GetEnvironmentVariables() is returning TESTINGPLATFORM_UI_LANGUAGE. + // In that case, we do a "TryAdd" which effectively means the value from the original dictionary wins. + environmentVariables.TryAdd(key!, entry.Value!.ToString()!); } if (disableTelemetry) diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs index cb798a263e..6be55cb242 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxer.cs @@ -107,7 +107,7 @@ public async Task ExecuteAsync( cleanDefaultEnvironmentVariableIfCustomAreProvided: true, timeoutInSeconds: timeoutInSeconds); - private IDictionary MergeEnvironmentVariables( + private static IDictionary MergeEnvironmentVariables( IDictionary environmentVariables1, IDictionary environmentVariables2) { @@ -122,9 +122,9 @@ public async Task ExecuteAsync( } IDictionary mergedEnvironmentVariables = new Dictionary(environmentVariables1); - foreach (KeyValuePair kvp in environmentVariables2) + foreach ((string key, string? value) in environmentVariables2) { - mergedEnvironmentVariables[kvp.Key] = kvp.Value; + mergedEnvironmentVariables[key] = value; } return mergedEnvironmentVariables; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxerResult.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxerResult.cs index 589972d538..a02d2ba47a 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxerResult.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/DotnetMuxerResult.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.ObjectModel; -using System.Globalization; -using System.Text; namespace Microsoft.Testing.TestInfrastructure; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj index 927d31caaf..5557b9cd96 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/Microsoft.Testing.TestInfrastructure.csproj @@ -1,4 +1,4 @@ - + $(MicrosoftTestingTargetFrameworks);netstandard2.0 @@ -7,15 +7,12 @@ - - - - + diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs index 4b8d554e4d..a7a7b0a8fa 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessFactory.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.TestInfrastructure; public static class ProcessFactory @@ -33,14 +31,14 @@ public static IProcessHandle Start(ProcessConfiguration config, bool cleanDefaul processStartInfo.EnvironmentVariables.Clear(); } - foreach (KeyValuePair kvp in config.EnvironmentVariables) + foreach ((string key, string? value) in config.EnvironmentVariables) { - if (kvp.Value is null) + if (value is null) { continue; } - processStartInfo.EnvironmentVariables[kvp.Key] = kvp.Value; + processStartInfo.EnvironmentVariables[key] = value; } } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs index f6d4d5c172..fb32b027b3 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProcessHandle.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; - namespace Microsoft.Testing.TestInfrastructure; public sealed class ProcessHandle : IProcessHandle, IDisposable diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs index a15bbf5485..91deff4f72 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/ProjectSystem.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Globalization; -using System.Text; -using System.Xml.Linq; - namespace Microsoft.Testing.TestInfrastructure; public class VSSolution : Folder diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs index c0261fca89..a2adaf6262 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/RetryHelper.cs @@ -7,11 +7,13 @@ namespace Microsoft.Testing.TestInfrastructure; public class RetryHelper { - public static async Task RetryAsync(Func action, uint times, TimeSpan every, Func? predicate = null) => await Policy.Handle(exception => predicate is null || predicate(exception)) + public static async Task RetryAsync(Func action, uint times, TimeSpan every, Func? predicate = null) + => await Policy.Handle(exception => predicate is null || predicate(exception)) .WaitAndRetryAsync((int)times, _ => every) .ExecuteAsync(action); - public static async Task RetryAsync(Func> action, uint times, TimeSpan every, Func? predicate = null) => await Policy.Handle(exception => predicate is null || predicate(exception)) + public static async Task RetryAsync(Func> action, uint times, TimeSpan every, Func? predicate = null) + => await Policy.Handle(exception => predicate is null || predicate(exception)) .WaitAndRetryAsync((int)times, _ => every) .ExecuteAsync(action); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs index 15a0fec88d..cbf0399e57 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/SlowestTestsConsumer.cs @@ -9,7 +9,7 @@ namespace Microsoft.Testing.TestInfrastructure; public sealed class SlowestTestsConsumer : IDataConsumer, ITestSessionLifetimeHandler { - private readonly List<(string TestId, double Milliseconds)> _testPerf = []; + private readonly List<(string TestId, string DisplayName, double Milliseconds)> _testPerf = []; public Type[] DataTypesConsumed => [typeof(TestNodeUpdateMessage)]; @@ -32,7 +32,7 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo } double milliseconds = testNodeUpdatedMessage.TestNode.Properties.Single().GlobalTiming.Duration.TotalMilliseconds; - _testPerf.Add((testNodeUpdatedMessage.TestNode.Uid, milliseconds)); + _testPerf.Add((testNodeUpdatedMessage.TestNode.Uid, testNodeUpdatedMessage.TestNode.DisplayName, milliseconds)); return Task.CompletedTask; } @@ -40,9 +40,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo public Task OnTestSessionFinishingAsync(SessionUid sessionUid, CancellationToken cancellationToken) { Console.WriteLine("Slowest 10 tests"); - foreach ((string testId, double milliseconds) in _testPerf.OrderByDescending(x => x.Milliseconds).Take(10)) + foreach ((_, string displayName, double milliseconds) in _testPerf.OrderByDescending(x => x.Milliseconds).Take(10)) { - Console.WriteLine($"{testId} {TimeSpan.FromMilliseconds(milliseconds).TotalSeconds:F5}s"); + Console.WriteLine($"{displayName} {TimeSpan.FromMilliseconds(milliseconds).TotalSeconds:F5}s"); } return Task.CompletedTask; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/SourceCodeExtensions.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/SourceCodeExtensions.cs index 88e502c555..afa810c240 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/SourceCodeExtensions.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/SourceCodeExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Testing.Internal.Framework; - namespace Microsoft.Testing.TestInfrastructure; public static class SourceCodeExtensions @@ -10,6 +8,6 @@ public static class SourceCodeExtensions public static string PatchCodeWithReplace(this string code, string pattern, string value) => code.Replace(pattern, value); - public static string PatchTargetFrameworks(this string code, params TestArgumentsEntry[] targetFrameworks) + public static string PatchTargetFrameworks(this string code, params string[] targetFrameworks) => PatchCodeWithReplace(code, "$TargetFrameworks$", targetFrameworks.ToMSBuildTargetFrameworks()); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs index d217e073ec..3d4b2ffb33 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TargetFrameworks.cs @@ -1,32 +1,35 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Runtime.InteropServices; - -using Microsoft.Testing.Internal.Framework; - namespace Microsoft.Testing.TestInfrastructure; public static class TargetFrameworks { - public static TestArgumentsEntry[] Net { get; } = + public static string[] Net { get; } = [ - new("net8.0", "net8.0"), + "net9.0", #if !SKIP_INTERMEDIATE_TARGET_FRAMEWORKS - new("net7.0", "net7.0"), - new("net6.0", "net6.0"), + "net8.0", + "net7.0", + "net6.0", #endif ]; - public static TestArgumentsEntry NetCurrent { get; } = Net[0]; + public static IEnumerable NetForDynamicData { get; } = + Net.Select(tfm => new object[] { tfm }); - public static TestArgumentsEntry[] NetFramework { get; } = [new("net462", "net462")]; + public static string NetCurrent { get; } = Net[0]; - public static TestArgumentsEntry[] All { get; } + public static string[] NetFramework { get; } = ["net462"]; + + public static string[] All { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Net.Concat(NetFramework).ToArray() : Net; - public static string ToMSBuildTargetFrameworks(this TestArgumentsEntry[] targetFrameworksEntries) - => string.Join(";", targetFrameworksEntries.Select(x => x.Arguments)); + public static IEnumerable AllForDynamicData { get; } = + All.Select(tfm => new object[] { tfm }); + + public static string ToMSBuildTargetFrameworks(this string[] targetFrameworksEntries) + => string.Join(";", targetFrameworksEntries); } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs index 95da03bbb7..a41a32e696 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TempDirectory.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; - namespace Microsoft.Testing.TestInfrastructure; public class TempDirectory : IDisposable @@ -25,29 +23,34 @@ public TempDirectory(string? subDirectory = null, bool arcadeConvention = true, _cleanup = cleanup; } - ~TempDirectory() => Clean(); - public string Path { get; } public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) { if (_isDisposed) { return; } - if (disposing && _cleanup) + _isDisposed = true; + + if (!_cleanup) { - Clean(); + return; } - _isDisposed = true; + if (!Directory.Exists(_baseDirectory)) + { + return; + } + + try + { + Directory.Delete(_baseDirectory, recursive: true); + } + catch + { + } } public DirectoryInfo CreateDirectory(string dir) => Directory.CreateDirectory(System.IO.Path.Combine(Path, dir)); @@ -228,22 +231,6 @@ internal static (string BaseDirectory, string FinalDirectory) CreateUniqueDirect private static string GetTempPath() => Environment.GetEnvironmentVariable("AGENT_TEMPDIRECTORY") ?? System.IO.Path.GetTempPath(); - public void Clean() - { - if (!Directory.Exists(_baseDirectory)) - { - return; - } - - try - { - Directory.Delete(_baseDirectory, recursive: true); - } - catch - { - } - } - public void Add(string fileContents) { List files = InlineFileParser.ParseFiles(fileContents); diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs index e40adefc3e..e2eff19a9b 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestAssetFixtureBase.cs @@ -1,26 +1,37 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Concurrent; +namespace Microsoft.Testing.TestInfrastructure; -using Microsoft.Testing.Internal.Framework; +public interface ITestAssetFixture : IDisposable +{ + Task InitializeAsync(); +} -namespace Microsoft.Testing.TestInfrastructure; +public sealed class NopAssetFixture : ITestAssetFixture +{ + public Task InitializeAsync() => Task.CompletedTask; + + public void Dispose() + { + } +} -public abstract class TestAssetFixtureBase : IDisposable, IAsyncInitializable +public abstract class TestAssetFixtureBase : ITestAssetFixture { private readonly ConcurrentDictionary _testAssets = new(); private readonly TempDirectory _nugetGlobalPackagesDirectory; private bool _disposedValue; - protected TestAssetFixtureBase(TempDirectory nugetGlobalPackagesDirectory) => _nugetGlobalPackagesDirectory = nugetGlobalPackagesDirectory; + protected TestAssetFixtureBase(TempDirectory nugetGlobalPackagesDirectory) + => _nugetGlobalPackagesDirectory = nugetGlobalPackagesDirectory; public string GetAssetPath(string assetID) => !_testAssets.TryGetValue(assetID, out TestAsset? testAsset) ? throw new ArgumentNullException(nameof(assetID), $"Cannot find target path for test asset '{assetID}'") : testAsset.TargetAssetPath; - public async Task InitializeAsync(InitializationContext context) + public async Task InitializeAsync() #if NET => await Parallel.ForEachAsync(GetAssetsToGenerate(), async (asset, _) => { diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestBase.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestBase.cs deleted file mode 100644 index c5124a15f3..0000000000 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Testing.Internal.Framework; - -namespace Microsoft.Testing.TestInfrastructure; - -public abstract class TestBase -{ - protected TestBase(ITestExecutionContext testExecutionContext) => TestsRunWatchDog.AddTestRun(testExecutionContext.TestInfo.StableUid); -} diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs index 60676a556a..32352b750b 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHost.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - using Polly; using Polly.Contrib.WaitAndRetry; diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHostResult.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHostResult.cs index 31f86f2d16..8b8647cc85 100644 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHostResult.cs +++ b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestHostResult.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.ObjectModel; -using System.Globalization; -using System.Text; namespace Microsoft.Testing.TestInfrastructure; @@ -21,17 +19,16 @@ public sealed class TestHostResult(string command, int exitCode, string standard public ReadOnlyCollection StandardErrorLines { get; } = standardErrorLines; - public override string ToString() - { - StringBuilder stringBuilder = new(); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Command: {Command}"); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"===================="); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"ExitCode: {ExitCode}"); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"===================="); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"StandardOutput:\n{StandardOutput}"); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"===================="); - stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"StandardError:\n{StandardError}"); - - return stringBuilder.ToString(); - } + public override string ToString() => + $""" + Command: {Command} + ==================== + ExitCode: {ExitCode} + ==================== + StandardOutput: + {StandardOutput} + ==================== + StandardError: + {StandardError} + """; } diff --git a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs b/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs deleted file mode 100644 index f35320d659..0000000000 --- a/test/Utilities/Microsoft.Testing.TestInfrastructure/TestsRunWatchDog.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Concurrent; -using System.Globalization; -using System.Text; - -using Microsoft.Testing.Internal.Framework; - -namespace Microsoft.Testing.TestInfrastructure; - -public static class TestsRunWatchDog -{ - private static readonly ConcurrentDictionary TestNodes = new(); - - public static string? BaselineFile { get; set; } - - public static void AddTestRun(TestNodeUid testNodeUid) => TestNodes.AddOrUpdate(testNodeUid, 1, (_, count) => count + 1); - - public static async Task VerifyAsync(bool skip = false, bool fixBaseLine = false) - { - if (skip) - { - return; - } - - if (BaselineFile is null) - { - throw new InvalidOperationException("Baseline file should not be null"); - } - - if (TestNodes.IsEmpty) - { - throw new InvalidOperationException("No tests were executed. Have you called 'TestsRunWatchDog.AddTestRun'?"); - } - - List expectedTestsDidNotRun = []; - List unexpectedRanTests = []; - using (FileStream fs = File.OpenRead(BaselineFile)) - { - using StreamReader streamReader = new(fs); - string? testFullyQualifiedName; - while ((testFullyQualifiedName = await streamReader.ReadLineAsync()) != null) - { - if (string.IsNullOrWhiteSpace(testFullyQualifiedName)) - { - // Skip empty lines. - continue; - } - else if (!TestNodes.TryGetValue(testFullyQualifiedName, out int _)) - { - expectedTestsDidNotRun.Add(testFullyQualifiedName); - } - else - { - TestNodes[testFullyQualifiedName]--; - if (TestNodes[testFullyQualifiedName] == 0) - { - TestNodes.TryRemove(testFullyQualifiedName, out _); - } - } - } - } - - if (!TestNodes.IsEmpty) - { - foreach (KeyValuePair notRunNodes in TestNodes) - { - for (int i = 0; i < notRunNodes.Value; i++) - { - unexpectedRanTests.Add(notRunNodes.Key.Value); - } - } - } - - StringBuilder sb = new(); - if (unexpectedRanTests.Count > 0) - { - sb.AppendLine(); - sb.AppendLine(CultureInfo.InvariantCulture, $"Unexpected tests that ran (base line file name {BaselineFile}):"); - sb.AppendLine(); - foreach (string unexpectedTest in unexpectedRanTests) - { - sb.AppendLine(unexpectedTest); - } - } - - if (expectedTestsDidNotRun.Count > 0) - { - sb.AppendLine(); - sb.AppendLine(CultureInfo.InvariantCulture, $"Expected tests that did not run (base line file name {BaselineFile}):"); - sb.AppendLine(); - foreach (string missingTest in expectedTestsDidNotRun) - { - sb.AppendLine(missingTest); - } - } - - try - { - if (unexpectedRanTests.Count > 0 || expectedTestsDidNotRun.Count > 0) - { - throw new InvalidOperationException(sb.ToString()); - } - } - finally - { - if (fixBaseLine) - { - List tests = [.. File.ReadAllLines(BaselineFile)]; - tests.RemoveAll(expectedTestsDidNotRun.Contains); - tests.AddRange(unexpectedRanTests); - tests.Sort(); - File.WriteAllLines(BaselineFile, tests); - Console.WriteLine(); - Console.WriteLine($"FIXED BASELINE: '{BaselineFile}'"); - } - } - } -} diff --git a/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs b/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs index 16b511b637..82f19d1675 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/AdapterToTestPlatform.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; diff --git a/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs b/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs deleted file mode 100644 index 2ca70c0553..0000000000 --- a/test/Utilities/TestFramework.ForTestingMSTest/CallerArgumentExpressionAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#if !NET5_0 && !NET6_0 -namespace System.Runtime.CompilerServices; - -/// -/// Allows capturing of the expressions passed to a method. -/// -[AttributeUsage(AttributeTargets.Parameter)] -internal sealed class CallerArgumentExpressionAttribute : Attribute -{ - /// - /// Initializes a new instance of the class. - /// - /// The name of the targeted parameter. - public CallerArgumentExpressionAttribute(string parameterName) => ParameterName = parameterName; - - /// - /// Gets the target parameter name of the CallerArgumentExpression. - /// - /// - /// The name of the targeted parameter of the CallerArgumentExpression. - /// - public string ParameterName { get; } -} -#endif diff --git a/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs b/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs deleted file mode 100644 index 42657395bd..0000000000 --- a/test/Utilities/TestFramework.ForTestingMSTest/CodeAnalysisAttributes.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#if !NETCOREAPP3_0_OR_GREATER -namespace System.Diagnostics.CodeAnalysis; - -/// Applied to a method that will never return under any circumstance. -[AttributeUsage(AttributeTargets.Method, Inherited = false)] -internal sealed class DoesNotReturnAttribute : Attribute; - -/// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. -[AttributeUsage(AttributeTargets.Parameter)] -internal sealed class NotNullWhenAttribute : Attribute -{ - /// - /// Initializes a new instance of the class.Initializes the attribute with the specified return value condition. - /// - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets a value indicating whether gets to return value condition. - public bool ReturnValue { get; } -} -#endif diff --git a/test/Utilities/TestFramework.ForTestingMSTest/DoesNotReturnIfAttribute.cs b/test/Utilities/TestFramework.ForTestingMSTest/DoesNotReturnIfAttribute.cs deleted file mode 100644 index bb519abe20..0000000000 --- a/test/Utilities/TestFramework.ForTestingMSTest/DoesNotReturnIfAttribute.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#if NETSTANDARD2_0 || NETFRAMEWORK - -#pragma warning disable SA1623 // Remove ridiculous stylecop documentation warning -namespace System.Diagnostics.CodeAnalysis; - -/// -/// Specifies that the method will not return if the associated -/// parameter is passed the specified value. -/// -[ExcludeFromCodeCoverage] -[DebuggerNonUserCode] -[AttributeUsage(AttributeTargets.Parameter)] -public sealed class DoesNotReturnIfAttribute : - Attribute -{ - /// - /// Gets the condition parameter value. - /// Code after the method is considered unreachable by diagnostics if the argument - /// to the associated parameter matches this value. - /// - public bool ParameterValue { get; } - - /// - /// Initializes a new instance of the - /// class with the specified parameter value. - /// - /// - /// The condition parameter value. - /// Code after the method is considered unreachable by diagnostics if the argument - /// to the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => - ParameterValue = parameterValue; -} - -#endif diff --git a/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs b/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs index c242c4a119..5c4cd8aff2 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs +++ b/test/Utilities/TestFramework.ForTestingMSTest/TestContainer.cs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - namespace TestFramework.ForTestingMSTest; /// @@ -83,6 +80,26 @@ public static Exception VerifyThrows( return null; } + public static async Task VerifyThrowsAsync( + Func taskGetter, + [CallerArgumentExpression(nameof(taskGetter))] string? expression = default, + [CallerMemberName] string? caller = default, + [CallerFilePath] string? filePath = default, + [CallerLineNumber] int lineNumber = default) + { + try + { + await taskGetter(); + } + catch (Exception ex) + { + return ex; + } + + Throw(expression, caller, filePath, lineNumber); + return null; + } + public static T VerifyThrows( Action action, [CallerArgumentExpression(nameof(action))] @@ -105,6 +122,28 @@ public static T VerifyThrows( return null; } + public static async Task VerifyThrowsAsync( + Func taskGetter, + [CallerArgumentExpression(nameof(taskGetter))] + string? expression = default, + [CallerMemberName] string? caller = default, + [CallerFilePath] string? filePath = default, + [CallerLineNumber] int lineNumber = default) + where T : Exception + { + try + { + await taskGetter(); + } + catch (T ex) + { + return ex; + } + + Throw(expression, caller, filePath, lineNumber); + return null; + } + public static void Fail( [CallerMemberName] string? caller = default, [CallerFilePath] string? filePath = default, diff --git a/test/Utilities/TestFramework.ForTestingMSTest/TestFramework.ForTestingMSTest.csproj b/test/Utilities/TestFramework.ForTestingMSTest/TestFramework.ForTestingMSTest.csproj index 02afe9633d..9977742afb 100644 --- a/test/Utilities/TestFramework.ForTestingMSTest/TestFramework.ForTestingMSTest.csproj +++ b/test/Utilities/TestFramework.ForTestingMSTest/TestFramework.ForTestingMSTest.csproj @@ -10,6 +10,11 @@ + + + + +