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