changeset 21508:6b2a5418cbc3 v8.2.1304

patch 8.2.1304: debug backtrace isn't tested much Commit: https://github.com/vim/vim/commit/6ca6ca48898750dd55cad13c88a9c1dfd7fdaad5 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Jul 27 19:47:07 2020 +0200 patch 8.2.1304: debug backtrace isn't tested much Problem: Debug backtrace isn't tested much. Solution: Add more specific tests. (Ben Jackson, closes https://github.com/vim/vim/issues/6540)
author Bram Moolenaar <Bram@vim.org>
date Mon, 27 Jul 2020 20:00:04 +0200
parents 9e21fa7044b5
children 00a2e4d3fc38
files src/testdir/runtest.vim src/testdir/test_debugger.vim src/version.c
diffstat 3 files changed, 453 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/runtest.vim
+++ b/src/testdir/runtest.vim
@@ -13,6 +13,9 @@
 " For csh:
 "     setenv TEST_FILTER Test_channel
 "
+" While working on a test you can make $TEST_NO_RETRY non-empty to not retry:
+"     export TEST_NO_RETRY=yes
+"
 " To ignore failure for tests that are known to fail in a certain environment,
 " set $TEST_MAY_FAIL to a comma separated list of function names.  E.g. for
 " sh/bash:
@@ -440,9 +443,11 @@ for g:testfunc in sort(s:tests)
   call RunTheTest(g:testfunc)
 
   " Repeat a flaky test.  Give up when:
+  " - $TEST_NO_RETRY is not empty
   " - it fails again with the same message
   " - it fails five times (with a different message)
   if len(v:errors) > 0
+        \ $TEST_NO_RETRY == ''
         \ && (index(s:flaky_tests, g:testfunc) >= 0
         \      || g:test_is_flaky)
     while 1
--- a/src/testdir/test_debugger.vim
+++ b/src/testdir/test_debugger.vim
@@ -4,6 +4,19 @@ source shared.vim
 source screendump.vim
 source check.vim
 
+func CheckDbgOutput(buf, lines, options = {})
+  " Verify the expected output
+  let lnum = 20 - len(a:lines)
+  for l in a:lines
+    if get(a:options, 'match', 'equal') ==# 'pattern'
+      call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, 200)
+    else
+      call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200)
+    endif
+    let lnum += 1
+  endfor
+endfunc
+
 " Run a Vim debugger command
 " If the expected output argument is supplied, then check for it.
 func RunDbgCmd(buf, cmd, ...)
