| " Tests for the Vim script debug commands |
| |
| source shared.vim |
| source screendump.vim |
| source check.vim |
| |
| " Run a Vim debugger command |
| " If the expected output argument is supplied, then check for it. |
| func RunDbgCmd(buf, cmd, ...) |
| call term_sendkeys(a:buf, a:cmd . "\r") |
| call term_wait(a:buf) |
| |
| if a:0 != 0 |
| " Verify the expected output |
| let lnum = 20 - len(a:1) |
| for l in a:1 |
| call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200) |
| let lnum += 1 |
| endfor |
| endif |
| endfunc |
| |
| " Debugger tests |
| func Test_Debugger() |
| CheckRunVimInTerminal |
| |
| " Create a Vim script with some functions |
| let lines =<< trim END |
| func Foo() |
| let var1 = 1 |
| let var2 = Bar(var1) + 9 |
| return var2 |
| endfunc |
| func Bar(var) |
| let var1 = 2 + a:var |
| let var2 = Bazz(var1) + 4 |
| return var2 |
| endfunc |
| func Bazz(var) |
| try |
| let var1 = 3 + a:var |
| let var3 = "another var" |
| let var3 = "value2" |
| catch |
| let var4 = "exception" |
| endtry |
| return var1 |
| endfunc |
| END |
| call writefile(lines, 'Xtest.vim') |
| |
| " Start Vim in a terminal |
| let buf = RunVimInTerminal('-S Xtest.vim', {}) |
| |
| " Start the Vim debugger |
| call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()']) |
| |
| " Create a few stack frames by stepping through functions |
| call RunDbgCmd(buf, 'step', ['line 1: let var1 = 1']) |
| call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9']) |
| call RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var']) |
| call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4']) |
| call RunDbgCmd(buf, 'step', ['line 1: try']) |
| call RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var']) |
| call RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"']) |
| |
| " check backtrace |
| call RunDbgCmd(buf, 'backtrace', [ |
| \ ' 2 function Foo[2]', |
| \ ' 1 Bar[2]', |
| \ '->0 Bazz', |
| \ 'line 3: let var3 = "another var"']) |
| |
| " Check variables in different stack frames |
| call RunDbgCmd(buf, 'echo var1', ['6']) |
| |
| call RunDbgCmd(buf, 'up') |
| call RunDbgCmd(buf, 'back', [ |
| \ ' 2 function Foo[2]', |
| \ '->1 Bar[2]', |
| \ ' 0 Bazz', |
| \ 'line 3: let var3 = "another var"']) |
| call RunDbgCmd(buf, 'echo var1', ['3']) |
| |
| call RunDbgCmd(buf, 'u') |
| call RunDbgCmd(buf, 'bt', [ |
| \ '->2 function Foo[2]', |
| \ ' 1 Bar[2]', |
| \ ' 0 Bazz', |
| \ 'line 3: let var3 = "another var"']) |
| call RunDbgCmd(buf, 'echo var1', ['1']) |
| |
| " Undefined variables |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, 'frame 2') |
| call RunDbgCmd(buf, 'echo var3', [ |
| \ 'Error detected while processing function Foo[2]..Bar[2]..Bazz:', |
| \ 'line 4:', |
| \ 'E121: Undefined variable: var3']) |
| |
| " var3 is defined in this level with some other value |
| call RunDbgCmd(buf, 'fr 0') |
| call RunDbgCmd(buf, 'echo var3', ['another var']) |
| |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, '') |
| call RunDbgCmd(buf, '') |
| call RunDbgCmd(buf, '') |
| call RunDbgCmd(buf, '') |
| call RunDbgCmd(buf, 'step', [ |
| \ 'function Foo[2]..Bar', |
| \ 'line 3: End of function']) |
| call RunDbgCmd(buf, 'up') |
| |
| " Undefined var2 |
| call RunDbgCmd(buf, 'echo var2', [ |
| \ 'Error detected while processing function Foo[2]..Bar:', |
| \ 'line 3:', |
| \ 'E121: Undefined variable: var2']) |
| |
| " Var2 is defined with 10 |
| call RunDbgCmd(buf, 'down') |
| call RunDbgCmd(buf, 'echo var2', ['10']) |
| |
| " Backtrace movements |
| call RunDbgCmd(buf, 'b', [ |
| \ ' 1 function Foo[2]', |
| \ '->0 Bar', |
| \ 'line 3: End of function']) |
| |
| " next command cannot go down, we are on bottom |
| call RunDbgCmd(buf, 'down', ['frame is zero']) |
| call RunDbgCmd(buf, 'up') |
| |
| " next command cannot go up, we are on top |
| call RunDbgCmd(buf, 'up', ['frame at highest level: 1']) |
| call RunDbgCmd(buf, 'where', [ |
| \ '->1 function Foo[2]', |
| \ ' 0 Bar', |
| \ 'line 3: End of function']) |
| |
| " fil is not frame or finish, it is file |
| call RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--']) |
| |
| " relative backtrace movement |
| call RunDbgCmd(buf, 'fr -1') |
| call RunDbgCmd(buf, 'frame', [ |
| \ ' 1 function Foo[2]', |
| \ '->0 Bar', |
| \ 'line 3: End of function']) |
| |
| call RunDbgCmd(buf, 'fr +1') |
| call RunDbgCmd(buf, 'fram', [ |
| \ '->1 function Foo[2]', |
| \ ' 0 Bar', |
| \ 'line 3: End of function']) |
| |
| " go beyond limits does not crash |
| call RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1']) |
| call RunDbgCmd(buf, 'fra', [ |
| \ '->1 function Foo[2]', |
| \ ' 0 Bar', |
| \ 'line 3: End of function']) |
| |
| call RunDbgCmd(buf, 'frame -40', ['frame is zero']) |
| call RunDbgCmd(buf, 'fram', [ |
| \ ' 1 function Foo[2]', |
| \ '->0 Bar', |
| \ 'line 3: End of function']) |
| |
| " final result 19 |
| call RunDbgCmd(buf, 'cont', ['19']) |
| |
| " breakpoints tests |
| |
| " Start a debug session, so that reading the last line from the terminal |
| " works properly. |
| call RunDbgCmd(buf, ':debug echo Foo()') |
| |
| " No breakpoints |
| call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) |
| |
| " Place some breakpoints |
| call RunDbgCmd(buf, 'breaka func Bar') |
| call RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1']) |
| call RunDbgCmd(buf, 'breakadd func 3 Bazz') |
| call RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1', |
| \ ' 2 func Bazz line 3']) |
| |
| " Check whether the breakpoints are hit |
| call RunDbgCmd(buf, 'cont', [ |
| \ 'Breakpoint in "Bar" line 1', |
| \ 'function Foo[2]..Bar', |
| \ 'line 1: let var1 = 2 + a:var']) |
| call RunDbgCmd(buf, 'cont', [ |
| \ 'Breakpoint in "Bazz" line 3', |
| \ 'function Foo[2]..Bar[2]..Bazz', |
| \ 'line 3: let var3 = "another var"']) |
| |
| " Delete the breakpoints |
| call RunDbgCmd(buf, 'breakd 1') |
| call RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3']) |
| call RunDbgCmd(buf, 'breakdel func 3 Bazz') |
| call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) |
| |
| call RunDbgCmd(buf, 'cont') |
| |
| " Make sure the breakpoints are removed |
| call RunDbgCmd(buf, ':echo Foo()', ['19']) |
| |
| " Delete a non-existing breakpoint |
| call RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2']) |
| |
| " Expression breakpoint |
| call RunDbgCmd(buf, ':breakadd func 2 Bazz') |
| call RunDbgCmd(buf, ':echo Bazz(1)', [ |
| \ 'Entering Debug mode. Type "cont" to continue.', |
| \ 'function Bazz', |
| \ 'line 2: let var1 = 3 + a:var']) |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, 'breaka expr var3') |
| call RunDbgCmd(buf, 'breakl', [' 3 func Bazz line 2', |
| \ ' 4 expr var3']) |
| call RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5', |
| \ 'Oldval = "''another var''"', |
| \ 'Newval = "''value2''"', |
| \ 'function Bazz', |
| \ 'line 5: catch']) |
| |
| call RunDbgCmd(buf, 'breakdel *') |
| call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) |
| |
| " Check for error cases |
| call RunDbgCmd(buf, 'breakadd abcd', [ |
| \ 'Error detected while processing function Bazz:', |
| \ 'line 5:', |
| \ 'E475: Invalid argument: abcd']) |
| call RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func']) |
| call RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2']) |
| call RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()']) |
| call RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd']) |
| call RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func']) |
| call RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()']) |
| call RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a']) |
| call RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr']) |
| call RunDbgCmd(buf, 'breakd expr x', [ |
| \ 'E121: Undefined variable: x', |
| \ 'E161: Breakpoint not found: expr x']) |
| |
| " finish the current function |
| call RunDbgCmd(buf, 'finish', [ |
| \ 'function Bazz', |
| \ 'line 8: End of function']) |
| call RunDbgCmd(buf, 'cont') |
| |
| " Test for :next |
| call RunDbgCmd(buf, ':debug echo Bar(1)') |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, 'next') |
| call RunDbgCmd(buf, '', [ |
| \ 'function Bar', |
| \ 'line 3: return var2']) |
| call RunDbgCmd(buf, 'c') |
| |
| " Test for :interrupt |
| call RunDbgCmd(buf, ':debug echo Bazz(1)') |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, 'step') |
| call RunDbgCmd(buf, 'interrupt', [ |
| \ 'Exception thrown: Vim:Interrupt', |
| \ 'function Bazz', |
| \ 'line 5: catch']) |
| call RunDbgCmd(buf, 'c') |
| |
| " Test for :quit |
| call RunDbgCmd(buf, ':debug echo Foo()') |
| call RunDbgCmd(buf, 'breakdel *') |
| call RunDbgCmd(buf, 'breakadd func 3 Foo') |
| call RunDbgCmd(buf, 'breakadd func 3 Bazz') |
| call RunDbgCmd(buf, 'cont', [ |
| \ 'Breakpoint in "Bazz" line 3', |
| \ 'function Foo[2]..Bar[2]..Bazz', |
| \ 'line 3: let var3 = "another var"']) |
| call RunDbgCmd(buf, 'quit', [ |
| \ 'Breakpoint in "Foo" line 3', |
| \ 'function Foo', |
| \ 'line 3: return var2']) |
| call RunDbgCmd(buf, 'breakdel *') |
| call RunDbgCmd(buf, 'quit') |
| call RunDbgCmd(buf, 'enew! | only!') |
| |
| call StopVimInTerminal(buf) |
| |
| " Tests for :breakadd file and :breakadd here |
| " Breakpoints should be set before sourcing the file |
| |
| let lines =<< trim END |
| let var1 = 10 |
| let var2 = 20 |
| let var3 = 30 |
| let var4 = 40 |
| END |
| call writefile(lines, 'Xtest.vim') |
| |
| " Start Vim in a terminal |
| let buf = RunVimInTerminal('Xtest.vim', {}) |
| call RunDbgCmd(buf, ':breakadd file 2 Xtest.vim') |
| call RunDbgCmd(buf, ':4 | breakadd here') |
| call RunDbgCmd(buf, ':source Xtest.vim', ['line 2: let var2 = 20']) |
| call RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40']) |
| call RunDbgCmd(buf, 'cont') |
| |
| call StopVimInTerminal(buf) |
| |
| call delete('Xtest.vim') |
| endfunc |