summaryrefslogtreecommitdiffstats
path: root/src/testdir/test_terminal.vim
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 08:50:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 08:50:31 +0000
commitaed8ce9da277f5ecffe968b324f242c41c3b752a (patch)
treed2e538394cb7a8a7c42a4aac6ccf1a8e3256999b /src/testdir/test_terminal.vim
parentInitial commit. (diff)
downloadvim-aed8ce9da277f5ecffe968b324f242c41c3b752a.tar.xz
vim-aed8ce9da277f5ecffe968b324f242c41c3b752a.zip
Adding upstream version 2:9.0.1378.upstream/2%9.0.1378upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/testdir/test_terminal.vim')
-rw-r--r--src/testdir/test_terminal.vim2353
1 files changed, 2353 insertions, 0 deletions
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
new file mode 100644
index 0000000..b0c29ec
--- /dev/null
+++ b/src/testdir/test_terminal.vim
@@ -0,0 +1,2353 @@
+" Tests for the terminal window.
+" This is split in two, because it can take a lot of time.
+" See test_terminal2.vim and test_terminal3.vim for further tests.
+
+source check.vim
+CheckFeature terminal
+
+source shared.vim
+source screendump.vim
+source mouse.vim
+source term_util.vim
+
+let s:python = PythonProg()
+let $PROMPT_COMMAND=''
+
+func Test_terminal_basic()
+ call test_override('vterm_title', 1)
+ au TerminalOpen * let b:done = 'yes'
+ let buf = Run_shell_in_terminal({})
+
+ call assert_equal('t', mode())
+ call assert_equal('yes', b:done)
+ call assert_match('%aR[^\n]*running]', execute('ls'))
+ call assert_match('%aR[^\n]*running]', execute('ls R'))
+ call assert_notmatch('%[^\n]*running]', execute('ls F'))
+ call assert_notmatch('%[^\n]*running]', execute('ls ?'))
+ call assert_fails('set modifiable', 'E946:')
+
+ call StopShellInTerminal(buf)
+ call assert_equal('n', mode())
+ call assert_match('%aF[^\n]*finished]', execute('ls'))
+ call assert_match('%aF[^\n]*finished]', execute('ls F'))
+ call assert_notmatch('%[^\n]*finished]', execute('ls R'))
+ call assert_notmatch('%[^\n]*finished]', execute('ls ?'))
+
+ " closing window wipes out the terminal buffer a with finished job
+ close
+ call assert_equal("", bufname(buf))
+
+ au! TerminalOpen
+ call test_override('ALL', 0)
+ unlet g:job
+endfunc
+
+func Test_terminal_no_name()
+ let buf = Run_shell_in_terminal({})
+ call assert_match('^!', bufname(buf))
+ 0file
+ call assert_equal("", bufname(buf))
+ call assert_match('\[No Name\]', execute('file'))
+ call StopShellInTerminal(buf)
+endfunc
+
+func Test_terminal_TerminalWinOpen()
+ au TerminalWinOpen * let b:done = 'yes'
+ let buf = Run_shell_in_terminal({})
+ call assert_equal('yes', b:done)
+ call StopShellInTerminal(buf)
+ " closing window wipes out the terminal buffer with the finished job
+ close
+
+ if has("unix")
+ terminal ++hidden ++open sleep 1
+ sleep 1
+ call assert_fails("echo b:done", 'E121:')
+ endif
+
+ au! TerminalWinOpen
+endfunc
+
+func Test_terminal_make_change()
+ let buf = Run_shell_in_terminal({})
+ call StopShellInTerminal(buf)
+
+ setlocal modifiable
+ exe "normal Axxx\<Esc>"
+ call assert_fails(buf . 'bwipe', 'E89:')
+ undo
+
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_paste_register()
+ let @" = "text to paste"
+
+ let buf = Run_shell_in_terminal({})
+ " Wait for the shell to display a prompt
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+
+ call feedkeys("echo \<C-W>\"\" \<C-W>\"=37 + 5\<CR>\<CR>", 'xt')
+ call WaitForAssert({-> assert_match("echo text to paste 42$", getline(1))})
+ call WaitForAssert({-> assert_equal('text to paste 42', 2->getline())})
+
+ exe buf . 'bwipe!'
+ unlet g:job
+endfunc
+
+func Test_terminal_unload_buffer()
+ let buf = Run_shell_in_terminal({})
+ call assert_fails(buf . 'bunload', 'E948:')
+ exe buf . 'bunload!'
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+func Test_terminal_wipe_buffer()
+ let buf = Run_shell_in_terminal({})
+ call assert_fails(buf . 'bwipe', 'E948:')
+ exe buf . 'bwipe!'
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+" Test that using ':confirm bwipe' on terminal works
+func Test_terminal_confirm_wipe_buffer()
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ let buf = Run_shell_in_terminal({})
+ call assert_fails(buf . 'bwipe', 'E948:')
+ call feedkeys('n', 'L')
+ call assert_fails('confirm ' .. buf .. 'bwipe', 'E517:')
+ call assert_equal(buf, bufnr())
+ call assert_equal(1, &modified)
+ call feedkeys('y', 'L')
+ exe 'confirm ' .. buf .. 'bwipe'
+ call assert_notequal(buf, bufnr())
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+" Test that using :b! will hide the terminal
+func Test_terminal_goto_buffer()
+ let buf_mod = bufnr()
+ let buf_term = Run_shell_in_terminal({})
+ call assert_equal(buf_term, bufnr())
+ call assert_fails(buf_mod . 'b', 'E948:')
+ exe buf_mod . 'b!'
+ call assert_equal(buf_mod, bufnr())
+ call assert_equal('run', job_status(g:job))
+ call assert_notequal('', bufname(buf_term))
+ exec buf_mod .. 'bwipe!'
+ exec buf_term .. 'bwipe!'
+
+ unlet g:job
+endfunc
+
+" Test that using ':confirm :b' will kill terminal
+func Test_terminal_confirm_goto_buffer()
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ let buf_mod = bufnr()
+ let buf_term = Run_shell_in_terminal({})
+ call feedkeys('n', 'L')
+ exe 'confirm ' .. buf_mod .. 'b'
+ call assert_equal(buf_term, bufnr())
+ call feedkeys('y', 'L')
+ exec 'confirm ' .. buf_mod .. 'b'
+ call assert_equal(buf_mod, bufnr())
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf_term))
+ exec buf_mod .. 'bwipe!'
+
+ unlet g:job
+endfunc
+
+" Test that using :close! will hide the terminal
+func Test_terminal_close_win()
+ let buf = Run_shell_in_terminal({})
+ call assert_equal(buf, bufnr())
+ call assert_fails('close', 'E948:')
+ close!
+ call assert_notequal(buf, bufnr())
+ call assert_equal('run', job_status(g:job))
+ call assert_notequal('', bufname(buf))
+ exec buf .. 'bwipe!'
+
+ unlet g:job
+endfunc
+
+" Test that using ':confirm close' will kill terminal
+func Test_terminal_confirm_close_win()
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ let buf = Run_shell_in_terminal({})
+ call feedkeys('n', 'L')
+ confirm close
+ call assert_equal(buf, bufnr())
+ call feedkeys('y', 'L')
+ confirm close
+ call assert_notequal(buf, bufnr())
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+" Test that using :quit! will kill the terminal
+func Test_terminal_quit()
+ let buf = Run_shell_in_terminal({})
+ call assert_equal(buf, bufnr())
+ call assert_fails('quit', 'E948:')
+ quit!
+ call assert_notequal(buf, bufnr())
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+" Test that using ':confirm quit' will kill terminal
+func Test_terminal_confirm_quit()
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ let buf = Run_shell_in_terminal({})
+ call feedkeys('n', 'L')
+ confirm quit
+ call assert_equal(buf, bufnr())
+ call feedkeys('y', 'L')
+ confirm quit
+ call assert_notequal(buf, bufnr())
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+
+ unlet g:job
+endfunc
+
+" Test :q or :next
+
+func Test_terminal_split_quit()
+ let buf = Run_shell_in_terminal({})
+ split
+ quit!
+ call TermWait(buf)
+ sleep 50m
+ call assert_equal('run', job_status(g:job))
+
+ quit!
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+
+ call assert_equal("", bufname(buf))
+ unlet g:job
+endfunc
+
+func Test_terminal_hide_buffer_job_running()
+ let buf = Run_shell_in_terminal({})
+ setlocal bufhidden=hide
+ quit
+ for nr in range(1, winnr('$'))
+ call assert_notequal(winbufnr(nr), buf)
+ endfor
+ call assert_true(bufloaded(buf))
+ call assert_true(buflisted(buf))
+
+ exe 'split ' . buf . 'buf'
+ call StopShellInTerminal(buf)
+ exe buf . 'bwipe'
+
+ unlet g:job
+endfunc
+
+func Test_terminal_hide_buffer_job_finished()
+ term echo hello
+ let buf = bufnr()
+ call WaitForAssert({-> assert_equal('finished', term_getstatus(buf))})
+
+ call assert_true(bufloaded(buf))
+ call assert_true(buflisted(buf))
+
+ " Test :hide
+ hide
+ call assert_true(bufloaded(buf))
+ call assert_true(buflisted(buf))
+ split
+ exe buf .. 'buf'
+ call assert_equal(buf, bufnr())
+
+ " Test bufhidden, which exercises a different code path
+ setlocal bufhidden=hide
+ edit Xasdfasdf
+ call assert_true(bufloaded(buf))
+ call assert_true(buflisted(buf))
+ exe buf .. 'buf'
+ call assert_equal(buf, bufnr())
+ setlocal bufhidden=
+
+ edit Xasdfasdf
+ call assert_false(bufloaded(buf))
+ call assert_false(buflisted(buf))
+ bwipe Xasdfasdf
+endfunc
+
+func Test_terminal_rename_buffer()
+ let cmd = Get_cat_123_cmd()
+ let buf = term_start(cmd, {'term_name': 'foo'})
+ call WaitForAssert({-> assert_equal('finished', term_getstatus(buf))})
+ call assert_equal('foo', bufname())
+ call assert_match('foo.*finished', execute('ls'))
+ file bar
+ call assert_equal('bar', bufname())
+ call assert_match('bar.*finished', execute('ls'))
+ exe 'bwipe! ' .. buf
+endfunc
+
+func s:Nasty_exit_cb(job, st)
+ exe g:buf . 'bwipe!'
+ let g:buf = 0
+endfunc
+
+func Get_cat_123_cmd()
+ if has('win32')
+ if !has('conpty')
+ return 'cmd /c "cls && color 2 && echo 123"'
+ else
+ " When clearing twice, extra sequence is not output.
+ return 'cmd /c "cls && cls && color 2 && echo 123"'
+ endif
+ else
+ call writefile(["\<Esc>[32m123"], 'Xtext')
+ return "cat Xtext"
+ endif
+endfunc
+
+func Test_terminal_nasty_cb()
+ let cmd = Get_cat_123_cmd()
+ let g:buf = term_start(cmd, {'exit_cb': function('s:Nasty_exit_cb')})
+ let g:job = term_getjob(g:buf)
+
+ call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
+ call WaitForAssert({-> assert_equal(0, g:buf)})
+ unlet g:job
+ unlet g:buf
+ call delete('Xtext')
+endfunc
+
+func Check_123(buf)
+ let l = term_scrape(a:buf, 0)
+ call assert_true(len(l) == 0)
+ let l = term_scrape(a:buf, 999)
+ call assert_true(len(l) == 0)
+ let l = a:buf->term_scrape(1)
+ call assert_true(len(l) > 0)
+ call assert_equal('1', l[0].chars)
+ call assert_equal('2', l[1].chars)
+ call assert_equal('3', l[2].chars)
+ call assert_equal('#00e000', l[0].fg)
+ call assert_equal(0, term_getattr(l[0].attr, 'bold'))
+ call assert_equal(0, l[0].attr->term_getattr('italic'))
+ if has('win32')
+ " On Windows 'background' always defaults to dark, even though the terminal
+ " may use a light background. Therefore accept both white and black.
+ call assert_match('#ffffff\|#000000', l[0].bg)
+ else
+ if &background == 'light'
+ call assert_equal('#ffffff', l[0].bg)
+ else
+ call assert_equal('#000000', l[0].bg)
+ endif
+ endif
+
+ let l = term_getline(a:buf, -1)
+ call assert_equal('', l)
+ let l = term_getline(a:buf, 0)
+ call assert_equal('', l)
+ let l = term_getline(a:buf, 999)
+ call assert_equal('', l)
+ let l = term_getline(a:buf, 1)
+ call assert_equal('123', l)
+endfunc
+
+func Test_terminal_scrape_123()
+ let cmd = Get_cat_123_cmd()
+ let buf = term_start(cmd)
+
+ let termlist = term_list()
+ call assert_equal(1, len(termlist))
+ call assert_equal(buf, termlist[0])
+
+ " Nothing happens with invalid buffer number
+ call term_wait(1234)
+
+ call TermWait(buf)
+ " On MS-Windows we first get a startup message of two lines, wait for the
+ " "cls" to happen, after that we have one line with three characters.
+ call WaitForAssert({-> assert_equal(3, len(term_scrape(buf, 1)))})
+ call Check_123(buf)
+
+ " Must still work after the job ended.
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("dead", job_status(job))})
+ call TermWait(buf)
+ call Check_123(buf)
+
+ exe buf . 'bwipe'
+ call delete('Xtext')
+endfunc
+
+func Test_terminal_scrape_multibyte()
+ call writefile(["léttまrs"], 'Xtext', 'D')
+ if has('win32')
+ " Run cmd with UTF-8 codepage to make the type command print the expected
+ " multibyte characters.
+ let buf = term_start("cmd /K chcp 65001")
+ call term_sendkeys(buf, "type Xtext\<CR>")
+ eval buf->term_sendkeys("exit\<CR>")
+ let line = 4
+ else
+ let buf = term_start("cat Xtext")
+ let line = 1
+ endif
+
+ call WaitFor({-> len(term_scrape(buf, line)) >= 7 && term_scrape(buf, line)[0].chars == "l"})
+ let l = term_scrape(buf, line)
+ call assert_true(len(l) >= 7)
+ call assert_equal('l', l[0].chars)
+ call assert_equal('é', l[1].chars)
+ call assert_equal(1, l[1].width)
+ call assert_equal('t', l[2].chars)
+ call assert_equal('t', l[3].chars)
+ call assert_equal('ま', l[4].chars)
+ call assert_equal(2, l[4].width)
+ call assert_equal('r', l[5].chars)
+ call assert_equal('s', l[6].chars)
+
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("dead", job_status(job))})
+ call TermWait(buf)
+
+ exe buf . 'bwipe'
+endfunc
+
+func Test_terminal_one_column()
+ " This creates a terminal, displays a double-wide character and makes the
+ " window one column wide. This used to cause a crash.
+ let width = &columns
+ botright vert term
+ let buf = bufnr('$')
+ call TermWait(buf, 100)
+ exe "set columns=" .. (width / 2)
+ redraw
+ call term_sendkeys(buf, "キ")
+ call TermWait(buf, 10)
+ exe "set columns=" .. width
+ exe buf . 'bwipe!'
+endfunc
+
+func Test_terminal_scroll()
+ call writefile(range(1, 200), 'Xtext', 'D')
+ if has('win32')
+ let cmd = 'cmd /c "type Xtext"'
+ else
+ let cmd = "cat Xtext"
+ endif
+ let buf = term_start(cmd)
+
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("dead", job_status(job))})
+ call TermWait(buf)
+
+ " wait until the scrolling stops
+ while 1
+ let scrolled = buf->term_getscrolled()
+ sleep 20m
+ if scrolled == buf->term_getscrolled()
+ break
+ endif
+ endwhile
+
+ call assert_equal('1', getline(1))
+ call assert_equal('1', term_getline(buf, 1 - scrolled))
+ call assert_equal('49', getline(49))
+ call assert_equal('49', term_getline(buf, 49 - scrolled))
+ call assert_equal('200', getline(200))
+ call assert_equal('200', term_getline(buf, 200 - scrolled))
+
+ exe buf . 'bwipe'
+endfunc
+
+func Test_terminal_scrollback()
+ let buf = Run_shell_in_terminal({'term_rows': 15})
+ set termwinscroll=100
+ call writefile(range(150), 'Xtext', 'D')
+ if has('win32')
+ call term_sendkeys(buf, "type Xtext\<CR>")
+ else
+ call term_sendkeys(buf, "cat Xtext\<CR>")
+ endif
+ let rows = term_getsize(buf)[0]
+ " On MS-Windows there is an empty line, check both last line and above it.
+ call WaitForAssert({-> assert_match( '149', term_getline(buf, rows - 1) . term_getline(buf, rows - 2))})
+ let lines = line('$')
+ call assert_inrange(91, 100, lines)
+
+ call StopShellInTerminal(buf)
+ exe buf . 'bwipe'
+ set termwinscroll&
+endfunc
+
+func Test_terminal_postponed_scrollback()
+ " tail -f only works on Unix
+ CheckUnix
+
+ call writefile(range(50), 'Xtext', 'D')
+ call writefile([
+ \ 'set shell=/bin/sh noruler',
+ \ 'terminal',
+ \ 'sleep 200m',
+ \ 'call feedkeys("tail -n 100 -f Xtext\<CR>", "xt")',
+ \ 'sleep 100m',
+ \ 'call feedkeys("\<C-W>N", "xt")',
+ \ ], 'XTest_postponed', 'D')
+ let buf = RunVimInTerminal('-S XTest_postponed', {})
+ " Check that the Xtext lines are displayed and in Terminal-Normal mode
+ call VerifyScreenDump(buf, 'Test_terminal_scrollback_1', {})
+
+ silent !echo 'one more line' >>Xtext
+ " Screen will not change, move cursor to get a different dump
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_terminal_scrollback_2', {})
+
+ " Back to Terminal-Job mode, text will scroll and show the extra line.
+ call term_sendkeys(buf, "a")
+ call VerifyScreenDump(buf, 'Test_terminal_scrollback_3', {})
+
+ " stop "tail -f"
+ call term_sendkeys(buf, "\<C-C>")
+ call TermWait(buf, 25)
+ " stop shell
+ call term_sendkeys(buf, "exit\<CR>")
+ call TermWait(buf, 50)
+ " close terminal window
+ let tsk_ret = term_sendkeys(buf, ":q\<CR>")
+
+ " check type of term_sendkeys() return value
+ echo type(tsk_ret)
+
+ call StopVimInTerminal(buf)
+endfunc
+
+" Run diff on two dumps with different size.
+func Test_terminal_dumpdiff_size()
+ call assert_equal(1, winnr('$'))
+ call term_dumpdiff('dumps/Test_incsearch_search_01.dump', 'dumps/Test_popup_command_01.dump')
+ call assert_equal(2, winnr('$'))
+ call assert_match('Test_incsearch_search_01.dump', getline(10))
+ call assert_match(' +++++$', getline(11))
+ call assert_match('Test_popup_command_01.dump', getline(31))
+ call assert_equal(repeat('+', 75), getline(30))
+ quit
+endfunc
+
+func Test_terminal_size()
+ let cmd = Get_cat_123_cmd()
+
+ exe 'terminal ++rows=5 ' . cmd
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal(5, size[0])
+
+ call term_start(cmd, {'term_rows': 6})
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal(6, size[0])
+
+ vsplit
+ exe 'terminal ++rows=5 ++cols=33 ' . cmd
+ call assert_equal([5, 33], ''->term_getsize())
+
+ call term_setsize('', 6, 0)
+ call assert_equal([6, 33], term_getsize(''))
+
+ eval ''->term_setsize(0, 35)
+ call assert_equal([6, 35], term_getsize(''))
+
+ call term_setsize('', 7, 30)
+ call assert_equal([7, 30], term_getsize(''))
+
+ bwipe!
+ call assert_fails("call term_setsize('', 7, 30)", "E955:")
+
+ call term_start(cmd, {'term_rows': 6, 'term_cols': 36})
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal([6, 36], size)
+
+ exe 'vertical terminal ++cols=20 ' . cmd
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal(20, size[1])
+
+ eval cmd->term_start({'vertical': 1, 'term_cols': 26})
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal(26, size[1])
+
+ split
+ exe 'vertical terminal ++rows=6 ++cols=20 ' . cmd
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal([6, 20], size)
+
+ call term_start(cmd, {'vertical': 1, 'term_rows': 7, 'term_cols': 27})
+ let size = term_getsize('')
+ bwipe!
+ call assert_equal([7, 27], size)
+
+ call assert_fails("call term_start(cmd, {'term_rows': -1})", 'E475:')
+ call assert_fails("call term_start(cmd, {'term_rows': 1001})", 'E475:')
+ call assert_fails("call term_start(cmd, {'term_rows': 10.0})", 'E805:')
+
+ call delete('Xtext')
+endfunc
+
+func Test_terminal_zero_height()
+ split
+ wincmd j
+ anoremenu 1.1 WinBar.test :
+ terminal ++curwin
+ wincmd k
+ wincmd _
+ redraw
+
+ call term_sendkeys(bufnr(), "exit\r")
+ bwipe!
+endfunc
+
+func Test_terminal_curwin()
+ let cmd = Get_cat_123_cmd()
+ call assert_equal(1, winnr('$'))
+
+ split Xdummy
+ call setline(1, 'dummy')
+ write
+ call assert_equal(1, getbufinfo('Xdummy')[0].loaded)
+ exe 'terminal ++curwin ' . cmd
+ call assert_equal(2, winnr('$'))
+ call assert_equal(0, getbufinfo('Xdummy')[0].loaded)
+ bwipe!
+
+ split Xdummy
+ call term_start(cmd, {'curwin': 1})
+ call assert_equal(2, winnr('$'))
+ bwipe!
+
+ split Xdummy
+ call setline(1, 'change')
+ call assert_fails('terminal ++curwin ' . cmd, 'E37:')
+ call assert_equal(2, winnr('$'))
+ exe 'terminal! ++curwin ' . cmd
+ call assert_equal(2, winnr('$'))
+ bwipe!
+
+ split Xdummy
+ call setline(1, 'change')
+ call assert_fails("call term_start(cmd, {'curwin': 1})", 'E37:')
+ call assert_equal(2, winnr('$'))
+ bwipe!
+
+ split Xdummy
+ bwipe!
+ call delete('Xtext')
+ call delete('Xdummy')
+endfunc
+
+func s:get_sleep_cmd()
+ if s:python != ''
+ let cmd = s:python . " test_short_sleep.py"
+ " 500 was not enough for Travis
+ let waittime = 900
+ else
+ echo 'This will take five seconds...'
+ let waittime = 2000
+ if has('win32')
+ let cmd = $windir . '\system32\timeout.exe 1'
+ else
+ let cmd = 'sleep 1'
+ endif
+ endif
+ return [cmd, waittime]
+endfunc
+
+func Test_terminal_finish_open_close()
+ call assert_equal(1, winnr('$'))
+
+ let [cmd, waittime] = s:get_sleep_cmd()
+
+ " shell terminal closes automatically
+ terminal
+ let buf = bufnr('%')
+ call assert_equal(2, winnr('$'))
+ " Wait for the shell to display a prompt
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+ call StopShellInTerminal(buf)
+ call WaitForAssert({-> assert_equal(1, winnr('$'))}, waittime)
+
+ " shell terminal that does not close automatically
+ terminal ++noclose
+ let buf = bufnr('%')
+ call assert_equal(2, winnr('$'))
+ " Wait for the shell to display a prompt
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+ call StopShellInTerminal(buf)
+ call assert_equal(2, winnr('$'))
+ quit
+ call assert_equal(1, winnr('$'))
+
+ exe 'terminal ++close ' . cmd
+ call assert_equal(2, winnr('$'))
+ wincmd p
+ call WaitForAssert({-> assert_equal(1, winnr('$'))}, waittime)
+
+ call term_start(cmd, {'term_finish': 'close'})
+ call assert_equal(2, winnr('$'))
+ wincmd p
+ call WaitForAssert({-> assert_equal(1, winnr('$'))}, waittime)
+ call assert_equal(1, winnr('$'))
+
+ exe 'terminal ++open ' . cmd
+ close!
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ bwipe
+
+ call term_start(cmd, {'term_finish': 'open'})
+ close!
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ bwipe
+
+ exe 'terminal ++hidden ++open ' . cmd
+ call assert_equal(1, winnr('$'))
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ bwipe
+
+ call term_start(cmd, {'term_finish': 'open', 'hidden': 1})
+ call assert_equal(1, winnr('$'))
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ bwipe
+
+ call assert_fails("call term_start(cmd, {'term_opencmd': 'open'})", 'E475:')
+ call assert_fails("call term_start(cmd, {'term_opencmd': 'split %x'})", 'E475:')
+ call assert_fails("call term_start(cmd, {'term_opencmd': 'split %d and %s'})", 'E475:')
+ call assert_fails("call term_start(cmd, {'term_opencmd': 'split % and %d'})", 'E475:')
+
+ call term_start(cmd, {'term_finish': 'open', 'term_opencmd': '4split | buffer %d | let g:result = "opened the buffer in a window"'})
+ close!
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ call assert_equal(4, winheight(0))
+ call assert_equal('opened the buffer in a window', g:result)
+ unlet g:result
+ bwipe
+endfunc
+
+func Test_terminal_cwd()
+ if has('win32')
+ let cmd = 'cmd /c cd'
+ else
+ CheckExecutable pwd
+ let cmd = 'pwd'
+ endif
+ call mkdir('Xtermdir')
+ let buf = term_start(cmd, {'cwd': 'Xtermdir'})
+ " if the path is very long it may be split over two lines, join them
+ " together
+ call WaitForAssert({-> assert_equal('Xtermdir', fnamemodify(getline(1) .. getline(2), ":t"))})
+
+ exe buf . 'bwipe'
+ call delete('Xtermdir', 'rf')
+endfunc
+
+func Test_terminal_cwd_failure()
+ " Case 1: Provided directory is not actually a directory. Attempt to make
+ " the file executable as well.
+ call writefile([], 'Xtcfile', 'D')
+ call setfperm('Xtcfile', 'rwx------')
+ call assert_fails("call term_start(&shell, {'cwd': 'Xtcfile'})", 'E475:')
+
+ " Case 2: Directory does not exist.
+ call assert_fails("call term_start(&shell, {'cwd': 'Xdir'})", 'E475:')
+
+ " Case 3: Directory exists but is not accessible.
+ " Skip this for root, it will be accessible anyway.
+ if !IsRoot()
+ call mkdir('XdirNoAccess', '', '0600')
+ " return early if the directory permissions could not be set properly
+ if getfperm('XdirNoAccess')[2] == 'x'
+ call delete('XdirNoAccess', 'rf')
+ return
+ endif
+ call assert_fails("call term_start(&shell, {'cwd': 'XdirNoAccess'})", 'E475:')
+ call delete('XdirNoAccess', 'rf')
+ endif
+endfunc
+
+func Test_terminal_servername()
+ CheckFeature clientserver
+ call s:test_environment("VIM_SERVERNAME", v:servername)
+endfunc
+
+func Test_terminal_version()
+ call s:test_environment("VIM_TERMINAL", string(v:version))
+endfunc
+
+func s:test_environment(name, value)
+ let buf = Run_shell_in_terminal({})
+ " Wait for the shell to display a prompt
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+ if has('win32')
+ call term_sendkeys(buf, "echo %" . a:name . "%\r")
+ else
+ call term_sendkeys(buf, "echo $" . a:name . "\r")
+ endif
+ call TermWait(buf)
+ call StopShellInTerminal(buf)
+ call WaitForAssert({-> assert_equal(a:value, getline(2))})
+
+ exe buf . 'bwipe'
+ unlet buf
+endfunc
+
+func Test_terminal_env()
+ let buf = Run_shell_in_terminal({'env': {'TESTENV': 'correct'}})
+ " Wait for the shell to display a prompt
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+ if has('win32')
+ call term_sendkeys(buf, "echo %TESTENV%\r")
+ else
+ call term_sendkeys(buf, "echo $TESTENV\r")
+ endif
+ eval buf->TermWait()
+ call StopShellInTerminal(buf)
+ call WaitForAssert({-> assert_equal('correct', getline(2))})
+
+ exe buf . 'bwipe'
+endfunc
+
+func Test_terminal_list_args()
+ let buf = term_start([&shell, &shellcmdflag, 'echo "123"'])
+ call assert_fails(buf . 'bwipe', 'E948:')
+ exe buf . 'bwipe!'
+ call assert_equal("", bufname(buf))
+endfunction
+
+func Test_terminal_noblock()
+ let g:test_is_flaky = 1
+ let buf = term_start(&shell)
+ " Starting a terminal can be slow, esp. on busy CI machines.
+ let wait_time = 7500
+ let letters = 'abcdefghijklmnopqrstuvwxyz'
+ if has('bsd') || has('mac') || has('sun')
+ " The shell or something else has a problem dealing with more than 1000
+ " characters at the same time. It's very slow too.
+ let len = 1000
+ let wait_time = 15000
+ let letters = 'abcdefghijklm'
+ " NPFS is used in Windows, nonblocking mode does not work properly.
+ elseif has('win32')
+ let len = 1
+ else
+ let len = 5000
+ endif
+
+ " Send a lot of text lines, should be buffered properly.
+ for c in split(letters, '\zs')
+ call term_sendkeys(buf, 'echo ' . repeat(c, len) . "\<cr>")
+ endfor
+ call term_sendkeys(buf, "echo done\<cr>")
+
+ " On MS-Windows there is an extra empty line below "done". Find "done" in
+ " the last-but-one or the last-but-two line.
+ let lnum = term_getsize(buf)[0] - 1
+ call WaitForAssert({-> assert_match('done', term_getline(buf, lnum - 1) .. '//' .. term_getline(buf, lnum))}, wait_time)
+ let line = term_getline(buf, lnum)
+ if line !~ 'done'
+ let line = term_getline(buf, lnum - 1)
+ endif
+ call assert_match('done', line)
+
+ let g:job = term_getjob(buf)
+ call StopShellInTerminal(buf)
+ unlet g:job
+ bwipe
+endfunc
+
+func Test_terminal_write_stdin()
+ " TODO: enable once writing to stdin works on MS-Windows
+ CheckNotMSWindows
+ CheckExecutable wc
+ let g:test_is_flaky = 1
+
+ call setline(1, ['one', 'two', 'three'])
+ %term wc
+ call WaitForAssert({-> assert_match('3', getline("$"))})
+ let nrs = split(getline('$'))
+ call assert_equal(['3', '3', '14'], nrs)
+ %bwipe!
+
+ call setline(1, ['one', 'two', 'three', 'four'])
+ 2,3term wc
+ call WaitForAssert({-> assert_match('2', getline("$"))})
+ let nrs = split(getline('$'))
+ call assert_equal(['2', '2', '10'], nrs)
+ %bwipe!
+endfunc
+
+func Test_terminal_eof_arg()
+ call CheckPython(s:python)
+ let g:test_is_flaky = 1
+
+ call setline(1, ['print("hello")'])
+ exe '1term ++eof=exit(123) ' .. s:python
+ " MS-Windows echoes the input, Unix doesn't.
+ if has('win32')
+ call WaitFor({-> getline('$') =~ 'exit(123)'})
+ call assert_equal('hello', getline(line('$') - 1))
+ else
+ call WaitFor({-> getline('$') =~ 'hello'})
+ call assert_equal('hello', getline('$'))
+ endif
+ call assert_equal(123, bufnr()->term_getjob()->job_info().exitval)
+ %bwipe!
+endfunc
+
+func Test_terminal_eof_arg_win32_ctrl_z()
+ CheckMSWindows
+ call CheckPython(s:python)
+ let g:test_is_flaky = 1
+
+ call setline(1, ['print("hello")'])
+ exe '1term ++eof=<C-Z> ' .. s:python
+ call WaitForAssert({-> assert_match('\^Z', getline(line('$') - 1))})
+ call assert_match('\^Z', getline(line('$') - 1))
+ %bwipe!
+endfunc
+
+func Test_terminal_duplicate_eof_arg()
+ call CheckPython(s:python)
+ let g:test_is_flaky = 1
+
+ " Check the last specified ++eof arg is used and does not leak memory.
+ new
+ call setline(1, ['print("hello")'])
+ exe '1term ++eof=<C-Z> ++eof=exit(123) ' .. s:python
+ " MS-Windows echoes the input, Unix doesn't.
+ if has('win32')
+ call WaitFor({-> getline('$') =~ 'exit(123)'})
+ call assert_equal('hello', getline(line('$') - 1))
+ else
+ call WaitFor({-> getline('$') =~ 'hello'})
+ call assert_equal('hello', getline('$'))
+ endif
+ call assert_equal(123, bufnr()->term_getjob()->job_info().exitval)
+ %bwipe!
+endfunc
+
+func Test_terminal_no_cmd()
+ let g:test_is_flaky = 1
+ let buf = term_start('NONE', {})
+ call assert_notequal(0, buf)
+
+ let pty = job_info(term_getjob(buf))['tty_out']
+ call assert_notequal('', pty)
+ if has('gui_running') && !has('win32')
+ " In the GUI job_start() doesn't work, it does not read from the pty.
+ call system('echo "look here" > ' . pty)
+ else
+ " Otherwise using a job works on all systems.
+ call job_start([&shell, &shellcmdflag, 'echo "look here" > ' . pty])
+ endif
+ call WaitForAssert({-> assert_match('look here', term_getline(buf, 1))})
+
+ bwipe!
+endfunc
+
+func Test_terminal_special_chars()
+ " this file name only works on Unix
+ CheckUnix
+
+ call mkdir('Xdir with spaces', 'R')
+ call writefile(['x'], 'Xdir with spaces/quoted"file')
+ term ls Xdir\ with\ spaces/quoted\"file
+ call WaitForAssert({-> assert_match('quoted"file', term_getline('', 1))})
+ " make sure the job has finished
+ call WaitForAssert({-> assert_match('finish', term_getstatus(bufnr()))})
+
+ bwipe
+endfunc
+
+func Test_terminal_wrong_options()
+ call assert_fails('call term_start(&shell, {
+ \ "in_io": "file",
+ \ "in_name": "xxx",
+ \ "out_io": "file",
+ \ "out_name": "xxx",
+ \ "err_io": "file",
+ \ "err_name": "xxx"
+ \ })', 'E474:')
+ call assert_fails('call term_start(&shell, {
+ \ "out_buf": bufnr("%")
+ \ })', 'E474:')
+ call assert_fails('call term_start(&shell, {
+ \ "err_buf": bufnr("%")
+ \ })', 'E474:')
+endfunc
+
+func Test_terminal_redir_file()
+ let g:test_is_flaky = 1
+ let cmd = Get_cat_123_cmd()
+ let buf = term_start(cmd, {'out_io': 'file', 'out_name': 'Xtrfile'})
+ call TermWait(buf)
+ " ConPTY may precede escape sequence. There are things that are not so.
+ if !has('conpty')
+ call WaitForAssert({-> assert_notequal(0, len(readfile("Xtrfile")))})
+ call assert_match('123', readfile('Xtrfile')[0])
+ endif
+ let g:job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
+
+ if has('win32')
+ " On Windows we cannot delete a file being used by a process. When
+ " job_status() returns "dead", the process remains for a short time.
+ " Just wait for a moment.
+ sleep 50m
+ endif
+ call delete('Xtrfile')
+ bwipe
+
+ if has('unix')
+ call writefile(['one line'], 'Xtrfile', 'D')
+ let buf = term_start('cat', {'in_io': 'file', 'in_name': 'Xtrfile'})
+ call TermWait(buf)
+ call WaitForAssert({-> assert_equal('one line', term_getline(buf, 1))})
+ let g:job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ bwipe
+ endif
+endfunc
+
+func TerminalTmap(remap)
+ let buf = Run_shell_in_terminal({})
+ " Wait for the shell to display a prompt
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+ call assert_equal('t', mode())
+
+ if a:remap
+ tmap 123 456
+ else
+ tnoremap 123 456
+ endif
+ " don't use abcde, it's an existing command
+ tmap 456 abxde
+ call assert_equal('456', maparg('123', 't'))
+ call assert_equal('abxde', maparg('456', 't'))
+ call feedkeys("123", 'tx')
+ call WaitForAssert({-> assert_match('abxde\|456', term_getline(buf, term_getcursor(buf)[0]))})
+ let lnum = term_getcursor(buf)[0]
+ if a:remap
+ call assert_match('abxde', term_getline(buf, lnum))
+ else
+ call assert_match('456', term_getline(buf, lnum))
+ endif
+
+ call term_sendkeys(buf, "\r")
+ call StopShellInTerminal(buf)
+
+ tunmap 123
+ tunmap 456
+ call assert_equal('', maparg('123', 't'))
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_tmap()
+ call TerminalTmap(1)
+ call TerminalTmap(0)
+endfunc
+
+func Test_terminal_wall()
+ let buf = Run_shell_in_terminal({})
+ wall
+ call StopShellInTerminal(buf)
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_wqall()
+ let buf = Run_shell_in_terminal({})
+ call assert_fails('wqall', 'E948:')
+ call StopShellInTerminal(buf)
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_composing_unicode()
+ let g:test_is_flaky = 1
+ let save_enc = &encoding
+ set encoding=utf-8
+
+ if has('win32')
+ let cmd = "cmd /K chcp 65001"
+ let lnum = [3, 6, 9]
+ else
+ let cmd = &shell
+ let lnum = [1, 3, 5]
+ endif
+
+ enew
+ let buf = term_start(cmd, {'curwin': 1})
+ let g:job = term_getjob(buf)
+ call WaitFor({-> term_getline(buf, 1) !=# ''}, 1000)
+
+ if has('win32')
+ call assert_equal('cmd', job_info(g:job).cmd[0])
+ else
+ call assert_equal(&shell, job_info(g:job).cmd[0])
+ endif
+
+ " ascii + composing
+ let txt = "a\u0308bc"
+ call term_sendkeys(buf, "echo " . txt)
+ call TermWait(buf, 25)
+ call assert_match("echo " . txt, term_getline(buf, lnum[0]))
+ call term_sendkeys(buf, "\<cr>")
+ call WaitForAssert({-> assert_equal(txt, term_getline(buf, lnum[0] + 1))}, 1000)
+ let l = term_scrape(buf, lnum[0] + 1)
+ call assert_equal("a\u0308", l[0].chars)
+ call assert_equal("b", l[1].chars)
+ call assert_equal("c", l[2].chars)
+
+ " multibyte + composing: がぎぐげご
+ let txt = "\u304b\u3099\u304e\u304f\u3099\u3052\u3053\u3099"
+ call term_sendkeys(buf, "echo " . txt)
+ call TermWait(buf, 25)
+ call assert_match("echo " . txt, term_getline(buf, lnum[1]))
+ call term_sendkeys(buf, "\<cr>")
+ call WaitForAssert({-> assert_equal(txt, term_getline(buf, lnum[1] + 1))}, 1000)
+ let l = term_scrape(buf, lnum[1] + 1)
+ call assert_equal("\u304b\u3099", l[0].chars)
+ call assert_equal(2, l[0].width)
+ call assert_equal("\u304e", l[1].chars)
+ call assert_equal(2, l[1].width)
+ call assert_equal("\u304f\u3099", l[2].chars)
+ call assert_equal(2, l[2].width)
+ call assert_equal("\u3052", l[3].chars)
+ call assert_equal(2, l[3].width)
+ call assert_equal("\u3053\u3099", l[4].chars)
+ call assert_equal(2, l[4].width)
+
+ " \u00a0 + composing
+ let txt = "abc\u00a0\u0308"
+ call term_sendkeys(buf, "echo " . txt)
+ call TermWait(buf, 25)
+ call assert_match("echo " . txt, term_getline(buf, lnum[2]))
+ call term_sendkeys(buf, "\<cr>")
+ call WaitForAssert({-> assert_equal(txt, term_getline(buf, lnum[2] + 1))}, 1000)
+ let l = term_scrape(buf, lnum[2] + 1)
+ call assert_equal("\u00a0\u0308", l[3].chars)
+
+ call term_sendkeys(buf, "exit\r")
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ bwipe!
+ unlet g:job
+ let &encoding = save_enc
+endfunc
+
+func Test_terminal_aucmd_on_close()
+ fun Nop()
+ let s:called = 1
+ endfun
+
+ aug repro
+ au!
+ au BufWinLeave * call Nop()
+ aug END
+
+ let [cmd, waittime] = s:get_sleep_cmd()
+
+ call assert_equal(1, winnr('$'))
+ new
+ call setline(1, ['one', 'two'])
+ exe 'term ++close ' . cmd
+ wincmd p
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ call assert_equal(1, s:called)
+ bwipe!
+
+ unlet s:called
+ au! repro
+ delfunc Nop
+endfunc
+
+func Test_terminal_term_start_empty_command()
+ let cmd = "call term_start('', {'curwin' : 1, 'term_finish' : 'close'})"
+ call assert_fails(cmd, 'E474:')
+ let cmd = "call term_start('', {'curwin' : 1, 'term_finish' : 'close'})"
+ call assert_fails(cmd, 'E474:')
+ let cmd = "call term_start({}, {'curwin' : 1, 'term_finish' : 'close'})"
+ call assert_fails(cmd, 'E474:')
+ let cmd = "call term_start(0, {'curwin' : 1, 'term_finish' : 'close'})"
+ call assert_fails(cmd, 'E474:')
+ let cmd = "call term_start('', {'term_name' : []})"
+ call assert_fails(cmd, 'E730:')
+ let cmd = "call term_start('', {'term_finish' : 'axby'})"
+ call assert_fails(cmd, 'E475:')
+ let cmd = "call term_start('', {'eof_chars' : []})"
+ call assert_fails(cmd, 'E730:')
+ let cmd = "call term_start('', {'term_kill' : []})"
+ call assert_fails(cmd, 'E730:')
+ let cmd = "call term_start('', {'tty_type' : []})"
+ call assert_fails(cmd, 'E730:')
+ let cmd = "call term_start('', {'tty_type' : 'abc'})"
+ call assert_fails(cmd, 'E475:')
+ let cmd = "call term_start('', {'term_highlight' : []})"
+ call assert_fails(cmd, 'E730:')
+ if has('gui') || has('termguicolors')
+ let cmd = "call term_start('', {'ansi_colors' : 'abc'})"
+ call assert_fails(cmd, 'E475:')
+ let cmd = "call term_start('', {'ansi_colors' : [[]]})"
+ call assert_fails(cmd, 'E730:')
+ let cmd = "call term_start('', {'ansi_colors' : repeat(['blue'], 18)})"
+ if has('gui_running') || has('termguicolors')
+ call assert_fails(cmd, 'E475:')
+ else
+ call assert_fails(cmd, 'E254:')
+ endif
+ endif
+endfunc
+
+func Test_terminal_response_to_control_sequence()
+ CheckUnix
+
+ let buf = Run_shell_in_terminal({})
+ call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
+
+ call term_sendkeys(buf, "cat\<CR>")
+ call WaitForAssert({-> assert_match('cat', term_getline(buf, 1))})
+
+ " Request the cursor position.
+ call term_sendkeys(buf, "\x1b[6n\<CR>")
+
+ " Wait for output from tty to display, below an empty line.
+ call WaitForAssert({-> assert_match('3;1R', term_getline(buf, 4))})
+
+ " End "cat" gently.
+ call term_sendkeys(buf, "\<CR>\<C-D>")
+
+ call StopShellInTerminal(buf)
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+" Run this first, it fails when run after other tests.
+func Test_aa_terminal_focus_events()
+ CheckNotGui
+ CheckUnix
+ CheckRunVimInTerminal
+
+ let save_term = &term
+ let save_ttymouse = &ttymouse
+ set term=xterm ttymouse=xterm2
+
+ let lines =<< trim END
+ set term=xterm ttymouse=xterm2
+ au FocusLost * call setline(1, 'I am lost') | set nomod
+ au FocusGained * call setline(1, 'I am back') | set nomod
+ END
+ call writefile(lines, 'XtermFocus', 'D')
+ let buf = RunVimInTerminal('-S XtermFocus', #{rows: 6})
+
+ " Send a focus event to ourselves, it should be forwarded to the terminal
+ call feedkeys("\<Esc>[O", "Lx!")
+ call VerifyScreenDump(buf, 'Test_terminal_focus_1', {})
+
+ call feedkeys("\<Esc>[I", "Lx!")
+ call VerifyScreenDump(buf, 'Test_terminal_focus_2', {})
+
+ " check that a command line being edited is redrawn in place
+ call term_sendkeys(buf, ":" .. repeat('x', 80))
+ call TermWait(buf)
+ call feedkeys("\<Esc>[O", "Lx!")
+ call VerifyScreenDump(buf, 'Test_terminal_focus_3', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+ let &term = save_term
+ let &ttymouse = save_ttymouse
+endfunc
+
+" Run Vim, start a terminal in that Vim with the kill argument,
+" :qall works.
+func Run_terminal_qall_kill(line1, line2)
+ " 1. Open a terminal window and wait for the prompt to appear
+ " 2. set kill using term_setkill()
+ " 3. make Vim exit, it will kill the shell
+ let after = [
+ \ a:line1,
+ \ 'let buf = bufnr("%")',
+ \ 'while term_getline(buf, 1) =~ "^\\s*$"',
+ \ ' sleep 10m',
+ \ 'endwhile',
+ \ a:line2,
+ \ 'au VimLeavePre * call writefile(["done"], "Xdone")',
+ \ 'qall',
+ \ ]
+ if !RunVim([], after, '')
+ return
+ endif
+ call assert_equal("done", readfile("Xdone")[0])
+ call delete("Xdone")
+endfunc
+
+" Run Vim in a terminal, then start a terminal in that Vim with a kill
+" argument, check that :qall works.
+func Test_terminal_qall_kill_arg()
+ call Run_terminal_qall_kill('term ++kill=kill', '')
+endfunc
+
+" Run Vim, start a terminal in that Vim, set the kill argument with
+" term_setkill(), check that :qall works.
+func Test_terminal_qall_kill_func()
+ call Run_terminal_qall_kill('term', 'eval buf->term_setkill("kill")')
+endfunc
+
+" Run Vim, start a terminal in that Vim without the kill argument,
+" check that :qall does not exit, :qall! does.
+func Test_terminal_qall_exit()
+ let after =<< trim [CODE]
+ term
+ let buf = bufnr("%")
+ while term_getline(buf, 1) =~ "^\\s*$"
+ sleep 10m
+ endwhile
+ set nomore
+ au VimLeavePre * call writefile(["too early"], "Xdone")
+ qall
+ au! VimLeavePre * exe buf . "bwipe!" | call writefile(["done"], "Xdone")
+ cquit
+ [CODE]
+
+ if !RunVim([], after, '')
+ return
+ endif
+ call assert_equal("done", readfile("Xdone")[0])
+ call delete("Xdone")
+endfunc
+
+" Run Vim in a terminal, then start a terminal in that Vim without a kill
+" argument, check that :confirm qall works.
+func Test_terminal_qall_prompt()
+ CheckRunVimInTerminal
+
+ let buf = RunVimInTerminal('', {})
+
+ " the shell may set the window title, we don't want that here
+ call term_sendkeys(buf, ":call test_override('vterm_title', 1)\<CR>")
+
+ " Open a terminal window and wait for the prompt to appear
+ call term_sendkeys(buf, ":term\<CR>")
+ call WaitForAssert({-> assert_match('\[running]', term_getline(buf, 10))})
+ call WaitForAssert({-> assert_notmatch('^\s*$', term_getline(buf, 1))})
+
+ " make Vim exit, it will prompt to kill the shell
+ call term_sendkeys(buf, "\<C-W>:confirm qall\<CR>")
+ call WaitForAssert({-> assert_match('\[Y\]es, (N)o:', term_getline(buf, 20))})
+ call term_sendkeys(buf, "y")
+ call WaitForAssert({-> assert_equal('finished', term_getstatus(buf))})
+
+ " close the terminal window where Vim was running
+ quit
+endfunc
+
+" Run Vim in a terminal, then start a terminal window with a shell and check
+" that Vim exits if it is closed.
+func Test_terminal_exit()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ let winid = win_getid()
+ help
+ term
+ let termid = win_getid()
+ call win_gotoid(winid)
+ close
+ call win_gotoid(termid)
+ END
+ call writefile(lines, 'XtermExit', 'D')
+ let buf = RunVimInTerminal('-S XtermExit', #{rows: 10})
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("run", job_status(job))})
+
+ " quit the shell, it will make Vim exit
+ call term_sendkeys(buf, "exit\<CR>")
+ call WaitForAssert({-> assert_equal("dead", job_status(job))})
+endfunc
+
+func Test_terminal_open_autocmd()
+ augroup repro
+ au!
+ au TerminalOpen * let s:called += 1
+ augroup END
+
+ let s:called = 0
+
+ " Open a terminal window with :terminal
+ terminal
+ call assert_equal(1, s:called)
+ bwipe!
+
+ " Open a terminal window with term_start()
+ call term_start(&shell)
+ call assert_equal(2, s:called)
+ bwipe!
+
+ " Open a hidden terminal buffer with :terminal
+ terminal ++hidden
+ call assert_equal(3, s:called)
+ for buf in term_list()
+ exe buf . "bwipe!"
+ endfor
+
+ " Open a hidden terminal buffer with term_start()
+ let buf = term_start(&shell, {'hidden': 1})
+ call assert_equal(4, s:called)
+ exe buf . "bwipe!"
+
+ unlet s:called
+ au! repro
+endfunc
+
+func Test_open_term_from_cmd()
+ CheckUnix
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, ['a', 'b', 'c'])
+ 3
+ set incsearch
+ cnoremap <F3> <Cmd>call term_start(['/bin/sh', '-c', ':'])<CR>
+ END
+ call writefile(lines, 'Xopenterm', 'D')
+ let buf = RunVimInTerminal('-S Xopenterm', {})
+
+ " this opens a window, incsearch should not use the old cursor position
+ call term_sendkeys(buf, "/\<F3>")
+ call VerifyScreenDump(buf, 'Test_terminal_from_cmd', {})
+ call term_sendkeys(buf, "\<Esc>")
+ call term_sendkeys(buf, ":q\<CR>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_combining_double_width()
+ CheckUnix
+ CheckRunVimInTerminal
+
+ call writefile(["\xe3\x83\x9b\xe3\x82\x9a"], 'Xonedouble', 'D')
+ let lines =<< trim END
+ call term_start(['/bin/sh', '-c', 'cat Xonedouble'])
+ END
+ call writefile(lines, 'Xcombining', 'D')
+ let buf = RunVimInTerminal('-S Xcombining', #{rows: 9})
+
+ " this opens a window, incsearch should not use the old cursor position
+ call VerifyScreenDump(buf, 'Test_terminal_combining', {})
+ call term_sendkeys(buf, ":q\<CR>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_terminal_popup_with_cmd()
+ " this was crashing
+ let buf = term_start(&shell, #{hidden: v:true})
+ let s:winid = popup_create(buf, {})
+ tnoremap <F3> <Cmd>call popup_close(s:winid)<CR>
+ call feedkeys("\<F3>", 'xt')
+
+ tunmap <F3>
+ exe 'bwipe! ' .. buf
+ unlet s:winid
+endfunc
+
+func Test_terminal_popup_bufload()
+ let termbuf = term_start(&shell, #{hidden: v:true, term_finish: 'close'})
+ let winid = popup_create(termbuf, {})
+ sleep 50m
+
+ let newbuf = bufadd('')
+ call bufload(newbuf)
+ call setbufline(newbuf, 1, 'foobar')
+
+ " must not have switched to another window
+ call assert_equal(winid, win_getid())
+
+ call StopShellInTerminal(termbuf)
+ call WaitFor({-> win_getid() != winid})
+ exe 'bwipe! ' .. newbuf
+endfunc
+
+func Test_terminal_popup_two_windows()
+ CheckRunVimInTerminal
+ CheckUnix
+
+ " use "sh" instead of "&shell" in the hope it will use a short prompt
+ let lines =<< trim END
+ let termbuf = term_start('sh', #{hidden: v:true, term_finish: 'close'})
+ exe 'buffer ' .. termbuf
+
+ let winid = popup_create(termbuf, #{line: 2, minwidth: 30, border: []})
+ sleep 50m
+
+ call term_sendkeys(termbuf, "echo 'test'")
+ END
+ call writefile(lines, 'XpopupScript', 'D')
+ let buf = RunVimInTerminal('-S XpopupScript', {})
+
+ " typed text appears both in normal window and in popup
+ call WaitForAssert({-> assert_match("echo 'test'", term_getline(buf, 1))})
+ call WaitForAssert({-> assert_match("echo 'test'", term_getline(buf, 3))})
+
+ call term_sendkeys(buf, "\<CR>\<CR>exit\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":q\<CR>")
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_terminal_popup_insert_cmd()
+ CheckUnix
+
+ inoremap <F3> <Cmd>call StartTermInPopup()<CR>
+ func StartTermInPopup()
+ call term_start(['/bin/sh', '-c', 'cat'], #{hidden: v:true, term_finish: 'close'})->popup_create(#{highlight: 'Pmenu'})
+ endfunc
+ call feedkeys("i\<F3>")
+ sleep 10m
+ call assert_equal('n', mode())
+
+ call feedkeys("\<C-D>", 'xt')
+ call WaitFor({-> popup_list() == []})
+ delfunc StartTermInPopup
+ iunmap <F3>
+endfunc
+
+func Check_dump01(off)
+ call assert_equal('one two three four five', trim(getline(a:off + 1)))
+ call assert_equal('~ Select Word', trim(getline(a:off + 7)))
+ call assert_equal(':popup PopUp', trim(getline(a:off + 20)))
+endfunc
+
+func Test_terminal_dumpwrite_composing()
+ CheckRunVimInTerminal
+
+ let save_enc = &encoding
+ set encoding=utf-8
+ call assert_equal(1, winnr('$'))
+
+ let text = " a\u0300 e\u0302 o\u0308"
+ call writefile([text], 'Xcomposing', 'D')
+ let buf = RunVimInTerminal('--cmd "set encoding=utf-8" Xcomposing', {})
+ call WaitForAssert({-> assert_match(text, term_getline(buf, 1))})
+ eval 'Xdump'->term_dumpwrite(buf)
+ let dumpline = readfile('Xdump')[0]
+ call assert_match('|à| |ê| |ö', dumpline)
+
+ call StopVimInTerminal(buf)
+ call delete('Xdump')
+ let &encoding = save_enc
+endfunc
+
+" Tests for failures in the term_dumpwrite() function
+func Test_terminal_dumpwrite_errors()
+ CheckRunVimInTerminal
+ call assert_fails("call term_dumpwrite({}, 'Xtest.dump')", 'E728:')
+ let buf = RunVimInTerminal('', {})
+ call TermWait(buf)
+ call assert_fails("call term_dumpwrite(buf, 'Xtest.dump', '')", 'E1206:')
+ call assert_fails("call term_dumpwrite(buf, [])", 'E730:')
+ call writefile([], 'Xtest.dump')
+ call assert_fails("call term_dumpwrite(buf, 'Xtest.dump')", 'E953:')
+ call delete('Xtest.dump')
+ call assert_fails("call term_dumpwrite(buf, '')", 'E482:')
+ call assert_fails("call term_dumpwrite(buf, test_null_string())", 'E482:')
+ call test_garbagecollect_now()
+ call StopVimInTerminal(buf, 0)
+ call TermWait(buf)
+ call assert_fails("call term_dumpwrite(buf, 'Xtest.dump')", 'E958:')
+ call assert_fails('call term_sendkeys([], ":q\<CR>")', 'E745:')
+ call assert_equal(0, term_sendkeys(buf, ":q\<CR>"))
+endfunc
+
+" just testing basic functionality.
+func Test_terminal_dumpload()
+ let curbuf = winbufnr('')
+ call assert_equal(1, winnr('$'))
+ let buf = term_dumpload('dumps/Test_popup_command_01.dump')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(20, line('$'))
+ call Check_dump01(0)
+
+ " Load another dump in the same window
+ let buf2 = 'dumps/Test_diff_01.dump'->term_dumpload({'bufnr': buf})
+ call assert_equal(buf, buf2)
+ call assert_notequal('one two three four five', trim(getline(1)))
+
+ " Load the first dump again in the same window
+ let buf2 = term_dumpload('dumps/Test_popup_command_01.dump', {'bufnr': buf})
+ call assert_equal(buf, buf2)
+ call Check_dump01(0)
+
+ call assert_fails("call term_dumpload('dumps/Test_popup_command_01.dump', {'bufnr': curbuf})", 'E475:')
+ call assert_fails("call term_dumpload('dumps/Test_popup_command_01.dump', {'bufnr': 9999})", 'E86:')
+ new
+ let closedbuf = winbufnr('')
+ quit
+ call assert_fails("call term_dumpload('dumps/Test_popup_command_01.dump', {'bufnr': closedbuf})", 'E475:')
+ call assert_fails('call term_dumpload([])', 'E730:')
+ call assert_fails('call term_dumpload("xabcy.dump")', 'E485:')
+
+ quit
+endfunc
+
+func Test_terminal_dumpload_dump()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call term_dumpload('dumps/Test_popupwin_22.dump', #{term_rows: 12})
+ END
+ call writefile(lines, 'XtermDumpload', 'D')
+ let buf = RunVimInTerminal('-S XtermDumpload', #{rows: 15})
+ call VerifyScreenDump(buf, 'Test_terminal_dumpload', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_terminal_dumpdiff()
+ call assert_equal(1, winnr('$'))
+ eval 'dumps/Test_popup_command_01.dump'->term_dumpdiff('dumps/Test_popup_command_02.dump')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(62, line('$'))
+ call Check_dump01(0)
+ call Check_dump01(42)
+ call assert_equal(' bbbbbbbbbbbbbbbbbb ', getline(26)[0:29])
+ quit
+
+ call assert_fails('call term_dumpdiff("X1.dump", [])', 'E730:')
+ call assert_fails('call term_dumpdiff("X1.dump", "X2.dump")', 'E485:')
+ call writefile([], 'X1.dump', 'D')
+ call assert_fails('call term_dumpdiff("X1.dump", "X2.dump")', 'E485:')
+endfunc
+
+func Test_terminal_dumpdiff_swap()
+ call assert_equal(1, winnr('$'))
+ call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_popup_command_03.dump')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(62, line('$'))
+ call assert_match('Test_popup_command_01.dump', getline(21))
+ call assert_match('Test_popup_command_03.dump', getline(42))
+ call assert_match('Undo', getline(3))
+ call assert_match('three four five', getline(45))
+
+ normal s
+ call assert_match('Test_popup_command_03.dump', getline(21))
+ call assert_match('Test_popup_command_01.dump', getline(42))
+ call assert_match('three four five', getline(3))
+ call assert_match('Undo', getline(45))
+ quit
+
+ " Diff two terminal dump files with different number of rows
+ " Swap the diffs
+ call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_winline_rnu.dump')
+ call assert_match('Test_popup_command_01.dump', getline(21))
+ call assert_match('Test_winline_rnu.dump', getline(42))
+ normal s
+ call assert_match('Test_winline_rnu.dump', getline(6))
+ call assert_match('Test_popup_command_01.dump', getline(27))
+ quit
+endfunc
+
+func Test_terminal_dumpdiff_options()
+ set laststatus=0
+ call assert_equal(1, winnr('$'))
+ let height = winheight(0)
+ call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_popup_command_02.dump', {'vertical': 1, 'term_cols': 33})
+ call assert_equal(2, winnr('$'))
+ call assert_equal(height, winheight(winnr()))
+ call assert_equal(33, winwidth(winnr()))
+ call assert_equal('dump diff dumps/Test_popup_command_01.dump', bufname('%'))
+ quit
+
+ call assert_equal(1, winnr('$'))
+ call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_popup_command_02.dump', {'vertical': 0, 'term_rows': 13, 'term_name': 'something else'})
+ call assert_equal(2, winnr('$'))
+ call assert_equal(&columns, winwidth(0))
+ call assert_equal(13, winheight(0))
+ call assert_equal('something else', bufname('%'))
+ quit
+
+ call assert_equal(1, winnr('$'))
+ call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_popup_command_02.dump', {'curwin': 1})
+ call assert_equal(1, winnr('$'))
+ call assert_fails("call term_dumpdiff('dumps/Test_popup_command_01.dump', 'dumps/Test_popup_command_02.dump', {'bufnr': -1})", 'E475:')
+ bwipe
+
+ set laststatus&
+endfunc
+
+" When drawing the statusline the cursor position may not have been updated
+" yet.
+" 1. create a terminal, make it show 2 lines
+" 2. 0.5 sec later: leave terminal window, execute "i"
+" 3. 0.5 sec later: clear terminal window, now it's 1 line
+" 4. 0.5 sec later: redraw, including statusline (used to trigger bug)
+" 4. 0.5 sec later: should be done, clean up
+func Test_terminal_statusline()
+ CheckUnix
+ CheckFeature timers
+
+ set statusline=x
+ terminal
+ let tbuf = bufnr('')
+ call term_sendkeys(tbuf, "clear; echo a; echo b; sleep 1; clear\n")
+ call timer_start(500, { tid -> feedkeys("\<C-w>j", 'tx') })
+ call timer_start(1500, { tid -> feedkeys("\<C-l>", 'tx') })
+ au BufLeave * if &buftype == 'terminal' | silent! normal i | endif
+
+ sleep 2
+ exe tbuf . 'bwipe!'
+ au! BufLeave
+ set statusline=
+endfunc
+
+func CheckTerminalWindowWorks(buf)
+ call WaitForAssert({-> assert_match('!sh \[running\]', term_getline(a:buf, 10))})
+ call term_sendkeys(a:buf, "exit\<CR>")
+ call WaitForAssert({-> assert_match('!sh \[finished\]', term_getline(a:buf, 10))})
+ call term_sendkeys(a:buf, ":q\<CR>")
+ call WaitForAssert({-> assert_match('^\~', term_getline(a:buf, 10))})
+endfunc
+
+func Test_start_terminal_from_timer()
+ CheckUnix
+ CheckFeature timers
+
+ " Open a terminal window from a timer, typed text goes to the terminal
+ call writefile(["call timer_start(100, { -> term_start('sh') })"], 'XtimerTerm', 'D')
+ let buf = RunVimInTerminal('-S XtimerTerm', {})
+ call CheckTerminalWindowWorks(buf)
+
+ " do the same in Insert mode
+ call term_sendkeys(buf, ":call timer_start(200, { -> term_start('sh') })\<CR>a")
+ call CheckTerminalWindowWorks(buf)
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_terminal_window_focus()
+ let winid1 = win_getid()
+ terminal
+ let winid2 = win_getid()
+ call feedkeys("\<C-W>j", 'xt')
+ call assert_equal(winid1, win_getid())
+ call feedkeys("\<C-W>k", 'xt')
+ call assert_equal(winid2, win_getid())
+ " can use a cursor key here
+ call feedkeys("\<C-W>\<Down>", 'xt')
+ call assert_equal(winid1, win_getid())
+ call feedkeys("\<C-W>\<Up>", 'xt')
+ call assert_equal(winid2, win_getid())
+
+ bwipe!
+endfunc
+
+func Api_drop_common(options)
+ call assert_equal(1, winnr('$'))
+
+ " Use the title termcap entries to output the escape sequence.
+ call writefile([
+ \ 'set title',
+ \ 'exe "set t_ts=\<Esc>]51; t_fs=\x07"',
+ \ 'let &titlestring = ''["drop","Xtextfile"' . a:options . ']''',
+ \ 'redraw',
+ \ "set t_ts=",
+ \ ], 'Xscript')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call WaitFor({-> bufnr('Xtextfile') > 0})
+ call assert_equal('Xtextfile', expand('%:t'))
+ call assert_true(winnr('$') >= 3)
+ return buf
+endfunc
+
+func Test_terminal_api_drop_newwin()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common('')
+ call assert_equal(0, &bin)
+ call assert_equal('', &fenc)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_newwin_bin()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common(',{"bin":1}')
+ call assert_equal(1, &bin)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_newwin_binary()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common(',{"binary":1}')
+ call assert_equal(1, &bin)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_newwin_nobin()
+ CheckRunVimInTerminal
+ set binary
+ let buf = Api_drop_common(',{"nobin":1}')
+ call assert_equal(0, &bin)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+ set nobinary
+endfunc
+
+func Test_terminal_api_drop_newwin_nobinary()
+ CheckRunVimInTerminal
+ set binary
+ let buf = Api_drop_common(',{"nobinary":1}')
+ call assert_equal(0, &bin)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+ set nobinary
+endfunc
+
+func Test_terminal_api_drop_newwin_ff()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common(',{"ff":"dos"}')
+ call assert_equal("dos", &ff)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_newwin_fileformat()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common(',{"fileformat":"dos"}')
+ call assert_equal("dos", &ff)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_newwin_enc()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common(',{"enc":"utf-16"}')
+ call assert_equal("utf-16", &fenc)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_newwin_encoding()
+ CheckRunVimInTerminal
+ let buf = Api_drop_common(',{"encoding":"utf-16"}')
+ call assert_equal("utf-16", &fenc)
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ bwipe Xtextfile
+endfunc
+
+func Test_terminal_api_drop_oldwin()
+ CheckRunVimInTerminal
+ let firstwinid = win_getid()
+ split Xtextfile
+ let textfile_winid = win_getid()
+ call assert_equal(2, winnr('$'))
+ call win_gotoid(firstwinid)
+
+ " Use the title termcap entries to output the escape sequence.
+ call writefile([
+ \ 'set title',
+ \ 'exe "set t_ts=\<Esc>]51; t_fs=\x07"',
+ \ 'let &titlestring = ''["drop","Xtextfile"]''',
+ \ 'redraw',
+ \ "set t_ts=",
+ \ ], 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {'rows': 10})
+ call WaitForAssert({-> assert_equal('Xtextfile', expand('%:t'))})
+ call assert_equal(textfile_winid, win_getid())
+
+ call StopVimInTerminal(buf)
+ bwipe Xtextfile
+endfunc
+
+func Tapi_TryThis(bufnum, arg)
+ let g:called_bufnum = a:bufnum
+ let g:called_arg = a:arg
+endfunc
+
+func WriteApiCall(funcname)
+ " Use the title termcap entries to output the escape sequence.
+ call writefile([
+ \ 'set title',
+ \ 'exe "set t_ts=\<Esc>]51; t_fs=\x07"',
+ \ 'let &titlestring = ''["call","' . a:funcname . '",["hello",123]]''',
+ \ 'redraw',
+ \ "set t_ts=",
+ \ ], 'Xscript')
+endfunc
+
+func Test_terminal_api_call()
+ CheckRunVimInTerminal
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ call WriteApiCall('Tapi_TryThis')
+
+ " Default
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call WaitFor({-> exists('g:called_bufnum')})
+ call assert_equal(buf, g:called_bufnum)
+ call assert_equal(['hello', 123], g:called_arg)
+ call StopVimInTerminal(buf)
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ " Enable explicitly
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 'Tapi_Try'})
+ call WaitFor({-> exists('g:called_bufnum')})
+ call assert_equal(buf, g:called_bufnum)
+ call assert_equal(['hello', 123], g:called_arg)
+ call StopVimInTerminal(buf)
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ func! ApiCall_TryThis(bufnum, arg)
+ let g:called_bufnum2 = a:bufnum
+ let g:called_arg2 = a:arg
+ endfunc
+
+ call WriteApiCall('ApiCall_TryThis')
+
+ " Use prefix match
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 'ApiCall_'})
+ call WaitFor({-> exists('g:called_bufnum2')})
+ call assert_equal(buf, g:called_bufnum2)
+ call assert_equal(['hello', 123], g:called_arg2)
+ call StopVimInTerminal(buf)
+
+ call assert_fails("call term_start('ls', {'term_api' : []})", 'E730:')
+
+ unlet! g:called_bufnum2
+ unlet! g:called_arg2
+
+ call delete('Xscript')
+ delfunction! ApiCall_TryThis
+ unlet! g:called_bufnum2
+ unlet! g:called_arg2
+endfunc
+
+func Test_terminal_api_call_fails()
+ CheckRunVimInTerminal
+
+ func! TryThis(bufnum, arg)
+ let g:called_bufnum3 = a:bufnum
+ let g:called_arg3 = a:arg
+ endfunc
+
+ call WriteApiCall('TryThis')
+
+ unlet! g:called_bufnum3
+ unlet! g:called_arg3
+
+ " Not permitted
+ call ch_logfile('Xlog', 'w')
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': ''})
+ call WaitForAssert({-> assert_match('Unpermitted function: TryThis', string(readfile('Xlog')))})
+ call assert_false(exists('g:called_bufnum3'))
+ call assert_false(exists('g:called_arg3'))
+ call StopVimInTerminal(buf)
+
+ " No match
+ call ch_logfile('Xlog', 'w')
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': 'TryThat'})
+ call WaitFor({-> string(readfile('Xlog')) =~ 'Unpermitted function: TryThis'})
+ call assert_false(exists('g:called_bufnum3'))
+ call assert_false(exists('g:called_arg3'))
+ call StopVimInTerminal(buf)
+
+ call delete('Xscript')
+ call ch_logfile('')
+ call delete('Xlog')
+ delfunction! TryThis
+ unlet! g:called_bufnum3
+ unlet! g:called_arg3
+endfunc
+
+let s:caught_e937 = 0
+
+func Tapi_Delete(bufnum, arg)
+ try
+ execute 'bdelete!' a:bufnum
+ catch /E937:/
+ let s:caught_e937 = 1
+ endtry
+endfunc
+
+func Test_terminal_api_call_fail_delete()
+ CheckRunVimInTerminal
+
+ call WriteApiCall('Tapi_Delete')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call WaitForAssert({-> assert_equal(1, s:caught_e937)})
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ call ch_logfile('', '')
+endfunc
+
+func Test_terminal_setapi_and_call()
+ CheckRunVimInTerminal
+
+ call WriteApiCall('Tapi_TryThis')
+ call ch_logfile('Xlog', 'w')
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ let buf = RunVimInTerminal('-S Xscript', {'term_api': ''})
+ call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))})
+ call assert_false(exists('g:called_bufnum'))
+ call assert_false(exists('g:called_arg'))
+
+ eval buf->term_setapi('Tapi_')
+ call term_sendkeys(buf, ":set notitle\<CR>")
+ call term_sendkeys(buf, ":source Xscript\<CR>")
+ call WaitFor({-> exists('g:called_bufnum')})
+ call assert_equal(buf, g:called_bufnum)
+ call assert_equal(['hello', 123], g:called_arg)
+
+ call StopVimInTerminal(buf)
+
+ call delete('Xscript')
+ call ch_logfile('')
+ call delete('Xlog')
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+endfunc
+
+func Test_terminal_api_arg()
+ CheckRunVimInTerminal
+
+ call WriteApiCall('Tapi_TryThis')
+ call ch_logfile('Xlog', 'w')
+
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+
+ execute 'term ++api= ' .. GetVimCommandCleanTerm() .. '-S Xscript'
+ let buf = bufnr('%')
+ call WaitForAssert({-> assert_match('Unpermitted function: Tapi_TryThis', string(readfile('Xlog')))})
+ call assert_false(exists('g:called_bufnum'))
+ call assert_false(exists('g:called_arg'))
+
+ call StopVimInTerminal(buf)
+
+ call ch_logfile('Xlog', 'w')
+
+ execute 'term ++api=Tapi_ ' .. GetVimCommandCleanTerm() .. '-S Xscript'
+ let buf = bufnr('%')
+ call WaitFor({-> exists('g:called_bufnum')})
+ call assert_equal(buf, g:called_bufnum)
+ call assert_equal(['hello', 123], g:called_arg)
+
+ call StopVimInTerminal(buf)
+
+ call delete('Xscript')
+ call ch_logfile('')
+ call delete('Xlog')
+ unlet! g:called_bufnum
+ unlet! g:called_arg
+endfunc
+
+func Test_terminal_ansicolors_default()
+ CheckFunction term_getansicolors
+
+ let colors = [
+ \ '#000000', '#e00000',
+ \ '#00e000', '#e0e000',
+ \ '#0000e0', '#e000e0',
+ \ '#00e0e0', '#e0e0e0',
+ \ '#808080', '#ff4040',
+ \ '#40ff40', '#ffff40',
+ \ '#4040ff', '#ff40ff',
+ \ '#40ffff', '#ffffff',
+ \]
+
+ let buf = Run_shell_in_terminal({})
+ call assert_equal(colors, term_getansicolors(buf))
+ call StopShellInTerminal(buf)
+ call assert_equal([], term_getansicolors(buf))
+
+ exe buf . 'bwipe'
+endfunc
+
+let s:test_colors = [
+ \ '#616e64', '#0d0a79',
+ \ '#6d610d', '#0a7373',
+ \ '#690d0a', '#6d696e',
+ \ '#0d0a6f', '#616e0d',
+ \ '#0a6479', '#6d0d0a',
+ \ '#617373', '#0d0a69',
+ \ '#6d690d', '#0a6e6f',
+ \ '#610d0a', '#6e6479',
+ \]
+
+func Test_terminal_ansicolors_global()
+ CheckFeature termguicolors
+ CheckFunction term_getansicolors
+
+ if has('vtp') && !has('vcon') && !has('gui_running')
+ throw 'Skipped: does not support termguicolors'
+ endif
+
+ set tgc
+ let g:terminal_ansi_colors = reverse(copy(s:test_colors))
+ let buf = Run_shell_in_terminal({})
+ call assert_equal(g:terminal_ansi_colors, term_getansicolors(buf))
+ call StopShellInTerminal(buf)
+ set tgc&
+
+ exe buf . 'bwipe'
+ unlet g:terminal_ansi_colors
+endfunc
+
+func Test_terminal_ansicolors_func()
+ CheckFeature termguicolors
+ CheckFunction term_getansicolors
+
+ if has('vtp') && !has('vcon') && !has('gui_running')
+ throw 'Skipped: does not support termguicolors'
+ endif
+
+ set tgc
+ let g:terminal_ansi_colors = reverse(copy(s:test_colors))
+ let buf = Run_shell_in_terminal({'ansi_colors': s:test_colors})
+ call assert_equal(s:test_colors, term_getansicolors(buf))
+
+ call term_setansicolors(buf, g:terminal_ansi_colors)
+ call assert_equal(g:terminal_ansi_colors, buf->term_getansicolors())
+
+ let colors = [
+ \ 'ivory', 'AliceBlue',
+ \ 'grey67', 'dark goldenrod',
+ \ 'SteelBlue3', 'PaleVioletRed4',
+ \ 'MediumPurple2', 'yellow2',
+ \ 'RosyBrown3', 'OrangeRed2',
+ \ 'white smoke', 'navy blue',
+ \ 'grey47', 'gray97',
+ \ 'MistyRose2', 'DodgerBlue4',
+ \]
+ eval buf->term_setansicolors(colors)
+
+ let colors[4] = 'Invalid'
+ call assert_fails('call term_setansicolors(buf, colors)', 'E254:')
+ call assert_fails('call term_setansicolors(buf, {})', 'E1211:')
+ set tgc&
+
+ call StopShellInTerminal(buf)
+ call assert_equal(0, term_setansicolors(buf, []))
+ exe buf . 'bwipe'
+endfunc
+
+func Test_terminal_all_ansi_colors()
+ CheckRunVimInTerminal
+
+ " Use all the ANSI colors.
+ call writefile([
+ \ 'call setline(1, "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPP XXYYZZ")',
+ \ 'hi Tblack ctermfg=0 ctermbg=8',
+ \ 'hi Tdarkred ctermfg=1 ctermbg=9',
+ \ 'hi Tdarkgreen ctermfg=2 ctermbg=10',
+ \ 'hi Tbrown ctermfg=3 ctermbg=11',
+ \ 'hi Tdarkblue ctermfg=4 ctermbg=12',
+ \ 'hi Tdarkmagenta ctermfg=5 ctermbg=13',
+ \ 'hi Tdarkcyan ctermfg=6 ctermbg=14',
+ \ 'hi Tlightgrey ctermfg=7 ctermbg=15',
+ \ 'hi Tdarkgrey ctermfg=8 ctermbg=0',
+ \ 'hi Tred ctermfg=9 ctermbg=1',
+ \ 'hi Tgreen ctermfg=10 ctermbg=2',
+ \ 'hi Tyellow ctermfg=11 ctermbg=3',
+ \ 'hi Tblue ctermfg=12 ctermbg=4',
+ \ 'hi Tmagenta ctermfg=13 ctermbg=5',
+ \ 'hi Tcyan ctermfg=14 ctermbg=6',
+ \ 'hi Twhite ctermfg=15 ctermbg=7',
+ \ 'hi TdarkredBold ctermfg=1 cterm=bold',
+ \ 'hi TgreenBold ctermfg=10 cterm=bold',
+ \ 'hi TmagentaBold ctermfg=13 cterm=bold ctermbg=5',
+ \ '',
+ \ 'call matchadd("Tblack", "A")',
+ \ 'call matchadd("Tdarkred", "B")',
+ \ 'call matchadd("Tdarkgreen", "C")',
+ \ 'call matchadd("Tbrown", "D")',
+ \ 'call matchadd("Tdarkblue", "E")',
+ \ 'call matchadd("Tdarkmagenta", "F")',
+ \ 'call matchadd("Tdarkcyan", "G")',
+ \ 'call matchadd("Tlightgrey", "H")',
+ \ 'call matchadd("Tdarkgrey", "I")',
+ \ 'call matchadd("Tred", "J")',
+ \ 'call matchadd("Tgreen", "K")',
+ \ 'call matchadd("Tyellow", "L")',
+ \ 'call matchadd("Tblue", "M")',
+ \ 'call matchadd("Tmagenta", "N")',
+ \ 'call matchadd("Tcyan", "O")',
+ \ 'call matchadd("Twhite", "P")',
+ \ 'call matchadd("TdarkredBold", "X")',
+ \ 'call matchadd("TgreenBold", "Y")',
+ \ 'call matchadd("TmagentaBold", "Z")',
+ \ 'redraw',
+ \ ], 'Xcolorscript', 'D')
+ let buf = RunVimInTerminal('-S Xcolorscript', {'rows': 10})
+ call VerifyScreenDump(buf, 'Test_terminal_all_ansi_colors', {})
+
+ call term_sendkeys(buf, ":q\<CR>")
+ call StopVimInTerminal(buf)
+endfunc
+
+function On_BufFilePost()
+ doautocmd <nomodeline> User UserEvent
+endfunction
+
+func Test_terminal_nested_autocmd()
+ new
+ call setline(1, range(500))
+ $
+ let lastline = line('.')
+
+ augroup TermTest
+ autocmd BufFilePost * call On_BufFilePost()
+ autocmd User UserEvent silent
+ augroup END
+
+ let cmd = Get_cat_123_cmd()
+ let buf = term_start(cmd, #{term_finish: 'close', hidden: 1})
+ call assert_equal(lastline, line('.'))
+
+ let job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("dead", job_status(job))})
+ call delete('Xtext')
+ augroup TermTest
+ au!
+ augroup END
+endfunc
+
+func Test_terminal_adds_jump()
+ clearjumps
+ call term_start("ls", #{curwin: 1})
+ call assert_equal(1, getjumplist()[0]->len())
+ bwipe!
+endfunc
+
+func Close_cb(ch, ctx)
+ call term_wait(a:ctx.bufnr)
+ let g:close_done = 'done'
+endfunc
+
+func Test_term_wait_in_close_cb()
+ let g:close_done = ''
+ let ctx = {}
+ let ctx.bufnr = term_start('echo "HELLO WORLD"',
+ \ {'close_cb': {ch -> Close_cb(ch, ctx)}})
+
+ call WaitForAssert({-> assert_equal("done", g:close_done)})
+
+ unlet g:close_done
+ bwipe!
+endfunc
+
+func Test_term_TextChangedT()
+ augroup TermTest
+ autocmd TextChangedT * ++once
+ \ execute expand('<abuf>') . 'buffer' |
+ \ let b:called = 1 |
+ \ split |
+ \ enew
+ augroup END
+
+ terminal
+
+ let term_buf = bufnr()
+
+ let b:called = 0
+
+ call term_sendkeys(term_buf, "aaabbc\r")
+ call TermWait(term_buf)
+
+ call assert_equal(1, getbufvar(term_buf, 'called'))
+
+ " Current buffer will be restored
+ call assert_equal(bufnr(), term_buf)
+
+ bwipe!
+ augroup TermTest
+ au!
+ augroup END
+endfunc
+
+func Test_term_TextChangedT_close()
+ augroup TermTest
+ autocmd TextChangedT * ++once split | enew | 1close!
+ augroup END
+
+ terminal
+
+ let term_buf = bufnr()
+
+ call term_sendkeys(term_buf, "aaabbc\r")
+ call TermWait(term_buf)
+
+ " Current buffer will be restored
+ call assert_equal(bufnr(), term_buf)
+
+ bwipe!
+ augroup TermTest
+ au!
+ augroup END
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab