diff options
Diffstat (limited to 'mnv/src/testdir/test_plugin_termdebug.mnv')
| -rw-r--r-- | mnv/src/testdir/test_plugin_termdebug.mnv | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/mnv/src/testdir/test_plugin_termdebug.mnv b/mnv/src/testdir/test_plugin_termdebug.mnv new file mode 100644 index 0000000000..5f25f732ec --- /dev/null +++ b/mnv/src/testdir/test_plugin_termdebug.mnv @@ -0,0 +1,864 @@ +" Test for the termdebug plugin + +source util/screendump.mnv + +CheckUnix +CheckFeature terminal +CheckExecutable gdb +CheckExecutable gcc + +let g:GDB = exepath('gdb') +if g:GDB->empty() + throw 'Skipped: gdb is not found in $PATH' +endif + +let g:GCC = exepath('gcc') +if g:GCC->empty() + throw 'Skipped: gcc is not found in $PATH' +endif + +function s:generate_files(bin_name) + let src_name = a:bin_name .. '.c' + let lines =<< trim END + #include <stdio.h> + #include <stdlib.h> + + int isprime(int n) + { + if (n <= 1) + return 0; + + for (int i = 2; i <= n / 2; i++) + if (n % i == 0) + return 0; + + return 1; + } + + int main(int argc, char *argv[]) + { + int n = 7; + + printf("%d is %s prime\n", n, isprime(n) ? "a" : "not a"); + + return 0; + } + END + call writefile(lines, src_name) + call system($'{g:GCC} -g -o {a:bin_name} {src_name}') +endfunction + +function s:cleanup_files(bin_name) + call delete(a:bin_name) + call delete(a:bin_name .. '.c') +endfunction + +packadd termdebug + +func s:GetTermdebugFunction(name) + for line in execute('scriptnames')->split("\n") + if line =~# 'termdebug/plugin/termdebug\.mnv$' + let sid = matchstr(line, '^\s*\zs\d\+') + return function('<SNR>' .. sid .. '_' .. a:name) + endif + endfor + throw 'termdebug script not found' +endfunc + +func Test_termdebug_break_command_builder() + let bin_name = 'XTD_break_cmd' + let src_name = bin_name .. '.c' + let BuildBreakpointCommand = s:GetTermdebugFunction('BuildBreakpointCommand') + call s:generate_files(bin_name) + + execute 'edit ' .. src_name + call cursor(22, 1) + let here = '"' .. fnamemodify(src_name, ':p') .. ':22"' + + call assert_equal('-break-insert ' .. here, BuildBreakpointCommand('', v:false)) + call assert_equal('-break-insert -t ' .. here, BuildBreakpointCommand('', v:true)) + call assert_equal('-break-insert -c "argc == 1" ' .. here, + \ BuildBreakpointCommand('if argc == 1', v:false)) + call assert_equal('-break-insert -p 2 ' .. here, + \ BuildBreakpointCommand('thread 2', v:false)) + call assert_equal('-break-insert -p 2 -c "argc == 1" ' .. here, + \ BuildBreakpointCommand('thread 2 if argc == 1', v:false)) + call assert_equal('-break-insert -p 2 -c "argc == 1" 22', + \ BuildBreakpointCommand('22 thread 2 if argc == 1', v:false)) + call assert_equal('-break-insert -c "argc == 1" 22', + \ BuildBreakpointCommand('22 if argc == 1', v:false)) + call assert_equal('-break-insert -c "é == 1" 断点', + \ BuildBreakpointCommand('断点 if é == 1', v:false)) + call assert_equal('-break-insert -p 2 断点', + \ BuildBreakpointCommand('断点 thread 2', v:false)) + call assert_equal('-break-insert 断点 if', + \ BuildBreakpointCommand('断点 if', v:false)) + call assert_equal('-break-insert 断点 thread 2 if', + \ BuildBreakpointCommand('断点 thread 2 if', v:false)) + call assert_equal('-break-insert foo\ if\ bar', + \ BuildBreakpointCommand('foo\ if\ bar', v:false)) + + call s:cleanup_files(bin_name) + %bw! +endfunc + +func Test_termdebug_break_with_default_location_and_condition() + let g:test_is_flaky = 1 + let bin_name = 'XTD_break_if' + let src_name = bin_name .. '.c' + call s:generate_files(bin_name) + + execute 'edit ' .. src_name + execute 'Termdebug ./' .. bin_name + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + + call cursor(22, 1) + Break if argc == 1 + call term_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 22, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + Run + call term_wait(gdb_buf, 400) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 22, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': 22, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + Continue + call term_wait(gdb_buf) + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + call s:cleanup_files(bin_name) + %bw! +endfunc + +" should be the first test to run, since it validates the window layout with +" win ids +func Test_termdebug_basic() + let g:test_is_flaky = 1 + let bin_name = 'XTD_basic' + let src_name = bin_name .. '.c' + call s:generate_files(bin_name) + + edit XTD_basic.c + Termdebug ./XTD_basic + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + Break 9 + call term_wait(gdb_buf) + redraw! + call assert_equal([ + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + Run + call term_wait(gdb_buf, 400) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 9, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + Finish + call term_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}, + \ {'lnum': 20, 'id': 12, 'name': 'debugPC', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + Continue + call term_wait(gdb_buf) + + let g:termdebug_config = {} + let g:termdebug_config['signs'] = ['>1', '>2', '>3'] + let g:termdebug_config['sign'] = '>>' + let g:termdebug_config['sign_decimal'] = 1 + + let i = 2 + while i <= 258 + Break + call term_wait(gdb_buf) + if i == 2 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint2.0')[0].text, '>2')}) + endif + if i == 3 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint3.0')[0].text, '>3')}) + endif + if i == 4 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint4.0')[0].text, '>>')}) + endif + if i == 5 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint5.0')[0].text, '>>')}) + unlet g:termdebug_config['sign'] + endif + if i == 6 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint6.0')[0].text, '06')}) + endif + if i == 10 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint10.0')[0].text, '10')}) + endif + if i == 99 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint99.0')[0].text, '99')}) + endif + if i == 100 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint100.0')[0].text, '9+')}) + endif + if i == 110 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint110.0')[0].text, '9+')}) + unlet g:termdebug_config + endif + if i == 128 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint128.0')[0].text, '80')}) + endif + if i == 168 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint168.0')[0].text, 'A8')}) + endif + if i == 255 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint255.0')[0].text, 'FF')}) + endif + if i == 256 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint256.0')[0].text, 'F+')}) + endif + if i == 258 + call WaitForAssert({-> assert_equal(sign_getdefined('debugBreakpoint258.0')[0].text, 'F+')}) + endif + let i += 1 + endwhile + + let cn = 0 + " 60 is approx spaceBuffer * 3 + if winwidth(0) <= 78 + 60 + Var + call assert_equal(winnr('$'), winnr()) + call assert_equal(['col', [['leaf', 1002], ['leaf', 1001], ['leaf', 1000], ['leaf', 1003 + cn]]], winlayout()) + let cn += 1 + bw! + Asm + call assert_equal(winnr('$'), winnr()) + call assert_equal(['col', [['leaf', 1002], ['leaf', 1001], ['leaf', 1000], ['leaf', 1003 + cn]]], winlayout()) + let cn += 1 + bw! + endif + set columns=160 + call term_wait(gdb_buf) + let winw = winwidth(0) + Var + if winwidth(0) < winw + call assert_equal(winnr('$') - 1, winnr()) + call assert_equal(['col', [['leaf', 1002], ['leaf', 1001], ['row', [['leaf', 1003 + cn], ['leaf', 1000]]]]], winlayout()) + let cn += 1 + bw! + endif + let winw = winwidth(0) + Asm + if winwidth(0) < winw + call assert_equal(winnr('$') - 1, winnr()) + call assert_equal(['col', [['leaf', 1002], ['leaf', 1001], ['row', [['leaf', 1003 + cn], ['leaf', 1000]]]]], winlayout()) + let cn += 1 + bw! + endif + set columns& + call term_wait(gdb_buf) + + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal([], sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + for use_prompt in [v:true, v:false] + let g:termdebug_config = {} + let g:termdebug_config['use_prompt'] = use_prompt + TermdebugCommand ./XTD_basic arg args + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + unlet g:termdebug_config + endfor + + call s:cleanup_files(bin_name) + %bw! +endfunc + +func Test_termdebug_tbreak() + let g:test_is_flaky = 1 + let bin_name = 'XTD_tbreak' + let src_name = bin_name .. '.c' + + eval s:generate_files(bin_name) + + execute 'edit ' .. src_name + execute 'Termdebug ./' .. bin_name + + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + + let bp_line = 22 " 'return' statement in main + let temp_bp_line = 10 " 'if' statement in 'for' loop body + execute "Tbreak " .. temp_bp_line + execute "Break " .. bp_line + + call term_wait(gdb_buf) + redraw! + " both temporary and normal breakpoint signs were displayed... + call assert_equal([ + \ {'lnum': temp_bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + Run + call term_wait(gdb_buf, 400) + redraw! + " debugPC sign is on the line where the temp. bp was set; + " temp. bp sign was removed after hit; + " normal bp sign is still present + call WaitForAssert({-> assert_equal([ + \ {'lnum': temp_bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + Continue + call term_wait(gdb_buf) + redraw! + " debugPC is on the normal breakpoint, + " temp. bp on line 10 was only hit once + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal([], sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + eval s:cleanup_files(bin_name) + %bw! +endfunc + +func Test_termdebug_evaluate() + let bin_name = 'XTD_evaluate' + let src_name = bin_name .. '.c' + call s:generate_files(bin_name) + + edit XTD_evaluate.c + Termdebug ./XTD_evaluate + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + + " return stmt in main + Break 22 + call term_wait(gdb_buf) + Run + call term_wait(gdb_buf, 400) + redraw! + + " Evaluate an expression + Evaluate n + call term_wait(gdb_buf) + call assert_equal(execute('1messages')->trim(), '"n": 7') + Evaluate argc + call term_wait(gdb_buf) + call assert_equal(execute('1messages')->trim(), '"argc": 1') + Evaluate isprime(n) + call term_wait(gdb_buf) + call assert_equal(execute('1messages')->trim(), '"isprime(n)": 1') + + wincmd t + quit! + redraw! + call s:cleanup_files(bin_name) + %bw! +endfunc + +func Test_termdebug_evaluate_in_popup() + CheckScreendump + let bin_name = 'XTD_evaluate_in_popup' + let src_name = bin_name .. '.c' + let code =<< trim END + struct Point { + int x; + int y; + }; + + int main(int argc, char* argv[]) { + struct Point p = {argc, 2}; + struct Point* p_ptr = &p; + return 0; + } + END + call writefile(code, src_name, 'D') + call system($'{g:GCC} -g -o {bin_name} {src_name}') + + let lines =<< trim END + edit XTD_evaluate_in_popup.c + packadd termdebug + let g:termdebug_config = {} + let g:termdebug_config['evaluate_in_popup'] = v:true + Termdebug ./XTD_evaluate_in_popup + wincmd b + Break 9 + Run + END + + call writefile(lines, 'Xscript', 'D') + let buf = RunMNVInTerminal('-S Xscript', {}) + call TermWait(buf, 400) + + call term_sendkeys(buf, ":Evaluate p\<CR>") + call TermWait(buf, 400) + call VerifyScreenDump(buf, 'Test_termdebug_evaluate_in_popup_01', {}) + + call term_sendkeys(buf, ":Evaluate *p_ptr\<CR>") + call TermWait(buf, 400) + call VerifyScreenDump(buf, 'Test_termdebug_evaluate_in_popup_02', {}) + + " Cleanup + call term_sendkeys(buf, ":Gdb") + call term_sendkeys(buf, ":quit!\<CR>") + call term_sendkeys(buf, ":qa!\<CR>") + call StopMNVInTerminal(buf) + call delete(bin_name) + %bw! +endfunc + +func Test_termdebug_mapping() + %bw! + call assert_true(maparg('K', 'n', 0, 1)->empty()) + call assert_true(maparg('K', 'x', 0, 1)->empty()) + call assert_true(maparg('-', 'n', 0, 1)->empty()) + call assert_true(maparg('+', 'n', 0, 1)->empty()) + Termdebug + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd b + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'x', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'n', 0, 1).buffer) + call assert_false(maparg('-', 'n', 0, 1).buffer) + call assert_false(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs) + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_true(maparg('K', 'n', 0, 1)->empty()) + call assert_true(maparg('K', 'x', 0, 1)->empty()) + call assert_true(maparg('-', 'n', 0, 1)->empty()) + call assert_true(maparg('+', 'n', 0, 1)->empty()) + + %bw! + nnoremap K :echom "K"<cr> + xnoremap K :<C-U>echom "VK"<cr> + nnoremap - :echom "-"<cr> + nnoremap + :echom "+"<cr> + Termdebug + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd b + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'x', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'n', 0, 1).buffer) + call assert_false(maparg('K', 'x', 0, 1).buffer) + call assert_false(maparg('-', 'n', 0, 1).buffer) + call assert_false(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs) + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'x', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'n', 0, 1).buffer) + call assert_false(maparg('K', 'x', 0, 1).buffer) + call assert_false(maparg('-', 'n', 0, 1).buffer) + call assert_false(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':echom "K"<cr>', maparg('K', 'n', 0, 1).rhs) + call assert_equal(':<C-U>echom "VK"<cr>', maparg('K', 'x', 0, 1).rhs) + + %bw! + + " -- Test that local-buffer mappings are restored in the correct buffers -- + " local mappings for foo + file foo + nnoremap <buffer> K :echom "bK"<cr> + nnoremap <buffer> - :echom "b-"<cr> + nnoremap <buffer> + :echom "b+"<cr> + + " no mappings for 'bar' + enew + file bar + + " Start termdebug from foo + buffer foo + Termdebug + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd b + call assert_true(maparg('K', 'n', 0, 1).buffer) + call assert_true(maparg('-', 'n', 0, 1).buffer) + call assert_true(maparg('+', 'n', 0, 1).buffer) + call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>') + + Source + buffer bar + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_true(maparg('K', 'n', 0, 1).buffer->empty()) + call assert_true(maparg('-', 'n', 0, 1).buffer->empty()) + call assert_true(maparg('+', 'n', 0, 1).buffer->empty()) + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + " Termdebug session ended. Buffer 'bar' shall have no mappings + call assert_true(bufname() ==# 'bar') + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_true(maparg('K', 'n', 0, 1).buffer->empty()) + call assert_true(maparg('-', 'n', 0, 1).buffer->empty()) + call assert_true(maparg('+', 'n', 0, 1).buffer->empty()) + + " Buffer 'foo' shall have the same mapping as before running the termdebug + " session + buffer foo + call assert_true(bufname() ==# 'foo') + call assert_true(maparg('K', 'n', 0, 1).buffer) + call assert_true(maparg('-', 'n', 0, 1).buffer) + call assert_true(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':echom "bK"<cr>', maparg('K', 'n', 0, 1).rhs) + + nunmap K + nunmap + + nunmap - + %bw! +endfunc + +function Test_termdebug_save_restore_variables() + " saved mousemodel + let &mousemodel='' + + " saved keys + nnoremap K :echo "hello world!"<cr> + let expected_map_K = maparg('K', 'n', 0 , 1) + nnoremap + :echo "hello plus!"<cr> + let expected_map_plus = maparg('+', 'n', 0 , 1) + let expected_map_minus = {} + + " saved &columns + let expected_columns = &columns + + " We want termdebug to overwrite 'K' map but not '+' map. + let g:termdebug_config = {} + let g:termdebug_config['map_K'] = v:true + + Termdebug + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + call WaitForAssert({-> assert_match(&mousemodel, 'popup_setpos')}) + wincmd t + quit! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + call assert_true(empty(&mousemodel)) + + call assert_true(empty(expected_map_minus)) + call assert_equal(expected_map_K.rhs, maparg('K', 'n', 0, 1).rhs) + call assert_equal(expected_map_plus.rhs, maparg('+', 'n', 0, 1).rhs) + + call assert_equal(expected_columns, &columns) + + nunmap K + nunmap + + unlet g:termdebug_config +endfunction + +function Test_termdebug_sanity_check() + " Test if user has filename/folders with wrong names + let g:termdebug_config = {} + let s:dict = {'disasm_window': 'Termdebug-asm-listing', 'use_prompt': 'gdb', 'variables_window': 'Termdebug-variables-listing'} + + for key in keys(s:dict) + let s:filename = s:dict[key] + let g:termdebug_config[key] = v:true + let s:error_message = "You have a file/folder named '" .. s:filename .. "'" + + " Write dummy file with bad name + call writefile(['This', 'is', 'a', 'test'], s:filename, 'D') + Termdebug + call WaitForAssert({-> assert_true(execute('messages') =~ s:error_message)}) + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + call delete(s:filename) + call remove(g:termdebug_config, key) + endfor + + unlet g:termdebug_config +endfunction + +function Test_termdebug_double_termdebug_instances() + let s:error_message = 'Terminal debugger already running, cannot run two' + Termdebug + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + Termdebug + call WaitForAssert({-> assert_true(execute('messages') =~ s:error_message)}) + wincmd t + quit! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + :%bw! +endfunction + +function Test_termdebug_config_types() + " TODO Remove the deprecated features after 1 Jan 2025. + let g:test_is_flaky = 1 + let g:termdebug_config = {} + let s:error_message = 'Deprecation Warning:' + call assert_true(maparg('K', 'n', 0, 1)->empty()) + + for key in ['disasm_window', 'variables_window', 'map_K'] + for val in [0, 1, v:true, v:false] + let g:termdebug_config[key] = val + Termdebug + + " Type check: warning is displayed + if typename(val) == 'number' + call WaitForAssert({-> assert_true(execute('messages') =~ s:error_message)}) + endif + + " Test on g:termdebug_config keys + if val && key != 'map_K' + call WaitForAssert({-> assert_equal(4, winnr('$'))}) + call remove(g:termdebug_config, key) + else + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + endif + + " Test on mapping + if key == 'map_K' + if val + call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs) + else + call assert_true(maparg('K', 'n', 0, 1)->empty()) + endif + endif + + " Shutoff termdebug + wincmd t + quit! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + :%bw! + + endfor + endfor + + unlet g:termdebug_config +endfunction + +func Test_termdebug_toggle_break() + let g:test_is_flaky = 1 + let bin_name = 'XTD_tbreak' + let src_name = bin_name .. '.c' + + eval s:generate_files(bin_name) + + execute 'edit ' .. src_name + execute 'Termdebug ./' .. bin_name + + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + + let bp_line = 22 " 'return' statement in main + execute "normal! " .. bp_line .. "G" + execute "ToggleBreak" + + call term_wait(gdb_buf) + redraw! + call assert_equal([ + \ {'lnum': bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + RunOrContinue + call term_wait(gdb_buf, 400) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + " Add one break point + execute "normal! " .. bp_line .. "G" + execute "ToggleBreak" + call term_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + " Remove one break point + execute "normal! " .. bp_line .. "G" + execute "ToggleBreak" + call term_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', + \ 'priority': 110, 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + " Remove multiple break points + execute "Break" + execute "Break" + execute "Break" + execute "Break" + call term_wait(gdb_buf, 400) + execute "ToggleBreak" + call term_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal([], sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + eval s:cleanup_files(bin_name) + %bw! +endfunc + +" Check substitution capabilities and simulate remote debugging +func Test_termdebug_remote_basic() + let bin_name = 'XTD_basicremote' + let src_name = bin_name .. '.c' + call s:generate_files(bin_name) + defer s:cleanup_files(bin_name) + + " Duplicate sources to test the mapping + const pwd = getcwd() + const src_shadow_dir = "shadow" + call mkdir(src_shadow_dir) + const src_shadow_file = $"{src_shadow_dir}/{src_name}" + call filecopy(src_name, src_shadow_file) + defer delete(src_shadow_dir, 'rf') + + let modes = [v:true] + " termdebug only works fine if socat is available on the remote machine + " otherwise the communication pty will be unstable + if executable('socat') + let modes += [v:false] + endif + + for use_prompt in modes + " Set up mock remote and mapping + let g:termdebug_config = {} + + let g:termdebug_config['use_prompt'] = use_prompt + " favor socat if available + if executable('socat') + let g:termdebug_config['remote_window'] = + \ ['socat', '-d', '-d', '-', 'PTY,raw,echo=0'] + else + let g:termdebug_config['remote_window'] = ['sh'] + endif + + let g:termdebug_config['substitute_path'] = {} + let g:termdebug_config['substitute_path'][pwd] = pwd . '/' . src_shadow_dir + defer execute("unlet g:termdebug_config") + + " Launch the debugger and set breakpoints in the shadow file instead + exe $"edit {src_shadow_file}" + exe $"Termdebug ./{bin_name}" + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + Break 9 + sleep 100m + redraw! + call assert_equal([ + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + Run + call term_wait(gdb_buf, 400) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 9, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + Finish + call term_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}, + \ {'lnum': 20, 'id': 12, 'name': 'debugPC', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + " Cleanup, make sure the gdb job is terminated before return + " otherwise may interfere with next test + Gdb + bw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + endfor + + %bw! +endfunc + +" mnv: shiftwidth=2 sts=2 expandtab |
