Add initial implementation of Unit Test framework (#2)
Kudos to @Ellerbach who re-started this project and pulled it through!
This commit is contained in:
Родитель
fc8ef2037f
Коммит
afc6962021
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) .NET Foundation and Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="NuGet" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="Azure Artifacts nanoFramework dev" value="https://pkgs.dev.azure.com/nanoframework/feed/_packaging/sandbox/nuget/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
80
README.md
80
README.md
|
@ -1,2 +1,80 @@
|
|||
# nanoFramework.TestPlatform
|
||||
nanoFramework Unit Test platform
|
||||
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![NuGet](https://img.shields.io/nuget/dt/nanoframework.TestFramework.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoframework.TestFramework/) [![#yourfirstpr](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](https://github.com/nanoframework/Home/blob/master/CONTRIBUTING.md) [![Discord](https://img.shields.io/discord/478725473862549535.svg?logo=discord&logoColor=white&label=Discord&color=7289DA)](https://discord.gg/gCyBu8T)
|
||||
|
||||
![nanoFramework logo](https://github.com/nanoframework/Home/blob/master/resources/logo/nanoFramework-repo-logo.png)
|
||||
|
||||
-----
|
||||
|
||||
# Welcome to the **nanoFramework** Unit Test Framework repository!
|
||||
|
||||
## Build status
|
||||
|
||||
| Component | Build Status | NuGet Package |
|
||||
|:-|---|---|
|
||||
| nanoframework.TestFramework | [![Build Status](https://dev.azure.com/nanoframework/nanoframework.TestFramework/_apis/build/status/nanoFramework.TestFramework?repoName=nanoframework%2FnanoFramework.TestFramework&branchName=master)](https://dev.azure.com/nanoframework/nanoframework.TestFramework/_build/latest?definitionId=67&repoName=nanoframework%2FnanoFramework.TestFramework&branchName=master) | [![NuGet](https://img.shields.io/nuget/v/nanoframework.TestFramework.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoframework.TestFramework/) |
|
||||
| nanoframework.TestFramework (preview) | [![Build Status](https://dev.azure.com/nanoframework/nanoframework.TestFramework/_apis/build/status/nanoFramework.TestFramework?repoName=nanoframework%2FnanoFramework.TestFramework&branchName=develop)](https://dev.azure.com/nanoframework/nanoframework.TestFramework/_build/latest?definitionId=67&repoName=nanoframework%2FnanoFramework.TestFramework&branchName=develop) | [![](https://badgen.net/badge/NuGet/preview/D7B023?icon=https://simpleicons.now.sh/azuredevops/fff)](https://dev.azure.com/nanoframework/feed/_packaging?_a=package&feed=sandbox&package=nanoframework.TestFramework&protocolType=NuGet&view=overview) |
|
||||
|
||||
## What is the .NET nanoFramework Test Framework
|
||||
|
||||
nanoFramework TestFramework it's a Unit Test framework dedicated to .NET nanoFramework! It has all the benefits of what you're used to when using Microsoft Test platform for .NET or XUnit or any other!
|
||||
|
||||
The framework includes multiple elements that are distributed in a single NuGet package!
|
||||
|
||||
- `nanoFramework.TestFramework` which contains the attributes to decorate your code and the `Assert` classes to check that you're code is properly doing what's expected.
|
||||
- `nanoFramework.UnitTestLauncher` which is the engine launching and managing the Unit Tests.
|
||||
- `nanoFramework.TestAdapter` which is the Visual Studio Test platform adapter, allowing to have the test integration in Visual Studio.
|
||||
|
||||
The integration looks like that:
|
||||
|
||||
![test integration](assets/test-integration-vs.jpg)
|
||||
|
||||
And the integration will point you up to your code for successful or failed tests:
|
||||
|
||||
![test integration failed](assets/test-integration-vs-failed.jpg)
|
||||
|
||||
## Usage of .NET nanoFramework Test Framework
|
||||
|
||||
Simply add the `nanoFramework.TestFramework` nuget to your project and you're good to go!
|
||||
|
||||
![test nuget](assets/test-nuget-test-framework.jpg)
|
||||
|
||||
Once you'll build your project, the tests will be automatically discovered:
|
||||
|
||||
![test discovered](assets/test-discovered.jpg)
|
||||
|
||||
You can then run all the tests and you'll get the result:
|
||||
|
||||
![test success](assets/test-success.jpg)
|
||||
|
||||
To have more details on usage of the framework, please refer to the detailed [documentation here](https://docs.nanoframework.net/).
|
||||
|
||||
## What you'll find in this repository
|
||||
|
||||
This repository contains the source of the core elements. You'll find them in `sources` directory. The Visual Studio projects in the root directory will open those elements.
|
||||
|
||||
## Sample pack
|
||||
|
||||
You can find on our samples repo a [sample pack](https://github.com/nanoframework/Samples/tree/master/samples/UnitTest) with projects demoing how to use the Unit Test Framework.
|
||||
|
||||
## Feedback and documentation
|
||||
|
||||
For documentation, providing feedback, issues and finding out how to contribute please refer to the [Home repo](https://github.com/nanoframework/Home).
|
||||
|
||||
Join our Discord community [here](https://discord.gg/gCyBu8T).
|
||||
|
||||
## Credits
|
||||
|
||||
The list of contributors to this project can be found at [CONTRIBUTORS](https://github.com/nanoframework/Home/blob/master/CONTRIBUTORS.md).
|
||||
|
||||
## License
|
||||
|
||||
The .NET **nanoFramework** Test Framework is licensed under the [MIT license](LICENSE.md).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
|
||||
For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
|
||||
|
||||
### .NET Foundation
|
||||
|
||||
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.0 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 57 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 12 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 73 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 40 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 59 KiB |
|
@ -0,0 +1,321 @@
|
|||
trigger:
|
||||
branches:
|
||||
include: [master, develop, "release-*" ]
|
||||
paths:
|
||||
exclude: ["*.md", .gitignore]
|
||||
tags:
|
||||
include: ["v*"]
|
||||
|
||||
# PR always trigger build
|
||||
|
||||
# add nf-tools repo to resources (for Azure Pipelines templates)
|
||||
resources:
|
||||
repositories:
|
||||
- repository: templates
|
||||
type: github
|
||||
name: nanoframework/nf-tools
|
||||
endpoint: nanoframework
|
||||
|
||||
jobs:
|
||||
|
||||
##############################
|
||||
- job: Get_Build_Flags
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
# get commit message
|
||||
- powershell: |
|
||||
|
||||
# default to false
|
||||
$update = $false
|
||||
|
||||
if($env:Build_Reason -eq 'PullRequest')
|
||||
{
|
||||
# PR build, nothing interesting in commit message
|
||||
}
|
||||
else
|
||||
{
|
||||
# build is not from a PR
|
||||
|
||||
# get commit ID
|
||||
$commitId = git rev-parse HEAD
|
||||
|
||||
# get PR associate with commit
|
||||
$prUrl = "https://api.github.com/repos/nanoframework/nanoFramework.TestFramework/commits/$commitId/pulls"
|
||||
$commit = Invoke-RestMethod -Uri $prUrl -ContentType "application/json" -Headers @{"Accept"="application/vnd.github.groot-preview+json"} -Method GET
|
||||
|
||||
if($commit -ne $null)
|
||||
{
|
||||
# there is a PR, check labels
|
||||
$updateDependents = $commit.labels | where {$_.Name -eq 'CI: Update Dependents'}
|
||||
if($updateDependents -ne $null)
|
||||
{
|
||||
$update = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# set variable to foward to jobs
|
||||
echo "##vso[task.setvariable variable=RUN_UPDATE_DEPENDENTS;isOutput=true]$update"
|
||||
name: GetPRLabels
|
||||
displayName: Check build labels
|
||||
|
||||
#########################################################
|
||||
# build
|
||||
- job: Build_Test_Framework
|
||||
condition: eq(variables['UPDATE_DEPENDENTS'], 'false')
|
||||
|
||||
dependsOn:
|
||||
- Get_Build_Flags
|
||||
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
|
||||
variables:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
buildPlatform: 'Any CPU'
|
||||
buildConfiguration: 'Debug'
|
||||
|
||||
steps:
|
||||
|
||||
# need this here in order to persist GitHub credentials, do a shallow fetch AND init submodules
|
||||
- checkout: self
|
||||
persistCredentials: true
|
||||
|
||||
- script: |
|
||||
git config --global user.email "nanoframework@outlook.com"
|
||||
git config --global user.name "nfbot"
|
||||
displayName: Setup git identity
|
||||
|
||||
- task: NuGetToolInstaller@1
|
||||
condition: not(variables['StartReleaseCandidate'])
|
||||
displayName: 'Install specific version of NuGet'
|
||||
inputs:
|
||||
versionSpec: '5.8.0'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
condition: ne(variables['system.pullrequest.isfork'], true)
|
||||
displayName: Install NBGV tool
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
arguments: install -g nbgv
|
||||
|
||||
- task: InstallnFBuildComponents@1
|
||||
condition: ne( variables['StartReleaseCandidate'], true )
|
||||
displayName: Install nanoFramework MSBuild components
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet restore Test Adapter
|
||||
inputs:
|
||||
restoreSolution: 'nanoFramework.TestAdapter.sln'
|
||||
feedsToUse: config
|
||||
nugetConfigPath: 'NuGet.config'
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet restore Test Framework
|
||||
inputs:
|
||||
restoreSolution: 'nanoFramework.TestFramework.sln'
|
||||
feedsToUse: config
|
||||
nugetConfigPath: 'NuGet.config'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: Build Test Adapter
|
||||
inputs:
|
||||
solution: 'nanoFramework.TestAdapter.sln'
|
||||
platform: '$(buildPlatform)'
|
||||
msbuildArgs: '/p:PublicRelease=true'
|
||||
configuration: '$(buildConfiguration)'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: Build Test Framework
|
||||
inputs:
|
||||
solution: 'nanoFramework.TestFramework.sln'
|
||||
platform: '$(buildPlatform)'
|
||||
msbuildArgs: '/p:PublicRelease=true'
|
||||
configuration: '$(buildConfiguration)'
|
||||
|
||||
# - task: VisualStudioTestPlatformInstaller@1
|
||||
# condition: succeeded()
|
||||
# displayName: 'Visual Studio Test Platform Installer'
|
||||
# inputs:
|
||||
# versionSelector: latestStable
|
||||
|
||||
# - task: VSTest@2
|
||||
# condition: succeeded()
|
||||
# displayName: 'Running tests'
|
||||
# inputs:
|
||||
# testSelector: 'testAssemblies'
|
||||
# testAssemblyVer2: |
|
||||
# **\*Tests*.dll
|
||||
# !**\*TestAdapter*.dll
|
||||
# !**\*TestFramework*.dll
|
||||
# !**\obj\**
|
||||
# searchFolder: '$(System.DefaultWorkingDirectory)'
|
||||
# platform: '$(BuildPlatform)'
|
||||
# configuration: '$(BuildConfiguration)'
|
||||
# diagnosticsEnabled: true
|
||||
# vsTestVersion: toolsInstaller
|
||||
# codeCoverageEnabled: true
|
||||
|
||||
# update could build number (only possible if this is not a PR from a fork)
|
||||
- task: PowerShell@2
|
||||
condition: and( succeeded(), ne(variables['system.pullrequest.isfork'], true) )
|
||||
displayName: Update cloud build number
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: Write-Host "$("##vso[build.updatebuildnumber]")$env:NBGV_NuGetPackageVersion"
|
||||
|
||||
- task: NuGetCommand@2
|
||||
condition: succeeded()
|
||||
displayName: Pack NuGet with Test Framework
|
||||
inputs:
|
||||
command: 'custom'
|
||||
arguments: 'pack source\package.nuspec -Version $(NBGV_NuGetPackageVersion) -properties commit="$(Build.SourceVersion)"'
|
||||
|
||||
- task: CopyFiles@1
|
||||
condition: succeeded()
|
||||
displayName: Collecting NuGet package artifact
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)
|
||||
Contents: |
|
||||
**\nanoFramework.TestFramework*.nupkg
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)'
|
||||
flattenFolders: true
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: Install SignTool tool
|
||||
condition: and( succeeded(), eq(variables['System.PullRequest.PullRequestId'], '') )
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
arguments: install --tool-path . SignClient
|
||||
|
||||
- pwsh: |
|
||||
.\SignClient "Sign" `
|
||||
--baseDirectory "$(Build.ArtifactStagingDirectory)" `
|
||||
--input "**/*.nupkg" `
|
||||
--config "$(Build.Repository.LocalPath)\config\SignClient.json" `
|
||||
--filelist "$(Build.Repository.LocalPath)\config\filelist.txt" `
|
||||
--user "$(SignClientUser)" `
|
||||
--secret '$(SignClientSecret)' `
|
||||
--name "Test Framework" `
|
||||
--description "Test Framework" `
|
||||
--descriptionUrl "https://github.com/$env:Build_Repository_Name"
|
||||
displayName: Sign packages
|
||||
continueOnError: true
|
||||
condition: and( succeeded(), eq(variables['System.PullRequest.PullRequestId'], '') )
|
||||
|
||||
# publish artifacts (only possible if this is not a PR originated on a fork)
|
||||
- task: PublishBuildArtifacts@1
|
||||
condition: and( succeeded(), ne(variables['system.pullrequest.isfork'], true) )
|
||||
displayName: Publish deployables artifacts
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: deployables
|
||||
ArtifactType: Container
|
||||
|
||||
# push NuGet packages to Azure Artifacts feed (always happens except on PR builds)
|
||||
- task: NuGetCommand@2
|
||||
displayName: Push NuGet packages to Azure Artifacts
|
||||
condition: and( succeeded(), eq(variables['System.PullRequest.PullRequestId'], '') )
|
||||
continueOnError: true
|
||||
inputs:
|
||||
command: push
|
||||
nuGetFeedType: external
|
||||
packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
|
||||
publishFeedCredentials: 'AzureArtifacts-$(System.TeamProject)'
|
||||
allowPackageConflicts: true
|
||||
|
||||
# push NuGet class lib package to NuGet (always happens except on PR builds)
|
||||
- task: NuGetCommand@2
|
||||
condition: and( succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne( variables['StartReleaseCandidate'], true ) )
|
||||
continueOnError: true
|
||||
displayName: Push NuGet packages to NuGet
|
||||
inputs:
|
||||
command: push
|
||||
nuGetFeedType: external
|
||||
allowPackageConflicts: true
|
||||
packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
|
||||
publishFeedCredentials: 'NuGet-$(System.TeamProject)'
|
||||
|
||||
# create or update GitHub release
|
||||
- task: GitHubReleasePublish@1
|
||||
inputs:
|
||||
githubEndpoint: 'nanoFramework'
|
||||
githubOwner: 'nanoframework'
|
||||
githubRepositoryName: 'nanoFramework.TestPlatform'
|
||||
githubTag: v$(GitBuildVersionSimple)
|
||||
githubReleaseTitle: '.NET nanoFramework Unit Test Framework v$(GitBuildVersionSimple)'
|
||||
githubReleaseNotes: 'add description here'
|
||||
githubTargetCommitsh: $(Build.SourceVersion)
|
||||
githubReleaseDraft: false
|
||||
githubReleasePrerelease: true
|
||||
githubReuseDraftOnly: false
|
||||
githubReuseRelease: true
|
||||
githubEditRelease: true
|
||||
githubReleaseAsset: '$(Build.ArtifactStagingDirectory)/*.nupkg'
|
||||
condition: and( succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), not( startsWith(variables['Build.SourceBranch'], 'refs/tags/v') ) )
|
||||
displayName: Create/Update GitHub release
|
||||
|
||||
##############################
|
||||
- job: Update_Dependents
|
||||
condition: and( or( eq(dependencies.Get_Build_Flags.outputs['GetPRLabels.RUN_UPDATE_DEPENDENTS'], 'true'), eq(variables['UPDATE_DEPENDENTS'], 'true') ), not( startsWith(variables['Build.SourceBranch'], 'refs/tags/v') ) )
|
||||
|
||||
dependsOn:
|
||||
- Get_Build_Flags
|
||||
- Build_MDP
|
||||
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
|
||||
steps:
|
||||
# need this here in order to persist GitHub credentials
|
||||
- checkout: self
|
||||
fetchDepth: 100
|
||||
|
||||
- template: azure-pipelines-templates/install-nbgv-tool.yml@templates
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: 'Install specific version of NuGet'
|
||||
inputs:
|
||||
versionSpec: '5.8.0'
|
||||
|
||||
- script: nbgv cloud -a -c
|
||||
condition: succeeded()
|
||||
displayName: Set build number
|
||||
|
||||
# update dependencies
|
||||
- task: PowerShell@2
|
||||
displayName: Update dependent class libs
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: azure-pipelines/update-dependencies.ps1
|
||||
env:
|
||||
MY_GITHUB_TOKEN: $(GitHubToken)
|
||||
|
||||
##################################
|
||||
# report build failure to Discord
|
||||
- job: Report_Build_Failure
|
||||
dependsOn:
|
||||
- Build_MDP
|
||||
condition: failed('Build_MDP')
|
||||
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
|
||||
steps:
|
||||
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
# step from template @ nf-tools repo
|
||||
- template: azure-pipelines-templates/discord-webhook.yml@templates
|
||||
parameters:
|
||||
status: 'failure'
|
||||
webhookUrl: '$(DiscordWebhook)'
|
||||
message: ''
|
|
@ -0,0 +1,118 @@
|
|||
"Updating dependency at nf-Visual-Studio-extension" | Write-Host
|
||||
|
||||
# compute authorization header in format "AUTHORIZATION: basic 'encoded token'"
|
||||
# 'encoded token' is the Base64 of the string "nfbot:personal-token"
|
||||
$auth = "basic $([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("nfbot:$env:MY_GITHUB_TOKEN"))))"
|
||||
|
||||
# because it can take sometime for the package to become available on the NuGet providers
|
||||
# need to hang here for 1 minute (1 * 60)
|
||||
"Waiting 1 minute to let package process flow in Azure Artifacts feed..." | Write-Host
|
||||
Start-Sleep -Seconds 60
|
||||
|
||||
# init/reset these
|
||||
$commitMessage = ""
|
||||
$prTitle = ""
|
||||
$newBranchName = "develop-nfbot/update-dependencies/" + [guid]::NewGuid().ToString()
|
||||
$packageTargetVersion = $env:NBGV_NuGetPackageVersion
|
||||
|
||||
# working directory is agent temp directory
|
||||
Write-Debug "Changing working directory to $env:Agent_TempDirectory"
|
||||
Set-Location "$env:Agent_TempDirectory" | Out-Null
|
||||
|
||||
# clone repo and checkout develop branch
|
||||
Write-Debug "Init and featch nf-Visual-Studio-extension repo"
|
||||
|
||||
git clone --depth 1 https://github.com/nanoframework/nf-Visual-Studio-extension repo
|
||||
Set-Location repo | Out-Null
|
||||
git config --global gc.auto 0
|
||||
git config --global user.name nfbot
|
||||
git config --global user.email dependencybot@nanoframework.net
|
||||
git config --global core.autocrlf true
|
||||
|
||||
Write-Host "Checkout develop branch..."
|
||||
git checkout --quiet develop | Out-Null
|
||||
|
||||
####################
|
||||
# VS 2017
|
||||
|
||||
Write-Host "Updating nanoFramework.TestFramework package in VS2017 solution..."
|
||||
|
||||
dotnet remove VisualStudio.Extension\VisualStudio.Extension.csproj package nanoFramework.TestFramework
|
||||
|
||||
dotnet add VisualStudio.Extension\VisualStudio.Extension.csproj package nanoFramework.TestFramework --prerelease -s https://pkgs.dev.azure.com/nanoframework/feed/_packaging/sandbox/nuget/v3/index.json
|
||||
|
||||
####################
|
||||
# VS 2019
|
||||
|
||||
Write-Host "Updating nanoFramework.TestFramework package in VS2019 solution..."
|
||||
|
||||
dotnet remove VisualStudio.Extension-2019\VisualStudio.Extension.csproj package nanoFramework.TestFramework
|
||||
|
||||
dotnet add VisualStudio.Extension-2019\VisualStudio.Extension.csproj package nanoFramework.TestFramework --prerelease -s https://pkgs.dev.azure.com/nanoframework/feed/_packaging/sandbox/nuget/v3/index.json
|
||||
|
||||
#####################
|
||||
|
||||
"Bumping nanoFramework.TestFramework to $packageTargetVersion." | Write-Host -ForegroundColor Cyan
|
||||
|
||||
# build commit message
|
||||
$commitMessage += "Bumps nanoFramework.TestFramework to $packageTargetVersion.`n"
|
||||
# build PR title
|
||||
$prTitle = "Bumps nanoFramework.TestFramework to $packageTargetVersion"
|
||||
|
||||
# need this line so nfbot flags the PR appropriately
|
||||
$commitMessage += "`n[version update]`n`n"
|
||||
|
||||
# better add this warning line
|
||||
$commitMessage += "### :warning: This is an automated update. Merge only after all tests pass. :warning:`n"
|
||||
|
||||
Write-Debug "Git branch"
|
||||
|
||||
# create branch to perform updates
|
||||
git branch $newBranchName
|
||||
|
||||
Write-Debug "Checkout branch"
|
||||
|
||||
# checkout branch
|
||||
git checkout $newBranchName
|
||||
|
||||
Write-Debug "Add changes"
|
||||
|
||||
# commit changes
|
||||
git add -A > $null
|
||||
|
||||
Write-Debug "Commit changed files"
|
||||
|
||||
git commit -m "$prTitle ***NO_CI***" -m "$commitMessage" > $null
|
||||
|
||||
Write-Debug "Push changes"
|
||||
|
||||
git -c http.extraheader="AUTHORIZATION: $auth" push --set-upstream origin $newBranchName > $null
|
||||
|
||||
# start PR
|
||||
# we are hardcoding to 'develop' branch to have a fixed one
|
||||
# this is very important for tags (which don't have branch information)
|
||||
# considering that the base branch can be changed at the PR ther is no big deal about this
|
||||
$prRequestBody = @{title="$prTitle";body="$commitMessage";head="$newBranchName";base="develop"} | ConvertTo-Json
|
||||
$githubApiEndpoint = "https://api.github.com/repos/nanoframework/nf-Visual-Studio-extension/pulls"
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
$headers = @{}
|
||||
$headers.Add("Authorization","$auth")
|
||||
$headers.Add("Accept","application/vnd.github.symmetra-preview+json")
|
||||
|
||||
try
|
||||
{
|
||||
$result = Invoke-RestMethod -Method Post -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::InternetExplorer -Uri $githubApiEndpoint -Header $headers -ContentType "application/json" -Body $prRequestBody
|
||||
'Started PR with dependencies update...' | Write-Host -NoNewline
|
||||
'OK' | Write-Host -ForegroundColor Green
|
||||
}
|
||||
catch
|
||||
{
|
||||
$result = $_.Exception.Response.GetResponseStream()
|
||||
$reader = New-Object System.IO.StreamReader($result)
|
||||
$reader.BaseStream.Position = 0
|
||||
$reader.DiscardBufferedData()
|
||||
$responseBody = $reader.ReadToEnd();
|
||||
|
||||
throw "Error starting PR: $responseBody"
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"SignClient": {
|
||||
"AzureAd": {
|
||||
"AADInstance": "https://login.microsoftonline.com/",
|
||||
"ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8",
|
||||
"TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e"
|
||||
},
|
||||
"Service": {
|
||||
"Url": "https://codesign.dotnetfoundation.org/",
|
||||
"ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
**/nanoFramework.*
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31005.135
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nanoFramework.TestAdapter", "source\TestAdapter\nanoFramework.TestAdapter.csproj", "{7B3026CA-1CBB-4F28-AC42-29CD22710877}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestFrameworkShared", "source\TestFrameworkShared\TestFrameworkShared.shproj", "{55F048B5-6739-43C5-A93D-DB61DB8E912F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
source\TestFrameworkShared\TestFrameworkShared.projitems*{55f048b5-6739-43c5-a93d-db61db8e912f}*SharedItemsImports = 13
|
||||
source\TestFrameworkShared\TestFrameworkShared.projitems*{7b3026ca-1cbb-4f28-ac42-29cd22710877}*SharedItemsImports = 5
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7B3026CA-1CBB-4F28-AC42-29CD22710877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7B3026CA-1CBB-4F28-AC42-29CD22710877}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7B3026CA-1CBB-4F28-AC42-29CD22710877}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7B3026CA-1CBB-4F28-AC42-29CD22710877}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8DC0B70D-F42F-4257-A336-60E93BE828D2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31005.135
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.UnitTestLauncher", "source\UnitTestLauncher\nanoFramework.UnitTestLauncher.nfproj", "{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}"
|
||||
EndProject
|
||||
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.TestFramework", "source\TestFramework\nanoFramework.TestFramework.nfproj", "{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TestFrameworkShared", "source\TestFrameworkShared\TestFrameworkShared.shproj", "{55F048B5-6739-43C5-A93D-DB61DB8E912F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
source\TestFrameworkShared\TestFrameworkShared.projitems*{55f048b5-6739-43c5-a93d-db61db8e912f}*SharedItemsImports = 13
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{897FC4EA-823D-4343-8CC6-B6F28C3FF91E}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D66A7774-AFAB-466B-9CFB-3485B02F4AF4}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {45FCB25A-0B73-4A11-B3AC-29A48E46AB9C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -1,53 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>ConsoleApp3</RootNamespace>
|
||||
<AssemblyName>ConsoleApp3</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ConsoleApp3
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
DateTime alarmTime = new DateTime(2018, 10, 29, 23, 30, 15);
|
||||
|
||||
// c
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
|
||||
Console.WriteLine($"Set alarm time to {alarmTime.ToString("s")}");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
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.
|
||||
[assembly: AssemblyTitle("ConsoleApp3")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ConsoleApp3")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("cd1a924a-d2b8-4e51-bd01-ea71e61976d0")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2019
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "NFApp3", "NFApp3\NFApp3.nfproj", "{78E81FA7-C1BF-4F65-A45D-9736CA479649}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp3", "ConsoleApp3\ConsoleApp3.csproj", "{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|ARM.Deploy.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|x64.Deploy.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Debug|x86.Deploy.0 = Debug|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|ARM.Deploy.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|x64.Build.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|x64.Deploy.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|x86.Build.0 = Release|Any CPU
|
||||
{78E81FA7-C1BF-4F65-A45D-9736CA479649}.Release|x86.Deploy.0 = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CD1A924A-D2B8-4E51-BD01-EA71E61976D0}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {78B87301-5203-4FB7-A6B0-758075F129BA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,56 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<NanoFrameworkProjectSystemPath>$(MSBuildToolsPath)..\..\..\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\packages\nanoFramework.TestAdapter.1.0.4\build\nanoFramework10\nanoFramework.MSTest.TestAdapter.props" />
|
||||
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
|
||||
<ProjectGuid>78e81fa7-c1bf-4f65-a45d-9736ca479649</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<RootNamespace>NFApp3</RootNamespace>
|
||||
<AssemblyName>NFApp3</AssemblyName>
|
||||
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<System_Debug>True</System_Debug>
|
||||
<!-- <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> -->
|
||||
<!-- <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> -->
|
||||
<!-- <IsCodedUITest>False</IsCodedUITest> -->
|
||||
<!-- <TestProjectType>UnitTest</TestProjectType> -->
|
||||
<!-- <UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion> -->
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="NFUnitTest1.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mscorlib, Version=1.0.1.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
|
||||
<HintPath>..\packages\nanoFramework.CoreLibrary.1.0.1-preview127\lib\mscorlib.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<SpecificVersion>True</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="nanoFramework.TestPlatform.TestFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f932dcc4cfccae11">
|
||||
<HintPath>..\packages\nanoFramework.TestFramework.1.0.0-preview010\lib\nanoFramework.TestPlatform.TestFramework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<SpecificVersion>True</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
|
||||
<ProjectExtensions>
|
||||
<ProjectCapabilities>
|
||||
<ProjectConfigurationsDeclaredAsItems />
|
||||
</ProjectCapabilities>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -1,14 +0,0 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
|
||||
namespace UnitTestProject1_mstest
|
||||
{
|
||||
[TestClass]
|
||||
public class UnitTest11
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestMethod11()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace NFApp3
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
// need this?
|
||||
//Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();
|
||||
|
||||
//Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments);
|
||||
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Babel Obfuscator MSBuild Project File
|
||||
Version 9.0.0.0
|
||||
-->
|
||||
<Project DefaultTargets="NFApp3" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<UsingTask TaskName="Babel" AssemblyName="Babel.Build, Version=9.0.0.0, Culture=neutral, PublicKeyToken=138d17b5bd621ab7" />
|
||||
<Target Name="NFApp3">
|
||||
<PropertyGroup>
|
||||
<BabelInputFile>bin\Debug\NFApp3.exe</BabelInputFile>
|
||||
<CleanAttributes>\[mscorlib\]System.Runtime.CompilerServices.CompilerGeneratedAttribute;\[mscorlib\]System.Diagnostics.DebuggerDisplayAttribute;\[mscorlib\]System.Diagnostics.DebuggerBrowsableAttribute;\[mscorlib\]System.Diagnostics.DebuggerNonUserCodeAttribute;\[mscorlib\]System.Diagnostics.DebuggerHiddenAttribute;\[mscorlib\]System.Diagnostics.DebuggerStepThroughAttribute;false</CleanAttributes>
|
||||
<ConstRemoval>false</ConstRemoval>
|
||||
<ControlFlowObfuscation>goto=true;switch=true;case=true;if=true;call=true;value=false;token=false;underflow=false;true</ControlFlowObfuscation>
|
||||
<CopyAttributes>false</CopyAttributes>
|
||||
<DeadCodeElimination>false</DeadCodeElimination>
|
||||
<DebuggingProtection>false</DebuggingProtection>
|
||||
<DisgregateRemoval>false</DisgregateRemoval>
|
||||
<DynamicProxy>None</DynamicProxy>
|
||||
<EmitInvalidOpcodes>false</EmitInvalidOpcodes>
|
||||
<EnableObfuscationAgent>true</EnableObfuscationAgent>
|
||||
<EnumRemoval>false</EnumRemoval>
|
||||
<FlattenNamespaces>true</FlattenNamespaces>
|
||||
<GenerateDebug>false</GenerateDebug>
|
||||
<GenerateLogFile>false</GenerateLogFile>
|
||||
<GenerateMapOutFile>false</GenerateMapOutFile>
|
||||
<ILIterations>3</ILIterations>
|
||||
<InlineExpansion>false</InlineExpansion>
|
||||
<Instrument>false</Instrument>
|
||||
<InstrumentEmptyMethods>true</InstrumentEmptyMethods>
|
||||
<Internalize>false</Internalize>
|
||||
<ModuleInitializer>false</ModuleInitializer>
|
||||
<MsilEncryption>false</MsilEncryption>
|
||||
<ObfuscateEvents>true</ObfuscateEvents>
|
||||
<ObfuscateFields>true</ObfuscateFields>
|
||||
<ObfuscateMethods>true</ObfuscateMethods>
|
||||
<ObfuscateParameters>true</ObfuscateParameters>
|
||||
<ObfuscateProperties>true</ObfuscateProperties>
|
||||
<ObfuscateTypes>true</ObfuscateTypes>
|
||||
<ObfuscateXaml>false</ObfuscateXaml>
|
||||
<OverloadedRenaming>false</OverloadedRenaming>
|
||||
<ResourceEncryption>false</ResourceEncryption>
|
||||
<SealClasses>false</SealClasses>
|
||||
<SearchSatelliteAssemblies>true</SearchSatelliteAssemblies>
|
||||
<ShowLogo>true</ShowLogo>
|
||||
<ShowStatistics>true</ShowStatistics>
|
||||
<StringEncryption>false</StringEncryption>
|
||||
<SuppressIldasm>false</SuppressIldasm>
|
||||
<SuppressReflection>false</SuppressReflection>
|
||||
<TamperingDetection>false</TamperingDetection>
|
||||
<UnicodeNormalization>true</UnicodeNormalization>
|
||||
<ValueEncryption>false</ValueEncryption>
|
||||
<VerboseLevel>1</VerboseLevel>
|
||||
<VirtualFunctions>true</VirtualFunctions>
|
||||
</PropertyGroup>
|
||||
<Message Text="Obfuscating $(BabelInputFile)..." />
|
||||
<Babel CleanAttributes="$(CleanAttributes)" ConstRemoval="$(ConstRemoval)" ControlFlowObfuscation="$(ControlFlowObfuscation)" CopyAttributes="$(CopyAttributes)" DeadCodeElimination="$(DeadCodeElimination)" DebuggingProtection="$(DebuggingProtection)" DisgregateRemoval="$(DisgregateRemoval)" DynamicProxy="$(DynamicProxy)" EmitInvalidOpcodes="$(EmitInvalidOpcodes)" EnableObfuscationAgent="$(EnableObfuscationAgent)" EnumRemoval="$(EnumRemoval)" FlattenNamespaces="$(FlattenNamespaces)" GenerateDebug="$(GenerateDebug)" GenerateLogFile="$(GenerateLogFile)" GenerateMapOutFile="$(GenerateMapOutFile)" ILIterations="$(ILIterations)" InlineExpansion="$(InlineExpansion)" InputFile="$(BabelInputFile)" Instrument="$(Instrument)" InstrumentEmptyMethods="$(InstrumentEmptyMethods)" Internalize="$(Internalize)" ModuleInitializer="$(ModuleInitializer)" MsilEncryption="$(MsilEncryption)" ObfuscateEvents="$(ObfuscateEvents)" ObfuscateFields="$(ObfuscateFields)" ObfuscateMethods="$(ObfuscateMethods)" ObfuscateParameters="$(ObfuscateParameters)" ObfuscateProperties="$(ObfuscateProperties)" ObfuscateTypes="$(ObfuscateTypes)" ObfuscateXaml="$(ObfuscateXaml)" OverloadedRenaming="$(OverloadedRenaming)" ResourceEncryption="$(ResourceEncryption)" SealClasses="$(SealClasses)" SearchSatelliteAssemblies="$(SearchSatelliteAssemblies)" ShowLogo="$(ShowLogo)" ShowStatistics="$(ShowStatistics)" StringEncryption="$(StringEncryption)" SuppressIldasm="$(SuppressIldasm)" SuppressReflection="$(SuppressReflection)" TamperingDetection="$(TamperingDetection)" UnicodeNormalization="$(UnicodeNormalization)" ValueEncryption="$(ValueEncryption)" VerboseLevel="$(VerboseLevel)" VirtualFunctions="$(VirtualFunctions)" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<RunConfiguration>
|
||||
<TargetPlatform>X86</TargetPlatform>
|
||||
<TargetFrameworkVersion>.NETFramework,Version=v4.6.1</TargetFrameworkVersion>
|
||||
</RunConfiguration>
|
||||
</RunSettings>
|
|
@ -1,9 +0,0 @@
|
|||
<RunSettings>
|
||||
<RunConfiguration>
|
||||
<TargetPlatform>X86</TargetPlatform>
|
||||
<DesignMode>False</DesignMode>
|
||||
<TargetFrameworkVersion>Framework40</TargetFrameworkVersion>
|
||||
<CollectSourceInformation>True</CollectSourceInformation>
|
||||
<TestSessionTimeout>10000</TestSessionTimeout>
|
||||
</RunConfiguration>
|
||||
</RunSettings>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="nanoFramework.CoreLibrary" version="1.0.1-preview127" targetFramework="netnanoframework10" />
|
||||
<package id="nanoFramework.TestAdapter" version="1.0.4" targetFramework="netnanoframework10" />
|
||||
<package id="nanoFramework.TestFramework" version="1.0.0-preview010" targetFramework="netnanoframework10" />
|
||||
</packages>
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31019.35
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "NFUnitTest", "TestOfTestFramework\NFUnitTest.nfproj", "{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FBD29C49-D7DC-425E-BAD1-1AE63484A6CD}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {280DA13F-AFF7-4D5D-BA7F-BFC5E8F19995}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ProjectCapability Include="TestContainer" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<NanoFrameworkProjectSystemPath>$(MSBuildToolsPath)..\..\..\nanoFramework\v1.0\</NanoFrameworkProjectSystemPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.Default.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectTypeGuids>{11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<ProjectGuid>fbd29c49-d7dc-425e-bad1-1ae63484a6cd</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<RootNamespace>NFUnitTest</RootNamespace>
|
||||
<AssemblyName>NFUnitTest</AssemblyName>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
|
||||
<PropertyGroup>
|
||||
<RunSettingsFilePath>$(MSBuildProjectDirectory)\nano.runsettings</RunSettingsFilePath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Test.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mscorlib, Version=1.10.1.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
|
||||
<HintPath>..\packages\nanoFramework.CoreLibrary.1.10.1-preview.9\lib\mscorlib.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<SpecificVersion>True</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="nanoFramework.TestFramework, Version=1.0.25.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
|
||||
<HintPath>..\packages\nanoFramework.TestFramework.1.0.25\lib\nanoFramework.TestFramework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<SpecificVersion>True</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="nanoFramework.UnitTestLauncher, Version=0.0.0.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
|
||||
<HintPath>..\packages\nanoFramework.TestFramework.1.0.25\lib\nanoFramework.UnitTestLauncher.exe</HintPath>
|
||||
<Private>True</Private>
|
||||
<SpecificVersion>True</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
|
||||
<ProjectExtensions>
|
||||
<ProjectCapabilities>
|
||||
<ProjectConfigurationsDeclaredAsItems />
|
||||
</ProjectCapabilities>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -31,3 +31,9 @@ using System.Runtime.InteropServices;
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// This attribute is mandatory when building Interop libraries //
|
||||
// update this whenever the native assembly signature changes //
|
||||
[assembly: AssemblyNativeVersion("1.0.0.0")]
|
||||
/////////////////////////////////////////////////////////////////
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace nanoFramework.TestFramework.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class TestOfTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestRaisesException()
|
||||
{
|
||||
Debug.WriteLine("Test will raise exception");
|
||||
Assert.Trows(typeof(Exception), ThrowMe);
|
||||
}
|
||||
|
||||
private void ThrowMe()
|
||||
{
|
||||
throw new Exception("Test failed and it's a shame");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCheckAllEqual()
|
||||
{
|
||||
Debug.WriteLine("Test will check that all the Equal are actually equal");
|
||||
// Arrange
|
||||
byte bytea = 42; byte byteb = 42;
|
||||
char chara = (char)42; char charb = (char)42;
|
||||
sbyte sbytea = 42; sbyte sbyteb = 42;
|
||||
int inta = 42; int intb = 42;
|
||||
uint uinta = 42; uint uintb = 42;
|
||||
long longa = 42; long longb = 42;
|
||||
ulong ulonga = 42; ulong ulongb = 42;
|
||||
bool boola = true; bool boolb = true;
|
||||
short shorta = 42; short shortb = 42;
|
||||
ushort ushorta = 42; ushort ushortb = 42;
|
||||
float floata = 42; float floatb = 42;
|
||||
int[] intArraya = new int[5] { 1, 2, 3, 4, 5 };
|
||||
int[] intArrayb = new int[5] { 1, 2, 3, 4, 5 };
|
||||
object obja = new object(); object objb = obja;
|
||||
string stra = "42"; string strb = "42";
|
||||
byte[] arrayempty = new byte[0];
|
||||
// Assert
|
||||
Assert.True(boola);
|
||||
Assert.Equal(bytea, byteb);
|
||||
Assert.Equal(chara, charb);
|
||||
Assert.Equal(sbytea, sbyteb);
|
||||
Assert.Equal(inta, intb);
|
||||
Assert.Equal(uinta, uintb);
|
||||
Assert.Equal(longa, longb);
|
||||
Assert.Equal(ulonga, ulongb);
|
||||
Assert.Equal(boola, boolb);
|
||||
Assert.Equal(shorta, shortb);
|
||||
Assert.Equal(ushorta, ushortb);
|
||||
Assert.Equal(floata, floatb);
|
||||
Assert.Equal(intArraya, intArrayb);
|
||||
Assert.Equal(stra, strb);
|
||||
Assert.Same(obja, objb);
|
||||
Assert.Empty(arrayempty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCheckAllNotEqual()
|
||||
{
|
||||
Debug.WriteLine("Test will check that all the NotEqual are actually equal");
|
||||
// Arrange
|
||||
byte bytea = 42; byte byteb = 43;
|
||||
char chara = (char)42; char charb = (char)43;
|
||||
sbyte sbytea = 42; sbyte sbyteb = 43;
|
||||
int inta = 42; int intb = 43;
|
||||
uint uinta = 42; uint uintb = 43;
|
||||
long longa = 42; long longb = 43;
|
||||
ulong ulonga = 42; ulong ulongb = 43;
|
||||
bool boola = true; bool boolb = false;
|
||||
short shorta = 42; short shortb = 43;
|
||||
ushort ushorta = 42; ushort ushortb = 43;
|
||||
float floata = 42; float floatb = 43;
|
||||
int[] intArraya = new int[5] { 1, 2, 3, 4, 5 };
|
||||
int[] intArrayb = new int[5] { 1, 2, 3, 4, 6 };
|
||||
int[] intArraybis = new int[4] { 1, 2, 3, 4 };
|
||||
int[] intArrayter = null;
|
||||
object obja = new object(); object objb = new object();
|
||||
string stra = "42"; string strb = "43";
|
||||
// Assert
|
||||
Assert.False(boolb);
|
||||
Assert.NotEqual(bytea, byteb);
|
||||
Assert.NotEqual(chara, charb);
|
||||
Assert.NotEqual(sbytea, sbyteb);
|
||||
Assert.NotEqual(inta, intb);
|
||||
Assert.NotEqual(uinta, uintb);
|
||||
Assert.NotEqual(longa, longb);
|
||||
Assert.NotEqual(ulonga, ulongb);
|
||||
Assert.NotEqual(boola, boolb);
|
||||
Assert.NotEqual(shorta, shortb);
|
||||
Assert.NotEqual(ushorta, ushortb);
|
||||
Assert.NotEqual(floata, floatb);
|
||||
Assert.NotEqual(intArraya, intArrayb);
|
||||
Assert.NotEqual(intArraya, intArraybis);
|
||||
Assert.NotEqual(intArraya, intArrayter);
|
||||
Assert.NotEqual(stra, strb);
|
||||
Assert.NotSame(obja, objb);
|
||||
Assert.NotEmpty(intArraya);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestNullEmpty()
|
||||
{
|
||||
Debug.WriteLine("Test null, not null, types");
|
||||
// Arrange
|
||||
object objnull = null;
|
||||
object objnotnull = new object();
|
||||
Type typea = typeof(int);
|
||||
Type typeb = typeof(int);
|
||||
Type typec = typeof(long);
|
||||
// Assert
|
||||
Assert.Null(objnull);
|
||||
Assert.NotNull(objnotnull);
|
||||
Assert.IsType(typea, typeb);
|
||||
Assert.IsNotType(typea, typec);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestStringComparison()
|
||||
{
|
||||
Debug.WriteLine("Test string, Contains, EndsWith, StartWith");
|
||||
// Arrange
|
||||
string tocontains = "this text contains and end with contains";
|
||||
string startcontains = "contains start this text";
|
||||
string contains = "contains";
|
||||
string doesnotcontains = "this is totally something else";
|
||||
string empty = string.Empty;
|
||||
string stringnull = null;
|
||||
// Assert
|
||||
Assert.Contains(contains, tocontains);
|
||||
Assert.DoesNotContains(contains, doesnotcontains);
|
||||
Assert.DoesNotContains(contains, empty);
|
||||
Assert.DoesNotContains(contains, stringnull);
|
||||
Assert.StartsWith(contains, startcontains);
|
||||
Assert.EndsWith(contains, tocontains);
|
||||
}
|
||||
|
||||
[Setup]
|
||||
public void RunSetup()
|
||||
{
|
||||
Debug.WriteLine("Setup");
|
||||
}
|
||||
|
||||
public void Nothing()
|
||||
{
|
||||
Debug.WriteLine("Nothing and should not be called");
|
||||
}
|
||||
|
||||
[Cleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
Debug.WriteLine("Cleanup");
|
||||
}
|
||||
}
|
||||
|
||||
public class SomthingElse
|
||||
{
|
||||
public void NothingReally()
|
||||
{
|
||||
Debug.WriteLine("Test failed: This would never get through");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace nanoFramework.TestFramework.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class TestOfTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestRaisesException()
|
||||
{
|
||||
Debug.WriteLine("Test will raise exception");
|
||||
Assert.Trows(typeof(Exception), ThrowMe);
|
||||
}
|
||||
|
||||
private void ThrowMe()
|
||||
{
|
||||
throw new Exception("Test failed and it's a shame");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCheckAllEqual()
|
||||
{
|
||||
Debug.WriteLine("Test will check that all the Equal are actually equal");
|
||||
// Arrange
|
||||
byte bytea = 42; byte byteb = 42;
|
||||
char chara = (char)42; char charb = (char)42;
|
||||
sbyte sbytea = 42; sbyte sbyteb = 42;
|
||||
int inta = 42; int intb = 42;
|
||||
uint uinta = 42; uint uintb = 42;
|
||||
long longa = 42; long longb = 42;
|
||||
ulong ulonga = 42; ulong ulongb = 42;
|
||||
bool boola = true; bool boolb = true;
|
||||
short shorta = 42; short shortb = 42;
|
||||
ushort ushorta = 42; ushort ushortb = 42;
|
||||
float floata = 42; float floatb = 42;
|
||||
int[] intArraya = new int[5] { 1, 2, 3, 4, 5 };
|
||||
int[] intArrayb = new int[5] { 1, 2, 3, 4, 5 };
|
||||
object obja = new object(); object objb = obja;
|
||||
string stra = "42"; string strb = "42";
|
||||
byte[] arrayempty = new byte[0];
|
||||
// Assert
|
||||
Assert.True(boola);
|
||||
Assert.Equal(bytea, byteb);
|
||||
Assert.Equal(chara, charb);
|
||||
Assert.Equal(sbytea, sbyteb);
|
||||
Assert.Equal(inta, intb);
|
||||
Assert.Equal(uinta, uintb);
|
||||
Assert.Equal(longa, longb);
|
||||
Assert.Equal(ulonga, ulongb);
|
||||
Assert.Equal(boola, boolb);
|
||||
Assert.Equal(shorta, shortb);
|
||||
Assert.Equal(ushorta, ushortb);
|
||||
Assert.Equal(floata, floatb);
|
||||
Assert.Equal(intArraya, intArrayb);
|
||||
Assert.Equal(stra, strb);
|
||||
Assert.Same(obja, objb);
|
||||
Assert.Empty(arrayempty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCheckAllNotEqual()
|
||||
{
|
||||
Debug.WriteLine("Test will check that all the NotEqual are actually equal");
|
||||
// Arrange
|
||||
byte bytea = 42; byte byteb = 43;
|
||||
char chara = (char)42; char charb = (char)43;
|
||||
sbyte sbytea = 42; sbyte sbyteb = 43;
|
||||
int inta = 42; int intb = 43;
|
||||
uint uinta = 42; uint uintb = 43;
|
||||
long longa = 42; long longb = 43;
|
||||
ulong ulonga = 42; ulong ulongb = 43;
|
||||
bool boola = true; bool boolb = false;
|
||||
short shorta = 42; short shortb = 43;
|
||||
ushort ushorta = 42; ushort ushortb = 43;
|
||||
float floata = 42; float floatb = 43;
|
||||
int[] intArraya = new int[5] { 1, 2, 3, 4, 5 };
|
||||
int[] intArrayb = new int[5] { 1, 2, 3, 4, 6 };
|
||||
int[] intArraybis = new int[4] { 1, 2, 3, 4 };
|
||||
int[] intArrayter = null;
|
||||
object obja = new object(); object objb = new object();
|
||||
string stra = "42"; string strb = "43";
|
||||
// Assert
|
||||
Assert.False(boolb);
|
||||
Assert.NotEqual(bytea, byteb);
|
||||
Assert.NotEqual(chara, charb);
|
||||
Assert.NotEqual(sbytea, sbyteb);
|
||||
Assert.NotEqual(inta, intb);
|
||||
Assert.NotEqual(uinta, uintb);
|
||||
Assert.NotEqual(longa, longb);
|
||||
Assert.NotEqual(ulonga, ulongb);
|
||||
Assert.NotEqual(boola, boolb);
|
||||
Assert.NotEqual(shorta, shortb);
|
||||
Assert.NotEqual(ushorta, ushortb);
|
||||
Assert.NotEqual(floata, floatb);
|
||||
Assert.NotEqual(intArraya, intArrayb);
|
||||
Assert.NotEqual(intArraya, intArraybis);
|
||||
Assert.NotEqual(intArraya, intArrayter);
|
||||
Assert.NotEqual(stra, strb);
|
||||
Assert.NotSame(obja, objb);
|
||||
Assert.NotEmpty(intArraya);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestNullEmpty()
|
||||
{
|
||||
Debug.WriteLine("Test null, not null, types");
|
||||
// Arrange
|
||||
object objnull = null;
|
||||
object objnotnull = new object();
|
||||
Type typea = typeof(int);
|
||||
Type typeb = typeof(int);
|
||||
Type typec = typeof(long);
|
||||
// Assert
|
||||
Assert.Null(objnull);
|
||||
Assert.NotNull(objnotnull);
|
||||
Assert.IsType(typea, typeb);
|
||||
Assert.IsNotType(typea, typec);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestStringComparison()
|
||||
{
|
||||
Debug.WriteLine("Test string, Contains, EndsWith, StartWith");
|
||||
// Arrange
|
||||
string tocontains = "this text contains and end with contains";
|
||||
string startcontains = "contains start this text";
|
||||
string contains = "contains";
|
||||
string doesnotcontains = "this is totally something else";
|
||||
string empty = string.Empty;
|
||||
string stringnull = null;
|
||||
// Assert
|
||||
Assert.Contains(contains, tocontains);
|
||||
Assert.DoesNotContains(contains, doesnotcontains);
|
||||
Assert.DoesNotContains(contains, empty);
|
||||
Assert.DoesNotContains(contains, stringnull);
|
||||
Assert.StartsWith(contains, startcontains);
|
||||
Assert.EndsWith(contains, tocontains);
|
||||
}
|
||||
|
||||
[Setup]
|
||||
public void RunSetup()
|
||||
{
|
||||
Debug.WriteLine("Setup");
|
||||
}
|
||||
|
||||
public void Nothing()
|
||||
{
|
||||
Debug.WriteLine("Nothing and should not be called");
|
||||
}
|
||||
|
||||
[Cleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
Debug.WriteLine("Cleanup");
|
||||
}
|
||||
}
|
||||
|
||||
public class SomthingElse
|
||||
{
|
||||
public void NothingReally()
|
||||
{
|
||||
Debug.WriteLine("Test failed: This would never get thru");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- Configurations that affect the Test Framework -->
|
||||
<RunConfiguration>
|
||||
<MaxCpuCount>1</MaxCpuCount>
|
||||
<ResultsDirectory>.\TestResults</ResultsDirectory><!-- Path relative to solution directory -->
|
||||
<TestSessionTimeout>120000</TestSessionTimeout><!-- Milliseconds -->
|
||||
<TargetFrameworkVersion>Framework40</TargetFrameworkVersion>
|
||||
</RunConfiguration>
|
||||
<nanoFrameworkAdapter>
|
||||
<Logging>None</Logging>
|
||||
</nanoFrameworkAdapter>
|
||||
</RunSettings>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="nanoFramework.CoreLibrary" version="1.10.1-preview.9" targetFramework="netnanoframework10" />
|
||||
<package id="nanoFramework.TestFramework" version="1.0.25" targetFramework="netnanoframework10" developmentDependency="true" />
|
||||
</packages>
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Content Include="$(MSBuildThisFileDirectory)..\_common\nanoFramework.TestPlatform.TestAdapter.dll">
|
||||
<Link>nanoFramework.TestPlatform.TestAdapter.dll</Link>
|
||||
<Content Include="$(MSBuildThisFileDirectory)..\_common\nanoFramework.TestAdapter.dll">
|
||||
<Link>nanoFramework.TestPlatform.dll</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Visible>False</Visible>
|
||||
</Content>
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|AnyCPU">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|AnyCPU">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Dependency Include="Microsoft.VisualStudio.TestPlatform.ObjectModel">
|
||||
<Version>[14.0.0]</Version>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
</Dependency>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\TestAdapter\bin\$(Configuration)\nanoFramework.TestPlatform.TestAdapter.dll">
|
||||
<Link>build\_common\nanoFramework.TestPlatform.TestAdapter.dll</Link>
|
||||
</Content>
|
||||
<Content Include="..\Build\nanoFramework.MSTest.TestAdapter.props">
|
||||
<Link>build\nanoFramework10\nanoFramework.MSTest.TestAdapter.props</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>9607C081-2415-465C-8424-1D8C28D4C0AC</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<NuProjPath>..\packages\NuProj.0.20.4-beta\tools\</NuProjPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NuProjPath)\NuProj.props" Condition="Exists('$(NuProjPath)\NuProj.props')" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Id>nanoFramework.TestAdapter</Id>
|
||||
<Version>1.0.4</Version>
|
||||
<Title>nanoFramework.TestAdapter</Title>
|
||||
<Authors>jassimoes</Authors>
|
||||
<Owners>jassimoes</Owners>
|
||||
<Summary>nanoFramework.TestAdapter</Summary>
|
||||
<Description>nanoFramework.TestAdapter</Description>
|
||||
<ReleaseNotes>
|
||||
</ReleaseNotes>
|
||||
<ProjectUrl>
|
||||
</ProjectUrl>
|
||||
<LicenseUrl>
|
||||
</LicenseUrl>
|
||||
<Copyright>Copyright © jassimoes</Copyright>
|
||||
<Tags>nanoFramework.TestAdapter</Tags>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NuProjPath)\NuProj.targets" />
|
||||
</Project>
|
|
@ -1,52 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|AnyCPU">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|AnyCPU">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>AnyCPU</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\TestFramework\bin\$(Configuration)\nanoFramework.TestPlatform.TestFramework.dll">
|
||||
<Link>lib\nanoFramework.TestPlatform.TestFramework.dll</Link>
|
||||
</Content>
|
||||
<Content Include="..\TestFramework\bin\$(Configuration)\nanoFramework.TestPlatform.TestFramework.pdb">
|
||||
<Link>lib\nanoFramework.TestPlatform.TestFramework.pdb</Link>
|
||||
</Content>
|
||||
<Content Include="..\TestFramework\bin\$(Configuration)\nanoFramework.TestPlatform.TestFramework.pdbx">
|
||||
<Link>lib\nanoFramework.TestPlatform.TestFramework.pdbx</Link>
|
||||
</Content>
|
||||
<Content Include="..\TestFramework\bin\$(Configuration)\nanoFramework.TestPlatform.TestFramework.pe">
|
||||
<Link>lib\nanoFramework.TestPlatform.TestFramework.pe</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>9d39a999-0427-4238-b882-4878e3edb9ad</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<NuProjPath>..\packages\NuProj.0.20.4-beta\tools\</NuProjPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NuProjPath)\NuProj.props" Condition="Exists('$(NuProjPath)\NuProj.props')" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Id>nanoFramework.TestFramework</Id>
|
||||
<Version>1.0.0-preview010</Version>
|
||||
<Title>nanoFramework.TestFramework</Title>
|
||||
<Authors>jassimoes</Authors>
|
||||
<Owners>jassimoes</Owners>
|
||||
<Summary>nanoFramework.TestFramework</Summary>
|
||||
<Description>nanoFramework.TestFramework</Description>
|
||||
<ReleaseNotes>
|
||||
</ReleaseNotes>
|
||||
<ProjectUrl>
|
||||
</ProjectUrl>
|
||||
<LicenseUrl>
|
||||
</LicenseUrl>
|
||||
<Copyright>Copyright © jassimoes</Copyright>
|
||||
<Tags>nanoFramework.TestFramework</Tags>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(NuProjPath)\NuProj.targets" />
|
||||
</Project>
|
|
@ -1,682 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.PlatformServices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Helps resolve MSTestFramework assemblies for CLR loader.
|
||||
/// The idea is that Unit Test Adapter creates App Domain for running tests and sets AppBase to tests dir.
|
||||
/// Since we don't want to put our assemblies to GAC and they are not in tests dir, we use custom way to resolve them.
|
||||
/// </summary>
|
||||
public class AssemblyResolver : MarshalByRefObject, IDisposable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This will have the list of all directories read from runsettings.
|
||||
/// </summary>
|
||||
private Queue<RecursiveDirectoryPath> directoryList;
|
||||
|
||||
/// <summary>
|
||||
/// The directories to look for assemblies to resolve.
|
||||
/// </summary>
|
||||
private List<string> searchDirectories;
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of Assemblies discovered to date.
|
||||
/// </summary>
|
||||
private Dictionary<string, Assembly> resolvedAssemblies = new Dictionary<string, Assembly>();
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of Reflection-Only Assemblies discovered to date.
|
||||
/// </summary>
|
||||
private Dictionary<string, Assembly> reflectionOnlyResolvedAssemblies = new Dictionary<string, Assembly>();
|
||||
|
||||
/// <summary>
|
||||
/// lock for the loaded assemblies cache.
|
||||
/// </summary>
|
||||
private object syncLock = new object();
|
||||
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssemblyResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="directories">
|
||||
/// A list of directories for resolution path
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// If there are additonal paths where a recursive search is required
|
||||
/// call AddSearchDirectoryFromRunSetting method with that list.
|
||||
/// </remarks>
|
||||
public AssemblyResolver(
|
||||
IList<string> directories
|
||||
)
|
||||
{
|
||||
if (directories == null || directories.Count == 0)
|
||||
{
|
||||
throw new ArgumentNullException("directories");
|
||||
}
|
||||
|
||||
this.searchDirectories = new List<string>(directories);
|
||||
this.directoryList = new Queue<RecursiveDirectoryPath>();
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.OnResolve);
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(this.ReflectionOnlyOnResolve);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="AssemblyResolver"/> class.
|
||||
/// </summary>
|
||||
~AssemblyResolver()
|
||||
{
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="object"/>.
|
||||
/// </returns>
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// It will add a list of search directories path with property recursive/non-recursive in assembly resolver .
|
||||
/// </summary>
|
||||
/// <param name="recursiveDirectoryPath">
|
||||
/// The recursive Directory Path.
|
||||
/// </param>
|
||||
public void AddSearchDirectoriesFromRunSetting(
|
||||
List<RecursiveDirectoryPath> recursiveDirectoryPath
|
||||
)
|
||||
{
|
||||
// Enqueue elements from the list in Queue
|
||||
if (recursiveDirectoryPath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var recPath in recursiveDirectoryPath)
|
||||
{
|
||||
this.directoryList.Enqueue(recPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assembly Resolve event handler for App Domain - called when CLR loader cannot resolve assembly.
|
||||
/// </summary>
|
||||
/// <param name="sender"> The sender App Domain. </param>
|
||||
/// <param name="args"> The args. </param>
|
||||
/// <returns> The <see cref="Assembly"/>. </returns>
|
||||
internal Assembly ReflectionOnlyOnResolve(
|
||||
object sender,
|
||||
ResolveEventArgs args
|
||||
)
|
||||
{
|
||||
return this.OnResolveInternal(sender, args, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assembly Resolve event handler for App Domain - called when CLR loader cannot resolve assembly.
|
||||
/// </summary>
|
||||
/// <param name="sender"> The sender App Domain. </param>
|
||||
/// <param name="args"> The args. </param>
|
||||
/// <returns> The <see cref="Assembly"/>. </returns>
|
||||
internal Assembly OnResolve(
|
||||
object sender,
|
||||
ResolveEventArgs args
|
||||
)
|
||||
{
|
||||
return this.OnResolveInternal(sender, args, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the subdirectories of the provided path to the collection.
|
||||
/// </summary>
|
||||
/// <param name="path"> Path go get subdirectories for. </param>
|
||||
/// <param name="searchDirectories"> The search Directories. </param>
|
||||
internal void AddSubdirectories(
|
||||
string path,
|
||||
List<string> searchDirectories
|
||||
)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(path), "'path' cannot be null or empty.");
|
||||
Debug.Assert(searchDirectories != null, "'searchDirectories' cannot be null.");
|
||||
|
||||
// If the directory exists, get it's subdirectories
|
||||
if (this.DoesDirectoryExist(path))
|
||||
{
|
||||
// Get the directories in the path provided.
|
||||
var directories = this.GetDirectories(path);
|
||||
|
||||
// Add each directory and its subdirectories to the collection.
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
searchDirectories.Add(directory);
|
||||
|
||||
this.AddSubdirectories(directory, searchDirectories);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The dispose.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// The disposing.
|
||||
/// </param>
|
||||
protected virtual void Dispose(
|
||||
bool disposing
|
||||
)
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// cleanup Managed resourceslike calling dispose on other managed object created.
|
||||
AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(this.OnResolve);
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= new ResolveEventHandler(this.ReflectionOnlyOnResolve);
|
||||
}
|
||||
|
||||
// cleanup native resources
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies if a directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the directory.</param>
|
||||
/// <returns>True if the directory exists.</returns>
|
||||
/// <remarks>Only present for unit testing scenarios.</remarks>
|
||||
protected virtual bool DoesDirectoryExist(
|
||||
string path
|
||||
)
|
||||
{
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directories from a path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the directory.</param>
|
||||
/// <returns>A list of directories in path.</returns>
|
||||
/// <remarks>Only present for unit testing scenarios.</remarks>
|
||||
protected virtual string[] GetDirectories(
|
||||
string path
|
||||
)
|
||||
{
|
||||
return Directory.GetDirectories(path);
|
||||
}
|
||||
|
||||
protected virtual bool DoesFileExist(
|
||||
string filePath
|
||||
)
|
||||
{
|
||||
return File.Exists(filePath);
|
||||
}
|
||||
|
||||
protected virtual Assembly LoadAssemblyFrom(
|
||||
string path
|
||||
)
|
||||
{
|
||||
return Assembly.LoadFrom(path);
|
||||
}
|
||||
|
||||
protected virtual Assembly ReflectionOnlyLoadAssemblyFrom(
|
||||
string path
|
||||
)
|
||||
{
|
||||
return Assembly.ReflectionOnlyLoadFrom(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// It will search for a particular assembly in the given list of directory.
|
||||
/// </summary>
|
||||
/// <param name="searchDirectorypaths"> The search Directorypaths. </param>
|
||||
/// <param name="name"> The name. </param>
|
||||
/// <param name="isReflectionOnly"> Indicates whether this is called under a Reflection Only Load context. </param>
|
||||
/// <returns> The <see cref="Assembly"/>. </returns>
|
||||
protected virtual Assembly SearchAssembly(
|
||||
List<string> searchDirectorypaths,
|
||||
string name,
|
||||
bool isReflectionOnly
|
||||
)
|
||||
{
|
||||
if (searchDirectorypaths == null || searchDirectorypaths.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// args.Name is like: "Microsoft.VisualStudio.TestTools.Common, Version=[VersionMajor].0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
|
||||
AssemblyName requestedName = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Can throw ArgumentException, FileLoadException if arg is empty/wrong format, etc. Should not return null.
|
||||
requestedName = new AssemblyName(name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.SafeLog(
|
||||
name,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info(
|
||||
"AssemblyResolver: {0}: Failed to create assemblyName. Reason:{1} ",
|
||||
name,
|
||||
ex);
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Assert(requestedName != null && !string.IsNullOrEmpty(requestedName.Name), "AssemblyResolver.OnResolve: requested is null or name is empty!");
|
||||
|
||||
foreach (var dir in searchDirectorypaths)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dir))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.SafeLog(
|
||||
name,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsVerboseEnabled)
|
||||
{
|
||||
EqtTrace.Verbose("AssemblyResolver: Searching assembly: {0} in the directory: {1}", requestedName.Name, dir);
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var extension in new string[] { ".dll", ".exe" })
|
||||
{
|
||||
var assemblyPath = Path.Combine(dir, requestedName.Name + extension);
|
||||
|
||||
var assembly = this.SearchAndLoadAssembly(assemblyPath, name, requestedName, isReflectionOnly);
|
||||
if (assembly != null)
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that found assembly name matches requested to avoid security issues.
|
||||
/// Looks only at PublicKeyToken and Version, empty matches anything.
|
||||
/// </summary>
|
||||
/// <param name="requestedName"> The requested Name. </param>
|
||||
/// <param name="foundName"> The found Name. </param>
|
||||
/// <returns> The <see cref="bool"/>. </returns>
|
||||
private static bool RequestedAssemblyNameMatchesFound(
|
||||
AssemblyName requestedName,
|
||||
AssemblyName foundName
|
||||
)
|
||||
{
|
||||
Debug.Assert(requestedName != null, "requested assembly name should not be null.");
|
||||
Debug.Assert(foundName != null, "found assembly name should not be null.");
|
||||
|
||||
var requestedPublicKey = requestedName.GetPublicKeyToken();
|
||||
if (requestedPublicKey != null)
|
||||
{
|
||||
var foundPublicKey = foundName.GetPublicKeyToken();
|
||||
if (foundPublicKey == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < requestedPublicKey.Length; ++i)
|
||||
{
|
||||
if (requestedPublicKey[i] != foundPublicKey[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requestedName.Version == null || requestedName.Version.Equals(foundName.Version);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assembly Resolve event handler for App Domain - called when CLR loader cannot resolve assembly.
|
||||
/// </summary>
|
||||
/// <param name="senderAppDomain"> The sender App Domain. </param>
|
||||
/// <param name="args"> The args. </param>
|
||||
/// <param name="isReflectionOnly"> Indicates whether this is called under a Reflection Only Load context. </param>
|
||||
/// <returns> The <see cref="Assembly"/>. </returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "senderAppDomain", Justification = "This is an event handler.")]
|
||||
private Assembly OnResolveInternal(
|
||||
object senderAppDomain,
|
||||
ResolveEventArgs args,
|
||||
bool isReflectionOnly
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrEmpty(args?.Name))
|
||||
{
|
||||
Debug.Assert(false, "AssemblyResolver.OnResolve: args.Name is null or empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.SafeLog(
|
||||
args.Name,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info("AssemblyResolver: Resolving assembly: {0}.", args.Name);
|
||||
}
|
||||
});
|
||||
|
||||
string assemblyNameToLoad = AppDomain.CurrentDomain.ApplyPolicy(args.Name);
|
||||
|
||||
this.SafeLog(
|
||||
assemblyNameToLoad,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info("AssemblyResolver: Resolving assembly after applying policy: {0}.", assemblyNameToLoad);
|
||||
}
|
||||
});
|
||||
|
||||
lock (this.syncLock)
|
||||
{
|
||||
// Since both normal and reflection only cache are accessed in same block, putting only one lock should be sufficient.
|
||||
Assembly assembly = null;
|
||||
|
||||
if (this.TryLoadFromCache(assemblyNameToLoad, isReflectionOnly, out assembly))
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
|
||||
assembly = this.SearchAssembly(this.searchDirectories, assemblyNameToLoad, isReflectionOnly);
|
||||
|
||||
if (assembly != null)
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
|
||||
if (this.directoryList != null && this.directoryList.Any())
|
||||
{
|
||||
// required assembly is not present in searchDirectories??
|
||||
// see, if we can find it in user specified search directories.
|
||||
while (assembly == null && this.directoryList.Count > 0)
|
||||
{
|
||||
// instead of loading whole saerch directory in one time, we are adding directory on the basis of need
|
||||
var currentNode = this.directoryList.Dequeue();
|
||||
|
||||
List<string> increamentalSearchDirectory = new List<string>();
|
||||
|
||||
if (this.DoesDirectoryExist(currentNode.DirectoryPath))
|
||||
{
|
||||
increamentalSearchDirectory.Add(currentNode.DirectoryPath);
|
||||
|
||||
if (currentNode.IncludeSubDirectories)
|
||||
{
|
||||
// Add all its sub-directory in depth first search order.
|
||||
this.AddSubdirectories(currentNode.DirectoryPath, increamentalSearchDirectory);
|
||||
}
|
||||
|
||||
// Add this directory list in this.searchDirectories so that when we will try to resolve some other
|
||||
// assembly, then it will look in this whole directory first.
|
||||
this.searchDirectories.AddRange(increamentalSearchDirectory);
|
||||
|
||||
assembly = this.SearchAssembly(increamentalSearchDirectory, assemblyNameToLoad, isReflectionOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate warning that path does not exist.
|
||||
this.SafeLog(
|
||||
assemblyNameToLoad,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsWarningEnabled)
|
||||
{
|
||||
EqtTrace.Warning(
|
||||
"The Directory: {0}, does not exist",
|
||||
currentNode.DirectoryPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (assembly != null)
|
||||
{
|
||||
return assembly;
|
||||
}
|
||||
}
|
||||
|
||||
// Try for default load for System dlls that can't be found in search paths. Needs to loaded just by name.
|
||||
try
|
||||
{
|
||||
if (isReflectionOnly)
|
||||
{
|
||||
// Put it in the resolved assembly cache so that if the Load call below
|
||||
// triggers another assembly resolution, then we dont end up in stack overflow.
|
||||
this.reflectionOnlyResolvedAssemblies[assemblyNameToLoad] = null;
|
||||
|
||||
assembly = Assembly.ReflectionOnlyLoad(assemblyNameToLoad);
|
||||
|
||||
if (assembly != null)
|
||||
{
|
||||
this.reflectionOnlyResolvedAssemblies[assemblyNameToLoad] = assembly;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Put it in the resolved assembly cache so that if the Load call below
|
||||
// triggers another assembly resolution, then we dont end up in stack overflow.
|
||||
this.resolvedAssemblies[assemblyNameToLoad] = null;
|
||||
|
||||
assembly = Assembly.Load(assemblyNameToLoad);
|
||||
|
||||
if (assembly != null)
|
||||
{
|
||||
this.resolvedAssemblies[assemblyNameToLoad] = assembly;
|
||||
}
|
||||
}
|
||||
|
||||
return assembly;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.SafeLog(
|
||||
args?.Name,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info(
|
||||
"AssemblyResolver: {0}: Failed to load assembly. Reason:{1} ",
|
||||
assemblyNameToLoad,
|
||||
ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return assembly;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load assembly from cache if available.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"> The assembly Name. </param>
|
||||
/// <param name="isReflectionOnly">Indicates if this is a reflection-only context.</param>
|
||||
/// <param name="assembly"> The assembly. </param>
|
||||
/// <returns> The <see cref="bool"/>. </returns>
|
||||
private bool TryLoadFromCache(
|
||||
string assemblyName,
|
||||
bool isReflectionOnly,
|
||||
out Assembly assembly
|
||||
)
|
||||
{
|
||||
bool isFoundInCache = false;
|
||||
|
||||
if (isReflectionOnly)
|
||||
{
|
||||
isFoundInCache = this.reflectionOnlyResolvedAssemblies.TryGetValue(assemblyName, out assembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
isFoundInCache = this.resolvedAssemblies.TryGetValue(assemblyName, out assembly);
|
||||
}
|
||||
|
||||
if (isFoundInCache)
|
||||
{
|
||||
this.SafeLog(
|
||||
assemblyName,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info("AssemblyResolver: Resolved: {0}.", assemblyName);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call logger APIs safely. We do not want a stackoverflow when objectmodel assembly itself
|
||||
/// is being resolved and an EqtTrace message prompts the load of the same dll again.
|
||||
/// CLR does not trigger a load when the EqtTrace messages are in a lamda expression. Leaving it that way
|
||||
/// to preserve readability instead of creating wrapper functions.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">The assembly being rsolved.</param>
|
||||
/// <param name="loggerAction">The logger function.</param>
|
||||
private void SafeLog(
|
||||
string assemblyName,
|
||||
Action loggerAction
|
||||
)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(assemblyName) && !assemblyName.StartsWith(Constants.LoggerAssemblyName))
|
||||
{
|
||||
loggerAction.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for assembly and if exists then load.
|
||||
/// </summary>
|
||||
/// <param name="assemblyPath"> The assembly Path. </param>
|
||||
/// <param name="assemblyName"> The assembly Name. </param>
|
||||
/// <param name="requestedName"> The requested Name. </param>
|
||||
/// <param name="isReflectionOnly"> Indicates whether this is called under a Reflection Only Load context. </param>
|
||||
/// <returns> The <see cref="Assembly"/>. </returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom", Justification = "The assembly location is fugred out from the configuration that the user passes in.")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private Assembly SearchAndLoadAssembly(
|
||||
string assemblyPath,
|
||||
string assemblyName,
|
||||
AssemblyName requestedName,
|
||||
bool isReflectionOnly
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.DoesFileExist(assemblyPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var foundName = AssemblyName.GetAssemblyName(assemblyPath);
|
||||
|
||||
if (!RequestedAssemblyNameMatchesFound(requestedName, foundName))
|
||||
{
|
||||
return null; // File exists but version/public key is wrong. Try next extension.
|
||||
}
|
||||
|
||||
Assembly assembly;
|
||||
|
||||
if (isReflectionOnly)
|
||||
{
|
||||
assembly = this.ReflectionOnlyLoadAssemblyFrom(assemblyPath);
|
||||
this.reflectionOnlyResolvedAssemblies[assemblyName] = assembly;
|
||||
}
|
||||
else
|
||||
{
|
||||
assembly = this.LoadAssemblyFrom(assemblyPath);
|
||||
this.resolvedAssemblies[assemblyName] = assembly;
|
||||
}
|
||||
|
||||
this.SafeLog(
|
||||
assemblyName,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info("AssemblyResolver: Resolved assembly: {0}. ", assemblyName);
|
||||
}
|
||||
});
|
||||
return assembly;
|
||||
}
|
||||
catch (FileLoadException ex)
|
||||
{
|
||||
this.SafeLog(
|
||||
assemblyName,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info("AssemblyResolver: Failed to load assembly: {0}. Reason:{1} ", assemblyName, ex);
|
||||
}
|
||||
});
|
||||
|
||||
// Rethrow FileLoadException, because this exception means that the assembly
|
||||
// was found, but could not be loaded. This will allow us to report a more
|
||||
// specific error message to the user for things like access denied.
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// For all other exceptions, try the next extension.
|
||||
this.SafeLog(
|
||||
assemblyName,
|
||||
() =>
|
||||
{
|
||||
if (EqtTrace.IsInfoEnabled)
|
||||
{
|
||||
EqtTrace.Info("AssemblyResolver: Failed to load assembly: {0}. Reason:{1} ", assemblyName, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Used to specify deployment item (file or directory) for per-test deployment.
|
||||
/// Can be specified on test class or test method.
|
||||
/// Can have multiple instances of the attribute to specify more than one item.
|
||||
/// The item path can be absolute or relative, if relative, it is relative to RunConfig.RelativePathRoot.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// [DeploymentItem("file1.xml")]
|
||||
/// [DeploymentItem("file2.xml", "DataFiles")]
|
||||
/// [DeploymentItem("bin\Debug")]
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
|
||||
public sealed class DeploymentItemAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeploymentItemAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The file or directory to deploy. The path is relative to the build output directory. The item will be copied to the same directory as the deployed test assemblies.</param>
|
||||
public DeploymentItemAttribute(string path)
|
||||
{
|
||||
this.Path = path;
|
||||
this.OutputDirectory = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeploymentItemAttribute"/> class
|
||||
/// </summary>
|
||||
/// <param name="path">The relative or absolute path to the file or directory to deploy. The path is relative to the build output directory. The item will be copied to the same directory as the deployed test assemblies.</param>
|
||||
/// <param name="outputDirectory">The path of the directory to which the items are to be copied. It can be either absolute or relative to the deployment directory. All files and directories identified by <paramref name="path"/> will be copied to this directory.</param>
|
||||
public DeploymentItemAttribute(string path, string outputDirectory)
|
||||
{
|
||||
this.Path = path;
|
||||
this.OutputDirectory = outputDirectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the source file or folder to be copied.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the directory to which the item is copied.
|
||||
/// </summary>
|
||||
public string OutputDirectory { get; }
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// Parallel execution mode.
|
||||
/// </summary>
|
||||
public enum ExecutionScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Each thread of execution will be handed a TestClass worth of tests to execute.
|
||||
/// Within the TestClass, the test methods will execute serially.
|
||||
/// </summary>
|
||||
ClassLevel = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Each thread of execution will be handed TestMethods to execute.
|
||||
/// </summary>
|
||||
MethodLevel = 1,
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Attribute that specifies to expect an exception of the specified type
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class ExpectedExceptionAttribute : ExpectedExceptionBaseAttribute
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExpectedExceptionAttribute"/> class with the expected type
|
||||
/// </summary>
|
||||
/// <param name="exceptionType">Type of the expected exception</param>
|
||||
public ExpectedExceptionAttribute(Type exceptionType)
|
||||
: this(exceptionType, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExpectedExceptionAttribute"/> class with
|
||||
/// the expected type and the message to include when no exception is thrown by the test.
|
||||
/// </summary>
|
||||
/// <param name="exceptionType">Type of the expected exception</param>
|
||||
/// <param name="noExceptionMessage">
|
||||
/// Message to include in the test result if the test fails due to not throwing an exception
|
||||
/// </param>
|
||||
public ExpectedExceptionAttribute(Type exceptionType, string noExceptionMessage)
|
||||
: base(noExceptionMessage)
|
||||
{
|
||||
if (exceptionType == null)
|
||||
{
|
||||
throw new ArgumentNullException("exceptionType");
|
||||
}
|
||||
|
||||
if (!typeof(Exception).GetTypeInfo().IsAssignableFrom(exceptionType.GetTypeInfo()))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resource.UTF_ExpectedExceptionTypeMustDeriveFromException,
|
||||
"exceptionType");
|
||||
}
|
||||
|
||||
this.ExceptionType = exceptionType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the Type of the expected exception
|
||||
/// </summary>
|
||||
public Type ExceptionType
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to allow types derived from the type of the expected exception to
|
||||
/// qualify as expected
|
||||
/// </summary>
|
||||
public bool AllowDerivedTypes
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message to include in the test result if the test fails due to not throwing an exception
|
||||
/// </summary>
|
||||
protected internal override string NoExceptionMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTF_TestMethodNoException,
|
||||
this.ExceptionType.FullName,
|
||||
this.SpecifiedNoExceptionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the type of the exception thrown by the unit test is expected
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception thrown by the unit test</param>
|
||||
protected internal override void Verify(Exception exception)
|
||||
{
|
||||
Debug.Assert(exception != null, "'exception' is null");
|
||||
|
||||
Type thrownExceptionType = exception.GetType();
|
||||
if (this.AllowDerivedTypes)
|
||||
{
|
||||
if (!this.ExceptionType.GetTypeInfo().IsAssignableFrom(thrownExceptionType.GetTypeInfo()))
|
||||
{
|
||||
// If the exception is an AssertFailedException or an AssertInconclusiveException, then rethrow it to
|
||||
// preserve the test outcome and error message
|
||||
this.RethrowIfAssertException(exception);
|
||||
|
||||
string message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTF_TestMethodWrongExceptionDerivedAllowed,
|
||||
thrownExceptionType.FullName,
|
||||
this.ExceptionType.FullName,
|
||||
UtfHelper.GetExceptionMsg(exception));
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (thrownExceptionType != this.ExceptionType)
|
||||
{
|
||||
// If the exception is an AssertFailedException or an AssertInconclusiveException, then rethrow it to
|
||||
// preserve the test outcome and error message
|
||||
this.RethrowIfAssertException(exception);
|
||||
|
||||
string message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTF_TestMethodWrongException,
|
||||
thrownExceptionType.FullName,
|
||||
this.ExceptionType.FullName,
|
||||
UtfHelper.GetExceptionMsg(exception));
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for attributes that specify to expect an exception from a unit test
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public abstract class ExpectedExceptionBaseAttribute : Attribute
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExpectedExceptionBaseAttribute"/> class with a default no-exception message
|
||||
/// </summary>
|
||||
protected ExpectedExceptionBaseAttribute()
|
||||
: this(string.Empty)
|
||||
{
|
||||
this.SpecifiedNoExceptionMessage = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExpectedExceptionBaseAttribute"/> class with a no-exception message
|
||||
/// </summary>
|
||||
/// <param name="noExceptionMessage">
|
||||
/// Message to include in the test result if the test fails due to not throwing an
|
||||
/// exception
|
||||
/// </param>
|
||||
protected ExpectedExceptionBaseAttribute(string noExceptionMessage)
|
||||
{
|
||||
this.SpecifiedNoExceptionMessage =
|
||||
noExceptionMessage == null ?
|
||||
string.Empty :
|
||||
noExceptionMessage.Trim();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
// Todo: Test Context needs to be put in here for source compat.
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message to include in the test result if the test fails due to not throwing an exception
|
||||
/// </summary>
|
||||
protected internal virtual string NoExceptionMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(this.SpecifiedNoExceptionMessage != null, "'noExceptionMessage' is null");
|
||||
|
||||
if (string.IsNullOrEmpty(this.SpecifiedNoExceptionMessage))
|
||||
{
|
||||
// Provide a default message when none was provided by a derived class
|
||||
return GetDefaultNoExceptionMessage(this.GetType().FullName);
|
||||
}
|
||||
|
||||
return this.SpecifiedNoExceptionMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message to include in the test result if the test fails due to not throwing an exception
|
||||
/// </summary>
|
||||
protected string SpecifiedNoExceptionMessage { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default no-exception message
|
||||
/// </summary>
|
||||
/// <param name="expectedExceptionAttributeTypeName">The ExpectedException attribute type name</param>
|
||||
/// <returns>The default no-exception message</returns>
|
||||
internal static string GetDefaultNoExceptionMessage(string expectedExceptionAttributeTypeName)
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTF_TestMethodNoExceptionDefault,
|
||||
expectedExceptionAttributeTypeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the exception is expected. If the method returns, then it is
|
||||
/// understood that the exception was expected. If the method throws an exception, then it
|
||||
/// is understood that the exception was not expected, and the thrown exception's message
|
||||
/// is included in the test result. The <see cref="Assert"/> class can be used for
|
||||
/// convenience. If <see cref="Assert.Inconclusive()"/> is used and the assertion fails,
|
||||
/// then the test outcome is set to Inconclusive.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception thrown by the unit test</param>
|
||||
protected internal abstract void Verify(Exception exception);
|
||||
|
||||
/// <summary>
|
||||
/// Rethrow the exception if it is an AssertFailedException or an AssertInconclusiveException
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to rethrow if it is an assertion exception</param>
|
||||
protected void RethrowIfAssertException(Exception exception)
|
||||
{
|
||||
if (exception is AssertFailedException)
|
||||
{
|
||||
throw new AssertFailedException(exception.Message);
|
||||
}
|
||||
else if (exception is AssertInconclusiveException)
|
||||
{
|
||||
throw new AssertInconclusiveException(exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// The ignore attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class IgnoreAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IgnoreAttribute"/> class.
|
||||
/// </summary>
|
||||
public IgnoreAttribute()
|
||||
: this(string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IgnoreAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// Message specifies reason for ignoring.
|
||||
/// </param>
|
||||
public IgnoreAttribute(string message)
|
||||
{
|
||||
this.IgnoreMessage = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owner.
|
||||
/// </summary>
|
||||
public string IgnoreMessage { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Test Owner
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class OwnerAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OwnerAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">
|
||||
/// The owner.
|
||||
/// </param>
|
||||
public OwnerAttribute(string owner)
|
||||
{
|
||||
this.Owner = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the owner.
|
||||
/// </summary>
|
||||
public string Owner { get; }
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Priority attribute; used to specify the priority of a unit test.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class PriorityAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PriorityAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="priority">
|
||||
/// The priority.
|
||||
/// </param>
|
||||
public PriorityAttribute(int priority)
|
||||
{
|
||||
this.Priority = priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for the "Category" attribute
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The reason for this attribute is to let the users create their own implementation of test categories.
|
||||
/// - test framework (discovery, etc) deals with TestCategoryBaseAttribute.
|
||||
/// - The reason that TestCategories property is a collection rather than a string,
|
||||
/// is to give more flexibility to the user. For instance the implementation may be based on enums for which the values can be OR'ed
|
||||
/// in which case it makes sense to have single attribute rather than multiple ones on the same test.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public abstract class TestCategoryBaseAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestCategoryBaseAttribute"/> class.
|
||||
/// Applies the category to the test. The strings returned by TestCategories
|
||||
/// are used with the /category command to filter tests
|
||||
/// </summary>
|
||||
protected TestCategoryBaseAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the test category that has been applied to the test.
|
||||
/// </summary>
|
||||
public abstract IList<string> TestCategories
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// The test property attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class TestPropertyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestPropertyAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The name.
|
||||
/// </param>
|
||||
/// <param name="value">
|
||||
/// The value.
|
||||
/// </param>
|
||||
public TestPropertyAttribute(string name, string value)
|
||||
{
|
||||
// NOTE : DONT THROW EXCEPTIONS FROM HERE IT WILL CRASH GetCustomAttributes() call
|
||||
this.Name = name;
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
public string Value { get; }
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// TestResult object to be returned to adapter.
|
||||
/// </summary>
|
||||
public class TestResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestResult"/> class.
|
||||
/// </summary>
|
||||
public TestResult()
|
||||
{
|
||||
this.DatarowIndex = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the display name of the result. Useful when returning multiple results.
|
||||
/// If null then Method name is used as DisplayName.
|
||||
/// </summary>
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the outcome of the test execution.
|
||||
/// </summary>
|
||||
public UnitTestOutcome Outcome { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exception thrown when test is failed.
|
||||
/// </summary>
|
||||
public Exception TestFailureException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output of the message logged by test code.
|
||||
/// </summary>
|
||||
public string LogOutput { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output of the message logged by test code.
|
||||
/// </summary>
|
||||
public string LogError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the debug traces by test code.
|
||||
/// </summary>
|
||||
public string DebugTrace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the debug traces by test code.
|
||||
/// </summary>
|
||||
public string TestContextMessages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the execution id of the result.
|
||||
/// </summary>
|
||||
public Guid ExecutionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent execution id of the result.
|
||||
/// </summary>
|
||||
public Guid ParentExecId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the inner results count of the result.
|
||||
/// </summary>
|
||||
public int InnerResultsCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the duration of test execution.
|
||||
/// </summary>
|
||||
public TimeSpan Duration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data row index in data source. Set only for results of individual
|
||||
/// run of data row of a data driven test.
|
||||
/// </summary>
|
||||
public int DatarowIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the return value of the test method. (Currently null always).
|
||||
/// </summary>
|
||||
public object ReturnValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the result files attached by the test.
|
||||
/// </summary>
|
||||
public IList<string> ResultFiles { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// The test initialize attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class TestInitializeAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The test cleanup attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class TestCleanupAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class initialize attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class ClassInitializeAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class cleanup attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class ClassCleanupAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assembly initialize attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class AssemblyInitializeAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assembly cleanup attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class AssemblyCleanupAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Timeout attribute; used to specify the timeout of a unit test.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public sealed class TimeoutAttribute : Attribute
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimeoutAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timeout">
|
||||
/// The timeout.
|
||||
/// </param>
|
||||
public TimeoutAttribute(int timeout)
|
||||
{
|
||||
this.Timeout = timeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimeoutAttribute"/> class with a preset timeout
|
||||
/// </summary>
|
||||
/// <param name="timeout">
|
||||
/// The timeout
|
||||
/// </param>
|
||||
public TimeoutAttribute(TestTimeout timeout)
|
||||
{
|
||||
this.Timeout = (int)timeout;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timeout.
|
||||
/// </summary>
|
||||
public int Timeout { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration for timeouts, that can be used with the <see cref="TimeoutAttribute"/> class.
|
||||
/// The type of the enumeration must match
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "Compat reasons")]
|
||||
public enum TestTimeout
|
||||
{
|
||||
/// <summary>
|
||||
/// The infinite.
|
||||
/// </summary>
|
||||
Infinite = int.MaxValue
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
internal class Constants
|
||||
{
|
||||
internal const string NFExecutorUriString = "executor://nanoFrameworkTestExecutor/v1";
|
||||
|
||||
internal static readonly Uri ExecutorUri = new Uri(NFExecutorUriString);
|
||||
|
||||
/// <summary>
|
||||
/// The assembly name of the dll containing logger APIs(EqtTrace) from the TestPlatform.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The reason we have this is because the AssemblyResolver itself logs information during resolution.
|
||||
/// If the resolver is called for the assembly containing the logger APIs, we do not log so as to prevent a stack overflow.
|
||||
/// </remarks>
|
||||
internal const string LoggerAssemblyName = "Microsoft.VisualStudio.TestPlatform.ObjectModel";
|
||||
|
||||
internal const string TargetFrameworkAttributeFullName = "System.Runtime.Versioning.TargetFrameworkAttribute";
|
||||
|
||||
internal const string DotNetnanoFrameWorkStringPrefix = ".NETnanoFramework,Version=";
|
||||
|
||||
internal const string TargetFrameworkName = "TargetFrameworkName";
|
||||
|
||||
internal const string TargetFramework_nanoFramework = ".NETnanoFramework";
|
||||
|
||||
internal const string TestClassAttributeFullName = "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute";
|
||||
|
||||
internal const string TestMethodAttributeFullName = "Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute";
|
||||
|
||||
|
||||
internal static readonly TestProperty TestClassNameProperty = TestProperty.Register("TestDiscoverer.TestClassName", TestClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
internal static readonly TestProperty TestCategoryProperty = TestProperty.Register("TestDiscoverer.TestCategory", TestCategoryLabel, typeof(string[]), TestPropertyAttributes.Hidden | TestPropertyAttributes.Trait, typeof(TestCase));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
internal static readonly TestProperty PriorityProperty = TestProperty.Register("TestDiscoverer.Priority", PriorityLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
|
||||
|
||||
internal static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair<string, string>[]), TestPropertyAttributes.Hidden, typeof(TestCase));
|
||||
|
||||
internal static readonly TestProperty ExecutionIdProperty = TestProperty.Register("ExecutionId", ExecutionIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
|
||||
|
||||
internal static readonly TestProperty ParentExecIdProperty = TestProperty.Register("ParentExecId", ParentExecIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
|
||||
|
||||
internal static readonly TestProperty InnerResultsCountProperty = TestProperty.Register("InnerResultsCount", InnerResultsCountLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestResult));
|
||||
|
||||
#region Private Constants
|
||||
|
||||
/// <summary>
|
||||
/// These are the Test properties used by the adapter, which essentially correspond
|
||||
/// to attributes on tests, and may be available in command line/TeamBuild to filter tests.
|
||||
/// These Property names should not be localized.
|
||||
/// </summary>
|
||||
private const string TestClassNameLabel = "ClassName";
|
||||
private const string IsAsyncLabel = "IsAsync";
|
||||
private const string TestCategoryLabel = "TestCategory";
|
||||
private const string PriorityLabel = "Priority";
|
||||
private const string DeploymentItemsLabel = "DeploymentItems";
|
||||
private const string DoNotParallelizeLabel = "DoNotParallelize";
|
||||
private const string ExecutionIdLabel = "ExecutionId";
|
||||
private const string ParentExecIdLabel = "ParentExecId";
|
||||
private const string InnerResultsCountLabel = "InnerResultsCount";
|
||||
|
||||
private const string TestRunId = "__Tfs_TestRunId__";
|
||||
private const string TestPlanId = "__Tfs_TestPlanId__";
|
||||
private const string TestCaseId = "__Tfs_TestCaseId__";
|
||||
private const string TestPointId = "__Tfs_TestPointId__";
|
||||
private const string TestConfigurationId = "__Tfs_TestConfigurationId__";
|
||||
private const string TestConfigurationName = "__Tfs_TestConfigurationName__";
|
||||
private const string IsInLabEnvironment = "__Tfs_IsInLabEnvironment__";
|
||||
private const string BuildConfigurationId = "__Tfs_BuildConfigurationId__";
|
||||
private const string BuildDirectory = "__Tfs_BuildDirectory__";
|
||||
private const string BuildFlavor = "__Tfs_BuildFlavor__";
|
||||
private const string BuildNumber = "__Tfs_BuildNumber__";
|
||||
private const string BuildPlatform = "__Tfs_BuildPlatform__";
|
||||
private const string BuildUri = "__Tfs_BuildUri__";
|
||||
private const string TfsServerCollectionUrl = "__Tfs_TfsServerCollectionUrl__";
|
||||
private const string TfsTeamProject = "__Tfs_TeamProject__";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Deployment
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies type of deployment item origin, where the item comes from.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal enum DeploymentItemOriginType
|
||||
{
|
||||
/// <summary>
|
||||
/// A per test deployment item.
|
||||
/// </summary>
|
||||
PerTestDeployment,
|
||||
|
||||
/// <summary>
|
||||
/// A test storage.
|
||||
/// </summary>
|
||||
TestStorage,
|
||||
|
||||
/// <summary>
|
||||
/// A dependency item.
|
||||
/// </summary>
|
||||
Dependency,
|
||||
|
||||
/// <summary>
|
||||
/// A satellite assembly.
|
||||
/// </summary>
|
||||
Satellite,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The deployment item for a test class or a test method.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class DeploymentItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeploymentItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sourcePath">Absolute or relative to test assembly.</param>
|
||||
internal DeploymentItem(string sourcePath)
|
||||
: this(sourcePath, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeploymentItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sourcePath">Absolute or relative to test assembly.</param>
|
||||
/// <param name="relativeOutputDirectory">Relative to the deployment directory. string.Empty means deploy to
|
||||
/// deployment directory itself.
|
||||
/// </param>
|
||||
internal DeploymentItem(
|
||||
string sourcePath,
|
||||
string relativeOutputDirectory
|
||||
)
|
||||
: this(sourcePath, relativeOutputDirectory, DeploymentItemOriginType.PerTestDeployment)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeploymentItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sourcePath">Absolute or relative to test assembly.</param>
|
||||
/// <param name="relativeOutputDirectory">Relative to the deployment directory. string.Empty means deploy to deployment directory itself. </param>
|
||||
/// <param name="originType">Origin of this deployment directory.</param>
|
||||
internal DeploymentItem(
|
||||
string sourcePath,
|
||||
string relativeOutputDirectory,
|
||||
DeploymentItemOriginType originType
|
||||
)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(sourcePath), "sourcePath");
|
||||
Debug.Assert(relativeOutputDirectory != null, "relativeOutputDirectory");
|
||||
|
||||
this.SourcePath = sourcePath;
|
||||
this.RelativeOutputDirectory = relativeOutputDirectory;
|
||||
this.OriginType = originType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to the 'source' deployment item
|
||||
/// </summary>
|
||||
internal string SourcePath
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the directory in which item should be deployed. Relative to the deployment root.
|
||||
/// </summary>
|
||||
internal string RelativeOutputDirectory
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the origin of deployment item.
|
||||
/// </summary>
|
||||
internal DeploymentItemOriginType OriginType { get; private set; }
|
||||
|
||||
#region Object - overrides
|
||||
|
||||
/// <summary>
|
||||
/// Equals implementation.
|
||||
/// </summary>
|
||||
/// <param name="obj"> The object. </param>
|
||||
/// <returns> True if the two objects are equal. </returns>
|
||||
public override bool Equals(
|
||||
object obj
|
||||
)
|
||||
{
|
||||
DeploymentItem otherItem = obj as DeploymentItem;
|
||||
if (otherItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Assert(this.SourcePath != null, "SourcePath");
|
||||
Debug.Assert(this.RelativeOutputDirectory != null, "RelativeOutputDirectory");
|
||||
return this.SourcePath.Equals(otherItem.SourcePath, StringComparison.OrdinalIgnoreCase) &&
|
||||
this.RelativeOutputDirectory.Equals(otherItem.RelativeOutputDirectory, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GetHashCode implementation.
|
||||
/// </summary>
|
||||
/// <returns> The hash code value. </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
Debug.Assert(this.SourcePath != null, "SourcePath");
|
||||
Debug.Assert(this.RelativeOutputDirectory != null, "RelativeOutputDirectory");
|
||||
return this.SourcePath.GetHashCode() + this.RelativeOutputDirectory.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deployment item description.
|
||||
/// </summary>
|
||||
/// <returns> The <see cref="string"/> value. </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
Debug.Assert(this.SourcePath != null, "SourcePath");
|
||||
Debug.Assert(this.RelativeOutputDirectory != null, "RelativeOutputDirectory");
|
||||
|
||||
return
|
||||
string.IsNullOrEmpty(this.RelativeOutputDirectory) ?
|
||||
string.Format(CultureInfo.CurrentCulture, Resource.DeploymentItem, this.SourcePath) :
|
||||
string.Format(CultureInfo.CurrentCulture, Resource.DeploymentItemWithOutputDirectory, this.SourcePath, this.RelativeOutputDirectory);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Deployment
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// The test run directories.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TestRunDirectories
|
||||
{
|
||||
/// <summary>
|
||||
/// The default deployment root directory. We do not want to localize it.
|
||||
/// </summary>
|
||||
internal const string DefaultDeploymentRootDirectory = "TestResults";
|
||||
|
||||
/// <summary>
|
||||
/// The deployment in directory suffix.
|
||||
/// </summary>
|
||||
internal const string DeploymentInDirectorySuffix = "In";
|
||||
|
||||
/// <summary>
|
||||
/// The deployment out directory suffix.
|
||||
/// </summary>
|
||||
internal const string DeploymentOutDirectorySuffix = "Out";
|
||||
|
||||
public TestRunDirectories(string rootDirectory)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(rootDirectory), "rootDirectory");
|
||||
|
||||
this.RootDeploymentDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the root deployment directory
|
||||
/// </summary>
|
||||
public string RootDeploymentDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the In directory
|
||||
/// </summary>
|
||||
public string InDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(this.RootDeploymentDirectory, DeploymentInDirectorySuffix);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Out directory
|
||||
/// </summary>
|
||||
public string OutDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(this.RootDeploymentDirectory, DeploymentOutDirectorySuffix);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets In\MachineName directory
|
||||
/// </summary>
|
||||
public string InMachineNameDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Path.Combine(this.RootDeploymentDirectory, DeploymentInDirectorySuffix), Environment.MachineName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nerdbank.GitVersioning">
|
||||
<Version>3.3.37</Version>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,236 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
|
||||
using nanoFramework.TestAdapter;
|
||||
using nanoFramework.TestFramework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace nanoFramework.TestPlatform.TestAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// A Test Discoverer class
|
||||
/// </summary>
|
||||
[DefaultExecutorUri(TestsConstants.NanoExecutor)]
|
||||
[FileExtension(".exe")]
|
||||
[FileExtension(".dll")]
|
||||
public class TestDiscoverer : ITestDiscoverer
|
||||
{
|
||||
private LogMessenger _logger;
|
||||
private List<TestCase> _testCases;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void DiscoverTests(IEnumerable<string> sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink)
|
||||
{
|
||||
_testCases = new List<TestCase>();
|
||||
|
||||
var settingsProvider = discoveryContext.RunSettings.GetSettings(TestsConstants.SettingsName) as SettingsProvider;
|
||||
|
||||
_logger = new LogMessenger(
|
||||
logger,
|
||||
settingsProvider);
|
||||
|
||||
if (settingsProvider != null)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
"Getting ready to discover tests...",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
_logger.LogMessage(
|
||||
"Settings parsed",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogMessage(
|
||||
"Getting ready to discover tests...",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
_logger.LogMessage(
|
||||
"No settings for nanoFramework adapter",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
}
|
||||
|
||||
foreach (var source in sources)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
$" New file processed: {source}",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
if (!File.Exists(source))
|
||||
{
|
||||
_logger.LogMessage(
|
||||
$" File doesn't exist: {source}",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var cases = FindTestCases(source);
|
||||
if (cases.Count > 0)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
$" Adding {cases.Count} new tests",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
_testCases.AddRange(cases);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var testCase in _testCases)
|
||||
{
|
||||
discoverySink.SendTestCase(testCase);
|
||||
}
|
||||
|
||||
_logger.LogMessage(
|
||||
"Finished adding files",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find tests cases based on the source file
|
||||
/// </summary>
|
||||
/// <param name="source">the link on a file</param>
|
||||
/// <returns>A list of test cases</returns>
|
||||
public static List<TestCase> FindTestCases(string source)
|
||||
{
|
||||
List<TestCase> testCases = new List<TestCase>();
|
||||
|
||||
var nfprojSources = FindNfprojSources(source);
|
||||
if (nfprojSources.Length == 0)
|
||||
{
|
||||
return testCases;
|
||||
}
|
||||
|
||||
var allCsFils = GetAllCsFileNames(nfprojSources);
|
||||
|
||||
Assembly test = Assembly.LoadFile(source);
|
||||
AppDomain.CurrentDomain.AssemblyResolve += App_AssemblyResolve;
|
||||
AppDomain.CurrentDomain.Load(test.GetName());
|
||||
|
||||
Type[] allTypes = test.GetTypes();
|
||||
foreach (var type in allTypes)
|
||||
{
|
||||
if (type.IsClass)
|
||||
{
|
||||
var typeAttribs = type.GetCustomAttributes(true);
|
||||
foreach (var typeAttrib in typeAttribs)
|
||||
{
|
||||
if (typeof(TestClassAttribute).FullName == typeAttrib.GetType().FullName)
|
||||
{
|
||||
var methods = type.GetMethods();
|
||||
// First we look at Setup
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var attribs = method.GetCustomAttributes(true);
|
||||
|
||||
foreach (var attrib in attribs)
|
||||
{
|
||||
if (attrib.GetType().FullName == typeof(SetupAttribute).FullName ||
|
||||
attrib.GetType().FullName == typeof(TestMethodAttribute).FullName ||
|
||||
attrib.GetType().FullName == typeof(CleanupAttribute).FullName)
|
||||
{
|
||||
var testCase = GetFileNameAndLineNumber(allCsFils, type, method);
|
||||
testCase.Source = source;
|
||||
testCase.ExecutorUri = new Uri(TestsConstants.NanoExecutor);
|
||||
testCase.FullyQualifiedName = $"{type.FullName}.{testCase.DisplayName}";
|
||||
testCase.Traits.Add(new Trait("Type", attrib.GetType().Name.Replace("Attribute","")));
|
||||
testCases.Add(testCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return testCases;
|
||||
}
|
||||
|
||||
private static Assembly App_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
string dllName = args.Name.Split(new[] { ',' })[0] + ".dll";
|
||||
string path = Path.GetDirectoryName(args.RequestingAssembly.Location);
|
||||
return Assembly.LoadFrom(Path.Combine(path, dllName));
|
||||
}
|
||||
|
||||
private static string[] GetAllCsFileNames(FileInfo[] nfprojSources)
|
||||
{
|
||||
List<string> allCsFiles = new List<string>();
|
||||
foreach (var nfproj in nfprojSources)
|
||||
{
|
||||
var csFiles = Directory.GetFiles(Path.GetDirectoryName(nfproj.FullName), "*.cs", SearchOption.AllDirectories);
|
||||
// Get rid of those in /bin / obj
|
||||
var csFilesClean = csFiles.Where(m => !(m.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}") || m.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}")));
|
||||
allCsFiles.AddRange(csFilesClean);
|
||||
}
|
||||
|
||||
return allCsFiles.ToArray();
|
||||
}
|
||||
|
||||
private static FileInfo[] FindNfprojSources(string source)
|
||||
{
|
||||
if (Path.GetDirectoryName(source) == null)
|
||||
{
|
||||
return new FileInfo[0];
|
||||
}
|
||||
|
||||
// Find all the potential *.cs files present at same level or above a nfproj file,
|
||||
// if no nfproj file, then we will skip this source
|
||||
var mainDirectory = new DirectoryInfo(Path.GetDirectoryName(source));
|
||||
var nfproj = mainDirectory.GetFiles("*.nfproj");
|
||||
if (nfproj.Length == 0)
|
||||
{
|
||||
var ret = FindNfprojSources(mainDirectory.Parent.FullName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nfproj;
|
||||
}
|
||||
|
||||
private static TestCase GetFileNameAndLineNumber(string[] csFiles, Type className, MethodInfo method)
|
||||
{
|
||||
var clName = className.Name;
|
||||
var methodName = method.Name;
|
||||
TestCase flret = new TestCase();
|
||||
foreach (var csFile in csFiles)
|
||||
{
|
||||
StreamReader sr = new StreamReader(csFile);
|
||||
var allFile = sr.ReadToEnd();
|
||||
if (allFile.Contains($"class {clName}"))
|
||||
{
|
||||
if (allFile.Contains($" {methodName}("))
|
||||
{
|
||||
// We found it!
|
||||
int lineNum = 1;
|
||||
foreach (var line in allFile.Split('\r'))
|
||||
{
|
||||
if (line.Contains($" {methodName}("))
|
||||
{
|
||||
flret.CodeFilePath = csFile;
|
||||
flret.LineNumber = lineNum;
|
||||
flret.DisplayName = method.Name;
|
||||
return flret;
|
||||
}
|
||||
|
||||
lineNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,317 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates through all types in the assembly in search of valid test methods.
|
||||
/// </summary>
|
||||
internal class AssemblyEnumerator : MarshalByRefObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssemblyEnumerator"/> class.
|
||||
/// </summary>
|
||||
public AssemblyEnumerator()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssemblyEnumerator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings for the session.</param>
|
||||
/// <remarks>Use this constructor when creating this object in a new app domain so the settings for this app domain are set.</remarks>
|
||||
public AssemblyEnumerator(TestSettings settings)
|
||||
{
|
||||
// Populate the settings into the domain(Desktop workflow) performing discovery.
|
||||
// This would just be resettings the settings to itself in non desktop workflows.
|
||||
TestSettings.PopulateSettings(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="object"/>.
|
||||
/// </returns>
|
||||
[SecurityCritical]
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates through all types in the assembly in search of valid test methods.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFileName"> The assembly file name. </param>
|
||||
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
|
||||
/// <returns> A collection of Test Elements. </returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catching a generic exception since it is a requirement to not abort discovery in case of any errors.")]
|
||||
internal ICollection<UnitTestElement> EnumerateAssembly(
|
||||
string assemblyFileName,
|
||||
out ICollection<string> warnings)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrWhiteSpace(assemblyFileName), "Invalid assembly file name.");
|
||||
|
||||
var warningMessages = new List<string>();
|
||||
var tests = new List<UnitTestElement>();
|
||||
|
||||
Type testClassAttrib = null;
|
||||
Type testMethodAttrib = null;
|
||||
|
||||
|
||||
// load test attributes from TestFramework nanoFramework assembly
|
||||
var testFrameworkAssembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(
|
||||
Path.GetDirectoryName(assemblyFileName) + "\\nanoFramework.TestPlatform.TestFramework.dll",
|
||||
isReflectionOnly: true);
|
||||
|
||||
if(testFrameworkAssembly == null)
|
||||
{
|
||||
// If we fail to load the TestFramework assembly abort the discovery because that won't be possible
|
||||
string message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"Fail to load nanoFramework.TestPlatform.TestFramework assembly.");
|
||||
|
||||
warningMessages.Add(message);
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Fail to load nanoFramework.TestPlatform.TestFramework assembly.");
|
||||
|
||||
warningMessages.Add(message);
|
||||
}
|
||||
|
||||
var testFrameworkTypes = this.GetTypes(testFrameworkAssembly, assemblyFileName, null);
|
||||
|
||||
foreach (var type in testFrameworkTypes)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string typeFullName = null;
|
||||
|
||||
try
|
||||
{
|
||||
typeFullName = type.FullName;
|
||||
|
||||
if(typeFullName == Constants.TestClassAttributeFullName)
|
||||
{
|
||||
testClassAttrib = type;
|
||||
continue;
|
||||
}
|
||||
else if (typeFullName == Constants.TestMethodAttributeFullName)
|
||||
{
|
||||
testMethodAttrib = type;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// If we fail to discover the test attributes abort the discovery because that won't be possible
|
||||
string message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"Exception occurred when searching test attributes in nanoFramework.TestPlatform.TestFramework assembly. {0}",
|
||||
exception.Message);
|
||||
|
||||
warningMessages.Add(message);
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Could not find the test attributes in nanoFramework.TestPlatform.TestFramework assembly. {0}", exception);
|
||||
|
||||
warningMessages.Add(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (testClassAttrib != null && testMethodAttrib != null)
|
||||
{
|
||||
// we have test attributes so we are good to go
|
||||
// always need to load the source assembly in reflection only context
|
||||
Assembly assembly;
|
||||
assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(
|
||||
assemblyFileName,
|
||||
isReflectionOnly: true);
|
||||
|
||||
var types = this.GetTypes(assembly, assemblyFileName, warningMessages);
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string typeFullName = null;
|
||||
|
||||
try
|
||||
{
|
||||
ICollection<string> warningsFromTypeEnumerator;
|
||||
|
||||
typeFullName = type.FullName;
|
||||
var unitTestCases = this.GetTypeEnumerator(type, assemblyFileName, testClassAttrib, testMethodAttrib).Enumerate(out warningsFromTypeEnumerator);
|
||||
|
||||
if (warningsFromTypeEnumerator != null)
|
||||
{
|
||||
warningMessages.AddRange(warningsFromTypeEnumerator);
|
||||
}
|
||||
|
||||
if (unitTestCases != null)
|
||||
{
|
||||
tests.AddRange(unitTestCases);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// If we fail to discover type from a class, then don't abort the discovery
|
||||
// Move to the next type.
|
||||
string message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.CouldNotInspectTypeDuringDiscovery,
|
||||
typeFullName,
|
||||
assemblyFileName,
|
||||
exception.Message);
|
||||
warningMessages.Add(message);
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
|
||||
"AssemblyEnumerator: Exception occurred while enumerating type {0}. {1}",
|
||||
typeFullName,
|
||||
exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we fail to discover the test attributes abort the discovery because that won't be possible
|
||||
string message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"Fail to find test attributes in nanoFramework.TestPlatform.TestFramework assembly.");
|
||||
|
||||
warningMessages.Add(message);
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Fail to find test attributes in nanoFramework.TestPlatform.TestFramework assembly.");
|
||||
|
||||
warningMessages.Add(message);
|
||||
|
||||
}
|
||||
|
||||
warnings = warningMessages;
|
||||
return tests;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types defined in an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The reflected assembly.</param>
|
||||
/// <param name="assemblyFileName">The file name of the assembly.</param>
|
||||
/// <param name="warningMessages">Contains warnings if any, that need to be passed back to the caller.</param>
|
||||
/// <returns>Gets the types defined in the provided assembly.</returns>
|
||||
internal Type[] GetTypes(
|
||||
Assembly assembly,
|
||||
string assemblyFileName,
|
||||
ICollection<string> warningMessages)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
try
|
||||
{
|
||||
types.AddRange(assembly.DefinedTypes.Select(typeinfo => typeinfo.AsType()));
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
"TestExecutor.TryGetTests: Failed to discover tests from {0}. Reason:{1}",
|
||||
assemblyFileName,
|
||||
ex);
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("Exceptions thrown from the Loader :");
|
||||
|
||||
if (ex.LoaderExceptions != null)
|
||||
{
|
||||
// If not able to load all type, log a warning and continue with loaded types.
|
||||
var message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.TypeLoadFailed,
|
||||
assemblyFileName,
|
||||
this.GetLoadExceptionDetails(ex));
|
||||
|
||||
warningMessages?.Add(message);
|
||||
|
||||
foreach (var loaderEx in ex.LoaderExceptions)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("{0}", loaderEx);
|
||||
}
|
||||
}
|
||||
|
||||
return ex.Types;
|
||||
}
|
||||
|
||||
return types.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats load exception as multiline string, each line contains load error message.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception.</param>
|
||||
/// <returns>Returns loader exceptions as a multiline string.</returns>
|
||||
internal string GetLoadExceptionDetails(ReflectionTypeLoadException ex)
|
||||
{
|
||||
Debug.Assert(ex != null, "exception should not be null.");
|
||||
|
||||
var map = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); // Exception -> null.
|
||||
var errorDetails = new StringBuilder();
|
||||
|
||||
if (ex.LoaderExceptions != null)
|
||||
{
|
||||
// Loader exceptions can contain duplicates, leave only unique exceptions.
|
||||
foreach (var loaderException in ex.LoaderExceptions)
|
||||
{
|
||||
Debug.Assert(loaderException != null, "loader exception should not be null.");
|
||||
var line = string.Format(CultureInfo.CurrentCulture, Resource.EnumeratorLoadTypeErrorFormat, loaderException.GetType(), loaderException.Message);
|
||||
if (!map.ContainsKey(line))
|
||||
{
|
||||
map.Add(line, null);
|
||||
errorDetails.AppendLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorDetails.AppendLine(ex.Message);
|
||||
}
|
||||
|
||||
return errorDetails.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an instance of the <see cref="TypeEnumerator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to enumerate.</param>
|
||||
/// <param name="assemblyFileName">The reflected assembly name.</param>
|
||||
/// <returns>a TypeEnumerator instance.</returns>
|
||||
internal virtual TypeEnumerator GetTypeEnumerator(
|
||||
Type type,
|
||||
string assemblyFileName,
|
||||
Type testClassAttrib,
|
||||
Type testMethodAttrib)
|
||||
{
|
||||
var reflectHelper = new ReflectHelper();
|
||||
var typevalidator = new TypeValidator(reflectHelper, testClassAttrib);
|
||||
var testMethodValidator = new TestMethodValidator(reflectHelper, testMethodAttrib);
|
||||
|
||||
return new TypeEnumerator(type, assemblyFileName, reflectHelper, typevalidator, testMethodValidator);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates through an assembly to get a set of test methods.
|
||||
/// </summary>
|
||||
internal class AssemblyEnumeratorWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Assembly name for UTF
|
||||
/// </summary>
|
||||
//private static readonly AssemblyName UnitTestFrameworkAssemblyName =
|
||||
// typeof(TestMethodAttribute).GetTypeInfo().Assembly.GetName();
|
||||
|
||||
/// <summary>
|
||||
/// Gets test elements from an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFileName"> The assembly file name. </param>
|
||||
/// <param name="runSettings"> The run Settings. </param>
|
||||
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
|
||||
/// <returns> A collection of test elements. </returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catching a generic exception since it is a requirement to not abort discovery in case of any errors.")]
|
||||
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "This is only for internal use.")]
|
||||
internal ICollection<UnitTestElement> GetTests(
|
||||
string assemblyFileName,
|
||||
IRunSettings runSettings,
|
||||
out ICollection<string> warnings)
|
||||
{
|
||||
warnings = new List<string>();
|
||||
|
||||
if (string.IsNullOrEmpty(assemblyFileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fullFilePath = PlatformServiceProvider.Instance.FileOperations.GetFullFilePath(assemblyFileName);
|
||||
|
||||
try
|
||||
{
|
||||
if (!PlatformServiceProvider.Instance.FileOperations.DoesFileExist(fullFilePath))
|
||||
{
|
||||
var message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.TestAssembly_FileDoesNotExist,
|
||||
fullFilePath);
|
||||
throw new FileNotFoundException(message);
|
||||
}
|
||||
|
||||
// check
|
||||
// Setup app-domain
|
||||
var appDomainSetup = new AppDomainSetup();
|
||||
|
||||
// get target framework from assembly
|
||||
var targetFrameworkVersion = AppDomainUtilities.GetTargetFrameworkVersionString(assemblyFileName);
|
||||
|
||||
// sanity check for nanoFramework being the target framework of this assembly
|
||||
if (!targetFrameworkVersion.StartsWith(Constants.TargetFramework_nanoFramework))
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
$"TestDiscoverer.TryGetTests: Failed to discover tests from {fullFilePath}. Reason: this assembly is not targeting .NET nanoFramework.");
|
||||
|
||||
var message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.TestAssembly_AssemblyDiscoveryFailure,
|
||||
fullFilePath,
|
||||
"Reason: this assembly is not targeting .NET nanoFramework.");
|
||||
warnings.Add(message);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO
|
||||
//if (!PlatformServiceProvider.Instance.TestSource.IsAssemblyReferenced(
|
||||
// UnitTestFrameworkAssemblyName,
|
||||
// fullFilePath))
|
||||
//{
|
||||
// return null;
|
||||
//}
|
||||
|
||||
// Load the assembly in isolation if required.
|
||||
return this.GetTestsInIsolation(fullFilePath, runSettings, out warnings);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
"TestDiscoverer.TryGetTests: Failed to discover tests from {0}. Reason:{1}",
|
||||
fullFilePath,
|
||||
ex);
|
||||
|
||||
var message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.TestAssembly_AssemblyDiscoveryFailure,
|
||||
fullFilePath,
|
||||
ex.Message);
|
||||
warnings.Add(message);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
var message = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.TestAssembly_AssemblyDiscoveryFailure,
|
||||
fullFilePath,
|
||||
ex.Message);
|
||||
warnings.Add(message);
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
"MSPhoneTestDiscoverer.TryGetTests: Failed to discover tests from {0}. Reason:{1}",
|
||||
assemblyFileName,
|
||||
ex);
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("Exceptions thrown from the Loader :");
|
||||
|
||||
if (ex.LoaderExceptions != null)
|
||||
{
|
||||
foreach (var loaderEx in ex.LoaderExceptions)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("{0}", loaderEx);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
// Ignore BadImageFormatException when loading native dll in managed adapter.
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Catch all exceptions, if discoverer fails to load the dll then let caller continue with other sources.
|
||||
// Discover test doesn't work if there is a managed C++ project in solution
|
||||
// Assembly.Load() fails to load the managed cpp executable, with FileLoadException. It can load the dll
|
||||
// successfully though. This is known CLR issue.
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
"TestDiscoverer.TryGetTests: Failed to discover tests from {0}. Reason:{1}",
|
||||
assemblyFileName,
|
||||
ex);
|
||||
var message = ex is FileNotFoundException fileNotFoundEx
|
||||
? fileNotFoundEx.Message
|
||||
: string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.TestAssembly_AssemblyDiscoveryFailure,
|
||||
fullFilePath,
|
||||
ex.Message);
|
||||
warnings.Add(message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ICollection<UnitTestElement> GetTestsInIsolation(
|
||||
string fullFilePath,
|
||||
IRunSettings runSettings,
|
||||
out ICollection<string> warnings)
|
||||
{
|
||||
using (var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings, frameworkHandle: null))
|
||||
{
|
||||
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
|
||||
var assemblyEnumerator = isolationHost.CreateInstanceForType(
|
||||
typeof(AssemblyEnumerator), new object[] { TestSettings.CurrentSettings }) as AssemblyEnumerator;
|
||||
|
||||
// After loading adapter reset the child-domain's appbase to point to test source location
|
||||
isolationHost.UpdateAppBaseToTestSourceLocation();
|
||||
|
||||
return assemblyEnumerator.EnumerateAssembly(fullFilePath, out warnings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a method is a valid test method.
|
||||
/// </summary>
|
||||
internal class TestMethodValidator
|
||||
{
|
||||
private readonly ReflectHelper reflectHelper;
|
||||
private readonly Type _testMethodAttrib;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestMethodValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
|
||||
internal TestMethodValidator(
|
||||
ReflectHelper reflectHelper,
|
||||
Type testMethodAttrib)
|
||||
{
|
||||
this.reflectHelper = reflectHelper;
|
||||
_testMethodAttrib = testMethodAttrib;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a method is a valid test method.
|
||||
/// </summary>
|
||||
/// <param name="testMethodInfo"> The reflected method. </param>
|
||||
/// <param name="type"> The reflected type. </param>
|
||||
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
|
||||
/// <returns> Return true if a method is a valid test method. </returns>
|
||||
internal virtual bool IsValidTestMethod(
|
||||
MethodInfo testMethodInfo,
|
||||
Type type,
|
||||
ICollection<string> warnings)
|
||||
{
|
||||
if (!this.reflectHelper.IsAttributeDefined(testMethodInfo, _testMethodAttrib, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generic method Definitions are not valid.
|
||||
if (testMethodInfo.IsGenericMethodDefinition)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorGenericTestMethod, testMethodInfo.DeclaringType.FullName, testMethodInfo.Name);
|
||||
warnings.Add(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Todo: Decide wheter parameter count matters.
|
||||
// The isGenericMethod check below id to verify that there are no closed generic methods slipping through.
|
||||
// Closed generic methods being GenericMethod<int> and open being GenericMethod<T>.
|
||||
var isValidTestMethod = testMethodInfo.IsPublic && !testMethodInfo.IsAbstract && !testMethodInfo.IsStatic
|
||||
&& !testMethodInfo.IsGenericMethod
|
||||
&& testMethodInfo.IsVoidOrTaskReturnType();
|
||||
|
||||
if (!isValidTestMethod)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorIncorrectTestMethodSignature, type.FullName, testMethodInfo.Name);
|
||||
warnings.Add(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates through the type looking for Valid Test Methods to execute.
|
||||
/// </summary>
|
||||
internal class TypeEnumerator
|
||||
{
|
||||
private readonly Type type;
|
||||
private readonly string assemblyName;
|
||||
private readonly TypeValidator typeValidator;
|
||||
private readonly TestMethodValidator testMethodValidator;
|
||||
private readonly ReflectHelper reflectHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeEnumerator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type"> The reflected type. </param>
|
||||
/// <param name="assemblyName"> The name of the assembly being reflected. </param>
|
||||
/// <param name="reflectHelper"> An instance to reflection helper for type information. </param>
|
||||
/// <param name="typeValidator"> The validator for test classes. </param>
|
||||
/// <param name="testMethodValidator"> The validator for test methods. </param>
|
||||
internal TypeEnumerator(Type type, string assemblyName, ReflectHelper reflectHelper, TypeValidator typeValidator, TestMethodValidator testMethodValidator)
|
||||
{
|
||||
this.type = type;
|
||||
this.assemblyName = assemblyName;
|
||||
this.reflectHelper = reflectHelper;
|
||||
this.typeValidator = typeValidator;
|
||||
this.testMethodValidator = testMethodValidator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Walk through all methods in the type, and find out the test methods
|
||||
/// </summary>
|
||||
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
|
||||
/// <returns> list of test cases.</returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "This is only for internal use.")]
|
||||
internal virtual ICollection<UnitTestElement> Enumerate(out ICollection<string> warnings)
|
||||
{
|
||||
warnings = new Collection<string>();
|
||||
|
||||
if (!this.typeValidator.IsValidTestClass(this.type, warnings))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If test class is valid, then get the tests
|
||||
return this.GetTests(warnings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of valid tests in a type.
|
||||
/// </summary>
|
||||
/// <param name="warnings"> Contains warnings if any, that need to be passed back to the caller. </param>
|
||||
/// <returns> List of Valid Tests. </returns>
|
||||
internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)
|
||||
{
|
||||
var tests = new Collection<UnitTestElement>();
|
||||
|
||||
// Test class is already valid. Verify methods.
|
||||
foreach (var method in this.type.GetRuntimeMethods())
|
||||
{
|
||||
var isMethodDeclaredInTestTypeAssembly = this.reflectHelper.IsMethodDeclaredInSameAssemblyAsType(method, this.type);
|
||||
var enableMethodsFromOtherAssemblies = TestSettings.CurrentSettings.EnableBaseClassTestMethodsFromOtherAssemblies;
|
||||
|
||||
if (!isMethodDeclaredInTestTypeAssembly && !enableMethodsFromOtherAssemblies)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.testMethodValidator.IsValidTestMethod(method, this.type, warnings))
|
||||
{
|
||||
tests.Add(this.GetTestFromMethod(method, isMethodDeclaredInTestTypeAssembly, warnings));
|
||||
}
|
||||
}
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a UnitTestElement from a MethodInfo object filling it up with appropriate values.
|
||||
/// </summary>
|
||||
/// <param name="method">The reflected method.</param>
|
||||
/// <param name="isDeclaredInTestTypeAssembly">True if the reflected method is declared in the same assembly as the current type.</param>
|
||||
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
|
||||
/// <returns> Returns a UnitTestElement.</returns>
|
||||
internal UnitTestElement GetTestFromMethod(
|
||||
MethodInfo method,
|
||||
bool isDeclaredInTestTypeAssembly,
|
||||
ICollection<string> warnings)
|
||||
{
|
||||
Debug.Assert(this.type.AssemblyQualifiedName != null, "AssemblyQualifiedName for method is null.");
|
||||
|
||||
// This allows void returning async test method to be valid test method. Though they will be executed similar to non-async test method.
|
||||
var isAsync = ReflectHelper.MatchReturnType(method, typeof(Task));
|
||||
|
||||
var testMethod = new TestMethod(method.Name, this.type.FullName, this.assemblyName, isAsync);
|
||||
|
||||
if (!method.DeclaringType.FullName.Equals(this.type.FullName))
|
||||
{
|
||||
testMethod.DeclaringClassFullName = method.DeclaringType.FullName;
|
||||
}
|
||||
|
||||
if (!isDeclaredInTestTypeAssembly)
|
||||
{
|
||||
testMethod.DeclaringAssemblyName =
|
||||
PlatformServiceProvider.Instance.FileOperations.GetAssemblyPath(
|
||||
method.DeclaringType.GetTypeInfo().Assembly);
|
||||
}
|
||||
|
||||
var testElement = new UnitTestElement(testMethod);
|
||||
|
||||
// TODO currently nanoFramework does not support async
|
||||
//// Get compiler generated type name for async test method (either void returning or task returning).
|
||||
//var asyncTypeName = method.GetAsyncTypeName();
|
||||
//testElement.AsyncTypeName = asyncTypeName;
|
||||
|
||||
testElement.TestCategory = this.reflectHelper.GetCategories(method);
|
||||
|
||||
var traits = this.reflectHelper.GetTestPropertiesAsTraits(method);
|
||||
|
||||
var ownerTrait = this.reflectHelper.GetTestOwnerAsTraits(method);
|
||||
if (ownerTrait != null)
|
||||
{
|
||||
traits = traits.Concat(new[] { ownerTrait });
|
||||
}
|
||||
|
||||
testElement.Priority = this.reflectHelper.GetPriority(method);
|
||||
|
||||
// nanoFramework test adapter does not support Priority attribute
|
||||
if (testElement.Priority != null)
|
||||
{
|
||||
// issue a warning about this
|
||||
warnings.Add($"{this.type.FullName} in {this.assemblyName} has Priority attribute set. Ignoring it because nanoFramework test adapter does support this.");
|
||||
|
||||
// REset it to null
|
||||
testElement.Priority = null;
|
||||
}
|
||||
|
||||
var priorityTrait = this.reflectHelper.GetTestPriorityAsTraits(testElement.Priority);
|
||||
if (priorityTrait != null)
|
||||
{
|
||||
traits = traits.Concat(new[] { priorityTrait });
|
||||
}
|
||||
|
||||
testElement.Traits = traits.ToArray();
|
||||
|
||||
// TODO
|
||||
// Get Deployment items if any.
|
||||
//testElement.DeploymentItems = PlatformServiceProvider.Instance.TestDeployment.GetDeploymentItems(
|
||||
// method,
|
||||
// this.type,
|
||||
// warnings);
|
||||
|
||||
return testElement;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a type is a valid test class for this adapter.
|
||||
/// </summary>
|
||||
internal class TypeValidator
|
||||
{
|
||||
// Setting this to a string representation instead of a typeof(TestContext).FullName
|
||||
// since the later would require a load of the Test Framework extension assembly at this point.
|
||||
// TODO adjust after settling the assembly name
|
||||
private const string TestContextFullName = "nanoFramework.TestPlatform.MSTest.TestAdapter.TestContext";
|
||||
private readonly ReflectHelper reflectHelper;
|
||||
private readonly Type _testClassAttrib;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="reflectHelper">An instance to reflection helper for type information.</param>
|
||||
internal TypeValidator(
|
||||
ReflectHelper reflectHelper,
|
||||
Type testClassAttrib)
|
||||
{
|
||||
this.reflectHelper = reflectHelper;
|
||||
|
||||
_testClassAttrib = testClassAttrib;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a type is a valid test class for this adapter.
|
||||
/// </summary>
|
||||
/// <param name="type">The reflected type.</param>
|
||||
/// <param name="warnings">Contains warnings if any, that need to be passed back to the caller.</param>
|
||||
/// <returns>Return true if it is a valid test class.</returns>
|
||||
internal virtual bool IsValidTestClass(
|
||||
Type type,
|
||||
ICollection<string> warnings)
|
||||
{
|
||||
if (type.GetTypeInfo().IsClass && (this.reflectHelper.IsAttributeDefined(type, _testClassAttrib, false)))
|
||||
{
|
||||
var isPublic = type.GetTypeInfo().IsPublic || (type.GetTypeInfo().IsNested && type.GetTypeInfo().IsNestedPublic);
|
||||
|
||||
// non-public class
|
||||
if (!isPublic)
|
||||
{
|
||||
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorNonPublicTestClass, type.FullName);
|
||||
warnings.Add(warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generic class
|
||||
if (type.GetTypeInfo().IsGenericTypeDefinition && !type.GetTypeInfo().IsAbstract)
|
||||
{
|
||||
// In IDE generic classes that are not abstract are treated as not runnable. Keep consistence.
|
||||
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorNonPublicTestClass, type.FullName);
|
||||
warnings.Add(warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Class is not valid if the testContext property is incorrect
|
||||
if (!this.HasCorrectTestContextSignature(type))
|
||||
{
|
||||
var warning = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInValidTestContextSignature, type.FullName);
|
||||
warnings.Add(warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Abstract test classes can be base classes for derived test classes.
|
||||
// There is no way to see if there are derived test classes.
|
||||
// Thus if a test class is abstract, just ignore all test methods from it
|
||||
// (they will be visible in derived classes). No warnings (such as test method, deployment item,
|
||||
// etc attribute is defined on the class) will be generated for this class:
|
||||
// What we do is:
|
||||
// - report the class as "not valid" test class. This will cause to skip enumerating tests from it.
|
||||
// - Do not generate warnings/do not create NOT RUNNABLE tests.
|
||||
if (type.GetTypeInfo().IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the type has a valid TestContext property definition.
|
||||
/// </summary>
|
||||
/// <param name="type">The reflected type.</param>
|
||||
/// <returns>Returns true if type has a valid TestContext property definition.</returns>
|
||||
internal bool HasCorrectTestContextSignature(Type type)
|
||||
{
|
||||
Debug.Assert(type != null, "HasCorrectTestContextSignature type is null");
|
||||
|
||||
var propertyInfoEnumerable = type.GetTypeInfo().DeclaredProperties;
|
||||
var propertyInfo = new List<PropertyInfo>();
|
||||
|
||||
foreach (var pinfo in propertyInfoEnumerable)
|
||||
{
|
||||
// PropertyType.FullName can be null if the property is a generic type.
|
||||
if (TestContextFullName.Equals(pinfo.PropertyType.FullName, StringComparison.Ordinal))
|
||||
{
|
||||
propertyInfo.Add(pinfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyInfo.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var pinfo in propertyInfo)
|
||||
{
|
||||
var setInfo = pinfo.SetMethod;
|
||||
if (setInfo == null)
|
||||
{
|
||||
// we have a getter, but not a setter.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setInfo.IsPrivate || setInfo.IsStatic || setInfo.IsAbstract)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery
|
||||
{
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
internal class UnitTestDiscoverer
|
||||
{
|
||||
private readonly AssemblyEnumeratorWrapper assemblyEnumeratorWrapper;
|
||||
|
||||
internal UnitTestDiscoverer()
|
||||
{
|
||||
this.assemblyEnumeratorWrapper = new AssemblyEnumeratorWrapper();
|
||||
this.TestMethodFilter = new TestMethodFilter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets method filter for filtering tests
|
||||
/// </summary>
|
||||
private TestMethodFilter TestMethodFilter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Discovers the tests available from the provided sources.
|
||||
/// </summary>
|
||||
/// <param name="sources"> The sources. </param>
|
||||
/// <param name="logger"> The logger. </param>
|
||||
/// <param name="discoverySink"> The discovery Sink. </param>
|
||||
/// <param name="discoveryContext"> The discovery context. </param>
|
||||
internal void DiscoverTests(
|
||||
IEnumerable<string> sources,
|
||||
IMessageLogger logger,
|
||||
ITestCaseDiscoverySink discoverySink,
|
||||
IDiscoveryContext discoveryContext)
|
||||
{
|
||||
foreach (var source in sources)
|
||||
{
|
||||
this.DiscoverTestsInSource(source, logger, discoverySink, discoveryContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the tests from the parameter source
|
||||
/// </summary>
|
||||
/// <param name="source"> The source. </param>
|
||||
/// <param name="logger"> The logger. </param>
|
||||
/// <param name="discoverySink"> The discovery Sink. </param>
|
||||
/// <param name="discoveryContext"> The discovery context. </param>
|
||||
internal virtual void DiscoverTestsInSource(
|
||||
string source,
|
||||
IMessageLogger logger,
|
||||
ITestCaseDiscoverySink discoverySink,
|
||||
IDiscoveryContext discoveryContext)
|
||||
{
|
||||
ICollection<string> warnings;
|
||||
|
||||
var testElements = this.assemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, out warnings);
|
||||
|
||||
// log the warnings
|
||||
foreach (var warning in warnings)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
|
||||
"TestDiscoverer: Warning during discovery from {0}. {1} ",
|
||||
source,
|
||||
warning);
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.DiscoveryWarning, source, warning);
|
||||
logger.SendMessage(TestMessageLevel.Warning, message);
|
||||
}
|
||||
|
||||
// No tests found => nothing to do
|
||||
if (testElements == null || testElements.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
|
||||
"TestDiscoverer: Found {0} tests from source {1}",
|
||||
testElements.Count,
|
||||
source);
|
||||
|
||||
this.SendTestCases(source, testElements, discoverySink, discoveryContext, logger);
|
||||
}
|
||||
|
||||
internal void SendTestCases(
|
||||
string source,
|
||||
IEnumerable<UnitTestElement> testElements,
|
||||
ITestCaseDiscoverySink discoverySink,
|
||||
IDiscoveryContext discoveryContext,
|
||||
IMessageLogger logger)
|
||||
{
|
||||
var shouldCollectSourceInformation = TestSettings.RunConfigurationSettings.CollectSourceInformation;
|
||||
|
||||
var navigationSessions = new Dictionary<string, object>();
|
||||
try
|
||||
{
|
||||
if (shouldCollectSourceInformation)
|
||||
{
|
||||
navigationSessions.Add(source, PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(source));
|
||||
}
|
||||
|
||||
// Get filter expression and skip discovery in case filter expression has parsing error.
|
||||
bool filterHasError = false;
|
||||
ITestCaseFilterExpression filterExpression = this.TestMethodFilter.GetFilterExpression(discoveryContext, logger, out filterHasError);
|
||||
if (filterHasError)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var testElement in testElements)
|
||||
{
|
||||
var testCase = testElement.ToTestCase();
|
||||
|
||||
// Filter tests based on test case filters
|
||||
if (filterExpression != null && filterExpression.MatchTestCase(testCase, (p) => this.TestMethodFilter.PropertyValueProvider(testCase, p)) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
object testNavigationSession;
|
||||
if (shouldCollectSourceInformation)
|
||||
{
|
||||
string testSource = testElement.TestMethod.DeclaringAssemblyName ?? source;
|
||||
|
||||
if (!navigationSessions.TryGetValue(testSource, out testNavigationSession))
|
||||
{
|
||||
testNavigationSession = PlatformServiceProvider.Instance.FileOperations.CreateNavigationSession(testSource);
|
||||
navigationSessions.Add(testSource, testNavigationSession);
|
||||
}
|
||||
|
||||
if (testNavigationSession != null)
|
||||
{
|
||||
var className = testElement.TestMethod.DeclaringClassFullName
|
||||
?? testElement.TestMethod.FullClassName;
|
||||
|
||||
var methodName = testElement.TestMethod.Name;
|
||||
|
||||
// TODO currently nanoFramework does not support async
|
||||
//// If it is async test method use compiler generated type and method name for navigation data.
|
||||
//if (!string.IsNullOrEmpty(testElement.AsyncTypeName))
|
||||
//{
|
||||
// className = testElement.AsyncTypeName;
|
||||
|
||||
// // compiler generated method name is "MoveNext".
|
||||
// methodName = "MoveNext";
|
||||
//}
|
||||
|
||||
int minLineNumber;
|
||||
string fileName;
|
||||
|
||||
PlatformServiceProvider.Instance.FileOperations.GetNavigationData(
|
||||
testNavigationSession,
|
||||
className,
|
||||
methodName,
|
||||
out minLineNumber,
|
||||
out fileName);
|
||||
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
testCase.LineNumber = minLineNumber;
|
||||
testCase.CodeFilePath = fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
discoverySink.SendTestCase(testCase);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (object navigationSession in navigationSessions.Values)
|
||||
{
|
||||
PlatformServiceProvider.Instance.FileOperations.DisposeNavigationSession(navigationSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// AssertFailedException class. Used to indicate failure for a test case
|
||||
/// </summary>
|
||||
public partial class AssertFailedException : UnitTestAssertException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertFailedException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="msg"> The message. </param>
|
||||
/// <param name="ex"> The exception. </param>
|
||||
public AssertFailedException(string msg, Exception ex)
|
||||
: base(msg, ex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertFailedException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="msg"> The message. </param>
|
||||
public AssertFailedException(string msg)
|
||||
: base(msg)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertFailedException"/> class.
|
||||
/// </summary>
|
||||
public AssertFailedException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// The assert inconclusive exception.
|
||||
/// </summary>
|
||||
public partial class AssertInconclusiveException : UnitTestAssertException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertInconclusiveException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="msg"> The message. </param>
|
||||
/// <param name="ex"> The exception. </param>
|
||||
public AssertInconclusiveException(string msg, Exception ex)
|
||||
: base(msg, ex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertInconclusiveException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="msg"> The message. </param>
|
||||
public AssertInconclusiveException(string msg)
|
||||
: base(msg)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssertInconclusiveException"/> class.
|
||||
/// </summary>
|
||||
public AssertInconclusiveException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for Framework Exceptions.
|
||||
/// </summary>
|
||||
public abstract partial class UnitTestAssertException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitTestAssertException"/> class.
|
||||
/// </summary>
|
||||
protected UnitTestAssertException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitTestAssertException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="msg"> The message. </param>
|
||||
/// <param name="ex"> The exception. </param>
|
||||
protected UnitTestAssertException(string msg, Exception ex)
|
||||
: base(msg, ex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitTestAssertException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="msg"> The message. </param>
|
||||
protected UnitTestAssertException(string msg)
|
||||
: base(msg)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Interface;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Logging;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class LogMessageListener : IDisposable
|
||||
{
|
||||
private static LogMessageListener activeRedirector;
|
||||
private readonly LogMessageListener previousRedirector;
|
||||
private readonly TextWriter redirectLoggerOut;
|
||||
private readonly TextWriter redirectStdErr;
|
||||
private readonly bool captureDebugTraces;
|
||||
|
||||
/// <summary>
|
||||
/// Trace listener to capture Trace.WriteLines in the test cases
|
||||
/// </summary>
|
||||
private ITraceListener traceListener;
|
||||
|
||||
/// <summary>
|
||||
/// Trace listener Manager to perform operation on tracelistener objects.
|
||||
/// </summary>
|
||||
private ITraceListenerManager traceListenerManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogMessageListener"/> class.
|
||||
/// </summary>
|
||||
/// <param name="captureDebugTraces">Captures debug traces if true.</param>
|
||||
public LogMessageListener(bool captureDebugTraces)
|
||||
{
|
||||
this.captureDebugTraces = captureDebugTraces;
|
||||
|
||||
// Cache the original output/error streams and replace it with the own stream.
|
||||
this.redirectLoggerOut = new ThreadSafeStringWriter(CultureInfo.InvariantCulture);
|
||||
this.redirectStdErr = new ThreadSafeStringWriter(CultureInfo.InvariantCulture);
|
||||
|
||||
Logger.OnLogMessage += this.redirectLoggerOut.WriteLine;
|
||||
|
||||
// Cache the previous redirector if any and replace the trace listener.
|
||||
this.previousRedirector = activeRedirector;
|
||||
|
||||
if (this.captureDebugTraces)
|
||||
{
|
||||
this.traceListener = PlatformServiceProvider.Instance.GetTraceListener(new ThreadSafeStringWriter(CultureInfo.InvariantCulture));
|
||||
this.traceListenerManager = PlatformServiceProvider.Instance.GetTraceListenerManager(this.redirectLoggerOut, this.redirectStdErr);
|
||||
|
||||
// If there was a previous LogMessageListener active, remove its
|
||||
// TraceListener (it will be restored when this one is disposed).
|
||||
if (this.previousRedirector != null && this.previousRedirector.traceListener != null)
|
||||
{
|
||||
this.traceListenerManager.Remove(this.previousRedirector.traceListener);
|
||||
}
|
||||
|
||||
this.traceListenerManager.Add(this.traceListener);
|
||||
}
|
||||
|
||||
activeRedirector = this;
|
||||
}
|
||||
|
||||
~LogMessageListener()
|
||||
{
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets logger output
|
||||
/// </summary>
|
||||
public string StandardOutput => this.redirectLoggerOut.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets 'Error' Output from the redirected stream
|
||||
/// </summary>
|
||||
public string StandardError => this.redirectStdErr.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets 'Trace' Output from the redirected stream
|
||||
/// </summary>
|
||||
public string DebugTrace
|
||||
{
|
||||
get
|
||||
{
|
||||
return (this.traceListener == null || this.traceListener.GetWriter() == null) ?
|
||||
string.Empty : this.traceListener.GetWriter().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Logger.OnLogMessage -= this.redirectLoggerOut.WriteLine;
|
||||
Logger.OnLogMessage -= this.redirectStdErr.WriteLine;
|
||||
|
||||
this.redirectLoggerOut.Dispose();
|
||||
this.redirectStdErr.Dispose();
|
||||
|
||||
if (this.captureDebugTraces)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.traceListenerManager.Remove(this.traceListener);
|
||||
|
||||
// Restore the previous LogMessageListener's TraceListener (if there was one)
|
||||
if (this.previousRedirector != null && this.previousRedirector.traceListener != null)
|
||||
{
|
||||
this.traceListenerManager.Add(this.previousRedirector.traceListener);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Catch all exceptions since Dispose should not throw.
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError(
|
||||
"ConsoleOutputRedirector.Dispose threw exception: {0}",
|
||||
e);
|
||||
}
|
||||
|
||||
if (this.traceListener != null)
|
||||
{
|
||||
// Dispose trace manager and listeners
|
||||
this.traceListenerManager.Dispose(this.traceListener);
|
||||
this.traceListenerManager = null;
|
||||
this.traceListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
activeRedirector = this.previousRedirector;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using Extensions;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using ObjectModel;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Defines TestAssembly Info object
|
||||
/// </summary>
|
||||
public class TestAssemblyInfo
|
||||
{
|
||||
private MethodInfo assemblyCleanupMethod;
|
||||
|
||||
private MethodInfo assemblyInitializeMethod;
|
||||
private object assemblyInfoExecuteSyncObject;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestAssemblyInfo"/> class.
|
||||
/// </summary>
|
||||
internal TestAssemblyInfo()
|
||||
{
|
||||
this.assemblyInfoExecuteSyncObject = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>AssemblyInitialize</c> method for the assembly.
|
||||
/// </summary>
|
||||
public MethodInfo AssemblyInitializeMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.assemblyInitializeMethod;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
if (this.assemblyInitializeMethod != null)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiAssemblyInit, this.assemblyInitializeMethod.DeclaringType.FullName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
this.assemblyInitializeMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>AssemblyCleanup</c> method for the assembly.
|
||||
/// </summary>
|
||||
public MethodInfo AssemblyCleanupMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.assemblyCleanupMethod;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
if (this.assemblyCleanupMethod != null)
|
||||
{
|
||||
string message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiAssemblyClean, this.assemblyCleanupMethod.DeclaringType.FullName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
this.assemblyCleanupMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <c>AssemblyInitialize</c> has been executed.
|
||||
/// </summary>
|
||||
public bool IsAssemblyInitializeExecuted { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly initialization exception.
|
||||
/// </summary>
|
||||
public Exception AssemblyInitializationException { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this assembly has an executable <c>AssemblyCleanup</c> method.
|
||||
/// </summary>
|
||||
public bool HasExecutableCleanupMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
// If no assembly cleanup, then continue with the next one.
|
||||
if (this.AssemblyCleanupMethod == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If assembly initialization was successful, then only call assembly cleanup.
|
||||
if (this.AssemblyInitializationException != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs assembly initialize method.
|
||||
/// </summary>
|
||||
/// <param name="testContext"> The test context. </param>
|
||||
/// <exception cref="TestFailedException"> Throws a test failed exception if the initialization method throws an exception. </exception>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
public void RunAssemblyInitialize(TestContext testContext)
|
||||
{
|
||||
// No assembly initialize => nothing to do.
|
||||
if (this.AssemblyInitializeMethod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (testContext == null)
|
||||
{
|
||||
throw new NullReferenceException(Resource.TestContextIsNull);
|
||||
}
|
||||
|
||||
// If assembly initialization is not done, then do it.
|
||||
if (!this.IsAssemblyInitializeExecuted)
|
||||
{
|
||||
// Aquiring a lock is usually a costly operation which does not need to be
|
||||
// performed every time if the assembly init is already executed.
|
||||
lock (this.assemblyInfoExecuteSyncObject)
|
||||
{
|
||||
// Perform a check again.
|
||||
if (!this.IsAssemblyInitializeExecuted)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.AssemblyInitializeMethod.InvokeAsSynchronousTask(null, testContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.AssemblyInitializationException = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.IsAssemblyInitializeExecuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If assemblyInitialization was successful, then dont do anything
|
||||
if (this.AssemblyInitializationException == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache and return an already created TestFailedException.
|
||||
if (this.AssemblyInitializationException is TestFailedException)
|
||||
{
|
||||
throw this.AssemblyInitializationException;
|
||||
}
|
||||
|
||||
var realException = this.AssemblyInitializationException.InnerException
|
||||
?? this.AssemblyInitializationException;
|
||||
|
||||
var outcome = UnitTestOutcome.Failed;
|
||||
string errorMessage = null;
|
||||
StackTraceInformation stackTraceInfo = null;
|
||||
if (!realException.TryGetUnitTestAssertException(out outcome, out errorMessage, out stackTraceInfo))
|
||||
{
|
||||
var exception = realException.GetType().ToString();
|
||||
var message = StackTraceHelper.GetExceptionMessage(realException);
|
||||
errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_AssemblyInitMethodThrows,
|
||||
this.AssemblyInitializeMethod.DeclaringType.FullName,
|
||||
this.AssemblyInitializeMethod.Name,
|
||||
exception,
|
||||
message);
|
||||
stackTraceInfo = StackTraceHelper.GetStackTraceInformation(realException);
|
||||
}
|
||||
|
||||
var testFailedException = new TestFailedException(outcome, errorMessage, stackTraceInfo);
|
||||
this.AssemblyInitializationException = testFailedException;
|
||||
|
||||
throw testFailedException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run assembly cleanup methods
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Any exception that can be thrown as part of a assembly cleanup as warning messages.
|
||||
/// </returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
public string RunAssemblyCleanup()
|
||||
{
|
||||
if (this.AssemblyCleanupMethod == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (this.assemblyInfoExecuteSyncObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.AssemblyCleanupMethod.InvokeAsSynchronousTask(null);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var realException = ex.InnerException ?? ex;
|
||||
|
||||
string errorMessage;
|
||||
|
||||
// special case AssertFailedException to trim off part of the stack trace
|
||||
if (realException is AssertFailedException ||
|
||||
realException is AssertInconclusiveException)
|
||||
{
|
||||
errorMessage = realException.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = StackTraceHelper.GetExceptionMessage(realException);
|
||||
}
|
||||
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_AssemblyCleanupMethodWasUnsuccesful,
|
||||
this.AssemblyCleanupMethod.DeclaringType.Name,
|
||||
this.AssemblyCleanupMethod.Name,
|
||||
errorMessage,
|
||||
StackTraceHelper.GetStackTraceInformation(realException)?.ErrorStackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
|
||||
/// <summary>
|
||||
/// The test case discovery sink.
|
||||
/// </summary>
|
||||
internal class TestCaseDiscoverySink : ITestCaseDiscoverySink
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestCaseDiscoverySink"/> class.
|
||||
/// </summary>
|
||||
public TestCaseDiscoverySink()
|
||||
{
|
||||
this.Tests = new Collection<TestCase>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tests.
|
||||
/// </summary>
|
||||
public ICollection<TestCase> Tests { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sends the test case.
|
||||
/// </summary>
|
||||
/// <param name="discoveredTest"> The discovered test. </param>
|
||||
public void SendTestCase(TestCase discoveredTest)
|
||||
{
|
||||
if (discoveredTest != null)
|
||||
{
|
||||
this.Tests.Add(discoveredTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,352 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Extensions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Execution;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using ObjectModel;
|
||||
using UnitTestOutcome = nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the TestClassInfo object
|
||||
/// </summary>
|
||||
public class TestClassInfo
|
||||
{
|
||||
private MethodInfo classCleanupMethod;
|
||||
private MethodInfo classInitializeMethod;
|
||||
private MethodInfo testCleanupMethod;
|
||||
private MethodInfo testInitializeMethod;
|
||||
private object testClassExecuteSyncObject;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestClassInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">Underlying test class type.</param>
|
||||
/// <param name="constructor">Constructor for the test class.</param>
|
||||
/// <param name="testContextProperty">Reference to the <see cref="TestContext"/> property in test class.</param>
|
||||
/// <param name="classAttribute">Test class attribute.</param>
|
||||
/// <param name="parent">Parent assembly info.</param>
|
||||
internal TestClassInfo(
|
||||
Type type,
|
||||
ConstructorInfo constructor,
|
||||
PropertyInfo testContextProperty,
|
||||
TestClassAttribute classAttribute,
|
||||
TestAssemblyInfo parent)
|
||||
{
|
||||
Debug.Assert(type != null, "Type should not be null");
|
||||
Debug.Assert(constructor != null, "Constructor should not be null");
|
||||
Debug.Assert(parent != null, "Parent should not be null");
|
||||
Debug.Assert(classAttribute != null, "ClassAtribute should not be null");
|
||||
|
||||
this.ClassType = type;
|
||||
this.Constructor = constructor;
|
||||
this.TestContextProperty = testContextProperty;
|
||||
this.BaseTestInitializeMethodsQueue = new Queue<MethodInfo>();
|
||||
this.BaseTestCleanupMethodsQueue = new Queue<MethodInfo>();
|
||||
this.Parent = parent;
|
||||
this.ClassAttribute = classAttribute;
|
||||
this.testClassExecuteSyncObject = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class attribute.
|
||||
/// </summary>
|
||||
public TestClassAttribute ClassAttribute { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class type.
|
||||
/// </summary>
|
||||
public Type ClassType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the constructor.
|
||||
/// </summary>
|
||||
public ConstructorInfo Constructor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the test context property.
|
||||
/// </summary>
|
||||
public PropertyInfo TestContextProperty { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent <see cref="TestAssemblyInfo"/>.
|
||||
/// </summary>
|
||||
public TestAssemblyInfo Parent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class initialize method.
|
||||
/// </summary>
|
||||
public MethodInfo ClassInitializeMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.classInitializeMethod;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
if (this.classInitializeMethod != null)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClassInit, this.ClassType.FullName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
this.classInitializeMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether class initialize has executed.
|
||||
/// </summary>
|
||||
public bool IsClassInitializeExecuted { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception thrown during <see cref="ClassInitializeAttribute"/> method invocation.
|
||||
/// </summary>
|
||||
public Exception ClassInitializationException { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class cleanup method.
|
||||
/// </summary>
|
||||
public MethodInfo ClassCleanupMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.classCleanupMethod;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
if (this.classCleanupMethod != null)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClassClean, this.ClassType.FullName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
this.classCleanupMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this class info has a executable cleanup method.
|
||||
/// </summary>
|
||||
public bool HasExecutableCleanupMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
// If no class cleanup, then continue with the next one.
|
||||
if (this.ClassCleanupMethod == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If class initialization was successful, then only call class cleanup.
|
||||
if (this.ClassInitializationException != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the test initialize method.
|
||||
/// </summary>
|
||||
public MethodInfo TestInitializeMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.testInitializeMethod;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
if (this.testInitializeMethod != null)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiInit, this.ClassType.FullName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
this.testInitializeMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the test cleanup method.
|
||||
/// </summary>
|
||||
public MethodInfo TestCleanupMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.testCleanupMethod;
|
||||
}
|
||||
|
||||
internal set
|
||||
{
|
||||
if (this.testCleanupMethod != null)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorMultiClean, this.ClassType.FullName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
this.testCleanupMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a queue of test initialize methods to call for this type.
|
||||
/// </summary>
|
||||
public Queue<MethodInfo> BaseTestInitializeMethodsQueue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a queue of test cleanup methods to call for this type.
|
||||
/// </summary>
|
||||
public Queue<MethodInfo> BaseTestCleanupMethodsQueue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs the class initialize method.
|
||||
/// </summary>
|
||||
/// <param name="testContext"> The test context. </param>
|
||||
/// <exception cref="TestFailedException"> Throws a test failed exception if the initialization method throws an exception. </exception>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
public void RunClassInitialize(TestContext testContext)
|
||||
{
|
||||
// If no class initialize return
|
||||
if (this.ClassInitializeMethod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (testContext == null)
|
||||
{
|
||||
throw new NullReferenceException(Resource.TestContextIsNull);
|
||||
}
|
||||
|
||||
// If class initialization is not done, then do it.
|
||||
if (!this.IsClassInitializeExecuted)
|
||||
{
|
||||
// Aquiring a lock is usually a costly operation which does not need to be
|
||||
// performed every time if the class init is already executed.
|
||||
lock (this.testClassExecuteSyncObject)
|
||||
{
|
||||
// Perform a check again.
|
||||
if (!this.IsClassInitializeExecuted)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.ClassInitializeMethod.InvokeAsSynchronousTask(null, testContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.ClassInitializationException = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.IsClassInitializeExecuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If classInitialization was successful, then dont do anything
|
||||
if (this.ClassInitializationException == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ClassInitializationException is TestFailedException)
|
||||
{
|
||||
throw this.ClassInitializationException;
|
||||
}
|
||||
|
||||
// Fail the current test if it was a failure.
|
||||
var realException = this.ClassInitializationException.InnerException ?? this.ClassInitializationException;
|
||||
|
||||
var outcome = UnitTestOutcome.Failed;
|
||||
string errorMessage = null;
|
||||
StackTraceInformation exceptionStackTraceInfo = null;
|
||||
if (!realException.TryGetUnitTestAssertException(out outcome, out errorMessage, out exceptionStackTraceInfo))
|
||||
{
|
||||
errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_ClassInitMethodThrows,
|
||||
this.ClassType.FullName,
|
||||
this.ClassInitializeMethod.Name,
|
||||
realException.GetType().ToString(),
|
||||
StackTraceHelper.GetExceptionMessage(realException));
|
||||
|
||||
exceptionStackTraceInfo = realException.TryGetStackTraceInformation();
|
||||
}
|
||||
|
||||
var testFailedException = new TestFailedException(outcome, errorMessage, exceptionStackTraceInfo);
|
||||
this.ClassInitializationException = testFailedException;
|
||||
|
||||
throw testFailedException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run class cleanup methods
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Any exception that can be thrown as part of a class cleanup as warning messages.
|
||||
/// </returns>
|
||||
[SuppressMessageAttribute("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
public string RunClassCleanup()
|
||||
{
|
||||
if (this.ClassCleanupMethod == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.IsClassInitializeExecuted || this.ClassInitializeMethod == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.ClassCleanupMethod.InvokeAsSynchronousTask(null);
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var realException = exception.InnerException ?? exception;
|
||||
|
||||
string errorMessage;
|
||||
|
||||
// special case AssertFailedException to trim off part of the stack trace
|
||||
if (realException is AssertFailedException ||
|
||||
realException is AssertInconclusiveException)
|
||||
{
|
||||
errorMessage = realException.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = StackTraceHelper.GetExceptionMessage(realException);
|
||||
}
|
||||
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_ClassCleanupMethodWasUnsuccesful,
|
||||
this.ClassType.Name,
|
||||
this.ClassCleanupMethod.Name,
|
||||
errorMessage,
|
||||
StackTraceHelper.GetStackTraceInformation(realException)?.ErrorStackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,518 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Class responsible for execution of tests at assembly level and sending tests via framework handle
|
||||
/// </summary>
|
||||
public class TestExecutionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies whether the test run is canceled or not
|
||||
/// </summary>
|
||||
private TestRunCancellationToken cancellationToken;
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary for test run parameters
|
||||
/// </summary>
|
||||
private IDictionary<string, object> sessionParameters;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Need to over-write the keys in dictionary.")]
|
||||
public TestExecutionManager()
|
||||
{
|
||||
this.TestMethodFilter = new TestMethodFilter();
|
||||
this.sessionParameters = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets method filter for filtering tests
|
||||
/// </summary>
|
||||
private TestMethodFilter TestMethodFilter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether any test executed has failed.
|
||||
/// </summary>
|
||||
private bool HasAnyTestFailed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs the tests.
|
||||
/// </summary>
|
||||
/// <param name="tests">Tests to be run.</param>
|
||||
/// <param name="runContext">Context to use when executing the tests.</param>
|
||||
/// <param name="frameworkHandle">Handle to the framework to record results and to do framework operations.</param>
|
||||
/// <param name="runCancellationToken">Test run cancellation tokenn</param>
|
||||
public void RunTests(
|
||||
IEnumerable<TestCase> tests,
|
||||
IRunContext runContext,
|
||||
IFrameworkHandle frameworkHandle,
|
||||
TestRunCancellationToken runCancellationToken)
|
||||
{
|
||||
Debug.Assert(tests != null, "tests");
|
||||
Debug.Assert(runContext != null, "runContext");
|
||||
Debug.Assert(frameworkHandle != null, "frameworkHandle");
|
||||
Debug.Assert(runCancellationToken != null, "runCancellationToken");
|
||||
|
||||
this.cancellationToken = runCancellationToken;
|
||||
|
||||
//var isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
|
||||
|
||||
//// Placing this after deployment since we need information post deployment that we pass in as properties.
|
||||
//this.CacheSessionParameters(runContext, frameworkHandle);
|
||||
|
||||
//// Execute the tests
|
||||
//this.ExecuteTests(tests, runContext, frameworkHandle, isDeploymentDone);
|
||||
|
||||
//if (!this.HasAnyTestFailed)
|
||||
//{
|
||||
// PlatformServiceProvider.Instance.TestDeployment.Cleanup();
|
||||
//}
|
||||
}
|
||||
|
||||
public void RunTests(
|
||||
IEnumerable<string> sources,
|
||||
IRunContext runContext,
|
||||
IFrameworkHandle frameworkHandle,
|
||||
TestRunCancellationToken cancellationToken)
|
||||
{
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
var discoverySink = new TestCaseDiscoverySink();
|
||||
|
||||
var tests = new List<TestCase>();
|
||||
|
||||
// deploy everything first.
|
||||
foreach (var source in sources)
|
||||
{
|
||||
if (this.cancellationToken.Canceled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var logger = (IMessageLogger)frameworkHandle;
|
||||
|
||||
// discover the tests
|
||||
this.GetUnitTestDiscoverer().DiscoverTestsInSource(source, logger, discoverySink, runContext);
|
||||
tests.AddRange(discoverySink.Tests);
|
||||
|
||||
// Clear discoverSinksTests so that it just stores test for one source at one point of time
|
||||
discoverySink.Tests.Clear();
|
||||
}
|
||||
|
||||
bool isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
|
||||
|
||||
// Placing this after deployment since we need information post deployment that we pass in as properties.
|
||||
this.CacheSessionParameters(runContext, frameworkHandle);
|
||||
|
||||
// Run tests.
|
||||
this.ExecuteTests(tests, runContext, frameworkHandle, isDeploymentDone);
|
||||
|
||||
if (!this.HasAnyTestFailed)
|
||||
{
|
||||
PlatformServiceProvider.Instance.TestDeployment.Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the parameter tests
|
||||
/// </summary>
|
||||
/// <param name="tests">Tests to execute.</param>
|
||||
/// <param name="runContext">The run context.</param>
|
||||
/// <param name="frameworkHandle">Handle to record test start/end/results.</param>
|
||||
/// <param name="isDeploymentDone">Indicates if deployment is done.</param>
|
||||
internal virtual void ExecuteTests(
|
||||
IEnumerable<TestCase> tests,
|
||||
IRunContext runContext,
|
||||
IFrameworkHandle frameworkHandle,
|
||||
bool isDeploymentDone)
|
||||
{
|
||||
var testsBySource = from test in tests
|
||||
group test by test.Source into testGroup
|
||||
select new { Source = testGroup.Key, Tests = testGroup };
|
||||
|
||||
foreach (var group in testsBySource)
|
||||
{
|
||||
this.ExecuteTestsInSource(group.Tests, runContext, frameworkHandle, group.Source, isDeploymentDone);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual UnitTestDiscoverer GetUnitTestDiscoverer()
|
||||
{
|
||||
return new UnitTestDiscoverer();
|
||||
}
|
||||
|
||||
internal void SendTestResults(
|
||||
TestCase test,
|
||||
UnitTestResult[] unitTestResults,
|
||||
DateTimeOffset startTime,
|
||||
DateTimeOffset endTime,
|
||||
ITestExecutionRecorder testExecutionRecorder)
|
||||
{
|
||||
if (!(unitTestResults?.Length > 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var unitTestResult in unitTestResults)
|
||||
{
|
||||
if (test == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var testResult = unitTestResult.ToTestResult(test, startTime, endTime, MSTestSettings.CurrentSettings.MapInconclusiveToFailed);
|
||||
|
||||
if (unitTestResult.DatarowIndex >= 0)
|
||||
{
|
||||
testResult.DisplayName = string.Format(CultureInfo.CurrentCulture, Resource.DataDrivenResultDisplayName, test.DisplayName, unitTestResult.DatarowIndex);
|
||||
}
|
||||
|
||||
testExecutionRecorder.RecordEnd(test, testResult.Outcome);
|
||||
|
||||
if (testResult.Outcome == TestOutcome.Failed)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("TestExecutor:Test {0} failed. ErrorMessage:{1}, ErrorStackTrace:{2}.", testResult.TestCase.FullyQualifiedName, testResult.ErrorMessage, testResult.ErrorStackTrace);
|
||||
this.HasAnyTestFailed = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
testExecutionRecorder.RecordResult(testResult);
|
||||
}
|
||||
catch (TestCanceledException)
|
||||
{
|
||||
// Ignore this exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MatchTestFilter(
|
||||
ITestCaseFilterExpression filterExpression,
|
||||
TestCase test,
|
||||
TestMethodFilter testMethodFilter)
|
||||
{
|
||||
if (filterExpression != null && filterExpression.MatchTestCase(test, p => testMethodFilter.PropertyValueProvider(test, p)) == false)
|
||||
{
|
||||
// Skip test if not fitting filter criteria.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the parameter tests present in parameter source
|
||||
/// </summary>
|
||||
/// <param name="tests">Tests to execute.</param>
|
||||
/// <param name="runContext">The run context.</param>
|
||||
/// <param name="frameworkHandle">Handle to record test start/end/results.</param>
|
||||
/// <param name="source">The test container for the tests.</param>
|
||||
/// <param name="isDeploymentDone">Indicates if deployment is done.</param>
|
||||
private void ExecuteTestsInSource(
|
||||
IEnumerable<TestCase> tests,
|
||||
IRunContext runContext,
|
||||
IFrameworkHandle frameworkHandle,
|
||||
string source,
|
||||
bool isDeploymentDone)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(source), "Source cannot be empty");
|
||||
|
||||
if (isDeploymentDone)
|
||||
{
|
||||
source = Path.Combine(PlatformServiceProvider.Instance.TestDeployment.GetDeploymentDirectory(), Path.GetFileName(source));
|
||||
}
|
||||
|
||||
using (var isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(source, runContext?.RunSettings, frameworkHandle))
|
||||
{
|
||||
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
|
||||
var testRunner = isolationHost.CreateInstanceForType(
|
||||
typeof(UnitTestRunner),
|
||||
new object[] { MSTestSettings.CurrentSettings }) as UnitTestRunner;
|
||||
|
||||
// After loading adapter reset the chils-domain's appbase to point to test source location
|
||||
isolationHost.UpdateAppBaseToTestSourceLocation();
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Created unit-test runner {0}", source);
|
||||
|
||||
// Default test set is filtered tests based on user provided filter criteria
|
||||
IEnumerable<TestCase> testsToRun = Enumerable.Empty<TestCase>();
|
||||
var filterExpression = this.TestMethodFilter.GetFilterExpression(runContext, frameworkHandle, out var filterHasError);
|
||||
if (filterHasError)
|
||||
{
|
||||
// Bail out without processing everything else below.
|
||||
return;
|
||||
}
|
||||
|
||||
testsToRun = tests.Where(t => MatchTestFilter(filterExpression, t, this.TestMethodFilter));
|
||||
|
||||
// this is done so that appropriate values of testcontext properties are set at source level
|
||||
// and are merged with session level parameters
|
||||
var sourceLevelParameters = PlatformServiceProvider.Instance.SettingsProvider.GetProperties(source);
|
||||
|
||||
if (this.sessionParameters != null && this.sessionParameters.Count > 0)
|
||||
{
|
||||
sourceLevelParameters = sourceLevelParameters.Concat(this.sessionParameters).ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
|
||||
//var sourceSettingsProvider = isolationHost.CreateInstanceForType(
|
||||
// typeof(TestAssemblySettingsProvider),
|
||||
// null) as TestAssemblySettingsProvider;
|
||||
|
||||
//var sourceSettings = sourceSettingsProvider.GetSettings(source);
|
||||
//var parallelWorkers = sourceSettings.Workers;
|
||||
//var parallelScope = sourceSettings.Scope;
|
||||
|
||||
//if (MSTestSettings.CurrentSettings.ParallelizationWorkers.HasValue)
|
||||
//{
|
||||
// // The runsettings value takes precedence over an assembly level setting. Reset the level.
|
||||
// parallelWorkers = MSTestSettings.CurrentSettings.ParallelizationWorkers.Value;
|
||||
//}
|
||||
|
||||
//if (MSTestSettings.CurrentSettings.ParallelizationScope.HasValue)
|
||||
//{
|
||||
// // The runsettings value takes precedence over an assembly level setting. Reset the level.
|
||||
// parallelScope = MSTestSettings.CurrentSettings.ParallelizationScope.Value;
|
||||
//}
|
||||
|
||||
//if (!MSTestSettings.CurrentSettings.DisableParallelization && sourceSettings.CanParallelizeAssembly && parallelWorkers > 0)
|
||||
//{
|
||||
// // Parallelization is enabled. Let's do further classification for sets.
|
||||
// var logger = (IMessageLogger)frameworkHandle;
|
||||
// logger.SendMessage(
|
||||
// TestMessageLevel.Informational,
|
||||
// string.Format(CultureInfo.CurrentCulture, Resource.TestParallelizationBanner, source, parallelWorkers, parallelScope));
|
||||
|
||||
// // Create test sets for execution, we can execute them in parallel based on parallel settings
|
||||
// IEnumerable<IGrouping<bool, TestCase>> testsets = Enumerable.Empty<IGrouping<bool, TestCase>>();
|
||||
|
||||
// // Parallel and not parallel sets.
|
||||
// testsets = testsToRun.GroupBy(t => t.GetPropertyValue<bool>(TestAdapter.Constants.DoNotParallelizeProperty, false));
|
||||
|
||||
// var parallelizableTestSet = testsets.FirstOrDefault(g => g.Key == false);
|
||||
// var nonparallelizableTestSet = testsets.FirstOrDefault(g => g.Key == true);
|
||||
|
||||
// if (parallelizableTestSet != null)
|
||||
// {
|
||||
// ConcurrentQueue<IEnumerable<TestCase>> queue = null;
|
||||
|
||||
// // Chunk the sets into further groups based on parallel level
|
||||
// switch (parallelScope)
|
||||
// {
|
||||
// case ExecutionScope.MethodLevel:
|
||||
// queue = new ConcurrentQueue<IEnumerable<TestCase>>(parallelizableTestSet.Select(t => new[] { t }));
|
||||
// break;
|
||||
// case ExecutionScope.ClassLevel:
|
||||
// queue = new ConcurrentQueue<IEnumerable<TestCase>>(parallelizableTestSet.GroupBy(t => t.GetPropertyValue(TestAdapter.Constants.TestClassNameProperty) as string));
|
||||
// break;
|
||||
// }
|
||||
|
||||
// var tasks = new List<Task>();
|
||||
|
||||
// for (int i = 0; i < parallelWorkers; i++)
|
||||
// {
|
||||
// tasks.Add(Task.Factory.StartNew(
|
||||
// () =>
|
||||
// {
|
||||
// while (!queue.IsEmpty)
|
||||
// {
|
||||
// if (this.cancellationToken != null && this.cancellationToken.Canceled)
|
||||
// {
|
||||
// // if a cancellation has been requested, do not queue any more test runs.
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (queue.TryDequeue(out IEnumerable<TestCase> testSet))
|
||||
// {
|
||||
// this.ExecuteTestsWithTestRunner(testSet, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// CancellationToken.None,
|
||||
// TaskCreationOptions.LongRunning,
|
||||
// TaskScheduler.Default));
|
||||
// }
|
||||
|
||||
// Task.WaitAll(tasks.ToArray());
|
||||
// }
|
||||
|
||||
// // Queue the non parallel set
|
||||
// if (nonparallelizableTestSet != null)
|
||||
// {
|
||||
// this.ExecuteTestsWithTestRunner(nonparallelizableTestSet, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
{
|
||||
this.ExecuteTestsWithTestRunner(testsToRun, runContext, frameworkHandle, source, sourceLevelParameters, testRunner);
|
||||
}
|
||||
|
||||
this.RunCleanup(frameworkHandle, testRunner);
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
|
||||
"Executed tests belonging to source {0}",
|
||||
source);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteTestsWithTestRunner(
|
||||
IEnumerable<TestCase> tests,
|
||||
IRunContext runContext,
|
||||
ITestExecutionRecorder testExecutionRecorder,
|
||||
string source,
|
||||
IDictionary<string, object> sourceLevelParameters,
|
||||
UnitTestRunner testRunner)
|
||||
{
|
||||
foreach (var currentTest in tests)
|
||||
{
|
||||
if (this.cancellationToken != null && this.cancellationToken.Canceled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var unitTestElement = currentTest.ToUnitTestElement(source);
|
||||
testExecutionRecorder.RecordStart(currentTest);
|
||||
|
||||
var startTime = DateTimeOffset.Now;
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
|
||||
"Executing test {0}",
|
||||
unitTestElement.TestMethod.Name);
|
||||
|
||||
// Run single test passing test context properties to it.
|
||||
//var tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest);
|
||||
//var testContextProperties = this.GetTestContextProperties(tcmProperties, sourceLevelParameters);
|
||||
//var unitTestResult = testRunner.RunSingleTest(unitTestElement.TestMethod, testContextProperties);
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo(
|
||||
"Executed test {0}",
|
||||
unitTestElement.TestMethod.Name);
|
||||
|
||||
var endTime = DateTimeOffset.Now;
|
||||
|
||||
//this.SendTestResults(currentTest, unitTestResult, startTime, endTime, testExecutionRecorder);
|
||||
this.SendTestResults(currentTest, new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.NotFound, string.Format(CultureInfo.CurrentCulture, Resource.TestNotFound, currentTest.DisplayName) )}, startTime, endTime, testExecutionRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get test context properties.
|
||||
/// </summary>
|
||||
/// <param name="tcmProperties">Tcm properties.</param>
|
||||
/// <param name="sourceLevelParameters">Source level parameters.</param>
|
||||
/// <returns>Test context properties.</returns>
|
||||
private IDictionary<string, object> GetTestContextProperties(
|
||||
IDictionary<TestProperty, object> tcmProperties,
|
||||
IDictionary<string, object> sourceLevelParameters)
|
||||
{
|
||||
var testContextProperties = new Dictionary<string, object>();
|
||||
|
||||
// Add tcm properties.
|
||||
foreach (var propertyPair in tcmProperties)
|
||||
{
|
||||
testContextProperties[propertyPair.Key.Id] = propertyPair.Value;
|
||||
}
|
||||
|
||||
// Add source level parameters.
|
||||
foreach (var propertyPair in sourceLevelParameters)
|
||||
{
|
||||
testContextProperties[propertyPair.Key] = propertyPair.Value;
|
||||
}
|
||||
|
||||
return testContextProperties;
|
||||
}
|
||||
|
||||
private void RunCleanup(
|
||||
ITestExecutionRecorder testExecutionRecorder,
|
||||
UnitTestRunner testRunner)
|
||||
{
|
||||
// All cleanups (Class and Assembly) run at the end of test execution. Failures in these cleanup
|
||||
// methods will be reported as Warnings.
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executing cleanup methods.");
|
||||
// var cleanupResult = testRunner.RunCleanup();
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Executed cleanup methods.");
|
||||
//if (cleanupResult != null)
|
||||
//{
|
||||
// IList<string> warnings = cleanupResult.Warnings;
|
||||
|
||||
// // Do not attach the standard output and error messages to any test result. It is not
|
||||
// // guaranteed that a test method of same class would have run last. We will end up
|
||||
// // adding stdout to test method of another class.
|
||||
// this.LogWarnings(testExecutionRecorder, warnings);
|
||||
//}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle errors in user specified run parameters")]
|
||||
private void CacheSessionParameters(
|
||||
IRunContext runContext,
|
||||
ITestExecutionRecorder testExecutionRecorder)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(runContext?.RunSettings?.SettingsXml))
|
||||
{
|
||||
try
|
||||
{
|
||||
//var testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml);
|
||||
//if (testRunParameters != null)
|
||||
//{
|
||||
// // Clear sessionParameters to prevent key collisions of test run parameters in case
|
||||
// // "Keep Test Execution Engine Alive" is selected in VS.
|
||||
// this.sessionParameters.Clear();
|
||||
// foreach (var kvp in testRunParameters)
|
||||
// {
|
||||
// this.sessionParameters.Add(kvp);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
testExecutionRecorder.SendMessage(TestMessageLevel.Error, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log the parameter warnings on the parameter logger
|
||||
/// </summary>
|
||||
/// <param name="testExecutionRecorder">Handle to record test start/end/results/messages.</param>
|
||||
/// <param name="warnings">Any warnings during run operation.</param>
|
||||
private void LogWarnings(
|
||||
ITestExecutionRecorder testExecutionRecorder,
|
||||
IEnumerable<string> warnings)
|
||||
{
|
||||
if (warnings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(testExecutionRecorder != null, "Logger should not be null");
|
||||
|
||||
// log the warnings
|
||||
foreach (string warning in warnings)
|
||||
{
|
||||
testExecutionRecorder.SendMessage(TestMessageLevel.Warning, warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,712 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the TestMethod Info object
|
||||
/// </summary>
|
||||
public class TestMethodInfo : TestAdapter.ITestMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the timeout when it is not set in a test case
|
||||
/// </summary>
|
||||
public const int TimeoutWhenNotSet = 0;
|
||||
|
||||
private object[] arguments;
|
||||
|
||||
internal TestMethodInfo(
|
||||
MethodInfo testMethod,
|
||||
TestClassInfo parent,
|
||||
TestMethodOptions testmethodOptions)
|
||||
{
|
||||
Debug.Assert(testMethod != null, "TestMethod should not be null");
|
||||
Debug.Assert(parent != null, "Parent should not be null");
|
||||
|
||||
this.TestMethod = testMethod;
|
||||
this.Parent = parent;
|
||||
this.TestMethodOptions = testmethodOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether timeout is set.
|
||||
/// </summary>
|
||||
public bool IsTimeoutSet => this.TestMethodOptions.Timeout != TimeoutWhenNotSet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason why the test is not runnable
|
||||
/// </summary>
|
||||
public string NotRunnableReason { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether test is runnnable
|
||||
/// </summary>
|
||||
public bool IsRunnable => string.IsNullOrEmpty(this.NotRunnableReason);
|
||||
|
||||
public ParameterInfo[] ParameterTypes => this.TestMethod.GetParameters();
|
||||
|
||||
public Type ReturnType => this.TestMethod.ReturnType;
|
||||
|
||||
public string TestClassName => this.Parent.ClassType.FullName;
|
||||
|
||||
public string TestMethodName => this.TestMethod.Name;
|
||||
|
||||
public MethodInfo MethodInfo => this.TestMethod;
|
||||
|
||||
public object[] Arguments => this.arguments;
|
||||
|
||||
/// <summary>
|
||||
/// Gets testMethod referred by this object
|
||||
/// </summary>
|
||||
internal MethodInfo TestMethod { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent class Info object
|
||||
/// </summary>
|
||||
internal TestClassInfo Parent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options for the test method in this environment.
|
||||
/// </summary>
|
||||
internal TestMethodOptions TestMethodOptions { get; private set; }
|
||||
|
||||
public Attribute[] GetAllAttributes(bool inherit)
|
||||
{
|
||||
return ReflectHelper.GetCustomAttributes(this.TestMethod, inherit) as Attribute[];
|
||||
}
|
||||
|
||||
public TAttributeType[] GetAttributes<TAttributeType>(bool inherit)
|
||||
where TAttributeType : Attribute
|
||||
{
|
||||
Attribute[] attributeArray = ReflectHelper.GetCustomAttributes(this.TestMethod, typeof(TAttributeType), inherit);
|
||||
|
||||
TAttributeType[] tAttributeArray = attributeArray as TAttributeType[];
|
||||
if (tAttributeArray != null)
|
||||
{
|
||||
return tAttributeArray;
|
||||
}
|
||||
|
||||
List<TAttributeType> tAttributeList = new List<TAttributeType>();
|
||||
if (attributeArray != null)
|
||||
{
|
||||
foreach (Attribute attribute in attributeArray)
|
||||
{
|
||||
TAttributeType tAttribute = attribute as TAttributeType;
|
||||
if (tAttribute != null)
|
||||
{
|
||||
tAttributeList.Add(tAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tAttributeList.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute test method. Capture failures, handle async and return result.
|
||||
/// </summary>
|
||||
/// <param name="arguments">
|
||||
/// Arguments to pass to test method. (E.g. For data driven)
|
||||
/// </param>
|
||||
/// <returns>Result of test method invocation.</returns>
|
||||
public virtual TestResult Invoke(object[] arguments)
|
||||
{
|
||||
Stopwatch watch = new Stopwatch();
|
||||
TestResult result = null;
|
||||
|
||||
// check if arguments are set for data driven tests
|
||||
if (arguments == null)
|
||||
{
|
||||
arguments = this.Arguments;
|
||||
}
|
||||
|
||||
using (LogMessageListener listener = new LogMessageListener(this.TestMethodOptions.CaptureDebugTraces))
|
||||
{
|
||||
watch.Start();
|
||||
try
|
||||
{
|
||||
//if (this.IsTimeoutSet)
|
||||
//{
|
||||
// result = this.ExecuteInternalWithTimeout(arguments);
|
||||
//}
|
||||
//else
|
||||
{
|
||||
result = this.ExecuteInternal(arguments);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Handle logs & debug traces.
|
||||
watch.Stop();
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
result.Duration = watch.Elapsed;
|
||||
result.DebugTrace = listener.DebugTrace;
|
||||
result.LogOutput = listener.StandardOutput;
|
||||
result.LogError = listener.StandardError;
|
||||
result.TestContextMessages = this.TestMethodOptions.TestContext.GetAndClearDiagnosticMessages();
|
||||
result.ResultFiles = this.TestMethodOptions.TestContext.GetResultFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void SetArguments(object[] arguments)
|
||||
{
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute test without timeout.
|
||||
/// </summary>
|
||||
/// <param name="arguments">Arguments to be passed to the method.</param>
|
||||
/// <returns>The result of the execution.</returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private TestResult ExecuteInternal(object[] arguments)
|
||||
{
|
||||
Debug.Assert(this.TestMethod != null, "UnitTestExecuter.DefaultTestMethodInvoke: testMethod = null.");
|
||||
|
||||
var result = new TestResult();
|
||||
|
||||
// TODO remove dry violation with TestMethodRunner
|
||||
var classInstance = this.CreateTestClassInstance(result);
|
||||
var testContextSetup = false;
|
||||
bool isExceptionThrown = false;
|
||||
bool hasTestInitializePassed = false;
|
||||
Exception testRunnerException = null;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (classInstance != null && this.SetTestContext(classInstance, result))
|
||||
{
|
||||
// For any failure after this point, we must run TestCleanup
|
||||
testContextSetup = true;
|
||||
|
||||
if (this.RunTestInitializeMethod(classInstance, result))
|
||||
{
|
||||
hasTestInitializePassed = true;
|
||||
// TODO
|
||||
//PlatformServiceProvider.Instance.ThreadOperations.ExecuteWithAbortSafety(
|
||||
// () => this.TestMethod.InvokeAsSynchronousTask(classInstance, arguments));
|
||||
result.Outcome = UnitTestOutcome.Passed;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
isExceptionThrown = true;
|
||||
|
||||
if (this.IsExpectedException(ex, result))
|
||||
{
|
||||
// Expected Exception was thrown, so Pass the test
|
||||
result.Outcome = UnitTestOutcome.Passed;
|
||||
}
|
||||
else if (result.TestFailureException == null)
|
||||
{
|
||||
// This block should not throw. If it needs to throw, then handling of
|
||||
// ThreadAbortException will need to be revisited. See comment in RunTestMethod.
|
||||
result.TestFailureException = this.HandleMethodException(
|
||||
ex,
|
||||
this.TestClassName,
|
||||
this.TestMethodName);
|
||||
}
|
||||
|
||||
if (result.Outcome != UnitTestOutcome.Passed)
|
||||
{
|
||||
if (ex is AssertInconclusiveException || ex.InnerException is AssertInconclusiveException)
|
||||
{
|
||||
result.Outcome = UnitTestOutcome.Inconclusive;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Outcome = UnitTestOutcome.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we get here, the test method did not throw the exception
|
||||
// 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 && this.TestMethodOptions.ExpectedException != null)
|
||||
{
|
||||
result.TestFailureException = new TestFailedException(
|
||||
UnitTestOutcome.Failed,
|
||||
this.TestMethodOptions.ExpectedException.NoExceptionMessage);
|
||||
result.Outcome = UnitTestOutcome.Failed;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
testRunnerException = exception;
|
||||
}
|
||||
|
||||
// Set the current tests outcome before cleanup so it can be used in the cleanup logic.
|
||||
this.TestMethodOptions.TestContext.SetOutcome(result.Outcome);
|
||||
|
||||
// TestCleanup can potentially be a long running operation which should'nt ideally be in a finally block.
|
||||
// Pulling it out so extension writers can abort custom cleanups if need be. Having this in a finally block
|
||||
// does not allow a threadabort 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.
|
||||
if (classInstance != null && testContextSetup)
|
||||
{
|
||||
this.RunTestCleanupMethod(classInstance, result);
|
||||
}
|
||||
|
||||
if (testRunnerException != null)
|
||||
{
|
||||
throw testRunnerException;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private bool IsExpectedException(Exception ex, TestResult result)
|
||||
{
|
||||
Exception realException = this.GetRealException(ex);
|
||||
|
||||
// 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 (this.TestMethodOptions.ExpectedException != null)
|
||||
{
|
||||
Exception exceptionFromVerify;
|
||||
try
|
||||
{
|
||||
// If the expected exception attribute's Verify method returns, then it
|
||||
// considers this exception as expected, so the test passed
|
||||
this.TestMethodOptions.ExpectedException.Verify(realException);
|
||||
return true;
|
||||
}
|
||||
catch (Exception verifyEx)
|
||||
{
|
||||
var isTargetInvocationError = verifyEx is TargetInvocationException;
|
||||
if (isTargetInvocationError && verifyEx.InnerException != null)
|
||||
{
|
||||
exceptionFromVerify = verifyEx.InnerException;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Verify threw an exception, so the expected exception attribute does not
|
||||
// consider this exception to be expected. Include the exception message in
|
||||
// the test result.
|
||||
exceptionFromVerify = verifyEx;
|
||||
}
|
||||
}
|
||||
|
||||
// See if the verification exception (thrown by the expected exception
|
||||
// attribute's Verify method) is an AssertInconclusiveException. If so, set
|
||||
// the test outcome to Inconclusive.
|
||||
result.TestFailureException = new TestFailedException(
|
||||
exceptionFromVerify is AssertInconclusiveException ? UnitTestOutcome.Inconclusive : UnitTestOutcome.Failed,
|
||||
exceptionFromVerify.TryGetMessage(),
|
||||
realException.TryGetStackTraceInformation());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Exception GetRealException(Exception ex)
|
||||
{
|
||||
if (ex is TargetInvocationException)
|
||||
{
|
||||
Debug.Assert(ex.InnerException != null, "Inner exception of TargetInvocationException is null. This should occur because we should have caught this case above.");
|
||||
|
||||
// Our reflected call will typically always get back a TargetInvocationException
|
||||
// containing the real exception thrown by the test method as its inner exception
|
||||
return ex.InnerException;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the exception that is thrown by a test method. The exception can either
|
||||
/// be expected or not expected
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception that was thrown</param>
|
||||
/// <param name="className">The class name.</param>
|
||||
/// <param name="methodName">The method name.</param>
|
||||
/// <returns>Test framework exception with details.</returns>
|
||||
private Exception HandleMethodException(Exception ex, string className, string methodName)
|
||||
{
|
||||
Debug.Assert(ex != null, "exception should not be null.");
|
||||
|
||||
var isTargetInvocationException = ex is TargetInvocationException;
|
||||
if (isTargetInvocationException && ex.InnerException == null)
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_FailedToGetTestMethodException, className, methodName);
|
||||
return new TestFailedException(UnitTestOutcome.Error, errorMessage);
|
||||
}
|
||||
|
||||
// Get the real exception thrown by the test method
|
||||
Exception realException = this.GetRealException(ex);
|
||||
string exceptionMessage = null;
|
||||
StackTraceInformation exceptionStackTraceInfo = null;
|
||||
var outcome = UnitTestOutcome.Failed;
|
||||
|
||||
if (realException.TryGetUnitTestAssertException(out outcome, out exceptionMessage, out exceptionStackTraceInfo))
|
||||
{
|
||||
return new TestFailedException(outcome.ToUnitTestOutcome(), exceptionMessage, exceptionStackTraceInfo, realException);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage;
|
||||
|
||||
// Handle special case of UI objects in TestMethod to suggest UITestMethod
|
||||
if (realException.HResult == -2147417842)
|
||||
{
|
||||
errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_WrongThread,
|
||||
string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestMethodThrows, className, methodName, StackTraceHelper.GetExceptionMessage(realException)));
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_TestMethodThrows,
|
||||
className,
|
||||
methodName,
|
||||
StackTraceHelper.GetExceptionMessage(realException));
|
||||
}
|
||||
|
||||
StackTraceInformation stackTrace = null;
|
||||
|
||||
// For ThreadAbortException (that can be thrown only by aborting a thread as there's no public constructor)
|
||||
// there's no inner exception and exception itself contains reflection-related stack trace
|
||||
// (_RuntimeMethodHandle.InvokeMethodFast <- _RuntimeMethodHandle.Invoke <- UnitTestExecuter.RunTestMethod)
|
||||
// which has no meaningful info for the user. Thus, we do not show call stack for ThreadAbortException.
|
||||
if (realException.GetType().Name != "ThreadAbortException")
|
||||
{
|
||||
stackTrace = StackTraceHelper.GetStackTraceInformation(realException);
|
||||
}
|
||||
|
||||
return new TestFailedException(UnitTestOutcome.Failed, errorMessage, stackTrace, realException);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs TestCleanup methods of parent TestClass and base classes.
|
||||
/// </summary>
|
||||
/// <param name="classInstance">Instance of TestClass.</param>
|
||||
/// <param name="result">Instance of TestResult.</param>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private void RunTestCleanupMethod(object classInstance, TestResult result)
|
||||
{
|
||||
Debug.Assert(classInstance != null, "classInstance != null");
|
||||
Debug.Assert(result != null, "result != null");
|
||||
|
||||
var testCleanupMethod = this.Parent.TestCleanupMethod;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// Test cleanups are called in the order of discovery
|
||||
// Current TestClass -> Parent -> Grandparent
|
||||
testCleanupMethod?.InvokeAsSynchronousTask(classInstance, null);
|
||||
var baseTestCleanupQueue = new Queue<MethodInfo>(this.Parent.BaseTestCleanupMethodsQueue);
|
||||
while (baseTestCleanupQueue.Count > 0)
|
||||
{
|
||||
testCleanupMethod = baseTestCleanupQueue.Dequeue();
|
||||
testCleanupMethod?.InvokeAsSynchronousTask(classInstance, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
(classInstance as IDisposable)?.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var cleanupOutcome = UnitTestOutcome.Failed;
|
||||
var cleanupError = new StringBuilder();
|
||||
var cleanupStackTrace = new StringBuilder();
|
||||
StackTraceInformation cleanupStackTraceInfo = null;
|
||||
|
||||
TestFailedException testFailureException = result.TestFailureException as TestFailedException;
|
||||
testFailureException.TryGetTestFailureExceptionMessageAndStackTrace(cleanupError, cleanupStackTrace);
|
||||
|
||||
if (cleanupStackTrace.Length > 0)
|
||||
{
|
||||
cleanupStackTrace.Append(Resource.UTA_CleanupStackTrace);
|
||||
cleanupStackTrace.Append(Environment.NewLine);
|
||||
}
|
||||
|
||||
Exception realException = ex.GetInnerExceptionOrDefault();
|
||||
string exceptionMessage = null;
|
||||
StackTraceInformation realExceptionStackTraceInfo = null;
|
||||
|
||||
// special case UnitTestAssertException to trim off part of the stack trace
|
||||
if (!realException.TryGetUnitTestAssertException(out cleanupOutcome, out exceptionMessage, out realExceptionStackTraceInfo))
|
||||
{
|
||||
cleanupOutcome = UnitTestOutcome.Failed;
|
||||
exceptionMessage = this.GetTestCleanUpExceptionMessage(testCleanupMethod, realException);
|
||||
realExceptionStackTraceInfo = realException.TryGetStackTraceInformation();
|
||||
}
|
||||
|
||||
cleanupError.Append(exceptionMessage);
|
||||
if (realExceptionStackTraceInfo != null)
|
||||
{
|
||||
cleanupStackTrace.Append(realExceptionStackTraceInfo.ErrorStackTrace);
|
||||
cleanupStackTraceInfo = cleanupStackTraceInfo ?? realExceptionStackTraceInfo;
|
||||
}
|
||||
|
||||
UnitTestOutcome outcome = testFailureException == null ? cleanupOutcome : cleanupOutcome.GetMoreImportantOutcome(result.Outcome);
|
||||
StackTraceInformation finalStackTraceInfo = cleanupStackTraceInfo != null ?
|
||||
new StackTraceInformation(
|
||||
cleanupStackTrace.ToString(),
|
||||
cleanupStackTraceInfo.ErrorFilePath,
|
||||
cleanupStackTraceInfo.ErrorLineNumber,
|
||||
cleanupStackTraceInfo.ErrorColumnNumber) :
|
||||
new StackTraceInformation(cleanupStackTrace.ToString());
|
||||
|
||||
result.Outcome = outcome;
|
||||
result.TestFailureException = new TestFailedException(outcome.ToUnitTestOutcome(), cleanupError.ToString(), finalStackTraceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTestCleanUpExceptionMessage(MethodInfo testCleanupMethod, Exception exception)
|
||||
{
|
||||
if (testCleanupMethod != null)
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_CleanupMethodThrows,
|
||||
this.TestClassName,
|
||||
testCleanupMethod?.Name,
|
||||
exception.GetType().ToString(),
|
||||
StackTraceHelper.GetExceptionMessage(exception));
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_CleanupMethodThrowsGeneralError,
|
||||
this.TestClassName,
|
||||
StackTraceHelper.GetExceptionMessage(exception));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs TestInitialize methods of parent TestClass and the base classes.
|
||||
/// </summary>
|
||||
/// <param name="classInstance">Instance of TestClass.</param>
|
||||
/// <param name="result">Instance of TestResult.</param>
|
||||
/// <returns>True if the TestInitialize method(s) did not throw an exception.</returns>
|
||||
[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)
|
||||
{
|
||||
Debug.Assert(classInstance != null, "classInstance != null");
|
||||
Debug.Assert(result != null, "result != null");
|
||||
|
||||
MethodInfo testInitializeMethod = null;
|
||||
try
|
||||
{
|
||||
// TestInitialize methods for base classes are called in reverse order of discovery
|
||||
// Grandparent -> Parent -> Child TestClass
|
||||
var baseTestInitializeStack = new Stack<MethodInfo>(this.Parent.BaseTestInitializeMethodsQueue);
|
||||
while (baseTestInitializeStack.Count > 0)
|
||||
{
|
||||
testInitializeMethod = baseTestInitializeStack.Pop();
|
||||
testInitializeMethod?.InvokeAsSynchronousTask(classInstance, null);
|
||||
}
|
||||
|
||||
testInitializeMethod = this.Parent.TestInitializeMethod;
|
||||
testInitializeMethod?.InvokeAsSynchronousTask(classInstance, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var innerException = ex.GetInnerExceptionOrDefault();
|
||||
string exceptionMessage = null;
|
||||
StackTraceInformation exceptionStackTraceInfo = null;
|
||||
var outcome = UnitTestOutcome.Failed;
|
||||
|
||||
if (innerException.TryGetUnitTestAssertException(out outcome, out exceptionMessage, out exceptionStackTraceInfo))
|
||||
{
|
||||
result.Outcome = outcome;
|
||||
result.TestFailureException = new TestFailedException(
|
||||
UnitTestOutcome.Failed,
|
||||
exceptionMessage,
|
||||
exceptionStackTraceInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stackTrace = StackTraceHelper.GetStackTraceInformation(innerException);
|
||||
var errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_InitMethodThrows,
|
||||
this.TestClassName,
|
||||
testInitializeMethod?.Name,
|
||||
StackTraceHelper.GetExceptionMessage(innerException));
|
||||
|
||||
result.Outcome = UnitTestOutcome.Failed;
|
||||
result.TestFailureException = new TestFailedException(UnitTestOutcome.Failed, errorMessage, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="TestContext"/> on <see cref="classInstance"/>.
|
||||
/// </summary>
|
||||
/// <param name="classInstance">
|
||||
/// Reference to instance of TestClass.
|
||||
/// </param>
|
||||
/// <param name="result">
|
||||
/// Reference to instance of <see cref="TestResult"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if there no exceptions during set context operation.
|
||||
/// </returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private bool SetTestContext(object classInstance, TestResult result)
|
||||
{
|
||||
Debug.Assert(classInstance != null, "classInstance != null");
|
||||
Debug.Assert(result != null, "result != null");
|
||||
|
||||
try
|
||||
{
|
||||
if (this.Parent.TestContextProperty != null && this.Parent.TestContextProperty.CanWrite)
|
||||
{
|
||||
this.Parent.TestContextProperty.SetValue(classInstance, this.TestMethodOptions.TestContext);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var stackTraceInfo = StackTraceHelper.GetStackTraceInformation(ex.GetInnerExceptionOrDefault());
|
||||
var errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_TestContextSetError,
|
||||
this.TestClassName,
|
||||
StackTraceHelper.GetExceptionMessage(ex.GetInnerExceptionOrDefault()));
|
||||
|
||||
result.Outcome = UnitTestOutcome.Failed;
|
||||
result.TestFailureException = new TestFailedException(UnitTestOutcome.Failed, errorMessage, stackTraceInfo);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of TestClass. The TestMethod is invoked on this instance.
|
||||
/// </summary>
|
||||
/// <param name="result">
|
||||
/// Reference to the <see cref="TestResult"/> for this TestMethod.
|
||||
/// Outcome and TestFailureException are updated based on instance creation.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// An instance of the TestClass. Returns null if there are errors during class instantiation.
|
||||
/// </returns>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private object CreateTestClassInstance(TestResult result)
|
||||
{
|
||||
object classInstance = null;
|
||||
try
|
||||
{
|
||||
classInstance = this.Parent.Constructor.Invoke(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// In most cases, exception will be TargetInvocationException with real exception wrapped
|
||||
// in the InnerException; or user code throws an exception
|
||||
var actualException = ex.InnerException ?? ex;
|
||||
var exceptionMessage = StackTraceHelper.GetExceptionMessage(actualException);
|
||||
var stackTraceInfo = StackTraceHelper.GetStackTraceInformation(actualException);
|
||||
var errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_InstanceCreationError,
|
||||
this.TestClassName,
|
||||
exceptionMessage);
|
||||
|
||||
result.Outcome = UnitTestOutcome.Failed;
|
||||
result.TestFailureException = new TestFailedException(UnitTestOutcome.Failed, errorMessage, stackTraceInfo);
|
||||
}
|
||||
|
||||
return classInstance;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Execute test with a timeout
|
||||
///// </summary>
|
||||
///// <param name="arguments">The arguments to be passed.</param>
|
||||
///// <returns>The result of execution.</returns>
|
||||
//[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
//private TestResult ExecuteInternalWithTimeout(object[] arguments)
|
||||
//{
|
||||
// Debug.Assert(this.IsTimeoutSet, "Timeout should be set");
|
||||
|
||||
// TestResult result = null;
|
||||
// Exception failure = null;
|
||||
|
||||
// Action executeAsyncAction = () =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// result = this.ExecuteInternal(arguments);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// failure = ex;
|
||||
// }
|
||||
// };
|
||||
|
||||
// if (PlatformServiceProvider.Instance.ThreadOperations.Execute(executeAsyncAction, this.TestMethodOptions.Timeout))
|
||||
// {
|
||||
// if (failure != null)
|
||||
// {
|
||||
// throw failure;
|
||||
// }
|
||||
|
||||
// Debug.Assert(result != null, "no timeout, no failure result should not be null");
|
||||
// return result;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Timed out
|
||||
|
||||
// // If the method times out, then
|
||||
// //
|
||||
// // 1. If the test is stuck, then we can get CannotUnloadAppDomain exception.
|
||||
// //
|
||||
// // Which are handled as follows: -
|
||||
// //
|
||||
// // For #1, we are now restarting the execution process if adapter fails to unload app-domain.
|
||||
// string errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.Execution_Test_Timeout, this.TestMethodName);
|
||||
// MSTest.TestResult timeoutResult = new nanoFramework.TestPlatform.MSTest.TestResult() { Outcome = nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestOutcome.Timeout, TestFailureException = new TestFailedException(UnitTestOutcome.Timeout, errorMessage) };
|
||||
// return timeoutResult;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
|
||||
/// <summary>
|
||||
/// Cancellation token supporting cancellation of a test run.
|
||||
/// </summary>
|
||||
public class TestRunCancellationToken
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores whether the test run is cancelled or not.
|
||||
/// </summary>
|
||||
private bool cancelled;
|
||||
|
||||
/// <summary>
|
||||
/// Callback to be invoked when cancelled.
|
||||
/// </summary>
|
||||
private Action registeredCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the test run is cancelled.
|
||||
/// </summary>
|
||||
public bool Canceled
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
this.cancelled = value;
|
||||
if (this.cancelled)
|
||||
{
|
||||
this.registeredCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the execution of a test run.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
this.Canceled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback method to be invoked when cancelled.
|
||||
/// </summary>
|
||||
/// <param name="callback">Callback delegate for handling cancellation.</param>
|
||||
public void Register(Action callback)
|
||||
{
|
||||
ValidateArg.NotNull(callback, "callback");
|
||||
|
||||
Debug.Assert(this.registeredCallback == null, "Callback delegate is already registered, use a new cancellationToken");
|
||||
|
||||
this.registeredCallback = callback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister the callback method.
|
||||
/// </summary>
|
||||
public void Unregister()
|
||||
{
|
||||
this.registeredCallback = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// StringWriter which has thread safe ToString().
|
||||
/// </summary>
|
||||
internal class ThreadSafeStringWriter : StringWriter
|
||||
{
|
||||
private readonly object lockObject = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ThreadSafeStringWriter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="formatProvider">
|
||||
/// The format provider.
|
||||
/// </param>
|
||||
public ThreadSafeStringWriter(IFormatProvider formatProvider)
|
||||
: base(formatProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.ToString();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return default(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(char value)
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
InvokeBaseClass(() => base.Write(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(string value)
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
InvokeBaseClass(() => base.Write(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(char[] buffer, int index, int count)
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
InvokeBaseClass(() => base.Write(buffer, index, count));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
lock (this.lockObject)
|
||||
{
|
||||
InvokeBaseClass(() => base.Dispose(disposing));
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvokeBaseClass(Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,735 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Discovery;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Interface;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Defines type cache which reflects upon a type and cache its test artifacts
|
||||
/// </summary>
|
||||
internal class TypeCache : MarshalByRefObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Test context property name
|
||||
/// </summary>
|
||||
private const string TestContextPropertyName = "TestContext";
|
||||
|
||||
/// <summary>
|
||||
/// Predefined test Attribute names.
|
||||
/// </summary>
|
||||
private static readonly string[] PredefinedNames = new string[] { "Priority", "TestCategory", "Owner" };
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reflection API's.
|
||||
/// </summary>
|
||||
private ReflectHelper reflectionHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Assembly info cache
|
||||
/// </summary>
|
||||
private Dictionary<Assembly, TestAssemblyInfo> testAssemblyInfoCache;
|
||||
|
||||
/// <summary>
|
||||
/// ClassInfo cache
|
||||
/// </summary>
|
||||
private Dictionary<string, TestClassInfo> classInfoCache;
|
||||
|
||||
private object assemblyInfoSyncObject;
|
||||
private object classInfoSyncObject;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeCache"/> class.
|
||||
/// </summary>
|
||||
internal TypeCache()
|
||||
: this(new ReflectHelper())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeCache"/> class.
|
||||
/// </summary>
|
||||
/// <param name="reflectionHelper"> An instance to the <see cref="ReflectHelper"/> object. </param>
|
||||
internal TypeCache(ReflectHelper reflectionHelper)
|
||||
{
|
||||
this.reflectionHelper = reflectionHelper;
|
||||
this.testAssemblyInfoCache = new Dictionary<Assembly, TestAssemblyInfo>();
|
||||
this.classInfoCache = new Dictionary<string, TestClassInfo>(StringComparer.Ordinal);
|
||||
this.assemblyInfoSyncObject = new object();
|
||||
this.classInfoSyncObject = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Class Info cache which has cleanup methods to execute
|
||||
/// </summary>
|
||||
public IEnumerable<TestClassInfo> ClassInfoListWithExecutableCleanupMethods
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.classInfoCache.Values.Where(classInfo => classInfo.HasExecutableCleanupMethod).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Assembly Info cache which has cleanup methods to execute
|
||||
/// </summary>
|
||||
public IEnumerable<TestAssemblyInfo> AssemblyInfoListWithExecutableCleanupMethods
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.testAssemblyInfoCache.Values.Where(assemblyInfo => assemblyInfo.HasExecutableCleanupMethod).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of cached assembly info values.
|
||||
/// </summary>
|
||||
public IEnumerable<TestAssemblyInfo> AssemblyInfoCache
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.testAssemblyInfoCache.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of cached class info values.
|
||||
/// </summary>
|
||||
public IEnumerable<TestClassInfo> ClassInfoCache
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.classInfoCache.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the test method info corresponding to the parameter test Element
|
||||
/// </summary>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <param name="testContext"> The test Context. </param>
|
||||
/// <param name="captureDebugTraces"> Indicates whether the test method should capture debug traces.</param>
|
||||
/// <returns> The <see cref="TestMethodInfo"/>. </returns>
|
||||
public TestMethodInfo GetTestMethodInfo(TestMethod testMethod, ITestContext testContext, bool captureDebugTraces)
|
||||
{
|
||||
if (testMethod == null)
|
||||
{
|
||||
throw new ArgumentNullException("testMethod");
|
||||
}
|
||||
|
||||
if (testContext == null)
|
||||
{
|
||||
throw new ArgumentNullException("testContext");
|
||||
}
|
||||
|
||||
// Get the classInfo (This may throw as GetType calls assembly.GetType(..,true);)
|
||||
var testClassInfo = this.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 this.ResolveTestMethod(testMethod, testClassInfo, testContext, captureDebugTraces);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="object"/>.
|
||||
/// </returns>
|
||||
[SecurityCritical]
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#region ClassInfo creation and cache logic.
|
||||
|
||||
/// <summary>
|
||||
/// Gets the classInfo corresponding to the unit test.
|
||||
/// </summary>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <returns> The <see cref="TestClassInfo"/>. </returns>
|
||||
private TestClassInfo GetClassInfo(TestMethod testMethod)
|
||||
{
|
||||
Debug.Assert(testMethod != null, "test method is null");
|
||||
|
||||
var typeName = testMethod.FullClassName;
|
||||
|
||||
TestClassInfo classInfo;
|
||||
|
||||
if (!this.classInfoCache.TryGetValue(typeName, out classInfo))
|
||||
{
|
||||
// Aquiring a lock is usually a costly operation which does not need to be
|
||||
// performed every time if the type is found in the cache.
|
||||
lock (this.classInfoSyncObject)
|
||||
{
|
||||
// Perform a check again.
|
||||
if (!this.classInfoCache.TryGetValue(typeName, out classInfo))
|
||||
{
|
||||
// Load the class type
|
||||
Type type = this.LoadType(typeName, testMethod.AssemblyName);
|
||||
|
||||
if (type == 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 classInfo
|
||||
classInfo = this.CreateClassInfo(type, testMethod);
|
||||
|
||||
// Use the full type name for the cache.
|
||||
this.classInfoCache.Add(typeName, classInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return classInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the parameter type from the parameter assembly
|
||||
/// </summary>
|
||||
/// <param name="typeName"> The type Name. </param>
|
||||
/// <param name="assemblyName"> The assembly Name. </param>
|
||||
/// <returns> The <see cref="Type"/>. </returns>
|
||||
/// <exception cref="TypeInspectionException"> Thrown when there is a type load exception from the assembly. </exception>
|
||||
private Type LoadType(string typeName, string assemblyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Attempt to load the assembly using the full type name (includes assembly)
|
||||
// This call will load the assembly from the first location it is
|
||||
// found in (i.e. GAC, current directory, path)
|
||||
// If this fails, we will try to load the type from the assembly
|
||||
// location in the Out directory.
|
||||
var t = Type.GetType(typeName);
|
||||
|
||||
if (t == null)
|
||||
{
|
||||
var assembly = PlatformServiceProvider.Instance.FileOperations.LoadAssembly(assemblyName, isReflectionOnly: false);
|
||||
|
||||
// Attempt to load the type from the test assembly.
|
||||
// Allow this call to throw if the type can't be loaded.
|
||||
t = assembly.GetType(typeName);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
catch (TypeLoadException)
|
||||
{
|
||||
// This means the class containing the test method could not be found.
|
||||
// Return null so we return a not found result.
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TypeLoadError, typeName, ex);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the class Info
|
||||
/// </summary>
|
||||
/// <param name="classType"> The class Type. </param>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <returns> The <see cref="TestClassInfo"/>. </returns>
|
||||
private TestClassInfo CreateClassInfo(Type classType, TestMethod testMethod)
|
||||
{
|
||||
var constructors = classType.GetTypeInfo().DeclaredConstructors;
|
||||
var constructor = constructors.FirstOrDefault(ctor => ctor.GetParameters().Length == 0 && ctor.IsPublic);
|
||||
|
||||
if (constructor == null)
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_NoDefaultConstructor, testMethod.FullClassName);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
var testContextProperty = this.ResolveTestContext(classType);
|
||||
|
||||
var assemblyInfo = this.GetAssemblyInfo(classType);
|
||||
|
||||
var classInfo = new TestClassInfo(classType, constructor, testContextProperty, this.reflectionHelper.GetDerivedAttribute<TestClassAttribute>(classType, false), assemblyInfo);
|
||||
|
||||
var testInitializeAttributeType = typeof(TestInitializeAttribute);
|
||||
var testCleanupAttributeType = typeof(TestCleanupAttribute);
|
||||
var classInitializeAttributeType = typeof(ClassInitializeAttribute);
|
||||
var classCleanupAttributeType = typeof(ClassCleanupAttribute);
|
||||
|
||||
// List of instance methods present in the type as well its base type
|
||||
// which is used to decide whether TestInitialize/TestCleanup methods
|
||||
// present in the base type should be used or not. They are not used if
|
||||
// the method is overridden in the derived type.
|
||||
var instanceMethods = new Dictionary<string, string>();
|
||||
|
||||
foreach (var methodInfo in classType.GetTypeInfo().DeclaredMethods)
|
||||
{
|
||||
// Update test initialize/cleanup method
|
||||
this.UpdateInfoIfTestInitializeOrCleanupMethod(classInfo, methodInfo, isBase: false, instanceMethods: instanceMethods, testInitializeAttributeType: testInitializeAttributeType, testCleanupAttributeType: testCleanupAttributeType);
|
||||
|
||||
if (this.IsAssemblyOrClassInitializeMethod(methodInfo, classInitializeAttributeType))
|
||||
{
|
||||
// update class initialize method
|
||||
classInfo.ClassInitializeMethod = methodInfo;
|
||||
}
|
||||
else if (this.IsAssemblyOrClassCleanupMethod(methodInfo, classCleanupAttributeType))
|
||||
{
|
||||
// update class cleanup method
|
||||
classInfo.ClassCleanupMethod = methodInfo;
|
||||
}
|
||||
}
|
||||
|
||||
var baseType = classType.GetTypeInfo().BaseType;
|
||||
while (baseType != null)
|
||||
{
|
||||
foreach (var methodInfo in baseType.GetTypeInfo().DeclaredMethods)
|
||||
{
|
||||
if (methodInfo.IsPublic && !methodInfo.IsStatic)
|
||||
{
|
||||
// Update test initialize/cleanup method from base type.
|
||||
this.UpdateInfoIfTestInitializeOrCleanupMethod(classInfo, methodInfo, true, instanceMethods, testInitializeAttributeType, testCleanupAttributeType);
|
||||
}
|
||||
}
|
||||
|
||||
baseType = baseType.GetTypeInfo().BaseType;
|
||||
}
|
||||
|
||||
return classInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the test context property
|
||||
/// </summary>
|
||||
/// <param name="classType"> The class Type. </param>
|
||||
/// <returns> The <see cref="PropertyInfo"/> for TestContext property. Null if not defined. </returns>
|
||||
private PropertyInfo ResolveTestContext(Type classType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var testContextProperty = classType.GetRuntimeProperty(TestContextPropertyName);
|
||||
if (testContextProperty == null)
|
||||
{
|
||||
// that's okay may be the property was not defined
|
||||
return null;
|
||||
}
|
||||
|
||||
// check if testContextProperty is of correct type
|
||||
if (!testContextProperty.PropertyType.FullName.Equals(typeof(TestContext).FullName, StringComparison.Ordinal))
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestContextTypeMismatchLoadError, classType.FullName);
|
||||
throw new TypeInspectionException(errorMessage);
|
||||
}
|
||||
|
||||
return testContextProperty;
|
||||
}
|
||||
catch (AmbiguousMatchException ex)
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestContextLoadError, classType.FullName, ex.Message);
|
||||
throw new TypeInspectionException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AssemblyInfo creation and cache logic.
|
||||
|
||||
/// <summary>
|
||||
/// Get the assembly info for the parameter type
|
||||
/// </summary>
|
||||
/// <param name="type"> The type. </param>
|
||||
/// <returns> The <see cref="TestAssemblyInfo"/> instance. </returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Discoverer should continue with remaining sources.")]
|
||||
private TestAssemblyInfo GetAssemblyInfo(Type type)
|
||||
{
|
||||
var assembly = type.GetTypeInfo().Assembly;
|
||||
TestAssemblyInfo assemblyInfo;
|
||||
|
||||
if (!this.testAssemblyInfoCache.TryGetValue(assembly, out assemblyInfo))
|
||||
{
|
||||
// Aquiring a lock is usually a costly operation which does not need to be
|
||||
// performed every time if the assembly is found in the cache.
|
||||
lock (this.assemblyInfoSyncObject)
|
||||
{
|
||||
if (this.testAssemblyInfoCache.TryGetValue(assembly, out assemblyInfo))
|
||||
{
|
||||
return assemblyInfo;
|
||||
}
|
||||
|
||||
var assemblyInitializeType = typeof(AssemblyInitializeAttribute);
|
||||
var assemblyCleanupType = typeof(AssemblyCleanupAttribute);
|
||||
|
||||
assemblyInfo = new TestAssemblyInfo();
|
||||
|
||||
var types = new AssemblyEnumerator().GetTypes(assembly, assembly.FullName, null);
|
||||
|
||||
foreach (var t in types)
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Only examine test classes for test attributes
|
||||
if (!this.reflectionHelper.IsAttributeDefined(t, typeof(TestClassAttribute), inherit: true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If we fail to discover type from an assembly, then do not abort. Pick the next type.
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
"TypeCache: Exception occured while checking whether type {0} is a test class or not. {1}",
|
||||
t.FullName,
|
||||
ex);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Enumerate through all methods and identify the Assembly Init and cleanup methods.
|
||||
foreach (var methodInfo in t.GetTypeInfo().DeclaredMethods)
|
||||
{
|
||||
if (this.IsAssemblyOrClassInitializeMethod(methodInfo, assemblyInitializeType))
|
||||
{
|
||||
assemblyInfo.AssemblyInitializeMethod = methodInfo;
|
||||
}
|
||||
else if (this.IsAssemblyOrClassCleanupMethod(methodInfo, assemblyCleanupType))
|
||||
{
|
||||
assemblyInfo.AssemblyCleanupMethod = methodInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.testAssemblyInfoCache.Add(assembly, assemblyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return assemblyInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify if a given method is an Assembly or Class Initialize method.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"> The method info. </param>
|
||||
/// <param name="initializeAttributeType"> The initialization attribute type. </param>
|
||||
/// <returns> True if its an initialization method. </returns>
|
||||
private bool IsAssemblyOrClassInitializeMethod(MethodInfo methodInfo, Type initializeAttributeType)
|
||||
{
|
||||
if (!this.reflectionHelper.IsAttributeDefined(methodInfo, initializeAttributeType, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!methodInfo.HasCorrectClassOrAssemblyInitializeSignature())
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ClassOrAssemblyInitializeMethodHasWrongSignature, methodInfo.DeclaringType.FullName, methodInfo.Name);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify if a given method is an Assembly or Class cleanup method.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"> The method info. </param>
|
||||
/// <param name="cleanupAttributeType"> The cleanup attribute type. </param>
|
||||
/// <returns> True if its a cleanup method. </returns>
|
||||
private bool IsAssemblyOrClassCleanupMethod(MethodInfo methodInfo, Type cleanupAttributeType)
|
||||
{
|
||||
if (!this.reflectionHelper.IsAttributeDefined(methodInfo, cleanupAttributeType, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!methodInfo.HasCorrectClassOrAssemblyCleanupSignature())
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ClassOrAssemblyCleanupMethodHasWrongSignature, methodInfo.DeclaringType.FullName, methodInfo.Name);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Update the classInfo if the parameter method is a testInitialize/cleanup method
|
||||
/// </summary>
|
||||
/// <param name="classInfo"> The class Info. </param>
|
||||
/// <param name="methodInfo"> The method Info. </param>
|
||||
/// <param name="isBase"> If this needs to validate in base class or not. </param>
|
||||
/// <param name="instanceMethods"> The instance Methods. </param>
|
||||
/// <param name="testInitializeAttributeType"> The test Initialize Attribute Type. </param>
|
||||
/// <param name="testCleanupAttributeType"> The test Cleanup Attribute Type. </param>
|
||||
private void UpdateInfoIfTestInitializeOrCleanupMethod(
|
||||
TestClassInfo classInfo,
|
||||
MethodInfo methodInfo,
|
||||
bool isBase,
|
||||
Dictionary<string, string> instanceMethods,
|
||||
Type testInitializeAttributeType,
|
||||
Type testCleanupAttributeType)
|
||||
{
|
||||
var hasTestInitialize = this.reflectionHelper.IsAttributeDefined(methodInfo, testInitializeAttributeType, inherit: false);
|
||||
var hasTestCleanup = this.reflectionHelper.IsAttributeDefined(methodInfo, testCleanupAttributeType, inherit: false);
|
||||
|
||||
if (!hasTestCleanup && !hasTestInitialize)
|
||||
{
|
||||
if (methodInfo.HasCorrectTestInitializeOrCleanupSignature())
|
||||
{
|
||||
instanceMethods[methodInfo.Name] = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!methodInfo.HasCorrectTestInitializeOrCleanupSignature())
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_TestInitializeAndCleanupMethodHasWrongSignature, methodInfo.DeclaringType.FullName, methodInfo.Name);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
if (hasTestInitialize)
|
||||
{
|
||||
if (!isBase)
|
||||
{
|
||||
classInfo.TestInitializeMethod = methodInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!instanceMethods.ContainsKey(methodInfo.Name))
|
||||
{
|
||||
classInfo.BaseTestInitializeMethodsQueue.Enqueue(methodInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasTestCleanup)
|
||||
{
|
||||
if (!isBase)
|
||||
{
|
||||
classInfo.TestCleanupMethod = methodInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!instanceMethods.ContainsKey(methodInfo.Name))
|
||||
{
|
||||
classInfo.BaseTestCleanupMethodsQueue.Enqueue(methodInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instanceMethods[methodInfo.Name] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve the test method. The function will try to
|
||||
/// find a function that has the method name with 0 parameters. If the function
|
||||
/// cannot be found, or a function is found that returns non-void, the result is
|
||||
/// set to error.
|
||||
/// </summary>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <param name="testClassInfo"> The test Class Info. </param>
|
||||
/// <param name="testContext"> The test Context. </param>
|
||||
/// <param name="captureDebugTraces"> Indicates whether the test method should capture debug traces.</param>
|
||||
/// <returns>
|
||||
/// The TestMethodInfo for the given test method. Null if the test method could not be found.
|
||||
/// </returns>
|
||||
private TestMethodInfo ResolveTestMethod(TestMethod testMethod, TestClassInfo testClassInfo, ITestContext testContext, bool captureDebugTraces)
|
||||
{
|
||||
Debug.Assert(testMethod != null, "testMethod is Null");
|
||||
Debug.Assert(testClassInfo != null, "testClassInfo is Null");
|
||||
|
||||
var methodInfo = this.GetMethodInfoForTestMethod(testMethod, testClassInfo);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
// Means the specified test method could not be found.
|
||||
return null;
|
||||
}
|
||||
|
||||
var expectedExceptionAttribute = this.reflectionHelper.ResolveExpectedExceptionHelper(methodInfo, testMethod);
|
||||
var timeout = this.GetTestTimeout(methodInfo, testMethod);
|
||||
|
||||
var testMethodOptions = new TestMethodOptions() { Timeout = timeout, Executor = this.GetTestMethodAttribute(methodInfo, testClassInfo), ExpectedException = expectedExceptionAttribute, TestContext = testContext, CaptureDebugTraces = captureDebugTraces };
|
||||
var testMethodInfo = new TestMethodInfo(methodInfo, testClassInfo, testMethodOptions);
|
||||
|
||||
this.SetCustomProperties(testMethodInfo, testContext);
|
||||
|
||||
return testMethodInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the Test Method Extension Attribute of the TestClass.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"> The method info. </param>
|
||||
/// <param name="testClassInfo"> The test class info. </param>
|
||||
/// <returns>Test Method Attribute</returns>
|
||||
private TestMethodAttribute GetTestMethodAttribute(MethodInfo methodInfo, TestClassInfo testClassInfo)
|
||||
{
|
||||
// Get the derived TestMethod attribute from reflection
|
||||
var testMethodAttribute = this.reflectionHelper.GetDerivedAttribute<TestMethodAttribute>(methodInfo, 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;
|
||||
|
||||
return testMethodAttribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a method by using the method name.
|
||||
/// </summary>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <param name="testClassInfo"> The test Class Info. </param>
|
||||
/// <returns> The <see cref="MethodInfo"/>. </returns>
|
||||
private MethodInfo GetMethodInfoForTestMethod(TestMethod testMethod, TestClassInfo testClassInfo)
|
||||
{
|
||||
var methodsInClass = testClassInfo.ClassType.GetRuntimeMethods().ToArray();
|
||||
|
||||
var testMethodInfo =
|
||||
methodsInClass.Where(method => method.Name.Equals(testMethod.Name))
|
||||
.FirstOrDefault(method => method.HasCorrectTestMethodSignature(true));
|
||||
|
||||
// if correct method is not found, throw appropriate
|
||||
// exception about what is wrong.
|
||||
if (testMethodInfo == null)
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.CurrentCulture, Resource.UTA_MethodDoesNotExists, testMethod.FullClassName, testMethod.Name);
|
||||
throw new TypeInspectionException(errorMessage);
|
||||
}
|
||||
|
||||
return testMethodInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the test timeout for the parameter test method
|
||||
/// </summary>
|
||||
/// <param name="methodInfo"> The method Info. </param>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <returns> The timeout value if defined. 0 if not defined. </returns>
|
||||
private int GetTestTimeout(MethodInfo methodInfo, TestMethod testMethod)
|
||||
{
|
||||
Debug.Assert(methodInfo != null, "TestMethod should be non-null");
|
||||
var timeoutAttribute = this.reflectionHelper.GetAttribute<TimeoutAttribute>(methodInfo);
|
||||
var globalTimeout = MSTestSettings.CurrentSettings.TestTimeout;
|
||||
|
||||
if (timeoutAttribute != null)
|
||||
{
|
||||
if (!methodInfo.HasCorrectTimeout())
|
||||
{
|
||||
var message = string.Format(CultureInfo.CurrentCulture, Resource.UTA_ErrorInvalidTimeout, testMethod.FullClassName, testMethod.Name);
|
||||
throw new TypeInspectionException(message);
|
||||
}
|
||||
|
||||
return timeoutAttribute.Timeout;
|
||||
}
|
||||
else if (globalTimeout > 0)
|
||||
{
|
||||
return globalTimeout;
|
||||
}
|
||||
|
||||
return TestMethodInfo.TimeoutWhenNotSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set custom properties
|
||||
/// </summary>
|
||||
/// <param name="testMethodInfo"> The test Method Info. </param>
|
||||
/// <param name="testContext"> The test Context. </param>
|
||||
private void SetCustomProperties(TestMethodInfo testMethodInfo, ITestContext testContext)
|
||||
{
|
||||
Debug.Assert(testMethodInfo != null, "testMethodInfo is Null");
|
||||
Debug.Assert(testMethodInfo.TestMethod != null, "testMethodInfo.TestMethod is Null");
|
||||
|
||||
var attributes = testMethodInfo.TestMethod.GetCustomAttributes(typeof(TestPropertyAttribute), false);
|
||||
Debug.Assert(attributes != null, "attributes is null");
|
||||
|
||||
foreach (TestPropertyAttribute attribute in attributes)
|
||||
{
|
||||
if (!this.ValidateAndAssignTestProperty(testMethodInfo, testContext, attribute.Name, attribute.Value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates If a Custom test property is valid and then adds it to the TestContext property list.
|
||||
/// </summary>
|
||||
/// <param name="testMethodInfo"> The test method info. </param>
|
||||
/// <param name="testContext"> The test context. </param>
|
||||
/// <param name="propertyName"> The property name. </param>
|
||||
/// <param name="propertyValue"> The property value. </param>
|
||||
/// <returns> True if its a valid Test Property. </returns>
|
||||
private bool ValidateAndAssignTestProperty(
|
||||
TestMethodInfo testMethodInfo,
|
||||
ITestContext testContext,
|
||||
string propertyName,
|
||||
string propertyValue)
|
||||
{
|
||||
if (PredefinedNames.Any(predefinedProp => predefinedProp == propertyName))
|
||||
{
|
||||
testMethodInfo.NotRunnableReason = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_ErrorPredefinedTestProperty,
|
||||
testMethodInfo.TestMethod.DeclaringType.FullName,
|
||||
testMethodInfo.TestMethod.Name,
|
||||
propertyName);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(propertyName))
|
||||
{
|
||||
testMethodInfo.NotRunnableReason = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_ErrorTestPropertyNullOrEmpty,
|
||||
testMethodInfo.TestMethod.DeclaringType.FullName,
|
||||
testMethodInfo.TestMethod.Name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
object existingValue;
|
||||
if (testContext.TryGetPropertyValue(propertyName, out existingValue))
|
||||
{
|
||||
// Do not add to the test context because it would conflict with an already existing value.
|
||||
// We were at one point reporting a warning here. However with extensibility centered around TestProperty where
|
||||
// users can have multiple WorkItemAttributes(say) we cannot throw a warning here. Users would have multiple of these attributes
|
||||
// so that it shows up in reporting rather than seeing them in TestContext properties.
|
||||
}
|
||||
else
|
||||
{
|
||||
testContext.AddProperty(propertyName, propertyValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
|
||||
/// <summary>
|
||||
/// The runner that runs a single unit test. Also manages the assembly and class cleanup methods at the end of the run.
|
||||
/// </summary>
|
||||
internal class UnitTestRunner : MarshalByRefObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Type cache
|
||||
/// </summary>
|
||||
//private readonly TypeCache typeCache;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitTestRunner"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings"> Specifies adapter settings that need to be instantiated in the domain running these tests. </param>
|
||||
public UnitTestRunner(MSTestSettings settings)
|
||||
: this(settings, new ReflectHelper())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitTestRunner"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings"> Specifies adapter settings. </param>
|
||||
/// <param name="reflectHelper"> The reflect Helper. </param>
|
||||
internal UnitTestRunner(MSTestSettings settings, ReflectHelper reflectHelper)
|
||||
{
|
||||
//this.typeCache = new TypeCache(reflectHelper);
|
||||
|
||||
// Populate the settings into the domain(Desktop workflow) performing discovery.
|
||||
// This would just be resettings the settings to itself in non desktop workflows.
|
||||
MSTestSettings.PopulateSettings(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="object"/>.
|
||||
/// </returns>
|
||||
[SecurityCritical]
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a single test.
|
||||
/// </summary>
|
||||
/// <param name="testMethod"> The test Method. </param>
|
||||
/// <param name="testContextProperties"> The test context properties. </param>
|
||||
/// <returns> The <see cref="UnitTestResult"/>. </returns>
|
||||
internal UnitTestResult[] RunSingleTest(
|
||||
nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel.TestMethod testMethod,
|
||||
IDictionary<string, object> testContextProperties)
|
||||
{
|
||||
if (testMethod == null)
|
||||
{
|
||||
throw new ArgumentNullException("testMethod");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var writer = new ThreadSafeStringWriter(CultureInfo.InvariantCulture))
|
||||
{
|
||||
var properties = new Dictionary<string, object>(testContextProperties);
|
||||
var testContext = PlatformServiceProvider.Instance.GetTestContext(testMethod, writer, properties);
|
||||
testContext.SetOutcome(UnitTestOutcome.InProgress);
|
||||
|
||||
// Get the testMethod
|
||||
//var testMethodInfo = this.typeCache.GetTestMethodInfo(testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces);
|
||||
|
||||
// If the specified TestMethod could not be found, return a NotFound result.
|
||||
//if (testMethodInfo == null)
|
||||
{
|
||||
return new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.NotFound, string.Format(CultureInfo.CurrentCulture, Resource.TestNotFound, testMethod.Name)) };
|
||||
}
|
||||
|
||||
// If test cannot be executed, then bail out.
|
||||
//if (!testMethodInfo.IsRunnable)
|
||||
{
|
||||
//return new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.NotRunnable, testMethodInfo.NotRunnableReason) };
|
||||
}
|
||||
|
||||
//return new TestMethodRunner(testMethodInfo, testMethod, testContext, MSTestSettings.CurrentSettings.CaptureDebugTraces).Execute();
|
||||
}
|
||||
}
|
||||
catch (TypeInspectionException ex)
|
||||
{
|
||||
// Catch any exception thrown while inspecting the test method and return failure.
|
||||
return new UnitTestResult[] { new UnitTestResult(UnitTestOutcome.Failed, "this is a message"/*ex.Message*/) };
|
||||
}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Runs the class cleanup method.
|
||||
///// It returns any error information during the execution of the cleanup method
|
||||
///// </summary>
|
||||
///// <returns> The <see cref="RunCleanupResult"/>. </returns>
|
||||
//internal RunCleanupResult RunCleanup()
|
||||
//{
|
||||
// // No cleanup methods to execute, then return.
|
||||
// var assemblyInfoCache = this.typeCache.AssemblyInfoListWithExecutableCleanupMethods;
|
||||
// var classInfoCache = this.typeCache.ClassInfoListWithExecutableCleanupMethods;
|
||||
// if (!assemblyInfoCache.Any() && !classInfoCache.Any())
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// var result = new RunCleanupResult { Warnings = new List<string>() };
|
||||
|
||||
// using (var redirector = new LogMessageListener(MSTestSettings.CurrentSettings.CaptureDebugTraces))
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// this.RunClassCleanupMethods(classInfoCache, result.Warnings);
|
||||
// this.RunAssemblyCleanup(assemblyInfoCache, result.Warnings);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// // Replacing the null character with a string.replace should work.
|
||||
// // If this does not work for a specific dotnet version a custom function doing the same needs to be put in place.
|
||||
// result.StandardOut = redirector.StandardOutput?.Replace("\0", "\\0");
|
||||
// result.StandardError = redirector.StandardError?.Replace("\0", "\\0");
|
||||
// result.DebugTrace = redirector.DebugTrace?.Replace("\0", "\\0");
|
||||
// }
|
||||
// }
|
||||
|
||||
// return result;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Run assembly cleanup methods
|
||||
///// </summary>
|
||||
///// <param name="assemblyInfoCache"> The assembly Info Cache. </param>
|
||||
///// <param name="warnings"> The warnings. </param>
|
||||
//private void RunAssemblyCleanup(IEnumerable<TestAssemblyInfo> assemblyInfoCache, IList<string> warnings)
|
||||
//{
|
||||
// foreach (var assemblyInfo in assemblyInfoCache)
|
||||
// {
|
||||
// Debug.Assert(assemblyInfo.HasExecutableCleanupMethod, "HasExecutableCleanupMethod should be true.");
|
||||
|
||||
// var warning = assemblyInfo.RunAssemblyCleanup();
|
||||
// if (warning != null)
|
||||
// {
|
||||
// warnings.Add(warning);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Run class cleanup methods
|
||||
///// </summary>
|
||||
///// <param name="classInfoCache"> The class Info Cache. </param>
|
||||
///// <param name="warnings"> The warnings. </param>
|
||||
//private void RunClassCleanupMethods(IEnumerable<TestClassInfo> classInfoCache, IList<string> warnings)
|
||||
//{
|
||||
// foreach (var classInfo in classInfoCache)
|
||||
// {
|
||||
// Debug.Assert(classInfo.HasExecutableCleanupMethod, "HasExecutableCleanupMethod should be true.");
|
||||
|
||||
// var warning = classInfo.RunClassCleanup();
|
||||
// if (warning != null)
|
||||
// {
|
||||
// warnings.Add(warning);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
using nanoFramework.TestAdapter;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace nanoFramework.TestPlatform.TestAdapter
|
||||
{
|
||||
/// <summary>
|
||||
/// An Executor class
|
||||
/// </summary>
|
||||
[ExtensionUri(TestsConstants.NanoExecutor)]
|
||||
class Executor : ITestExecutor
|
||||
{
|
||||
private const string TestPassed = "Test passed: ";
|
||||
private const string TestFailed = "Test failed: ";
|
||||
private const string Exiting = "Exiting.";
|
||||
private const string Done = "Done.";
|
||||
private Settings _settings;
|
||||
private LogMessenger _logger;
|
||||
private Process _nanoClr;
|
||||
|
||||
private IFrameworkHandle _frameworkHandle = null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Cancel()
|
||||
{
|
||||
if (!_nanoClr.HasExited)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
"Canceling to test process. Attempting to kill nanoCLR process...",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
_nanoClr.Kill();
|
||||
// Wait 5 seconds maximum
|
||||
_nanoClr.WaitForExit(5000);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RunTests(IEnumerable<TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle)
|
||||
{
|
||||
var settingsProvider = runContext.RunSettings.GetSettings(TestsConstants.SettingsName) as SettingsProvider;
|
||||
|
||||
_logger = new LogMessenger(frameworkHandle, settingsProvider);
|
||||
|
||||
if (settingsProvider != null)
|
||||
{
|
||||
_settings = settingsProvider.Settings;
|
||||
|
||||
_logger.LogMessage(
|
||||
"Getting ready to run tests...",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
_logger.LogMessage(
|
||||
"Settings parsed",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogMessage(
|
||||
"Getting ready to run tests...",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
_logger.LogMessage(
|
||||
"No settings for nanoFramework adapter",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
}
|
||||
|
||||
var uniqueSources = tests.Select(m => m.Source).Distinct();
|
||||
|
||||
_logger.LogMessage(
|
||||
"Test sources enumerated",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
foreach (var source in uniqueSources)
|
||||
{
|
||||
var groups = tests.Where(m => m.Source == source);
|
||||
|
||||
_logger.LogMessage(
|
||||
$"Test group is '{source}'",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
var results = RunTest(groups.ToList());
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
frameworkHandle.RecordResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RunTests(IEnumerable<string> sources, IRunContext runContext, IFrameworkHandle frameworkHandle)
|
||||
{
|
||||
foreach (var source in sources)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
$"Finding test cases for '{source}'...",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
var testsCases = TestDiscoverer.FindTestCases(source);
|
||||
|
||||
RunTests(testsCases, runContext, frameworkHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestResult> RunTest(List<TestCase> tests)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
"Setting up test runner...",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
int runTimeout = 10000;
|
||||
if (_settings != null)
|
||||
{
|
||||
runTimeout = _settings.TestTimeOutSeconds * 1000;
|
||||
}
|
||||
|
||||
_logger.LogMessage(
|
||||
$"Timeout set to {runTimeout}ms",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
List<TestResult> results = new List<TestResult>();
|
||||
|
||||
foreach (var test in tests)
|
||||
{
|
||||
TestResult result = new TestResult(test) { Outcome = TestOutcome.None };
|
||||
results.Add(result);
|
||||
}
|
||||
|
||||
_logger.LogMessage(
|
||||
"Processing assemblies to load into test runner...",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
var source = tests.First().Source;
|
||||
var nfUnitTestLauncherLocation = source.Replace(Path.GetFileName(source), "nanoFramework.UnitTestLauncher.pe");
|
||||
var workingDirectory = Path.GetDirectoryName(nfUnitTestLauncherLocation);
|
||||
var mscorlibLocation = source.Replace(Path.GetFileName(source), "mscorlib.pe");
|
||||
var nfTestFrameworkLocation = source.Replace(Path.GetFileName(source), "nanoFramework.TestFramework.pe");
|
||||
var nfAssemblyUnderTestLocation = source.Replace(".dll", ".pe");
|
||||
|
||||
// prepare the process start of the WIN32 nanoCLR
|
||||
_nanoClr = new Process();
|
||||
|
||||
AutoResetEvent outputWaitHandle = new AutoResetEvent(false);
|
||||
AutoResetEvent errorWaitHandle = new AutoResetEvent(false);
|
||||
StringBuilder output = new StringBuilder();
|
||||
StringBuilder error = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
// prepare parameters to load nanoCLR, include:
|
||||
// 1. unit test launcher
|
||||
// 2. mscorlib
|
||||
// 3. test framework
|
||||
// 4. test application
|
||||
string parameter = $"-load {nfUnitTestLauncherLocation} -load {mscorlibLocation} -load {nfTestFrameworkLocation} -load {nfAssemblyUnderTestLocation}";
|
||||
|
||||
_logger.LogMessage(
|
||||
"Launching process with nanoCLR...",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
_nanoClr.StartInfo = new ProcessStartInfo(TestObjectHelper.GetNanoClrLocation(), parameter)
|
||||
{
|
||||
WorkingDirectory = workingDirectory,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true
|
||||
};
|
||||
|
||||
// launch nanoCLR
|
||||
if (!_nanoClr.Start())
|
||||
{
|
||||
results.First().Outcome = TestOutcome.Failed;
|
||||
results.First().ErrorMessage = "Failed to start nanoCLR";
|
||||
|
||||
_logger.LogPanicMessage(
|
||||
"Failed to start nanoCLR!");
|
||||
}
|
||||
|
||||
_nanoClr.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data == null)
|
||||
{
|
||||
outputWaitHandle.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
output.AppendLine(e.Data);
|
||||
}
|
||||
};
|
||||
|
||||
_nanoClr.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data == null)
|
||||
{
|
||||
errorWaitHandle.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
error.AppendLine(e.Data);
|
||||
}
|
||||
};
|
||||
|
||||
_nanoClr.Start();
|
||||
|
||||
_nanoClr.BeginOutputReadLine();
|
||||
_nanoClr.BeginErrorReadLine();
|
||||
|
||||
_logger.LogMessage(
|
||||
$"nanoCLR started @ process ID: {_nanoClr.Id}",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
|
||||
// wait for exit, no worries about the outcome
|
||||
_nanoClr.WaitForExit(runTimeout);
|
||||
|
||||
var outputStrings = Regex.Split(output.ToString(), @"((\r)+)?(\n)+((\r)+)?").Where(m => !string.IsNullOrEmpty(m));
|
||||
|
||||
_logger.LogMessage(
|
||||
"Parsing test results...",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
foreach (var line in outputStrings)
|
||||
{
|
||||
if (line.Contains(TestPassed))
|
||||
{
|
||||
// Format is "Test passed: MethodName, ticks";
|
||||
// We do get split with space if the coma is missing, happens time to time
|
||||
string method = line.Substring(line.IndexOf(TestPassed) + TestPassed.Length).Split(',')[0].Split(' ')[0];
|
||||
string ticks = line.Substring(line.IndexOf(TestPassed) + TestPassed.Length + method.Length + 2);
|
||||
long ticksNum = 0;
|
||||
|
||||
try
|
||||
{
|
||||
ticksNum = Convert.ToInt64(ticks);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// We won't do anything
|
||||
}
|
||||
|
||||
// Find the test
|
||||
var res = results.Where(m => m.TestCase.DisplayName == method);
|
||||
if (res.Any())
|
||||
{
|
||||
res.First().Duration = TimeSpan.FromTicks(ticksNum);
|
||||
res.First().Outcome = TestOutcome.Passed;
|
||||
}
|
||||
}
|
||||
else if (line.Contains(TestFailed))
|
||||
{
|
||||
// Format is "Test passed: MethodName, Exception message";
|
||||
string method = line.Substring(line.IndexOf(TestFailed) + TestFailed.Length).Split(',')[0].Split(' ')[0];
|
||||
string exception = line.Substring(line.IndexOf(TestFailed) + TestPassed.Length + method.Length + 2);
|
||||
|
||||
// Find the test
|
||||
var res = results.Where(m => m.TestCase.DisplayName == method);
|
||||
if (res.Any())
|
||||
{
|
||||
res.First().ErrorMessage = exception;
|
||||
res.First().Outcome = TestOutcome.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!output.ToString().Contains(Done))
|
||||
{
|
||||
results.First().Outcome = TestOutcome.Failed;
|
||||
results.First().ErrorMessage = output.ToString();
|
||||
}
|
||||
|
||||
var notPassedOrFailed = results.Where(m => m.Outcome != TestOutcome.Failed && m.Outcome != TestOutcome.Passed);
|
||||
if (notPassedOrFailed.Any())
|
||||
{
|
||||
notPassedOrFailed.First().ErrorMessage = output.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
$"Fatal exception when processing test results: >>>{ex.Message}\r\n{output}\r\n{error}",
|
||||
Settings.LoggingLevel.Detailed);
|
||||
|
||||
results.First().Outcome = TestOutcome.Failed;
|
||||
results.First().ErrorMessage = $"Fatal exception when processing test results. Set logging to 'Detailed' for details.";
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!_nanoClr.HasExited)
|
||||
{
|
||||
_logger.LogMessage(
|
||||
"Attempting to kill nanoCLR process...",
|
||||
Settings.LoggingLevel.Verbose);
|
||||
|
||||
_nanoClr.Kill();
|
||||
_nanoClr.WaitForExit(runTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Execution;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for the exception class.
|
||||
/// </summary>
|
||||
internal static class ExceptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the InnerException if available, else return the current Exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="Exception"/> instance.
|
||||
/// </returns>
|
||||
internal static Exception GetInnerExceptionOrDefault(this Exception exception)
|
||||
{
|
||||
return exception?.InnerException ?? exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the exception message if available, empty otherwise.
|
||||
/// </summary>
|
||||
/// <param name="exception">An <see cref="Exception"/> object</param>
|
||||
/// <returns>Exception message</returns>
|
||||
internal static string TryGetMessage(this Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, "null");
|
||||
}
|
||||
|
||||
// It is safe to retrieve an exception message, it should not throw in any case.
|
||||
return exception.Message ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an exception message with all inner exceptions messages.
|
||||
/// </summary>
|
||||
/// <param name="exception"> The exception. </param>
|
||||
/// <returns> Custom exception message that includes inner exceptions. </returns>
|
||||
internal static string GetExceptionMessage(this Exception exception)
|
||||
{
|
||||
Debug.Assert(exception != null, "Exception is null");
|
||||
|
||||
if (exception == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var exceptionString = exception.Message;
|
||||
var inner = exception.InnerException;
|
||||
while (inner != null)
|
||||
{
|
||||
exceptionString += Environment.NewLine + inner.Message;
|
||||
inner = inner.InnerException;
|
||||
}
|
||||
|
||||
return exceptionString;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="StackTraceInformation"/> for an exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">An <see cref="Exception"/> instance.</param>
|
||||
/// <returns>StackTraceInformation for the exception</returns>
|
||||
internal static StackTraceInformation TryGetStackTraceInformation(this Exception exception)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(exception?.StackTrace))
|
||||
{
|
||||
return StackTraceHelper.CreateStackTraceInformation(exception, false, exception.StackTrace);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether exception is an Assert exception
|
||||
/// </summary>
|
||||
/// <param name="exception">An <see cref="Exception"/> instance.</param>
|
||||
/// <param name="outcome"> Adapter's Outcome depending on type of assertion.</param>
|
||||
/// <param name="exceptionMessage">Exception message.</param>
|
||||
/// <param name="exceptionStackTrace">StackTraceInformation for the exception</param>
|
||||
/// <returns>True, if Assert exception. False, otherwise.</returns>
|
||||
internal static bool TryGetUnitTestAssertException(this Exception exception, out UnitTestOutcome outcome, out string exceptionMessage, out StackTraceInformation exceptionStackTrace)
|
||||
{
|
||||
if (exception is UnitTestAssertException)
|
||||
{
|
||||
outcome = exception is AssertInconclusiveException ?
|
||||
UnitTestOutcome.Inconclusive : UnitTestOutcome.Failed;
|
||||
|
||||
exceptionMessage = exception.TryGetMessage();
|
||||
exceptionStackTrace = exception.TryGetStackTraceInformation();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
outcome = UnitTestOutcome.Failed;
|
||||
exceptionMessage = null;
|
||||
exceptionStackTrace = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets exception message and stack trace for TestFailedException.
|
||||
/// </summary>
|
||||
/// <param name="testFailureException">An <see cref="TestFailedException"/> instance.</param>
|
||||
/// <param name="message"> Appends TestFailedException message to this message.</param>
|
||||
/// <param name="stackTrace"> Appends TestFailedExeption stacktrace to this stackTrace</param>
|
||||
internal static void TryGetTestFailureExceptionMessageAndStackTrace(this TestFailedException testFailureException, StringBuilder message, StringBuilder stackTrace)
|
||||
{
|
||||
if (testFailureException != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(testFailureException.Message))
|
||||
{
|
||||
message.Append(testFailureException.Message);
|
||||
message.AppendLine();
|
||||
}
|
||||
|
||||
if (testFailureException.StackTraceInformation != null && !string.IsNullOrEmpty(testFailureException.StackTraceInformation.ErrorStackTrace))
|
||||
{
|
||||
stackTrace.Append(testFailureException.StackTraceInformation.ErrorStackTrace);
|
||||
stackTrace.Append(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal static class MethodInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that the class initialize has the correct signature
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <returns>True if the method has the right Assembly/Class initialize signature.</returns>
|
||||
internal static bool HasCorrectClassOrAssemblyInitializeSignature(this MethodInfo method)
|
||||
{
|
||||
Debug.Assert(method != null, "method should not be null.");
|
||||
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
|
||||
return
|
||||
method.IsStatic &&
|
||||
method.IsPublic &&
|
||||
(parameters.Length == 1) &&
|
||||
parameters[0].ParameterType == typeof(TestContext) &&
|
||||
method.IsVoidOrTaskReturnType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the class cleanup has the correct signature
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <returns>True if the method has the right Assembly/Class cleanup signature.</returns>
|
||||
internal static bool HasCorrectClassOrAssemblyCleanupSignature(this MethodInfo method)
|
||||
{
|
||||
Debug.Assert(method != null, "method should not be null.");
|
||||
|
||||
return
|
||||
method.IsStatic &&
|
||||
method.IsPublic &&
|
||||
(method.GetParameters().Length == 0) &&
|
||||
method.IsVoidOrTaskReturnType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the test Initiailize/cleanup has the correct signature
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <returns>True if the method has the right test init/cleanup signature.</returns>
|
||||
internal static bool HasCorrectTestInitializeOrCleanupSignature(this MethodInfo method)
|
||||
{
|
||||
Debug.Assert(method != null, "method should not be null.");
|
||||
|
||||
return
|
||||
!method.IsStatic &&
|
||||
method.IsPublic &&
|
||||
(method.GetParameters().Length == 0) &&
|
||||
method.IsVoidOrTaskReturnType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the test method has the correct signature
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <param name="ignoreParameterLength">Indicates whether parameter lenght is to be ignored.</param>
|
||||
/// <returns>True if the method has the right test method signature.</returns>
|
||||
internal static bool HasCorrectTestMethodSignature(this MethodInfo method, bool ignoreParameterLength)
|
||||
{
|
||||
Debug.Assert(method != null, "method should not be null.");
|
||||
|
||||
return
|
||||
!method.IsAbstract &&
|
||||
!method.IsStatic &&
|
||||
!method.IsGenericMethod &&
|
||||
method.IsPublic &&
|
||||
(method.GetParameters().Length == 0 || ignoreParameterLength) &&
|
||||
method.IsVoidOrTaskReturnType(); // Match return type Task for async methods only. Else return type void.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether test method has correct Timeout attribute.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <returns>True if the method has the right test timeout signature.</returns>
|
||||
internal static bool HasCorrectTimeout(this MethodInfo method)
|
||||
{
|
||||
Debug.Assert(method != null, "method should not be null.");
|
||||
|
||||
// There should be one and only one TimeoutAttribute.
|
||||
var attributes = ReflectHelper.GetCustomAttributes(method, typeof(TimeoutAttribute), false);
|
||||
if (attributes?.Length != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Timeout cannot be less than 0.
|
||||
var attribute = attributes[0] as TimeoutAttribute;
|
||||
|
||||
return !(attribute?.Timeout < 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check is return type is void for non async and Task for async methods.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <returns>True if the method has a void/task return type..</returns>
|
||||
internal static bool IsVoidOrTaskReturnType(this MethodInfo method)
|
||||
{
|
||||
return method.GetAsyncTypeName() == null ? ReflectHelper.MatchReturnType(method, typeof(void))
|
||||
: ReflectHelper.MatchReturnType(method, typeof(Task));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For async methods compiler generates different type and method.
|
||||
/// Gets the compiler generated type name for given async test method.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to verify.</param>
|
||||
/// <returns>Compiler generated type name for given async test method..</returns>
|
||||
internal static string GetAsyncTypeName(this MethodInfo method)
|
||||
{
|
||||
var asyncStateMachineAttribute = ReflectHelper.GetCustomAttributes(method, typeof(AsyncStateMachineAttribute), false).FirstOrDefault() as AsyncStateMachineAttribute;
|
||||
|
||||
return asyncStateMachineAttribute?.StateMachineType?.FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke a <see cref="MethodInfo"/> as a synchronous <see cref="Task"/>.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">
|
||||
/// <see cref="MethodInfo"/> instance.
|
||||
/// </param>
|
||||
/// <param name="classInstance">
|
||||
/// Instance of the on which methodInfo is invoked.
|
||||
/// </param>
|
||||
/// <param name="parameters">
|
||||
/// Arguments for the methodInfo invoke.
|
||||
/// </param>
|
||||
internal static void InvokeAsSynchronousTask(this MethodInfo methodInfo, object classInstance, params object[] parameters)
|
||||
{
|
||||
var methodParameters = methodInfo.GetParameters();
|
||||
|
||||
// check if testmethod expected parameter values but no testdata was provided,
|
||||
// throw error with appropriate message.
|
||||
if (methodParameters != null && methodParameters.Length > 0 && parameters == null)
|
||||
{
|
||||
throw new TestFailedException(ObjectModel.UnitTestOutcome.Error, Resource.UTA_TestMethodExpectedParameters);
|
||||
}
|
||||
|
||||
var task = methodInfo.Invoke(classInstance, parameters) as Task;
|
||||
|
||||
// If methodInfo is an Async method, wait for returned task
|
||||
task?.GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions
|
||||
{
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
|
||||
/// <summary>
|
||||
/// Extension Methods for TestCase Class
|
||||
/// </summary>
|
||||
internal static class TestCaseExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The to unit test element.
|
||||
/// </summary>
|
||||
/// <param name="testCase"> The test case. </param>
|
||||
/// <param name="source"> The source. If deployed this is the full path of the source in the deployment directory. </param>
|
||||
/// <returns> The converted <see cref="UnitTestElement"/>. </returns>
|
||||
internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string source)
|
||||
{
|
||||
// TODO currently nanoFramework does not support async
|
||||
//var isAsync = (testCase.GetPropertyValue(Constants.AsyncTestProperty) as bool?) ?? false;
|
||||
var isAsync = false;
|
||||
|
||||
var testClassName = testCase.GetPropertyValue(TestAdapter.Constants.TestClassNameProperty) as string;
|
||||
|
||||
TestMethod testMethod = new TestMethod(testCase.DisplayName, testClassName, source, isAsync);
|
||||
|
||||
UnitTestElement testElement = new UnitTestElement(testMethod)
|
||||
{
|
||||
IsAsync = isAsync,
|
||||
TestCategory = testCase.GetPropertyValue(TestAdapter.Constants.TestCategoryProperty) as string[],
|
||||
Priority = testCase.GetPropertyValue(TestAdapter.Constants.PriorityProperty) as int?
|
||||
};
|
||||
|
||||
return testElement;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Interface;
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions
|
||||
{
|
||||
internal static class TestContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns diagnostic messages written to test context and clears from this instance.
|
||||
/// </summary>
|
||||
/// <param name="testContext">The test context instance.</param>
|
||||
/// <returns>The diagnostic messages.</returns>
|
||||
internal static string GetAndClearDiagnosticMessages(this ITestContext testContext)
|
||||
{
|
||||
var messages = testContext.GetDiagnosticMessages();
|
||||
|
||||
testContext.ClearDiagnosticMessages();
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Extensions
|
||||
{
|
||||
public static class UnitTestOutcomeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the test framework's UnitTestOutcome object to adapter's UnitTestOutcome object.
|
||||
/// </summary>
|
||||
/// <param name="frameworkTestOutcome">The test framework's UnitTestOutcome object.</param>
|
||||
/// <returns>The adapter's UnitTestOutcome object.</returns>
|
||||
public static UnitTestOutcome ToUnitTestOutcome(this UnitTestOutcome frameworkTestOutcome)
|
||||
{
|
||||
UnitTestOutcome outcome = UnitTestOutcome.Passed;
|
||||
|
||||
switch (frameworkTestOutcome)
|
||||
{
|
||||
case UnitTestOutcome.Failed:
|
||||
outcome = UnitTestOutcome.Failed;
|
||||
break;
|
||||
|
||||
case UnitTestOutcome.Inconclusive:
|
||||
outcome = UnitTestOutcome.Inconclusive;
|
||||
break;
|
||||
|
||||
case UnitTestOutcome.InProgress:
|
||||
outcome = UnitTestOutcome.InProgress;
|
||||
break;
|
||||
|
||||
case UnitTestOutcome.Passed:
|
||||
outcome = UnitTestOutcome.Passed;
|
||||
break;
|
||||
|
||||
case UnitTestOutcome.Timeout:
|
||||
outcome = UnitTestOutcome.Timeout;
|
||||
break;
|
||||
|
||||
case UnitTestOutcome.NotRunnable:
|
||||
outcome = UnitTestOutcome.NotRunnable;
|
||||
break;
|
||||
|
||||
case UnitTestOutcome.Unknown:
|
||||
default:
|
||||
outcome = UnitTestOutcome.Error;
|
||||
break;
|
||||
}
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns more important outcome of two.
|
||||
/// </summary>
|
||||
/// <param name="outcome1"> First outcome that needs to be compared. </param>
|
||||
/// <param name="outcome2"> Second outcome that needs to be compared. </param>
|
||||
/// <returns> Outcome which has higher importance.</returns>
|
||||
internal static UnitTestOutcome GetMoreImportantOutcome(
|
||||
this UnitTestOutcome outcome1,
|
||||
UnitTestOutcome outcome2)
|
||||
{
|
||||
var unitTestOutcome1 = outcome1.ToUnitTestOutcome();
|
||||
var unitTestOutcome2 = outcome2.ToUnitTestOutcome();
|
||||
return unitTestOutcome1 < unitTestOutcome2 ? outcome1 : outcome2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
|
@ -1,712 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers
|
||||
{
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Execution;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
|
||||
internal class ReflectHelper : MarshalByRefObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the memberInfo Vs the name/type of the attributes defined on that member. (FYI: - MemberInfo denotes properties, fields, methods, events)
|
||||
/// </summary>
|
||||
private Dictionary<MemberInfo, Dictionary<string, object>> attributeCache = new Dictionary<MemberInfo, Dictionary<string, object>>();
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the parameter memberInfo contains the parameter attribute or not.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">Member/Type to test</param>
|
||||
/// <param name="attributeType">Attribute to search for</param>
|
||||
/// <param name="inherit">Look through inheritance or not</param>
|
||||
/// <returns>True if the attribute of the specified type is defined.</returns>
|
||||
public virtual bool IsAttributeDefined(
|
||||
MemberInfo memberInfo,
|
||||
Type attributeType,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
if (memberInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(memberInfo));
|
||||
}
|
||||
|
||||
if (attributeType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributeType));
|
||||
}
|
||||
|
||||
Debug.Assert(attributeType != null, "attrbiuteType should not be null.");
|
||||
|
||||
// Get attributes defined on the member from the cache.
|
||||
Dictionary<string, object> attributes = this.GetAttributes(memberInfo, inherit);
|
||||
if (attributes == null)
|
||||
{
|
||||
// If we could not obtain all attributes from cache, just get the one we need.
|
||||
var specificAttributes = GetCustomAttributes(memberInfo, attributeType, inherit);
|
||||
var requiredAttributeQualifiedName = attributeType.AssemblyQualifiedName;
|
||||
|
||||
return specificAttributes.Any(a => string.Equals(a.GetType().AssemblyQualifiedName, requiredAttributeQualifiedName));
|
||||
}
|
||||
|
||||
string nameToFind = attributeType.AssemblyQualifiedName;
|
||||
if (attributes.ContainsKey(nameToFind))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the parameter memberInfo contains the parameter attribute or not.
|
||||
/// </summary>
|
||||
/// <param name="type">Member/Type to test</param>
|
||||
/// <param name="attributeType">Attribute to search for</param>
|
||||
/// <param name="inherit">Look throug inheritence or not</param>
|
||||
/// <returns>True if the specified attribute is defined on the type.</returns>
|
||||
public virtual bool IsAttributeDefined(
|
||||
Type type,
|
||||
Type attributeType,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
return this.IsAttributeDefined((MemberInfo)type.GetTypeInfo(), attributeType, inherit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when specified class/member has attribute derived from specific attribute.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="baseAttributeType">The base attribute type.</param>
|
||||
/// <param name="inherit">Should look at inheritance tree.</param>
|
||||
/// <returns>An object derived from Attribute that corresponds to the instance of found attribute.</returns>
|
||||
public virtual bool HasAttributeDerivedFrom(
|
||||
Type type,
|
||||
Type baseAttributeType,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
return this.HasAttributeDerivedFrom((MemberInfo)type.GetTypeInfo(), baseAttributeType, inherit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when specified class/member has attribute derived from specific attribute.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">The member info.</param>
|
||||
/// <param name="baseAttributeType">The base attribute type.</param>
|
||||
/// <param name="inherit">Should look at inheritance tree.</param>
|
||||
/// <returns>An object derived from Attribute that corresponds to the instance of found attribute.</returns>
|
||||
public bool HasAttributeDerivedFrom(
|
||||
MemberInfo memberInfo,
|
||||
Type baseAttributeType,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
if (memberInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(memberInfo));
|
||||
}
|
||||
|
||||
if (baseAttributeType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(baseAttributeType));
|
||||
}
|
||||
|
||||
// Get all attributes on the member.
|
||||
Dictionary<string, object> attributes = this.GetAttributes(memberInfo, inherit);
|
||||
if (attributes == null)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning("ReflectHelper.HasAttributeDerivedFrom: Failed to get attribute cache. Ignoring attribute inheritance and falling into 'type defines Attribute model', so that we have some data.");
|
||||
|
||||
return this.IsAttributeDefined(memberInfo, baseAttributeType, inherit);
|
||||
}
|
||||
|
||||
// Try to find the attribute that is derived from baseAttrType.
|
||||
foreach (object attribute in attributes.Values)
|
||||
{
|
||||
Debug.Assert(attribute != null, "ReflectHeler.DefinesAttributeDerivedFrom: internal error: wrong value in the attrs dictionary.");
|
||||
|
||||
Type attributeType = attribute.GetType();
|
||||
if (attributeType.GetTypeInfo().IsSubclassOf(baseAttributeType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the expected exception attribute. The function will try to
|
||||
/// get all the expected exception attributes defined for a testMethod.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">The MethodInfo instance.</param>
|
||||
/// <param name="testMethod">The test method.</param>
|
||||
/// <returns>
|
||||
/// The expected exception attribute found for this test. Null if not found.
|
||||
/// </returns>
|
||||
public virtual ExpectedExceptionBaseAttribute ResolveExpectedExceptionHelper(
|
||||
MethodInfo methodInfo,
|
||||
TestMethod testMethod
|
||||
)
|
||||
{
|
||||
Debug.Assert(methodInfo != null, "MethodInfo should be non-null");
|
||||
|
||||
// Get the expected exception attribute
|
||||
ExpectedExceptionBaseAttribute[] expectedExceptions;
|
||||
try
|
||||
{
|
||||
expectedExceptions = GetCustomAttributes(methodInfo, typeof(ExpectedExceptionBaseAttribute), true).OfType<ExpectedExceptionBaseAttribute>().ToArray();
|
||||
}
|
||||
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,
|
||||
StackTraceHelper.GetExceptionMessage(ex));
|
||||
throw new TypeInspectionException(errorMessage);
|
||||
}
|
||||
|
||||
if (expectedExceptions == null || expectedExceptions.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Verify that there is only one attribute (multiple attributes derived from
|
||||
// ExpectedExceptionBaseAttribute are not allowed on a test method)
|
||||
if (expectedExceptions.Length > 1)
|
||||
{
|
||||
string errorMessage = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTA_MultipleExpectedExceptionsOnTestMethod,
|
||||
testMethod.FullClassName,
|
||||
testMethod.Name);
|
||||
throw new TypeInspectionException(errorMessage);
|
||||
}
|
||||
|
||||
// Set the expected exception attribute to use for this test
|
||||
ExpectedExceptionBaseAttribute expectedException = expectedExceptions[0];
|
||||
|
||||
return expectedException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns object to be used for controlling lifetime, null means infinite lifetime.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="object"/>.
|
||||
/// </returns>
|
||||
[SecurityCritical]
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Match return type of method.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to inspect.</param>
|
||||
/// <param name="returnType">The return type to match.</param>
|
||||
/// <returns>True if there is a match.</returns>
|
||||
internal static bool MatchReturnType(
|
||||
MethodInfo method,
|
||||
Type returnType
|
||||
)
|
||||
{
|
||||
if (method == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(method));
|
||||
}
|
||||
|
||||
if (returnType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(returnType));
|
||||
}
|
||||
|
||||
return method.ReturnType.Equals(returnType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get custom attributes on a member for both normal and reflection only load.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">Memeber for which attributes needs to be retrieved.</param>
|
||||
/// <param name="type">Type of attribute to retrieve.</param>
|
||||
/// <param name="inherit">If inheritied type of attribute.</param>
|
||||
/// <returns>All attributes of give type on member.</returns>
|
||||
internal static Attribute[] GetCustomAttributes(
|
||||
MemberInfo memberInfo,
|
||||
Type type,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
if (memberInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var attributesArray = PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(
|
||||
memberInfo,
|
||||
type,
|
||||
inherit);
|
||||
|
||||
return attributesArray.OfType<Attribute>().ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get custom attributes on a member for both normal and reflection only load.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">Memeber for which attributes needs to be retrieved.</param>
|
||||
/// <param name="inherit">If inheritied type of attribute.</param>
|
||||
/// <returns>All attributes of give type on member.</returns>
|
||||
internal static object[] GetCustomAttributes(
|
||||
MemberInfo memberInfo,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
if (memberInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var attributesArray = PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(
|
||||
memberInfo,
|
||||
inherit);
|
||||
|
||||
return attributesArray.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first attribute of the specified type or null if no attribute
|
||||
/// of the specified type is set on the method.
|
||||
/// </summary>
|
||||
/// <typeparam name="AttributeType">The type of attribute to return.</typeparam>
|
||||
/// <param name="method">The method on which the attribute is defined.</param>
|
||||
/// <returns>The attribute or null if none exists.</returns>
|
||||
internal AttributeType GetAttribute<AttributeType>(
|
||||
MethodInfo method
|
||||
)
|
||||
where AttributeType : class
|
||||
{
|
||||
if (this.IsAttributeDefined(method, typeof(AttributeType), false))
|
||||
{
|
||||
object[] attributes = GetCustomAttributes(method, typeof(AttributeType), false);
|
||||
Debug.Assert(attributes.Length == 1, "Should only be one attribute.");
|
||||
return attributes[0] as AttributeType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the attribute of the specified type. Null if no attribute of the specified type is defined.
|
||||
/// </summary>
|
||||
/// <param name="attributeType">The attribute type.</param>
|
||||
/// <param name="method">The method to inspect.</param>
|
||||
/// <returns>Attribute of the specified type. Null if not found.</returns>
|
||||
internal Attribute GetAttribute(
|
||||
Type attributeType,
|
||||
MethodInfo method
|
||||
)
|
||||
{
|
||||
if (this.IsAttributeDefined(method, attributeType, false))
|
||||
{
|
||||
object[] attributes = GetCustomAttributes(method, attributeType, false);
|
||||
Debug.Assert(attributes.Length == 1, "Should only be one attribute.");
|
||||
return attributes[0] as Attribute;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when the method is delcared in the assembly where the type is declared.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to check for.</param>
|
||||
/// <param name="type">The type declared in the assembly to check.</param>
|
||||
/// <returns>True if the method is declared in the assembly where the type is declared.</returns>
|
||||
internal virtual bool IsMethodDeclaredInSameAssemblyAsType(
|
||||
MethodInfo method,
|
||||
Type type
|
||||
)
|
||||
{
|
||||
return method.DeclaringType.GetTypeInfo().Assembly.Equals(type.GetTypeInfo().Assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get categories applied to the test method
|
||||
/// </summary>
|
||||
/// <param name="categoryAttributeProvider">The member to inspect.</param>
|
||||
/// <returns>Categories defined.</returns>
|
||||
internal virtual string[] GetCategories(
|
||||
MemberInfo categoryAttributeProvider
|
||||
)
|
||||
{
|
||||
var categories = this.GetCustomAttributesRecursively(categoryAttributeProvider, typeof(TestCategoryBaseAttribute));
|
||||
List<string> testCategories = new List<string>();
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
foreach (TestCategoryBaseAttribute category in categories)
|
||||
{
|
||||
testCategories.AddRange(category.TestCategories);
|
||||
}
|
||||
}
|
||||
|
||||
return testCategories.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets custom attributes at the class and assembly for a method.
|
||||
/// </summary>
|
||||
/// <param name="attributeProvider">Method Info or Member Info or a Type</param>
|
||||
/// <param name="type"> What type of CustomAttribute you need. For instance: TestCategory, Owner etc.,</param>
|
||||
/// <returns>The categories of the specified type on the method. </returns>
|
||||
internal IEnumerable<object> GetCustomAttributesRecursively(
|
||||
MemberInfo attributeProvider,
|
||||
Type type
|
||||
)
|
||||
{
|
||||
var categories = this.GetCustomAttributes(attributeProvider, typeof(TestCategoryBaseAttribute));
|
||||
if (categories != null)
|
||||
{
|
||||
categories = categories.Concat(this.GetCustomAttributes(attributeProvider.DeclaringType.GetTypeInfo(), typeof(TestCategoryBaseAttribute))).ToArray();
|
||||
}
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
categories = categories.Concat(this.GetCustomAttributeForAssembly(attributeProvider, typeof(TestCategoryBaseAttribute))).ToArray();
|
||||
}
|
||||
|
||||
if (categories != null)
|
||||
{
|
||||
return categories;
|
||||
}
|
||||
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom attributes on the assembly of a member info
|
||||
/// NOTE: having it as separate virtual method, so that we can extend it for testing.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">The member to inspect.</param>
|
||||
/// <param name="type">The attribute type to find.</param>
|
||||
/// <returns>Custom attributes defined.</returns>
|
||||
internal virtual Attribute[] GetCustomAttributeForAssembly(
|
||||
MemberInfo memberInfo,
|
||||
Type type
|
||||
)
|
||||
{
|
||||
return
|
||||
PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(
|
||||
memberInfo.DeclaringType.GetTypeInfo().Assembly,
|
||||
type).OfType<Attribute>().ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom attributes of the provided type on a memberInfo
|
||||
/// </summary>
|
||||
/// <param name="attributeProvider"> The member to reflect on. </param>
|
||||
/// <param name="type"> The attribute type. </param>
|
||||
/// <returns>Attributes defined.</returns>
|
||||
internal virtual Attribute[] GetCustomAttributes(
|
||||
MemberInfo attributeProvider,
|
||||
Type type
|
||||
)
|
||||
{
|
||||
return GetCustomAttributes(attributeProvider, type, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KeyValue pairs that are provided by TestOwnerAttribute of the given test method.
|
||||
/// </summary>
|
||||
/// <param name="ownerAttributeProvider">The member to inspect.</param>
|
||||
/// <returns>The owner trait.</returns>
|
||||
internal virtual Trait GetTestOwnerAsTraits(
|
||||
MemberInfo ownerAttributeProvider
|
||||
)
|
||||
{
|
||||
string owner = this.GetOwner(ownerAttributeProvider);
|
||||
|
||||
if (string.IsNullOrEmpty(owner))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Trait("Owner", owner);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KeyValue pairs that are provided by TestPriorityAttributes of the given test method.
|
||||
/// </summary>
|
||||
/// <param name="testPriority">The priority</param>
|
||||
/// <returns>The corresponding trait.</returns>
|
||||
internal virtual Trait GetTestPriorityAsTraits(
|
||||
int? testPriority
|
||||
)
|
||||
{
|
||||
if (testPriority == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Trait("Priority", ((int)testPriority).ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority if any set for test method. Will return priority if attribute is applied to TestMethod
|
||||
/// else null;
|
||||
/// </summary>
|
||||
/// <param name="priorityAttributeProvider">The member to inspect.</param>
|
||||
/// <returns>Priority value if defined. Null otherwise.</returns>
|
||||
internal virtual int? GetPriority(
|
||||
MemberInfo priorityAttributeProvider
|
||||
)
|
||||
{
|
||||
var priorityAttribute = GetCustomAttributes(priorityAttributeProvider, typeof(PriorityAttribute), true);
|
||||
|
||||
if (priorityAttribute == null || priorityAttribute.Length != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (priorityAttribute[0] as PriorityAttribute).Priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority if any set for test method. Will return priority if attribute is applied to TestMethod
|
||||
/// else null;
|
||||
/// </summary>
|
||||
/// <param name="ignoreAttributeProvider">The member to inspect.</param>
|
||||
/// <returns>Priority value if defined. Null otherwise.</returns>
|
||||
internal virtual string GetIgnoreMessage(
|
||||
MemberInfo ignoreAttributeProvider
|
||||
)
|
||||
{
|
||||
var ignoreAttribute = GetCustomAttributes(ignoreAttributeProvider, typeof(IgnoreAttribute), true);
|
||||
|
||||
if (!ignoreAttribute.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (ignoreAttribute?.FirstOrDefault() as IgnoreAttribute).IgnoreMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KeyValue pairs that are provided by TestPropertyAttributes of the given test method.
|
||||
/// </summary>
|
||||
/// <param name="testPropertyProvider">The member to inspect.</param>
|
||||
/// <returns>List of traits.</returns>
|
||||
internal virtual IEnumerable<Trait> GetTestPropertiesAsTraits(
|
||||
MemberInfo testPropertyProvider
|
||||
)
|
||||
{
|
||||
var testPropertyAttributes = this.GetTestPropertyAttributes(testPropertyProvider);
|
||||
|
||||
foreach (TestPropertyAttribute testProperty in testPropertyAttributes)
|
||||
{
|
||||
Trait testPropertyPair;
|
||||
if (testProperty.Name == null)
|
||||
{
|
||||
testPropertyPair = new Trait(string.Empty, testProperty.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
testPropertyPair = new Trait(testProperty.Name, testProperty.Value);
|
||||
}
|
||||
|
||||
yield return testPropertyPair;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get attribute defined on a method which is of given type of subtype of given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="AttributeType">The attribute type.</typeparam>
|
||||
/// <param name="memberInfo">The member to inspect.</param>
|
||||
/// <param name="inherit">Look at inheritance chain.</param>
|
||||
/// <returns>An instance of the attribute.</returns>
|
||||
internal AttributeType GetDerivedAttribute<AttributeType>(
|
||||
MemberInfo memberInfo, bool inherit
|
||||
)
|
||||
where AttributeType : Attribute
|
||||
{
|
||||
Dictionary<string, object> attributes = this.GetAttributes(memberInfo, inherit);
|
||||
if (attributes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to find the attribute that is derived from baseAttrType.
|
||||
foreach (object attribute in attributes.Values)
|
||||
{
|
||||
Debug.Assert(attribute != null, "ReflectHeler.DefinesAttributeDerivedFrom: internal error: wrong value in the attrs dictionary.");
|
||||
|
||||
Type attributeType = attribute.GetType();
|
||||
if (attributeType.Equals(typeof(AttributeType)) || attributeType.GetTypeInfo().IsSubclassOf(typeof(AttributeType)))
|
||||
{
|
||||
return attribute as AttributeType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get attribute defined on a method which is of given type of subtype of given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="AttributeType">The attribute type.</typeparam>
|
||||
/// <param name="type">The type to inspect.</param>
|
||||
/// <param name="inherit">Look at inheritance chain.</param>
|
||||
/// <returns>An instance of the attribute.</returns>
|
||||
internal AttributeType GetDerivedAttribute<AttributeType>(
|
||||
Type type,
|
||||
bool inherit
|
||||
)
|
||||
where AttributeType : Attribute
|
||||
{
|
||||
var attributes = GetCustomAttributes(type.GetTypeInfo(), inherit);
|
||||
if (attributes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to find the attribute that is derived from baseAttrType.
|
||||
foreach (object attribute in attributes)
|
||||
{
|
||||
Debug.Assert(attribute != null, "ReflectHeler.DefinesAttributeDerivedFrom: internal error: wrong value in the attrs dictionary.");
|
||||
|
||||
Type attributeType = attribute.GetType();
|
||||
if (attributeType.Equals(typeof(AttributeType)) || attributeType.GetTypeInfo().IsSubclassOf(typeof(AttributeType)))
|
||||
{
|
||||
return attribute as AttributeType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns owner if attribute is applied to TestMethod, else null;
|
||||
/// </summary>
|
||||
/// <param name="ownerAttributeProvider">The member to inspect.</param>
|
||||
/// <returns>owner if attribute is applied to TestMethod, else null;</returns>
|
||||
private string GetOwner(
|
||||
MemberInfo ownerAttributeProvider
|
||||
)
|
||||
{
|
||||
var ownerAttribute = GetCustomAttributes(ownerAttributeProvider, typeof(OwnerAttribute), true).ToArray();
|
||||
|
||||
if (ownerAttribute == null || ownerAttribute.Length != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (ownerAttribute[0] as OwnerAttribute).Owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return TestProperties attributes applied to TestMethod
|
||||
/// </summary>
|
||||
/// <param name="propertyAttributeProvider">The member to inspect.</param>
|
||||
/// <returns>TestProperty attributes if defined. Empty otherwise.</returns>
|
||||
private IEnumerable<Attribute> GetTestPropertyAttributes(
|
||||
MemberInfo propertyAttributeProvider
|
||||
)
|
||||
{
|
||||
return GetCustomAttributes(propertyAttributeProvider, typeof(TestPropertyAttribute), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Attributes (TypeName/TypeObject) for a given member.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">The member to inspect.</param>
|
||||
/// <param name="inherit">Look at inheritance chain.</param>
|
||||
/// <returns>attributes defined.</returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
private Dictionary<string, object> GetAttributes(
|
||||
MemberInfo memberInfo,
|
||||
bool inherit
|
||||
)
|
||||
{
|
||||
// If the information is cached, then use it otherwise populate the cache using
|
||||
// the reflection APIs.
|
||||
Dictionary<string, object> attributes;
|
||||
lock (this.attributeCache)
|
||||
{
|
||||
if (!this.attributeCache.TryGetValue(memberInfo, out attributes))
|
||||
{
|
||||
// Populate the cache
|
||||
attributes = new Dictionary<string, object>();
|
||||
|
||||
object[] customAttributesArray = null;
|
||||
try
|
||||
{
|
||||
customAttributesArray = GetCustomAttributes(memberInfo, inherit);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Get the exception description
|
||||
string description;
|
||||
try
|
||||
{
|
||||
// Can throw if the Message or StackTrace properties throw exceptions
|
||||
description = ex.ToString();
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
description =
|
||||
ex.GetType().FullName +
|
||||
": (Failed to get exception description due to an exception of type " +
|
||||
ex2.GetType().FullName + ')';
|
||||
}
|
||||
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogWarning(
|
||||
"Getting custom attributes for type {0} threw exception (will ignore and use the reflection way): {1}",
|
||||
memberInfo.GetType().FullName,
|
||||
description);
|
||||
|
||||
// Since we cannot check by attribute names, do it in reflection way.
|
||||
// Note 1: this will not work for different version of assembly but it is better than nothing.
|
||||
// Note 2: we cannot cache this because we don't know if there are other attributes defined.
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Assert(customAttributesArray != null, "attributes should not be null.");
|
||||
|
||||
foreach (object customAttribute in customAttributesArray)
|
||||
{
|
||||
Type attrType = customAttribute.GetType();
|
||||
attributes[attrType.AssemblyQualifiedName] = customAttribute;
|
||||
}
|
||||
|
||||
this.attributeCache.Add(memberInfo, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Execution
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides helper methods to parse stack trace.
|
||||
/// </summary>
|
||||
internal static class StackTraceHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Type that need to be excluded.
|
||||
/// </summary>
|
||||
private static List<string> typesToBeExcluded;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types whose methods should be ignored in the reported call stacks.
|
||||
/// This is used to remove our stack that the user will not care about.
|
||||
/// </summary>
|
||||
private static List<string> TypeToBeExcluded
|
||||
{
|
||||
get
|
||||
{
|
||||
if (typesToBeExcluded == null)
|
||||
{
|
||||
typesToBeExcluded = new List<string>();
|
||||
typesToBeExcluded.Add(typeof(Assert).Namespace);
|
||||
typesToBeExcluded.Add(typeof(TestExecutor).Namespace);
|
||||
}
|
||||
|
||||
return typesToBeExcluded;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stack trace for an exception, including all stack traces for inner
|
||||
/// exceptions.
|
||||
/// </summary>
|
||||
/// <param name="ex">
|
||||
/// The exception.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="StackTraceInformation"/> for the provided exception.
|
||||
/// </returns>
|
||||
internal static StackTraceInformation GetStackTraceInformation(Exception ex)
|
||||
{
|
||||
Debug.Assert(ex != null, "exception should not be null.");
|
||||
|
||||
Stack<string> stackTraces = new Stack<string>();
|
||||
|
||||
for (Exception curException = ex;
|
||||
curException != null;
|
||||
curException = curException.InnerException)
|
||||
{
|
||||
// TODO:Fix the shadow stack-trace used in Private Object
|
||||
// (Look-in Assertion.cs in the UnitTestFramework assembly)
|
||||
|
||||
// Sometimes the stacktrace can be null, but the inner stacktrace
|
||||
// contains information. We are not interested in null stacktraces
|
||||
// so we simply ignore this case
|
||||
try
|
||||
{
|
||||
if (curException.StackTrace != null)
|
||||
{
|
||||
stackTraces.Push(curException.StackTrace);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// curException.StackTrace can throw exception, Although MSDN doc doesn't say that.
|
||||
try
|
||||
{
|
||||
// try to get stacktrace
|
||||
if (e.StackTrace != null)
|
||||
{
|
||||
stackTraces.Push(e.StackTrace);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
PlatformServiceProvider.Instance.AdapterTraceLogger.LogError(
|
||||
"StackTraceHelper.GetStackTraceInformation: Failed to get stacktrace info.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
bool first = true;
|
||||
while (stackTraces.Count != 0)
|
||||
{
|
||||
result.Append(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"{0} {1}{2}",
|
||||
first ? string.Empty : (Resource.UTA_EndOfInnerExceptionTrace + Environment.NewLine),
|
||||
stackTraces.Pop(),
|
||||
Environment.NewLine));
|
||||
first = false;
|
||||
}
|
||||
|
||||
return CreateStackTraceInformation(ex, true, result.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all stack frames that refer to nanoFramework.TestPlatform.TestTools.UnitTesting.Assertion
|
||||
/// </summary>
|
||||
/// <param name="stackTrace">
|
||||
/// The stack Trace.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The trimmed stack trace removing traces of the framework and adapter from the stack.
|
||||
/// </returns>
|
||||
internal static string TrimStackTrace(string stackTrace)
|
||||
{
|
||||
Debug.Assert(stackTrace != null && stackTrace.Length > 0, "stack trace should be non-empty.");
|
||||
|
||||
StringBuilder result = new StringBuilder(stackTrace.Length);
|
||||
string[] stackFrames = Regex.Split(stackTrace, Environment.NewLine);
|
||||
|
||||
foreach (string stackFrame in stackFrames)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stackFrame))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the frame to the result if it does not refer to
|
||||
// the assertion class in the test framework
|
||||
bool hasReference = HasReferenceToUTF(stackFrame);
|
||||
if (!hasReference)
|
||||
{
|
||||
result.Append(stackFrame);
|
||||
result.Append(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception messages, including the messages for all inner exceptions
|
||||
/// recursively
|
||||
/// </summary>
|
||||
/// <param name="ex">
|
||||
/// The exception.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The aggregated exception message that considers inner exceptions.
|
||||
/// </returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
internal static string GetExceptionMessage(Exception ex)
|
||||
{
|
||||
Debug.Assert(ex != null, "exception should not be null.");
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
bool first = true;
|
||||
for (Exception curException = ex;
|
||||
curException != null;
|
||||
curException = curException.InnerException)
|
||||
{
|
||||
// Get the exception message. Need to check for errors because the Message property
|
||||
// may have been overridden by the exception type in user code.
|
||||
string msg;
|
||||
try
|
||||
{
|
||||
msg = curException.Message;
|
||||
}
|
||||
catch
|
||||
{
|
||||
msg = string.Format(CultureInfo.CurrentCulture, Resource.UTF_FailedToGetExceptionMessage, curException.GetType());
|
||||
}
|
||||
|
||||
result.Append(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"{0}{1}: {2}",
|
||||
first ? string.Empty : " ---> ",
|
||||
curException.GetType(),
|
||||
msg));
|
||||
first = false;
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create stack trace information
|
||||
/// </summary>
|
||||
/// <param name="ex">
|
||||
/// The exception.
|
||||
/// </param>
|
||||
/// <param name="checkInnerExceptions">
|
||||
/// Whether the inner exception needs to be checked too.
|
||||
/// </param>
|
||||
/// <param name="stackTraceString">
|
||||
/// The stack Trace String.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="StackTraceInformation"/>.
|
||||
/// </returns>
|
||||
internal static StackTraceInformation CreateStackTraceInformation(
|
||||
Exception ex,
|
||||
bool checkInnerExceptions,
|
||||
string stackTraceString)
|
||||
{
|
||||
if (checkInnerExceptions && ex.InnerException != null)
|
||||
{
|
||||
return CreateStackTraceInformation(ex.InnerException, checkInnerExceptions, stackTraceString);
|
||||
}
|
||||
|
||||
var stackTrace = StackTraceHelper.TrimStackTrace(stackTraceString);
|
||||
|
||||
if (!string.IsNullOrEmpty(stackTrace))
|
||||
{
|
||||
return new StackTraceInformation(stackTrace, null, 0, 0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the parameter stackFrame has reference to UTF
|
||||
/// </summary>
|
||||
/// <param name="stackFrame">
|
||||
/// The stack Frame.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the framework or the adapter methods are in the stack frame.
|
||||
/// </returns>
|
||||
internal static bool HasReferenceToUTF(string stackFrame)
|
||||
{
|
||||
foreach (var type in TypeToBeExcluded)
|
||||
{
|
||||
if (stackFrame.IndexOf(type, StringComparison.Ordinal) > -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Helpers
|
||||
{
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
|
||||
internal static class UnitTestOutcomeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the parameter unitTestOutcome to testOutcome
|
||||
/// </summary>
|
||||
/// <param name="unitTestOutcome"> The unit Test Outcome. </param>
|
||||
/// <param name="mapInconclusiveToFailed">Should map inconclusive to failed.</param>
|
||||
/// <returns>The Test platforms outcome.</returns>
|
||||
internal static TestOutcome ToTestOutcome(UnitTestOutcome unitTestOutcome, bool mapInconclusiveToFailed)
|
||||
{
|
||||
switch (unitTestOutcome)
|
||||
{
|
||||
case UnitTestOutcome.Passed:
|
||||
return TestOutcome.Passed;
|
||||
|
||||
case UnitTestOutcome.Failed:
|
||||
case UnitTestOutcome.Error:
|
||||
case UnitTestOutcome.Timeout:
|
||||
return TestOutcome.Failed;
|
||||
|
||||
case UnitTestOutcome.NotRunnable:
|
||||
return TestOutcome.None;
|
||||
|
||||
case UnitTestOutcome.Ignored:
|
||||
return TestOutcome.Skipped;
|
||||
|
||||
case UnitTestOutcome.Inconclusive:
|
||||
{
|
||||
if (mapInconclusiveToFailed)
|
||||
{
|
||||
return TestOutcome.Failed;
|
||||
}
|
||||
|
||||
return TestOutcome.Skipped;
|
||||
}
|
||||
|
||||
case UnitTestOutcome.NotFound:
|
||||
return TestOutcome.NotFound;
|
||||
|
||||
default:
|
||||
return TestOutcome.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Resources;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Provides helper functionality for the unit test framework
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
internal static class UtfHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the exception messages, including the messages for all inner exceptions
|
||||
/// recursively
|
||||
/// </summary>
|
||||
/// <param name="ex">Exception to get messages for</param>
|
||||
/// <returns>string with error message information</returns>
|
||||
internal static string GetExceptionMsg(Exception ex)
|
||||
{
|
||||
Debug.Assert(ex != null, "exception is null");
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
bool first = true;
|
||||
for (Exception curException = ex;
|
||||
curException != null;
|
||||
curException = curException.InnerException)
|
||||
{
|
||||
// Get the exception message. Need to check for errors because the Message property
|
||||
// may have been overridden by the exception type in user code.
|
||||
string msg;
|
||||
try
|
||||
{
|
||||
msg = curException.Message;
|
||||
}
|
||||
catch
|
||||
{
|
||||
msg = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resource.UTF_FailedToGetExceptionMessage,
|
||||
curException.GetType());
|
||||
}
|
||||
|
||||
result.Append(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
"{0}{1}: {2}",
|
||||
first ? string.Empty : " ---> ",
|
||||
curException.GetType(),
|
||||
msg));
|
||||
first = false;
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.PlatformServices.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// A service to log any trace messages from the adapter that would be shown in *.TpTrace files.
|
||||
/// </summary>
|
||||
public interface IAdapterTraceLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Log an error in a given format.
|
||||
/// </summary>
|
||||
/// <param name="format"> The format. </param>
|
||||
/// <param name="args"> The args. </param>
|
||||
void LogError(
|
||||
string format,
|
||||
params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Log a warning in a given format.
|
||||
/// </summary>
|
||||
/// <param name="format"> The format. </param>
|
||||
/// <param name="args"> The args. </param>
|
||||
void LogWarning(
|
||||
string format,
|
||||
params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Log an information message in a given format.
|
||||
/// </summary>
|
||||
/// <param name="format"> The format. </param>
|
||||
/// <param name="args"> The args. </param>
|
||||
void LogInfo(
|
||||
string format,
|
||||
params object[] args);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interfaces
|
||||
{
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Abstraction for Assembly Methods
|
||||
/// </summary>
|
||||
public interface IAssemblyLoadContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads assembly from given path
|
||||
/// </summary>
|
||||
/// <param name="assemblyPath">Assembly path</param>
|
||||
/// <returns>Assembly from given path</returns>
|
||||
Assembly LoadAssemblyFromPath(string assemblyPath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets Assembly Name from given path
|
||||
/// </summary>
|
||||
/// <param name="assemblyPath">Assembly path</param>
|
||||
/// <returns>AssemblyName from given path</returns>
|
||||
AssemblyName GetAssemblyNameFromPath(string assemblyPath);
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// This service is responsible for any file based operations.
|
||||
/// </summary>
|
||||
public interface IFileOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads an assembly into the current context.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">
|
||||
/// The name of the assembly.
|
||||
/// </param>
|
||||
/// <param name="isReflectionOnly">
|
||||
/// Indicates whether this should be a reflection only load.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A handle to the loaded assembly.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// A platform can choose how it wants the assembly loaded. (ReflectionOnlyLoad/Load etc).
|
||||
/// </remarks>
|
||||
Assembly LoadAssembly(string assemblyName, bool isReflectionOnly);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the .DLL of the assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly.</param>
|
||||
/// <returns>Path to the .DLL of the assembly.</returns>
|
||||
string GetAssemblyPath(Assembly assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Verify if a file exists in the current context.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFileName"> The assembly file name. </param>
|
||||
/// <returns> true if the file exists. </returns>
|
||||
bool DoesFileExist(string assemblyFileName);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Navigation session for the source file.
|
||||
/// This is used to get file path and line number information for its components.
|
||||
/// </summary>
|
||||
/// <param name="source"> The source file. </param>
|
||||
/// <returns> A Navigation session instance for the current platform. </returns>
|
||||
/// <remarks>
|
||||
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
|
||||
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
|
||||
/// </remarks>
|
||||
object CreateNavigationSession(string source);
|
||||
|
||||
/// <summary>
|
||||
/// Get's the navigation data for a navigation session.
|
||||
/// </summary>
|
||||
/// <param name="navigationSession"> The navigation session. </param>
|
||||
/// <param name="className"> The class name. </param>
|
||||
/// <param name="methodName"> The method name. </param>
|
||||
/// <param name="minLineNumber"> The min line number. </param>
|
||||
/// <param name="fileName"> The file name. </param>
|
||||
/// <remarks>
|
||||
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
|
||||
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
|
||||
/// </remarks>
|
||||
void GetNavigationData(object navigationSession, string className, string methodName, out int minLineNumber, out string fileName);
|
||||
|
||||
/// <summary>
|
||||
/// Dispose's the navigation session instance.
|
||||
/// </summary>
|
||||
/// <param name="navigationSession"> The navigation session. </param>
|
||||
/// <remarks>
|
||||
/// Unfortunately we cannot use INavigationSession introduced in Object Model in Dev14 update-2 because
|
||||
/// the adapter needs to work with older VS versions as well where this new type would not be defined resulting in a type not found exception.
|
||||
/// </remarks>
|
||||
void DisposeNavigationSession(object navigationSession);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full file path of an assembly file.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFileName"> The file name. </param>
|
||||
/// <returns> The full file path. </returns>
|
||||
string GetFullFilePath(string assemblyFileName);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.Interface;
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.PlatformServices.Interface;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// The definition of a PlatformServiceProvider with a hook to all the services.
|
||||
/// </summary>
|
||||
internal interface IPlatformServiceProvider
|
||||
{
|
||||
///// <summary>
|
||||
///// Gets an instance to the platform service validator for test sources.
|
||||
///// </summary>
|
||||
ITestSource TestSource { get; }
|
||||
|
||||
///// <summary>
|
||||
///// Gets an instance to the platform service to data drive a test.
|
||||
///// </summary>
|
||||
//ITestDataSource TestDataSource { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance to the platform service for file operations.
|
||||
/// </summary>
|
||||
IFileOperations FileOperations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance to the platform service for trace logging.
|
||||
/// </summary>
|
||||
IAdapterTraceLogger AdapterTraceLogger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of the test deployment service.
|
||||
/// </summary>
|
||||
ITestDeployment TestDeployment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance to the platform service for a Settings Provider.
|
||||
/// </summary>
|
||||
ISettingsProvider SettingsProvider { get; }
|
||||
|
||||
///// <summary>
|
||||
///// Gets an instance to the platform service for thread operations.
|
||||
///// </summary>
|
||||
//IThreadOperations ThreadOperations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance to the platform service for reflection operations specific to a platform.
|
||||
/// </summary>
|
||||
IReflectionOperations ReflectionOperations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance to the platform service for a test source host.
|
||||
/// </summary>
|
||||
/// <param name="source">
|
||||
/// The source.
|
||||
/// </param>
|
||||
/// <param name="runSettings">
|
||||
/// The run Settings for the session.
|
||||
/// </param>
|
||||
/// <param name="frameworkHandle">
|
||||
/// The handle to the the test platform.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns the host for the source provided.
|
||||
/// </returns>
|
||||
ITestSourceHost CreateTestSourceHost(
|
||||
string source,
|
||||
Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunSettings runSettings,
|
||||
Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IFrameworkHandle frameworkHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance to the platform service listener who monitors trace and debug output
|
||||
/// on provided text writer.
|
||||
/// </summary>
|
||||
/// <param name="textWriter">
|
||||
/// The text Writer.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ITraceListener"/>.
|
||||
/// </returns>
|
||||
ITraceListener GetTraceListener(TextWriter textWriter);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance to the platform service trace-listener manager which updates the output/error streams
|
||||
/// with redirected streams and performs operations on the listener provided as argument.
|
||||
/// </summary>
|
||||
/// <param name="outputWriter">
|
||||
/// The redirected output stream writer.
|
||||
/// </param>
|
||||
/// <param name="errorWriter">
|
||||
/// The redirected error stream writer.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The manager for trace listeners.
|
||||
/// </returns>
|
||||
ITraceListenerManager GetTraceListenerManager(TextWriter outputWriter, TextWriter errorWriter);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TestContext object for a platform.
|
||||
/// </summary>
|
||||
/// <param name="testMethod">
|
||||
/// The test method.
|
||||
/// </param>
|
||||
/// <param name="writer">
|
||||
/// The writer instance for logging.
|
||||
/// </param>
|
||||
/// <param name="properties">
|
||||
/// The default set of properties the test context needs to be filled with.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ITestContext"/> instance.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
|
||||
/// </remarks>
|
||||
ITestContext GetTestContext(ObjectModel.ITestMethod testMethod, StringWriter writer, IDictionary<string, object> properties);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// This service is responsible for platform specific reflection operations.
|
||||
/// </summary>
|
||||
public interface IReflectionOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all the custom attributes adorned on a member.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo"> The member. </param>
|
||||
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
|
||||
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
|
||||
object[] GetCustomAttributes(MemberInfo memberInfo, bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the custom attributes of a given type adorned on a member.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo"> The member info. </param>
|
||||
/// <param name="type"> The attribute type. </param>
|
||||
/// <param name="inherit"> True to inspect the ancestors of element; otherwise, false. </param>
|
||||
/// <returns> The list of attributes on the member. Empty list if none found. </returns>
|
||||
object[] GetCustomAttributes(MemberInfo memberInfo, Type type, bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the custom attributes of a given type on an assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly"> The assembly. </param>
|
||||
/// <param name="type"> The attribute type. </param>
|
||||
/// <returns> The list of attributes of the given type on the member. Empty list if none found. </returns>
|
||||
object[] GetCustomAttributes(Assembly assembly, Type type);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
|
||||
/// <summary>
|
||||
/// To read settings from the runsettings xml for the corresponding platform service.
|
||||
/// </summary>
|
||||
public interface ISettingsProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Load settings from the xml reader instance which are specific
|
||||
/// for the corresponding platform service.
|
||||
/// </summary>
|
||||
/// <param name="reader">
|
||||
/// Reader that can be used to read current node and all its descendants,
|
||||
/// to load the settings from.</param>
|
||||
void Load(XmlReader reader);
|
||||
|
||||
/// <summary>
|
||||
/// The set of properties/settings specific to the platform, that will be surfaced to the user through the test context.
|
||||
/// </summary>
|
||||
/// <param name="source">
|
||||
/// source is used to find application base directory used for setting test context properties.
|
||||
/// </param>
|
||||
/// <returns>Properties specific to the platform.</returns>
|
||||
IDictionary<string, object> GetProperties(string source);
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using nanoFramework.TestPlatform.MSTest.TestAdapter.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Operations on the TestContext object that is implemented differently for each platform.
|
||||
/// </summary>
|
||||
public interface ITestContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the inner test context object.
|
||||
/// </summary>
|
||||
TestContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether property with parameter name is present.
|
||||
/// </summary>
|
||||
/// <param name="propertyName"> The property Name. </param>
|
||||
/// <param name="propertyValue"> The property Value. </param>
|
||||
/// <returns> True if the property is present. </returns>
|
||||
bool TryGetPropertyValue(string propertyName, out object propertyValue);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the parameter name/value pair to property bag.
|
||||
/// </summary>
|
||||
/// <param name="propertyName"> The property Name. </param>
|
||||
/// <param name="propertyValue"> The property Value. </param>
|
||||
void AddProperty(string propertyName, string propertyValue);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the outcome of a Test Method in the TestContext.
|
||||
/// </summary>
|
||||
/// <param name="outcome"> The outcome. </param>
|
||||
void SetOutcome(UnitTestOutcome outcome);
|
||||
|
||||
/// <summary>
|
||||
/// Set data row for particular run of TestMethod.
|
||||
/// </summary>
|
||||
/// <param name="dataRow">data row</param>
|
||||
void SetDataRow(object dataRow);
|
||||
|
||||
/// <summary>
|
||||
/// Set connection for TestContext
|
||||
/// </summary>
|
||||
/// <param name="dbConnection">db Connection.</param>
|
||||
void SetDataConnection(object dbConnection);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attached Result files
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The list of result files.
|
||||
/// </returns>
|
||||
IList<string> GetResultFiles();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diagnostics messages written to TestContext.WriteLine()
|
||||
/// </summary>
|
||||
/// <returns>The test context messages added so far.</returns>
|
||||
string GetDiagnosticMessages();
|
||||
|
||||
/// <summary>
|
||||
/// Clears the previous testContext writeline messages.
|
||||
/// </summary>
|
||||
void ClearDiagnosticMessages();
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
|
||||
/// <summary>
|
||||
/// The TestDeployment interface.
|
||||
/// </summary>
|
||||
public interface ITestDeployment
|
||||
{
|
||||
/// <summary>
|
||||
/// Deploy deployment items for the specified test cases.
|
||||
/// </summary>
|
||||
/// <param name="testCases"> The test cases. </param>
|
||||
/// <param name="runContext"> The run context. </param>
|
||||
/// <param name="frameworkHandle"> The framework handle. </param>
|
||||
/// <returns> True if deployment is done. </returns>
|
||||
bool Deploy(IEnumerable<TestCase> testCases, IRunContext runContext, IFrameworkHandle frameworkHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of deployment items on a method and its corresponding class.
|
||||
/// </summary>
|
||||
/// <param name="method"> The method. </param>
|
||||
/// <param name="type"> The type. </param>
|
||||
/// <param name="warnings"> The warnings. </param>
|
||||
/// <returns> A KeyValuePair of deployment items. </returns>
|
||||
KeyValuePair<string, string>[] GetDeploymentItems(MethodInfo method, Type type, ICollection<string> warnings);
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup deployment item directories.
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deployment output directory where the source file along with all its dependencies is dropped.
|
||||
/// </summary>
|
||||
/// <returns> The deployment output directory. </returns>
|
||||
string GetDeploymentDirectory();
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// TestMethod for execution.
|
||||
/// </summary>
|
||||
public partial interface ITestMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of test method.
|
||||
/// </summary>
|
||||
string TestMethodName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of test class.
|
||||
/// </summary>
|
||||
string TestClassName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the return type of test method.
|
||||
/// </summary>
|
||||
Type ReturnType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the arguments with which test method is invoked.
|
||||
/// </summary>
|
||||
object[] Arguments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameters of test method.
|
||||
/// </summary>
|
||||
ParameterInfo[] ParameterTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the methodInfo for test method.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is just to retrieve additional information about the method.
|
||||
/// Do not directly invoke the method using MethodInfo. Use ITestMethod.Invoke instead.
|
||||
/// </remarks>
|
||||
MethodInfo MethodInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get all attributes of the test method.
|
||||
/// </summary>
|
||||
/// <param name="inherit">
|
||||
/// Whether attribute defined in parent class is valid.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// All attributes.
|
||||
/// </returns>
|
||||
Attribute[] GetAllAttributes(bool inherit);
|
||||
|
||||
/// <summary>
|
||||
/// Get attribute of specific type.
|
||||
/// </summary>
|
||||
/// <typeparam name="AttributeType"> System.Attribute type. </typeparam>
|
||||
/// <param name="inherit">
|
||||
/// Whether attribute defined in parent class is valid.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The attributes of the specified type.
|
||||
/// </returns>
|
||||
AttributeType[] GetAttributes<AttributeType>(bool inherit)
|
||||
where AttributeType : Attribute;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// This platform service is responsible for any data or operations to validate
|
||||
/// the test sources provided to the adapter.
|
||||
/// </summary>
|
||||
public interface ITestSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the set of valid extensions for sources targeting this platform.
|
||||
/// </summary>
|
||||
IEnumerable<string> ValidSourceExtensions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Verifies if the assembly provided is referenced by the source.
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"> The assembly name. </param>
|
||||
/// <param name="source"> The source. </param>
|
||||
/// <returns> True if the assembly is referenced. </returns>
|
||||
bool IsAssemblyReferenced(AssemblyName assemblyName, string source);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of sources (dll's/exe's) that contain tests.
|
||||
/// </summary>
|
||||
/// <param name="sources"> Sources given to the adapter. </param>
|
||||
/// <returns> Sources that contains tests. </returns>
|
||||
IEnumerable<string> GetTestSources(IEnumerable<string> sources);
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public interface ITestSourceHost : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets up the isolation host.
|
||||
/// </summary>
|
||||
void SetupHost();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of a given type in the test source host.
|
||||
/// </summary>
|
||||
/// <param name="type"> The type that needs to be created in the host. </param>
|
||||
/// <param name="args">The arguments to pass to the constructor.
|
||||
/// This array of arguments must match in number, order, and type the parameters of the constructor to invoke.
|
||||
/// Pass in null for a constructor with no arguments.
|
||||
/// </param>
|
||||
/// <returns> An instance of the type created in the host. </returns>
|
||||
/// <remarks> If a type is to be created in isolation then it needs to be a MarshalByRefObject. </remarks>
|
||||
object CreateInstanceForType(Type type, object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Updates child-domain's appbase to point to test source location
|
||||
/// </summary>
|
||||
void UpdateAppBaseToTestSourceLocation();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Operations on the TraceListener object that is implemented differently for each platform.
|
||||
/// </summary>
|
||||
public interface ITraceListener
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the text writer that receives the tracing or debugging output.
|
||||
/// </summary>
|
||||
/// <returns>The writer instance.</returns>
|
||||
TextWriter GetWriter();
|
||||
|
||||
/// <summary>
|
||||
/// Disposes this TraceListener object.
|
||||
/// </summary>
|
||||
void Dispose();
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Interface
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Manager to perform operations on the TraceListener object passed as parameter.
|
||||
/// These operations are implemented differently for each platform service.
|
||||
/// </summary>
|
||||
public interface ITraceListenerManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the arguement traceListener object to TraceListenerCollection.
|
||||
/// </summary>
|
||||
/// <param name="traceListener">The trace listener instance.</param>
|
||||
void Add(ITraceListener traceListener);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the arguement traceListener object from TraceListenerCollection.
|
||||
/// </summary>
|
||||
/// <param name="traceListener">The trace listener instance.</param>
|
||||
void Remove(ITraceListener traceListener);
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the traceListener object passed as arguement.
|
||||
/// </summary>
|
||||
/// <param name="traceListener">The trace listener instance.</param>
|
||||
void Dispose(ITraceListener traceListener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
|
||||
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
|
||||
using nanoFramework.TestPlatform.TestAdapter;
|
||||
|
||||
namespace nanoFramework.TestAdapter
|
||||
{
|
||||
internal class LogMessenger
|
||||
{
|
||||
private IFrameworkHandle _frameworkHandle = null;
|
||||
private IMessageLogger _logger = null;
|
||||
private Settings _settings = null;
|
||||
|
||||
/// <summary>
|
||||
/// A log messenger to log during the discovery and executor process
|
||||
/// </summary>
|
||||
/// <param name="frameworkHandle">The framework handle</param>
|
||||
/// <param name="provider">The settings provider</param>
|
||||
public LogMessenger(
|
||||
IFrameworkHandle frameworkHandle,
|
||||
SettingsProvider provider)
|
||||
{
|
||||
_frameworkHandle = frameworkHandle;
|
||||
if (provider != null)
|
||||
{
|
||||
_settings = provider.Settings;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A log messenger to log during the discovery and executor process
|
||||
/// </summary>
|
||||
/// <param name="logger">A platform logger</param>
|
||||
/// <param name="provider">The settings provider</param>
|
||||
|
||||
public LogMessenger(
|
||||
IMessageLogger logger,
|
||||
SettingsProvider provider)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
if (provider != null)
|
||||
{
|
||||
_settings = provider.Settings;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a panic message
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public void LogPanicMessage(
|
||||
string message)
|
||||
{
|
||||
LogMessage(
|
||||
message,
|
||||
Settings.LoggingLevel.Error,
|
||||
true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
/// <param name="logLevel">The log level</param>
|
||||
/// <param name="panicMessage">Is it a panic message</param>
|
||||
public void LogMessage(
|
||||
string message,
|
||||
Settings.LoggingLevel logLevel,
|
||||
bool panicMessage = false)
|
||||
{
|
||||
if (logLevel >= _settings?.Logging)
|
||||
{
|
||||
_frameworkHandle?.SendMessage(
|
||||
TestMessageLevel.Informational,
|
||||
$"[nanoTestAdapter]: {message}");
|
||||
|
||||
_logger?.SendMessage(
|
||||
TestMessageLevel.Informational,
|
||||
$"[nanoTestAdapter]: {message}");
|
||||
}
|
||||
else if (panicMessage)
|
||||
{
|
||||
_frameworkHandle?.SendMessage(
|
||||
TestMessageLevel.Error,
|
||||
$"[nanoTestAdapter] **PANIC**: {message}");
|
||||
_logger?.SendMessage(
|
||||
TestMessageLevel.Error,
|
||||
$"[nanoTestAdapter] **PANIC**: {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 The nanoFramework project contributors
|
||||
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace nanoFramework.TestPlatform.MSTest.TestAdapter.Logging
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Enables users to log/write traces from unit tests for diagnostics.
|
||||
/// </summary>
|
||||
public class Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// Handler for LogMessage.
|
||||
/// </summary>
|
||||
/// <param name="message">Message to log.</param>
|
||||
public delegate void LogMessageHandler(string message);
|
||||
|
||||
/// <summary>
|
||||
/// Event to listen. Raised when unit test writer writes some message.
|
||||
/// Mainly to consume by adapter.
|
||||
/// </summary>
|
||||
public static event LogMessageHandler OnLogMessage;
|
||||
|
||||
/// <summary>
|
||||
/// API for test writer to call to Log messages.
|
||||
/// </summary>
|
||||
/// <param name="format">String format with placeholders.</param>
|
||||
/// <param name="args">Parameters for placeholders.</param>
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")]
|
||||
public static void LogMessage(string format, params object[] args)
|
||||
{
|
||||
if (OnLogMessage != null)
|
||||
{
|
||||
if (format == null)
|
||||
{
|
||||
throw new ArgumentNullException("format");
|
||||
}
|
||||
|
||||
string message = string.Format(CultureInfo.InvariantCulture, format, args);
|
||||
|
||||
// Making sure all event handlers are called in sunc on same thread.
|
||||
foreach (var invoker in OnLogMessage.GetInvocationList())
|
||||
{
|
||||
try
|
||||
{
|
||||
invoker.GetMethodInfo().Invoke(invoker.Target, new object[] { message });
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Catch and ignore all exceptions thrown by event handlers.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче