├── .gitattributes ├── .github └── workflows │ └── main.yaml ├── .gitignore ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.md ├── NuGet.Config ├── README.md ├── VsixTesting.png ├── VsixTesting.sln ├── appveyor.yml ├── codecov.yml ├── key.snk ├── src ├── VsixTesting.Installer │ ├── Program.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── References │ │ └── Microsoft.VisualStudio.ExtensionManager.dll │ ├── VsixTesting.Installer.Common.csproj │ ├── VsixTesting.Installer.csproj │ ├── VsixTesting.Installer.props │ └── VsixTesting.Installer.x64.csproj ├── VsixTesting.Invoker │ ├── 17 │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── VsixTesting.Invoker.17.csproj │ │ └── source.extension.vsixmanifest │ ├── InvokerPackage.cs │ ├── InvokerService.cs │ ├── Properties │ │ └── launchSettings.json │ ├── VsixTesting.Invoker.csproj │ └── source.extension.vsixmanifest ├── VsixTesting.Xunit │ ├── Instances.cs │ ├── Internal │ │ ├── Diagnostics.cs │ │ ├── RemoteTestAssemblyRunner.cs │ │ ├── Utilities │ │ │ ├── ExceptionRunner.cs │ │ │ ├── ExceptionTestCase.cs │ │ │ ├── FakeException.cs │ │ │ ├── IFinishedMessageExtensions.cs │ │ │ ├── SerializableRunSummary.cs │ │ │ └── XunitSerializationInfo.cs │ │ ├── VsInstanceTestAssemblyRunner.cs │ │ ├── VsInstanceTestCase.cs │ │ ├── VsSkippedDataRowTestCase.cs │ │ ├── VsTestAssemblyRunner.cs │ │ ├── VsTestCase.cs │ │ ├── VsTestCaseBase.cs │ │ ├── VsTestCaseFactory.cs │ │ ├── VsTestCaseRunner.cs │ │ ├── VsTestFrameworkExecutor.cs │ │ ├── VsTestInvoker.cs │ │ ├── VsTestRunner.cs │ │ ├── VsTestSettingsUtil.cs │ │ ├── VsTheoryTestCase.cs │ │ └── VsTheoryTestCaseRunner.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── VsFactAttribute.cs │ ├── VsFactDiscoverer.cs │ ├── VsTestFramework.cs │ ├── VsTestSettingsAttribute.cs │ ├── VsTheoryAttribute.cs │ ├── VsTheoryDiscoverer.cs │ ├── VsixTesting.Xunit.csproj │ ├── VsixTesting.Xunit.props │ └── VsixTesting.Xunit.targets └── VsixTesting │ ├── Common │ ├── IAsyncDisposable.cs │ ├── ProcessExtensions.cs │ ├── RunningObjectTable.cs │ ├── TempFile.cs │ ├── ThreadUtil.cs │ └── VersionExtensions.cs │ ├── GlobalSuppressions.cs │ ├── IDiagnostics.cs │ ├── ITestSettings.cs │ ├── Interop │ ├── Kernel32.cs │ ├── Ntdll.cs │ ├── Ole32.cs │ ├── Psapi.cs │ └── User32.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Remote.cs │ ├── Remoting │ ├── ChannelUtil.cs │ └── IRemoteComInvoker.cs │ ├── Utilities │ ├── EmbeddedResourceUtil.cs │ ├── KillProcessJob.cs │ ├── ProcessUtil.cs │ ├── RetryMessageFilter.cs │ └── ScreenshotUtil.cs │ ├── Vs │ ├── VersionRange.cs │ ├── VersionUtil.cs │ ├── VisualStudioUtil.Installer.cs │ ├── VisualStudioUtil.cs │ ├── VsHive.cs │ ├── VsInstallation.cs │ └── VsVersion.cs │ ├── VsInstance.cs │ ├── VsTestSettings.cs │ └── VsixTesting.csproj ├── stylecop.json ├── test ├── VsixTesting.Installer.Tests │ └── VsixTesting.Installer.Tests.csproj ├── VsixTesting.Tests │ ├── Common │ │ ├── ThreadUtilTests.cs │ │ └── VersionExtensions.cs │ ├── GlobalSuppressions.cs │ ├── Vs │ │ ├── VersionRangeTests.cs │ │ ├── VersionTests.cs │ │ └── VisualStudioUtilTests.cs │ └── VsixTesting.Tests.csproj └── VsixTesting.Xunit.Tests │ ├── AssemblyTests.cs │ ├── ClassFixtureTests.cs │ ├── CollectionFixtureTests.cs │ ├── InstancesTests.cs │ ├── Internal │ ├── ExceptionTestCaseTests.cs │ ├── VsInstanceTestCaseTests.cs │ ├── VsSkippedDataRowTestCaseTests.cs │ └── VsTestCaseTests.cs │ ├── ProjectReferenceTests.cs │ ├── Properties │ └── launchSettings.json │ ├── Util.cs │ ├── Utilities │ └── AsyncTestSyncContextExtensions.cs │ ├── VsFactTests.cs │ ├── VsTestCaseFactoryTests.cs │ ├── VsTheoryTests.cs │ └── VsixTesting.Xunit.Tests.csproj └── version.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository 8 | 9 | strategy: 10 | matrix: 11 | os: ["windows-2019", "windows-2022"] 12 | 13 | runs-on: ${{ matrix.os }} 14 | env: 15 | CONFIGURATION: Release 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | fetch-depth: '0' 21 | 22 | - name: Add msbuild to PATH 23 | uses: microsoft/setup-msbuild@v1.1 24 | 25 | - name: Set Version 26 | run: | 27 | mkdir artifacts 28 | dotnet tool install -g nbgv 29 | nbgv cloud --all-vars 30 | 31 | - name: Restore VsixTesting.Xunit 32 | run: msbuild src/VsixTesting.Xunit /v:m /nologo /t:restore 33 | 34 | - name: Build VsixTesting.Xunit 35 | run: msbuild src/VsixTesting.Xunit /v:m /nologo 36 | 37 | - name: Restore 38 | run: msbuild /v:m /nologo /t:restore 39 | 40 | - name: Build 41 | run: msbuild /v:m /nologo 42 | 43 | - name: Test 44 | run: msbuild /t:test 45 | 46 | - name: Upload nuget packages 47 | if: matrix.os == 'windows-2022' 48 | uses: actions/upload-artifact@v2 49 | with: 50 | name: VsixTesting 51 | path: | 52 | artifacts/*.nupkg 53 | 54 | - name: Upload test results 55 | uses: actions/upload-artifact@v2 56 | with: 57 | name: VsixTesting.TestResults.${{matrix.os}} 58 | path: | 59 | artifacts/*.TestResults.xml 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | # 264 | Local.targets 265 | gitignore 266 | init.cmd -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(MSBuildThisFileDirectory) 4 | Debug 5 | 6 | 7 | 8 | $(SolutionDir)obj\$(MSBuildProjectName)\ 9 | $(SolutionDir)bin\$(MSBuildProjectName)\$(Configuration)\ 10 | $(SolutionDir)key.snk 11 | $(USERPROFILE)\.nuget\packages\ 12 | $(SolutionDir)artifacts 13 | latest 14 | false 15 | false 16 | false 17 | true 18 | 19 | 20 | 21 | Apache-2.0 22 | https://github.com/josetr/VsixTesting 23 | https://github.com/josetr/VsixTesting 24 | git 25 | VisualStudio, Extension, Testing, Xunit, VSIX 26 | © Jose Torres. All Rights Reserved. 27 | Jose Torres 28 | VsixTesting 29 | VsixTesting allows you to easily test your Visual Studio Extensions. 30 | 31 | 32 | 33 | $(DefineConstants);CI 34 | 35 | 36 | 37 | 38 | $(SolutionDir)bin\VsixTesting.Xunit\$(Configuration) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.3.1 5 | 4.6.519 6 | 1.0.5 7 | 3.1.2 8 | 1.1.0-beta2-21168-01 9 | 1.2.0.1 10 | $(GlobalPackagesPath)opencover\$(OpenCoverVersion)\tools\opencover.console.exe 11 | $(GlobalPackagesPath)codecov\$(CodeCovVersion)\tools\codecov.exe 12 | $(GlobalPackagesPath)reportgenerator\$(ReportGeneratorVersion)\tools\ReportGenerator.exe 13 | $(GlobalPackagesPath)Microsoft.DiaSymReader.Pdb2Pdb\$(Pdb2PdbVersion)\tools\Pdb2Pdb.exe 14 | $(GlobalPackagesPath)InheritDoc\$(InheritDocVersion)\tools\InheritDoc.exe 15 | true 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | $(SolutionDir)artifacts\ 38 | $(SolutionDir)artifacts\ 39 | $(TestResultsOutputDirectory)$(AssemblyName).TestResults.xml 40 | $(CoverageOutputDirectory)$(AssemblyName).Coverage.xml 41 | $(CoverageOutputDirectory)$(AssemblyName).Coverage 42 | @(TestAssemblies->'%(FullPath)', ' ') -stoponfail -noshadow -appveyor -diagnostics -xml $(TestResultsOutputXmlPath) -notrait VisualStudio=2012FilterTest 43 | 44 | 45 | 49 | 50 | 60 | 61 | 66 | 67 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Embedded Resource " + path + " doesn't exist."); 110 | else 111 | { 112 | var item = new XElement("Item"); 113 | item.Add(new XAttribute("Name", Path.GetFileName(path))); 114 | item.Add(new XAttribute("CreationTime", File.GetCreationTime(path).Ticks)); 115 | item.Add(new XAttribute("LastWriteTime", File.GetLastWriteTime(path).Ticks)); 116 | item.Add(new XAttribute("LastAccessTime", File.GetLastAccessTime(path).Ticks)); 117 | items.Add(item); 118 | } 119 | } 120 | 121 | new XDocument(items).Save(SaveTo); 122 | ]]> 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Jose Torres. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VsixTesting 2 | [![#](https://img.shields.io/nuget/v/VsixTesting.Xunit.svg?style=flat)](http://www.nuget.org/packages/VsixTesting.Xunit/) 3 | [![Join the chat at https://gitter.im/josetr/VsixTesting](https://badges.gitter.im/josetr/VsixTesting.svg)](https://gitter.im/josetr/VsixTesting) 4 | [![CI](https://github.com/josetr/VsixTesting/actions/workflows/main.yaml/badge.svg)](https://github.com/josetr/VsixTesting/actions) 5 | [![Build status](https://ci.appveyor.com/api/projects/status/github/josetr/VsixTesting?branch=master&svg=true)](https://ci.appveyor.com/project/josetr/vsixtesting/branch/master) 6 | [![codecov](https://codecov.io/gh/josetr/VsixTesting/branch/master/graph/badge.svg)](https://codecov.io/gh/josetr/VsixTesting) 7 | [![MyGet](https://img.shields.io/myget/vsixtesting/v/VsixTesting.Xunit.svg)](https://www.myget.org/feed/vsixtesting/package/nuget/VsixTesting.Xunit) 8 | 9 | VsixTesting allows you to easily test your Visual Studio Extensions. 10 | 11 | ![Image](https://raw.githubusercontent.com/josetr/VsixTesting/master/VsixTesting.png) 12 | 13 | It also supports Visual Studio 2019 / 2022. 14 | 15 | ## Getting Started 16 | 17 | The fastest way to get started is cloning the [VsixTestingSamples](https://github.com/josetr/VsixTestingSamples) repo and playing with it. 18 | 19 | `git clone https://github.com/josetr/VsixTestingSamples` 20 | 21 | ## Create the test project 22 | 23 | TestProject.csproj 24 | ```xml 25 | 26 | 27 | net461 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 51 | 52 | 53 | ``` 54 | 55 | TestClass.cs 56 | ```csharp 57 | using Microsoft.VisualStudio.Shell; 58 | using Microsoft.VisualStudio.Shell.Interop; 59 | using Xunit; 60 | 61 | namespace Tests 62 | { 63 | public class TestClass 64 | { 65 | [VsFact] 66 | void FactTest() 67 | => Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 68 | 69 | [VsTheory] 70 | [InlineData(123)] 71 | void TheoryTest(int n) 72 | { 73 | Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 74 | Assert.Equal(123, n); 75 | } 76 | } 77 | } 78 | 79 | ``` 80 | 81 | ### Configuring how tests run 82 | 83 | All the settings are located in [ITestSettings](src/VsixTesting/ITestSettings.cs) and are implemented by 3 attributes: 84 | 85 | * `[VsTestSettings]` for classes/collections/assemblies 86 | * `[VsFact]` and `[VsTheory]` for methods 87 | 88 | Intellisense should be used to read the documentation for each property that can be set. 89 | 90 | ## License 91 | 92 | This repository is licensed with the Apache, Version 2.0 license. 93 | -------------------------------------------------------------------------------- /VsixTesting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josetr/VsixTesting/11500eb0b91ae5bd06b1448f2392891fd3a67906/VsixTesting.png -------------------------------------------------------------------------------- /VsixTesting.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2018 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57DC6192-9049-4023-B6B8-F0587F704BFE}" 7 | ProjectSection(SolutionItems) = preProject 8 | .gitattributes = .gitattributes 9 | .gitignore = .gitignore 10 | appveyor.yml = appveyor.yml 11 | Directory.Build.props = Directory.Build.props 12 | Directory.Build.targets = Directory.Build.targets 13 | key.snk = key.snk 14 | LICENSE.md = LICENSE.md 15 | .github\workflows\main.yaml = .github\workflows\main.yaml 16 | NuGet.Config = NuGet.Config 17 | README.md = README.md 18 | stylecop.json = stylecop.json 19 | version.json = version.json 20 | VsixTesting.png = VsixTesting.png 21 | EndProjectSection 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Installer", "src\VsixTesting.Installer\VsixTesting.Installer.csproj", "{51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Xunit", "src\VsixTesting.Xunit\VsixTesting.Xunit.csproj", "{5B2D4187-0A44-4FD1-9B17-4D090F0001D3}" 26 | ProjectSection(ProjectDependencies) = postProject 27 | {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE} = {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE} 28 | {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1} = {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1} 29 | EndProjectSection 30 | EndProject 31 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Invoker", "src\VsixTesting.Invoker\VsixTesting.Invoker.csproj", "{1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE}" 32 | EndProject 33 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting", "src\VsixTesting\VsixTesting.csproj", "{D592D582-B012-425E-8B4D-A2067D040363}" 34 | ProjectSection(ProjectDependencies) = postProject 35 | {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE} = {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE} 36 | {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1} = {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1} 37 | EndProjectSection 38 | EndProject 39 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Xunit.Tests", "test\VsixTesting.Xunit.Tests\VsixTesting.Xunit.Tests.csproj", "{7367D06A-C4D7-48B8-8FFC-436568B45618}" 40 | EndProject 41 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Tests", "test\VsixTesting.Tests\VsixTesting.Tests.csproj", "{BEBF4C01-C29C-49D7-93C3-234273940D1C}" 42 | EndProject 43 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Installer.Tests", "test\VsixTesting.Installer.Tests\VsixTesting.Installer.Tests.csproj", "{C0291053-3322-41F2-8107-89381C1F67DC}" 44 | EndProject 45 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Installer.x64", "src\VsixTesting.Installer\VsixTesting.Installer.x64.csproj", "{A2C44214-BB2E-4F86-A3B8-FB5F2ADB5A1E}" 46 | EndProject 47 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VsixTesting.Invoker.17", "src\VsixTesting.Invoker\17\VsixTesting.Invoker.17.csproj", "{1A3C5130-61DB-4DE0-85C0-30847F2807F6}" 48 | EndProject 49 | Global 50 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 51 | Debug|Any CPU = Debug|Any CPU 52 | Release|Any CPU = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 55 | {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {51DB3A8A-C551-4D8D-A7A5-FCA2B76DE4B1}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {5B2D4187-0A44-4FD1-9B17-4D090F0001D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {5B2D4187-0A44-4FD1-9B17-4D090F0001D3}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {5B2D4187-0A44-4FD1-9B17-4D090F0001D3}.Release|Any CPU.ActiveCfg = Release|Any CPU 62 | {5B2D4187-0A44-4FD1-9B17-4D090F0001D3}.Release|Any CPU.Build.0 = Release|Any CPU 63 | {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {1E08940F-AF0E-47F4-AFE6-34DEFF0C44FE}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {D592D582-B012-425E-8B4D-A2067D040363}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 68 | {D592D582-B012-425E-8B4D-A2067D040363}.Debug|Any CPU.Build.0 = Debug|Any CPU 69 | {D592D582-B012-425E-8B4D-A2067D040363}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {D592D582-B012-425E-8B4D-A2067D040363}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {7367D06A-C4D7-48B8-8FFC-436568B45618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 72 | {7367D06A-C4D7-48B8-8FFC-436568B45618}.Debug|Any CPU.Build.0 = Debug|Any CPU 73 | {7367D06A-C4D7-48B8-8FFC-436568B45618}.Release|Any CPU.ActiveCfg = Release|Any CPU 74 | {7367D06A-C4D7-48B8-8FFC-436568B45618}.Release|Any CPU.Build.0 = Release|Any CPU 75 | {BEBF4C01-C29C-49D7-93C3-234273940D1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 76 | {BEBF4C01-C29C-49D7-93C3-234273940D1C}.Debug|Any CPU.Build.0 = Debug|Any CPU 77 | {BEBF4C01-C29C-49D7-93C3-234273940D1C}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {BEBF4C01-C29C-49D7-93C3-234273940D1C}.Release|Any CPU.Build.0 = Release|Any CPU 79 | {C0291053-3322-41F2-8107-89381C1F67DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {C0291053-3322-41F2-8107-89381C1F67DC}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {C0291053-3322-41F2-8107-89381C1F67DC}.Release|Any CPU.ActiveCfg = Release|Any CPU 82 | {C0291053-3322-41F2-8107-89381C1F67DC}.Release|Any CPU.Build.0 = Release|Any CPU 83 | {A2C44214-BB2E-4F86-A3B8-FB5F2ADB5A1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 84 | {A2C44214-BB2E-4F86-A3B8-FB5F2ADB5A1E}.Debug|Any CPU.Build.0 = Debug|Any CPU 85 | {A2C44214-BB2E-4F86-A3B8-FB5F2ADB5A1E}.Release|Any CPU.ActiveCfg = Release|Any CPU 86 | {A2C44214-BB2E-4F86-A3B8-FB5F2ADB5A1E}.Release|Any CPU.Build.0 = Release|Any CPU 87 | {1A3C5130-61DB-4DE0-85C0-30847F2807F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 88 | {1A3C5130-61DB-4DE0-85C0-30847F2807F6}.Debug|Any CPU.Build.0 = Debug|Any CPU 89 | {1A3C5130-61DB-4DE0-85C0-30847F2807F6}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {1A3C5130-61DB-4DE0-85C0-30847F2807F6}.Release|Any CPU.Build.0 = Release|Any CPU 91 | EndGlobalSection 92 | GlobalSection(SolutionProperties) = preSolution 93 | HideSolutionNode = FALSE 94 | EndGlobalSection 95 | GlobalSection(ExtensibilityGlobals) = postSolution 96 | SolutionGuid = {EEA30FC1-DE80-4F3A-A71A-FE28650D0700} 97 | EndGlobalSection 98 | EndGlobal 99 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | configuration: Release 3 | platform: Any CPU 4 | version: 0.0.{build} 5 | skip_branch_with_pr: true 6 | 7 | install: 8 | - mkdir artifacts 9 | - dotnet tool install -g nbgv --version 3.4.255 10 | - nbgv cloud --all-vars 11 | - msbuild src/VsixTesting.Xunit /t:restore /v:m /nologo 12 | - msbuild src/VsixTesting.Xunit /v:m /nologo 13 | - msbuild /t:restore /v:m /nologo 14 | 15 | build_script: 16 | - msbuild /v:m /nologo 17 | 18 | deploy: 19 | provider: NuGet 20 | server: https://www.myget.org/F/vsixtesting/api/v2/package 21 | api_key: 22 | secure: 7fpFGKl529yWoO7qD5t+quDen2GSeUfdsuGOYH3zMSI/nwDEY9bvyiOl/xOoaajX 23 | artifact: /.*\.nupkg/ 24 | 25 | artifacts: 26 | - path: 'artifacts/*.TestResults.xml' 27 | - path: 'artifacts/*.nupkg' 28 | 29 | test_script: 30 | - msbuild /t:test 31 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | patch: no 4 | -------------------------------------------------------------------------------- /key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josetr/VsixTesting/11500eb0b91ae5bd06b1448f2392891fd3a67906/key.snk -------------------------------------------------------------------------------- /src/VsixTesting.Installer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: InternalsVisibleTo("VsixTesting.Installer.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3")] 6 | -------------------------------------------------------------------------------- /src/VsixTesting.Installer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VsixTesting.Installer.Install": { 4 | "commandName": "Project", 5 | "commandLineArgs": "/ApplicationPath \"$(DevEnvDir)devenv.exe\" /RootSuffix Exp /Install \"$(SolutionDir)bin\\VsixTesting.Invoker\\$(Configuration)\\net452\\VsixTesting.Invoker.vsix\"" 6 | }, 7 | "VsixTesting.Installer.InstallAndStart": { 8 | "commandName": "Project", 9 | "commandLineArgs": "/ApplicationPath \"$(DevEnvDir)devenv.exe\" /RootSuffix Exp /InstallAndStart \"$(SolutionDir)bin\\VsixTesting.Invoker\\$(Configuration)\\net452\\VsixTesting.Invoker.vsix\"" 10 | }, 11 | "VsixTesting.Installer.Uninstall": { 12 | "commandName": "Project", 13 | "commandLineArgs": "/ApplicationPath \"$(DevEnvDir)devenv.exe\" /RootSuffix Exp /Uninstall 9eef3d53-333f-4be2-900e-a1f104fc325a" 14 | }, 15 | "VsixTesting.Installer.IsProfileInitialized": { 16 | "commandName": "Project", 17 | "commandLineArgs": "/ApplicationPath \"$(DevEnvDir)devenv.exe\" /RootSuffix Exp /IsProfileInitialized" 18 | }, 19 | "VsixTesting.Installer.2012.Install": { 20 | "commandName": "Project", 21 | "commandLineArgs": "/ApplicationPath \"C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\Common7\\IDE\\devenv.exe\" /RootSuffix Exp /Install \"$(SolutionDir)bin\\VsixTesting.Invoker\\$(Configuration)\\net452\\VsixTesting.Invoker.vsix\"" 22 | }, 23 | "VsixTesting.Installer.2012.InstallAndStart": { 24 | "commandName": "Project", 25 | "commandLineArgs": "/ApplicationPath \"C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\Common7\\IDE\\devenv.exe\" /RootSuffix Exp /InstallAndStart \"$(SolutionDir)bin\\VsixTesting.Invoker\\$(Configuration)\\net452\\VsixTesting.Invoker.vsix\"" 26 | }, 27 | "VsixTesting.Installer.2012.Uninstall": { 28 | "commandName": "Project", 29 | "commandLineArgs": "/ApplicationPath \"C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\Common7\\IDE\\devenv.exe\" /RootSuffix Exp /Uninstall 9eef3d53-333f-4be2-900e-a1f104fc325a" 30 | }, 31 | "VsixTesting.Installer.2012.IsProfileInitialized": { 32 | "commandName": "Project", 33 | "commandLineArgs": "/ApplicationPath \"C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\Common7\\IDE\\devenv.exe\" /RootSuffix Exp /IsProfileInitialized" 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/VsixTesting.Installer/References/Microsoft.VisualStudio.ExtensionManager.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josetr/VsixTesting/11500eb0b91ae5bd06b1448f2392891fd3a67906/src/VsixTesting.Installer/References/Microsoft.VisualStudio.ExtensionManager.dll -------------------------------------------------------------------------------- /src/VsixTesting.Installer/VsixTesting.Installer.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net452 4 | Exe 5 | true 6 | true 7 | VisualStudio, Extension, Installer, VSIXInstaller 8 | Command Line Utility for installing Visual Studio Extensions. 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | References/Microsoft.VisualStudio.ExtensionManager.dll 26 | false 27 | False 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/VsixTesting.Installer/VsixTesting.Installer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | $(Title) Installer 6 | $(PackageTags), VSIX Installer, Installer 7 | true 8 | true 9 | true 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/VsixTesting.Installer/VsixTesting.Installer.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | $(MSBuildThisFileDirectory)../tools/VsixTesting.Installer.exe 4 | $(MSBuildThisFileDirectory)../tools/VsixTesting.Installer.x64.exe 5 | 6 | -------------------------------------------------------------------------------- /src/VsixTesting.Installer/VsixTesting.Installer.x64.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | false 6 | 7 | -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/17/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VsixTesting.Invoker": { 4 | "commandName": "Executable", 5 | "executablePath": "$(DevEnvDir)devenv.exe", 6 | "commandLineArgs": "/rootSuffix Exp" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/17/VsixTesting.Invoker.17.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/17/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | VsixTesting.Invoker 6 | VsixTesting.Invoker 7 | VsixTesting.Invoker 8 | 9 | 10 | 11 | amd64 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/InvokerPackage.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Invoker 4 | { 5 | using System; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Shell; 8 | 9 | [PackageRegistration(UseManagedResourcesOnly = true)] 10 | [ProvideAutomationObject(AutomationObjectName)] 11 | #if false 12 | [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)] 13 | [ProvideAutoLoad(VSConstants.UICONTEXT.NoSolution_string)] 14 | #endif 15 | public sealed class InvokerPackage : Package 16 | { 17 | private const string AutomationObjectName = "VsixTesting.Invoker"; 18 | private Lazy automationObject = new Lazy(() => new InvokerService()); 19 | 20 | protected sealed override object GetAutomationObject(string name) 21 | { 22 | if (name == AutomationObjectName) 23 | return automationObject.Value; 24 | return base.GetAutomationObject(name); 25 | } 26 | 27 | #if false 28 | protected override void Initialize() 29 | => base.Initialize(); 30 | #endif 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/InvokerService.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Invoker 4 | { 5 | using System; 6 | using System.Reflection; 7 | 8 | public class InvokerService 9 | { 10 | public object InvokeMethod(string assemblyPath, string @class, string method, object obj, params object[] arguments) 11 | { 12 | var assembly = Assembly.LoadFrom(assemblyPath); 13 | var assemblyPublicKey = BitConverter.ToString(assembly.GetName().GetPublicKey()).Replace("-", string.Empty); 14 | 15 | if (assemblyPublicKey.Equals("0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3", StringComparison.OrdinalIgnoreCase)) 16 | { 17 | return assembly 18 | .GetType(@class) 19 | .GetMethod(method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) 20 | .Invoke(obj, arguments); 21 | } 22 | 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VsixTesting.Invoker": { 4 | "commandName": "Executable", 5 | "executablePath": "$(DevEnvDir)devenv.exe", 6 | "commandLineArgs": "/rootSuffix Exp" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/VsixTesting.Invoker.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | False 7 | net452 8 | false 9 | 10 | 11 | 12 | false 13 | 14 | 15 | 16 | true 17 | true 18 | true 19 | false 20 | false 21 | true 22 | true 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/VsixTesting.Invoker/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | VsixTesting.Invoker 6 | VsixTesting.Invoker 7 | VsixTesting.Invoker 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Instances.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting 4 | { 5 | using Xunit; 6 | 7 | internal class Instances 8 | { 9 | [Fact] 10 | public void VisualStudio() 11 | { 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/RemoteTestAssemblyRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1202 // Elements should be ordered by access 3 | 4 | namespace VsixTesting.XunitX.Internal 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using VsixTesting.XunitX.Internal.Utilities; 12 | using Xunit.Abstractions; 13 | using Xunit.Sdk; 14 | 15 | internal sealed class RemoteTestAssemblyRunner : LongLivedMarshalByRefObject, IDisposable 16 | { 17 | private readonly XunitTestAssemblyRunner runner; 18 | 19 | public RemoteTestAssemblyRunner(ITestAssembly testAssembly, IEnumerable testCases, IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions, IMessageBus messageBus) 20 | { 21 | runner = new RealTestAssemblyRunner(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions, messageBus); 22 | } 23 | 24 | public SerializableRunSummary Run() 25 | { 26 | var result = runner.RunAsync().GetAwaiter().GetResult(); 27 | return new SerializableRunSummary 28 | { 29 | Total = result.Total, 30 | Failed = result.Failed, 31 | Skipped = result.Skipped, 32 | Time = result.Time, 33 | }; 34 | } 35 | 36 | public void Dispose() => runner.Dispose(); 37 | 38 | private class RealTestAssemblyRunner : XunitTestAssemblyRunner 39 | { 40 | private readonly IMessageBus messageBus; 41 | 42 | public RealTestAssemblyRunner(ITestAssembly testAssembly, IEnumerable testCases, IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions, IMessageBus messageBus) 43 | : base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions) 44 | { 45 | this.messageBus = messageBus; 46 | } 47 | 48 | protected override IMessageBus CreateMessageBus() 49 | { 50 | return messageBus; 51 | } 52 | 53 | protected override Task RunTestCollectionAsync(IMessageBus messageBus, ITestCollection testCollection, IEnumerable testCases, CancellationTokenSource cancellationTokenSource) 54 | { 55 | testCases = testCases.Cast().Select(tc => VsTestCaseBase.DeserializeFromString(tc.SerializeToString())); 56 | return base.RunTestCollectionAsync(messageBus, testCollection, testCases, cancellationTokenSource); 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/Utilities/ExceptionRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal.Utilities 4 | { 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Xunit.Sdk; 9 | 10 | internal class ExceptionRunner : TestCaseRunner 11 | { 12 | private readonly Exception exception; 13 | private readonly string[] exceptionTypes; 14 | private readonly string[] exceptionMessages; 15 | private readonly string[] exceptionStackTraces; 16 | private readonly int[] exceptionParenIndices; 17 | private readonly string output; 18 | 19 | public ExceptionRunner(IXunitTestCase testCase, Exception exception, string output, IMessageBus messageBus, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 20 | : base(testCase, messageBus, aggregator, cancellationTokenSource) 21 | { 22 | this.exception = exception; 23 | this.output = output; 24 | } 25 | 26 | public ExceptionRunner(IXunitTestCase testCase, string[] exceptionTypes, string[] exceptionMessages, string[] exceptionStackTraces, int[] exceptionParenIndices, string output, IMessageBus messageBus, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 27 | : base(testCase, messageBus, aggregator, cancellationTokenSource) 28 | { 29 | this.exceptionTypes = exceptionTypes; 30 | this.exceptionMessages = exceptionMessages; 31 | this.exceptionStackTraces = exceptionStackTraces; 32 | this.exceptionParenIndices = exceptionParenIndices; 33 | this.output = output; 34 | } 35 | 36 | protected override Task RunTestAsync() 37 | { 38 | var test = new XunitTest(TestCase, TestCase.DisplayName); 39 | var summary = new RunSummary { Total = 1 }; 40 | 41 | if (!MessageBus.QueueMessage(new TestStarting(test))) 42 | CancellationTokenSource.Cancel(); 43 | else 44 | { 45 | if (!string.IsNullOrEmpty(TestCase.SkipReason)) 46 | { 47 | summary.Skipped = 1; 48 | 49 | if (!MessageBus.QueueMessage(new TestSkipped(test, TestCase.SkipReason))) 50 | CancellationTokenSource.Cancel(); 51 | } 52 | else 53 | { 54 | summary.Failed = 1; 55 | 56 | if (!MessageBus.QueueMessage(exception != null 57 | ? new TestFailed(test, 0, output, exception) 58 | : new TestFailed(test, 0, output, exceptionTypes, exceptionMessages, exceptionStackTraces, exceptionParenIndices))) 59 | { 60 | CancellationTokenSource.Cancel(); 61 | } 62 | } 63 | 64 | if (!MessageBus.QueueMessage(new TestFinished(test, 0, output))) 65 | CancellationTokenSource.Cancel(); 66 | } 67 | 68 | return Task.FromResult(summary); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/Utilities/ExceptionTestCase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal.Utilities 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Linq; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Vs; 12 | using Xunit.Abstractions; 13 | using Xunit.Sdk; 14 | 15 | internal class ExceptionTestCase : XunitTestCase 16 | { 17 | [EditorBrowsable(EditorBrowsableState.Never)] 18 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] 19 | public ExceptionTestCase() 20 | { 21 | } 22 | 23 | public ExceptionTestCase(Exception exception, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod) 24 | : base(diagnosticMessageSink, defaultMethodDisplay, testMethod) 25 | { 26 | var finfo = ExceptionUtility.ConvertExceptionToFailureInformation(exception); 27 | Types = finfo.ExceptionTypes; 28 | Messages = finfo.Messages; 29 | StackTraces = finfo.StackTraces; 30 | ParentIndices = finfo.ExceptionParentIndices; 31 | } 32 | 33 | internal string[] Types { get; private set; } 34 | internal string[] Messages { get; private set; } 35 | internal string[] StackTraces { get; private set; } 36 | internal int[] ParentIndices { get; private set; } 37 | 38 | public override void Serialize(IXunitSerializationInfo data) 39 | { 40 | base.Serialize(data); 41 | data.AddValue("ExceptionTypes", Types); 42 | data.AddValue("ExceptionMessages", Messages); 43 | data.AddValue("ExceptionStackTraces", StackTraces); 44 | data.AddValue("ExceptionParentIndices", ParentIndices); 45 | } 46 | 47 | public override void Deserialize(IXunitSerializationInfo data) 48 | { 49 | base.Deserialize(data); 50 | Types = data.GetValue("ExceptionTypes"); 51 | Messages = data.GetValue("ExceptionMessages"); 52 | StackTraces = data.GetValue("ExceptionStackTraces"); 53 | ParentIndices = data.GetValue("ExceptionParentIndices"); 54 | } 55 | 56 | public override Task RunAsync(IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 57 | { 58 | return new ExceptionRunner(this, Types, Messages, StackTraces, ParentIndices, string.Empty, messageBus, aggregator, cancellationTokenSource).RunAsync(); 59 | } 60 | 61 | protected override void Initialize() 62 | { 63 | base.Initialize(); 64 | 65 | try 66 | { 67 | var testSettings = VsTestSettingsUtil.FromTestMethod(TestMethod); 68 | var year = VersionUtil.GetYear(testSettings.SupportedVersionRanges.OrderBy(r => r.Minimum).First().Minimum); 69 | Traits[VsTestCaseBase.TraitKey] = new List(new[] { year + testSettings.RootSuffix }); 70 | } 71 | catch 72 | { 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/Utilities/FakeException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal.Utilities 4 | { 5 | using System; 6 | 7 | [Serializable] 8 | internal class FakeException : Exception 9 | { 10 | public FakeException() 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/Utilities/IFinishedMessageExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal.Utilities 4 | { 5 | using Xunit.Abstractions; 6 | using Xunit.Sdk; 7 | 8 | internal static class IFinishedMessageExtensions 9 | { 10 | public static RunSummary ToRunSummary(this IFinishedMessage message) 11 | { 12 | return new RunSummary 13 | { 14 | Total = message.TestsRun, 15 | Failed = message.TestsFailed, 16 | Skipped = message.TestsSkipped, 17 | Time = message.ExecutionTime, 18 | }; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/Utilities/SerializableRunSummary.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal.Utilities 4 | { 5 | using System; 6 | using Xunit.Sdk; 7 | 8 | [Serializable] 9 | internal class SerializableRunSummary 10 | { 11 | public int Total { get; set; } 12 | public int Failed { get; set; } 13 | public int Skipped { get; set; } 14 | public decimal Time { get; set; } 15 | 16 | public static implicit operator RunSummary(SerializableRunSummary summary) => new RunSummary 17 | { 18 | Total = summary.Total, 19 | Failed = summary.Failed, 20 | Skipped = summary.Skipped, 21 | Time = summary.Time, 22 | }; 23 | } 24 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsInstanceTestCase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using Common; 13 | using Vs; 14 | using VsixTesting.Utilities; 15 | using VsixTesting.XunitX.Internal.Utilities; 16 | using Xunit.Abstractions; 17 | using Xunit.Sdk; 18 | 19 | internal class VsInstanceTestCase : VsTestCaseBase 20 | { 21 | [EditorBrowsable(EditorBrowsableState.Never)] 22 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] 23 | public VsInstanceTestCase() 24 | { 25 | } 26 | 27 | public VsInstanceTestCase(string instanceId, string applicationPath, string rootSuffix, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) 28 | : base(instanceId, diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) 29 | { 30 | ApplicationPath = applicationPath; 31 | RootSuffix = rootSuffix; 32 | DebugMixedMode = false; 33 | ExtensionDirectories = new HashSet(); 34 | } 35 | 36 | public string ApplicationPath { get; private set; } 37 | public string RootSuffix { get; private set; } 38 | public bool DebugMixedMode { get; private set; } 39 | public HashSet ExtensionDirectories { get; private set; } 40 | 41 | public override void Serialize(IXunitSerializationInfo info) 42 | { 43 | base.Serialize(info); 44 | info.AddValue(nameof(ApplicationPath), ApplicationPath); 45 | info.AddValue(nameof(RootSuffix), RootSuffix); 46 | info.AddValue(nameof(DebugMixedMode), DebugMixedMode); 47 | info.AddValue(nameof(ExtensionDirectories), string.Join(";", ExtensionDirectories)); 48 | } 49 | 50 | public override void Deserialize(IXunitSerializationInfo info) 51 | { 52 | base.Deserialize(info); 53 | ApplicationPath = info.GetValue(nameof(ApplicationPath)); 54 | RootSuffix = info.GetValue(nameof(RootSuffix)); 55 | DebugMixedMode = info.GetValue(nameof(DebugMixedMode)); 56 | ExtensionDirectories = new HashSet(info.GetValue(nameof(ExtensionDirectories)).Split(';')); 57 | } 58 | 59 | public override Task RunAsync(IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 60 | => new ExceptionRunner(this, new NotImplementedException(), string.Empty, messageBus, aggregator, cancellationTokenSource).RunAsync(); 61 | 62 | public async Task LaunchAndDebug(IMessageBus messageBus, CancellationTokenSource cancellationTokenSource) 63 | { 64 | await ThreadUtil.RunOnStaThreadAsync(async () => 65 | { 66 | using (var retryFilter = new RetryMessageFilter()) 67 | { 68 | var diagnostics = new VisualDiagnostics(this, DiagnosticMessageSink, messageBus, cancellationTokenSource); 69 | var extensionsToInstall = VsInstance.GetExtensionsToInstall(ExtensionDirectories); 70 | var installation = VisualStudioUtil.FindInstallations().First(i => i.ApplicationPath == ApplicationPath); 71 | var hive = new VsHive(installation, RootSuffix); 72 | await VsInstance.Prepare(hive, extensionsToInstall, resetSettings: false, diagnostics, installInvoker: false); 73 | var process = await diagnostics.RunAsync("Launching Instance", () => Task.FromResult(VisualStudioUtil.StartProcess(hive))); 74 | if (Debugger.IsAttached) 75 | await VsInstance.AttachDebugger(process, DebugMixedMode, diagnostics); 76 | } 77 | }); 78 | } 79 | 80 | public void MergeSettings(ITestSettings settings) 81 | { 82 | ExtensionDirectories.Add(settings.ExtensionsDirectory); 83 | if (settings.DebugMixedMode) 84 | DebugMixedMode = true; 85 | } 86 | 87 | protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) 88 | => $"{TraitKey} [{TraitValue}]"; 89 | 90 | protected override string GetUniqueID() 91 | => base.GetUniqueID() + "-" + InstanceId; 92 | } 93 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsSkippedDataRowTestCase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.ComponentModel; 7 | using Xunit.Abstractions; 8 | using Xunit.Sdk; 9 | 10 | internal class VsSkippedDataRowTestCase : VsTestCaseBase 11 | { 12 | private string skipReason; 13 | 14 | [EditorBrowsable(EditorBrowsableState.Never)] 15 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] 16 | public VsSkippedDataRowTestCase() 17 | { 18 | } 19 | 20 | public VsSkippedDataRowTestCase(string instanceId, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, string skipReason, object[] testMethodArguments = null) 21 | : base(instanceId, diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) 22 | { 23 | this.skipReason = skipReason; 24 | } 25 | 26 | public override void Serialize(IXunitSerializationInfo info) 27 | { 28 | base.Serialize(info); 29 | info.AddValue("SkipReason", skipReason); 30 | } 31 | 32 | public override void Deserialize(IXunitSerializationInfo info) 33 | { 34 | base.Deserialize(info); 35 | skipReason = info.GetValue("SkipReason"); 36 | } 37 | 38 | protected override string GetSkipReason(IAttributeInfo factAttribute) 39 | => skipReason; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestAssemblyRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Common; 10 | using VsixTesting.Utilities; 11 | using Xunit.Abstractions; 12 | using Xunit.Sdk; 13 | 14 | internal class VsTestAssemblyRunner : XunitTestAssemblyRunner 15 | { 16 | public VsTestAssemblyRunner(ITestAssembly testAssembly, IEnumerable testCases, IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions) 17 | : base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions) 18 | { 19 | } 20 | 21 | private IEnumerable LocalTestCases => TestCases.Except(TestCases.OfType()); 22 | private IEnumerable RemoteTestCases => TestCases.OfType().Except(TestCases.OfType()); 23 | 24 | protected override async Task RunTestCollectionsAsync(IMessageBus messageBus, CancellationTokenSource cancellationTokenSource) 25 | { 26 | var result = await Local_RunTestCasesAsync(LocalTestCases, messageBus, cancellationTokenSource); 27 | 28 | foreach (var remoteTestCases in RemoteTestCases.GroupBy(tc => tc.InstanceId).OrderBy(g => g.Key)) 29 | result.Aggregate(await Remote_RunTestCasesAsync(remoteTestCases.Key, remoteTestCases, messageBus, cancellationTokenSource)); 30 | 31 | foreach (var instanceTestCase in TestCases.OfType()) 32 | { 33 | if (!RemoteTestCases.Any(tc => tc.InstanceId == instanceTestCase.InstanceId)) 34 | await instanceTestCase.LaunchAndDebug(messageBus, cancellationTokenSource); 35 | } 36 | 37 | return result; 38 | } 39 | 40 | private async Task Local_RunTestCasesAsync(IEnumerable testCases, IMessageBus messageBus, CancellationTokenSource cancellationTokenSource) 41 | { 42 | var allTestCases = TestCases; 43 | TestCases = testCases; 44 | var result = await base.RunTestCollectionsAsync(messageBus, cancellationTokenSource); 45 | TestCases = allTestCases; 46 | return result; 47 | } 48 | 49 | private async Task Remote_RunTestCasesAsync(string instanceId, IEnumerable testCases, IMessageBus messageBus, CancellationTokenSource cancellationTokenSource) 50 | { 51 | if (testCases.All(tc => !string.IsNullOrEmpty(tc.SkipReason))) 52 | return await Local_RunTestCasesAsync(testCases, messageBus, cancellationTokenSource); 53 | 54 | var instanceTestCase = TestCases.OfType().FirstOrDefault(tc => tc.InstanceId == instanceId); 55 | 56 | return await ThreadUtil.RunOnStaThreadAsync(async () => 57 | { 58 | var runner = new VsInstanceTestAssemblyRunner(TestAssembly, testCases, instanceTestCase, DiagnosticMessageSink, ExecutionMessageSink, ExecutionOptions, () => CreateMessageBus(), Aggregator); 59 | 60 | try 61 | { 62 | using (var retryFilter = new RetryMessageFilter()) 63 | { 64 | return await runner.RunAsync(cancellationTokenSource); 65 | } 66 | } 67 | finally 68 | { 69 | await runner.DisposeAsync(); 70 | } 71 | }); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestCase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.ComponentModel; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Xunit.Abstractions; 10 | using Xunit.Sdk; 11 | 12 | internal class VsTestCase : VsTestCaseBase 13 | { 14 | [EditorBrowsable(EditorBrowsableState.Never)] 15 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] 16 | public VsTestCase() 17 | { 18 | } 19 | 20 | public VsTestCase(string instanceId, string instancePath, VsTestSettings testSettings, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) 21 | : base(instanceId, diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) 22 | { 23 | InstancePath = instancePath; 24 | Settings = testSettings; 25 | } 26 | 27 | public string InstancePath { get; private set; } 28 | public VsTestSettings Settings { get; private set; } 29 | 30 | public override Task RunAsync(IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 31 | => new VsTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource).RunAsync(); 32 | 33 | public override void Serialize(IXunitSerializationInfo info) 34 | { 35 | base.Serialize(info); 36 | info.AddValue(nameof(InstancePath), InstancePath); 37 | } 38 | 39 | public override void Deserialize(IXunitSerializationInfo info) 40 | { 41 | base.Deserialize(info); 42 | InstancePath = info.GetValue(nameof(InstancePath)); 43 | Settings = VsTestSettingsUtil.FromTestMethod(TestMethod); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestCaseBase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using Xunit.Abstractions; 9 | using Xunit.Internal; 10 | using Xunit.Sdk; 11 | 12 | internal class VsTestCaseBase : XunitTestCase 13 | { 14 | [EditorBrowsable(EditorBrowsableState.Never)] 15 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] 16 | public VsTestCaseBase() 17 | { 18 | } 19 | 20 | public VsTestCaseBase(string instanceId, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) 21 | : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) 22 | { 23 | InstanceId = instanceId; 24 | } 25 | 26 | public string InstanceId { get; private set; } 27 | internal static string TraitKey => "VisualStudio"; 28 | protected string TraitValue => InstanceId.Replace(" ", string.Empty); 29 | 30 | public override void Serialize(IXunitSerializationInfo info) 31 | { 32 | base.Serialize(info); 33 | info.AddValue(nameof(InstanceId), InstanceId); 34 | } 35 | 36 | public override void Deserialize(IXunitSerializationInfo info) 37 | { 38 | base.Deserialize(info); 39 | InstanceId = info.GetValue(nameof(InstanceId)); 40 | } 41 | 42 | public string SerializeToString() 43 | { 44 | var triple = new XunitSerializationTriple(nameof(VsTestCaseBase), this, GetType()); 45 | return XunitSerializationInfo.SerializeTriple(triple); 46 | } 47 | 48 | public static VsTestCaseBase DeserializeFromString(string value) 49 | { 50 | var triple = XunitSerializationInfo.DeserializeTriple(value); 51 | return (VsTestCaseBase)triple.Value; 52 | } 53 | 54 | protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) 55 | => base.GetDisplayName(factAttribute, displayName) + " - " + InstanceId; 56 | 57 | protected override string GetUniqueID() 58 | => base.GetUniqueID() + "-" + InstanceId; 59 | 60 | protected override void Initialize() 61 | { 62 | base.Initialize(); 63 | Traits[TraitKey] = new List(new[] { TraitValue }); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestCaseRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | using System.Threading; 9 | using Xunit.Abstractions; 10 | using Xunit.Sdk; 11 | 12 | internal class VsTestCaseRunner : XunitTestCaseRunner 13 | { 14 | public VsTestCaseRunner(IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, object[] testMethodArguments, IMessageBus messageBus, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 15 | : base(testCase, displayName, skipReason, constructorArguments, testMethodArguments, messageBus, aggregator, cancellationTokenSource) 16 | { 17 | } 18 | 19 | protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, string skipReason, IReadOnlyList beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 20 | => new VsTestRunner(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource); 21 | } 22 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestFrameworkExecutor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System.Collections.Generic; 6 | using System.Reflection; 7 | using Xunit.Abstractions; 8 | using Xunit.Sdk; 9 | 10 | internal class VsTestFrameworkExecutor : XunitTestFrameworkExecutor 11 | { 12 | public VsTestFrameworkExecutor(AssemblyName assemblyName, ISourceInformationProvider sourceInformationProvider, IMessageSink diagnosticMessageSink) 13 | : base(assemblyName, sourceInformationProvider, diagnosticMessageSink) 14 | { 15 | } 16 | 17 | protected override async void RunTestCases(IEnumerable testCases, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions) 18 | { 19 | using (var assemblyRunner = new VsTestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, executionOptions)) 20 | await assemblyRunner.RunAsync(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestInvoker.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.IO; 9 | using System.Reflection; 10 | using System.Threading; 11 | using System.Windows; 12 | using VsixTesting.Utilities; 13 | using VsixTesting.XunitX.Internal.Utilities; 14 | using Xunit.Abstractions; 15 | using Xunit.Sdk; 16 | 17 | internal class VsTestInvoker : XunitTestInvoker 18 | { 19 | public VsTestInvoker(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, IReadOnlyList beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 20 | : base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, beforeAfterAttributes, aggregator, cancellationTokenSource) 21 | { 22 | } 23 | 24 | protected VsTestSettings Settings => ((VsTestCase)TestCase).Settings; 25 | 26 | protected override object CallTestMethod(object testClassInstance) 27 | { 28 | try 29 | { 30 | return TestMethod.Invoke(testClassInstance, TestMethodArguments); 31 | } 32 | catch (Exception e) 33 | { 34 | if (Settings.TakeScreenshotOnFailure) 35 | { 36 | try 37 | { 38 | TakeScreenShot(Test.DisplayName); 39 | } 40 | catch (Exception exception) 41 | { 42 | Aggregator.Add(exception); 43 | } 44 | } 45 | 46 | if (e is TargetInvocationException && e.InnerException is FakeException) 47 | return null; 48 | 49 | throw; 50 | } 51 | } 52 | 53 | private void TakeScreenShot(string name) 54 | { 55 | try 56 | { 57 | var safename = string.Join("_", name.Split(Path.GetInvalidFileNameChars())); 58 | var date = DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss"); 59 | var path = Path.Combine(Settings.ScreenshotsDirectory, $"{date} {safename}.png"); 60 | 61 | Directory.CreateDirectory(Path.GetDirectoryName(path)); 62 | ScreenshotUtil.CaptureWindow(Process.GetCurrentProcess().MainWindowHandle, path); 63 | } 64 | catch (Exception e) 65 | { 66 | Aggregator.Add(new Exception("Failed saving screenshot", e)); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Windows; 11 | using System.Windows.Threading; 12 | using Xunit.Abstractions; 13 | using Xunit.Sdk; 14 | 15 | internal class VsTestRunner : XunitTestRunner 16 | { 17 | public VsTestRunner(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, string skipReason, IReadOnlyList beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 18 | : base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, aggregator, cancellationTokenSource) 19 | { 20 | } 21 | 22 | protected new VsTestCase TestCase => (VsTestCase)base.TestCase; 23 | 24 | protected override Task InvokeTestMethodAsync(ExceptionAggregator aggregator) 25 | { 26 | var result = TestCase.Settings.UIThread 27 | ? InvokeOnUIThreadAsync(aggregator) 28 | : InvokeAsync(aggregator); 29 | 30 | return result; 31 | } 32 | 33 | private Task InvokeOnUIThreadAsync(ExceptionAggregator aggregator) 34 | { 35 | var tcs = new TaskCompletionSource(); 36 | 37 | Application.Current.Dispatcher.BeginInvoke( 38 | new Action(async () => 39 | { 40 | try 41 | { 42 | var result = await InvokeAsync(aggregator); 43 | tcs.SetResult(result); 44 | } 45 | catch (Exception e) 46 | { 47 | tcs.SetException(e); 48 | } 49 | }), DispatcherPriority.Background); 50 | 51 | return tcs.Task; 52 | } 53 | 54 | private Task InvokeAsync(ExceptionAggregator aggregator) 55 | => new VsTestInvoker(Test, MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments, BeforeAfterAttributes, aggregator, CancellationTokenSource).RunAsync(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTestSettingsUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Reflection; 9 | using VsixTesting; 10 | using Xunit.Abstractions; 11 | using Xunit.Sdk; 12 | 13 | internal class VsTestSettingsUtil 14 | { 15 | public static VsTestSettings FromTestMethod(ITestMethod testMethod) 16 | { 17 | if (testMethod == null) 18 | throw new ArgumentNullException(nameof(TestMethod)); 19 | 20 | var defaultDirectory = Path.GetDirectoryName(testMethod.TestClass.TestCollection.TestAssembly.Assembly.AssemblyPath); 21 | var defaults = VsTestSettings.Defaults; 22 | 23 | return new VsTestSettings() 24 | { 25 | Version = GetTestAttributeArgument( 26 | nameof(ITestSettings.Version), defaults.Version), 27 | 28 | RootSuffix = GetTestAttributeArgument( 29 | nameof(ITestSettings.RootSuffix), defaults.RootSuffix), 30 | 31 | DebugMixedMode = GetTestAttributeArgument( 32 | nameof(ITestSettings.DebugMixedMode), defaults.DebugMixedMode), 33 | 34 | SecureChannel = GetTestAttributeArgument( 35 | nameof(ITestSettings.SecureChannel), defaults.SecureChannel), 36 | 37 | ExtensionsDirectory = NormalizeDirectory( 38 | GetTestAttributeArgument( 39 | nameof(ITestSettings.ExtensionsDirectory), 40 | defaults.ExtensionsDirectory)), 41 | 42 | ScreenshotsDirectory = NormalizeDirectory( 43 | GetTestAttributeArgument( 44 | nameof(ITestSettings.ScreenshotsDirectory), 45 | defaults.ScreenshotsDirectory)), 46 | 47 | UIThread = GetTestAttributeArgument( 48 | nameof(ITestSettings.UIThread), defaults.UIThread), 49 | 50 | ReuseInstance = GetTestAttributeArgument( 51 | nameof(ITestSettings.ReuseInstance), defaults.ReuseInstance), 52 | 53 | TakeScreenshotOnFailure = GetTestAttributeArgument( 54 | nameof(ITestSettings.TakeScreenshotOnFailure), defaults.TakeScreenshotOnFailure), 55 | }; 56 | 57 | TValue GetTestAttributeArgument(string argumentName, TValue defaultValue) 58 | { 59 | var methodAttr = testMethod.Method.GetCustomAttributes(typeof(ITestSettings)).FirstOrDefault(); 60 | if (methodAttr?.GetNamedArgument(argumentName) is TValue methodValue && IsNamedArg(methodAttr)) 61 | return methodValue; 62 | 63 | var classAttr = testMethod.TestClass.Class.GetCustomAttributes(typeof(ITestSettings)).FirstOrDefault(); 64 | if (classAttr?.GetNamedArgument(argumentName) is TValue classValue && IsNamedArg(classAttr)) 65 | return classValue; 66 | 67 | var collectionAttr = testMethod.TestClass.TestCollection.CollectionDefinition?.GetCustomAttributes(typeof(ITestSettings)).FirstOrDefault(); 68 | if (collectionAttr?.GetNamedArgument(argumentName) is TValue collectionValue && IsNamedArg(collectionAttr)) 69 | return collectionValue; 70 | 71 | var assemblyAttr = testMethod.TestClass.Class.Assembly.GetCustomAttributes(typeof(ITestSettings)).FirstOrDefault(); 72 | if (assemblyAttr?.GetNamedArgument(argumentName) is TValue assemblyValue && IsNamedArg(assemblyAttr)) 73 | return assemblyValue; 74 | 75 | return defaultValue; 76 | 77 | bool IsNamedArg(IAttributeInfo attributeInfo) 78 | { 79 | if (attributeInfo is ReflectionAttributeInfo reflectionAttributeInfo) 80 | return reflectionAttributeInfo.AttributeData.NamedArguments.Any(arg => arg.MemberName == argumentName); 81 | return false; 82 | } 83 | } 84 | 85 | string NormalizeDirectory(string path) 86 | { 87 | if (string.IsNullOrWhiteSpace(path)) 88 | return defaultDirectory; 89 | 90 | if (Path.IsPathRooted(path)) 91 | return path; 92 | 93 | return Path.Combine(defaultDirectory, path); 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTheoryTestCase.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.ComponentModel; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Xunit.Abstractions; 10 | using Xunit.Sdk; 11 | 12 | internal class VsTheoryTestCase : VsTestCase 13 | { 14 | [EditorBrowsable(EditorBrowsableState.Never)] 15 | [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] 16 | public VsTheoryTestCase() 17 | { 18 | } 19 | 20 | public VsTheoryTestCase(string instanceId, string instancePath, VsTestSettings testSettings, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) 21 | : base(instanceId, instancePath, testSettings, diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) 22 | { 23 | } 24 | 25 | public override Task RunAsync(IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 26 | => new VsTheoryTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource).RunAsync(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Internal/VsTheoryTestCaseRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | using System.Threading; 9 | using Xunit.Abstractions; 10 | using Xunit.Sdk; 11 | 12 | internal class VsTheoryTestCaseRunner : XunitTheoryTestCaseRunner 13 | { 14 | public VsTheoryTestCaseRunner(IXunitTestCase testCase, string displayName, string skipReason, object[] constructorArguments, IMessageSink diagnosticMessageSink, IMessageBus messageBus, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 15 | : base(testCase, displayName, skipReason, constructorArguments, diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource) 16 | { 17 | } 18 | 19 | protected override XunitTestRunner CreateTestRunner(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments, MethodInfo testMethod, object[] testMethodArguments, string skipReason, IReadOnlyList beforeAfterAttributes, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) 20 | => new VsTestRunner(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, beforeAfterAttributes, new ExceptionAggregator(aggregator), cancellationTokenSource); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: InternalsVisibleTo("VsixTesting.Xunit.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3")] 6 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VsixTesting.Xunit": { 4 | "commandName": "Executable", 5 | "executablePath": "msbuild", 6 | "commandLineArgs": "/t:test", 7 | "workingDirectory": "$(SolutionDir)/src/$(ProjectName)" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsFactAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Xunit 4 | { 5 | using System; 6 | using VsixTesting; 7 | using VsixTesting.XunitX; 8 | using Xunit.Sdk; 9 | 10 | /// 11 | /// Attribute used to specify that a method is a fact that must be run in a Visual Studio process. 12 | /// 13 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 14 | [XunitTestCaseDiscoverer("VsixTesting.XunitX." + nameof(VsFactDiscoverer), ThisAssembly.AssemblyName)] 15 | public class VsFactAttribute : FactAttribute, ITestSettings 16 | { 17 | /// 18 | public string Version { get; set; } 19 | 20 | /// 21 | public string RootSuffix { get; set; } 22 | 23 | /// 24 | public bool DebugMixedMode { get; set; } 25 | 26 | /// 27 | public bool SecureChannel { get; set; } 28 | 29 | /// 30 | public string ExtensionsDirectory { get; set; } 31 | 32 | /// 33 | public string ScreenshotsDirectory { get; set; } 34 | 35 | /// 36 | public bool UIThread { get; set; } 37 | 38 | /// 39 | public bool ReuseInstance { get; set; } 40 | 41 | /// 42 | public bool TakeScreenshotOnFailure { get; set; } 43 | } 44 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsFactDiscoverer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using VsixTesting.XunitX.Internal; 9 | using VsixTesting.XunitX.Internal.Utilities; 10 | using Xunit.Abstractions; 11 | using Xunit.Sdk; 12 | 13 | internal class VsFactDiscoverer : FactDiscoverer 14 | { 15 | public VsFactDiscoverer(IMessageSink diagnosticMessageSink) 16 | : base(diagnosticMessageSink) 17 | { 18 | } 19 | 20 | public override IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) 21 | { 22 | var results = new List(); 23 | 24 | if (testMethod.Method.GetParameters().Any()) 25 | results.Add(new ExecutionErrorTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod, "[VsFact] methods are not allowed to have parameters. Did you mean to use [VsTheory]?")); 26 | else if (testMethod.Method.IsGenericMethodDefinition) 27 | results.Add(new ExecutionErrorTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod, "[VsFact] methods are not allowed to be generic.")); 28 | else 29 | { 30 | try 31 | { 32 | results.AddRange(VsTestCaseFactory.CreateTestCases(testMethod, null, discoveryOptions.MethodDisplayOrDefault(), DiagnosticMessageSink)); 33 | } 34 | catch (Exception exception) 35 | { 36 | results.Add(new ExceptionTestCase(exception, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod)); 37 | } 38 | } 39 | 40 | return results; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsTestFramework.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Xunit 4 | { 5 | using System; 6 | using System.Reflection; 7 | using VsixTesting.XunitX.Internal; 8 | using Xunit.Abstractions; 9 | using Xunit.Sdk; 10 | 11 | internal class VsTestFramework : XunitTestFramework 12 | { 13 | private static bool isUsed = false; 14 | 15 | public VsTestFramework(IMessageSink messageSink) 16 | : base(messageSink) 17 | { 18 | isUsed = true; 19 | } 20 | 21 | internal static void ThrowIfNotInUse() 22 | { 23 | if (isUsed == false) 24 | { 25 | var type = MethodBase.GetCurrentMethod().DeclaringType; 26 | var assemblyName = type.Assembly.GetName().Name; 27 | var attribute = $"TestFramework(\"{type.FullName}\", \"{assemblyName}\")]"; 28 | throw new InvalidOperationException($"[assembly: {attribute} is required."); 29 | } 30 | } 31 | 32 | protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) 33 | { 34 | return new VsTestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsTestSettingsAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Xunit 4 | { 5 | using System; 6 | using VsixTesting; 7 | 8 | /// 9 | /// Attribute used to specify the test settings for test methods decorated with VsFact or VsTheory. 10 | /// 11 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class)] 12 | public class VsTestSettingsAttribute : Attribute, ITestSettings 13 | { 14 | /// 15 | public string Version { get; set; } 16 | 17 | /// 18 | public string RootSuffix { get; set; } 19 | 20 | /// 21 | public bool DebugMixedMode { get; set; } 22 | 23 | /// 24 | public bool SecureChannel { get; set; } 25 | 26 | /// 27 | public string ExtensionsDirectory { get; set; } 28 | 29 | /// 30 | public string ScreenshotsDirectory { get; set; } 31 | 32 | /// 33 | public bool UIThread { get; set; } 34 | 35 | /// 36 | public bool ReuseInstance { get; set; } 37 | 38 | /// 39 | public bool TakeScreenshotOnFailure { get; set; } 40 | } 41 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsTheoryAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Xunit 4 | { 5 | using System; 6 | using VsixTesting.XunitX; 7 | using Xunit.Sdk; 8 | 9 | /// 10 | /// Attribute used to specify that a method is a theory that must be run in a Visual Studio process. 11 | /// 12 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 13 | [XunitTestCaseDiscoverer("VsixTesting.XunitX." + nameof(VsTheoryDiscoverer), ThisAssembly.AssemblyName)] 14 | public sealed class VsTheoryAttribute : VsFactAttribute 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsTheoryDiscoverer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using VsixTesting.XunitX.Internal; 9 | using VsixTesting.XunitX.Internal.Utilities; 10 | using Xunit.Abstractions; 11 | using Xunit.Sdk; 12 | 13 | internal class VsTheoryDiscoverer : TheoryDiscoverer 14 | { 15 | public VsTheoryDiscoverer(IMessageSink diagnosticMessageSink) 16 | : base(diagnosticMessageSink) 17 | { 18 | } 19 | 20 | public override IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) 21 | { 22 | try 23 | { 24 | return base.Discover(discoveryOptions, testMethod, theoryAttribute).ToArray(); 25 | } 26 | catch (Exception exception) 27 | { 28 | return new IXunitTestCase[] { new ExceptionTestCase(exception, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) }; 29 | } 30 | } 31 | 32 | protected override IEnumerable CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow) 33 | => VsTestCaseFactory.CreateTestCases(testMethod, dataRow, discoveryOptions.MethodDisplayOrDefault(), DiagnosticMessageSink); 34 | 35 | protected override IEnumerable CreateTestCasesForSkip(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, string skipReason) 36 | => VsTestCaseFactory.CreateTestCases(testMethod, null, discoveryOptions.MethodDisplayOrDefault(), DiagnosticMessageSink); 37 | 38 | protected override IEnumerable CreateTestCasesForSkippedDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow, string skipReason) 39 | => VsTestCaseFactory.CreateSkippedDataRowTestCases(testMethod, discoveryOptions.MethodDisplayOrDefault(), DiagnosticMessageSink, dataRow, skipReason); 40 | 41 | protected override IEnumerable CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) 42 | => VsTestCaseFactory.CreateTheoryTestCases(testMethod, discoveryOptions.MethodDisplayOrDefault(), DiagnosticMessageSink); 43 | } 44 | } -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsixTesting.Xunit.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | true 4 | net452 5 | false 6 | true 7 | true 8 | embedded 9 | true 10 | true 11 | true 12 | true 13 | VsixTesting.XunitX 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | $(TargetsForTfmSpecificBuildOutput);CopyOutputPathAssembliesToPackage 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsixTesting.Xunit.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | <_Parameter1>Xunit.VsTestFramework 5 | <_Parameter2>VsixTesting.Xunit 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/VsixTesting.Xunit/VsixTesting.Xunit.targets: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | <_ProjectReferenceVsixOutputs> 22 | $(OutDir)%(_ProjectReferenceVsixOutputs.ExtensionsDirectory) 23 | 24 | 25 | 26 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | { 56 | try 57 | { 58 | var projectCollection = new ProjectCollection(); 59 | var project = projectCollection.LoadProject(projectReference.ItemSpec); 60 | var isVsixProject = VSSDKTargets.All(targetName => project.Targets.Any(target => target.Key == targetName)); 61 | projectCollection.UnloadProject(project); 62 | return isVsixProject; 63 | } 64 | catch 65 | { 66 | return false; 67 | } 68 | }).ToArray(); 69 | 70 | return true; 71 | ]]> 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/VsixTesting/Common/IAsyncDisposable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common 4 | { 5 | using System.Threading.Tasks; 6 | 7 | internal interface IAsyncDisposable 8 | { 9 | Task DisposeAsync(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/VsixTesting/Common/ProcessExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common 4 | { 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using VsixTesting.Interop; 12 | 13 | internal static class ProcessExtensions 14 | { 15 | internal static Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default) 16 | { 17 | var taskCompletionSource = new TaskCompletionSource(); 18 | process.EnableRaisingEvents = true; 19 | process.Exited += (sender, e) => taskCompletionSource.TrySetResult(default); 20 | if (cancellationToken != default) 21 | cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()); 22 | 23 | return taskCompletionSource.Task; 24 | } 25 | 26 | internal static string GetMainModuleFileName(this Process process, int bufferCapacity = 2056) 27 | { 28 | try 29 | { 30 | if (process == null) 31 | return string.Empty; 32 | var processPath = new StringBuilder(bufferCapacity); 33 | var result = Psapi.GetModuleFileNameEx(process.Handle, IntPtr.Zero, processPath, processPath.Capacity); 34 | if (result <= 0) 35 | return string.Empty; 36 | if (result == processPath.Capacity - 1) 37 | return GetMainModuleFileName(process, bufferCapacity * 2); 38 | return processPath.ToString(); 39 | } 40 | catch 41 | { 42 | return string.Empty; 43 | } 44 | } 45 | 46 | internal static string GetProcessName(this Process process) 47 | { 48 | return Path.GetFileNameWithoutExtension(GetMainModuleFileName(process)); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/VsixTesting/Common/RunningObjectTable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1310 // Field names must not contain underscore 3 | 4 | namespace Common 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Runtime.InteropServices; 10 | using System.Runtime.InteropServices.ComTypes; 11 | using System.Text.RegularExpressions; 12 | using VsixTesting.Interop; 13 | 14 | internal static class RunningObjectTable 15 | { 16 | private const int S_OK = 0; 17 | 18 | public static IEnumerable GetRunningObjects(string namePattern = ".*") 19 | { 20 | IEnumMoniker enumMoniker = null; 21 | IRunningObjectTable rot = null; 22 | IBindCtx bindCtx = null; 23 | 24 | try 25 | { 26 | Marshal.ThrowExceptionForHR(Ole32.CreateBindCtx(0, out bindCtx)); 27 | bindCtx.GetRunningObjectTable(out rot); 28 | rot.EnumRunning(out enumMoniker); 29 | 30 | var moniker = new IMoniker[1]; 31 | var fetched = IntPtr.Zero; 32 | 33 | while (enumMoniker.Next(1, moniker, fetched) == S_OK) 34 | { 35 | object runningObject = null; 36 | try 37 | { 38 | var roMoniker = moniker.First(); 39 | if (roMoniker == null) 40 | continue; 41 | roMoniker.GetDisplayName(bindCtx, null, out var name); 42 | if (!Regex.IsMatch(name, namePattern)) 43 | continue; 44 | Marshal.ThrowExceptionForHR(rot.GetObject(roMoniker, out runningObject)); 45 | } 46 | catch (UnauthorizedAccessException) 47 | { 48 | continue; 49 | } 50 | 51 | if (runningObject != null) 52 | yield return runningObject; 53 | } 54 | } 55 | finally 56 | { 57 | if (enumMoniker != null) 58 | Marshal.ReleaseComObject(enumMoniker); 59 | if (rot != null) 60 | Marshal.ReleaseComObject(rot); 61 | if (bindCtx != null) 62 | Marshal.ReleaseComObject(bindCtx); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/VsixTesting/Common/TempFile.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common 4 | { 5 | using System; 6 | using System.IO; 7 | 8 | internal sealed class TempFile : IDisposable 9 | { 10 | public TempFile(string path) 11 | { 12 | Path = path; 13 | } 14 | 15 | ~TempFile() 16 | { 17 | TryDeleteFile(); 18 | } 19 | 20 | public string Path { get; private set; } 21 | 22 | public void Dispose() 23 | { 24 | if (TryDeleteFile()) 25 | GC.SuppressFinalize(this); 26 | } 27 | 28 | private bool TryDeleteFile() 29 | { 30 | if (Path != null) 31 | { 32 | try 33 | { 34 | File.Delete(Path); 35 | Path = null; 36 | } 37 | catch 38 | { 39 | return false; 40 | } 41 | } 42 | 43 | return true; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/VsixTesting/Common/ThreadUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common 4 | { 5 | using System; 6 | using System.Collections.Concurrent; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | internal static class ThreadUtil 11 | { 12 | public static Task RunOnStaThreadAsync(Func> func) 13 | { 14 | var tcs = new TaskCompletionSource(); 15 | var thread = new Thread(new ThreadStart(() => 16 | { 17 | try 18 | { 19 | using (var syncContext = new SingleThreadSyncContext()) 20 | { 21 | SynchronizationContext.SetSynchronizationContext(syncContext); 22 | var task = func(); 23 | task.ContinueWith(delegate { syncContext.CompleteAdding(); }, TaskScheduler.Default); 24 | syncContext.Run(); 25 | tcs.SetResult(task.GetAwaiter().GetResult()); 26 | } 27 | } 28 | catch (Exception e) 29 | { 30 | tcs.SetException(e); 31 | } 32 | })); 33 | thread.SetApartmentState(ApartmentState.STA); 34 | thread.Start(); 35 | return tcs.Task; 36 | } 37 | 38 | public static Task RunOnStaThreadAsync(Func func) 39 | { 40 | return RunOnStaThreadAsync(async () => 41 | { 42 | await func(); 43 | return Task.FromResult(0); 44 | }); 45 | } 46 | 47 | private sealed class SingleThreadSyncContext : SynchronizationContext, IDisposable 48 | { 49 | private readonly BlockingCollection> statefulCallbacks = new BlockingCollection>(); 50 | private bool disposed = false; 51 | 52 | public override void Post(SendOrPostCallback d, object state) 53 | => statefulCallbacks.Add(Tuple.Create(d, state)); 54 | 55 | public override void Send(SendOrPostCallback d, object state) 56 | => throw new NotSupportedException(); 57 | 58 | public void Run() 59 | { 60 | foreach (var statefulCallback in statefulCallbacks.GetConsumingEnumerable()) 61 | statefulCallback.Item1(statefulCallback.Item2); 62 | } 63 | 64 | public void CompleteAdding() 65 | => statefulCallbacks.CompleteAdding(); 66 | 67 | public void Dispose() 68 | { 69 | if (!disposed) 70 | { 71 | statefulCallbacks.Dispose(); 72 | disposed = true; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/VsixTesting/Common/VersionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common 4 | { 5 | using System; 6 | 7 | internal static class VersionExtensions 8 | { 9 | public static Version IncreaseMinorVersion(this Version version) 10 | { 11 | if (version.Minor < int.MaxValue) 12 | return NewVersion(version.Major, version.Minor + 1, version.Build, version.Revision); 13 | if (version.Major != int.MaxValue) 14 | return NewVersion(version.Major + 1, 0, version.Build, version.Revision); 15 | return version; 16 | } 17 | 18 | public static Version DecreaseMinorVersion(this Version version) 19 | { 20 | if (version.Minor >= 1) 21 | return NewVersion(version.Major, version.Minor - 1, version.Build, version.Revision); 22 | if (version.Major != 0) 23 | return NewVersion(version.Major - 1, int.MaxValue, version.Build, version.Revision); 24 | return version; 25 | } 26 | 27 | private static Version NewVersion(int major, int minor, int build, int revision) 28 | { 29 | if (revision >= 0) 30 | return new Version(major, minor, build, revision); 31 | if (build >= 0) 32 | return new Version(major, minor, build); 33 | return new Version(major, minor); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/VsixTesting/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "S.")] 4 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "S.")] 5 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1516:Elements must be separated by blank line", Justification = "S.")] 6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces must not be omitted", Justification = "S.")] 7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1520:Use braces consistently", Justification = "S.")] 8 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements must be documented", Justification = "P.")] 9 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "S.")] 10 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "P.")] 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop", "SA0001", Justification = ".")] 12 | -------------------------------------------------------------------------------- /src/VsixTesting/IDiagnostics.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1402 // File may only contain a single type 3 | #pragma warning disable SA1649 // File name should match first type name 4 | 5 | namespace VsixTesting 6 | { 7 | using System; 8 | using System.Collections.Concurrent; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | 15 | internal interface IDiagnostics : IOutput 16 | { 17 | Task RunAsync(string displayName, Func> function); 18 | } 19 | 20 | internal interface IOutput 21 | { 22 | void WriteLine(string message); 23 | void WriteLine(string format, params object[] args); 24 | } 25 | 26 | internal static class IDiagnosticsExtensions 27 | { 28 | public static Task RunAsync(this IDiagnostics diagnostics, string displayName, Func> function) 29 | => diagnostics.RunAsync(displayName, output => function()); 30 | 31 | public static Task RunAsync(this IDiagnostics diagnostics, string displayName, Func function) 32 | => diagnostics.RunAsync(displayName, async output => 33 | { 34 | await function(output); 35 | return 0; 36 | }); 37 | 38 | public static Task RunAsync(this IDiagnostics diagnostics, string displayName, Action function) 39 | => diagnostics.RunAsync(displayName, output => 40 | { 41 | function(output); 42 | return Task.FromResult(0); 43 | }); 44 | 45 | public static Task RunAsync(this IDiagnostics diagnostics, string displayName, Func function) 46 | => diagnostics.RunAsync(displayName, output => function()); 47 | 48 | public static Task RunAsync(this IDiagnostics diagnostics, string displayName, Action action) 49 | { 50 | return diagnostics.RunAsync(displayName, output => 51 | { 52 | action(); 53 | return Task.FromResult(0); 54 | }); 55 | } 56 | } 57 | 58 | internal class DiagnosticOutput : IOutput 59 | { 60 | private int id = 0; 61 | 62 | public ConcurrentBag<(DateTime date, int id, string message)> Entries { get; set; } 63 | = new ConcurrentBag<(DateTime date, int id, string message)>(); 64 | 65 | public void WriteLine(string message) 66 | => Entries.Add((DateTime.Now, Interlocked.Increment(ref id), message)); 67 | 68 | public void WriteLine(string format, params object[] args) 69 | => WriteLine(string.Format(format, args)); 70 | 71 | public override string ToString() 72 | => Entries 73 | .OrderBy(msg => (msg.date, msg.id)) 74 | .Aggregate(new StringBuilder(), (sb, msg) => sb.AppendLine(msg.message), sb => sb.ToString()); 75 | } 76 | 77 | internal class MultiOutput : IOutput 78 | { 79 | private readonly List outputs = new List(); 80 | 81 | public MultiOutput(IEnumerable outputs) 82 | => this.outputs.AddRange(outputs); 83 | 84 | public void WriteLine(string message) 85 | => outputs.ForEach(output => output.WriteLine(message)); 86 | 87 | public void WriteLine(string format, params object[] args) 88 | => outputs.ForEach(output => output.WriteLine(format, args)); 89 | } 90 | } -------------------------------------------------------------------------------- /src/VsixTesting/ITestSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting 4 | { 5 | internal interface ITestSettings 6 | { 7 | /// 8 | /// Gets or sets the version range. 9 | /// The default value is 2012- which targets Visual Studio 2012 and higher. 10 | /// 11 | /// 12 | /// The full version format used in the vsixmanifest is supported. 13 | /// 14 | string Version { get; set; } 15 | 16 | /// 17 | /// Gets or sets the Visual Studio Root Suffix. 18 | /// The default value is `Exp` which targets the default Visual Studio Experimental Instance. 19 | /// See for more information. 20 | /// 21 | string RootSuffix { get; set; } 22 | 23 | /// 24 | /// Gets or sets a value indicating whether the mixed 'Managed/Native' debugging engine should be used to attach/debug the remote Visual Studio Instance. 25 | /// The default is . 26 | /// 27 | bool DebugMixedMode { get; set; } 28 | 29 | /// 30 | /// Gets or sets a value indicating whether the channel used to communicate to the remote Visual Studio Instance must be secure. 31 | /// The default value is because if it's set to then "xunit.AppDomain" must be set to as well. 32 | /// 33 | bool SecureChannel { get; set; } 34 | 35 | /// 36 | /// Gets or sets a directory path containing .vsix packages to install before launching the Visual Studio Instance. 37 | /// 38 | /// 39 | /// The path is relative to the tested assembly, unless an absolute path is given. 40 | /// 41 | string ExtensionsDirectory { get; set; } 42 | 43 | /// 44 | /// Gets or sets a directory path where screenshots will be stored when an error occurs within the Visual Studio Instance. 45 | /// 46 | string ScreenshotsDirectory { get; set; } 47 | 48 | /// 49 | /// Gets or sets a value indicating whether to invoke the test method in the main Visual Studio UI Thread. 50 | /// The default value is . 51 | /// 52 | bool UIThread { get; set; } 53 | 54 | /// 55 | /// Gets or sets a value indicating whether to reuse the same Visual Studio Instance from a previous test. 56 | /// The default value is . 57 | /// 58 | bool ReuseInstance { get; set; } 59 | 60 | /// 61 | /// Gets or sets a value indicating whether to capture a screenshot if the test fails. 62 | /// The default value is . 63 | /// 64 | bool TakeScreenshotOnFailure { get; set; } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/VsixTesting/Interop/Kernel32.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1602 // Enumeration items should be documented 3 | #pragma warning disable SA1313 // Parameter names should begin with lower-case letter 4 | #pragma warning disable 0649 5 | 6 | namespace VsixTesting.Interop 7 | { 8 | using System; 9 | using System.Runtime.InteropServices; 10 | using Microsoft.Win32.SafeHandles; 11 | 12 | internal class Kernel32 13 | { 14 | public enum JOBOBJECTINFOCLASS 15 | { 16 | JobObjectBasicLimitInformation = 2, 17 | JobObjectBasicUIRestrictions = 4, 18 | JobObjectSecurityLimitInformation = 5, 19 | JobObjectEndOfJobTimeInformation = 6, 20 | JobObjectAssociateCompletionPortInformation = 7, 21 | JobObjectExtendedLimitInformation = 9, 22 | JobObjectGroupInformation = 11, 23 | } 24 | 25 | public enum JOB_OBJECT_LIMIT_FLAGS 26 | { 27 | JOB_OBJECT_LIMIT_WORKINGSET = 1, 28 | JOB_OBJECT_LIMIT_PROCESS_TIME = 2, 29 | JOB_OBJECT_LIMIT_JOB_TIME = 4, 30 | JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 8, 31 | JOB_OBJECT_LIMIT_AFFINITY = 16, 32 | JOB_OBJECT_LIMIT_PRIORITY_CLASS = 32, 33 | JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 64, 34 | JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 128, 35 | JOB_OBJECT_LIMIT_PROCESS_MEMORY = 256, 36 | JOB_OBJECT_LIMIT_JOB_MEMORY = 512, 37 | JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 1024, 38 | JOB_OBJECT_LIMIT_BREAKAWAY_OK = 2048, 39 | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 4096, 40 | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 8192, 41 | JOB_OBJECT_LIMIT_SUBSET_AFFINITY = 16384, 42 | } 43 | 44 | [DllImport("kernel32.dll", SetLastError = true)] 45 | public static extern bool SetInformationJobObject( 46 | SafeFileHandle hJob, 47 | JOBOBJECTINFOCLASS JobObjectInfoClass, 48 | ref JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo, 49 | int cbJobObjectInfoLength); 50 | 51 | [DllImport("kernel32.dll", SetLastError = true)] 52 | public static extern bool AssignProcessToJobObject(SafeFileHandle hJob, IntPtr hProcess); 53 | 54 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 55 | public static extern SafeFileHandle CreateJobObject(IntPtr lpJobAttributes, string lpName); 56 | 57 | public struct IO_COUNTERS 58 | { 59 | public ulong ReadOperationCount; 60 | public ulong WriteOperationCount; 61 | public ulong OtherOperationCount; 62 | public ulong ReadTransferCount; 63 | public ulong WriteTransferCount; 64 | public ulong OtherTransferCount; 65 | } 66 | 67 | public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION 68 | { 69 | public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; 70 | public IO_COUNTERS IoInfo; 71 | public UIntPtr ProcessMemoryLimit; 72 | public UIntPtr JobMemoryLimit; 73 | public UIntPtr PeakProcessMemoryUsed; 74 | public UIntPtr PeakJobMemoryUsed; 75 | } 76 | 77 | public struct JOBOBJECT_BASIC_LIMIT_INFORMATION 78 | { 79 | public long PerProcessUserTimeLimit; 80 | public long PerJobUserTimeLimit; 81 | public JOB_OBJECT_LIMIT_FLAGS LimitFlags; 82 | public UIntPtr MinimumWorkingSetSize; 83 | public UIntPtr MaximumWorkingSetSize; 84 | public uint ActiveProcessLimit; 85 | public UIntPtr Affinity; 86 | public uint PriorityClass; 87 | public uint SchedulingClass; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/VsixTesting/Interop/Ntdll.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1310 // Field names should not contain underscore 3 | 4 | namespace VsixTesting.Interop 5 | { 6 | using System; 7 | using System.Runtime.InteropServices; 8 | 9 | internal class Ntdll 10 | { 11 | public const int STATUS_SUCCESS = 0; 12 | 13 | [DllImport("ntdll.dll")] 14 | public static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref PROCESS_BASIC_INFORMATION processInformation, int processInformationLength, out int returnLength); 15 | 16 | public struct PROCESS_BASIC_INFORMATION 17 | { 18 | public IntPtr ExitStatus; 19 | public IntPtr PebBaseAddress; 20 | public IntPtr AffinityMask; 21 | public IntPtr BasePriority; 22 | public UIntPtr UniqueProcessId; 23 | public IntPtr InheritedFromUniqueProcessId; 24 | public int Size => Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/VsixTesting/Interop/Ole32.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1649 // File name should match first type name 3 | #pragma warning disable SA1602 // Enumeration items should be documented 4 | #pragma warning disable SA1310 // Field names should not contain underscore 5 | 6 | namespace VsixTesting.Interop 7 | { 8 | using System; 9 | using System.Runtime.InteropServices; 10 | using System.Runtime.InteropServices.ComTypes; 11 | 12 | internal static class Ole32 13 | { 14 | public const int S_OK = 0; 15 | 16 | internal enum PENDINGMSG 17 | { 18 | PENDINGMSG_CANCELCALL, 19 | PENDINGMSG_WAITNOPROCESS, 20 | PENDINGMSG_WAITDEFPROCESS, 21 | } 22 | 23 | internal enum SERVERCALL 24 | { 25 | SERVERCALL_ISHANDLED, 26 | SERVERCALL_REJECTED, 27 | SERVERCALL_RETRYLATER, 28 | } 29 | 30 | [ComImport] 31 | [Guid("00000016-0000-0000-C000-000000000046")] 32 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 33 | internal interface IMessageFilter 34 | { 35 | [PreserveSig] 36 | int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); 37 | [PreserveSig] 38 | int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); 39 | [PreserveSig] 40 | int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); 41 | } 42 | 43 | [DllImport("ole32.dll")] 44 | public static extern int CoRegisterMessageFilter(IMessageFilter lpMessageFilter, out IMessageFilter lplpMessageFilter); 45 | 46 | [DllImport("ole32.dll")] 47 | public static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc); 48 | } 49 | } -------------------------------------------------------------------------------- /src/VsixTesting/Interop/Psapi.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Interop 4 | { 5 | using System; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | internal class Psapi 10 | { 11 | [DllImport("psapi.dll")] 12 | public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, int nSize); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/VsixTesting/Interop/User32.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Interop 4 | { 5 | using System; 6 | using System.Runtime.InteropServices; 7 | 8 | internal class User32 9 | { 10 | [DllImport("user32.dll", SetLastError = true)] 11 | public static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect); 12 | 13 | public struct RECT 14 | { 15 | public int Left; 16 | public int Top; 17 | public int Right; 18 | public int Bottom; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/VsixTesting/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | using System.Runtime.CompilerServices; 4 | 5 | [assembly: InternalsVisibleTo("VsixTesting.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3")] 6 | [assembly: InternalsVisibleTo("VsixTesting.MSTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3")] 7 | [assembly: InternalsVisibleTo("VsixTesting.Xunit, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3")] 8 | [assembly: InternalsVisibleTo("VsixTesting.Xunit.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d55b6b77e75142dc724b2e721fcddb95564623d7404f5c98bd595ca11c8e2ee1bcd63f46c24e446be029895312590edf9eb489e9ad5d10442a3ef87b965cc7f8400d829f68b8c8ee8a2c90901b5fb9d38acb0d82c8aae828390b8ff2d21bea1be6b296c3ba41bc57852c18784c7e6c078f2e48ee9b40af8353023ed6667afbb3")] 9 | -------------------------------------------------------------------------------- /src/VsixTesting/Remote.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting 4 | { 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Reflection; 9 | using System.Runtime.Remoting; 10 | using System.Runtime.Remoting.Channels; 11 | using System.Windows; 12 | using VsixTesting.Remoting; 13 | 14 | internal sealed class Remote 15 | { 16 | private static ResolveEventHandler resolver; 17 | private static IChannel channel; 18 | 19 | public static void SetAssemblyResolver(string directory, bool requireExactVersion) 20 | { 21 | resolver = (object sender, ResolveEventArgs eventArgs) => 22 | { 23 | var assemblyName = new AssemblyName(eventArgs.Name); 24 | 25 | if (Environment.Is64BitProcess) 26 | { 27 | if (assemblyName.Name == "Microsoft.VisualStudio.Shell.11.0") 28 | { 29 | if (File.Exists("VsixTesting.Xunit.Tests.dll")) 30 | return Assembly.Load("Microsoft.VisualStudio.Shell.15.0"); 31 | } 32 | } 33 | 34 | var assemblyFile = Path.Combine(directory, $"{assemblyName.Name}.dll"); 35 | if (File.Exists(assemblyFile)) 36 | { 37 | var assembly = Assembly.LoadFrom(assemblyFile); 38 | 39 | if (requireExactVersion == false || assembly.GetName().Version == assemblyName.Version) 40 | return assembly; 41 | } 42 | 43 | return null; 44 | }; 45 | 46 | AppDomain.CurrentDomain.AssemblyResolve += resolver; 47 | } 48 | 49 | public static void RegisterIpcChannel(string name, string portName, bool ensureSecurity) 50 | => channel = ChannelUtil.RegisterIpcChannel(name, portName, ensureSecurity); 51 | 52 | public static void RegisterWellKnownServiceType(string assemblyName, string fullTypeName, WellKnownObjectMode mode) 53 | => RemotingConfiguration.RegisterWellKnownServiceType(Assembly.Load(assemblyName).GetType(fullTypeName), fullTypeName, mode); 54 | 55 | public static void AutoKillWhenProcessExits(int processId) 56 | { 57 | var process = Process.GetProcessById(processId); 58 | process.EnableRaisingEvents = true; 59 | process.Exited += (_, e) => Process.GetCurrentProcess().Kill(); 60 | } 61 | 62 | public static void InitServiceProviderGlobalProvider() 63 | { 64 | Application.Current.Dispatcher.Invoke(() => 65 | { 66 | var majorVersion = Process.GetCurrentProcess().MainModule.FileVersionInfo.FileMajorPart; 67 | 68 | // Initialize ServiceProvider.GlobalProvider in Visual Studio 2010 SDK and above 69 | for (var shellVersion = majorVersion; shellVersion >= 10; shellVersion--) 70 | { 71 | var type = Type.GetType($"Microsoft.VisualStudio.Shell.ServiceProvider, Microsoft.VisualStudio.Shell.{shellVersion}.0, Version={shellVersion}.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false); 72 | var prop = type?.GetProperty("GlobalProvider", new Type[0]); 73 | prop?.GetValue(null); 74 | } 75 | 76 | // Initialize AsyncServiceProvider.GlobalProvider in Visual Studio 2015 SDK and above 77 | for (var shellVersion = majorVersion; shellVersion >= 14; shellVersion--) 78 | { 79 | var type = Type.GetType($"Microsoft.VisualStudio.Shell.AsyncServiceProvider, Microsoft.VisualStudio.Shell.{shellVersion}.0, Version={shellVersion}.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false); 80 | var prop = type?.GetProperty("GlobalProvider", new Type[0]); 81 | prop?.GetValue(null); 82 | } 83 | }); 84 | } 85 | 86 | public static void Dispose() 87 | { 88 | if (channel != null) 89 | { 90 | ChannelServices.UnregisterChannel(channel); 91 | channel = null; 92 | } 93 | 94 | if (resolver != null) 95 | { 96 | AppDomain.CurrentDomain.AssemblyResolve -= resolver; 97 | resolver = null; 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/VsixTesting/Remoting/ChannelUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Remoting 4 | { 5 | using System.Collections; 6 | using System.Runtime.Remoting.Channels; 7 | using System.Runtime.Remoting.Channels.Ipc; 8 | using System.Runtime.Serialization.Formatters; 9 | 10 | internal class ChannelUtil 11 | { 12 | public static IpcChannel RegisterIpcChannel(string name, string portName, bool ensureSecurity) 13 | { 14 | var ipcChannel = new IpcChannel( 15 | properties: new Hashtable 16 | { 17 | ["name"] = name, 18 | ["portName"] = portName, 19 | }, 20 | clientSinkProvider: new BinaryClientFormatterSinkProvider { }, 21 | serverSinkProvider: new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full }); 22 | 23 | ChannelServices.RegisterChannel(ipcChannel, ensureSecurity); 24 | return ipcChannel; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/VsixTesting/Remoting/IRemoteComInvoker.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Remoting 4 | { 5 | using System; 6 | using System.Runtime.InteropServices; 7 | 8 | [ComImport] 9 | [Guid("00020400-0000-0000-C000-000000000046")] 10 | [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] 11 | internal interface IRemoteComInvoker 12 | { 13 | object InvokeMethod(string assemblyPath, string @class, string method, object obj, params object[] arguments); 14 | } 15 | } -------------------------------------------------------------------------------- /src/VsixTesting/Utilities/EmbeddedResourceUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Utilities 4 | { 5 | using System; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Xml.Linq; 10 | 11 | internal static class EmbeddedResourceUtil 12 | { 13 | public static string ExtractResource(Assembly assembly, string resourceName) 14 | { 15 | var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); 16 | Directory.CreateDirectory(dir); 17 | 18 | var path = Path.Combine(dir, resourceName); 19 | using (var resourceStream = assembly.GetManifestResourceStream(resourceName)) 20 | { 21 | if (resourceStream == null) 22 | throw new ArgumentOutOfRangeException(nameof(resourceName), $"Embedded resource {resourceName} doesn't exist in assembly {assembly.GetName().FullName}."); 23 | 24 | using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) 25 | resourceStream.CopyTo(fileStream); 26 | } 27 | 28 | return path; 29 | } 30 | 31 | public static void ApplyDateTime(string filePath, Assembly assembly, string resourceName) 32 | { 33 | var metadataName = $"{assembly.GetName().Name}.EmbeddedFileDateTime.xml"; 34 | using (var metadataStream = assembly.GetManifestResourceStream(metadataName)) 35 | { 36 | if (metadataStream == null) 37 | throw new FileNotFoundException($"Embedded resource {metadataName} doesn't exist in assembly {assembly.GetName().FullName}."); 38 | var xml = XDocument.Load(metadataStream); 39 | var item = xml.Descendants("Item").First(i => i.Attribute("Name").Value == resourceName); 40 | File.SetCreationTime(filePath, new DateTime(long.Parse(item.Attribute("CreationTime").Value))); 41 | File.SetLastWriteTime(filePath, new DateTime(long.Parse(item.Attribute("LastWriteTime").Value))); 42 | File.SetLastAccessTime(filePath, new DateTime(long.Parse(item.Attribute("LastAccessTime").Value))); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/VsixTesting/Utilities/KillProcessJob.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Utilities 4 | { 5 | using System; 6 | using System.Diagnostics; 7 | using System.Runtime.InteropServices; 8 | using Common; 9 | using Microsoft.Win32.SafeHandles; 10 | using static Interop.Kernel32; 11 | 12 | internal sealed class KillProcessJob 13 | { 14 | private SafeFileHandle jobObject; 15 | 16 | public KillProcessJob(params Process[] processes) 17 | { 18 | jobObject = CreateJobObject(IntPtr.Zero, null); 19 | if (jobObject.IsInvalid) 20 | throw new InvalidOperationException("Failed to create job object"); 21 | 22 | var jobObjectELI = default(JOBOBJECT_EXTENDED_LIMIT_INFORMATION); 23 | jobObjectELI.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_FLAGS.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 24 | 25 | if (!SetInformationJobObject(jobObject, JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation, ref jobObjectELI, Marshal.SizeOf(jobObjectELI))) 26 | throw new InvalidOperationException("Failed to set job object information"); 27 | 28 | foreach (var process in processes) 29 | { 30 | if (!AssignProcessToJobObject(jobObject, process.Handle)) 31 | throw new InvalidOperationException($"Failed to assign process {process.GetProcessName()} to job object"); 32 | } 33 | } 34 | 35 | public void Release() 36 | { 37 | if (!jobObject.IsClosed) 38 | { 39 | var jobObjectELI = default(JOBOBJECT_EXTENDED_LIMIT_INFORMATION); 40 | jobObjectELI.BasicLimitInformation.LimitFlags = 0; 41 | SetInformationJobObject(jobObject, JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation, ref jobObjectELI, Marshal.SizeOf(jobObjectELI)); 42 | jobObject.Dispose(); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/VsixTesting/Utilities/ProcessUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Utilities 4 | { 5 | using System; 6 | using System.Diagnostics; 7 | using VsixTesting.Interop; 8 | 9 | internal struct ProcessUtil 10 | { 11 | public static Process TryGetParentProcess(Process process) 12 | { 13 | try 14 | { 15 | var processInformation = default(Ntdll.PROCESS_BASIC_INFORMATION); 16 | if (Ntdll.NtQueryInformationProcess(process.Handle, 0, ref processInformation, processInformation.Size, out var returnLength) == Ntdll.STATUS_SUCCESS) 17 | return Process.GetProcessById(processInformation.InheritedFromUniqueProcessId.ToInt32()); 18 | } 19 | catch 20 | { 21 | } 22 | 23 | return null; 24 | } 25 | 26 | public static Process TryGetParentProcess(Process process, Func predicate) 27 | { 28 | while (true) 29 | { 30 | process = TryGetParentProcess(process); 31 | if (process == null) 32 | return null; 33 | 34 | if (predicate(process)) 35 | return process; 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/VsixTesting/Utilities/RetryMessageFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Utilities 4 | { 5 | using System; 6 | using System.Diagnostics; 7 | using System.Threading; 8 | using static Interop.Ole32; 9 | 10 | internal sealed class RetryMessageFilter : IMessageFilter, IDisposable 11 | { 12 | private const int RetryImmediately = 99; 13 | private const int CancelCall = -1; 14 | private readonly IMessageFilter prevFilter; 15 | private TimeSpan timeout = TimeSpan.FromSeconds(30); 16 | 17 | public RetryMessageFilter() 18 | { 19 | Debug.Assert(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA, "This class requires a STA thread."); 20 | if (CoRegisterMessageFilter(this, out prevFilter) != S_OK) 21 | throw new InvalidOperationException("Cannot register ole message filter."); 22 | } 23 | 24 | ~RetryMessageFilter() 25 | => Dispose(false); 26 | 27 | int IMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo) 28 | => (int)SERVERCALL.SERVERCALL_ISHANDLED; 29 | 30 | int IMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) 31 | { 32 | if (dwRejectType == (int)SERVERCALL.SERVERCALL_RETRYLATER && dwTickCount < timeout.TotalMilliseconds) 33 | return RetryImmediately; 34 | 35 | return CancelCall; 36 | } 37 | 38 | int IMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType) 39 | => (int)PENDINGMSG.PENDINGMSG_WAITDEFPROCESS; 40 | 41 | public void Dispose() 42 | { 43 | Dispose(true); 44 | GC.SuppressFinalize(this); 45 | } 46 | 47 | private void Dispose(bool disposing) 48 | => CoRegisterMessageFilter(prevFilter, out _); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/VsixTesting/Utilities/ScreenshotUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.Utilities 4 | { 5 | using System; 6 | using System.Drawing; 7 | using System.Drawing.Imaging; 8 | using static Interop.User32; 9 | 10 | internal static class ScreenshotUtil 11 | { 12 | public static void CaptureWindow(IntPtr hwnd, string path) 13 | { 14 | var rect = default(RECT); 15 | GetWindowRect(hwnd, ref rect); 16 | CaptureScreenArea( 17 | path, 18 | left: rect.Left, 19 | top: rect.Top, 20 | width: rect.Right - rect.Left, 21 | height: rect.Bottom - rect.Top); 22 | } 23 | 24 | public static void CaptureScreenArea(string path, int left, int top, int width, int height) 25 | { 26 | using (var bitmap = new Bitmap(width, height)) 27 | using (var image = Graphics.FromImage(bitmap)) 28 | { 29 | image.CopyFromScreen( 30 | sourceX: left, 31 | sourceY: top, 32 | blockRegionSize: new Size(width, height), 33 | copyPixelOperation: CopyPixelOperation.SourceCopy, 34 | destinationX: 0, 35 | destinationY: 0); 36 | 37 | bitmap.Save(path, ImageFormat.Png); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/VsixTesting/Vs/VersionRange.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs 4 | { 5 | using System; 6 | using System.Text.RegularExpressions; 7 | using Common; 8 | 9 | internal class VersionRange 10 | { 11 | public VersionRange(string version) 12 | { 13 | var ver = Parse(version); 14 | Init(ver.Minimum, ver.Maximum); 15 | } 16 | 17 | public VersionRange(Version minVersion, Version maxVersion) 18 | => Init(minVersion, maxVersion); 19 | 20 | public Version Minimum { get; private set; } 21 | public Version Maximum { get; private set; } 22 | 23 | public static VersionRange Parse(string version) 24 | { 25 | if (string.IsNullOrWhiteSpace(version)) 26 | throw new ArgumentException($"Version cannot be empty.", nameof(version)); 27 | 28 | if (ParseSingleVersion(version, out VersionRange range) || ParseRange(version, out range)) 29 | return range; 30 | 31 | throw new NotSupportedException($"The version range format {version} is not supported."); 32 | } 33 | 34 | public static bool TryParse(string version, out VersionRange ver) 35 | { 36 | try 37 | { 38 | ver = Parse(version); 39 | return true; 40 | } 41 | catch 42 | { 43 | } 44 | 45 | ver = null; 46 | return false; 47 | } 48 | 49 | public override bool Equals(object obj) 50 | { 51 | return obj is VersionRange range 52 | && obj.GetType() == range.GetType() 53 | && Minimum == range.Minimum 54 | && Maximum == range.Maximum; 55 | } 56 | 57 | public override int GetHashCode() 58 | { 59 | var hashCode = 913158992; 60 | hashCode = (hashCode * -1521134295) + Minimum.GetHashCode(); 61 | hashCode = (hashCode * -1521134295) + Maximum.GetHashCode(); 62 | return hashCode; 63 | } 64 | 65 | public override string ToString() 66 | => $"[{Minimum.ToString()}-{Maximum.ToString()}]"; 67 | 68 | private void Init(Version minVersion, Version maxVersion) 69 | { 70 | Minimum = minVersion; 71 | Maximum = maxVersion; 72 | } 73 | 74 | private static bool ParseSingleVersion(string input, out VersionRange range) 75 | { 76 | input = NormalizeMajorVersion(input); 77 | var singleVersionMatch = Regex.Match(input, @"^(\d+)((?:\.\d){0,3})$"); 78 | if (!singleVersionMatch.Success) 79 | { 80 | range = null; 81 | return false; 82 | } 83 | 84 | var isMissingMinorVersion = string.IsNullOrEmpty(singleVersionMatch.Groups[2].Value); 85 | 86 | if (isMissingMinorVersion) 87 | { 88 | var majorVersion = int.Parse(singleVersionMatch.Groups[1].Value); 89 | range = new VersionRange( 90 | minVersion: new Version(majorVersion, 0), 91 | maxVersion: new Version(majorVersion, int.MaxValue)); 92 | } 93 | else 94 | { 95 | var version = Version.Parse(input); 96 | range = new VersionRange(version, version); 97 | } 98 | 99 | return true; 100 | } 101 | 102 | private static bool ParseRange(string version, out VersionRange range) 103 | { 104 | var rangeVersionMatch = Regex.Match(version, $@"^(\(|\[)?([\d|\.]+)-([\d|\.]+)?(\)|\])?$"); 105 | if (!rangeVersionMatch.Success) 106 | { 107 | range = null; 108 | return false; 109 | } 110 | 111 | var left = TextOr(rangeVersionMatch.Groups[1].Value, "["); 112 | var minimumVersionText = NormalizeVersion(rangeVersionMatch.Groups[2].Value, 0); 113 | var minimunVersion = new Version(minimumVersionText); 114 | 115 | var maximumVersionText = NormalizeVersion(rangeVersionMatch.Groups[3].Value, int.MaxValue); 116 | var maximumVersion = string.IsNullOrEmpty(maximumVersionText) 117 | ? new Version(int.MaxValue, int.MaxValue) 118 | : new Version(maximumVersionText); 119 | var right = TextOr(rangeVersionMatch.Groups[4].Value, "]"); 120 | 121 | if (left == "(" /* is exclusive*/) 122 | minimunVersion = minimunVersion.IncreaseMinorVersion(); 123 | if (right == ")" /* is exclusive*/) 124 | maximumVersion = maximumVersion.DecreaseMinorVersion(); 125 | range = new VersionRange(minimunVersion, maximumVersion); 126 | return true; 127 | } 128 | 129 | private static string NormalizeVersion(string version, int minorVersion) 130 | { 131 | version = NormalizeMajorVersion(version); 132 | var pattern = @"^(\d+)$"; 133 | if (Regex.IsMatch(version, pattern)) 134 | return version + "." + minorVersion; 135 | return version; 136 | } 137 | 138 | private static string TextOr(string text, string or) => 139 | !string.IsNullOrEmpty(text) ? text : or; 140 | 141 | private static string NormalizeMajorVersion(string version) 142 | { 143 | return Regex.Replace(version, @"^(\d{4})\b", a => 144 | { 145 | var majorVersion = VersionUtil.FromYear(int.Parse(a.Groups[1].Value)).Major; 146 | return majorVersion.ToString(); 147 | }); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /src/VsixTesting/Vs/VersionUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs 4 | { 5 | using System; 6 | 7 | internal static class VersionUtil 8 | { 9 | public static int GetYear(Version version) 10 | { 11 | switch (version.Major) 12 | { 13 | case 11: return 2012; 14 | case 12: return 2013; 15 | case 14: return 2015; 16 | case 15: return 2017; 17 | case 16: return 2019; 18 | case 17: return 2022; 19 | } 20 | 21 | throw new ArgumentOutOfRangeException(nameof(version)); 22 | } 23 | 24 | public static Version FromYear(int year) 25 | { 26 | switch (year) 27 | { 28 | case 2012: return new Version(11, 0); 29 | case 2013: return new Version(12, 0); 30 | case 2015: return new Version(14, 0); 31 | case 2017: return new Version(15, 0); 32 | case 2019: return new Version(16, 0); 33 | case 2022: return new Version(17, 0); 34 | default: throw new ArgumentOutOfRangeException(nameof(year), $"VS{year} is not supported."); 35 | } 36 | } 37 | 38 | public static Version FromVsVersion(VsVersion version) 39 | { 40 | return FromYear(GetYear(new Version((int)version, 0))); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/VsixTesting/Vs/VisualStudioUtil.Installer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Threading.Tasks; 11 | using Common; 12 | using VsixTesting.Utilities; 13 | 14 | internal static partial class VisualStudioUtil 15 | { 16 | public static async Task<(int InstallCount, string Output)> InstallExtensionsAsync(VsHive hive, IEnumerable extensions) 17 | { 18 | var (result, output) = await RunInstallerAsync(hive, new[] { "/Install" }.Concat(extensions.Select(e => QuotePath(e)))); 19 | return (result, output); 20 | } 21 | 22 | public static async Task IsProfileInitializedAsync(VsHive hive) 23 | { 24 | var (result, _) = await RunInstallerAsync(hive, new[] { "/IsProfileInitialized" }); 25 | return result == 1 ? true : false; 26 | } 27 | 28 | public static async Task<(int Result, string Output)> RunInstallerAsync(VsHive hive, IEnumerable args) 29 | { 30 | var suffix = hive.Version >= VersionUtil.FromVsVersion(VsVersion.VS2022) ? ".x64" : string.Empty; 31 | 32 | using (var visualStudioInstaller = new TempFile(EmbeddedResourceUtil.ExtractResource(Assembly.GetExecutingAssembly(), $"VsixTesting.Installer{suffix}.exe"))) 33 | { 34 | var process = Process.Start(new ProcessStartInfo 35 | { 36 | FileName = visualStudioInstaller.Path, 37 | Arguments = string.Join(" ", new string[] 38 | { 39 | "/ApplicationPath", QuotePath(hive.ApplicationPath), 40 | "/RootSuffix", hive.RootSuffix, 41 | }.Concat(args)), 42 | CreateNoWindow = true, 43 | UseShellExecute = false, 44 | RedirectStandardError = true, 45 | RedirectStandardOutput = true, 46 | }); 47 | 48 | await process.WaitForExitAsync(); 49 | 50 | if (process.ExitCode < 0) 51 | throw new Exception(process.StandardError.ReadToEnd()); 52 | 53 | return (process.ExitCode, process.StandardOutput.ReadToEnd()); 54 | } 55 | } 56 | 57 | private static string QuotePath(string str) => $"\"{str}\""; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/VsixTesting/Vs/VsHive.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs 4 | { 5 | using System; 6 | 7 | internal class VsHive 8 | { 9 | public VsHive(VsInstallation installation, string rootSuffix = "") 10 | { 11 | Installation = installation; 12 | RootSuffix = rootSuffix ?? throw new ArgumentNullException(nameof(rootSuffix)); 13 | } 14 | 15 | public VsInstallation Installation { get; set; } 16 | public Version Version => Installation.Version; 17 | public string Path => Installation.Path; 18 | public string ApplicationPath => Installation.ApplicationPath; 19 | public string RootSuffix { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/VsixTesting/Vs/VsInstallation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | internal class VsInstallation 10 | { 11 | public VsInstallation(Version version, string path, string name) 12 | { 13 | Version = version ?? throw new ArgumentNullException(nameof(version)); 14 | Name = name ?? throw new ArgumentNullException(nameof(name)); 15 | Path = path ?? throw new ArgumentNullException(nameof(path)); 16 | } 17 | 18 | public Version Version { get; } 19 | public string Path { get; } 20 | public string Name { get; } 21 | public string ApplicationPath => VisualStudioUtil.GetApplicationPath(Path); 22 | public IEnumerable PackageIds { get; set; } = Enumerable.Empty(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/VsixTesting/Vs/VsVersion.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | #pragma warning disable SA1602 // Enumeration items should be documented 3 | 4 | namespace Vs 5 | { 6 | internal enum VsVersion 7 | { 8 | VS2012 = 11, 9 | VS2013 = 12, 10 | VS2015 = 14, 11 | VS2017 = 15, 12 | VS2019 = 16, 13 | VS2022 = 17, 14 | } 15 | } -------------------------------------------------------------------------------- /src/VsixTesting/VsTestSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using Vs; 9 | 10 | internal class VsTestSettings : ITestSettings 11 | { 12 | internal static readonly VsTestSettings Defaults = new VsTestSettings(); 13 | public string Version { get; set; } = "2012-"; 14 | public string RootSuffix { get; set; } = "Exp"; 15 | public int LaunchTimeoutInSeconds { get; set; } = 90; 16 | public bool DebugMixedMode { get; set; } = false; 17 | public bool ResetSettings { get; set; } = false; 18 | public bool SecureChannel { get; set; } = false; 19 | public string ExtensionsDirectory { get; set; } = string.Empty; 20 | public string ScreenshotsDirectory { get; set; } = "Screenshots"; 21 | public bool ReuseInstance { get; set; } = true; 22 | public bool TakeScreenshotOnFailure { get; set; } = false; 23 | public bool UIThread { get; set; } = false; 24 | 25 | public IEnumerable SupportedVersionRanges => Version.Split(';').Select(v => new VersionRange(v)); 26 | public TimeSpan GetLaunchTimeout() => TimeSpan.FromSeconds(LaunchTimeoutInSeconds); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/VsixTesting/VsixTesting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net452 4 | true 5 | true 6 | false 7 | true 8 | embedded 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | true 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /stylecop.json: -------------------------------------------------------------------------------- 1 |  { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "xmlHeader": false, 6 | "companyName": "Jose Torres", 7 | "copyrightText": "Copyright (c) 2018 {companyName}. All rights reserved. Licensed under the {licenseName}. See {licenseFile} file in the project root for full license information.", 8 | "variables": { 9 | "licenseName": "Apache License, Version 2.0", 10 | "licenseFile": "LICENSE.md" 11 | } 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /test/VsixTesting.Installer.Tests/VsixTesting.Installer.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net452 4 | true 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/VsixTesting.Tests/Common/ThreadUtilTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common.Tests 4 | { 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | 9 | public class ThreadUtilTests 10 | { 11 | [Fact] 12 | public async Task RunOnStaThreadAsync() 13 | { 14 | Assert.Equal(1520, await ThreadUtil.RunOnStaThreadAsync(async () => 15 | { 16 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 17 | var id = Thread.CurrentThread.ManagedThreadId; 18 | await Task.Yield(); 19 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 20 | Assert.Equal(Thread.CurrentThread.ManagedThreadId, id); 21 | return 1520; 22 | })); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/VsixTesting.Tests/Common/VersionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Common.Tests 4 | { 5 | using System; 6 | using Xunit; 7 | 8 | public class VersionExtensions 9 | { 10 | [Theory] 11 | [InlineData(11, 1, 11, 0)] 12 | [InlineData(int.MaxValue, 2, int.MaxValue, 1)] 13 | [InlineData(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue - 1)] 14 | [InlineData(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)] 15 | void IncreaseMinorVersion(int expectedMajor, int expectedMinor, int major, int minor) 16 | => Assert.Equal(new Version(expectedMajor, expectedMinor), new Version(major, minor).IncreaseMinorVersion()); 17 | 18 | [Theory] 19 | [InlineData(10, int.MaxValue, 11, 0)] 20 | [InlineData(0, 0, 0, 1)] 21 | [InlineData(0, 0, 0, 0)] 22 | void DecreaseMinorVersion(int expectedMajor, int expectedMinor, int major, int minor) 23 | => Assert.Equal(new Version(expectedMajor, expectedMinor), new Version(major, minor).DecreaseMinorVersion()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/VsixTesting.Tests/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "S.")] 4 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = ".")] 5 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements should be ordered by access", Justification = ".")] 6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1204:Static elements should appear before instance elements", Justification = "S.")] 7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:Parameter should not span multiple lines", Justification = ".")] 8 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1400:Access modifier should be declared", Justification = ".")] 9 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = ".")] 10 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces must not be omitted", Justification = "S.")] 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1516:Elements must be separated by blank line", Justification = "S.")] 12 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1520:Use braces consistently", Justification = "S.")] 13 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements must be documented", Justification = "P.")] 14 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "S.")] 15 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop", "SA0001", Justification = ".")] -------------------------------------------------------------------------------- /test/VsixTesting.Tests/Vs/VersionRangeTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs.Tests 4 | { 5 | using System; 6 | using Xunit; 7 | 8 | public class VersionRangeTests 9 | { 10 | [Theory] 11 | [InlineData("11.0", "11.0")] 12 | [InlineData("12.0", "12.0")] 13 | [InlineData("13.0", "13.0")] 14 | [InlineData("14.0", "14.0")] 15 | [InlineData("15.0", "15.0")] 16 | [InlineData("15.5.6", "15.5.6")] 17 | [InlineData("15.5.6.7", "15.5.6.7")] 18 | void ParseSingleVersion(string expected, string input) 19 | { 20 | var range = VersionRange.Parse(input); 21 | Assert.Equal(Version.Parse(expected), range.Minimum); 22 | Assert.Equal(Version.Parse(expected), range.Maximum); 23 | } 24 | 25 | [Theory] 26 | [InlineData("11.0", "16.0", "[11.0-16.0]")] 27 | [InlineData("11.0", "15.2147483647", "[11.0-16.0)")] 28 | [InlineData("11.1", "16.0", "(11.0-16.0]")] 29 | [InlineData("11.0", "15.2147483647", "11-15")] 30 | [InlineData("11.0", "11.2147483647", "11")] 31 | [InlineData("15.0", "15.2147483647", "15")] 32 | [InlineData("11.1.2.7", "12.5.5.7", "[11.1.2.7-12.5.5.7]")] 33 | void ParseRange(string minVersionExpected, string maxVersionExpected, string input) 34 | { 35 | var range = VersionRange.Parse(input); 36 | Assert.Equal(Version.Parse(minVersionExpected), range.Minimum); 37 | Assert.Equal(Version.Parse(maxVersionExpected), range.Maximum); 38 | } 39 | 40 | [Theory] 41 | [InlineData("11.0", "11.0-")] 42 | [InlineData("11.0", "[11.0-")] 43 | [InlineData("11.0", "11.0-]")] 44 | [InlineData("11.0", "[11.0-]")] 45 | [InlineData("11.1", "(11.0-]")] 46 | void ParseUnboundedMaxVersion(string expected, string input) 47 | { 48 | var range = VersionRange.Parse(input); 49 | Assert.Equal(Version.Parse(expected), range.Minimum); 50 | Assert.Equal(new Version(int.MaxValue, int.MaxValue), range.Maximum); 51 | } 52 | 53 | [Theory] 54 | [InlineData("(11.0")] 55 | [InlineData("[11.0")] 56 | [InlineData("11.0]")] 57 | [InlineData("11.0)")] 58 | [InlineData("[-]")] 59 | void InvalidFormatThrows(string input) 60 | => Assert.Throws(() => VersionRange.Parse(input)); 61 | 62 | [Fact] 63 | void EmptyFormatThrows() 64 | => Assert.Throws(() => VersionRange.Parse(string.Empty)); 65 | 66 | [Theory] 67 | [InlineData(1, 2)] 68 | [InlineData(1, 3)] 69 | [InlineData(2, 3)] 70 | void Equality(int major, int minor) 71 | { 72 | var version = new Version(major, minor, 3, 7); 73 | var range = new VersionRange(version, version); 74 | Assert.Equal(range, range); 75 | Assert.Equal(range.GetHashCode(), range.GetHashCode()); 76 | } 77 | 78 | [Theory] 79 | [InlineData(11, 0, 15, int.MaxValue, "2012-2017")] 80 | [InlineData(11, 0, 15, int.MaxValue, "11-15")] 81 | [InlineData(11, 0, 11, int.MaxValue, "2012")] 82 | [InlineData(11, 0, 11, int.MaxValue, "11")] 83 | [InlineData(11, 1, 15, 1, "2012.1-2017.1")] 84 | [InlineData(11, 1, 15, 1, "2012.1-15.1")] 85 | [InlineData(11, 1, 15, 1, "11.1-2017.1")] 86 | [InlineData(11, 1, 11, 1, "2012.1")] 87 | [InlineData(11, 1, 11, 1, "11.1")] 88 | void TestAllowedRangeFormats(int expectedMinMajor, int expectedMinMinor, int expectedMaxMajor, int expectedMaxMinor, string input) 89 | { 90 | var expectedVersionRange = new VersionRange(new Version(expectedMinMajor, expectedMinMinor), new Version(expectedMaxMajor, expectedMaxMinor)); 91 | var versionRange = new VersionRange(input); 92 | Assert.Equal(expectedVersionRange, versionRange); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /test/VsixTesting.Tests/Vs/VersionTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs.Tests 4 | { 5 | using System; 6 | using Xunit; 7 | 8 | public class VersionTests 9 | { 10 | [Theory] 11 | [InlineData(2012, 11)] 12 | [InlineData(2013, 12)] 13 | [InlineData(2015, 14)] 14 | [InlineData(2017, 15)] 15 | [InlineData(2019, 16)] 16 | [InlineData(2022, 17)] 17 | void GetYearFromMajorVersion(int expectedYear, int majorVersion) 18 | => Assert.Equal(expectedYear, VersionUtil.GetYear(new Version(majorVersion, 0))); 19 | 20 | [Theory] 21 | [InlineData(11, 2012)] 22 | [InlineData(12, 2013)] 23 | [InlineData(14, 2015)] 24 | [InlineData(15, 2017)] 25 | [InlineData(16, 2019)] 26 | [InlineData(17, 2022)] 27 | void GetMajorVersionFromYear(int expectedMajorVersion, int year) 28 | => Assert.Equal(VersionUtil.FromYear(year).Major, expectedMajorVersion); 29 | 30 | [Theory] 31 | [InlineData(10)] // Not supported 32 | [InlineData(13)] // Doesn't exist 33 | [InlineData(18)] // Not supported yet 34 | void BadMajorVersions(int major) 35 | => Assert.Throws(() => VersionUtil.GetYear(new Version(major, 0))); 36 | 37 | [Theory] 38 | [InlineData(2010)] // Not supported 39 | [InlineData(2014)] // Doesn't exist 40 | [InlineData(2016)] // Doesn't exist 41 | [InlineData(2018)] // Doesn't exist 42 | [InlineData(2020)] // Doesn't exist 43 | [InlineData(2021)] // Doesn't exist 44 | [InlineData(2023)] // Not supported yet 45 | void BadYears(int year) 46 | => Assert.Throws(() => VersionUtil.FromYear(year)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/VsixTesting.Tests/Vs/VisualStudioUtilTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace Vs.Tests 4 | { 5 | using System; 6 | using Xunit; 7 | 8 | public class VisualStudioUtilTests 9 | { 10 | [Fact] 11 | void ThrowOnBadProcessNameWorks() 12 | { 13 | VisualStudioUtil.ThrowOnBadProcessName("DEVenv"); 14 | Assert.Throws(() => VisualStudioUtil.ThrowOnBadProcessName("devenv2")); 15 | Assert.Throws(() => VisualStudioUtil.ThrowOnBadProcessName("wdexpress")); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/VsixTesting.Tests/VsixTesting.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net452 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/AssemblyTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using System.Linq; 6 | using System.Reflection; 7 | using Xunit; 8 | 9 | public class AssemblyTests 10 | { 11 | [Fact] 12 | void DeployedTestFrameworkExists() 13 | { 14 | Assert.NotNull(GetTestFrameworkAttribute()); 15 | } 16 | 17 | [Fact] 18 | void DeployedTestFrameworkIsValid() 19 | { 20 | var testFrameworkTypeName = GetTestFrameworkAttribute().ConstructorArguments[0].Value; 21 | var assemblyName = GetTestFrameworkAttribute().ConstructorArguments[1].Value; 22 | 23 | Assert.Equal(typeof(Xunit.VsTestFramework).Assembly.GetName().Name, assemblyName); 24 | Assert.Equal(typeof(Xunit.VsTestFramework).FullName, testFrameworkTypeName); 25 | } 26 | 27 | static CustomAttributeData GetTestFrameworkAttribute() 28 | { 29 | return Assembly.GetExecutingAssembly().CustomAttributes 30 | .FirstOrDefault(c => c.AttributeType == typeof(TestFrameworkAttribute)); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/ClassFixtureTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using System; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using Xunit; 9 | 10 | public class ClassFixtureTests : IClassFixture 11 | { 12 | private static int classCounter = 0; 13 | 14 | public ClassFixtureTests(MyClassFixture fixture) 15 | => Assert.Equal(++classCounter, ++fixture.Counter); 16 | 17 | [VsFact] 18 | void Fact() => Assert.True(true); 19 | 20 | [VsTheory] 21 | [InlineData(0)] 22 | void Theory(int zero) => Assert.Equal(0, zero); 23 | } 24 | 25 | public class MyClassFixture : IDisposable 26 | { 27 | public int Counter { get; set; } = 0; 28 | 29 | public MyClassFixture() 30 | { 31 | Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 32 | } 33 | 34 | public void Dispose() 35 | { 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/CollectionFixtureTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using Xunit; 8 | 9 | public class CollectionFixtureTests 10 | { 11 | private static int counter = 0; 12 | 13 | [Collection("CollectionName")] 14 | public class First 15 | { 16 | public First(TestCollectionFixture collectionFixture) 17 | { 18 | Assert.Equal(++counter, ++collectionFixture.Counter); 19 | } 20 | 21 | [VsFact] 22 | void Fact() => Assert.True(true); 23 | 24 | [VsTheory] 25 | [InlineData(0)] 26 | void Theory(int zero) => Assert.Equal(0, zero); 27 | } 28 | 29 | [Collection("CollectionName")] 30 | public class Second 31 | { 32 | public Second(TestCollectionFixture collectionFixture) 33 | { 34 | Assert.Equal(++counter, ++collectionFixture.Counter); 35 | } 36 | 37 | [VsFact] 38 | void Fact() => Assert.True(true); 39 | 40 | [VsTheory] 41 | [InlineData(0)] 42 | void Theory(int zero) => Assert.Equal(0, zero); 43 | } 44 | } 45 | 46 | [CollectionDefinition("CollectionName")] 47 | public class TestCollectionFixtureDefinition : ICollectionFixture 48 | { 49 | } 50 | 51 | public class TestCollectionFixture 52 | { 53 | public int Counter { get; set; } = 0; 54 | 55 | public TestCollectionFixture() 56 | => Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/InstancesTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using VsixTesting; 6 | using Xunit; 7 | 8 | public class InstancesTests 9 | { 10 | [Fact] 11 | public void VisualStudioTest() 12 | { 13 | new Instances().VisualStudio(); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Internal/ExceptionTestCaseTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | #pragma warning disable CS0618 // Type or member is obsolete 4 | 5 | namespace VsixTesting.XunitX.Tests 6 | { 7 | using System; 8 | using System.Collections.Concurrent; 9 | using System.Linq; 10 | using System.Threading; 11 | using VsixTesting.XunitX.Internal.Utilities; 12 | using VsixTesting.XunitX.Tests.Utilities; 13 | using Xunit; 14 | using Xunit.Abstractions; 15 | using Xunit.Internal; 16 | using Xunit.Sdk; 17 | 18 | public class ExceptionTestCaseTests 19 | { 20 | [Fact] 21 | void SerializationWorks() 22 | { 23 | var exceptionTestCase = CreateTestCase(); 24 | 25 | var info = new XunitSerializationInfo(); 26 | exceptionTestCase.Serialize(info); 27 | 28 | var deserializedTestCase = new ExceptionTestCase(); 29 | deserializedTestCase.Deserialize(info); 30 | 31 | Assert.Equal(exceptionTestCase.Types, deserializedTestCase.Types); 32 | Assert.Equal(exceptionTestCase.Messages, deserializedTestCase.Messages); 33 | Assert.Equal(exceptionTestCase.StackTraces, deserializedTestCase.StackTraces); 34 | Assert.Equal(exceptionTestCase.ParentIndices, deserializedTestCase.ParentIndices); 35 | } 36 | 37 | [Fact] 38 | async void TestRunnerWorks() 39 | { 40 | var bus = new MessageCollector(); 41 | await CreateTestCase().RunAsync(new NullMessageSink(), bus, new string[] { }, new ExceptionAggregator(), new CancellationTokenSource()); 42 | 43 | var testFailed = bus.Messages.OfType().FirstOrDefault(); 44 | 45 | Assert.NotNull(testFailed); 46 | Assert.Equal(testFailed.ExceptionTypes, testFailed.ExceptionTypes); 47 | Assert.Equal(testFailed.Messages, testFailed.Messages); 48 | Assert.Equal(testFailed.StackTraces, testFailed.StackTraces); 49 | Assert.Equal(testFailed.ExceptionParentIndices, testFailed.ExceptionParentIndices); 50 | } 51 | 52 | static ExceptionTestCase CreateTestCase() 53 | { 54 | try 55 | { 56 | throw new Exception("Hello"); 57 | } 58 | catch (Exception e) 59 | { 60 | var testMethod = Util.CreateTestMethod(typeof(ExceptionTestCaseTests), nameof(SerializationWorks)); 61 | var exceptionTestCase = new ExceptionTestCase(new Exception("World", e), new NullMessageSink(), default, testMethod); 62 | return exceptionTestCase; 63 | } 64 | } 65 | 66 | private class MessageCollector : IMessageBus 67 | { 68 | public ConcurrentBag Messages { get; } = new ConcurrentBag(); 69 | 70 | public void Dispose() 71 | { 72 | } 73 | 74 | public bool QueueMessage(IMessageSinkMessage message) 75 | { 76 | Messages.Add(message); 77 | return true; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Internal/VsInstanceTestCaseTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | #pragma warning disable CS0618 // Type or member is obsolete 4 | 5 | namespace VsixTesting.XunitX.Tests 6 | { 7 | using VsixTesting.XunitX.Internal; 8 | using Xunit; 9 | using Xunit.Internal; 10 | using Xunit.Sdk; 11 | 12 | public class VsInstanceTestCaseTests 13 | { 14 | [Fact] 15 | void SerializationWorks() 16 | { 17 | var info = new XunitSerializationInfo(); 18 | var exceptionTestCase = new VsInstanceTestCase("VS 2015", "c:/path/to/devenv.exe", "Exp", new NullMessageSink(), default, default); 19 | exceptionTestCase.MergeSettings(new VsTestSettings { DebugMixedMode = true, ExtensionsDirectory = "dir" }); 20 | exceptionTestCase.Serialize(info); 21 | 22 | var deserializedTestCase = new VsInstanceTestCase(); 23 | deserializedTestCase.Deserialize(info); 24 | 25 | Assert.Equal(exceptionTestCase.InstanceId, deserializedTestCase.InstanceId); 26 | Assert.Equal(exceptionTestCase.ApplicationPath, deserializedTestCase.ApplicationPath); 27 | Assert.Equal(exceptionTestCase.RootSuffix, deserializedTestCase.RootSuffix); 28 | Assert.Equal(exceptionTestCase.DebugMixedMode, deserializedTestCase.DebugMixedMode); 29 | Assert.Equal(exceptionTestCase.ExtensionDirectories, deserializedTestCase.ExtensionDirectories); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Internal/VsSkippedDataRowTestCaseTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | #pragma warning disable CS0618 // Type or member is obsolete 4 | 5 | namespace VsixTesting.XunitX.Tests 6 | { 7 | using VsixTesting.XunitX.Internal; 8 | using VsixTesting.XunitX.Tests.Utilities; 9 | using Xunit; 10 | using Xunit.Internal; 11 | using Xunit.Sdk; 12 | 13 | public class VsSkippedDataRowTestCaseTests 14 | { 15 | [Fact] 16 | void SerializationWorks() 17 | { 18 | var info = new XunitSerializationInfo(); 19 | var testMethod = Util.CreateTestMethod(typeof(VsSkippedDataRowTestCaseTests), nameof(SerializationWorks)); 20 | 21 | var exceptionTestCase = new VsSkippedDataRowTestCase("VS 2015", default, default, testMethod, "No Reason"); 22 | exceptionTestCase.Serialize(info); 23 | 24 | var deserializedTestCase = new VsSkippedDataRowTestCase(); 25 | deserializedTestCase.Deserialize(info); 26 | 27 | Assert.Equal(exceptionTestCase.InstanceId, deserializedTestCase.InstanceId); 28 | Assert.Equal(deserializedTestCase.SkipReason, deserializedTestCase.SkipReason); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Internal/VsTestCaseTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | #pragma warning disable CS0618 // Type or member is obsolete 4 | 5 | namespace VsixTesting.XunitX.Tests 6 | { 7 | using VsixTesting.XunitX.Internal; 8 | using VsixTesting.XunitX.Tests.Utilities; 9 | using Xunit; 10 | using Xunit.Internal; 11 | 12 | public class VsTestCaseTests 13 | { 14 | [Fact] 15 | void SerializationWorks() 16 | { 17 | var testMethod = Util.CreateTestMethod(typeof(VsTestCaseTests), nameof(SerializationWorks)); 18 | var info = new XunitSerializationInfo(); 19 | 20 | var exceptionTestCase = new VsTestCase("VS 2015", "C:/path/to/instance.exe", default, default, default, testMethod); 21 | exceptionTestCase.Serialize(info); 22 | 23 | var deserializedTestCase = new VsTestCase(); 24 | deserializedTestCase.Deserialize(info); 25 | 26 | Assert.Equal(exceptionTestCase.InstanceId, deserializedTestCase.InstanceId); 27 | Assert.Equal(deserializedTestCase.SkipReason, deserializedTestCase.SkipReason); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/ProjectReferenceTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using Xunit; 9 | 10 | public class ProjectReferenceTests 11 | { 12 | const bool CopyVsixDefaultValue = true; 13 | 14 | [Theory] 15 | [InlineData(null, "CopyVsix/Default")] 16 | [InlineData(true, "CopyVsix", "True.Relative")] 17 | [InlineData(true, "CopyVsix", "True.Absolute")] 18 | [InlineData(false, "CopyVsix", "False")] 19 | void CopyVsix(bool? copyVsix, params string[] paths) 20 | { 21 | var path = Path.Combine(paths.Concat(new[] { "VsixTesting.Invoker.vsix" }).ToArray()); 22 | Assert.Equal(copyVsix.GetValueOrDefault(CopyVsixDefaultValue), File.Exists(path)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "VsixTesting.Xunit.Tests": { 4 | "commandName": "Executable", 5 | "environmentVariables": { 6 | "APPVEYOR_API_URL": "123456" 7 | }, 8 | "executablePath": "$(XunitConsolePathX86)", 9 | "commandLineArgs": "$(AssemblyName).dll -appveyor" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Util.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests.Utilities 4 | { 5 | using System; 6 | using System.Linq; 7 | using Xunit.Sdk; 8 | 9 | class Util 10 | { 11 | internal static TestMethod CreateTestMethod(Type @class, string name) 12 | { 13 | var assemblyInfo = new ReflectionAssemblyInfo(@class.Assembly); 14 | var testAssembly = new TestAssembly(assemblyInfo); 15 | var testCollection = new TestCollection(testAssembly, null, string.Empty); 16 | var classInfo = new ReflectionTypeInfo(@class); 17 | var testClass = new TestClass(testCollection, classInfo); 18 | var method = testClass.Class.GetMethods(true).First(m => m.Name == name); 19 | var testMethod = new TestMethod(testClass, method); 20 | return testMethod; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/Utilities/AsyncTestSyncContextExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests.Utilities 4 | { 5 | using System.Reflection; 6 | using System.Threading; 7 | using Xunit.Sdk; 8 | 9 | static class AsyncTestSyncContextExtensions 10 | { 11 | public static SynchronizationContext GetInnerSyncContext(this AsyncTestSyncContext context) 12 | { 13 | return (SynchronizationContext)context.GetType() 14 | .GetField("innerContext", BindingFlags.NonPublic | BindingFlags.Instance) 15 | .GetValue(context); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/VsFactTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Threading; 9 | using System.Windows.Threading; 10 | using Microsoft.VisualStudio.ComponentModelHost; 11 | using Microsoft.VisualStudio.Shell; 12 | using Microsoft.VisualStudio.Shell.Interop; 13 | using Vs; 14 | using VsixTesting.XunitX.Internal.Utilities; 15 | using VsixTesting.XunitX.Tests.Utilities; 16 | using Xunit; 17 | using Xunit.Abstractions; 18 | using Xunit.Sdk; 19 | using Task = System.Threading.Tasks.Task; 20 | 21 | public class VsFactTests 22 | { 23 | [VsFact] 24 | void FactWorks() 25 | => Assert.True(true); 26 | 27 | [VsFact] 28 | void FactRunningInsideVisualStudio() 29 | => Assert.NotNull(VisualStudioUtil.GetDTEObject(Process.GetCurrentProcess())); 30 | 31 | [VsFact(Skip = "Fact Skip works.")] 32 | void FactSkipWorks() 33 | => throw new NotImplementedException(); 34 | 35 | [VsFact(Version = "2014", Skip = "Invalid Fact Skip works.")] 36 | void InvalidFactSkipWorks() 37 | => throw new NotImplementedException(); 38 | 39 | #if CI 40 | [VsFact(Version = "2012", RootSuffix = "FilterTest")] 41 | void FilterWorks() 42 | => throw new NotImplementedException(); 43 | #endif 44 | 45 | [VsFact] 46 | void WebBrowsingServiceIsAvailable() 47 | => Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 48 | 49 | public class ScreenShotTests : IDisposable 50 | { 51 | private const string ScreenshotsDirectory = "Screenshots"; 52 | 53 | public ScreenShotTests() 54 | { 55 | if (Directory.Exists(ScreenshotsDirectory)) 56 | Directory.Delete(ScreenshotsDirectory, true); 57 | } 58 | 59 | [VsFact(TakeScreenshotOnFailure = true)] 60 | void DirectoryNotEmpty() 61 | { 62 | throw new FakeException(); 63 | } 64 | 65 | public void Dispose() 66 | { 67 | Assert.NotEmpty(Directory.GetFiles(ScreenshotsDirectory)); 68 | Directory.Delete(ScreenshotsDirectory, true); 69 | } 70 | } 71 | 72 | [VsTestSettings(UIThread = true)] 73 | public class UIThreadTests 74 | { 75 | readonly SynchronizationContext synchronizationContext; 76 | readonly int constructorThreadId; 77 | private ITestOutputHelper h; 78 | 79 | public UIThreadTests(ITestOutputHelper h) 80 | { 81 | this.h = h; 82 | Assert.IsType(SynchronizationContext.Current); 83 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 84 | constructorThreadId = Thread.CurrentThread.ManagedThreadId; 85 | synchronizationContext = SynchronizationContext.Current; 86 | } 87 | 88 | [VsFact] 89 | void MethodHasSameManagedThreadAsConstructor() 90 | => Assert.Equal(constructorThreadId, Thread.CurrentThread.ManagedThreadId); 91 | 92 | [VsFact] 93 | void MethodHasSameSyncContextTypeAsConstructor() 94 | { 95 | var asyncTestSyncContext = (AsyncTestSyncContext)SynchronizationContext.Current; 96 | Assert.IsType(synchronizationContext.GetType(), asyncTestSyncContext.GetInnerSyncContext()); 97 | } 98 | 99 | [VsFact] 100 | async void ContinuationRunsOnSameManagedThread() 101 | { 102 | var currentThread = Thread.CurrentThread.ManagedThreadId; 103 | Assert.Equal(currentThread, Thread.CurrentThread.ManagedThreadId); 104 | await Task.Yield(); 105 | Assert.Equal(currentThread, Thread.CurrentThread.ManagedThreadId); 106 | } 107 | 108 | [VsFact] 109 | async void ContinuationRunsOnSameStaThread() 110 | { 111 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 112 | await Task.Yield(); 113 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 114 | } 115 | 116 | [VsFact] 117 | async void ContinuationRunsOnSameSynchronizationContext() 118 | { 119 | var asyncTestSyncContext = (AsyncTestSyncContext)SynchronizationContext.Current; 120 | Assert.IsType(asyncTestSyncContext.GetInnerSyncContext()); 121 | await Task.Yield(); 122 | Assert.IsType(SynchronizationContext.Current); 123 | } 124 | } 125 | 126 | public class ServiceProviderTests 127 | { 128 | [VsFact(Version = "2017-", ReuseInstance = false, UIThread = false)] 129 | public async Task CanUseAsyncServiceProviderGetServiceAsync() 130 | { 131 | // Check AsyncServiceProvider.GlobalProvider from Microsoft.VisualStudio.Shell.14.0 132 | // has been initialized when running in Visual Studio 2017+ 133 | var type = GetShellType($"Microsoft.VisualStudio.Shell.AsyncServiceProvider", shellVersion: 14); 134 | var prop = type?.GetProperty("GlobalProvider", new Type[0]); 135 | dynamic asyncServiceProvider = prop.GetValue(null); 136 | var componentModel = await asyncServiceProvider.GetServiceAsync(typeof(SComponentModel)); 137 | Assert.NotNull(componentModel); 138 | } 139 | 140 | [VsFact(Version = "2017-", ReuseInstance = false, UIThread = false)] 141 | public void CanFindVsTaskLibraryHelperServiceInstance() 142 | { 143 | // Check ServiceProvider.GlobalProvider from Microsoft.VisualStudio.Shell.14.0 144 | // has been initialized when running in Visual Studio 2017+ 145 | var type = GetShellType("Microsoft.VisualStudio.Shell.VsTaskLibraryHelper", shellVersion: 14); 146 | var prop = type?.GetProperty("ServiceInstance", new Type[0]); 147 | var serviceInstance = prop?.GetValue(null); 148 | Assert.NotNull(serviceInstance); 149 | } 150 | 151 | private static Type GetShellType(string typeName, int shellVersion = 14) 152 | { 153 | var type = Type.GetType($"{typeName}, Microsoft.VisualStudio.Shell.{shellVersion}.0, Version={shellVersion}.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false); 154 | return type != null ? type : (shellVersion == 14 ? GetShellType(typeName, 15) : null); 155 | } 156 | } 157 | 158 | public class BackgroundThreadTests 159 | { 160 | [VsFact(UIThread = false)] 161 | async void BackgroundThread() 162 | { 163 | var asyncTestSyncContext = (AsyncTestSyncContext)SynchronizationContext.Current; 164 | Assert.IsType(asyncTestSyncContext.GetInnerSyncContext()); 165 | await Task.Yield(); 166 | Assert.IsType(SynchronizationContext.Current); 167 | } 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/VsTestCaseFactoryTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using System; 6 | using System.Linq; 7 | using Vs; 8 | using VsixTesting.XunitX.Internal; 9 | using Xunit; 10 | 11 | public class VsTestCaseFactoryTests 12 | { 13 | [Fact] 14 | void OneInstancePerMajorVersion() 15 | { 16 | var installations = new VsInstallation[] 17 | { 18 | new VsInstallation(new Version(15, 1), string.Empty, string.Empty), 19 | new VsInstallation(new Version(15, 7), string.Empty, "VisualStudio/15.7.3+27703.2026"), 20 | }; 21 | 22 | Assert.Single(VsTestCaseFactory.FilterInstallations(installations, new VsTestSettings())); 23 | } 24 | 25 | [Theory] 26 | [InlineData("14", "VisualStudio/14.0.0")] 27 | [InlineData("14-", "VisualStudio/14.0.0")] 28 | [InlineData("15", "VisualStudio/15.0.2")] 29 | void FilterInstallations_PreferSpecificInstallation(string version, string expectedName) 30 | { 31 | var installationPath = "C:\\Program Files (x86)\\Microsoft Visual Studio 15.0"; 32 | var installations = new[] 33 | { 34 | new VsInstallation(new Version(14, 0, 0), string.Empty, "VisualStudio/14.0.0"), 35 | new VsInstallation(new Version(15, 0, 3), string.Empty, "VisualStudio/15.0.3"), 36 | new VsInstallation(new Version(15, 0, 2), installationPath, "VisualStudio/15.0.2"), 37 | new VsInstallation(new Version(15, 0, 1), string.Empty, "VisualStudio/15.0.1"), 38 | }; 39 | 40 | var applicationPath = VisualStudioUtil.GetApplicationPath(installationPath); 41 | var installation = VsTestCaseFactory.FilterInstallations(installations, new VsTestSettings { Version = version }, preferedAppPath: applicationPath).First(); 42 | Assert.Equal(expectedName, installation.Name); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/VsTheoryTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 Jose Torres. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE.md file in the project root for full license information. 2 | 3 | namespace VsixTesting.XunitX.Tests 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Threading; 9 | using System.Windows.Threading; 10 | using Microsoft.VisualStudio.Shell; 11 | using Microsoft.VisualStudio.Shell.Interop; 12 | using Vs; 13 | using VsixTesting.XunitX.Tests.Utilities; 14 | using Xunit; 15 | using Xunit.Sdk; 16 | using Task = System.Threading.Tasks.Task; 17 | 18 | public class VsTheoryTests 19 | { 20 | private static HashSet theoryWorksNumbers = new HashSet(); 21 | 22 | [VsTheory] 23 | [InlineData(1)] 24 | [InlineData(100)] 25 | [InlineData(5)] 26 | void TheoryWorks(int number) 27 | { 28 | Assert.True(theoryWorksNumbers.Add(number)); 29 | Assert.True(number == 1 || number == 5 || number == 100); 30 | } 31 | 32 | [VsTheory] 33 | [InlineData(0)] 34 | void TheoryRunningInsideVisualStudio(int zero) 35 | { 36 | Assert.NotNull(VisualStudioUtil.GetDTEObject(Process.GetCurrentProcess())); 37 | Assert.Equal(0, zero); 38 | } 39 | 40 | [VsTheory] 41 | [MemberData(nameof(Data))] 42 | void TheoryWithNonSerializableClassDataWorks(ClassData c) 43 | { 44 | Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 45 | Assert.True(c.Value == 1 || c.Value == 5 || c.Value == 100); 46 | } 47 | 48 | [VsTheory(Skip = "Theory Skip works.")] 49 | [InlineData(0)] 50 | [InlineData(1)] 51 | void TheorySkipWorks(int n) 52 | => throw new NotImplementedException(); 53 | 54 | [VsTheory] 55 | [InlineData(0)] 56 | [InlineData(1, Skip = "Theory Data Row Skip works.")] 57 | void TheoryDataRowSkipWorks(int n) 58 | => Assert.Equal(0, n); 59 | 60 | public static IEnumerable Data => 61 | new List 62 | { 63 | new object[] { new ClassData(1) }, 64 | new object[] { new ClassData(100) }, 65 | new object[] { new ClassData(5) }, 66 | }; 67 | 68 | [VsTheory] 69 | [InlineData(0)] 70 | void WebBrowsingServiceIsAvailable(int zero) 71 | { 72 | Assert.NotNull(Package.GetGlobalService(typeof(SVsWebBrowsingService))); 73 | Assert.Equal(0, zero); 74 | } 75 | 76 | [VsTestSettings(UIThread = true)] 77 | public class UIThreadTests 78 | { 79 | readonly SynchronizationContext synchronizationContext; 80 | readonly int constructorThreadId; 81 | 82 | public UIThreadTests() 83 | { 84 | Assert.IsType(SynchronizationContext.Current); 85 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 86 | constructorThreadId = Thread.CurrentThread.ManagedThreadId; 87 | synchronizationContext = SynchronizationContext.Current; 88 | } 89 | 90 | [VsTheory] 91 | [InlineData(0)] 92 | void MethodHasSameManagedThreadAsConstructor(int zero) 93 | => Assert.Equal(constructorThreadId, Thread.CurrentThread.ManagedThreadId); 94 | 95 | [VsTheory] 96 | [InlineData(0)] 97 | void MethodHasSameSyncContextTypeAsConstructor(int zero) 98 | { 99 | var asyncTestSyncContext = (AsyncTestSyncContext)SynchronizationContext.Current; 100 | Assert.IsType(synchronizationContext.GetType(), asyncTestSyncContext.GetInnerSyncContext()); 101 | } 102 | 103 | [VsTheory] 104 | [InlineData(0)] 105 | async void ContinuationRunsOnSameManagedThread(int zero) 106 | { 107 | var currentThread = Thread.CurrentThread.ManagedThreadId; 108 | Assert.Equal(currentThread, Thread.CurrentThread.ManagedThreadId); 109 | await Task.Yield(); 110 | Assert.Equal(currentThread, Thread.CurrentThread.ManagedThreadId); 111 | } 112 | 113 | [VsTheory] 114 | [InlineData(0)] 115 | async void ContinuationRunsOnSameStaThread(int zero) 116 | { 117 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 118 | await Task.Yield(); 119 | Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); 120 | } 121 | 122 | [VsTheory] 123 | [InlineData(0)] 124 | async void ContinuationRunsOnSameSynchronizationContext(int zero) 125 | { 126 | var asyncTestSyncContext = (AsyncTestSyncContext)SynchronizationContext.Current; 127 | Assert.IsType(asyncTestSyncContext.GetInnerSyncContext()); 128 | await Task.Yield(); 129 | Assert.IsType(SynchronizationContext.Current); 130 | } 131 | } 132 | } 133 | 134 | public class ClassData 135 | { 136 | public int Value { get; } 137 | 138 | public ClassData(int v) 139 | { 140 | Value = v; 141 | } 142 | 143 | public override string ToString() 144 | { 145 | return this.Value.ToString(); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /test/VsixTesting.Xunit.Tests/VsixTesting.Xunit.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net452 6 | true 7 | VsixTesting.XunitX.Tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "0.1", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/master$", 6 | "^refs/tags/v\\d\\.\\d" 7 | ] 8 | } --------------------------------------------------------------------------------