Skip to content

Commit e427967

Browse files
committed
Add test debugging feature using omnisharp and vimspector
1 parent ee32c96 commit e427967

File tree

4 files changed

+126
-7
lines changed

4 files changed

+126
-7
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ It is possible to run unit tests via OmniSharp-roslyn, with success/failures lis
414414
" Run the current unit test (the cursor should be on/inside the test method)
415415
:OmniSharpRunTest
416416
417+
" Debug the current unit test (This will not populate the quickfix list with the result)
418+
:OmniSharpDebugTest
419+
417420
" Run all unit tests in the current file
418421
:OmniSharpRunTestsInFile
419422

autoload/OmniSharp/actions/test.vim

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,34 @@ set cpoptions&vim
33

44
let s:runningTest = 0
55

6-
function! OmniSharp#actions#test#Run(...) abort
6+
function! s:BindTest(bufnr, Callback) abort
77
if !s:CheckCapabilities() | return | endif
8-
let bufnr = a:0 ? a:1 : bufnr('%')
9-
if !has_key(OmniSharp#GetHost(bufnr), 'project')
8+
if !has_key(OmniSharp#GetHost(a:bufnr), 'project')
109
" Initialize the test by fetching the project for the buffer - then call
1110
" this function again in the callback
12-
call OmniSharp#actions#project#Get(bufnr,
13-
\ function('OmniSharp#actions#test#Run', [bufnr]))
11+
call OmniSharp#actions#project#Get(a:bufnr,
12+
\ function('s:BindTest', [a:bufnr, a:Callback]))
1413
return
1514
endif
1615
let s:runningTest = 1
17-
call OmniSharp#actions#codestructure#Get(bufnr,
18-
\ function('s:RunTest', [function('s:CBRunTest')]))
16+
call OmniSharp#actions#codestructure#Get(a:bufnr,
17+
\ a:Callback)
18+
endfunction
19+
20+
function! OmniSharp#actions#test#Run(...) abort
21+
let bufnr = a:0 ? a:1 : bufnr('%')
22+
call s:BindTest(bufnr, function('s:RunTest', [function('s:CBRunTest')]))
23+
endfunction
24+
25+
function! OmniSharp#actions#test#Debug(...) abort
26+
if !exists('g:vimspector_home')
27+
echohl WarningMsg
28+
echomsg 'Vimspector required to debug tests'
29+
echohl None
30+
return
31+
endif
32+
let bufnr = a:0 ? a:1 : bufnr('%')
33+
call s:BindTest(bufnr, function('s:DebugTest', [function('s:CBDebugTest')]))
1934
endfunction
2035

2136
function! s:CBRunTest(summary) abort
@@ -38,6 +53,15 @@ function! s:CBRunTest(summary) abort
3853
endif
3954
endfunction
4055

56+
function! s:CBDebugTest(response) abort
57+
if !a:response.Success
58+
echohl WarningMsg
59+
echomsg 'Error debugging unit test'
60+
echomsg a:response.Message
61+
echohl None
62+
endif
63+
endfunction
64+
4165
function! OmniSharp#actions#test#RunInFile(...) abort
4266
if !s:CheckCapabilities() | return | endif
4367
if a:0 && type(a:1) == type([])
@@ -181,6 +205,66 @@ function! s:RunTestsRH(Callback, bufnr, tests, response) abort
181205
call a:Callback(summary)
182206
endfunction
183207