@@ -11,12 +24,11 @@ func RunDbgCmd(buf, cmd, ...)
   call TermWait(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
+    let options = #{match: 'equal'}
+    if a:0 > 1
+      call extend(options, a:2)
+    endif
+    call CheckDbgOutput(a:buf, a:1, options)
   endif
 endfunc
 
@@ -315,3 +327,431 @@ func Test_Debugger()
 
   call delete('Xtest.vim')
 endfunc
+
+func Test_Backtrace_Through_Source()
+  CheckRunVimInTerminal
+
+  let file1 =<< trim END
+    func SourceAnotherFile()
+      source Xtest2.vim
+    endfunc
+
+    func CallAFunction()
+      call SourceAnotherFile()
+      call File2Function()
+    endfunc
+
+    func GlobalFunction()
+      call CallAFunction()
+    endfunc
+  END
+  call writefile(file1, 'Xtest1.vim')
+
+  let file2 =<< trim END
+    func DoAThing()
+      echo "DoAThing"
+    endfunc
+
+    func File2Function()
+      call DoAThing()
+    endfunc
+
+    call File2Function()
+  END
+  call writefile(file2, 'Xtest2.vim')
+
+  let buf = RunVimInTerminal('-S Xtest1.vim', {})
+
+  call RunDbgCmd(buf,
+                \ ':debug call GlobalFunction()',
+                \ ['cmd: call GlobalFunction()'])
+  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
+
+  call RunDbgCmd(buf, 'backtrace', ['>backtrace',
+                                    \ '->0 function GlobalFunction',
+                                    \ 'line 1: call CallAFunction()'])
+
+  call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
+  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
+
+  call RunDbgCmd(buf, 'backtrace', ['>backtrace',
+                                    \ '  2 function GlobalFunction[1]',
+                                    \ '  1 CallAFunction[1]',
+                                    \ '->0 SourceAnotherFile',
+                                    \ 'line 1: source Xtest2.vim'])
+
+  " Step into the 'source' command. Note that we print the full trace all the
+  " way though the source command.
+  call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  3 function GlobalFunction[1]',
+        \ '  2 CallAFunction[1]',
+        \ '  1 SourceAnotherFile[1]',
+        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
+        \ 'line 1: func DoAThing()'])
+
+  " step until we have another meaninfgul trace
+  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
+  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  3 function GlobalFunction[1]',
+        \ '  2 CallAFunction[1]',
+        \ '  1 SourceAnotherFile[1]',
+        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
+        \ 'line 9: call File2Function()'])
+
+  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
+  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  5 function GlobalFunction[1]',
+        \ '  4 CallAFunction[1]',
+        \ '  3 SourceAnotherFile[1]',
+        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
+        \ '  1 function File2Function[1]',
+        \ '->0 DoAThing',
+        \ 'line 1: echo "DoAThing"'])
+
+  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
+  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
+  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  1 function GlobalFunction[1]',
+        \ '->0 CallAFunction',
+        \ 'line 2: call File2Function()'])
+
+  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  2 function GlobalFunction[1]',
+        \ '  1 CallAFunction[2]',
+        \ '->0 File2Function',
+        \ 'line 1: call DoAThing()'])
+
+  call StopVimInTerminal(buf)
+  call delete('Xtest1.vim')
+  call delete('Xtest2.vim')
+endfunc
+
+func Test_Backtrace_Autocmd()
+  CheckRunVimInTerminal
+
+  let file1 =<< trim END
+    func SourceAnotherFile()
+      source Xtest2.vim
+    endfunc
+
+    func CallAFunction()
+      call SourceAnotherFile()
+      call File2Function()
+    endfunc
+
+    func GlobalFunction()
+      call CallAFunction()
+    endfunc
+
+    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
+  END
+  call writefile(file1, 'Xtest1.vim')
+
+  let file2 =<< trim END
+    func DoAThing()
+      echo "DoAThing"
+    endfunc
+
+    func File2Function()
+      call DoAThing()
+    endfunc
+
+    call File2Function()
+  END
+  call writefile(file2, 'Xtest2.vim')
+
+  let buf = RunVimInTerminal('-S Xtest1.vim', {})
+
+  call RunDbgCmd(buf,
+                \ ':debug doautocmd User TestGlobalFunction',
+                \ ['cmd: doautocmd User TestGlobalFunction'])
+  call RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"'])
+
+  " At this point the ontly thing in the stack is the autocommand
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '->0 User Autocommands for "TestGlobalFunction"',
+        \ 'cmd: call GlobalFunction() | echo "Done"'])
+
+  " And now we're back into the call stack
+  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  1 User Autocommands for "TestGlobalFunction"',
+        \ '->0 function GlobalFunction',
+        \ 'line 1: call CallAFunction()'])
+
+  call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
+  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
+
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  3 User Autocommands for "TestGlobalFunction"',
+        \ '  2 function GlobalFunction[1]',
+        \ '  1 CallAFunction[1]',
+        \ '->0 SourceAnotherFile',
+        \ 'line 1: source Xtest2.vim'])
+
+  " Step into the 'source' command. Note that we print the full trace all the
+  " way though the source command.
+  call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  4 User Autocommands for "TestGlobalFunction"',
+        \ '  3 function GlobalFunction[1]',
+        \ '  2 CallAFunction[1]',
+        \ '  1 SourceAnotherFile[1]',
+        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
+        \ 'line 1: func DoAThing()'])
+
+  " step until we have another meaninfgul trace
+  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
+  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  4 User Autocommands for "TestGlobalFunction"',
+        \ '  3 function GlobalFunction[1]',
+        \ '  2 CallAFunction[1]',
+        \ '  1 SourceAnotherFile[1]',
+        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
+        \ 'line 9: call File2Function()'])
+
+  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
+  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  6 User Autocommands for "TestGlobalFunction"',
+        \ '  5 function GlobalFunction[1]',
+        \ '  4 CallAFunction[1]',
+        \ '  3 SourceAnotherFile[1]',
+        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
+        \ '  1 function File2Function[1]',
+        \ '->0 DoAThing',
+        \ 'line 1: echo "DoAThing"'])
+
+  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
+  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
+  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  2 User Autocommands for "TestGlobalFunction"',
+        \ '  1 function GlobalFunction[1]',
+        \ '->0 CallAFunction',
+        \ 'line 2: call File2Function()'])
+
+  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  3 User Autocommands for "TestGlobalFunction"',
+        \ '  2 function GlobalFunction[1]',
+        \ '  1 CallAFunction[2]',
+        \ '->0 File2Function',
+        \ 'line 1: call DoAThing()'])
+
+
+  " Now unwind so that we get back to the original autocommand (and the second
+  " cmd echo "Done")
+  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  3 User Autocommands for "TestGlobalFunction"',
+        \ '  2 function GlobalFunction[1]',
+        \ '  1 CallAFunction[2]',
+        \ '->0 File2Function',
+        \ 'line 1: End of function'])
+
+  call RunDbgCmd(buf, 'finish', ['line 2: End of function'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  2 User Autocommands for "TestGlobalFunction"',
+        \ '  1 function GlobalFunction[1]',
+        \ '->0 CallAFunction',
+        \ 'line 2: End of function'])
+
+  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  1 User Autocommands for "TestGlobalFunction"',
+        \ '->0 function GlobalFunction',
+        \ 'line 1: End of function'])
+
+  call RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '->0 User Autocommands for "TestGlobalFunction"',
+        \ 'cmd: echo "Done"'])
+
+  call StopVimInTerminal(buf)
+  call delete('Xtest1.vim')
+  call delete('Xtest2.vim')
+endfunc
+
+func Test_Backtrace_CmdLine()
+  CheckRunVimInTerminal
+
+  let file1 =<< trim END
+    func SourceAnotherFile()
+      source Xtest2.vim
+    endfunc
+
+    func CallAFunction()
+      call SourceAnotherFile()
+      call File2Function()
+    endfunc
+
+    func GlobalFunction()
+      call CallAFunction()
+    endfunc
+
+    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
+  END
+  call writefile(file1, 'Xtest1.vim')
+
+  let file2 =<< trim END
+    func DoAThing()
+      echo "DoAThing"
+    endfunc
+
+    func File2Function()
+      call DoAThing()
+    endfunc
+
+    call File2Function()
+  END
+  call writefile(file2, 'Xtest2.vim')
+
+  let buf = RunVimInTerminal(
+        \ '-S Xtest1.vim -c "debug call GlobalFunction()"',
+        \ {'wait_for_ruler': 0})
+
+  " Need to wait for the vim-in-terminal to be ready
+  call CheckDbgOutput(buf, ['command line',
+                            \ 'cmd: call GlobalFunction()'])
+
+  " At this point the ontly thing in the stack is the cmdline
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '->0 command line',
+        \ 'cmd: call GlobalFunction()'])
+
+  " And now we're back into the call stack
+  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '>backtrace',
+        \ '  1 command line',
+        \ '->0 function GlobalFunction',
+        \ 'line 1: call CallAFunction()'])
+
+  call StopVimInTerminal(buf)
+  call delete('Xtest1.vim')
+  call delete('Xtest2.vim')
+endfunc
+
+func Test_Backtrace_DefFunction()
+  CheckRunVimInTerminal
+
+  let file1 =<< trim END
+    vim9script
+    import File2Function from './Xtest2.vim'
+
+    def SourceAnotherFile()
+      source Xtest2.vim
+    enddef
+
+    def CallAFunction()
+      SourceAnotherFile()
+      File2Function()
+    enddef
+
+    def g:GlobalFunction()
+      CallAFunction()
+    enddef
+
+    defcompile
+  END
+  call writefile(file1, 'Xtest1.vim')
+
+  let file2 =<< trim END
+    vim9script
+
+    def DoAThing(): number
+      let a = 100 * 2
+      a += 3
+      return a
+    enddef
+
+    export def File2Function()
+      DoAThing()
+    enddef
+
+    defcompile
+    File2Function()
+  END
+  call writefile(file2, 'Xtest2.vim')
+
+  let buf = RunVimInTerminal('-S Xtest1.vim', {})
+
+  call RunDbgCmd(buf,
+                \ ':debug call GlobalFunction()',
+                \ ['cmd: call GlobalFunction()'])
+
+  " FIXME: Vim9 lines are not debugged!
+  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
+
+  " But they do appear in the backtrace
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '\V>backtrace',
+        \ '\V  2 function GlobalFunction[1]',
+        \ '\V  1 <SNR>\.\*_CallAFunction[1]',
+        \ '\V->0 <SNR>\.\*_SourceAnotherFile',
+        \ '\Vline 1: source Xtest2.vim'],
+        \ #{match: 'pattern'})
+
+
+  call RunDbgCmd(buf, 'step', ['line 1: vim9script'])
+  call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
+  call RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
+  call RunDbgCmd(buf, 'step', ['line 9: def File2Function()'])
+  call RunDbgCmd(buf, 'step', ['line 13: defcompile'])
+  call RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '\V>backtrace',
+        \ '\V  3 function GlobalFunction[1]',
+        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
+        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
+        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
+        \ '\Vline 14: File2Function()'],
+        \ #{match: 'pattern'})
+
+  " Don't step into compiled functions...
+  call RunDbgCmd(buf, 'step', ['line 15: End of sourced file'])
+  call RunDbgCmd(buf, 'backtrace', [
+        \ '\V>backtrace',
+        \ '\V  3 function GlobalFunction[1]',
+        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
+        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
+        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
+        \ '\Vline 15: End of sourced file'],
+        \ #{match: 'pattern'})
+
+
+  call StopVimInTerminal(buf)
+  call delete('Xtest1.vim')
+  call delete('Xtest2.vim')
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1304,
+/**/
     1303,
 /**/
     1302,