├── .config
└── dotnet-tools.json
├── .editorconfig
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── build.yml
│ ├── publish.yml
│ └── update.yml
├── .gitignore
├── CONTRIBUTING.md
├── GitVersion.yml
├── LICENSE
├── Machine.Specifications.Runner.ReSharper.sln
├── README.md
├── build.ps1
├── build.sh
├── build
├── build.cs
└── build.csproj
├── images
├── icon.png
└── pluginIcon.svg
├── install-plugin.ps1
├── plugin.xml
└── src
├── Machine.Specifications.Runner.ReSharper.Adapters
├── Discovery
│ ├── Discoverer.cs
│ ├── DiscoveryCache.cs
│ ├── IMspecController.cs
│ ├── IMspecDiscoverySink.cs
│ ├── MspecController.cs
│ ├── MspecDiscoveryException.cs
│ └── MspecDiscoverySink.cs
├── Elements
│ ├── BehaviorElement.cs
│ ├── ContextElement.cs
│ ├── ContextInfoExtensions.cs
│ ├── IBehaviorElement.cs
│ ├── IContextElement.cs
│ ├── IMspecElement.cs
│ ├── ISpecificationElement.cs
│ ├── SpecificationElement.cs
│ └── SpecificationInfoExtensions.cs
├── ExceptionResultExtensions.cs
├── Execution
│ ├── ConcurrentLookup.cs
│ ├── ElementCache.cs
│ ├── ExecutionAdapterRunListener.cs
│ ├── Executor.cs
│ ├── IExecutionListener.cs
│ ├── RunContext.cs
│ ├── RunTracker.cs
│ ├── TaskWrapper.cs
│ └── TestExecutionListener.cs
├── Listeners
│ ├── AdapterListener.cs
│ ├── AssemblyInfoExtensions.cs
│ ├── ContextInfoExtensions.cs
│ ├── ExceptionResultExtensions.cs
│ ├── IRunListener.cs
│ ├── LoggingRunListener.cs
│ ├── ResultExtensions.cs
│ ├── SpecificationInfoExtensions.cs
│ ├── StatusExtensions.cs
│ ├── TestAssemblyInfo.cs
│ ├── TestContextInfo.cs
│ ├── TestError.cs
│ ├── TestRunResult.cs
│ ├── TestSpecificationInfo.cs
│ └── TestStatus.cs
├── Machine.Specifications.Runner.ReSharper.Adapters.csproj
├── MspecReSharperId.cs
├── MspecRunner.cs
├── RemoteTaskBuilder.cs
└── RemoteTaskDepot.cs
├── Machine.Specifications.Runner.ReSharper.Tasks
├── Machine.Specifications.Runner.ReSharper.Tasks.csproj
├── MspecBehaviorSpecificationRemoteTask.cs
├── MspecContextRemoteTask.cs
├── MspecRemoteTask.cs
├── MspecSpecificationRemoteTask.cs
└── MspecTestContainer.cs
├── Machine.Specifications.Runner.ReSharper.Tests
├── Adapters
│ ├── Discovery
│ │ └── MspecDiscoverySinkTests.cs
│ ├── Elements
│ │ ├── BehaviorElementTests.cs
│ │ ├── ContextElementTests.cs
│ │ └── SpecificationElementTests.cs
│ ├── Execution
│ │ ├── ConcurrentLookupTests.cs
│ │ ├── ElementCacheTests.cs
│ │ ├── ExecutionAdapterRunListenerTests.cs
│ │ ├── RunContextTests.cs
│ │ ├── TaskWrapperTests.cs
│ │ └── TestExecutionListenerTests.cs
│ └── RemoteTaskDepotTests.cs
├── Fixtures
│ ├── ElementFixtures.cs
│ └── RemoteTaskFixtures.cs
├── Machine.Specifications.Runner.ReSharper.Tests.csproj
└── Tasks
│ ├── MspecBehaviorSpecificationRemoteTaskTests.cs
│ ├── MspecContextRemoteTaskTests.cs
│ └── MspecSpecificationRemoteTaskTests.cs
└── Machine.Specifications.Runner.ReSharper
├── Elements
├── IMspecTestElement.cs
├── MspecBehaviorSpecificationTestElement.cs
├── MspecContextTestElement.cs
└── MspecSpecificationTestElement.cs
├── EnumerableExtensions.cs
├── Machine.Specifications.Runner.ReSharper.csproj
├── Mappings
├── MspecBehaviorSpecificationMapping.cs
├── MspecContextMapping.cs
├── MspecElementMapping.cs
├── MspecElementMappingKeys.cs
└── MspecSpecificationMapping.cs
├── MspecProviderSettings.cs
├── MspecPsiFileExplorer.cs
├── MspecServiceProvider.cs
├── MspecTestExplorerFromArtifacts.cs
├── MspecTestExplorerFromFile.cs
├── MspecTestExplorerFromMetadata.cs
├── MspecTestExplorerFromTestRunner.cs
├── MspecTestMetadataExplorer.cs
├── MspecTestProvider.cs
├── MspecTestRunnerOrchestrator.cs
├── Options
└── MspecPage.cs
├── Reflection
├── IAttributeInfo.cs
├── IFieldInfo.cs
├── ITypeInfo.cs
├── MetadataAttributeInfoAdapter.cs
├── MetadataExtensions.cs
├── MetadataFieldInfoAdapter.cs
├── MetadataTypeInfoAdapter.cs
├── PsiAttributeInfoAdapter.cs
├── PsiExtensions.cs
├── PsiFieldInfoAdapter.cs
├── PsiTypeInfoAdapter.cs
├── ReflectionExtensions.cs
└── UnknownTypeInfoAdapter.cs
├── Resources
├── Machine.png
└── MspecThemedIcons.cs
├── Rules
└── EnsureAncestorsAddedToExecutedElementsRule.cs
├── Runner
├── AgentManagerHost.cs
├── IAgentManagerHost.cs
└── MspecTestRunnerRunStrategy.cs
├── TypeInfoFactory.cs
├── UnitTestElementFactory.cs
└── ZoneMarker.cs
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "gitversion.tool": {
6 | "version": "5.12.0",
7 | "commands": [
8 | "dotnet-gitversion"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; This file is for unifying the coding style for different editors and IDEs.
2 | ; More information at http://EditorConfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.{cs,cake,ps1}]
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [*.gold]
15 | insert_final_newline = false
16 | trim_trailing_whitespace = false
17 |
18 | [*.{csproj,targets,props}]
19 | indent_style = space
20 | indent_size = 2
21 |
--------------------------------------------------------------------------------
/.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/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "nuget"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 |
8 | - package-ecosystem: "nuget"
9 | directory: "/build"
10 | schedule:
11 | interval: "weekly"
12 |
13 | - package-ecosystem: "nuget"
14 | directory: "/.config"
15 | schedule:
16 | interval: "weekly"
17 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | env:
10 | DOTNET_NOLOGO: true
11 |
12 | jobs:
13 | build:
14 | name: build
15 | runs-on: windows-latest
16 |
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v3
20 | - name: Fetch all tags and branches
21 | run: git fetch --prune --unshallow
22 | - name: Build
23 | run: ./build.ps1
24 | - name: Upload artifacts
25 | uses: actions/upload-artifact@v4
26 | with:
27 | path: |
28 | artifacts/*.nupkg
29 | artifacts/*.zip
30 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: publish
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | env:
8 | DOTNET_NOLOGO: true
9 |
10 | jobs:
11 | publish:
12 | name: publish
13 | runs-on: windows-latest
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v3
18 | - name: Fetch all tags and branches
19 | run: git fetch --prune --unshallow
20 | - name: Deploy
21 | env:
22 | JETBRAINS_API_KEY: ${{ secrets.JETBRAINS_API_KEY }}
23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 | run: ./build.ps1 publish
25 | - name: Upload artifacts
26 | uses: actions/upload-artifact@v4
27 | with:
28 | path: |
29 | artifacts/*.nupkg
30 | artifacts/*.zip
31 | - uses: NBTX/upload-release-assets@v1
32 | if: github.event_name == 'release'
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 | with:
36 | targets: 'artifacts/*.*'
37 | upload_url: ${{ github.event.release.upload_url }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/update.yml:
--------------------------------------------------------------------------------
1 | name: update
2 |
3 | on:
4 | schedule:
5 | - cron: '0 8 * * 5'
6 |
7 | jobs:
8 | update:
9 | name: update
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v3
14 | - run: dotnet restore
15 | - uses: MeilCli/nuget-update-check-action@v3
16 | id: outdated
17 | with:
18 | include_prerelease: true
19 | frameworks: net461
20 | - uses: juztcode/gitter-github-action@v1
21 | if: steps.outdated.outputs.has_nuget_update != 'false' && contains(steps.outdated.outputs.nuget_update_json, 'JetBrains.ReSharper.SDK')
22 | with:
23 | room-id: ${{ secrets.GITTER_ROOM_ID }}
24 | token: ${{ secrets.GITTER_API_KEY }}
25 | text: ${{ steps.outdated.outputs.nuget_update_text }}
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 | _JetPackages/
42 |
43 | # NUNIT
44 | *.VisualState.xml
45 | TestResult.xml
46 |
47 | # Build Results of an ATL Project
48 | [Dd]ebugPS/
49 | [Rr]eleasePS/
50 | dlldata.c
51 |
52 | # Benchmark Results
53 | BenchmarkDotNet.Artifacts/
54 |
55 | # .NET Core
56 | project.lock.json
57 | project.fragment.lock.json
58 | artifacts/
59 |
60 | # StyleCop
61 | StyleCopReport.xml
62 |
63 | # Files built by Visual Studio
64 | *_i.c
65 | *_p.c
66 | *_h.h
67 | *.ilk
68 | *.meta
69 | *.obj
70 | *.iobj
71 | *.pch
72 | *.pdb
73 | *.ipdb
74 | *.pgc
75 | *.pgd
76 | *.rsp
77 | *.sbr
78 | *.tlb
79 | *.tli
80 | *.tlh
81 | *.tmp
82 | *.tmp_proj
83 | *_wpftmp.csproj
84 | *.log
85 | *.vspscc
86 | *.vssscc
87 | .builds
88 | *.pidb
89 | *.svclog
90 | *.scc
91 |
92 | # Chutzpah Test files
93 | _Chutzpah*
94 |
95 | # Visual C++ cache files
96 | ipch/
97 | *.aps
98 | *.ncb
99 | *.opendb
100 | *.opensdf
101 | *.sdf
102 | *.cachefile
103 | *.VC.db
104 | *.VC.VC.opendb
105 |
106 | # Visual Studio profiler
107 | *.psess
108 | *.vsp
109 | *.vspx
110 | *.sap
111 |
112 | # Visual Studio Trace Files
113 | *.e2e
114 |
115 | # TFS 2012 Local Workspace
116 | $tf/
117 |
118 | # Guidance Automation Toolkit
119 | *.gpState
120 |
121 | # ReSharper is a .NET coding add-in
122 | _ReSharper*/
123 | #*.[Rr]e[Ss]harper
124 | *.DotSettings.user
125 |
126 | # JustCode is a .NET coding add-in
127 | .JustCode
128 |
129 | # TeamCity is a build add-in
130 | _TeamCity*
131 |
132 | # DotCover is a Code Coverage Tool
133 | *.dotCover
134 |
135 | # AxoCover is a Code Coverage Tool
136 | .axoCover/*
137 | !.axoCover/settings.json
138 |
139 | # Visual Studio code coverage results
140 | *.coverage
141 | *.coveragexml
142 |
143 | # NCrunch
144 | _NCrunch_*
145 | .*crunch*.local.xml
146 | nCrunchTemp_*
147 |
148 | # MightyMoose
149 | *.mm.*
150 | AutoTest.Net/
151 |
152 | # Web workbench (sass)
153 | .sass-cache/
154 |
155 | # Installshield output folder
156 | [Ee]xpress/
157 |
158 | # DocProject is a documentation generator add-in
159 | DocProject/buildhelp/
160 | DocProject/Help/*.HxT
161 | DocProject/Help/*.HxC
162 | DocProject/Help/*.hhc
163 | DocProject/Help/*.hhk
164 | DocProject/Help/*.hhp
165 | DocProject/Help/Html2
166 | DocProject/Help/html
167 |
168 | # Click-Once directory
169 | publish/
170 |
171 | # Publish Web Output
172 | *.[Pp]ublish.xml
173 | *.azurePubxml
174 | # Note: Comment the next line if you want to checkin your web deploy settings,
175 | # but database connection strings (with potential passwords) will be unencrypted
176 | *.pubxml
177 | *.publishproj
178 |
179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
180 | # checkin your Azure Web App publish settings, but sensitive information contained
181 | # in these scripts will be unencrypted
182 | PublishScripts/
183 |
184 | # NuGet Packages
185 | *.nupkg
186 | # The packages folder can be ignored because of Package Restore
187 | **/[Pp]ackages/*
188 | # except build/, which is used as an MSBuild target.
189 | !**/[Pp]ackages/build/
190 | # Uncomment if necessary however generally it will be regenerated when needed
191 | #!**/[Pp]ackages/repositories.config
192 | # NuGet v3's project.json files produces more ignorable files
193 | *.nuget.props
194 | *.nuget.targets
195 |
196 | # Microsoft Azure Build Output
197 | csx/
198 | *.build.csdef
199 |
200 | # Microsoft Azure Emulator
201 | ecf/
202 | rcf/
203 |
204 | # Windows Store app package directories and files
205 | AppPackages/
206 | BundleArtifacts/
207 | Package.StoreAssociation.xml
208 | _pkginfo.txt
209 | *.appx
210 |
211 | # Visual Studio cache files
212 | # files ending in .cache can be ignored
213 | *.[Cc]ache
214 | # but keep track of directories ending in .cache
215 | !?*.[Cc]ache/
216 |
217 | # Others
218 | ClientBin/
219 | ~$*
220 | *~
221 | *.dbmdl
222 | *.dbproj.schemaview
223 | *.jfm
224 | *.pfx
225 | *.publishsettings
226 | orleans.codegen.cs
227 |
228 | # Including strong name files can present a security risk
229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
230 | #*.snk
231 |
232 | # Since there are multiple workflows, uncomment next line to ignore bower_components
233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
234 | #bower_components/
235 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
236 | **/wwwroot/lib/
237 |
238 | # RIA/Silverlight projects
239 | Generated_Code/
240 |
241 | # Backup & report files from converting an old project file
242 | # to a newer Visual Studio version. Backup files are not needed,
243 | # because we have git ;-)
244 | _UpgradeReport_Files/
245 | Backup*/
246 | UpgradeLog*.XML
247 | UpgradeLog*.htm
248 | ServiceFabricBackup/
249 | *.rptproj.bak
250 |
251 | # SQL Server files
252 | *.mdf
253 | *.ldf
254 | *.ndf
255 |
256 | # Business Intelligence projects
257 | *.rdl.data
258 | *.bim.layout
259 | *.bim_*.settings
260 | *.rptproj.rsuser
261 |
262 | # Microsoft Fakes
263 | FakesAssemblies/
264 |
265 | # GhostDoc plugin setting file
266 | *.GhostDoc.xml
267 |
268 | # Node.js Tools for Visual Studio
269 | .ntvs_analysis.dat
270 | node_modules/
271 |
272 | # Visual Studio 6 build log
273 | *.plg
274 |
275 | # Visual Studio 6 workspace options file
276 | *.opt
277 |
278 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
279 | *.vbw
280 |
281 | # Visual Studio LightSwitch build output
282 | **/*.HTMLClient/GeneratedArtifacts
283 | **/*.DesktopClient/GeneratedArtifacts
284 | **/*.DesktopClient/ModelManifest.xml
285 | **/*.Server/GeneratedArtifacts
286 | **/*.Server/ModelManifest.xml
287 | _Pvt_Extensions
288 |
289 | # Paket dependency manager
290 | .paket/paket.exe
291 | paket-files/
292 |
293 | # FAKE - F# Make
294 | .fake/
295 |
296 | # JetBrains Rider
297 | .idea/
298 | *.sln.iml
299 |
300 | # CodeRush personal settings
301 | .cr/personal
302 |
303 | # Python Tools for Visual Studio (PTVS)
304 | __pycache__/
305 | *.pyc
306 |
307 | # Cake - Uncomment if you are using it
308 | tools/**
309 | !tools/packages.config
310 |
311 | # Tabs Studio
312 | *.tss
313 |
314 | # Telerik's JustMock configuration file
315 | *.jmconfig
316 |
317 | # BizTalk build output
318 | *.btp.cs
319 | *.btm.cs
320 | *.odx.cs
321 | *.xsd.cs
322 |
323 | # OpenCover UI analysis results
324 | OpenCover/
325 |
326 | # Azure Stream Analytics local run output
327 | ASALocalRun/
328 |
329 | # MSBuild Binary and Structured Log
330 | *.binlog
331 |
332 | # NVidia Nsight GPU debugger configuration file
333 | *.nvuser
334 |
335 | # MFractors (Xamarin productivity tool) working folder
336 | .mfractor/
337 |
338 | # Local History for Visual Studio
339 | .localhistory/
340 |
341 | # BeatPulse healthcheck temp database
342 | healthchecksdb
343 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | First of all, thank you for wanting to contribute to Machine.Specifications! We really appreciate all the awesome support we get from our community. We want to keep it as easy as possible for you to contribute changes that make Machine.Specifications better for you. There are a few guidelines that we need contributors to follow so that we can all work together happily.
4 |
5 | ## Preparation
6 |
7 | Before starting work on a new bug, feature, etc. ensure that an [issue](https://github.com/machine/machine.specifications/issues) has been raised. Indicate your intention to work on the issue by writing a comment against it. This will prevent duplication of effort. If the issue is a new feature, it's usually best to propose a design in the issue comments.
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | mode: ContinuousDelivery
2 | commit-message-incrementing: Disabled
3 | legacy-semver-padding: 0
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008 Machine Project
2 | Portions Copyright (c) 2008 Jacob Lewallen, Aaron Jensen
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
22 | *****************************
23 | Some parts licensed under MS-PL
24 | *****************************
25 |
26 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
27 |
28 | 1. Definitions
29 |
30 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
31 |
32 | A "contribution" is the original software, or any additions or changes to the software.
33 |
34 | A "contributor" is any person that distributes its contribution under this license.
35 |
36 | "Licensed patents" are a contributor's patent claims that read directly on its contribution.
37 |
38 | 2. Grant of Rights
39 |
40 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
41 |
42 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
43 |
44 | 3. Conditions and Limitations
45 |
46 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
47 |
48 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
49 |
50 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
51 |
52 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
53 |
54 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
--------------------------------------------------------------------------------
/Machine.Specifications.Runner.ReSharper.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33110.190
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Machine.Specifications.Runner.ReSharper", "src\Machine.Specifications.Runner.ReSharper\Machine.Specifications.Runner.ReSharper.csproj", "{79A3A8CA-CAEF-4DC6-83C9-E19A0C720B0E}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9725D2B9-232B-45A2-92CC-4F7CDE717C94}"
9 | ProjectSection(SolutionItems) = preProject
10 | .github\workflows\build.yml = .github\workflows\build.yml
11 | CONTRIBUTING.md = CONTRIBUTING.md
12 | .config\dotnet-tools.json = .config\dotnet-tools.json
13 | install-plugin.ps1 = install-plugin.ps1
14 | plugin.xml = plugin.xml
15 | .github\workflows\publish.yml = .github\workflows\publish.yml
16 | README.md = README.md
17 | .github\workflows\update.yml = .github\workflows\update.yml
18 | EndProjectSection
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Machine.Specifications.Runner.ReSharper.Adapters", "src\Machine.Specifications.Runner.ReSharper.Adapters\Machine.Specifications.Runner.ReSharper.Adapters.csproj", "{49062A8E-B51E-4DE6-AD81-9CEE62BBB25B}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Machine.Specifications.Runner.ReSharper.Tasks", "src\Machine.Specifications.Runner.ReSharper.Tasks\Machine.Specifications.Runner.ReSharper.Tasks.csproj", "{8788CA0C-44A2-40EF-9859-076313FE884E}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Machine.Specifications.Runner.ReSharper.Tests", "src\Machine.Specifications.Runner.ReSharper.Tests\Machine.Specifications.Runner.ReSharper.Tests.csproj", "{64E9C141-E05B-4CBD-B8FC-75AF0EBAF907}"
25 | EndProject
26 | Global
27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
28 | Debug|Any CPU = Debug|Any CPU
29 | Release|Any CPU = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {79A3A8CA-CAEF-4DC6-83C9-E19A0C720B0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {79A3A8CA-CAEF-4DC6-83C9-E19A0C720B0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {79A3A8CA-CAEF-4DC6-83C9-E19A0C720B0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {79A3A8CA-CAEF-4DC6-83C9-E19A0C720B0E}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {49062A8E-B51E-4DE6-AD81-9CEE62BBB25B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {49062A8E-B51E-4DE6-AD81-9CEE62BBB25B}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {49062A8E-B51E-4DE6-AD81-9CEE62BBB25B}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {49062A8E-B51E-4DE6-AD81-9CEE62BBB25B}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {8788CA0C-44A2-40EF-9859-076313FE884E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {8788CA0C-44A2-40EF-9859-076313FE884E}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {8788CA0C-44A2-40EF-9859-076313FE884E}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {8788CA0C-44A2-40EF-9859-076313FE884E}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {64E9C141-E05B-4CBD-B8FC-75AF0EBAF907}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {64E9C141-E05B-4CBD-B8FC-75AF0EBAF907}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {64E9C141-E05B-4CBD-B8FC-75AF0EBAF907}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {64E9C141-E05B-4CBD-B8FC-75AF0EBAF907}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {96104B85-D662-42D9-8F61-E8D8C126C3A8}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Machine.Specifications.Runner.Resharper
2 | Machine.Specifications provides a Resharper plugin to integrate with the ReSharper test runner. Search for `Machine.Specifications.Runner.Resharper` in the Resharper extension gallery.
3 |
4 | To open an issue, please visit the [core issue tracker](https://github.com/machine/machine.specifications/issues).
5 |
6 | ### Debugging
7 |
8 | 1) Run the `install-plugin.ps1` script the first time to install ReSharper to an experimental hive and deploy the plugin
9 | 2) Set the startup project to `Machine.Specifications.Runner.Resharper`
10 | 3) Run and debug from Visual Studio
11 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env pwsh
2 |
3 | Set-StrictMode -Version Latest
4 | $ErrorActionPreference = "Stop"
5 |
6 | if ($null -eq (Get-Command "dotnet" -ErrorAction Ignore)) {
7 | throw "Could not find 'dotnet', please install .NET SDK"
8 | }
9 |
10 | Push-Location (Split-Path $MyInvocation.MyCommand.Definition)
11 |
12 | try {
13 | & dotnet run --project build --no-launch-profile -- $args
14 | }
15 | finally {
16 | Pop-Location
17 | }
18 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euo pipefail
4 |
5 | if which dotnet > /dev/null; then
6 | dotnet run --project build --no-launch-profile -- "$@"
7 | else
8 | echo "error(1): Could not find 'dotnet', please install .NET SDK"
9 | exit 1
10 | fi
11 |
--------------------------------------------------------------------------------
/build/build.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Linq;
5 | using System.Net.Http;
6 | using System.Text;
7 | using System.Text.Json;
8 | using System.Threading.Tasks;
9 | using System.Xml.Linq;
10 | using System.Xml.XPath;
11 | using GlobExpressions;
12 | using static Bullseye.Targets;
13 | using static SimpleExec.Command;
14 |
15 | var configuration = "Release";
16 | var version = await GetGitVersion();
17 | var waveVersion = GetWaveVersion();
18 | var notes = GetReleaseNotes();
19 | var apiKey = Environment.GetEnvironmentVariable("JETBRAINS_API_KEY");
20 |
21 | Target("clean", () =>
22 | {
23 | Run("dotnet", $"clean --configuration {configuration}");
24 |
25 | if (Directory.Exists("artifacts"))
26 | {
27 | Directory.Delete("artifacts", true);
28 | }
29 | });
30 |
31 | Target("restore", DependsOn("clean"), () =>
32 | {
33 | Run("dotnet", "restore");
34 | });
35 |
36 | Target("build", DependsOn("restore"), () =>
37 | {
38 | Run("dotnet", "build " +
39 | "--no-restore " +
40 | $"--configuration {configuration} " +
41 | "--property HostFullIdentifier= " +
42 | $"--property Version={version.SemVer} " +
43 | $"--property AssemblyVersion={version.AssemblySemVer} " +
44 | $"--property FileVersion={version.AssemblySemFileVer} " +
45 | $"--property InformationalVersion={version.InformationalVersion}");
46 | });
47 |
48 | Target("test", DependsOn("build"), () =>
49 | {
50 | Run("dotnet", $"test --configuration {configuration} --no-restore --no-build");
51 | });
52 |
53 | Target("package", DependsOn("build", "test"), () =>
54 | {
55 | Run("dotnet", $"pack --configuration {configuration} --no-restore --no-build --output artifacts --property Version={version.SemVer}");
56 | });
57 |
58 | Target("zip", DependsOn("package"), () =>
59 | {
60 | var artifactPath = Path.Combine("artifacts", "machine-specifications");
61 | var dotnetPath = Path.Combine(artifactPath, "dotnet");
62 | var libPath = Path.Combine(artifactPath, "lib");
63 | var metaPath = Path.Combine(libPath, "META-INF");
64 | var jarPath = Path.Combine(libPath, $"machine-specifications-{version.SemVer}.jar");
65 | var zipPath = Path.Combine("artifacts", $"machine-specifications-{version.SemVer}.zip");
66 |
67 | Directory.CreateDirectory(dotnetPath);
68 | Directory.CreateDirectory(metaPath);
69 |
70 | var nupkg = Glob.Files("artifacts", "*.nupkg").First();
71 |
72 | using var archive = ZipFile.OpenRead(Path.Combine("artifacts", nupkg));
73 |
74 | foreach (var entry in archive.Entries.Where(x => x.FullName.Contains("DotFiles")))
75 | {
76 | entry.ExtractToFile(Path.Combine(dotnetPath, entry.Name));
77 | }
78 |
79 | var plugin = File.ReadAllText("plugin.xml")
80 | .Replace("${Version}", version.SemVer)
81 | .Replace("${SinceBuild}", waveVersion + ".0")
82 | .Replace("${UntilBuild}", waveVersion + ".*")
83 | .Replace("${ChangeNotes}", notes);
84 |
85 | var icon = File.ReadAllBytes(Path.Combine("images", "pluginIcon.svg"));
86 |
87 | File.WriteAllText(Path.Combine(metaPath, "plugin.xml"), plugin);
88 | File.WriteAllText(Path.Combine(metaPath, "MANIFEST.MF"), "Manifest-Version: 1.0");
89 | File.WriteAllBytes(Path.Combine(metaPath, "pluginIcon.svg"), icon);
90 |
91 | ZipFile.CreateFromDirectory(metaPath, jarPath, CompressionLevel.Optimal, true, new UnixUTF8Encoding());
92 |
93 | Directory.Delete(metaPath, true);
94 |
95 | ZipFile.CreateFromDirectory(artifactPath, zipPath, CompressionLevel.Optimal, true, new UnixUTF8Encoding());
96 |
97 | Console.WriteLine($"Created {zipPath}");
98 | });
99 |
100 | Target("publish-nuget", DependsOn("package"), () =>
101 | {
102 | var githubToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
103 |
104 | var packages = Glob.Files("artifacts", "*.nupkg")
105 | .ToArray();
106 |
107 | Run("dotnet", $"nuget add source https://nuget.pkg.github.com/machine/index.json -n github -u machine -p {githubToken} --store-password-in-clear-text");
108 |
109 | foreach (var package in packages)
110 | {
111 | var path = Path.Combine("artifacts", package);
112 |
113 | Run("dotnet", $"nuget push {path} --source https://plugins.jetbrains.com/ --api-key {apiKey}");
114 | Run("dotnet", $"nuget push {path} --source github");
115 |
116 | Console.WriteLine($"Published plugin {package} to JetBrains hub");
117 | }
118 | });
119 |
120 | Target("publish-zip", DependsOn("zip"), () =>
121 | {
122 | using var client = new HttpClient();
123 |
124 | client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
125 |
126 | var plugins = Glob.Files("artifacts", "*.zip");
127 |
128 | foreach (var file in plugins)
129 | {
130 | var filename = Path.GetFileName(file);
131 |
132 | var content = new MultipartFormDataContent
133 | {
134 | { new StringContent("com.intellij.resharper.machine.specifications"), "xmlId" },
135 | { new ByteArrayContent(File.ReadAllBytes(Path.Combine("artifacts", file))), "file", filename },
136 | { new StringContent(notes), "notes" }
137 | };
138 |
139 | if (!string.IsNullOrEmpty(version.PreReleaseTag))
140 | {
141 | content.Add(new StringContent("channel"), "Beta");
142 | }
143 |
144 | var response = client.PostAsync("https://plugins.jetbrains.com/plugin/uploadPlugin", content);
145 | response.Wait(TimeSpan.FromMinutes(10));
146 | response.Result.EnsureSuccessStatusCode();
147 |
148 | Console.WriteLine($"Published plugin {filename} to JetBrains hub");
149 | }
150 | });
151 |
152 | Target("publish", DependsOn("publish-nuget", "publish-zip"));
153 |
154 | Target("default", DependsOn("zip"));
155 |
156 | await RunTargetsAndExitAsync(args);
157 |
158 | async Task GetGitVersion()
159 | {
160 | Run("dotnet", "tool restore");
161 |
162 | var (value, _) = await ReadAsync("dotnet", "dotnet-gitversion");
163 |
164 | return JsonSerializer.Deserialize(value);
165 | }
166 |
167 | string GetWaveVersion()
168 | {
169 | var value = GetXmlValue("SdkVersion");
170 |
171 | if (string.IsNullOrEmpty(value))
172 | {
173 | return string.Empty;
174 | }
175 |
176 | return $"{value.Substring(2,2)}{value.Substring(5,1)}";
177 | }
178 |
179 | string GetReleaseNotes()
180 | {
181 | return GetXmlValue("PackageReleaseNotes");
182 | }
183 |
184 | string GetXmlValue(string name)
185 | {
186 | var projects = Glob.Files(Environment.CurrentDirectory, "src/**/*.csproj");
187 |
188 | foreach (var project in projects)
189 | {
190 | var document = XDocument.Load(project);
191 | var node = document.XPathSelectElement($"/Project/PropertyGroup/{name}");
192 |
193 | if (!string.IsNullOrEmpty(node!.Value))
194 | {
195 | return node.Value;
196 | }
197 | }
198 |
199 | return string.Empty;
200 | }
201 |
202 | class GitVersion
203 | {
204 | public string SemVer { get; set; }
205 |
206 | public string AssemblySemVer { get; set; }
207 |
208 | public string AssemblySemFileVer { get; set; }
209 |
210 | public string InformationalVersion { get; set; }
211 |
212 | public string PreReleaseTag { get; set; }
213 | }
214 |
215 | class UnixUTF8Encoding : UTF8Encoding
216 | {
217 | public override byte[] GetBytes(string s)
218 | {
219 | s = s.Replace("\\", "/");
220 |
221 | return base.GetBytes(s);
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/build/build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machine/machine.specifications.runner.resharper/f7e86ca30bcc244ab85eaebcf4f94741fc253a2d/images/icon.png
--------------------------------------------------------------------------------
/images/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
31 |
--------------------------------------------------------------------------------
/install-plugin.ps1:
--------------------------------------------------------------------------------
1 | Set-StrictMode -Version Latest
2 | $ErrorActionPreference = "Stop"
3 |
4 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
5 |
6 | $PluginId = "Machine.Specifications.Runner.Resharper9"
7 | $RootSuffix = "ReSharper"
8 | $ResharperUrl = "https://data.services.jetbrains.com/products/releases?code=RSU&type=release"
9 | $NugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
10 | $Version = "0.1.0"
11 | $MainProject = "$PSScriptRoot\src\Machine.Specifications.Runner.ReSharper\Machine.Specifications.Runner.ReSharper.csproj"
12 | $NugetExe = "$PSScriptRoot\tools\nuget.exe"
13 |
14 | Function Invoke-Exe {
15 | param(
16 | [Parameter(Mandatory = $true, Position = 0)]
17 | [ValidateNotNullOrEmpty()]
18 | [String]
19 | $Executable,
20 |
21 | [Parameter(ValueFromRemainingArguments = $true)]
22 | [String[]]
23 | $Arguments
24 | )
25 |
26 | Write-Host "> $Executable $Arguments"
27 | $result = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Wait -Passthru
28 |
29 | if (-Not $result.ExitCode -eq 0) {
30 | throw "'$Executable $Arguments' failed with exit code $($result.ExitCode)"
31 | }
32 | }
33 |
34 | Function Write-User-Settings {
35 | param(
36 | [Parameter(Mandatory = $true)]
37 | [String]
38 | $HostIdentifier,
39 |
40 | [Parameter(Mandatory = $true)]
41 | [String]
42 | $UserProjectFile
43 | )
44 |
45 | if (!(Test-Path "$UserProjectFile")) {
46 | Set-Content -Path "$UserProjectFile" -Value ""
47 | }
48 |
49 | $xml = [xml] (Get-Content "$UserProjectFile")
50 | $node = $xml.SelectSingleNode(".//HostFullIdentifier")
51 | $node.InnerText = $HostIdentifier
52 | $xml.Save("$UserProjectFile")
53 | }
54 |
55 | Function Read-SdkVersion {
56 | $xml = [xml] (Get-Content "$MainProject")
57 | $node = $xml.SelectSingleNode(".//SdkVersion")
58 |
59 | if ($null -eq $node) {
60 | throw "SdkVersion not found in '$MainProject'"
61 | }
62 |
63 | $value = $node.InnerText
64 |
65 | return $value.Substring(2, 2) + $value.Substring(5, 1)
66 | }
67 |
68 | Function Write-Nuspec {
69 | param(
70 | [Parameter(Mandatory=$true)]
71 | [String]
72 | $NuspecFile
73 | )
74 |
75 | $sdkVersion = Read-SdkVersion
76 | $nextSdkVersion = [int]$sdkVersion + 1
77 |
78 | $nuspec = @'
79 |
80 |
81 |
82 | Machine.Specifications.Runner.Resharper9
83 | {0}
84 | Machine Specifications
85 | Machine Specifications
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | '@ -f $Version,$sdkVersion,$nextSdkVersion
103 |
104 | Set-Content -Path "$NuspecFile" -Value $nuspec
105 | }
106 |
107 | Function Get-ToolPath {
108 | param(
109 | [Parameter(Mandatory = $true)]
110 | [String]
111 | $ToolUrl
112 | )
113 |
114 | $file = $ToolUrl.Substring($ToolUrl.LastIndexOf("/") + 1)
115 |
116 | return "$PSScriptRoot\tools\" + $file
117 | }
118 |
119 | Function Get-Tool {
120 | param(
121 | [Parameter(Mandatory = $true)]
122 | [String]
123 | $ToolUrl
124 | )
125 |
126 | $file = Get-ToolPath $ToolUrl
127 |
128 | if (!(Test-Path $file)) {
129 | mkdir -Force $(Split-Path $file -Parent) > $null
130 | Write-Output "Downloading from $ToolUrl"
131 | (New-Object System.Net.WebClient).DownloadFile($ToolUrl, $file)
132 | } else {
133 | $filename = $file.Substring($file.LastIndexOf("\") + 1)
134 | Write-Output "Using $($filename) from cache"
135 | }
136 | }
137 |
138 | Function Install-Hive {
139 | param(
140 | [Parameter(Mandatory = $true)]
141 | [String]
142 | $InstallerFile
143 | )
144 |
145 | Write-Output "Installing experimental hive"
146 | Invoke-Exe $InstallerFile "/SpecificProductNames=ReSharper" "/Hive=$RootSuffix" "/VsVersion=15.0;16.0" "/Silent=True"
147 | }
148 |
149 | Function Get-InstallationPath {
150 | $version = Read-SdkVersion
151 |
152 | return $(Get-ChildItem "$env:APPDATA\JetBrains\ReSharperPlatformVs*\v$version`_*$RootSuffix\NuGet.Config" | Sort-Object | Select-Object -Last 1).Directory
153 | }
154 |
155 | Function Save-PackagesConfig {
156 | $installPath = Get-InstallationPath
157 |
158 | if (Test-Path "$installPath\packages.config") {
159 | $xml = [xml] (Get-Content "$installPath\packages.config")
160 | } else {
161 | $xml = [xml] ("")
162 | }
163 |
164 | if ($null -eq $xml.SelectSingleNode(".//package[@id='$PluginId']/@id")) {
165 | $pluginNode = $xml.CreateElement('package')
166 | $pluginNode.setAttribute("id", "$PluginId")
167 | $pluginNode.setAttribute("version", "$Version")
168 |
169 | $packagesNode = $xml.SelectSingleNode("//packages")
170 | $packagesNode.AppendChild($PluginNode) > $null
171 |
172 | $xml.Save("$installPath\packages.config")
173 | }
174 | }
175 |
176 | # Download tools
177 | $url = [uri] $(Invoke-WebRequest -UseBasicParsing $ResharperUrl | ConvertFrom-Json).RSU[0].downloads.windows.link
178 | $resharperTool = Get-ToolPath $url
179 |
180 | Get-Tool $url
181 | Get-Tool $NugetUrl
182 |
183 | # Build plugin
184 | $artifacts = "$PSScriptRoot\artifacts"
185 |
186 | Write-Nuspec "$artifacts\Package.nuspec"
187 | Invoke-Exe dotnet build "$PSScriptRoot\Machine.Specifications.Runner.Resharper.sln" /p:Version=$Version /p:HostFullIdentifier="" /p:UseSharedCompilation=false
188 | Invoke-Exe $NugetExe pack "$artifacts\Package.nuspec" -version $Version -outputDirectory "$artifacts"
189 |
190 | # Install hive
191 | Install-Hive $resharperTool
192 |
193 | # Set up environment
194 | Save-PackagesConfig
195 |
196 | # Install plugin
197 | Invoke-Exe $NugetExe install $PluginId -OutputDirectory "$env:LOCALAPPDATA\JetBrains\plugins" -Source "$artifacts" -DependencyVersion Ignore
198 |
199 | # Reinstall hive
200 | Install-Hive $resharperTool
201 |
202 | # Set user project settings
203 | $installPath = Get-InstallationPath
204 | $hostIdentifier = "$($installPath.Parent.Name)_$($installPath.Name.Split('_')[-1])"
205 |
206 | Write-User-Settings $hostIdentifier "$PSScriptRoot\src\Machine.Specifications.Runner.ReSharper\Machine.Specifications.Runner.ReSharper.csproj.user"
207 | Write-User-Settings $hostIdentifier "$PSScriptRoot\src\Machine.Specifications.Runner.ReSharper.Adapters\Machine.Specifications.Runner.ReSharper.Adapters.csproj.user"
208 | Write-User-Settings $hostIdentifier "$PSScriptRoot\src\Machine.Specifications.Runner.ReSharper.Tasks\Machine.Specifications.Runner.ReSharper.Tasks.csproj.user"
209 |
210 | Write-Output "Installed plugin to hive"
211 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.intellij.resharper.machine.specifications
3 | Machine.Specifications for Rider
4 | ${Version}
5 | Unit testing
6 | Machine
7 |
8 |
9 | com.intellij.modules.rider
10 |
11 |
12 | This plugin adds test discovery and runner support for the Machine.Specifications framework. Tests can be run from the unit test window or using the gutter marks. A sub-menu will give you options to run tests in debug mode or for different .NET framework targets.
14 |
15 | For issues with this plugin, please head to GitHub.
16 | ]]>
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/Discoverer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Threading;
6 | using JetBrains.ReSharper.TestRunner.Abstractions;
7 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
8 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
9 | using Machine.Specifications.Runner.ReSharper.Tasks;
10 |
11 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
12 |
13 | public class Discoverer(
14 | TestRequest request,
15 | IMspecController controller,
16 | ITestDiscoverySink sink,
17 | RemoteTaskDepot depot,
18 | CancellationToken token)
19 | {
20 | private readonly ILogger logger = Logger.GetLogger();
21 |
22 | public void Discover()
23 | {
24 | logger.Info($"Discovering tests from {request.Container.Location}");
25 |
26 | var source = new List();
27 |
28 | try
29 | {
30 | var discoverySink = new MspecDiscoverySink(token);
31 |
32 | controller.Find(discoverySink, request.Container.Location);
33 |
34 | foreach (var element in discoverySink.Elements.Result)
35 | {
36 | var task = GetRemoteTask(element);
37 | var parent = GetParent(element);
38 |
39 | source.Add(task);
40 |
41 | if (parent != null && depot[element] == null && depot[parent] != null && depot[parent]!.RunAllChildren)
42 | {
43 | depot.Add(task);
44 | }
45 |
46 | depot.Bind(element, task);
47 | }
48 |
49 | if (source.Any())
50 | {
51 | logger.Debug($"Sending {source.Count} discovery results to server");
52 |
53 | sink.TestsDiscovered(source.ToArray());
54 | }
55 | }
56 | catch (OperationCanceledException)
57 | {
58 | logger.Info("Discovery was aborted");
59 | }
60 | catch (TargetInvocationException ex) when(ex.InnerException != null)
61 | {
62 | throw new MspecDiscoveryException(ex.InnerException.Message, ex.InnerException.StackTrace);
63 | }
64 | catch (Exception ex) when (ex.GetType().FullName.StartsWith("Machine.Specifications"))
65 | {
66 | throw new MspecDiscoveryException(ex.Message, ex.StackTrace);
67 | }
68 |
69 | logger.Info("Discovery completed");
70 | }
71 |
72 | private MspecRemoteTask GetRemoteTask(IMspecElement element)
73 | {
74 | return RemoteTaskBuilder.GetRemoteTask(element);
75 | }
76 |
77 | private IMspecElement? GetParent(IMspecElement element)
78 | {
79 | return element switch
80 | {
81 | IContextElement => null,
82 | IBehaviorElement behavior => behavior.Context,
83 | ISpecificationElement {Behavior: null} specification => specification.Context,
84 | ISpecificationElement {Behavior: not null} specification => specification.Behavior,
85 | _ => throw new ArgumentOutOfRangeException(nameof(element))
86 | };
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/DiscoveryCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
6 | using Machine.Specifications.Runner.Utility;
7 |
8 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
9 |
10 | public class DiscoveryCache(Assembly assembly)
11 | {
12 | private readonly Dictionary types = new();
13 |
14 | private readonly Dictionary fields = new();
15 |
16 | private readonly Dictionary behaviorFieldsByType = new();
17 |
18 | private readonly Dictionary ignoreReasons = new();
19 |
20 | private readonly Dictionary behaviors = new();
21 |
22 | public IBehaviorElement? GetOrAddBehavior(IContextElement context, SpecificationInfo specification)
23 | {
24 | var behaviorField = GetOrAddBehaviorFieldByType(context.TypeName, specification.ContainingType);
25 |
26 | if (behaviorField == null)
27 | {
28 | return null;
29 | }
30 |
31 | var key = $"{context.TypeName}.{behaviorField.Name}";
32 |
33 | if (!behaviors.TryGetValue(key, out var behavior))
34 | {
35 | // MSpec behaviors don't seem to inherit an 'ignored' attribute from the context
36 | var ignoreReason = GetIgnoreReason(context.TypeName, behaviorField.Name, false);
37 |
38 | behavior = behaviors[key] = new BehaviorElement(context, specification.ContainingType, behaviorField.Name, ignoreReason);
39 | }
40 |
41 | return behavior;
42 | }
43 |
44 | public string? GetIgnoreReason(string typeName)
45 | {
46 | if (!ignoreReasons.TryGetValue(typeName, out var reason))
47 | {
48 | var type = GetOrAddType(typeName);
49 |
50 | reason = type == null
51 | ? null
52 | : GetIgnoreReason(type);
53 |
54 | ignoreReasons[typeName] = reason;
55 | }
56 |
57 | return reason;
58 | }
59 |
60 | public string? GetIgnoreReason(string typeName, string fieldName, bool inherit)
61 | {
62 | var key = $"{typeName}.{fieldName}";
63 |
64 | if (!ignoreReasons.TryGetValue(key, out var reason))
65 | {
66 | var type = GetOrAddType(typeName);
67 | var field = GetOrAddField(type, fieldName);
68 |
69 | reason = field == null
70 | ? null
71 | : GetIgnoreReason(field);
72 |
73 | if (string.IsNullOrEmpty(reason) && inherit)
74 | {
75 | reason = GetIgnoreReason(typeName);
76 | }
77 |
78 | ignoreReasons[key] = reason;
79 | }
80 |
81 | return reason;
82 | }
83 |
84 | private FieldInfo? GetOrAddBehaviorFieldByType(string contextTypeName, string behaviorType)
85 | {
86 | var key = $"{contextTypeName}.{behaviorType}";
87 |
88 | if (!behaviorFieldsByType.TryGetValue(key, out var field))
89 | {
90 | var type = GetOrAddType(contextTypeName);
91 |
92 | field = GetBehaviorField(type, x => x.FieldType.GenericTypeArguments.First().FullName == behaviorType);
93 |
94 | behaviorFieldsByType[key] = field;
95 |
96 | if (field != null)
97 | {
98 | fields[$"{contextTypeName}.{field.Name}"] = field;
99 | }
100 | }
101 |
102 | return field;
103 | }
104 |
105 | private string? GetIgnoreReason(MemberInfo member)
106 | {
107 | var attribute = member.GetCustomAttributesData()
108 | .FirstOrDefault(x => x.AttributeType.FullName == FullNames.IgnoreAttribute);
109 |
110 | var attributeValue = attribute?.ConstructorArguments.FirstOrDefault();
111 |
112 | return attributeValue?.Value as string;
113 | }
114 |
115 | private Type? GetOrAddType(string typeName)
116 | {
117 | if (!types.TryGetValue(typeName, out var type))
118 | {
119 | type = types[typeName] = assembly.GetType(typeName);
120 | }
121 |
122 | return type;
123 | }
124 |
125 | private FieldInfo? GetOrAddField(Type? type, string fieldName, Func? predicate = null)
126 | {
127 | if (type == null)
128 | {
129 | return null;
130 | }
131 |
132 | var key = $"{type.FullName}.{fieldName}";
133 |
134 | if (!fields.TryGetValue(key, out var field))
135 | {
136 | field = type
137 | .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
138 | .Where(x => x.Name == fieldName)
139 | .FirstOrDefault(predicate ?? (_ => true));
140 |
141 | fields[key] = field;
142 | }
143 |
144 | return field;
145 | }
146 |
147 | private FieldInfo? GetBehaviorField(Type? type, Func predicate)
148 | {
149 | return type?
150 | .GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
151 | .Where(IsBehavesLikeField)
152 | .FirstOrDefault(predicate);
153 | }
154 |
155 | private bool IsBehavesLikeField(FieldInfo field)
156 | {
157 | return field.FieldType.IsGenericType &&
158 | field.FieldType.GetCustomAttributes(false)
159 | .Any(x => x.GetType().FullName == FullNames.BehaviorDelegateAttribute);
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/IMspecController.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
2 |
3 | public interface IMspecController
4 | {
5 | void Find(IMspecDiscoverySink sink, string assemblyPath);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/IMspecDiscoverySink.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
4 |
5 | public interface IMspecDiscoverySink
6 | {
7 | void OnSpecification(ISpecificationElement specification);
8 |
9 | void OnDiscoveryCompleted();
10 | }
11 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/MspecController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Reflection;
5 | using System.Threading;
6 | using System.Xml.Linq;
7 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
8 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
9 | using Machine.Specifications.Runner.Utility;
10 |
11 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
12 |
13 | public class MspecController : IMspecController
14 | {
15 | private readonly TestRequest request;
16 |
17 | private readonly CancellationToken token;
18 |
19 | private readonly object controller;
20 |
21 | private readonly MethodInfo invoker;
22 |
23 | public MspecController(TestRequest request, CancellationToken token)
24 | {
25 | this.request = request;
26 | this.token = token;
27 |
28 | var controllerType = GetControllerType();
29 |
30 | invoker = controllerType.GetMethod("DiscoverSpecs", BindingFlags.Instance | BindingFlags.Public)!;
31 | controller = Activator.CreateInstance(controllerType, (Action) Listener);
32 | }
33 |
34 | public void Find(IMspecDiscoverySink sink, string assemblyPath)
35 | {
36 | var assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
37 | var assembly = Assembly.Load(assemblyName);
38 |
39 | var results = (string) invoker.Invoke(controller, [assembly]);
40 |
41 | var specifications = GetSpecifications(assembly, results);
42 |
43 | foreach (var specification in specifications)
44 | {
45 | sink.OnSpecification(specification);
46 | }
47 |
48 | sink.OnDiscoveryCompleted();
49 | }
50 |
51 | private Type GetControllerType()
52 | {
53 | var type = Type.GetType("Machine.Specifications.Controller.Controller, Machine.Specifications");
54 |
55 | if (type == null)
56 | {
57 | var path = Path.GetDirectoryName(request.Container.Location);
58 | var assemblyPath = Path.Combine(path!, "Machine.Specifications.dll");
59 |
60 | var assembly = Assembly.LoadFrom(assemblyPath);
61 |
62 | type = assembly.GetType("Machine.Specifications.Controller.Controller");
63 | }
64 |
65 | if (type == null)
66 | {
67 | throw new InvalidOperationException("Cannot find 'Machine.Specifications.dll' controller type");
68 | }
69 |
70 | return type;
71 | }
72 |
73 | private IEnumerable GetSpecifications(Assembly assembly, string xml)
74 | {
75 | var document = XDocument.Parse(xml);
76 |
77 | var cache = new DiscoveryCache(assembly);
78 |
79 | foreach (var contextElement in document.Descendants("contextinfo"))
80 | {
81 | token.ThrowIfCancellationRequested();
82 |
83 | var contextInfo = ContextInfo.Parse(contextElement.ToString());
84 | var contextIgnore = cache.GetIgnoreReason(contextInfo.TypeName);
85 |
86 | var context = contextInfo.ToElement(contextIgnore);
87 |
88 | foreach (var specificationElement in contextElement.Descendants("specificationinfo"))
89 | {
90 | token.ThrowIfCancellationRequested();
91 |
92 | var specification = SpecificationInfo.Parse(specificationElement.ToString());
93 |
94 | var behavior = specification.IsBehavior(context.TypeName)
95 | ? cache.GetOrAddBehavior(context, specification)
96 | : null;
97 |
98 | var specificationIgnore = behavior != null
99 | ? cache.GetIgnoreReason(behavior.TypeName, specification.FieldName, false) ?? behavior.IgnoreReason
100 | : cache.GetIgnoreReason(context.TypeName, specification.FieldName, true);
101 |
102 | yield return SpecificationInfo.Parse(specificationElement.ToString())
103 | .ToElement(context, specificationIgnore, behavior);
104 | }
105 | }
106 | }
107 |
108 | private void Listener(string _)
109 | {
110 | token.ThrowIfCancellationRequested();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/MspecDiscoveryException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
5 |
6 | [Serializable]
7 | public class MspecDiscoveryException : Exception
8 | {
9 | public MspecDiscoveryException(string message, string stackTrace)
10 | : base(message)
11 | {
12 | StackTrace = stackTrace;
13 | }
14 |
15 | protected MspecDiscoveryException(SerializationInfo info, StreamingContext context)
16 | : base(info, context)
17 | {
18 | StackTrace = info.GetString(nameof(StackTrace));
19 | }
20 |
21 | public override string StackTrace { get; }
22 |
23 | public override void GetObjectData(SerializationInfo info, StreamingContext context)
24 | {
25 | base.GetObjectData(info, context);
26 |
27 | info.AddValue(nameof(StackTrace), StackTrace);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Discovery/MspecDiscoverySink.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
7 |
8 | public class MspecDiscoverySink(CancellationToken token) : IMspecDiscoverySink
9 | {
10 | private readonly TaskCompletionSource source = new();
11 |
12 | private readonly List elements = new();
13 |
14 | private readonly HashSet handledContexts = new();
15 |
16 | private readonly HashSet handledBehaviors = new();
17 |
18 | private readonly HashSet handledSpecifications = new();
19 |
20 | public Task Elements => source.Task;
21 |
22 | public void OnSpecification(ISpecificationElement specification)
23 | {
24 | token.ThrowIfCancellationRequested();
25 |
26 | if (handledContexts.Add(specification.Context))
27 | {
28 | elements.Add(specification.Context);
29 | }
30 |
31 | if (specification.Behavior != null && handledBehaviors.Add(specification.Behavior))
32 | {
33 | elements.Add(specification.Behavior);
34 | }
35 |
36 | if (handledSpecifications.Add(specification))
37 | {
38 | elements.Add(specification);
39 | }
40 | }
41 |
42 | public void OnDiscoveryCompleted()
43 | {
44 | source.SetResult(elements.ToArray());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/BehaviorElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public class BehaviorElement(IContextElement context, string typeName, string fieldName, string? ignoreReason)
4 | : IBehaviorElement
5 | {
6 | public string Id { get; } = $"{context.TypeName}.{fieldName}";
7 |
8 | public string AggregateId { get; } = $"{context.TypeName}.{typeName}";
9 |
10 | public string? IgnoreReason { get; } = ignoreReason;
11 |
12 | public IContextElement Context { get; } = context;
13 |
14 | public string TypeName { get; } = typeName;
15 |
16 | public string FieldName { get; } = fieldName;
17 | }
18 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/ContextElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public class ContextElement(string typeName, string subject, string? ignoreReason) : IContextElement
4 | {
5 | public string Id { get; } = typeName;
6 |
7 | public string AggregateId => Id;
8 |
9 | public string? IgnoreReason { get; } = ignoreReason;
10 |
11 | public string TypeName { get; } = typeName;
12 |
13 | public string Subject { get; } = subject;
14 | }
15 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/ContextInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
4 |
5 | public static class ContextInfoExtensions
6 | {
7 | public static IContextElement ToElement(this ContextInfo context, string? ignoreReason)
8 | {
9 | return new ContextElement(context.TypeName, context.Concern, ignoreReason);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/IBehaviorElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public interface IBehaviorElement : IMspecElement
4 | {
5 | IContextElement Context { get; }
6 |
7 | string TypeName { get; }
8 |
9 | string FieldName { get; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/IContextElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public interface IContextElement : IMspecElement
4 | {
5 | string TypeName { get; }
6 |
7 | string Subject { get; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/IMspecElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public interface IMspecElement
4 | {
5 | string Id { get; }
6 |
7 | string AggregateId { get; }
8 |
9 | string? IgnoreReason { get; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/ISpecificationElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public interface ISpecificationElement : IMspecElement
4 | {
5 | IContextElement Context { get; }
6 |
7 | string FieldName { get; }
8 |
9 | IBehaviorElement? Behavior { get; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/SpecificationElement.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | public class SpecificationElement(
4 | IContextElement context,
5 | string fieldName,
6 | string? ignoreReason = null,
7 | IBehaviorElement? behavior = null)
8 | : ISpecificationElement
9 | {
10 | public string Id { get; } = behavior != null
11 | ? $"{context.TypeName}.{behavior.FieldName}.{fieldName}"
12 | : $"{context.TypeName}.{fieldName}";
13 |
14 | public string AggregateId { get; } = behavior != null
15 | ? $"{context.TypeName}.{behavior.TypeName}.{fieldName}"
16 | : $"{context.TypeName}.{fieldName}";
17 |
18 | public string? IgnoreReason { get; } = ignoreReason;
19 |
20 | public IContextElement Context { get; } = context;
21 |
22 | public string FieldName { get; } = fieldName;
23 |
24 | public IBehaviorElement? Behavior { get; } = behavior;
25 | }
26 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Elements/SpecificationInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Elements;
4 |
5 | public static class SpecificationInfoExtensions
6 | {
7 | public static ISpecificationElement ToElement(this SpecificationInfo specification, IContextElement context, string? ignoreReason, IBehaviorElement? behavior = null)
8 | {
9 | return new SpecificationElement(context, specification.FieldName, ignoreReason, behavior);
10 | }
11 |
12 | public static bool IsBehavior(this SpecificationInfo specification, string contextTypeName)
13 | {
14 | return specification.ContainingType != contextTypeName;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/ExceptionResultExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
6 | using Machine.Specifications.Runner.Utility;
7 |
8 | namespace Machine.Specifications.Runner.ReSharper.Adapters;
9 |
10 | internal static class ExceptionResultExtensions
11 | {
12 | private static IEnumerable Flatten(this ExceptionResult result)
13 | {
14 | var exception = result;
15 |
16 | if (exception.FullTypeName == typeof(TargetInvocationException).FullName && exception.InnerExceptionResult != null)
17 | {
18 | exception = exception.InnerExceptionResult;
19 | }
20 |
21 | for (var current = exception; current != null; current = current.InnerExceptionResult)
22 | {
23 | yield return current;
24 | }
25 | }
26 |
27 | public static ExceptionInfo[] GetExceptions(this ExceptionResult? result)
28 | {
29 | if (result == null)
30 | {
31 | return [];
32 | }
33 |
34 | return result.Flatten()
35 | .Select(x => new ExceptionInfo(x.FullTypeName, x.Message, x.StackTrace))
36 | .ToArray();
37 | }
38 |
39 | public static string GetExceptionMessage(this ExceptionResult? result)
40 | {
41 | if (result == null)
42 | {
43 | return string.Empty;
44 | }
45 |
46 | var exception = result.Flatten().FirstOrDefault();
47 |
48 | return exception != null
49 | ? $"{exception.FullTypeName}: {exception.Message}"
50 | : string.Empty;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/ConcurrentLookup.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
5 |
6 | public class ConcurrentLookup
7 | where T : IMspecElement
8 | {
9 | private readonly ConcurrentDictionary> values = new();
10 |
11 | public void Add(T? element)
12 | {
13 | if (element == null)
14 | {
15 | return;
16 | }
17 |
18 | var bag = values.GetOrAdd(element.AggregateId, _ => new ConcurrentBag());
19 |
20 | bag.Add(element);
21 | }
22 |
23 | public T? Take(string id)
24 | {
25 | if (values.TryGetValue(id, out var bag) && bag.TryTake(out var value))
26 | {
27 | return value;
28 | }
29 |
30 | return default;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/ElementCache.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
6 |
7 | public class ElementCache
8 | {
9 | private readonly HashSet behaviorTypes;
10 |
11 | private readonly ILookup behaviorsByContext;
12 |
13 | private readonly ILookup specificationsByContext;
14 |
15 | private readonly ILookup specificationsByBehavior;
16 |
17 | public ElementCache(ISpecificationElement[] specifications)
18 | {
19 | var types = specifications
20 | .Where(x => x.Behavior != null)
21 | .Select(x => x.Behavior!.TypeName)
22 | .Distinct();
23 |
24 | behaviorTypes = [..types];
25 |
26 | behaviorsByContext = specifications
27 | .Where(x => x.Behavior != null)
28 | .Select(x => x.Behavior!)
29 | .Distinct()
30 | .ToLookup(x => x!.Context);
31 |
32 | specificationsByContext = specifications
33 | .ToLookup(x => x.Context);
34 |
35 | specificationsByBehavior = specifications
36 | .Where(x => x.Behavior != null)
37 | .ToLookup(x => x.Behavior!);
38 | }
39 |
40 | public bool IsBehavior(string type)
41 | {
42 | return behaviorTypes.Contains(type);
43 | }
44 |
45 | public IEnumerable GetBehaviors(IContextElement element)
46 | {
47 | return behaviorsByContext[element];
48 | }
49 |
50 | public IEnumerable GetSpecifications(IContextElement element)
51 | {
52 | return specificationsByContext[element];
53 | }
54 |
55 | public IEnumerable GetSpecifications(IBehaviorElement behavior)
56 | {
57 | return specificationsByBehavior[behavior];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/ExecutionAdapterRunListener.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
7 |
8 | public class ExecutionAdapterRunListener(IExecutionListener listener, ElementCache cache, RunTracker tracker)
9 | : IRunListener
10 | {
11 | private readonly HashSet behaviors = new();
12 |
13 | private IContextElement? currentContext;
14 |
15 | public void OnAssemblyStart(TestAssemblyInfo assemblyInfo)
16 | {
17 | listener.OnAssemblyStart(assemblyInfo.Location);
18 | }
19 |
20 | public void OnAssemblyEnd(TestAssemblyInfo assemblyInfo)
21 | {
22 | listener.OnAssemblyEnd(assemblyInfo.Location);
23 | }
24 |
25 | public void OnRunStart()
26 | {
27 | listener.OnRunStart();
28 | }
29 |
30 | public void OnRunEnd()
31 | {
32 | listener.OnRunEnd();
33 | }
34 |
35 | public void OnContextStart(TestContextInfo contextInfo)
36 | {
37 | var element = tracker.StartContext(contextInfo.TypeName);
38 |
39 | currentContext = element;
40 |
41 | if (element != null)
42 | {
43 | listener.OnContextStart(element);
44 | }
45 | }
46 |
47 | public void OnContextEnd(TestContextInfo contextInfo)
48 | {
49 | var element = tracker.FinishContext(contextInfo.TypeName);
50 |
51 | if (element != null)
52 | {
53 | var runningBehaviors = cache.GetBehaviors(element)
54 | .Where(x => behaviors.Contains(x));
55 |
56 | foreach (var behavior in runningBehaviors)
57 | {
58 | listener.OnBehaviorEnd(behavior, contextInfo.CapturedOutput);
59 | }
60 |
61 | listener.OnContextEnd(element, contextInfo.CapturedOutput);
62 | }
63 |
64 | currentContext = null;
65 | }
66 |
67 | public void OnSpecificationStart(TestSpecificationInfo specificationInfo)
68 | {
69 | var isBehavior = cache.IsBehavior(specificationInfo.ContainingType);
70 |
71 | if (isBehavior)
72 | {
73 | var key = $"{currentContext?.TypeName}.{specificationInfo.ContainingType}.{specificationInfo.FieldName}";
74 |
75 | var element = tracker.StartSpecification(key);
76 |
77 | if (element?.Behavior != null && behaviors.Add(element.Behavior))
78 | {
79 | listener.OnBehaviorStart(element.Behavior);
80 | }
81 |
82 | if (element != null)
83 | {
84 | listener.OnSpecificationStart(element);
85 | }
86 | }
87 | else
88 | {
89 | var key = $"{specificationInfo.ContainingType}.{specificationInfo.FieldName}";
90 |
91 | var element = tracker.StartSpecification(key);
92 |
93 | if (element != null)
94 | {
95 | listener.OnSpecificationStart(element);
96 | }
97 | }
98 | }
99 |
100 | public void OnSpecificationEnd(TestSpecificationInfo specificationInfo, TestRunResult runResult)
101 | {
102 | var isBehavior = cache.IsBehavior(specificationInfo.ContainingType);
103 |
104 | if (isBehavior)
105 | {
106 | var key = $"{currentContext?.TypeName}.{specificationInfo.ContainingType}.{specificationInfo.FieldName}";
107 |
108 | var element = tracker.FinishSpecification(key);
109 |
110 | if (element != null)
111 | {
112 | listener.OnSpecificationEnd(element, specificationInfo.CapturedOutput, runResult);
113 | }
114 | }
115 | else
116 | {
117 | var key = $"{specificationInfo.ContainingType}.{specificationInfo.FieldName}";
118 |
119 | var element = tracker.FinishSpecification(key);
120 |
121 | if (element != null)
122 | {
123 | listener.OnSpecificationEnd(element, specificationInfo.CapturedOutput, runResult);
124 | }
125 | }
126 | }
127 |
128 | public void OnFatalError(TestError? error)
129 | {
130 | listener.OnFatalError(error);
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/Executor.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using JetBrains.ReSharper.TestRunner.Abstractions;
4 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
5 | using Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
6 | using Machine.Specifications.Runner.Utility;
7 |
8 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
9 |
10 | public class Executor(
11 | ITestExecutionSink executionSink,
12 | TestRunRequest request,
13 | RemoteTaskDepot depot,
14 | CancellationToken token)
15 | {
16 | private readonly RunContext context = new(depot, executionSink);
17 |
18 | public void Execute()
19 | {
20 | var assembly = request.Container.Location;
21 |
22 | var testsToRun = context.GetTestsToRun().ToArray();
23 |
24 | var contexts = testsToRun
25 | .Select(x => x.Context.TypeName)
26 | .Distinct();
27 |
28 | var cache = new ElementCache(testsToRun);
29 | var tracker = new RunTracker(testsToRun);
30 |
31 | var listener = new TestExecutionListener(context, cache, token);
32 | var adapter = new ExecutionAdapterRunListener(listener, cache, tracker);
33 | var loggingListener = new LoggingRunListener(adapter);
34 | var machineAdapter = new AdapterListener(loggingListener, assembly);
35 |
36 | var runOptions = RunOptions.Custom.FilterBy(contexts);
37 |
38 | var runner = new AppDomainRunner(machineAdapter, runOptions);
39 | runner.RunAssembly(new AssemblyPath(request.Container.Location));
40 |
41 | listener.Finished.WaitOne();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/IExecutionListener.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
5 |
6 | public interface IExecutionListener
7 | {
8 | void OnAssemblyStart(string assemblyLocation);
9 |
10 | void OnAssemblyEnd(string assemblyLocation);
11 |
12 | void OnRunStart();
13 |
14 | void OnRunEnd();
15 |
16 | void OnContextStart(IContextElement context);
17 |
18 | void OnContextEnd(IContextElement context, string capturedOutput);
19 |
20 | void OnBehaviorStart(IBehaviorElement behavior);
21 |
22 | void OnBehaviorEnd(IBehaviorElement behavior, string capturedOutput);
23 |
24 | void OnSpecificationStart(ISpecificationElement specification);
25 |
26 | void OnSpecificationEnd(ISpecificationElement specification, string capturedOutput, TestRunResult runResult);
27 |
28 | void OnFatalError(TestError? error);
29 | }
30 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/RunContext.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using System.Collections.Generic;
3 | using JetBrains.ReSharper.TestRunner.Abstractions;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
5 | using Machine.Specifications.Runner.ReSharper.Tasks;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
8 |
9 | public class RunContext(RemoteTaskDepot depot, ITestExecutionSink sink)
10 | {
11 | private readonly ConcurrentDictionary tasks = new();
12 |
13 | private readonly ConcurrentDictionary tasksById = new();
14 |
15 | public IEnumerable GetTestsToRun()
16 | {
17 | return depot.GetTestsToRun();
18 | }
19 |
20 | public TaskWrapper GetTask(IMspecElement element)
21 | {
22 | return tasks.GetOrAdd(element, key =>
23 | {
24 | var task = depot[MspecReSharperId.Create(key)] ?? CreateTask(element);
25 |
26 | return tasksById.GetOrAdd(task.TestId, _ => new TaskWrapper(task, sink));
27 | });
28 | }
29 |
30 | private MspecRemoteTask CreateTask(IMspecElement element)
31 | {
32 | var task = RemoteTaskBuilder.GetRemoteTask(element);
33 |
34 | sink.DynamicTestDiscovered(task);
35 |
36 | depot.Add(task);
37 |
38 | return task;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/RunTracker.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
5 |
6 | public class RunTracker
7 | {
8 | private readonly ConcurrentLookup pendingContexts = new();
9 |
10 | private readonly ConcurrentLookup runningContexts = new();
11 |
12 | private readonly ConcurrentLookup pendingSpecifications = new();
13 |
14 | private readonly ConcurrentLookup runningSpecifications = new();
15 |
16 | public RunTracker(ISpecificationElement[] specifications)
17 | {
18 | var contexts = specifications
19 | .Select(x => x.Context)
20 | .Distinct();
21 |
22 | foreach (var context in contexts)
23 | {
24 | pendingContexts.Add(context);
25 | }
26 |
27 | foreach (var specification in specifications)
28 | {
29 | pendingSpecifications.Add(specification);
30 | }
31 | }
32 |
33 | public IContextElement? StartContext(string id)
34 | {
35 | var element = pendingContexts.Take(id);
36 |
37 | runningContexts.Add(element);
38 |
39 | return element;
40 | }
41 |
42 | public ISpecificationElement? StartSpecification(string id)
43 | {
44 | var element = pendingSpecifications.Take(id);
45 |
46 | runningSpecifications.Add(element);
47 |
48 | return element;
49 | }
50 |
51 | public IContextElement? FinishContext(string id)
52 | {
53 | return runningContexts.Take(id);
54 | }
55 |
56 | public ISpecificationElement? FinishSpecification(string id)
57 | {
58 | return runningSpecifications.Take(id);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/TaskWrapper.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Threading;
3 | using JetBrains.ReSharper.TestRunner.Abstractions;
4 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
5 | using Machine.Specifications.Runner.ReSharper.Tasks;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
8 |
9 | public class TaskWrapper(MspecRemoteTask? task, ITestExecutionSink sink)
10 | {
11 | private readonly Stopwatch watch = new();
12 |
13 | private int started;
14 |
15 | private int finished;
16 |
17 | private TestOutcome result;
18 |
19 | private string? message;
20 |
21 | public void Starting()
22 | {
23 | if (Interlocked.Exchange(ref started, 1) != 0)
24 | {
25 | return;
26 | }
27 |
28 | if (task != null)
29 | {
30 | watch.Restart();
31 |
32 | sink.TestStarting(task);
33 | }
34 | }
35 |
36 | public void Output(string output)
37 | {
38 | if (string.IsNullOrEmpty(output))
39 | {
40 | return;
41 | }
42 |
43 | if (task != null)
44 | {
45 | sink.TestOutput(task, output, TestOutputType.STDOUT);
46 | }
47 | }
48 |
49 | public void Skipped(string? reason = null)
50 | {
51 | result = TestOutcome.Ignored;
52 | message = reason ?? task?.IgnoreReason ?? "Ignored";
53 |
54 | Finished();
55 | }
56 |
57 | public void Passed()
58 | {
59 | result = TestOutcome.Success;
60 | }
61 |
62 | public void Failed(ExceptionInfo[] exceptions, string exceptionMessage)
63 | {
64 | result = TestOutcome.Failed;
65 | message = exceptionMessage;
66 |
67 | if (task != null)
68 | {
69 | sink.TestException(task, exceptions);
70 | }
71 | }
72 |
73 | public void Finished(bool childTestsFailed = false)
74 | {
75 | watch.Stop();
76 |
77 | if (result == TestOutcome.Inconclusive)
78 | {
79 | result = childTestsFailed
80 | ? TestOutcome.Failed
81 | : TestOutcome.Success;
82 |
83 | message = childTestsFailed
84 | ? "One or more child tests failed"
85 | : string.Empty;
86 | }
87 |
88 | if (Interlocked.Exchange(ref finished, 1) != 0)
89 | {
90 | return;
91 | }
92 |
93 | if (task != null)
94 | {
95 | sink.TestFinished(task, result, message, watch.Elapsed);
96 | }
97 | }
98 |
99 | public void Reset()
100 | {
101 | finished = 0;
102 | result = TestOutcome.Inconclusive;
103 | message = string.Empty;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Execution/TestExecutionListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading;
6 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
7 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
8 | using Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
9 |
10 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Execution;
11 |
12 | public class TestExecutionListener(RunContext runContext, ElementCache cache, CancellationToken token)
13 | : IExecutionListener
14 | {
15 | private readonly CancellationToken token = token;
16 |
17 | private readonly HashSet passed = [];
18 |
19 | private readonly HashSet failed = [];
20 |
21 | private readonly ManualResetEvent waitEvent = new(false);
22 |
23 | public WaitHandle Finished => waitEvent;
24 |
25 | public void OnAssemblyStart(string assemblyLocation)
26 | {
27 | if (string.IsNullOrEmpty(assemblyLocation))
28 | {
29 | return;
30 | }
31 |
32 | var path = Path.GetDirectoryName(assemblyLocation);
33 |
34 | if (!string.IsNullOrEmpty(path))
35 | {
36 | Environment.CurrentDirectory = path;
37 | }
38 | }
39 |
40 | public void OnAssemblyEnd(string assemblyLocation)
41 | {
42 | }
43 |
44 | public void OnRunStart()
45 | {
46 | }
47 |
48 | public void OnRunEnd()
49 | {
50 | waitEvent.Set();
51 | }
52 |
53 | public void OnContextStart(IContextElement context)
54 | {
55 | if (token.IsCancellationRequested)
56 | {
57 | return;
58 | }
59 |
60 | runContext.GetTask(context).Starting();
61 | }
62 |
63 | public void OnContextEnd(IContextElement context, string capturedOutput)
64 | {
65 | if (token.IsCancellationRequested)
66 | {
67 | return;
68 | }
69 |
70 | var task = runContext.GetTask(context);
71 | var tests = cache.GetSpecifications(context).ToArray();
72 | var behaviors = cache.GetBehaviors(context).ToArray();
73 |
74 | var testsPassed = tests.All(x => passed.Contains(x));
75 | var testsFailed = tests.Any(x => failed.Contains(x));
76 |
77 | if (testsPassed)
78 | {
79 | task.Passed();
80 | }
81 |
82 | task.Output(capturedOutput);
83 |
84 | var ignoreReason = !behaviors.Any() || behaviors.Any(x => string.IsNullOrEmpty(x.IgnoreReason))
85 | ? null
86 | : context.IgnoreReason;
87 |
88 | if (string.IsNullOrEmpty(ignoreReason))
89 | {
90 | task.Finished(testsFailed);
91 | }
92 | else
93 | {
94 | task.Skipped(context.IgnoreReason);
95 | }
96 | }
97 |
98 | public void OnBehaviorStart(IBehaviorElement behavior)
99 | {
100 | if (token.IsCancellationRequested)
101 | {
102 | return;
103 | }
104 |
105 | runContext.GetTask(behavior).Starting();
106 | }
107 |
108 | public void OnBehaviorEnd(IBehaviorElement behavior, string capturedOutput)
109 | {
110 | if (token.IsCancellationRequested)
111 | {
112 | return;
113 | }
114 |
115 | var task = runContext.GetTask(behavior);
116 | var tests = cache.GetSpecifications(behavior).ToArray();
117 |
118 | var testsPassed = tests.All(x => passed.Contains(x));
119 | var testsFailed = tests.Any(x => failed.Contains(x));
120 |
121 | if (testsPassed)
122 | {
123 | task.Passed();
124 | }
125 |
126 | task.Output(capturedOutput);
127 |
128 | if (string.IsNullOrEmpty(behavior.IgnoreReason))
129 | {
130 | task.Finished(testsFailed);
131 | }
132 | else
133 | {
134 | task.Skipped(behavior.IgnoreReason);
135 | }
136 | }
137 |
138 | public void OnSpecificationStart(ISpecificationElement specification)
139 | {
140 | if (token.IsCancellationRequested)
141 | {
142 | return;
143 | }
144 |
145 | runContext.GetTask(specification).Starting();
146 | }
147 |
148 | public void OnSpecificationEnd(ISpecificationElement specification, string capturedOutput, TestRunResult runResult)
149 | {
150 | if (token.IsCancellationRequested)
151 | {
152 | return;
153 | }
154 |
155 | var task = runContext.GetTask(specification);
156 |
157 | task.Output(capturedOutput);
158 |
159 | if (runResult.Status == TestStatus.Failing)
160 | {
161 | failed.Add(specification);
162 |
163 | task.Failed(runResult.Exception?.Exceptions ?? Array.Empty(), runResult.Exception?.ExceptionMessage ?? string.Empty);
164 | task.Finished();
165 | }
166 | else if (runResult.Status == TestStatus.Passing)
167 | {
168 | passed.Add(specification);
169 |
170 | task.Passed();
171 | task.Finished();
172 | }
173 | else if (runResult.Status == TestStatus.NotImplemented)
174 | {
175 | task.Skipped("Not implemented");
176 | }
177 | else if (runResult.Status == TestStatus.Ignored)
178 | {
179 | task.Skipped();
180 | }
181 | }
182 |
183 | public void OnFatalError(TestError? error)
184 | {
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/AdapterListener.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | internal class AdapterListener(IRunListener listener, string assemblyPath) : ISpecificationRunListener
6 | {
7 | public void OnAssemblyStart(AssemblyInfo assemblyInfo)
8 | {
9 | listener.OnAssemblyStart(new TestAssemblyInfo(assemblyPath));
10 | }
11 |
12 | public void OnAssemblyEnd(AssemblyInfo assemblyInfo)
13 | {
14 | listener.OnAssemblyEnd(assemblyInfo.ToTestAssembly());
15 | }
16 |
17 | public void OnRunStart()
18 | {
19 | listener.OnRunStart();
20 | }
21 |
22 | public void OnRunEnd()
23 | {
24 | listener.OnRunEnd();
25 | }
26 |
27 | public void OnContextStart(ContextInfo contextInfo)
28 | {
29 | listener.OnContextStart(contextInfo.ToTestContext());
30 | }
31 |
32 | public void OnContextEnd(ContextInfo contextInfo)
33 | {
34 | listener.OnContextEnd(contextInfo.ToTestContext());
35 | }
36 |
37 | public void OnSpecificationStart(SpecificationInfo specificationInfo)
38 | {
39 | listener.OnSpecificationStart(specificationInfo.ToTestSpecification());
40 | }
41 |
42 | public void OnSpecificationEnd(SpecificationInfo specificationInfo, Result result)
43 | {
44 | listener.OnSpecificationEnd(specificationInfo.ToTestSpecification(), result.ToTestResult());
45 | }
46 |
47 | public void OnFatalError(ExceptionResult exceptionResult)
48 | {
49 | listener.OnFatalError(exceptionResult.ToTestError());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/AssemblyInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | internal static class AssemblyInfoExtensions
6 | {
7 | public static TestAssemblyInfo ToTestAssembly(this AssemblyInfo assembly)
8 | {
9 | return new TestAssemblyInfo(assembly.Location);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/ContextInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | internal static class ContextInfoExtensions
6 | {
7 | public static TestContextInfo ToTestContext(this ContextInfo context)
8 | {
9 | return new TestContextInfo(context.TypeName, context.CapturedOutput);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/ExceptionResultExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | internal static class ExceptionResultExtensions
6 | {
7 | public static TestError? ToTestError(this ExceptionResult? result)
8 | {
9 | if (result == null)
10 | {
11 | return null;
12 | }
13 |
14 | return new TestError(result.FullTypeName, result.GetExceptions(), result.GetExceptionMessage());
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/IRunListener.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
2 |
3 | public interface IRunListener
4 | {
5 | void OnAssemblyStart(TestAssemblyInfo assemblyInfo);
6 |
7 | void OnAssemblyEnd(TestAssemblyInfo assemblyInfo);
8 |
9 | void OnRunStart();
10 |
11 | void OnRunEnd();
12 |
13 | void OnContextStart(TestContextInfo contextInfo);
14 |
15 | void OnContextEnd(TestContextInfo contextInfo);
16 |
17 | void OnSpecificationStart(TestSpecificationInfo specificationInfo);
18 |
19 | void OnSpecificationEnd(TestSpecificationInfo specificationInfo, TestRunResult runResult);
20 |
21 | void OnFatalError(TestError? error);
22 | }
23 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/LoggingRunListener.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestRunner.Abstractions;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | public class LoggingRunListener(IRunListener listener) : IRunListener
6 | {
7 | private readonly ILogger logger = Logger.GetLogger();
8 |
9 | public void OnAssemblyStart(TestAssemblyInfo assemblyInfo)
10 | {
11 | logger.Trace($"OnAssemblyStart: {assemblyInfo.Location}");
12 |
13 | logger.Catch(() => listener.OnAssemblyStart(assemblyInfo));
14 | }
15 |
16 | public void OnAssemblyEnd(TestAssemblyInfo assemblyInfo)
17 | {
18 | logger.Trace($"OnAssemblyEnd: {assemblyInfo.Location}");
19 |
20 | logger.Catch(() => listener.OnAssemblyEnd(assemblyInfo));
21 | }
22 |
23 | public void OnRunStart()
24 | {
25 | logger.Trace("OnRunStart:");
26 |
27 | logger.Catch(listener.OnRunStart);
28 | }
29 |
30 | public void OnRunEnd()
31 | {
32 | logger.Trace("OnRunEnd:");
33 |
34 | logger.Catch(listener.OnRunEnd);
35 | }
36 |
37 | public void OnContextStart(TestContextInfo contextInfo)
38 | {
39 | logger.Trace($"OnContextStart: {contextInfo.TypeName}");
40 |
41 | logger.Catch(() => listener.OnContextStart(contextInfo));
42 | }
43 |
44 | public void OnContextEnd(TestContextInfo contextInfo)
45 | {
46 | logger.Trace($"OnContextEnd: {contextInfo.TypeName}");
47 |
48 | logger.Catch(() => listener.OnContextEnd(contextInfo));
49 | }
50 |
51 | public void OnSpecificationStart(TestSpecificationInfo specificationInfo)
52 | {
53 | logger.Trace($"OnSpecificationStart: {specificationInfo.ContainingType}.{specificationInfo.FieldName}");
54 |
55 | logger.Catch(() => listener.OnSpecificationStart(specificationInfo));
56 | }
57 |
58 | public void OnSpecificationEnd(TestSpecificationInfo specificationInfo, TestRunResult runResult)
59 | {
60 | logger.Trace($"OnSpecificationEnd: {specificationInfo.ContainingType}.{specificationInfo.FieldName}");
61 |
62 | logger.Catch(() => listener.OnSpecificationEnd(specificationInfo, runResult));
63 | }
64 |
65 | public void OnFatalError(TestError? error)
66 | {
67 | logger.Trace($"OnFatalError: {error?.FullTypeName}");
68 |
69 | logger.Catch(() => listener.OnFatalError(error));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/ResultExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | internal static class ResultExtensions
6 | {
7 | public static TestRunResult ToTestResult(this Result result)
8 | {
9 | return new TestRunResult(result.Status.ToTestStatus(), result.Exception.ToTestError());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/SpecificationInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.Utility;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | internal static class SpecificationInfoExtensions
6 | {
7 | public static TestSpecificationInfo ToTestSpecification(this SpecificationInfo specification)
8 | {
9 | return new TestSpecificationInfo(specification.ContainingType, specification.FieldName, specification.CapturedOutput);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/StatusExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Machine.Specifications.Runner.Utility;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
5 |
6 | internal static class StatusExtensions
7 | {
8 | public static TestStatus ToTestStatus(this Status status)
9 | {
10 | return status switch
11 | {
12 | Status.Ignored => TestStatus.Ignored,
13 | Status.Failing => TestStatus.Failing,
14 | Status.NotImplemented => TestStatus.NotImplemented,
15 | Status.Passing => TestStatus.Passing,
16 | _ => throw new ArgumentException()
17 | };
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/TestAssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
2 |
3 | public class TestAssemblyInfo(string location)
4 | {
5 | public string Location { get; } = location;
6 | }
7 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/TestContextInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
2 |
3 | public class TestContextInfo(string typeName, string capturedOutput)
4 | {
5 | public string TypeName { get; } = typeName;
6 |
7 | public string CapturedOutput { get; } = capturedOutput;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/TestError.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
4 |
5 | public class TestError(string fullTypeName, ExceptionInfo[] exceptions, string exceptionMessage)
6 | {
7 | public string FullTypeName { get; } = fullTypeName;
8 |
9 | public ExceptionInfo[] Exceptions { get; } = exceptions;
10 |
11 | public string ExceptionMessage { get; } = exceptionMessage;
12 | }
13 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/TestRunResult.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
2 |
3 | public class TestRunResult(TestStatus status, TestError? exception = null)
4 | {
5 | public TestStatus Status { get; } = status;
6 |
7 | public TestError? Exception { get; } = exception;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/TestSpecificationInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
2 |
3 | public class TestSpecificationInfo(string containingType, string fieldName, string capturedOutput)
4 | {
5 | public string ContainingType { get; } = containingType;
6 |
7 | public string FieldName { get; } = fieldName;
8 |
9 | public string CapturedOutput { get; } = capturedOutput;
10 | }
11 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Listeners/TestStatus.cs:
--------------------------------------------------------------------------------
1 | namespace Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
2 |
3 | public enum TestStatus
4 | {
5 | Failing,
6 | Passing,
7 | NotImplemented,
8 | Ignored,
9 | }
10 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/Machine.Specifications.Runner.ReSharper.Adapters.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net461
5 | true
6 | Machine.Specifications.Runner.ReSharper.Adapters
7 | false
8 | latest
9 | enable
10 |
11 | Machine.Specifications.Runner.ReSharper.Adapters.net461
12 | Machine.Specifications.Runner.ReSharper.Adapters.netstandard20
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | "$(ILRepack)" --internalize --norepackres --out:"$(OutputPath)$(AssemblyName).dll" "$(TargetPath)" "$(OutputPath)Machine.Specifications.Runner.Utility.dll"
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/MspecReSharperId.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Adapters;
5 |
6 | public class MspecReSharperId : IEquatable
7 | {
8 | public MspecReSharperId(IContextElement context)
9 | {
10 | Id = context.TypeName;
11 | }
12 |
13 | public MspecReSharperId(IBehaviorElement behavior)
14 | {
15 | Id = $"{behavior.Context.TypeName}.{behavior.FieldName}";
16 | }
17 |
18 | public MspecReSharperId(ISpecificationElement specification)
19 | {
20 | Id = specification.Behavior != null
21 | ? $"{specification.Context.TypeName}.{specification.Behavior.FieldName}.{specification.FieldName}"
22 | : $"{specification.Context.TypeName}.{specification.FieldName}";
23 | }
24 |
25 | public string Id { get; }
26 |
27 | public bool Equals(MspecReSharperId? other)
28 | {
29 | return other != null && Id == other.Id;
30 | }
31 |
32 | public override bool Equals(object? obj)
33 | {
34 | return Equals(obj as MspecReSharperId);
35 | }
36 |
37 | public override int GetHashCode()
38 | {
39 | return Id.GetHashCode();
40 | }
41 |
42 | public override string ToString()
43 | {
44 | return $"ReSharperId({Id})";
45 | }
46 |
47 | public static string Self(IContextElement context)
48 | {
49 | return new MspecReSharperId(context).Id;
50 | }
51 |
52 | public static string Self(IBehaviorElement behavior)
53 | {
54 | return new MspecReSharperId(behavior).Id;
55 | }
56 |
57 | public static string Self(ISpecificationElement specification)
58 | {
59 | return new MspecReSharperId(specification).Id;
60 | }
61 |
62 | public static string Self(IMspecElement element)
63 | {
64 | return Create(element).Id;
65 | }
66 |
67 | public static MspecReSharperId Create(IMspecElement element)
68 | {
69 | return element switch
70 | {
71 | IContextElement context => new MspecReSharperId(context),
72 | ISpecificationElement specification => new MspecReSharperId(specification),
73 | IBehaviorElement behavior => new MspecReSharperId(behavior),
74 | _ => throw new ArgumentOutOfRangeException(nameof(element))
75 | };
76 | }
77 |
78 | public static string? Parent(IMspecElement element)
79 | {
80 | return element switch
81 | {
82 | IContextElement => null,
83 | IBehaviorElement behavior => Self(behavior.Context),
84 | ISpecificationElement {Behavior: not null} specification => Self(specification.Behavior),
85 | ISpecificationElement {Behavior: null} specification => Self(specification.Context),
86 | _ => throw new ArgumentOutOfRangeException(nameof(element))
87 | };
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/MspecRunner.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using JetBrains.ReSharper.TestRunner.Abstractions;
3 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
5 | using Machine.Specifications.Runner.ReSharper.Adapters.Execution;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Adapters;
8 |
9 | public class MspecRunner : LongLivedMarshalByRefObject, ITestDiscoverer, ITestExecutor
10 | {
11 | private readonly ILogger logger = Logger.GetLogger();
12 |
13 | private readonly CancellationTokenSource discoveryToken = new();
14 |
15 | private readonly CancellationTokenSource executionToken = new();
16 |
17 | public void DiscoverTests(TestDiscoveryRequest request, ITestDiscoverySink discoverySink)
18 | {
19 | logger.Info("Exploration started");
20 |
21 | var depot = new RemoteTaskDepot([]);
22 | var controller = new MspecController(request, discoveryToken.Token);
23 |
24 | var discoverer = new Discoverer(request, controller, discoverySink, depot, discoveryToken.Token);
25 | discoverer.Discover();
26 |
27 | logger.Info("Exploration completed");
28 | }
29 |
30 | public void AbortDiscovery()
31 | {
32 | discoveryToken.Cancel();
33 | }
34 |
35 | public void RunTests(TestRunRequest request, ITestDiscoverySink discoverySink, ITestExecutionSink executionSink)
36 | {
37 | logger.Info("Execution started");
38 |
39 | var depot = new RemoteTaskDepot(request.Selection);
40 | var controller = new MspecController(request, discoveryToken.Token);
41 |
42 | var discoverer = new Discoverer(request, controller, discoverySink, depot, discoveryToken.Token);
43 | discoverer.Discover();
44 |
45 | var executor = new Executor(executionSink, request, depot, executionToken.Token);
46 | executor.Execute();
47 |
48 | logger.Info("Execution completed");
49 | }
50 |
51 | public void AbortRun()
52 | {
53 | executionToken.Cancel();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/RemoteTaskBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
3 | using Machine.Specifications.Runner.ReSharper.Tasks;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Adapters;
6 |
7 | public static class RemoteTaskBuilder
8 | {
9 | public static MspecRemoteTask GetRemoteTask(IMspecElement element)
10 | {
11 | return element switch
12 | {
13 | IContextElement context => FromContext(context),
14 | IBehaviorElement behavior => FromBehavior(behavior),
15 | ISpecificationElement specification => FromSpecification(specification),
16 | _ => throw new ArgumentOutOfRangeException(nameof(element))
17 | };
18 | }
19 |
20 | private static MspecRemoteTask FromContext(IContextElement context)
21 | {
22 | return MspecContextRemoteTask.ToServer(MspecReSharperId.Self(context), context.Subject, null, context.IgnoreReason);
23 | }
24 |
25 | private static MspecRemoteTask FromBehavior(IBehaviorElement behavior)
26 | {
27 | return MspecSpecificationRemoteTask.ToServer(behavior.Context.TypeName, behavior.FieldName, behavior.TypeName, null, null, behavior.IgnoreReason);
28 | }
29 |
30 | private static MspecRemoteTask FromSpecification(ISpecificationElement specification)
31 | {
32 | if (specification.Behavior != null)
33 | {
34 | return MspecBehaviorSpecificationRemoteTask.ToServer(
35 | $"{specification.Context.TypeName}.{specification.Behavior.FieldName}",
36 | specification.Context.TypeName,
37 | specification.FieldName,
38 | specification.IgnoreReason);
39 | }
40 |
41 | return MspecSpecificationRemoteTask.ToServer(specification.Context.TypeName, specification.FieldName, null, null, null, specification.IgnoreReason);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Adapters/RemoteTaskDepot.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
5 | using Machine.Specifications.Runner.ReSharper.Tasks;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Adapters;
8 |
9 | public class RemoteTaskDepot
10 | {
11 | private readonly Dictionary tasksByReSharperId = new();
12 |
13 | private readonly List testsToRun = [];
14 |
15 | public RemoteTaskDepot(RemoteTask[] tasks)
16 | {
17 | foreach (var task in tasks.OfType())
18 | {
19 | tasksByReSharperId[task.TestId] = task;
20 | }
21 | }
22 |
23 | public MspecRemoteTask? this[IMspecElement element]
24 | {
25 | get
26 | {
27 | tasksByReSharperId.TryGetValue(MspecReSharperId.Self(element), out var value);
28 |
29 | return value;
30 | }
31 | }
32 |
33 | public MspecRemoteTask? this[MspecReSharperId id]
34 | {
35 | get
36 | {
37 | tasksByReSharperId.TryGetValue(id.Id, out var value);
38 |
39 | return value;
40 | }
41 | }
42 |
43 | public void Add(MspecRemoteTask task)
44 | {
45 | tasksByReSharperId[task.TestId] = task;
46 | }
47 |
48 | public void Bind(IMspecElement element, MspecRemoteTask task)
49 | {
50 | if (tasksByReSharperId.ContainsKey(task.TestId) && element is ISpecificationElement specification)
51 | {
52 | testsToRun.Add(specification);
53 | }
54 | }
55 |
56 | public IEnumerable GetTestsToRun()
57 | {
58 | return testsToRun;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tasks/Machine.Specifications.Runner.ReSharper.Tasks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net461
5 | Machine.Specifications.Runner.ReSharper.Tasks
6 | false
7 | latest
8 | enable
9 |
10 | Machine.Specifications.Runner.ReSharper.Tasks.net461
11 | Machine.Specifications.Runner.ReSharper.Tasks.netstandard20
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tasks/MspecBehaviorSpecificationRemoteTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Tasks;
4 |
5 | [Serializable]
6 | public class MspecBehaviorSpecificationRemoteTask : MspecRemoteTask
7 | {
8 | private MspecBehaviorSpecificationRemoteTask(string testId, string? ignoreReason, bool runExplicitly)
9 | : base(testId, ignoreReason, true, runExplicitly)
10 | {
11 | }
12 |
13 | private MspecBehaviorSpecificationRemoteTask(string parentId, string contextTypeName, string fieldName, string? ignoreReason)
14 | : base($"{parentId}.{fieldName}", ignoreReason)
15 | {
16 | ParentId = parentId;
17 | ContextTypeName = contextTypeName;
18 | FieldName = fieldName;
19 | }
20 |
21 | public string? ParentId { get; set; }
22 |
23 | public string? ContextTypeName { get; set; }
24 |
25 | public string? FieldName { get; set; }
26 |
27 | public static MspecBehaviorSpecificationRemoteTask ToClient(string testId, string? ignoreReason, bool runExplicitly)
28 | {
29 | return new(testId, ignoreReason, runExplicitly);
30 | }
31 |
32 | public static MspecBehaviorSpecificationRemoteTask ToServer(string parentId, string contextTypeName, string fieldName, string? ignoreReason)
33 | {
34 | return new(parentId, contextTypeName, fieldName, ignoreReason);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tasks/MspecContextRemoteTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Tasks;
4 |
5 | [Serializable]
6 | public class MspecContextRemoteTask : MspecRemoteTask
7 | {
8 | private MspecContextRemoteTask(string testId, string? ignoreReason, bool runAllChildren, bool runExplicitly)
9 | : base(testId, ignoreReason, runAllChildren, runExplicitly)
10 | {
11 | }
12 |
13 | private MspecContextRemoteTask(string typeName, string? subject, string[]? tags, string? ignoreReason)
14 | : base(typeName, ignoreReason)
15 | {
16 | Subject = subject;
17 | Tags = tags;
18 | }
19 |
20 | public string ContextTypeName => TestId;
21 |
22 | public string? Subject { get; set; }
23 |
24 | public string[]? Tags { get; set; }
25 |
26 | public static MspecContextRemoteTask ToClient(string testId, string? ignoreReason, bool runAllChildren, bool runExplicitly)
27 | {
28 | return new(testId, ignoreReason, runAllChildren, runExplicitly);
29 | }
30 |
31 | public static MspecContextRemoteTask ToServer(string typeName, string? subject, string[]? tags, string? ignoreReason)
32 | {
33 | return new(typeName, subject, tags, ignoreReason);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tasks/MspecRemoteTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Tasks;
5 |
6 | [Serializable]
7 | public abstract class MspecRemoteTask(
8 | string testId,
9 | string? ignoreReason,
10 | bool runAllChildren = true,
11 | bool runExplicitly = false)
12 | : RemoteTask
13 | {
14 | public string TestId { get; } = testId;
15 |
16 | public string? IgnoreReason { get; } = ignoreReason;
17 |
18 | public bool RunAllChildren { get; } = runAllChildren;
19 |
20 | public bool RunExplicitly { get; } = runExplicitly;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tasks/MspecSpecificationRemoteTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Tasks;
4 |
5 | [Serializable]
6 | public class MspecSpecificationRemoteTask : MspecRemoteTask
7 | {
8 | private MspecSpecificationRemoteTask(string testId, string? ignoreReason, bool runAllChildren, bool runExplicitly)
9 | : base(testId, ignoreReason, runAllChildren, runExplicitly)
10 | {
11 | }
12 |
13 | private MspecSpecificationRemoteTask(
14 | string contextTypeName,
15 | string fieldName,
16 | string? behaviorType,
17 | string? subject,
18 | string[]? tags,
19 | string? ignoreReason)
20 | : base($"{contextTypeName}.{fieldName}", ignoreReason)
21 | {
22 | ContextTypeName = contextTypeName;
23 | FieldName = fieldName;
24 | BehaviorType = behaviorType;
25 | Subject = subject;
26 | Tags = tags;
27 | }
28 |
29 | public string? ContextTypeName { get; set; }
30 |
31 | public string? BehaviorType { get; set; }
32 |
33 | public string? FieldName { get; set; }
34 |
35 | public string? Subject { get; }
36 |
37 | public string[]? Tags { get; set; }
38 |
39 | public static MspecSpecificationRemoteTask ToClient(
40 | string testId,
41 | string? ignoreReason,
42 | bool runAllChildren,
43 | bool runExplicitly)
44 | {
45 | return new(testId, ignoreReason, runAllChildren, runExplicitly);
46 | }
47 |
48 | public static MspecSpecificationRemoteTask ToServer(
49 | string contextTypeName,
50 | string fieldName,
51 | string? behaviorType,
52 | string? subject,
53 | string[]? tags,
54 | string? ignoreReason)
55 | {
56 | return new(contextTypeName, fieldName, behaviorType, subject, tags, ignoreReason);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tasks/MspecTestContainer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Tasks;
5 |
6 | [Serializable]
7 | public class MspecTestContainer(string location, ShadowCopy shadowCopy) : TestContainer(location, shadowCopy);
8 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Discovery/MspecDiscoverySinkTests.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Machine.Specifications.Runner.ReSharper.Adapters.Discovery;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
5 | using NUnit.Framework;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Discovery;
8 |
9 | [TestFixture]
10 | public class MspecDiscoverySinkTests
11 | {
12 | [Test]
13 | public async Task AddsSingleContext()
14 | {
15 | var context = new ContextElement("Namespace.ContextType", "subject", null);
16 |
17 | var specification1 = new SpecificationElement(context, "should_be");
18 | var specification2 = new SpecificationElement(context, "should_not_be");
19 |
20 | var sink = new MspecDiscoverySink(CancellationToken.None);
21 | sink.OnSpecification(specification1);
22 | sink.OnSpecification(specification2);
23 | sink.OnDiscoveryCompleted();
24 |
25 | var results = await sink.Elements;
26 |
27 | Assert.That(results.Length, Is.EqualTo(3));
28 | Assert.That(results, Has.Member(context));
29 | Assert.That(results, Has.Member(specification1));
30 | Assert.That(results, Has.Member(specification2));
31 | }
32 |
33 | [Test]
34 | public async Task AddsSingleBehavior()
35 | {
36 | var context = new ContextElement("Namespace.ContextType", "subject", null);
37 |
38 | var behavior = new BehaviorElement(context, "Namespace.ABehavior", "a_vehicle_that_is_started", null);
39 |
40 | var specification1 = new SpecificationElement(context, "should_be", null, behavior);
41 | var specification2 = new SpecificationElement(context, "should_not_be", null, behavior);
42 |
43 | var sink = new MspecDiscoverySink(CancellationToken.None);
44 | sink.OnSpecification(specification1);
45 | sink.OnSpecification(specification2);
46 | sink.OnDiscoveryCompleted();
47 |
48 | var results = await sink.Elements;
49 |
50 | Assert.That(results.Length, Is.EqualTo(4));
51 | Assert.That(results, Has.Member(context));
52 | Assert.That(results, Has.Member(behavior));
53 | Assert.That(results, Has.Member(specification1));
54 | Assert.That(results, Has.Member(specification2));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Elements/BehaviorElementTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 | using NSubstitute;
3 | using NUnit.Framework;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Elements;
6 |
7 | [TestFixture]
8 | public class BehaviorElementTests
9 | {
10 | [Test]
11 | public void SetsCorrectId()
12 | {
13 | var context = Substitute.For();
14 | context.TypeName.Returns("Namespace.ContextType");
15 |
16 | var element = new BehaviorElement(context, "Namespace.BehaviorType", "a_vehicle_that_is_started", null);
17 |
18 | Assert.That(element.Id, Is.EqualTo("Namespace.ContextType.a_vehicle_that_is_started"));
19 | }
20 |
21 | [Test]
22 | public void SetsCorrectAggregateId()
23 | {
24 | var context = Substitute.For();
25 | context.TypeName.Returns("Namespace.ContextType");
26 |
27 | var element = new BehaviorElement(context, "Namespace.BehaviorType", "a_vehicle_that_is_started", null);
28 |
29 | Assert.That(element.AggregateId, Is.EqualTo("Namespace.ContextType.Namespace.BehaviorType"));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Elements/ContextElementTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 | using NUnit.Framework;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Elements;
5 |
6 | [TestFixture]
7 | public class ContextElementTests
8 | {
9 | [Test]
10 | public void SetsCorrectId()
11 | {
12 | var context = new ContextElement("Namespace.ContextType", "subject", null);
13 |
14 | Assert.That(context.Id, Is.EqualTo("Namespace.ContextType"));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Elements/SpecificationElementTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 | using NSubstitute;
3 | using NUnit.Framework;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Elements;
6 |
7 | [TestFixture]
8 | public class SpecificationElementTests
9 | {
10 | [Test]
11 | public void SetsCorrectIdForSpecification()
12 | {
13 | var context = Substitute.For();
14 | context.TypeName.Returns("Namespace.ContextType");
15 |
16 | var specification = new SpecificationElement(context, "should_be");
17 |
18 | Assert.That(specification.Id, Is.EqualTo("Namespace.ContextType.should_be"));
19 | }
20 |
21 | [Test]
22 | public void SetsCorrectIdForBehaviorSpecification()
23 | {
24 | var context = Substitute.For();
25 | context.TypeName.Returns("Namespace.ContextType");
26 |
27 | var behavior = Substitute.For();
28 | behavior.FieldName.Returns("behaves_like");
29 |
30 | var specification = new SpecificationElement(context, "should_be", null, behavior);
31 |
32 | Assert.That(specification.Id, Is.EqualTo("Namespace.ContextType.behaves_like.should_be"));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Execution/ConcurrentLookupTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Execution;
3 | using NUnit.Framework;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Execution;
6 |
7 | public class ConcurrentLookupTests
8 | {
9 | [Test]
10 | public void CanAddWithSameAggregateId()
11 | {
12 | var lookup = new ConcurrentLookup();
13 |
14 | lookup.Add(new ContextElement("Type", "Subject", null));
15 | lookup.Add(new ContextElement("Type", "SubjectS", null));
16 | }
17 |
18 | [Test]
19 | public void CanTakeWhenUsingDuplicateIds()
20 | {
21 | var lookup = new ConcurrentLookup();
22 |
23 | lookup.Add(new ContextElement("Type", "Subject", null));
24 | lookup.Add(new ContextElement("Type", "SubjectS", null));
25 |
26 | Assert.That(lookup.Take("Type"), Is.Not.Null);
27 | Assert.That(lookup.Take("Type"), Is.Not.Null);
28 | }
29 |
30 | [Test]
31 | public void ReturnsNullWhenAllTaken()
32 | {
33 | var lookup = new ConcurrentLookup();
34 |
35 | lookup.Add(new ContextElement("Type", "Subject", null));
36 | lookup.Add(new ContextElement("Type", "SubjectS", null));
37 |
38 | Assert.That(lookup.Take("Type"), Is.Not.Null);
39 | Assert.That(lookup.Take("Type"), Is.Not.Null);
40 | Assert.That(lookup.Take("Type"), Is.Null);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Execution/ElementCacheTests.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Execution;
3 | using Machine.Specifications.Runner.ReSharper.Tests.Fixtures;
4 | using NUnit.Framework;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Execution;
7 |
8 | public class ElementCacheTests
9 | {
10 | [Test]
11 | public void CanGetBehaviorType()
12 | {
13 | var cache = new ElementCache([
14 | ElementFixtures.Specification1,
15 | ElementFixtures.Behavior1Specification1,
16 | ElementFixtures.Behavior2Specification1
17 | ]);
18 |
19 | Assert.That(cache.IsBehavior(ElementFixtures.Behavior1.TypeName), Is.True);
20 | Assert.That(cache.IsBehavior(ElementFixtures.Context.TypeName), Is.False);
21 | }
22 |
23 | [Test]
24 | public void CanGetBehaviors()
25 | {
26 | var cache = new ElementCache([
27 | ElementFixtures.Specification1,
28 | ElementFixtures.Behavior1Specification1,
29 | ElementFixtures.Behavior1Specification2,
30 | ElementFixtures.Behavior2Specification1
31 | ]);
32 |
33 | var behaviors = cache.GetBehaviors(ElementFixtures.Context).ToArray();
34 |
35 | Assert.That(behaviors, Has.Length.EqualTo(2));
36 | Assert.That(behaviors, Contains.Item(ElementFixtures.Behavior1));
37 | Assert.That(behaviors, Contains.Item(ElementFixtures.Behavior2));
38 | }
39 |
40 | [Test]
41 | public void CanGetSpecificationsByContext()
42 | {
43 | var cache = new ElementCache([
44 | ElementFixtures.Specification1,
45 | ElementFixtures.Behavior1Specification1,
46 | ElementFixtures.Behavior1Specification2,
47 | ElementFixtures.Behavior2Specification1
48 | ]);
49 |
50 | var specifications = cache.GetSpecifications(ElementFixtures.Context).ToArray();
51 |
52 | Assert.That(specifications, Has.Length.EqualTo(4));
53 | Assert.That(specifications, Contains.Item(ElementFixtures.Specification1));
54 | Assert.That(specifications, Contains.Item(ElementFixtures.Behavior1Specification1));
55 | Assert.That(specifications, Contains.Item(ElementFixtures.Behavior1Specification2));
56 | Assert.That(specifications, Contains.Item(ElementFixtures.Behavior2Specification1));
57 | }
58 |
59 | [Test]
60 | public void CanGetSpecificationsByBehavior()
61 | {
62 | var cache = new ElementCache([
63 | ElementFixtures.Specification1,
64 | ElementFixtures.Behavior1Specification1,
65 | ElementFixtures.Behavior1Specification2,
66 | ElementFixtures.Behavior2Specification1
67 | ]);
68 |
69 | var specifications1 = cache.GetSpecifications(ElementFixtures.Behavior1).ToArray();
70 | var specifications2 = cache.GetSpecifications(ElementFixtures.Behavior2).ToArray();
71 |
72 | Assert.That(specifications1, Has.Length.EqualTo(2));
73 | Assert.That(specifications1, Contains.Item(ElementFixtures.Behavior1Specification1));
74 | Assert.That(specifications1, Contains.Item(ElementFixtures.Behavior1Specification2));
75 |
76 | Assert.That(specifications2, Has.Length.EqualTo(1));
77 | Assert.That(specifications2, Contains.Item(ElementFixtures.Behavior2Specification1));
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Execution/ExecutionAdapterRunListenerTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
3 | using Machine.Specifications.Runner.ReSharper.Adapters.Execution;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Listeners;
5 | using Machine.Specifications.Runner.ReSharper.Tests.Fixtures;
6 | using NSubstitute;
7 | using NUnit.Framework;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Execution;
10 |
11 | [TestFixture]
12 | public class ExecutionAdapterRunListenerTests
13 | {
14 | [Test]
15 | public void CanNotifyRun()
16 | {
17 | var sink = Substitute.For();
18 | var cache = new ElementCache([]);
19 | var tracker = new RunTracker([]);
20 |
21 | var listener = new ExecutionAdapterRunListener(sink, cache, tracker);
22 |
23 | listener.OnRunStart();
24 | listener.OnRunEnd();
25 |
26 | sink.Received().OnRunStart();
27 | sink.Received().OnRunEnd();
28 | }
29 |
30 | [Test]
31 | public void CanNotifyAssembly()
32 | {
33 | const string path = "path/to/assembly.dll";
34 |
35 | var sink = Substitute.For();
36 | var cache = new ElementCache([]);
37 | var tracker = new RunTracker([]);
38 |
39 | var listener = new ExecutionAdapterRunListener(sink, cache, tracker);
40 |
41 | var assembly = new TestAssemblyInfo(path);
42 |
43 | listener.OnAssemblyStart(assembly);
44 | listener.OnAssemblyEnd(assembly);
45 |
46 | sink.Received().OnAssemblyStart(path);
47 | sink.Received().OnAssemblyEnd(path);
48 | }
49 |
50 | [Test]
51 | public void CanStartAndEndContext()
52 | {
53 | var sink = Substitute.For();
54 | var elements = new ISpecificationElement[]
55 | {
56 | ElementFixtures.Specification1,
57 | ElementFixtures.Behavior1Specification1,
58 | ElementFixtures.Behavior1Specification2
59 | };
60 |
61 | var cache = new ElementCache(elements);
62 | var tracker = new RunTracker(elements);
63 |
64 | var listener = new ExecutionAdapterRunListener(sink, cache, tracker);
65 |
66 | var context = new TestContextInfo(ElementFixtures.Context.TypeName, string.Empty);
67 |
68 | listener.OnContextStart(context);
69 | listener.OnContextEnd(context);
70 |
71 | sink.Received().OnContextStart(ElementFixtures.Context);
72 | sink.Received().OnContextEnd(ElementFixtures.Context, Arg.Any());
73 | }
74 |
75 | [Test]
76 | public void CanStartAndEndSpecification()
77 | {
78 | var sink = Substitute.For();
79 | var elements = new ISpecificationElement[]
80 | {
81 | ElementFixtures.Specification1,
82 | ElementFixtures.Behavior1Specification1,
83 | ElementFixtures.Behavior1Specification2
84 | };
85 |
86 | var cache = new ElementCache(elements);
87 | var tracker = new RunTracker(elements);
88 |
89 | var listener = new ExecutionAdapterRunListener(sink, cache, tracker);
90 |
91 | var context = new TestContextInfo(ElementFixtures.Context.TypeName, string.Empty);
92 | var specification = new TestSpecificationInfo(ElementFixtures.Context.TypeName, ElementFixtures.Specification1.FieldName, string.Empty);
93 |
94 | listener.OnContextStart(context);
95 | listener.OnSpecificationStart(specification);
96 | listener.OnSpecificationEnd(specification, new TestRunResult(TestStatus.Passing));
97 | listener.OnContextEnd(context);
98 |
99 | sink.Received().OnContextStart(ElementFixtures.Context);
100 | sink.Received().OnSpecificationStart(ElementFixtures.Specification1);
101 | sink.Received().OnSpecificationEnd(ElementFixtures.Specification1, Arg.Any(), Arg.Any());
102 | sink.Received().OnContextEnd(ElementFixtures.Context, Arg.Any());
103 | }
104 |
105 | [Test]
106 | public void CanStartAndEndBehaviorSpecifications()
107 | {
108 | var sink = Substitute.For();
109 | var elements = new ISpecificationElement[]
110 | {
111 | ElementFixtures.Behavior1Specification1,
112 | ElementFixtures.Behavior1Specification2
113 | };
114 |
115 | var cache = new ElementCache(elements);
116 | var tracker = new RunTracker(elements);
117 |
118 | var listener = new ExecutionAdapterRunListener(sink, cache, tracker);
119 |
120 | var context = new TestContextInfo(ElementFixtures.Context.TypeName, string.Empty);
121 | var specification1 = new TestSpecificationInfo(ElementFixtures.Behavior1.TypeName, ElementFixtures.Behavior1Specification1.FieldName, string.Empty);
122 | var specification2 = new TestSpecificationInfo(ElementFixtures.Behavior1.TypeName, ElementFixtures.Behavior1Specification2.FieldName, string.Empty);
123 |
124 | listener.OnContextStart(context);
125 | listener.OnSpecificationStart(specification1);
126 | listener.OnSpecificationEnd(specification1, new TestRunResult(TestStatus.Passing));
127 | listener.OnSpecificationStart(specification2);
128 | listener.OnSpecificationEnd(specification2, new TestRunResult(TestStatus.Passing));
129 | listener.OnContextEnd(context);
130 |
131 | sink.Received().OnContextStart(ElementFixtures.Context);
132 | sink.Received().OnBehaviorStart(ElementFixtures.Behavior1);
133 | sink.Received().OnSpecificationStart(ElementFixtures.Behavior1Specification1);
134 | sink.Received().OnSpecificationEnd(ElementFixtures.Behavior1Specification1, Arg.Any(), Arg.Any());
135 | sink.Received().OnSpecificationStart(ElementFixtures.Behavior1Specification2);
136 | sink.Received().OnSpecificationEnd(ElementFixtures.Behavior1Specification2, Arg.Any(), Arg.Any());
137 | sink.Received().OnBehaviorEnd(ElementFixtures.Behavior1, Arg.Any());
138 | sink.Received().OnContextEnd(ElementFixtures.Context, Arg.Any());
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Execution/RunContextTests.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestRunner.Abstractions;
2 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
3 | using Machine.Specifications.Runner.ReSharper.Adapters;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Execution;
5 | using Machine.Specifications.Runner.ReSharper.Tests.Fixtures;
6 | using NSubstitute;
7 | using NUnit.Framework;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Execution;
10 |
11 | [TestFixture]
12 | public class RunContextTests
13 | {
14 | [Test]
15 | public void TaskIsNotReportedToSink()
16 | {
17 | var sink = Substitute.For();
18 |
19 | var depot = new RemoteTaskDepot([
20 | RemoteTaskFixtures.Context,
21 | RemoteTaskFixtures.Behavior1,
22 | RemoteTaskFixtures.Specification1,
23 | RemoteTaskFixtures.Behavior1Specification1
24 | ]);
25 |
26 | var context = new RunContext(depot, sink);
27 |
28 | Assert.That(context.GetTask(ElementFixtures.Specification1), Is.Not.Null);
29 | sink.DidNotReceive().DynamicTestDiscovered(Arg.Any());
30 | }
31 |
32 | [Test]
33 | public void TaskNotInDepotIsReportedToSink()
34 | {
35 | var sink = Substitute.For();
36 |
37 | var depot = new RemoteTaskDepot([
38 | RemoteTaskFixtures.Context
39 | ]);
40 |
41 | var context = new RunContext(depot, sink);
42 |
43 | Assert.That(context.GetTask(ElementFixtures.Specification1), Is.Not.Null);
44 | sink.Received().DynamicTestDiscovered(Arg.Any());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/Execution/TaskWrapperTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.ReSharper.TestRunner.Abstractions;
3 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
4 | using Machine.Specifications.Runner.ReSharper.Adapters.Execution;
5 | using Machine.Specifications.Runner.ReSharper.Tasks;
6 | using NSubstitute;
7 | using NUnit.Framework;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters.Execution;
10 |
11 | [TestFixture]
12 | public class TaskWrapperTests
13 | {
14 | [Test]
15 | public void StartsOnce()
16 | {
17 | var sink = Substitute.For();
18 | var task = MspecContextRemoteTask.ToServer("ContextType", null, null, null);
19 |
20 | var wrapper = new TaskWrapper(task, sink);
21 |
22 | wrapper.Starting();
23 | wrapper.Starting();
24 |
25 | sink.Received(1).TestStarting(task);
26 | }
27 |
28 | [Test]
29 | public void SetsOutput()
30 | {
31 | var sink = Substitute.For();
32 | var task = MspecContextRemoteTask.ToServer("ContextType", null, null, null);
33 |
34 | var wrapper = new TaskWrapper(task, sink);
35 |
36 | wrapper.Output("my result");
37 |
38 | sink.Received().TestOutput(task, "my result", TestOutputType.STDOUT);
39 | }
40 |
41 | [Test]
42 | public void CallsFinishedOnce()
43 | {
44 | var sink = Substitute.For();
45 | var task = MspecContextRemoteTask.ToServer("ContextType", null, null, null);
46 |
47 | var wrapper = new TaskWrapper(task, sink);
48 |
49 | wrapper.Finished();
50 | wrapper.Finished();
51 |
52 | sink.Received(1).TestFinished(task, TestOutcome.Success, Arg.Any(), Arg.Any());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Adapters/RemoteTaskDepotTests.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Machine.Specifications.Runner.ReSharper.Adapters;
3 | using Machine.Specifications.Runner.ReSharper.Tests.Fixtures;
4 | using NUnit.Framework;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Tests.Adapters;
7 |
8 | [TestFixture]
9 | public class RemoteTaskDepotTests
10 | {
11 | [Test]
12 | public void CanGetContextByElement()
13 | {
14 | var depot = new RemoteTaskDepot([
15 | RemoteTaskFixtures.Context
16 | ]);
17 |
18 | Assert.That(depot[ElementFixtures.Context], Is.Not.Null);
19 | }
20 |
21 | [Test]
22 | public void CanGetSpecificationByElement()
23 | {
24 | var depot = new RemoteTaskDepot([
25 | RemoteTaskFixtures.Context,
26 | RemoteTaskFixtures.Specification1
27 | ]);
28 |
29 | Assert.That(depot[ElementFixtures.Specification1], Is.Not.Null);
30 | }
31 |
32 | [Test]
33 | public void CanGetBehaviorByElement()
34 | {
35 | var depot = new RemoteTaskDepot([
36 | RemoteTaskFixtures.Context,
37 | RemoteTaskFixtures.Behavior1
38 | ]);
39 |
40 | Assert.That(depot[ElementFixtures.Behavior1], Is.Not.Null);
41 | }
42 |
43 | [Test]
44 | public void CanGetBehaviorSpecificationByElement()
45 | {
46 | var depot = new RemoteTaskDepot([
47 | RemoteTaskFixtures.Context,
48 | RemoteTaskFixtures.Behavior1,
49 | RemoteTaskFixtures.Behavior1Specification1
50 | ]);
51 |
52 | Assert.That(depot[ElementFixtures.Behavior1Specification1], Is.Not.Null);
53 | }
54 |
55 | [Test]
56 | public void CanGetBehaviorWhenThereIsSpecificationWithSameName()
57 | {
58 | var depot = new RemoteTaskDepot([
59 | RemoteTaskFixtures.Context,
60 | RemoteTaskFixtures.Behavior1,
61 | RemoteTaskFixtures.Specification1,
62 | RemoteTaskFixtures.Behavior1Specification1
63 | ]);
64 |
65 | Assert.That(depot[ElementFixtures.Specification1], Is.Not.Null);
66 | }
67 |
68 | [Test]
69 | public void BoundElementsAreRunnable()
70 | {
71 | var depot = new RemoteTaskDepot([
72 | RemoteTaskFixtures.Context,
73 | RemoteTaskFixtures.Behavior1,
74 | RemoteTaskFixtures.Specification1,
75 | RemoteTaskFixtures.Behavior1Specification1
76 | ]);
77 |
78 | depot.Bind(ElementFixtures.Context, RemoteTaskFixtures.Context);
79 | depot.Bind(ElementFixtures.Behavior1, RemoteTaskFixtures.Behavior1);
80 | depot.Bind(ElementFixtures.Specification1, RemoteTaskFixtures.Specification1);
81 | depot.Bind(ElementFixtures.Behavior1Specification1, RemoteTaskFixtures.Behavior1Specification1);
82 |
83 | var selected = depot.GetTestsToRun().ToArray();
84 |
85 | Assert.That(selected, Has.Member(ElementFixtures.Specification1));
86 | Assert.That(selected, Has.Member(ElementFixtures.Behavior1Specification1));
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Fixtures/ElementFixtures.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Adapters.Elements;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Tests.Fixtures;
4 |
5 | public static class ElementFixtures
6 | {
7 | public static ContextElement Context { get; } = new("Namespace.Context", string.Empty, null);
8 |
9 | public static BehaviorElement Behavior1 { get; } = new(Context, "Namespace.Behavior1", "behaves_like_1", null);
10 |
11 | public static BehaviorElement Behavior2 { get; } = new(Context, "Namespace.Behavior2", "behaves_like_2", null);
12 |
13 | public static SpecificationElement Specification1 { get; } = new(Context, "should_1");
14 |
15 | public static SpecificationElement Specification2 { get; } = new(Context, "should_2");
16 |
17 | public static SpecificationElement Behavior1Specification1 { get; } = new(Context, "should_behave_1", null, Behavior1);
18 |
19 | public static SpecificationElement Behavior1Specification2 { get; } = new(Context, "should_behave_2", null, Behavior1);
20 |
21 | public static SpecificationElement Behavior2Specification1 { get; } = new(Context, "should_behave_1", null, Behavior2);
22 | }
23 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Fixtures/RemoteTaskFixtures.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Tasks;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Tests.Fixtures;
4 |
5 | public static class RemoteTaskFixtures
6 | {
7 | public static MspecContextRemoteTask Context { get; } =
8 | MspecContextRemoteTask.ToServer(
9 | ElementFixtures.Context.TypeName,
10 | null,
11 | null,
12 | null);
13 |
14 | public static MspecSpecificationRemoteTask Specification1 { get; } =
15 | MspecSpecificationRemoteTask.ToServer(
16 | ElementFixtures.Context.TypeName,
17 | ElementFixtures.Specification1.FieldName,
18 | null,
19 | null,
20 | null,
21 | null);
22 |
23 | public static MspecSpecificationRemoteTask Specification2 { get; } =
24 | MspecSpecificationRemoteTask.ToServer(
25 | ElementFixtures.Context.TypeName,
26 | ElementFixtures.Specification2.FieldName,
27 | null,
28 | null,
29 | null,
30 | null);
31 |
32 | public static MspecSpecificationRemoteTask Behavior1 { get; } =
33 | MspecSpecificationRemoteTask.ToServer(
34 | ElementFixtures.Context.TypeName,
35 | ElementFixtures.Behavior1.FieldName,
36 | ElementFixtures.Behavior1.TypeName,
37 | null,
38 | null,
39 | null);
40 |
41 | public static MspecBehaviorSpecificationRemoteTask Behavior1Specification1 { get; } =
42 | MspecBehaviorSpecificationRemoteTask.ToServer(
43 | ElementFixtures.Behavior1.Id,
44 | ElementFixtures.Context.TypeName,
45 | ElementFixtures.Behavior1Specification1.FieldName,
46 | null);
47 |
48 | public static MspecBehaviorSpecificationRemoteTask Behavior1Specification2 { get; } =
49 | MspecBehaviorSpecificationRemoteTask.ToServer(
50 | ElementFixtures.Behavior1.Id,
51 | ElementFixtures.Context.TypeName,
52 | ElementFixtures.Behavior1Specification2.FieldName,
53 | null);
54 | }
55 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Machine.Specifications.Runner.ReSharper.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472
5 | latest
6 | enable
7 | false
8 | MSB3277
9 | None
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | all
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Always
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Tasks/MspecBehaviorSpecificationRemoteTaskTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Tasks;
2 | using NUnit.Framework;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Tests.Tasks;
5 |
6 | [TestFixture]
7 | public class MspecBehaviorSpecificationRemoteTaskTests
8 | {
9 | [Test]
10 | public void ServerTaskHasCorrectId()
11 | {
12 | var task = MspecBehaviorSpecificationRemoteTask.ToServer("Namespace.Context.behaves_like", "Namespace.Context", "should", null);
13 |
14 | Assert.That(task.TestId, Is.EqualTo("Namespace.Context.behaves_like.should"));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Tasks/MspecContextRemoteTaskTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Tasks;
2 | using NUnit.Framework;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Tests.Tasks;
5 |
6 | [TestFixture]
7 | public class MspecContextRemoteTaskTests
8 | {
9 | [Test]
10 | public void ServerTaskHasCorrectId()
11 | {
12 | var task = MspecContextRemoteTask.ToServer("Namespace.Context", null, null, null);
13 |
14 | Assert.That(task.TestId, Is.EqualTo("Namespace.Context"));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper.Tests/Tasks/MspecSpecificationRemoteTaskTests.cs:
--------------------------------------------------------------------------------
1 | using Machine.Specifications.Runner.ReSharper.Tasks;
2 | using NUnit.Framework;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper.Tests.Tasks;
5 |
6 | [TestFixture]
7 | public class MspecSpecificationRemoteTaskTests
8 | {
9 | [Test]
10 | public void SpecificationServerTaskHasCorrectId()
11 | {
12 | var task = MspecSpecificationRemoteTask.ToServer("Namespace.Context", "should", null, null, null, null);
13 |
14 | Assert.That(task.TestId, Is.EqualTo("Namespace.Context.should"));
15 | }
16 |
17 | [Test]
18 | public void BehaviorServerTaskHasCorrectId()
19 | {
20 | var task = MspecSpecificationRemoteTask.ToServer("Namespace.Context", "behaves_like", "Namespace.ABehavior", null, null, null);
21 |
22 | Assert.That(task.TestId, Is.EqualTo("Namespace.Context.behaves_like"));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Elements/IMspecTestElement.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.UnitTestFramework.Elements;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Elements;
4 |
5 | public interface IMspecTestElement : IUnitTestElement
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Elements/MspecBehaviorSpecificationTestElement.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using JetBrains.Annotations;
3 | using JetBrains.Metadata.Reader.Impl;
4 | using JetBrains.ProjectModel;
5 | using JetBrains.ReSharper.Psi;
6 | using JetBrains.ReSharper.Psi.Util;
7 | using JetBrains.ReSharper.UnitTestFramework;
8 | using JetBrains.ReSharper.UnitTestFramework.Elements;
9 | using JetBrains.ReSharper.UnitTestFramework.Persistence;
10 | using Machine.Specifications.Runner.Utility;
11 |
12 | namespace Machine.Specifications.Runner.ReSharper.Elements;
13 |
14 | public class MspecBehaviorSpecificationTestElement : ClrUnitTestElement.Row, ITestCase, IMspecTestElement
15 | {
16 | [UsedImplicitly]
17 | public MspecBehaviorSpecificationTestElement()
18 | {
19 | }
20 |
21 | public MspecBehaviorSpecificationTestElement(MspecSpecificationTestElement parent, string fieldName, string? ignoreReason)
22 | : base($"{parent.Context.TypeName.FullName}.{parent.FieldName}.{fieldName}", parent)
23 | {
24 | FieldName = fieldName;
25 | DisplayName = fieldName.ToFormat();
26 | IgnoreReason = ignoreReason;
27 | }
28 |
29 | public override string Kind => "Behavior Specification";
30 |
31 | public bool IsNotRunnableStandalone => Origin == UnitTestElementOrigin.Dynamic;
32 |
33 | public MspecSpecificationTestElement Specification => (MspecSpecificationTestElement) Parent!;
34 |
35 | [Persist]
36 | [UsedImplicitly]
37 | public string FieldName { get; set; } = null!;
38 |
39 | [Persist]
40 | [UsedImplicitly]
41 | public string DisplayName { get; set; } = null!;
42 |
43 | public override string ShortName => DisplayName;
44 |
45 | [Persist]
46 | [UsedImplicitly]
47 | public string? IgnoreReason { get; set; }
48 |
49 | public override IDeclaredElement? GetDeclaredElement()
50 | {
51 | if (Specification.BehaviorType == null)
52 | {
53 | return null;
54 | }
55 |
56 | using (CompilationContextCookie.OverrideOrCreate(Project.GetResolveContext(TargetFrameworkId)))
57 | {
58 | var behaviorType = UT.Facade.TypeCache.GetTypeElement(
59 | Project,
60 | TargetFrameworkId,
61 | new ClrTypeName(Specification.BehaviorType),
62 | false,
63 | true);
64 |
65 | return behaviorType?
66 | .EnumerateMembers(FieldName, behaviorType.CaseSensitiveName)
67 | .FirstOrDefault();
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Elements/MspecContextTestElement.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Annotations;
2 | using JetBrains.Metadata.Reader.API;
3 | using JetBrains.ReSharper.UnitTestFramework.Elements;
4 | using JetBrains.ReSharper.UnitTestFramework.Persistence;
5 | using Machine.Specifications.Runner.Utility;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Elements;
8 |
9 | public class MspecContextTestElement : ClrUnitTestElement.FromClass, IMspecTestElement
10 | {
11 | [UsedImplicitly]
12 | public MspecContextTestElement()
13 | {
14 | }
15 |
16 | public MspecContextTestElement(IClrTypeName typeName, string? subject, string? ignoreReason)
17 | : base(typeName.FullName, typeName, GetDisplayName(typeName, subject))
18 | {
19 | Subject = subject;
20 | IgnoreReason = ignoreReason;
21 | }
22 |
23 | public override string Kind => "Context";
24 |
25 | [Persist]
26 | [UsedImplicitly]
27 | public string? Subject { get; set; }
28 |
29 | [Persist]
30 | [UsedImplicitly]
31 | public string? IgnoreReason { get; set; }
32 |
33 | private static string GetDisplayName(IClrTypeName typeName, string? subject)
34 | {
35 | var display = typeName.ShortName.ToFormat();
36 |
37 | return string.IsNullOrEmpty(subject)
38 | ? display
39 | : $"{subject}, {display}";
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Elements/MspecSpecificationTestElement.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using JetBrains.Annotations;
3 | using JetBrains.ReSharper.Psi;
4 | using JetBrains.ReSharper.Psi.Util;
5 | using JetBrains.ReSharper.UnitTestFramework.Elements;
6 | using JetBrains.ReSharper.UnitTestFramework.Persistence;
7 | using Machine.Specifications.Runner.Utility;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper.Elements;
10 |
11 | public class MspecSpecificationTestElement : ClrUnitTestElement.FromMethod, IMspecTestElement
12 | {
13 | [UsedImplicitly]
14 | public MspecSpecificationTestElement()
15 | {
16 | }
17 |
18 | public MspecSpecificationTestElement(MspecContextTestElement parent, string fieldName, string? behaviorType, string? declaredInTypeShortName, string? ignoreReason)
19 | : base($"{parent.TypeName.FullName}.{fieldName}", parent, fieldName, declaredInTypeShortName)
20 | {
21 | FieldName = fieldName;
22 | BehaviorType = behaviorType;
23 | DisplayName = fieldName.ToFormat();
24 | IgnoreReason = ignoreReason;
25 | }
26 |
27 | public MspecContextTestElement Context => Parent;
28 |
29 | public override string Kind => "Specification";
30 |
31 | [Persist]
32 | [UsedImplicitly]
33 | public string FieldName { get; set; } = null!;
34 |
35 | [Persist]
36 | [UsedImplicitly]
37 | public string? BehaviorType { get; set; }
38 |
39 | [Persist]
40 | [UsedImplicitly]
41 | public string DisplayName { get; set; } = null!;
42 |
43 | public override string ShortName => DisplayName;
44 |
45 | [Persist]
46 | [UsedImplicitly]
47 | public string? IgnoreReason { get; set; }
48 |
49 | protected override IDeclaredElement? GetTypeMember(ITypeElement declaredType)
50 | {
51 | return declaredType
52 | .EnumerateMembers(FieldName, declaredType.CaseSensitiveName)
53 | .FirstOrDefault();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/EnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper;
6 |
7 | public static class EnumerableExtensions
8 | {
9 | public static IEnumerable Flatten(this IEnumerable source, Func> selector)
10 | {
11 | var items = source.ToArray();
12 |
13 | return items
14 | .SelectMany(c => selector(c).Flatten(selector))
15 | .Concat(items);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Machine.Specifications.Runner.ReSharper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.1.0
5 | net472
6 | Machine.Specifications.Runner.Resharper9
7 | latest
8 | enable
9 | false
10 | NU5128,NU5100
11 | None
12 |
13 |
14 |
15 | Program
16 | $(DevEnvDir)devenv.exe
17 | /RootSuffix ReSharper /ReSharper.Internal
18 |
19 |
20 |
21 | Machine.Specifications for ReSharper
22 | A ReSharper runner for the Context/Specification framework Machine.Specifications
23 | Machine
24 | Machine
25 | test;unit;testing;context;specification;bdd;tdd;mspec;runner;resharper
26 | Adds support for R# and Rider 2025.1
27 | Machine.png
28 | http://github.com/machine/machine.specifications.runner.resharper/raw/master/images/icon.png
29 | https://github.com/machine/machine.specifications.runner.resharper
30 | http://github.com/machine/machine.specifications.runner.resharper
31 | MIT
32 |
33 |
34 |
35 | 2025.1.0
36 | $(SdkVersion.Substring(2,2))$(SdkVersion.Substring(5,1))
37 | $(WaveVersionBase).0.0
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | $(TargetsForTfmSpecificContentInPackage);PackageItems
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Mappings/MspecBehaviorSpecificationMapping.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Parts;
2 | using JetBrains.ProjectModel;
3 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
4 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
5 | using JetBrains.Util;
6 | using Machine.Specifications.Runner.ReSharper.Elements;
7 | using Machine.Specifications.Runner.ReSharper.Tasks;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper.Mappings;
10 |
11 | [SolutionComponent(Instantiation.DemandAnyThreadUnsafe)]
12 | public class MspecBehaviorSpecificationMapping(MspecServiceProvider services)
13 | : MspecElementMapping(services)
14 | {
15 | protected override MspecBehaviorSpecificationRemoteTask ToRemoteTask(MspecBehaviorSpecificationTestElement element, ITestRunnerExecutionContext context)
16 | {
17 | var task = MspecBehaviorSpecificationRemoteTask.ToClient(
18 | element.NaturalId.TestId,
19 | element.IgnoreReason,
20 | context.IsRunExplicitly(element));
21 |
22 | task.ContextTypeName = element.Specification.Context.TypeName.FullName;
23 | task.FieldName = element.FieldName;
24 | task.ParentId = $"{element.Specification.Context.TypeName.FullName}.{element.Specification.FieldName}";
25 |
26 | return task;
27 | }
28 |
29 | protected override MspecBehaviorSpecificationTestElement? ToElement(MspecBehaviorSpecificationRemoteTask task, ITestRunnerDiscoveryContext context, IUnitTestElementObserver observer)
30 | {
31 | if (task.ParentId == null)
32 | {
33 | context.Logger.Warn($"Cannot create element for MspecBehaviorSpecificationTestElement '{task.TestId}': ParentId is missing");
34 |
35 | return null;
36 | }
37 |
38 | var specificationElement = observer.GetElementById(task.ParentId);
39 |
40 | if (specificationElement == null)
41 | {
42 | context.Logger.Warn("Cannot create element for MspecBehaviorSpecificationTestElement '" + task.TestId + "': Specification is missing");
43 |
44 | return null;
45 | }
46 |
47 | var factory = GetFactory(context);
48 |
49 | return factory.GetOrCreateBehaviorSpecification(
50 | specificationElement,
51 | task.FieldName!,
52 | task.IgnoreReason);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Mappings/MspecContextMapping.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using JetBrains.Application.Parts;
4 | using JetBrains.Metadata.Reader.Impl;
5 | using JetBrains.ProjectModel;
6 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
7 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
8 | using Machine.Specifications.Runner.ReSharper.Elements;
9 | using Machine.Specifications.Runner.ReSharper.Tasks;
10 |
11 | namespace Machine.Specifications.Runner.ReSharper.Mappings;
12 |
13 | [SolutionComponent(Instantiation.DemandAnyThreadUnsafe)]
14 | public class MspecContextMapping(MspecServiceProvider services)
15 | : MspecElementMapping(services)
16 | {
17 | protected override MspecContextRemoteTask ToRemoteTask(MspecContextTestElement element, ITestRunnerExecutionContext context)
18 | {
19 | var task = MspecContextRemoteTask.ToClient(
20 | element.NaturalId.TestId,
21 | element.IgnoreReason,
22 | context.RunAllChildren(element),
23 | context.IsRunExplicitly(element));
24 |
25 | task.Subject = element.Subject;
26 | task.Tags = element.OwnCategories?.Select(x => x.Name).ToArray() ?? Array.Empty();
27 |
28 | return task;
29 | }
30 |
31 | protected override MspecContextTestElement ToElement(MspecContextRemoteTask task, ITestRunnerDiscoveryContext context, IUnitTestElementObserver observer)
32 | {
33 | var factory = GetFactory(context);
34 |
35 | return factory.GetOrCreateContext(
36 | new ClrTypeName(task.ContextTypeName),
37 | task.Subject,
38 | task.Tags,
39 | task.IgnoreReason);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Mappings/MspecElementMapping.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
2 | using JetBrains.ReSharper.UnitTestFramework.Elements;
3 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
4 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Mappings;
7 |
8 | public abstract class MspecElementMapping(MspecServiceProvider services)
9 | : IUnitTestElementToRemoteTaskMapping, IRemoteTaskToUnitTestElementMapping
10 | where TElement : IUnitTestElement
11 | where TTask : RemoteTask
12 | {
13 | protected MspecServiceProvider Services { get; } = services;
14 |
15 | protected abstract TTask ToRemoteTask(TElement element, ITestRunnerExecutionContext context);
16 |
17 | protected abstract TElement? ToElement(TTask task, ITestRunnerDiscoveryContext context, IUnitTestElementObserver observer);
18 |
19 | public RemoteTask GetRemoteTask(TElement element, ITestRunnerExecutionContext context)
20 | {
21 | return ToRemoteTask(element, context);
22 | }
23 |
24 | public IUnitTestElement? GetElement(TTask task, ITestRunnerDiscoveryContext context, IUnitTestElementObserver observer)
25 | {
26 | return ToElement(task, context, observer);
27 | }
28 |
29 | protected UnitTestElementFactory GetFactory(ITestRunnerDiscoveryContext context)
30 | {
31 | return context.GetOrCreateDataUnderLock(
32 | MspecElementMappingKeys.ElementFactoryKey, static () => new UnitTestElementFactory());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Mappings/MspecElementMappingKeys.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Util;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Mappings;
4 |
5 | public static class MspecElementMappingKeys
6 | {
7 | public static readonly Key ElementFactoryKey = new("MspecElementMapping.MspecElementFactory");
8 | }
9 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Mappings/MspecSpecificationMapping.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Parts;
2 | using JetBrains.ProjectModel;
3 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
4 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
5 | using JetBrains.Util;
6 | using Machine.Specifications.Runner.ReSharper.Elements;
7 | using Machine.Specifications.Runner.ReSharper.Tasks;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper.Mappings;
10 |
11 | [SolutionComponent(Instantiation.DemandAnyThreadUnsafe)]
12 | public class MspecSpecificationMapping(MspecServiceProvider services)
13 | : MspecElementMapping(services)
14 | {
15 | protected override MspecSpecificationRemoteTask ToRemoteTask(MspecSpecificationTestElement element, ITestRunnerExecutionContext context)
16 | {
17 | var task = MspecSpecificationRemoteTask.ToClient(
18 | element.NaturalId.TestId,
19 | element.IgnoreReason,
20 | context.RunAllChildren(element),
21 | context.IsRunExplicitly(element));
22 |
23 | task.ContextTypeName = element.Context.TypeName.FullName;
24 | task.FieldName = element.FieldName;
25 | task.BehaviorType = element.BehaviorType;
26 |
27 | return task;
28 | }
29 |
30 | protected override MspecSpecificationTestElement? ToElement(MspecSpecificationRemoteTask task, ITestRunnerDiscoveryContext context, IUnitTestElementObserver observer)
31 | {
32 | if (task.ContextTypeName == null)
33 | {
34 | context.Logger.Warn($"Cannot create element for MspecSpecificationTestElement '{task.TestId}': ContextTypeName is missing");
35 |
36 | return null;
37 | }
38 |
39 | var factory = GetFactory(context);
40 |
41 | var contextElement = observer.GetElementById(task.ContextTypeName);
42 |
43 | if (contextElement == null)
44 | {
45 | context.Logger.Warn($"Cannot create element for MspecSpecificationTestElement '{task.TestId}': Context is missing");
46 |
47 | return null;
48 | }
49 |
50 | return factory.GetOrCreateSpecification(
51 | contextElement,
52 | task.FieldName!,
53 | task.BehaviorType,
54 | task.IgnoreReason);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecProviderSettings.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application;
2 | using JetBrains.Application.Settings;
3 | using JetBrains.Application.Settings.Extentions;
4 | using JetBrains.Lifetimes;
5 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Artifacts;
6 | using JetBrains.ReSharper.UnitTestFramework.Settings;
7 |
8 | namespace Machine.Specifications.Runner.ReSharper;
9 |
10 | [SettingsKey(typeof(UnitTestingSettings), "Settings for MSpec unit test provider")]
11 | public class MspecProviderSettings
12 | {
13 | [SettingsEntry(DiscoveryMethod.Metadata, "Discovery method for discovery tests from artifacts")]
14 | public DiscoveryMethod TestDiscoveryFromArtifactsMethod;
15 |
16 | [ShellComponent]
17 | public class Reader(ISettingsStore settingsStore, ISettingsOptimization settingsOptimization)
18 | : ICachedSettingsReader
19 | {
20 | public ISettingsOptimization SettingsOptimization { get; } = settingsOptimization;
21 |
22 | public SettingsKey KeyExposed { get; } = settingsStore.Schema.GetKey();
23 |
24 | public MspecProviderSettings ReadData(Lifetime lifetime, IContextBoundSettingsStore store)
25 | {
26 | return (MspecProviderSettings)store.GetKey(KeyExposed, null, SettingsOptimization)!;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecPsiFileExplorer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using JetBrains.Metadata.Reader.Impl;
5 | using JetBrains.ReSharper.Psi;
6 | using JetBrains.ReSharper.Psi.Tree;
7 | using JetBrains.ReSharper.UnitTestFramework.Elements;
8 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
9 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Daemon;
10 | using Machine.Specifications.Runner.ReSharper.Elements;
11 | using Machine.Specifications.Runner.ReSharper.Reflection;
12 |
13 | namespace Machine.Specifications.Runner.ReSharper;
14 |
15 | public class MspecPsiFileExplorer(IUnitTestElementObserverOnFile observer, Func interrupted)
16 | : UnitTestElementRecursivePsiProcessor(interrupted)
17 | {
18 | private readonly UnitTestElementFactory factory = new();
19 |
20 | private readonly Dictionary recentContexts = new();
21 |
22 | public override bool InteriorShouldBeProcessed(ITreeNode element)
23 | {
24 | if (element is ITypeMemberDeclaration)
25 | {
26 | return element is ITypeDeclaration;
27 | }
28 |
29 | return true;
30 | }
31 |
32 | public override void ProcessBeforeInterior(ITreeNode element)
33 | {
34 | if (element is not IDeclaration declaration)
35 | {
36 | return;
37 | }
38 |
39 | var declaredElement = declaration.DeclaredElement;
40 |
41 | if (declaredElement is IClass type)
42 | {
43 | ProcessType(type.AsTypeInfo(), declaration);
44 | }
45 | else if (declaredElement is IField field)
46 | {
47 | ProcessField(field.AsFieldInfo(), declaration);
48 | }
49 | }
50 |
51 | public override void ProcessAfterInterior(ITreeNode element)
52 | {
53 | }
54 |
55 | private void ProcessType(ITypeInfo type, IDeclaration declaration)
56 | {
57 | if (type.IsContext())
58 | {
59 | ProcessContext(type, declaration, true);
60 | }
61 | }
62 |
63 | private void ProcessContext(ITypeInfo type, IDeclaration declaration, bool isClear)
64 | {
65 | var name = new ClrTypeName(type.FullyQualifiedName);
66 |
67 | var context = factory.GetOrCreateContext(
68 | name,
69 | type.GetSubject(),
70 | type.GetTags().ToArray(),
71 | type.GetIgnoreReason());
72 |
73 | recentContexts[name] = context;
74 |
75 | if (isClear)
76 | {
77 | OnUnitTestElement(context, declaration);
78 | }
79 | }
80 |
81 | private void ProcessField(IFieldInfo field, IDeclaration? declaration = null)
82 | {
83 | if (field.IsSpecification())
84 | {
85 | ProcessSpecificationField(field, declaration);
86 | }
87 | else if (field.IsBehavior())
88 | {
89 | ProcessBehaviorField(field, declaration);
90 | }
91 | }
92 |
93 | private void ProcessSpecificationField(IFieldInfo field, IDeclaration? declaration = null)
94 | {
95 | var containingType = new ClrTypeName(field.DeclaringType);
96 |
97 | if (recentContexts.TryGetValue(containingType, out var context))
98 | {
99 | var specification = factory.GetOrCreateSpecification(
100 | context,
101 | field.ShortName,
102 | null,
103 | field.GetIgnoreReason() ?? context.IgnoreReason);
104 |
105 | OnUnitTestElement(specification, declaration);
106 | }
107 | }
108 |
109 | private void ProcessBehaviorField(IFieldInfo field, IDeclaration? declaration = null)
110 | {
111 | var behaviorType = field.FieldType.GetGenericArguments().FirstOrDefault();
112 | var containingType = new ClrTypeName(field.DeclaringType);
113 |
114 | if (recentContexts.TryGetValue(containingType, out var context) && behaviorType != null && behaviorType.IsBehaviorContainer())
115 | {
116 | var specification = factory.GetOrCreateSpecification(
117 | context,
118 | field.ShortName,
119 | behaviorType.FullyQualifiedName,
120 | field.GetIgnoreReason());
121 |
122 | OnUnitTestElement(specification, declaration);
123 | }
124 | }
125 |
126 | private void OnUnitTestElement(IUnitTestElement element, IDeclaration? declaration = null)
127 | {
128 | if (declaration == null)
129 | {
130 | return;
131 | }
132 |
133 | var navigationRange = declaration.GetNameDocumentRange().TextRange;
134 | var containingRange = declaration.GetDocumentRange().TextRange;
135 |
136 | observer.OnUnitTestElement(element);
137 | observer.OnUnitTestElementDisposition(element, navigationRange, containingRange);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecServiceProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.Application.Parts;
3 | using JetBrains.ProjectModel;
4 | using JetBrains.ReSharper.UnitTestFramework.Execution;
5 | using JetBrains.Util;
6 | using Machine.Specifications.Runner.ReSharper.Runner;
7 |
8 | namespace Machine.Specifications.Runner.ReSharper;
9 |
10 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
11 | public class MspecServiceProvider(ISolution solution)
12 | {
13 | private readonly Lazy runner = Lazy.Of(solution.GetComponent, true);
14 |
15 | public IUnitTestRunStrategy GetRunStrategy()
16 | {
17 | return runner.Value;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestExplorerFromArtifacts.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Parts;
2 | using JetBrains.Application.Settings;
3 | using JetBrains.ProjectModel;
4 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Artifacts;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper;
7 |
8 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
9 | public class MspecTestExplorerFromArtifacts(
10 | ISettingsStore settingsStore,
11 | MspecTestExplorerFromMetadata metadataExplorer,
12 | MspecTestExplorerFromTestRunner testRunnerExplorer)
13 | : UnitTestExplorerFrom.Switching(settingsStore, x => x.TestDiscoveryFromArtifactsMethod, metadataExplorer, testRunnerExplorer);
14 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestExplorerFromFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using JetBrains.Application.Parts;
3 | using JetBrains.ProjectModel;
4 | using JetBrains.ReSharper.Psi;
5 | using JetBrains.ReSharper.Psi.Tree;
6 | using JetBrains.ReSharper.UnitTestFramework;
7 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
8 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Daemon;
9 |
10 | namespace Machine.Specifications.Runner.ReSharper;
11 |
12 | [SolutionComponent(Instantiation.DemandAnyThreadUnsafe)]
13 | public class MspecTestExplorerFromFile(MspecTestProvider provider) : IUnitTestExplorerFromFile
14 | {
15 | public IUnitTestProvider Provider { get; } = provider;
16 |
17 | public void ProcessFile(IFile psiFile, IUnitTestElementObserverOnFile observer, Func interrupted)
18 | {
19 | if (!IsProjectFile(psiFile))
20 | {
21 | return;
22 | }
23 |
24 | var explorer = new MspecPsiFileExplorer(observer, interrupted);
25 |
26 | psiFile.ProcessDescendants(explorer);
27 | }
28 |
29 | private static bool IsProjectFile(IFile psiFile)
30 | {
31 | return psiFile.GetSourceFile().ToProjectFile() != null;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestExplorerFromMetadata.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using JetBrains.Application.Parts;
4 | using JetBrains.Metadata.Reader.API;
5 | using JetBrains.ProjectModel;
6 | using JetBrains.ProjectModel.Assemblies.AssemblyToAssemblyResolvers;
7 | using JetBrains.ProjectModel.Assemblies.Impl;
8 | using JetBrains.ProjectModel.NuGet.Packaging;
9 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
10 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Artifacts;
11 | using JetBrains.Util;
12 | using JetBrains.Util.Dotnet.TargetFrameworkIds;
13 |
14 | namespace Machine.Specifications.Runner.ReSharper;
15 |
16 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
17 | public class MspecTestExplorerFromMetadata(
18 | MspecTestProvider provider,
19 | AssemblyToAssemblyReferencesResolveManager resolveManager,
20 | ResolveContextManager resolveContextManager,
21 | NuGetInstalledPackageChecker installedPackageChecker,
22 | ILogger logger)
23 | : UnitTestExplorerFrom.Metadata(provider, resolveManager, resolveContextManager, installedPackageChecker, logger)
24 | {
25 | private readonly ILogger logger = logger;
26 |
27 | protected override IEnumerable GetRequiredNuGetDependencies(IProject project, TargetFrameworkId targetFrameworkId)
28 | {
29 | foreach (var dependency in base.GetRequiredNuGetDependencies(project, targetFrameworkId))
30 | {
31 | yield return dependency;
32 | }
33 |
34 | yield return "Machine.Specifications.Runner.VisualStudio";
35 | }
36 |
37 | protected override void ProcessProject(
38 | MetadataLoader loader,
39 | IUnitTestElementObserver observer,
40 | CancellationToken token)
41 | {
42 | MetadataElementsSource.ExploreProject(observer.Source.Project, observer.Source.Output, loader, logger, token, assembly =>
43 | {
44 | var explorer = new MspecTestMetadataExplorer(observer);
45 |
46 | explorer.ExploreAssembly(assembly, token);
47 | });
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestExplorerFromTestRunner.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Parts;
2 | using JetBrains.ProjectModel;
3 | using JetBrains.ProjectModel.NuGet.Packaging;
4 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
5 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Artifacts;
6 | using JetBrains.Util.Collections;
7 |
8 | namespace Machine.Specifications.Runner.ReSharper;
9 |
10 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
11 | public class MspecTestExplorerFromTestRunner(
12 | MspecTestProvider provider,
13 | ITestRunnerAgentManager agentManager,
14 | MspecTestRunnerOrchestrator adapter,
15 | NuGetInstalledPackageChecker installedPackageChecker)
16 | : UnitTestExplorerFrom.TestRunner(provider, agentManager, adapter, installedPackageChecker, 1.Minute());
17 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestMetadataExplorer.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using JetBrains.Metadata.Reader.API;
4 | using JetBrains.Metadata.Reader.Impl;
5 | using JetBrains.ReSharper.UnitTestFramework.Exploration;
6 | using Machine.Specifications.Runner.ReSharper.Elements;
7 | using Machine.Specifications.Runner.ReSharper.Reflection;
8 |
9 | namespace Machine.Specifications.Runner.ReSharper;
10 |
11 | public class MspecTestMetadataExplorer(IUnitTestElementObserver observer)
12 | {
13 | private readonly UnitTestElementFactory factory = new();
14 |
15 | public void ExploreAssembly(IMetadataAssembly assembly, CancellationToken token)
16 | {
17 | var types = assembly.GetTypes()
18 | .Flatten(x => x.GetNestedTypes());
19 |
20 | foreach (var type in types)
21 | {
22 | if (token.IsCancellationRequested)
23 | {
24 | break;
25 | }
26 |
27 | ExploreType(type.AsTypeInfo());
28 | }
29 | }
30 |
31 | private void ExploreType(ITypeInfo type)
32 | {
33 | if (!type.IsContext())
34 | {
35 | return;
36 | }
37 |
38 | ExploreContext(type);
39 | }
40 |
41 | private void ExploreContext(ITypeInfo type)
42 | {
43 | var contextElement = factory.GetOrCreateContext(
44 | new ClrTypeName(type.FullyQualifiedName),
45 | type.GetSubject(),
46 | type.GetTags().ToArray(),
47 | type.GetIgnoreReason());
48 |
49 | observer.OnUnitTestElement(contextElement);
50 |
51 | var fields = type.GetFields().ToArray();
52 |
53 | var specifications = fields.Where(x => x.IsSpecification());
54 | var behaviors = fields.Where(x => x.IsBehavior());
55 |
56 | foreach (var specification in specifications)
57 | {
58 | ExploreSpecification(contextElement, specification);
59 | }
60 |
61 | foreach (var behavior in behaviors)
62 | {
63 | ExploreBehavior(contextElement, behavior);
64 | }
65 |
66 | observer.OnUnitTestElement(contextElement);
67 | }
68 |
69 | private void ExploreSpecification(MspecContextTestElement contextElement, IFieldInfo field)
70 | {
71 | var specificationElement = factory.GetOrCreateSpecification(
72 | contextElement,
73 | field.ShortName,
74 | null,
75 | field.GetIgnoreReason() ?? contextElement.IgnoreReason);
76 |
77 | observer.OnUnitTestElement(specificationElement);
78 | }
79 |
80 | private void ExploreBehavior(MspecContextTestElement contextElement, IFieldInfo field)
81 | {
82 | var behaviorType = field.FieldType.GetGenericArguments()
83 | .FirstOrDefault();
84 |
85 | var specificationElement = factory.GetOrCreateSpecification(
86 | contextElement,
87 | field.ShortName,
88 | behaviorType?.FullyQualifiedName,
89 | field.GetIgnoreReason());
90 |
91 | observer.OnUnitTestElement(specificationElement);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestProvider.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Metadata.Utils;
2 | using JetBrains.ProjectModel;
3 | using JetBrains.ReSharper.Psi;
4 | using JetBrains.ReSharper.Resources.Shell;
5 | using JetBrains.ReSharper.UnitTestFramework;
6 | using JetBrains.ReSharper.UnitTestFramework.Elements;
7 | using JetBrains.ReSharper.UnitTestFramework.Execution;
8 | using JetBrains.ReSharper.UnitTestFramework.Execution.Hosting;
9 | using JetBrains.Util.Dotnet.TargetFrameworkIds;
10 | using JetBrains.Util.Reflection;
11 | using Machine.Specifications.Runner.ReSharper.Elements;
12 | using Machine.Specifications.Runner.ReSharper.Reflection;
13 |
14 | namespace Machine.Specifications.Runner.ReSharper;
15 |
16 | [UnitTestProvider]
17 | public class MspecTestProvider : IDotNetArtifactBasedUnitTestProvider
18 | {
19 | public const string Id = "Machine.Specifications";
20 |
21 | private static readonly AssemblyNameInfo MspecReferenceName = AssemblyNameInfoFactory.Create2(Id, null);
22 |
23 | public string ID => Id;
24 |
25 | public string Name => Id;
26 |
27 | public IUnitTestRunStrategy GetRunStrategy(IUnitTestElement element, IHostProvider hostProvider)
28 | {
29 | return UT.Facade.Get().GetRunStrategy();
30 | }
31 |
32 | public bool IsElementOfKind(IUnitTestElement element, UnitTestElementKind elementKind)
33 | {
34 | switch (elementKind)
35 | {
36 | case UnitTestElementKind.Test:
37 | return element is MspecSpecificationTestElement or MspecBehaviorSpecificationTestElement;
38 |
39 | case UnitTestElementKind.TestContainer:
40 | return element is MspecContextTestElement;
41 |
42 | case UnitTestElementKind.TestStuff:
43 | return element is MspecContextTestElement or MspecSpecificationTestElement or MspecBehaviorSpecificationTestElement;
44 |
45 | case UnitTestElementKind.Unknown:
46 | return element is not MspecContextTestElement &&
47 | element is not MspecSpecificationTestElement &&
48 | element is not MspecBehaviorSpecificationTestElement;
49 | }
50 |
51 | return false;
52 | }
53 |
54 | public bool IsElementOfKind(IDeclaredElement element, UnitTestElementKind elementKind)
55 | {
56 | switch (elementKind)
57 | {
58 | case UnitTestElementKind.Test:
59 | return element.IsSpecification();
60 |
61 | case UnitTestElementKind.TestContainer:
62 | return element.IsContext();
63 |
64 | case UnitTestElementKind.TestStuff:
65 | return element.IsSpecification() || element.IsContext() || element.IsBehavior();
66 |
67 | case UnitTestElementKind.Unknown:
68 | return !(element.IsSpecification() || element.IsContext() || element.IsBehavior());
69 | }
70 |
71 | return false;
72 | }
73 |
74 | public bool IsSupported(IHostProvider hostProvider, IProject project, TargetFrameworkId targetFrameworkId)
75 | {
76 | return IsSupported(project, targetFrameworkId);
77 | }
78 |
79 | public bool IsSupported(IProject project, TargetFrameworkId targetFrameworkId)
80 | {
81 | using (ReadLockCookie.Create())
82 | {
83 | return ReferencedAssembliesService.IsProjectReferencingAssemblyByName(project, targetFrameworkId, MspecReferenceName, out _);
84 | }
85 | }
86 |
87 | public bool SupportsResultEventsForParentOf(IUnitTestElement element)
88 | {
89 | return element is not MspecContextTestElement || element.Parent is not MspecContextTestElement;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/MspecTestRunnerOrchestrator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Reflection;
3 | using JetBrains.Application.Parts;
4 | using JetBrains.ProjectModel;
5 | using JetBrains.ReSharper.TestRunner.Abstractions;
6 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
7 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
8 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner.Extensions;
9 | using JetBrains.Util;
10 | using Machine.Specifications.Runner.ReSharper.Tasks;
11 |
12 | namespace Machine.Specifications.Runner.ReSharper;
13 |
14 | [SolutionComponent(Instantiation.DemandAnyThreadSafe)]
15 | public class MspecTestRunnerOrchestrator : ITestRunnerAdapter
16 | {
17 | private const string Namespace = "Machine.Specifications.Runner.ReSharper";
18 |
19 | private static readonly FileSystemPath Root = Assembly.GetExecutingAssembly().GetPath().Directory;
20 |
21 | public Assembly InProcessAdapterAssembly => typeof (MspecTestContainer).Assembly;
22 |
23 | public int Priority => 10;
24 |
25 | public TestAdapterLoader GetTestAdapterLoader(ITestRunnerContext context)
26 | {
27 | var framework = context.RuntimeDescriptor.TargetFrameworkId.IsNetCoreSdk()
28 | ? "netstandard20"
29 | : "net461";
30 |
31 | var adapters = Root.Combine($"{Namespace}.Adapters.{framework}.dll");
32 | var tasks = Root.Combine($"{Namespace}.Tasks.{framework}.dll");
33 |
34 | var type = TypeInfoFactory.Create($"{Namespace}.Adapters.MspecRunner", adapters.FullPath);
35 |
36 | return new TestAdapterInfo(type, type)
37 | {
38 | AdditionalAssemblies = new[]
39 | {
40 | tasks.FullPath
41 | }
42 | };
43 | }
44 |
45 | public TestContainer GetTestContainer(ITestRunnerContext context)
46 | {
47 | return new MspecTestContainer(context.GetOutputPath().FullPath, context.Settings.TestRunner.ToShadowCopy());
48 | }
49 |
50 | public IEnumerable GetMessageHandlers(ITestRunnerContext context)
51 | {
52 | yield break;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Options/MspecPage.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Environment;
2 | using JetBrains.Application.Environment.Helpers;
3 | using JetBrains.Application.UI.Options;
4 | using JetBrains.Application.UI.Options.OptionsDialog;
5 | using JetBrains.Application.UI.Options.OptionsDialog.SimpleOptions.ViewModel;
6 | using JetBrains.IDE.UI.Options;
7 | using JetBrains.Lifetimes;
8 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Artifacts;
9 | using JetBrains.ReSharper.UnitTestFramework.UI.Options;
10 | using Machine.Specifications.Runner.ReSharper.Resources;
11 |
12 | namespace Machine.Specifications.Runner.ReSharper.Options;
13 |
14 | [OptionsPage("MSpecPage", "MSpec", typeof(MspecThemedIcons.Mspec), ParentId = UnitTestingPages.Frameworks)]
15 | public class MspecPage : BeSimpleOptionsPage
16 | {
17 | public MspecPage(
18 | Lifetime lifetime,
19 | OptionsPageContext optionsPageContext,
20 | OptionsSettingsSmartContext optionsSettingsSmartContext,
21 | MspecTestProvider provider,
22 | RunsProducts.ProductConfigurations productConfigurations)
23 | : base(lifetime, optionsPageContext, optionsSettingsSmartContext)
24 | {
25 | var supportEnabledOption = UnitTestProviderOptionsPage.GetTestSupportEnabledOption(lifetime, optionsSettingsSmartContext, provider.ID);
26 |
27 | if (productConfigurations.IsInternalMode())
28 | {
29 | AddBoolOption(supportEnabledOption, "_Enable " + provider.Name + " support", string.Empty);
30 | }
31 |
32 | AddHeader("Test discovery");
33 |
34 | AddRadioOption(
35 | x => x.TestDiscoveryFromArtifactsMethod,
36 | "When running tests discovery from artifacts, use:",
37 | new RadioOptionPoint(
38 | DiscoveryMethod.Metadata,
39 | "Metadata",
40 | "Fast, but might not be able to find certain entities, such as custom categories or dynamic tests. They will appear after the first execution."),
41 | new RadioOptionPoint(
42 | DiscoveryMethod.TestRunner,
43 | "Test Runner",
44 | "Slower, finds all tests or categories"));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/IAttributeInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
4 |
5 | public interface IAttributeInfo
6 | {
7 | IEnumerable GetParameters();
8 | }
9 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/IFieldInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
4 |
5 | public interface IFieldInfo
6 | {
7 | string DeclaringType { get; }
8 |
9 | string ShortName { get; }
10 |
11 | ITypeInfo FieldType { get; }
12 |
13 | IEnumerable GetCustomAttributes(string typeName, bool inherit);
14 | }
15 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/ITypeInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
4 |
5 | public interface ITypeInfo
6 | {
7 | string FullyQualifiedName { get; }
8 |
9 | bool IsAbstract { get; }
10 |
11 | ITypeInfo? GetContainingType();
12 |
13 | IEnumerable GetFields();
14 |
15 | IEnumerable GetCustomAttributes(string typeName, bool inherit);
16 |
17 | IEnumerable GetGenericArguments();
18 | }
19 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/MetadataAttributeInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.API;
4 | using JetBrains.Metadata.Reader.Impl;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
7 |
8 | public class MetadataAttributeInfoAdapter(IMetadataCustomAttribute attribute) : IAttributeInfo
9 | {
10 | public IEnumerable GetParameters()
11 | {
12 | var arguments = attribute.ConstructorArguments
13 | .Where(x => !x.IsBadValue())
14 | .ToArray();
15 |
16 | var types = arguments
17 | .Select(x => x.Value)
18 | .OfType()
19 | .Select(x => new ClrTypeName(x.Type.FullyQualifiedName))
20 | .Select(x => x.ShortName);
21 |
22 | var values = arguments
23 | .Select(x => x.Value)
24 | .OfType();
25 |
26 | var arrayValues = arguments
27 | .Where(x => x.ValuesArray != null)
28 | .SelectMany(x => x.ValuesArray)
29 | .Select(x => x.Value)
30 | .OfType();
31 |
32 | return types
33 | .Concat(values)
34 | .Concat(arrayValues);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/MetadataExtensions.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Metadata.Reader.API;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
4 |
5 | public static class MetadataExtensions
6 | {
7 | public static ITypeInfo AsTypeInfo(this IMetadataTypeInfo type, IMetadataClassType? classType = null)
8 | {
9 | return new MetadataTypeInfoAdapter(type, classType);
10 | }
11 |
12 | public static IAttributeInfo AsAttributeInfo(this IMetadataCustomAttribute attribute)
13 | {
14 | return new MetadataAttributeInfoAdapter(attribute);
15 | }
16 |
17 | public static IFieldInfo AsFieldInfo(this IMetadataField field)
18 | {
19 | return new MetadataFieldInfoAdapter(field);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/MetadataFieldInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.API;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
6 |
7 | public class MetadataFieldInfoAdapter(IMetadataField field) : IFieldInfo
8 | {
9 | public string DeclaringType => field.DeclaringType.FullyQualifiedName;
10 |
11 | public string ShortName => field.Name;
12 |
13 | public ITypeInfo FieldType
14 | {
15 | get
16 | {
17 | if (field.Type is IMetadataClassType classType)
18 | {
19 | return classType.Type.AsTypeInfo(classType);
20 | }
21 |
22 | return UnknownTypeInfoAdapter.Default;
23 | }
24 | }
25 |
26 | public IEnumerable GetCustomAttributes(string typeName, bool inherit)
27 | {
28 | return field.GetCustomAttributes(typeName)
29 | .Select(x => x.AsAttributeInfo());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/MetadataTypeInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.API;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
6 |
7 | public class MetadataTypeInfoAdapter(IMetadataTypeInfo type, IMetadataClassType? classType = null)
8 | : ITypeInfo
9 | {
10 | public string FullyQualifiedName => type.FullyQualifiedName;
11 |
12 | public bool IsAbstract => type.IsAbstract;
13 |
14 | public ITypeInfo? GetContainingType()
15 | {
16 | return type.DeclaringType?.AsTypeInfo();
17 | }
18 |
19 | public IEnumerable GetFields()
20 | {
21 | return type.GetFields()
22 | .Where(x => !x.IsStatic)
23 | .Select(x => x.AsFieldInfo());
24 | }
25 |
26 | public IEnumerable GetCustomAttributes(string typeName, bool inherit)
27 | {
28 | var attributes = type.GetCustomAttributes(typeName)
29 | .Select(x => x.AsAttributeInfo());
30 |
31 | if (!inherit)
32 | {
33 | return attributes;
34 | }
35 |
36 | var baseAttributes = GetBaseTypes()
37 | .SelectMany(x => x.GetCustomAttributes(typeName, false));
38 |
39 | return attributes.Concat(baseAttributes);
40 | }
41 |
42 | public IEnumerable GetGenericArguments()
43 | {
44 | if (classType == null)
45 | {
46 | return type.TypeParameters.Select(_ => UnknownTypeInfoAdapter.Default);
47 | }
48 |
49 | return classType.Arguments
50 | .OfType()
51 | .Select(x => x.Type.AsTypeInfo());
52 | }
53 |
54 | private IEnumerable GetBaseTypes()
55 | {
56 | var current = type;
57 |
58 | while (current.Base != null)
59 | {
60 | yield return current.Base.Type.AsTypeInfo();
61 |
62 | current = current.Base.Type;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/PsiAttributeInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.ReSharper.Psi;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
6 |
7 | public class PsiAttributeInfoAdapter(IAttributeInstance attribute) : IAttributeInfo
8 | {
9 | public IEnumerable GetParameters()
10 | {
11 | var parameters = attribute.PositionParameters()
12 | .Where(x => !x.IsBadValue)
13 | .ToArray();
14 |
15 | var typeItems = parameters
16 | .Where(x => x.IsType)
17 | .Select(x => x.TypeValue)
18 | .OfType()
19 | .Where(x => x.IsValid())
20 | .Select(x => x.GetClrName().ShortName);
21 |
22 | var arrayItems = parameters
23 | .Where(x => x.IsArray)
24 | .SelectMany(x => x.ArrayValue);
25 |
26 | var stringItems = parameters
27 | .Where(x => x.IsConstant)
28 | .Concat(arrayItems)
29 | .Select(x => x.ConstantValue.AsString())
30 | .Where(x => x != null)
31 | .Select(x => x!);
32 |
33 | return typeItems.Concat(stringItems);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/PsiExtensions.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.Psi;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
4 |
5 | public static class PsiExtensions
6 | {
7 | public static ITypeInfo AsTypeInfo(this ITypeElement type, IDeclaredType? declaredType = null)
8 | {
9 | return new PsiTypeInfoAdapter(type, declaredType);
10 | }
11 |
12 | public static IAttributeInfo AsAttributeInfo(this IAttributeInstance attribute)
13 | {
14 | return new PsiAttributeInfoAdapter(attribute);
15 | }
16 |
17 | public static IFieldInfo AsFieldInfo(this IField field)
18 | {
19 | return new PsiFieldInfoAdapter(field);
20 | }
21 |
22 | public static bool IsContext(this IDeclaredElement element)
23 | {
24 | return element is IClass type && type.AsTypeInfo().IsContext();
25 | }
26 |
27 | public static bool IsSpecification(this IDeclaredElement element)
28 | {
29 | return element is IField field && field.AsFieldInfo().IsSpecification();
30 | }
31 |
32 | public static bool IsBehavior(this IDeclaredElement element)
33 | {
34 | return element is IField field && field.AsFieldInfo().IsBehavior();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/PsiFieldInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.Impl;
4 | using JetBrains.ReSharper.Psi;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
7 |
8 | public class PsiFieldInfoAdapter(IField field) : IFieldInfo
9 | {
10 | public string DeclaringType => field.ContainingType!.GetClrName().FullName;
11 |
12 | public string ShortName => field.ShortName;
13 |
14 | public ITypeInfo FieldType
15 | {
16 | get
17 | {
18 | if (field.Type is IDeclaredType {IsResolved: true} type)
19 | {
20 | return type.GetTypeElement()!.AsTypeInfo(type);
21 | }
22 |
23 | return UnknownTypeInfoAdapter.Default;
24 | }
25 | }
26 |
27 | public IEnumerable GetCustomAttributes(string typeName, bool inherit)
28 | {
29 | return field.GetAttributeInstances(new ClrTypeName(typeName), inherit)
30 | .Select(x => x.AsAttributeInfo());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/PsiTypeInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.Impl;
4 | using JetBrains.ReSharper.Psi;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
7 |
8 | public class PsiTypeInfoAdapter(ITypeElement type, IDeclaredType? declaredType = null) : ITypeInfo
9 | {
10 | public string FullyQualifiedName => type.GetClrName().FullName;
11 |
12 | public bool IsAbstract => type is IModifiersOwner {IsAbstract: true};
13 |
14 | public ITypeInfo? GetContainingType()
15 | {
16 | return type.GetContainingType()?.AsTypeInfo();
17 | }
18 |
19 | public IEnumerable GetFields()
20 | {
21 | if (type is not IClass classType)
22 | {
23 | return Enumerable.Empty();
24 | }
25 |
26 | return classType.Fields
27 | .Where(x => !x.IsStatic)
28 | .Select(x => x.AsFieldInfo());
29 | }
30 |
31 | public IEnumerable GetCustomAttributes(string typeName, bool inherit)
32 | {
33 | return type.GetAttributeInstances(new ClrTypeName(typeName), inherit)
34 | .Select(x => x.AsAttributeInfo());
35 | }
36 |
37 | public IEnumerable GetGenericArguments()
38 | {
39 | if (declaredType != null)
40 | {
41 | var substitution = declaredType.GetSubstitution();
42 |
43 | return substitution.Domain
44 | .Select(x => substitution.Apply(x).GetScalarType())
45 | .Where(x => x != null)
46 | .Select(x => x?.GetTypeElement())
47 | .OfType()
48 | .Select(x => x.AsTypeInfo());
49 | }
50 |
51 | if (type is ITypeParametersOwner owner)
52 | {
53 | return owner.TypeParameters.Select(x => x.AsTypeInfo());
54 | }
55 |
56 | return Enumerable.Empty();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/ReflectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Util;
4 | using Machine.Specifications.Runner.Utility;
5 |
6 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
7 |
8 | public static class ReflectionExtensions
9 | {
10 | public static IEnumerable GetTags(this ITypeInfo type)
11 | {
12 | return type.GetCustomAttributes(FullNames.TagsAttribute, false)
13 | .SelectMany(x => x.GetParameters())
14 | .Distinct();
15 | }
16 |
17 | public static string GetSubject(this ITypeInfo type)
18 | {
19 | var attributes = type.GetCustomAttributes(FullNames.SubjectAttribute, true)
20 | .ToArray();
21 |
22 | if (!attributes.Any())
23 | {
24 | return type.GetContainingType()?.GetSubject() ?? string.Empty;
25 | }
26 |
27 | return attributes.First()
28 | .GetParameters()
29 | .Join(" ");
30 | }
31 |
32 | public static bool IsSpecification(this IFieldInfo field)
33 | {
34 | return field.FieldType.GetCustomAttributes(FullNames.AssertDelegateAttribute, false).Any();
35 | }
36 |
37 | public static bool IsBehavior(this IFieldInfo field)
38 | {
39 | var type = field.FieldType;
40 | var behaviorType = type.GetGenericArguments().FirstOrDefault();
41 |
42 | return type.GetCustomAttributes(FullNames.BehaviorDelegateAttribute, false).Any() &&
43 | behaviorType?.GetFields().Any(x => x.IsSpecification()) == true;
44 | }
45 |
46 | public static string? GetIgnoreReason(this ITypeInfo type)
47 | {
48 | return type.GetCustomAttributes(FullNames.IgnoreAttribute, false)
49 | .SelectMany(x => x.GetParameters())
50 | .FirstOrDefault();
51 | }
52 |
53 | public static string? GetIgnoreReason(this IFieldInfo field)
54 | {
55 | return field.GetCustomAttributes(FullNames.IgnoreAttribute, false)
56 | .SelectMany(x => x.GetParameters())
57 | .FirstOrDefault();
58 | }
59 |
60 | public static bool IsBehaviorContainer(this ITypeInfo type)
61 | {
62 | return !type.IsAbstract &&
63 | type.GetCustomAttributes(FullNames.BehaviorsAttribute, false).Any() &&
64 | type.GetFields().Any(x => x.IsSpecification());
65 | }
66 |
67 | public static bool IsContext(this ITypeInfo type)
68 | {
69 | return !type.IsAbstract &&
70 | !type.GetCustomAttributes(FullNames.BehaviorsAttribute, false).Any() &&
71 | !type.GetGenericArguments().Any() &&
72 | type.GetFields().Any(x => x.IsSpecification() || x.IsBehavior());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Reflection/UnknownTypeInfoAdapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Reflection;
4 |
5 | public class UnknownTypeInfoAdapter : ITypeInfo
6 | {
7 | public static readonly UnknownTypeInfoAdapter Default = new();
8 |
9 | public string FullyQualifiedName => string.Empty;
10 |
11 | public bool IsAbstract => false;
12 |
13 | public ITypeInfo? GetContainingType()
14 | {
15 | return null;
16 | }
17 |
18 | public IEnumerable GetFields()
19 | {
20 | return [];
21 | }
22 |
23 | public IEnumerable GetCustomAttributes(string typeName, bool inherit)
24 | {
25 | return [];
26 | }
27 |
28 | public IEnumerable GetGenericArguments()
29 | {
30 | return [];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Resources/Machine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/machine/machine.specifications.runner.resharper/f7e86ca30bcc244ab85eaebcf4f94741fc253a2d/src/Machine.Specifications.Runner.ReSharper/Resources/Machine.png
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Resources/MspecThemedIcons.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Icons.CompiledIconsCs;
2 | using JetBrains.UI.Icons;
3 | using JetBrains.Util.Icons;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Resources;
6 |
7 | public static class MspecThemedIcons
8 | {
9 | [CompiledIconCs]
10 | public sealed class Mspec : CompiledIconCsClass
11 | {
12 | public static IconId Id = new CompiledIconCsId(typeof(Mspec));
13 |
14 | public TiImage Load()
15 | {
16 | return TiImageConverter.FromTiSvg(
17 | "");
66 | }
67 |
68 | public override CompiledIconCsIdOwner.ThemedIconThemeImage[] GetThemeImages()
69 | {
70 | return
71 | [
72 | new("Default", Load)
73 | ];
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Rules/EnsureAncestorsAddedToExecutedElementsRule.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.ReSharper.UnitTestFramework.Criteria;
4 | using JetBrains.ReSharper.UnitTestFramework.Elements;
5 | using JetBrains.ReSharper.UnitTestFramework.Execution.Hosting;
6 | using JetBrains.ReSharper.UnitTestFramework.Execution.Launch.Rules;
7 | using JetBrains.ReSharper.UnitTestFramework.Session;
8 | using JetBrains.Util;
9 | using Machine.Specifications.Runner.ReSharper.Elements;
10 |
11 | namespace Machine.Specifications.Runner.ReSharper.Rules;
12 |
13 | [UnitTestElementsTransformationRule(Priority = 90)]
14 | public class EnsureAncestorsAddedToExecutedElementsRule : IUnitTestElementsTransformationRule
15 | {
16 | public IUnitTestElementCriterion Apply(
17 | IUnitTestElementCriterion criterion,
18 | IUnitTestSession session,
19 | IHostProvider hostProvider)
20 | {
21 | return criterion;
22 | }
23 |
24 | public void Apply(ISet elements, IUnitTestSession session, IHostProvider hostProvider)
25 | {
26 | var ancestors = new HashSet();
27 |
28 | foreach (var element in elements.OfType())
29 | {
30 | var parent = element.Parent;
31 |
32 | while (parent != null && ancestors.Add(parent))
33 | {
34 | parent = parent.Parent;
35 | }
36 | }
37 |
38 | elements.AddRange(ancestors);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Runner/AgentManagerHost.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Parts;
2 | using JetBrains.ProjectModel;
3 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
4 |
5 | namespace Machine.Specifications.Runner.ReSharper.Runner;
6 |
7 | [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)]
8 | public class AgentManagerHost(ITestRunnerAgentManager testRunnerAgentManager) : IAgentManagerHost
9 | {
10 | public ITestRunnerAgentManager AgentManager { get; } = testRunnerAgentManager;
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Runner/IAgentManagerHost.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper.Runner;
4 |
5 | public interface IAgentManagerHost
6 | {
7 | ITestRunnerAgentManager AgentManager { get; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/Runner/MspecTestRunnerRunStrategy.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.Parts;
2 | using JetBrains.ProjectModel;
3 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner;
4 | using JetBrains.ReSharper.UnitTestFramework.Execution.TestRunner.DataCollection;
5 | using JetBrains.ReSharper.UnitTestFramework.Exploration.Artifacts;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper.Runner;
8 |
9 | [SolutionComponent(Instantiation.DemandAnyThreadUnsafe)]
10 | public class MspecTestRunnerRunStrategy(
11 | IDataCollectorFactory dataCollectorFactory,
12 | IAgentManagerHost agentManagerHost,
13 | MspecTestRunnerOrchestrator adapter,
14 | IUnitTestProjectArtifactResolver artifactResolver)
15 | : TestRunnerRunStrategy(dataCollectorFactory, agentManagerHost.AgentManager, adapter, artifactResolver);
16 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/TypeInfoFactory.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.ReSharper.TestRunner.Abstractions.Objects;
2 |
3 | namespace Machine.Specifications.Runner.ReSharper;
4 |
5 | public static class TypeInfoFactory
6 | {
7 | public static TypeInfo Create(string typeName, string assemblyLocation)
8 | {
9 | return new TypeInfo(typeName, assemblyLocation);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/UnitTestElementFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using JetBrains.Metadata.Reader.API;
4 | using JetBrains.ReSharper.UnitTestFramework.Elements;
5 | using Machine.Specifications.Runner.ReSharper.Elements;
6 |
7 | namespace Machine.Specifications.Runner.ReSharper;
8 |
9 | public class UnitTestElementFactory
10 | {
11 | private readonly JetHashSet elements = new(UnitTestElement.Comparer.ByNaturalId);
12 |
13 | public MspecContextTestElement GetOrCreateContext(
14 | IClrTypeName typeName,
15 | string? subject,
16 | string[]? tags,
17 | string? ignoreReason)
18 | {
19 | var context = new MspecContextTestElement(typeName, subject, ignoreReason);
20 |
21 | if (tags != null)
22 | {
23 | context.OwnCategories = tags.Select(x => new UnitTestElementCategory(x)).ToJetHashSet();
24 | }
25 |
26 | return (MspecContextTestElement) elements.Intern(context);
27 | }
28 |
29 | public MspecSpecificationTestElement GetOrCreateSpecification(
30 | MspecContextTestElement context,
31 | string fieldName,
32 | string? behaviorType,
33 | string? ignoreReason)
34 | {
35 | var specification = new MspecSpecificationTestElement(context, fieldName, behaviorType, null, ignoreReason);
36 |
37 | return (MspecSpecificationTestElement) elements.Intern(specification);
38 | }
39 |
40 | public MspecBehaviorSpecificationTestElement GetOrCreateBehaviorSpecification(
41 | MspecSpecificationTestElement parent,
42 | string fieldName,
43 | string? ignoreReason)
44 | {
45 | var specification = new MspecBehaviorSpecificationTestElement(parent, fieldName, ignoreReason ?? parent.IgnoreReason);
46 |
47 | return (MspecBehaviorSpecificationTestElement) elements.Intern(specification);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Machine.Specifications.Runner.ReSharper/ZoneMarker.cs:
--------------------------------------------------------------------------------
1 | using JetBrains.Application.BuildScript.Application.Zones;
2 | using JetBrains.ReSharper.UnitTestFramework;
3 |
4 | namespace Machine.Specifications.Runner.ReSharper;
5 |
6 | [ZoneMarker]
7 | public class ZoneMarker : IRequire
8 | {
9 | }
10 |
--------------------------------------------------------------------------------