208+
function! s:DebugTest(Callback, bufnr, codeElements) abort
209+
let tests = s:FindTests(a:codeElements)
210+
let currentTest = s:FindTest(tests)
211+
if type(currentTest) != type({})
212+
echohl WarningMsg | echom 'No test found' | echohl None
213+
let s:runningTest = 0
214+
return
215+
endif
216+
let project = OmniSharp#GetHost(a:bufnr).project
217+
let targetFramework = project.MsBuildProject.TargetFramework
218+
let opts = {
219+
\ 'ResponseHandler': function('s:DebugTestsRH', [a:Callback, a:bufnr, tests]),
220+
\ 'Parameters': {
221+
\ 'MethodName': currentTest.name,
222+
\ 'TestFrameworkName': currentTest.framework,
223+
\ 'TargetFrameworkVersion': targetFramework
224+
\ },
225+
\ 'SendBuffer': 0
226+
\}
227+
echomsg 'Debugging test ' . currentTest.name
228+
call OmniSharp#stdio#Request('/v2/debugtest/getstartinfo', opts)
229+
endfunction
230+
231+
function! s:DebugTestsRH(Callback, bufnr, tests, response) abort
232+
let testhost = [a:response.Body.FileName] + split(substitute(a:response.Body.Arguments, '\"', '', 'g'), ' ')
233+
let testhost_job_pid = s:StartTestProcess(testhost)
234+
let g:testhost_job_pid = testhost_job_pid
235+
236+
let host = OmniSharp#GetHost()
237+
let s:omnisharp_pre_debug_cwd = getcwd()
238+
let new_cwd = fnamemodify(host.sln_or_dir, ':p:h')
239+
call vimspector#LaunchWithConfigurations({
240+
\ 'attach': {
241+
\ 'adapter': 'netcoredbg',
242+
\ 'configuration': {
243+
\ 'request': 'attach',
244+
\ 'processId': testhost_job_pid
245+
\ }
246+
\ }
247+
\})
248+
execute 'tcd '.new_cwd
249+
250+
call s:LaunchDebuggedTest(a:Callback, testhost_job_pid)
251+
endfunction
252+
253+
function! s:LaunchDebuggedTest(Callback, pid) abort
254+
let opts = {
255+
\ 'ResponseHandler': function('s:LaunchDebuggedTestRH', [a:Callback, a:pid]),
256+
\ 'Parameters': {
257+
\ 'TargetProcessId': a:pid
258+
\ }
259+
\}
260+
echomsg 'Launching debugged test'
261+
call OmniSharp#stdio#Request('/v2/debugtest/launch', opts)
262+
endfunction
263+
264+
function! s:LaunchDebuggedTestRH(Callback, pid, response) abort
265+
call a:Callback(a:response)
266+
endfunction
267+
184268
function! s:FindTestsInFiles(Callback, buffers, ...) abort
185269
call OmniSharp#util#AwaitParallel(
186270
\ map(copy(a:buffers), {i,b -> function('OmniSharp#actions#codestructure#Get', [b])}),
@@ -284,6 +368,29 @@ function! s:CheckCapabilities() abort
284368
return 1
285369
endfunction
286370

371+
function! s:TestProcessClosed(...) abort
372+
call OmniSharp#stdio#Request('/v2/debugtest/stop', {})
373+
let s:runningTest = 0
374+
call vimspector#Reset()
375+
execute 'tcd '.s:omnisharp_pre_debug_cwd
376+
unlet s:omnisharp_pre_debug_cwd
377+
endfunction
378+
379+
function! s:StartTestProcess(command) abort
380+
if OmniSharp#proc#supportsNeovimJobs()
381+
let job = jobpid(jobstart(a:command, {
382+
\ 'on_exit': function('s:TestProcessClosed')
383+
\ }))
384+
elseif OmniSharp#proc#supportsVimJobs()
385+
let job = split(job_start(a:command, {
386+
\ 'close_cb': function('s:TestProcessClosed')
387+
\ }), ' ',)[1]
388+
else
389+
echohl WarningMsg | echomsg 'Cannot launch test process.' | echohl None
390+
endif
391+
return job
392+
endfunction
393+
287394
let &cpoptions = s:save_cpo
288395
unlet s:save_cpo
289396

doc/omnisharp-vim.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,14 @@ convenient user re-mapping. These can be used like so: >
563563
Run the current unit test. The cursor can be anywhere in the test method.
564564
If the test fails, the failure message and location will be displayed in
565565
the quickfix list.
566+
*:OmniSharpDebugTest*
567+
*<Plug>(omnisharp_debug_test)*
568+
:OmniSharpDebugTest
569+
Debug the current unit test. The cursor can be anywhere in the test method.
570+
This uses the Vimspector plugin. You must have Vimpsector installed and
571+
have the +python3 vim feature.
572+
When debugging a test, the quickfix list is not populated with the result of
573+
the test.
566574

567575
*:OmniSharpRunTestsInFile*
568576
*<Plug>(omnisharp_run_tests_in_file)*

ftplugin/cs/OmniSharp.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ command! -buffer -bar OmniSharpRename call OmniSharp#actions#rename#Prompt()
4747
command! -buffer -nargs=1 OmniSharpRenameTo call OmniSharp#actions#rename#To(<q-args>)
4848
command! -buffer -bar OmniSharpRepeatCodeAction call OmniSharp#actions#codeactions#Repeat('normal')
4949
command! -buffer -bar OmniSharpRunTest call OmniSharp#actions#test#Run()
50+
command! -buffer -bar OmniSharpDebugTest call OmniSharp#actions#test#Debug()
5051
command! -buffer -bar -nargs=* -complete=file OmniSharpRunTestsInFile call OmniSharp#actions#test#RunInFile(<f-args>)
5152
command! -buffer -bar OmniSharpSignatureHelp call OmniSharp#actions#signature#SignatureHelp()
5253
command! -buffer -bar OmniSharpTypeLookup call OmniSharp#actions#documentation#TypeLookup()

0 commit comments

Comments
 (0)