1" Tests for the Vim script debug commands 2 3source shared.vim 4source screendump.vim 5 6" Run a Vim debugger command 7" If the expected output argument is supplied, then check for it. 8func RunDbgCmd(buf, cmd, ...) 9 call term_sendkeys(a:buf, a:cmd . "\r") 10 call term_wait(a:buf) 11 12 if a:0 != 0 13 " Verify the expected output 14 let lnum = 20 - len(a:1) 15 for l in a:1 16 call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}) 17 let lnum += 1 18 endfor 19 endif 20endfunc 21 22" Debugger tests 23func Test_Debugger() 24 if !CanRunVimInTerminal() 25 throw 'Skipped: cannot run Vim in a terminal window' 26 endif 27 28 " Create a Vim script with some functions 29 call writefile([ 30 \ 'func Foo()', 31 \ ' let var1 = 1', 32 \ ' let var2 = Bar(var1) + 9', 33 \ ' return var2', 34 \ 'endfunc', 35 \ 'func Bar(var)', 36 \ ' let var1 = 2 + a:var', 37 \ ' let var2 = Bazz(var1) + 4', 38 \ ' return var2', 39 \ 'endfunc', 40 \ 'func Bazz(var)', 41 \ ' try', 42 \ ' let var1 = 3 + a:var', 43 \ ' let var3 = "another var"', 44 \ ' let var3 = "value2"', 45 \ ' catch', 46 \ ' let var4 = "exception"', 47 \ ' endtry', 48 \ ' return var1', 49 \ 'endfunc'], 'Xtest.vim') 50 51 " Start Vim in a terminal 52 let buf = RunVimInTerminal('-S Xtest.vim', {}) 53 54 " Start the Vim debugger 55 call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()']) 56 57 " Create a few stack frames by stepping through functions 58 call RunDbgCmd(buf, 'step', ['line 1: let var1 = 1']) 59 call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9']) 60 call RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var']) 61 call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4']) 62 call RunDbgCmd(buf, 'step', ['line 1: try']) 63 call RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var']) 64 call RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"']) 65 66 " check backtrace 67 call RunDbgCmd(buf, 'backtrace', [ 68 \ ' 2 function Foo[2]', 69 \ ' 1 Bar[2]', 70 \ '->0 Bazz', 71 \ 'line 3: let var3 = "another var"']) 72 73 " Check variables in different stack frames 74 call RunDbgCmd(buf, 'echo var1', ['6']) 75 76 call RunDbgCmd(buf, 'up') 77 call RunDbgCmd(buf, 'back', [ 78 \ ' 2 function Foo[2]', 79 \ '->1 Bar[2]', 80 \ ' 0 Bazz', 81 \ 'line 3: let var3 = "another var"']) 82 call RunDbgCmd(buf, 'echo var1', ['3']) 83 84 call RunDbgCmd(buf, 'u') 85 call RunDbgCmd(buf, 'bt', [ 86 \ '->2 function Foo[2]', 87 \ ' 1 Bar[2]', 88 \ ' 0 Bazz', 89 \ 'line 3: let var3 = "another var"']) 90 call RunDbgCmd(buf, 'echo var1', ['1']) 91 92 " Undefined variables 93 call RunDbgCmd(buf, 'step') 94 call RunDbgCmd(buf, 'frame 2') 95 call RunDbgCmd(buf, 'echo var3', [ 96 \ 'Error detected while processing function Foo[2]..Bar[2]..Bazz:', 97 \ 'line 4:', 98 \ 'E121: Undefined variable: var3']) 99 100 " var3 is defined in this level with some other value 101 call RunDbgCmd(buf, 'fr 0') 102 call RunDbgCmd(buf, 'echo var3', ['another var']) 103 104 call RunDbgCmd(buf, 'step') 105 call RunDbgCmd(buf, '') 106 call RunDbgCmd(buf, '') 107 call RunDbgCmd(buf, '') 108 call RunDbgCmd(buf, '') 109 call RunDbgCmd(buf, 'step', [ 110 \ 'function Foo[2]..Bar', 111 \ 'line 3: End of function']) 112 call RunDbgCmd(buf, 'up') 113 114 " Undefined var2 115 call RunDbgCmd(buf, 'echo var2', [ 116 \ 'Error detected while processing function Foo[2]..Bar:', 117 \ 'line 3:', 118 \ 'E121: Undefined variable: var2']) 119 120 " Var2 is defined with 10 121 call RunDbgCmd(buf, 'down') 122 call RunDbgCmd(buf, 'echo var2', ['10']) 123 124 " Backtrace movements 125 call RunDbgCmd(buf, 'b', [ 126 \ ' 1 function Foo[2]', 127 \ '->0 Bar', 128 \ 'line 3: End of function']) 129 130 " next command cannot go down, we are on bottom 131 call RunDbgCmd(buf, 'down', ['frame is zero']) 132 call RunDbgCmd(buf, 'up') 133 134 " next command cannot go up, we are on top 135 call RunDbgCmd(buf, 'up', ['frame at highest level: 1']) 136 call RunDbgCmd(buf, 'where', [ 137 \ '->1 function Foo[2]', 138 \ ' 0 Bar', 139 \ 'line 3: End of function']) 140 141 " fil is not frame or finish, it is file 142 call RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--']) 143 144 " relative backtrace movement 145 call RunDbgCmd(buf, 'fr -1') 146 call RunDbgCmd(buf, 'frame', [ 147 \ ' 1 function Foo[2]', 148 \ '->0 Bar', 149 \ 'line 3: End of function']) 150 151 call RunDbgCmd(buf, 'fr +1') 152 call RunDbgCmd(buf, 'fram', [ 153 \ '->1 function Foo[2]', 154 \ ' 0 Bar', 155 \ 'line 3: End of function']) 156 157 " go beyond limits does not crash 158 call RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1']) 159 call RunDbgCmd(buf, 'fra', [ 160 \ '->1 function Foo[2]', 161 \ ' 0 Bar', 162 \ 'line 3: End of function']) 163 164 call RunDbgCmd(buf, 'frame -40', ['frame is zero']) 165 call RunDbgCmd(buf, 'fram', [ 166 \ ' 1 function Foo[2]', 167 \ '->0 Bar', 168 \ 'line 3: End of function']) 169 170 " final result 19 171 call RunDbgCmd(buf, 'cont', ['19']) 172 173 " breakpoints tests 174 175 " Start a debug session, so that reading the last line from the terminal 176 " works properly. 177 call RunDbgCmd(buf, ':debug echo Foo()') 178 179 " No breakpoints 180 call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 181 182 " Place some breakpoints 183 call RunDbgCmd(buf, 'breaka func Bar') 184 call RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1']) 185 call RunDbgCmd(buf, 'breakadd func 3 Bazz') 186 call RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1', 187 \ ' 2 func Bazz line 3']) 188 189 " Check whether the breakpoints are hit 190 call RunDbgCmd(buf, 'cont', [ 191 \ 'Breakpoint in "Bar" line 1', 192 \ 'function Foo[2]..Bar', 193 \ 'line 1: let var1 = 2 + a:var']) 194 call RunDbgCmd(buf, 'cont', [ 195 \ 'Breakpoint in "Bazz" line 3', 196 \ 'function Foo[2]..Bar[2]..Bazz', 197 \ 'line 3: let var3 = "another var"']) 198 199 " Delete the breakpoints 200 call RunDbgCmd(buf, 'breakd 1') 201 call RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3']) 202 call RunDbgCmd(buf, 'breakdel func 3 Bazz') 203 call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 204 205 call RunDbgCmd(buf, 'cont') 206 207 " Make sure the breakpoints are removed 208 call RunDbgCmd(buf, ':echo Foo()', ['19']) 209 210 " Delete a non-existing breakpoint 211 call RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2']) 212 213 " Expression breakpoint 214 call RunDbgCmd(buf, ':breakadd func 2 Bazz') 215 call RunDbgCmd(buf, ':echo Bazz(1)', [ 216 \ 'Entering Debug mode. Type "cont" to continue.', 217 \ 'function Bazz', 218 \ 'line 2: let var1 = 3 + a:var']) 219 call RunDbgCmd(buf, 'step') 220 call RunDbgCmd(buf, 'step') 221 call RunDbgCmd(buf, 'breaka expr var3') 222 call RunDbgCmd(buf, 'breakl', [' 3 func Bazz line 2', 223 \ ' 4 expr var3']) 224 call RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5', 225 \ 'Oldval = "''another var''"', 226 \ 'Newval = "''value2''"', 227 \ 'function Bazz', 228 \ 'line 5: catch']) 229 230 call RunDbgCmd(buf, 'breakdel *') 231 call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 232 233 " Check for error cases 234 call RunDbgCmd(buf, 'breakadd abcd', [ 235 \ 'Error detected while processing function Bazz:', 236 \ 'line 5:', 237 \ 'E475: Invalid argument: abcd']) 238 call RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func']) 239 call RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2']) 240 call RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()']) 241 call RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd']) 242 call RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func']) 243 call RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()']) 244 call RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a']) 245 call RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr']) 246 call RunDbgCmd(buf, 'breakd expr x', [ 247 \ 'E121: Undefined variable: x', 248 \ 'E161: Breakpoint not found: expr x']) 249 250 " finish the current function 251 call RunDbgCmd(buf, 'finish', [ 252 \ 'function Bazz', 253 \ 'line 8: End of function']) 254 call RunDbgCmd(buf, 'cont') 255 256 " Test for :next 257 call RunDbgCmd(buf, ':debug echo Bar(1)') 258 call RunDbgCmd(buf, 'step') 259 call RunDbgCmd(buf, 'next') 260 call RunDbgCmd(buf, '', [ 261 \ 'function Bar', 262 \ 'line 3: return var2']) 263 call RunDbgCmd(buf, 'c') 264 265 " Test for :interrupt 266 call RunDbgCmd(buf, ':debug echo Bazz(1)') 267 call RunDbgCmd(buf, 'step') 268 call RunDbgCmd(buf, 'step') 269 call RunDbgCmd(buf, 'interrupt', [ 270 \ 'Exception thrown: Vim:Interrupt', 271 \ 'function Bazz', 272 \ 'line 5: catch']) 273 call RunDbgCmd(buf, 'c') 274 275 " Test for :quit 276 call RunDbgCmd(buf, ':debug echo Foo()') 277 call RunDbgCmd(buf, 'breakdel *') 278 call RunDbgCmd(buf, 'breakadd func 3 Foo') 279 call RunDbgCmd(buf, 'breakadd func 3 Bazz') 280 call RunDbgCmd(buf, 'cont', [ 281 \ 'Breakpoint in "Bazz" line 3', 282 \ 'function Foo[2]..Bar[2]..Bazz', 283 \ 'line 3: let var3 = "another var"']) 284 call RunDbgCmd(buf, 'quit', [ 285 \ 'Breakpoint in "Foo" line 3', 286 \ 'function Foo', 287 \ 'line 3: return var2']) 288 call RunDbgCmd(buf, 'breakdel *') 289 call RunDbgCmd(buf, 'quit') 290 call RunDbgCmd(buf, 'enew! | only!') 291 292 call StopVimInTerminal(buf) 293 294 " Tests for :breakadd file and :breakadd here 295 " Breakpoints should be set before sourcing the file 296 297 call writefile([ 298 \ 'let var1 = 10', 299 \ 'let var2 = 20', 300 \ 'let var3 = 30', 301 \ 'let var4 = 40'], 'Xtest.vim') 302 303 " Start Vim in a terminal 304 let buf = RunVimInTerminal('Xtest.vim', {}) 305 call RunDbgCmd(buf, ':breakadd file 2 Xtest.vim') 306 call RunDbgCmd(buf, ':4 | breakadd here') 307 call RunDbgCmd(buf, ':source Xtest.vim', ['line 2: let var2 = 20']) 308 call RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40']) 309 call RunDbgCmd(buf, 'cont') 310 311 call StopVimInTerminal(buf) 312 313 call delete('Xtest.vim') 314endfunc 315