summaryrefslogtreecommitdiffstats
path: root/src/testdir/test_terminal.vim
diff options
context:
space:
mode:
Diffstat (limited to 'src/testdir/test_terminal.vim')
-rw-r--r--src/testdir/test_terminal.vim1758
1 files changed, 1758 insertions, 0 deletions
diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim
new file mode 100644
index 0000000..8eb43a0
--- /dev/null
+++ b/src/testdir/test_terminal.vim
@@ -0,0 +1,1758 @@
+" Tests for the terminal window.
+
+if !has('terminal')
+ finish
+endif
+
+source shared.vim
+source screendump.vim
+
+let s:python = PythonProg()
+
+" Open a terminal with a shell, assign the job to g:job and return the buffer
+" number.
+func Run_shell_in_terminal(options)
+ if has('win32')
+ let buf = term_start([&shell,'/k'], a:options)
+ else
+ let buf = term_start(&shell, a:options)
+ endif
+
+ let termlist = term_list()
+ call assert_equal(1, len(termlist))
+ call assert_equal(buf, termlist[0])
+
+ let g:job = term_getjob(buf)
+ call assert_equal(v:t_job, type(g:job))
+
+ let string = string({'job': term_getjob(buf)})
+ call assert_match("{'job': 'process \\d\\+ run'}", string)
+
+ return buf
+endfunc
+
+func Test_terminal_basic()
+ au TerminalOpen * let b:done = 'yes'
+ let buf = Run_shell_in_terminal({})
+
+ if has("unix")
+ call assert_match('^/dev/', job_info(g:job).tty_out)
+ call assert_match('^/dev/', term_gettty(''))
+ else
+ " ConPTY works on anonymous pipe.
+ if !has('conpty')
+ call assert_match('^\\\\.\\pipe\\', job_info(g:job).tty_out)
+ call assert_match('^\\\\.\\pipe\\', term_gettty(''))
+ endif
+ endif
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(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
+ unlet g:job
+endfunc
+
+func Test_terminal_make_change()
+ let buf = Run_shell_in_terminal({})
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+
+ setlocal modifiable
+ exe "normal Axxx\<Esc>"
+ call assert_fails(buf . 'bwipe', 'E517')
+ undo
+
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_wipe_buffer()
+ let buf = Run_shell_in_terminal({})
+ call assert_fails(buf . 'bwipe', 'E517')
+ exe buf . 'bwipe!'
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+func Test_terminal_split_quit()
+ let buf = Run_shell_in_terminal({})
+ call term_wait(buf)
+ split
+ quit!
+ call term_wait(buf)
+ sleep 50m
+ call assert_equal('run', job_status(g:job))
+
+ quit!
+ call WaitForAssert({-> assert_equal('dead', job_status(g:job))})
+
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_hide_buffer()
+ 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 Stop_shell_in_terminal(buf)
+ exe buf . 'bwipe'
+
+ unlet g:job
+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 = term_scrape(a:buf, 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)
+ 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 term_wait(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 term_wait(buf)
+ call Check_123(buf)
+
+ exe buf . 'bwipe'
+ call delete('Xtext')
+endfunc
+
+func Test_terminal_scrape_multibyte()
+ call writefile(["léttまrs"], 'Xtext')
+ 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>")
+ call term_sendkeys(buf, "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 term_wait(buf)
+
+ exe buf . 'bwipe'
+ call delete('Xtext')
+endfunc
+
+func Test_terminal_scroll()
+ call writefile(range(1, 200), 'Xtext')
+ 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 term_wait(buf)
+ if has('win32')
+ " TODO: this should not be needed
+ sleep 100m
+ endif
+
+ let scrolled = term_getscrolled(buf)
+ 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'
+ call delete('Xtext')
+endfunc
+
+func Test_terminal_scrollback()
+ let buf = Run_shell_in_terminal({'term_rows': 15})
+ set termwinscroll=100
+ call writefile(range(150), 'Xtext')
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+ set termwinscroll&
+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(''))
+
+ call 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])
+
+ call term_start(cmd, {'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 delete('Xtext')
+endfunc
+
+func Test_terminal_curwin()
+ let cmd = Get_cat_123_cmd()
+ call assert_equal(1, winnr('$'))
+
+ split dummy
+ exe 'terminal ++curwin ' . cmd
+ call assert_equal(2, winnr('$'))
+ bwipe!
+
+ split dummy
+ call term_start(cmd, {'curwin': 1})
+ call assert_equal(2, winnr('$'))
+ bwipe!
+
+ split dummy
+ 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 dummy
+ call setline(1, 'change')
+ call assert_fails("call term_start(cmd, {'curwin': 1})", 'E37:')
+ call assert_equal(2, winnr('$'))
+ bwipe!
+
+ split dummy
+ bwipe!
+ call delete('Xtext')
+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 Stop_shell_in_terminal(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 Stop_shell_in_terminal(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'})
+ close!
+ call WaitForAssert({-> assert_equal(2, winnr('$'))}, waittime)
+ call assert_equal(4, winheight(0))
+ bwipe
+endfunc
+
+func Test_terminal_cwd()
+ if !executable('pwd')
+ return
+ endif
+ call mkdir('Xdir')
+ let buf = term_start('pwd', {'cwd': 'Xdir'})
+ call WaitForAssert({-> assert_equal('Xdir', fnamemodify(getline(1), ":t"))})
+
+ exe buf . 'bwipe'
+ call delete('Xdir', '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([], 'Xfile')
+ call setfperm('Xfile', 'rwx------')
+ call assert_fails("call term_start(&shell, {'cwd': 'Xfile'})", 'E475:')
+ call delete('Xfile')
+
+ " 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 $USER != 'root'
+ 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()
+ if !has('clientserver')
+ return
+ endif
+ 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 term_wait(buf)
+ call Stop_shell_in_terminal(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
+ call term_wait(buf)
+ call Stop_shell_in_terminal(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', 'E517')
+ exe buf . 'bwipe!'
+ call assert_equal("", bufname(buf))
+endfunction
+
+func Test_terminal_noblock()
+ let buf = term_start(&shell)
+ 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.
+ let len = 1000
+ " NPFS is used in Windows, nonblocking mode does not work properly.
+ elseif has('win32')
+ let len = 1
+ else
+ let len = 5000
+ endif
+
+ for c in ['a','b','c','d','e','f','g','h','i','j','k']
+ 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 WaitFor({-> term_getline(buf, lnum) =~ "done" || term_getline(buf, lnum - 1) =~ "done"}, 10000)
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ unlet g:job
+ bwipe
+endfunc
+
+func Test_terminal_write_stdin()
+ if !executable('wc')
+ throw 'skipped: wc command not available'
+ endif
+ if has('win32')
+ " TODO: enable once writing to stdin works on MS-Windows
+ return
+ endif
+ new
+ 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
+
+ new
+ 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
+
+ if executable('python')
+ new
+ call setline(1, ['print("hello")'])
+ 1term ++eof=exit() python
+ " MS-Windows echoes the input, Unix doesn't.
+ call WaitFor('getline("$") =~ "exit" || getline(1) =~ "hello"')
+ if getline(1) =~ 'hello'
+ call assert_equal('hello', getline(1))
+ else
+ call assert_equal('hello', getline(line('$') - 1))
+ endif
+ bwipe
+
+ if has('win32')
+ new
+ call setline(1, ['print("hello")'])
+ 1term ++eof=<C-Z> python
+ call WaitForAssert({-> assert_match('Z', getline("$"))})
+ call assert_equal('hello', getline(line('$') - 1))
+ bwipe
+ endif
+ endif
+
+ bwipe!
+endfunc
+
+func Test_terminal_no_cmd()
+ 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
+ if !has('unix')
+ return
+ endif
+ call mkdir('Xdir with spaces')
+ call writefile(['x'], 'Xdir with spaces/quoted"file')
+ term ls Xdir\ with\ spaces/quoted\"file
+ call WaitForAssert({-> assert_match('quoted"file', term_getline('', 1))})
+ call term_wait('')
+
+ call delete('Xdir with spaces', 'rf')
+ 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 cmd = Get_cat_123_cmd()
+ let buf = term_start(cmd, {'out_io': 'file', 'out_name': 'Xfile'})
+ call term_wait(buf)
+ " ConPTY may precede escape sequence. There are things that are not so.
+ if !has('conpty')
+ call WaitForAssert({-> assert_notequal(0, len(readfile("Xfile")))})
+ call assert_match('123', readfile('Xfile')[0])
+ endif
+ let g:job = term_getjob(buf)
+ call WaitForAssert({-> assert_equal("dead", job_status(g:job))})
+ call delete('Xfile')
+ bwipe
+
+ if has('unix')
+ call writefile(['one line'], 'Xfile')
+ let buf = term_start('cat', {'in_io': 'file', 'in_name': 'Xfile'})
+ call term_wait(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
+ call delete('Xfile')
+ endif
+endfunc
+
+func TerminalTmap(remap)
+ let buf = Run_shell_in_terminal({})
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+
+ tunmap 123
+ tunmap 456
+ call assert_equal('', maparg('123', 't'))
+ close
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_wqall()
+ let buf = Run_shell_in_terminal({})
+ call assert_fails('wqall', 'E948')
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+ unlet g:job
+endfunc
+
+func Test_terminal_composing_unicode()
+ 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': bufnr('')})
+ let g:job = term_getjob(buf)
+ call term_wait(buf, 50)
+
+ 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 . "\r")
+ call term_wait(buf, 50)
+ call assert_match("echo " . txt, term_getline(buf, lnum[0]))
+ call assert_equal(txt, term_getline(buf, lnum[0] + 1))
+ 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 . "\r")
+ call term_wait(buf, 50)
+ call assert_match("echo " . txt, term_getline(buf, lnum[1]))
+ call assert_equal(txt, term_getline(buf, lnum[1] + 1))
+ let l = term_scrape(buf, lnum[1] + 1)
+ call assert_equal("\u304b\u3099", l[0].chars)
+ call assert_equal("\u304e", l[1].chars)
+ call assert_equal("\u304f\u3099", l[2].chars)
+ call assert_equal("\u3052", l[3].chars)
+ call assert_equal("\u3053\u3099", l[4].chars)
+
+ " \u00a0 + composing
+ let txt = "abc\u00a0\u0308"
+ call term_sendkeys(buf, "echo " . txt . "\r")
+ call term_wait(buf, 50)
+ call assert_match("echo " . txt, term_getline(buf, lnum[2]))
+ call assert_equal(txt, term_getline(buf, lnum[2] + 1))
+ 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')
+endfunc
+
+func Test_terminal_response_to_control_sequence()
+ if !has('unix')
+ return
+ endif
+
+ 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 Stop_shell_in_terminal(buf)
+ exe buf . 'bwipe'
+ unlet g:job
+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', 'call term_setkill(buf, "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 = [
+ \ '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',
+ \ ]
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ let buf = RunVimInTerminal('', {})
+
+ " 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('ancel:', 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
+
+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
+endfunction
+
+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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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')
+ let buf = RunVimInTerminal('--cmd "set encoding=utf-8" Xcomposing', {})
+ call WaitForAssert({-> assert_match(text, term_getline(buf, 1))})
+ call term_dumpwrite(buf, 'Xdump')
+ let dumpline = readfile('Xdump')[0]
+ call assert_match('|à| |ê| |ö', dumpline)
+
+ call StopVimInTerminal(buf)
+ call delete('Xcomposing')
+ call delete('Xdump')
+ let &encoding = save_enc
+endfunc
+
+" just testing basic functionality.
+func Test_terminal_dumpload()
+ call assert_equal(1, winnr('$'))
+ call term_dumpload('dumps/Test_popup_command_01.dump')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(20, line('$'))
+ call Check_dump01(0)
+ quit
+endfunc
+
+func Test_terminal_dumpdiff()
+ call assert_equal(1, winnr('$'))
+ call term_dumpdiff('dumps/Test_popup_command_01.dump', '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
+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('$'))
+ let width = winwidth(0)
+ 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(width, winwidth(winnr()))
+ call assert_equal(13, winheight(winnr()))
+ 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('$'))
+ bwipe
+
+ set laststatus&
+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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ 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')
+ let buf = RunVimInTerminal('-S Xscript', {'rows': 10})
+ call WaitForAssert({-> assert_equal('Xtextfile', expand('%:t'))})
+ call assert_equal(textfile_winid, win_getid())
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ 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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call WriteApiCall('Tapi_TryThis')
+ 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)
+ call delete('Xscript')
+ unlet g:called_bufnum
+ unlet g:called_arg
+endfunc
+
+func Test_terminal_api_call_fails()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ call WriteApiCall('TryThis')
+ call ch_logfile('Xlog', 'w')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call WaitForAssert({-> assert_match('Invalid function name: TryThis', string(readfile('Xlog')))})
+
+ call StopVimInTerminal(buf)
+ call delete('Xscript')
+ call ch_logfile('', '')
+ call delete('Xlog')
+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()
+ if !CanRunVimInTerminal()
+ return
+ endif
+
+ 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_ansicolors_default()
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(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()
+ 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 Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+
+ exe buf . 'bwipe'
+ unlet g:terminal_ansi_colors
+endfunc
+
+func Test_terminal_ansicolors_func()
+ 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, term_getansicolors(buf))
+
+ let colors = [
+ \ 'ivory', 'AliceBlue',
+ \ 'grey67', 'dark goldenrod',
+ \ 'SteelBlue3', 'PaleVioletRed4',
+ \ 'MediumPurple2', 'yellow2',
+ \ 'RosyBrown3', 'OrangeRed2',
+ \ 'white smoke', 'navy blue',
+ \ 'grey47', 'gray97',
+ \ 'MistyRose2', 'DodgerBlue4',
+ \]
+ call term_setansicolors(buf, colors)
+
+ let colors[4] = 'Invalid'
+ call assert_fails('call term_setansicolors(buf, colors)', 'E474:')
+
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+endfunc
+
+func Test_terminal_termwinsize_option_fixed()
+ if !CanRunVimInTerminal()
+ return
+ endif
+ set termwinsize=6x40
+ let text = []
+ for n in range(10)
+ call add(text, repeat(n, 50))
+ endfor
+ call writefile(text, 'Xwinsize')
+ let buf = RunVimInTerminal('Xwinsize', {})
+ let win = bufwinid(buf)
+ call assert_equal([6, 40], term_getsize(buf))
+ call assert_equal(6, winheight(win))
+ call assert_equal(40, winwidth(win))
+
+ " resizing the window doesn't resize the terminal.
+ resize 10
+ vertical resize 60
+ call assert_equal([6, 40], term_getsize(buf))
+ call assert_equal(10, winheight(win))
+ call assert_equal(60, winwidth(win))
+
+ call StopVimInTerminal(buf)
+ call delete('Xwinsize')
+
+ call assert_fails('set termwinsize=40', 'E474')
+ call assert_fails('set termwinsize=10+40', 'E474')
+ call assert_fails('set termwinsize=abc', 'E474')
+
+ set termwinsize=
+endfunc
+
+func Test_terminal_termwinsize_option_zero()
+ set termwinsize=0x0
+ let buf = Run_shell_in_terminal({})
+ let win = bufwinid(buf)
+ call assert_equal([winheight(win), winwidth(win)], term_getsize(buf))
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+
+ set termwinsize=7x0
+ let buf = Run_shell_in_terminal({})
+ let win = bufwinid(buf)
+ call assert_equal([7, winwidth(win)], term_getsize(buf))
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+
+ set termwinsize=0x33
+ let buf = Run_shell_in_terminal({})
+ let win = bufwinid(buf)
+ call assert_equal([winheight(win), 33], term_getsize(buf))
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+
+ set termwinsize=
+endfunc
+
+func Test_terminal_termwinsize_mininmum()
+ set termwinsize=10*50
+ vsplit
+ let buf = Run_shell_in_terminal({})
+ let win = bufwinid(buf)
+ call assert_inrange(10, 1000, winheight(win))
+ call assert_inrange(50, 1000, winwidth(win))
+ call assert_equal([winheight(win), winwidth(win)], term_getsize(buf))
+
+ resize 15
+ vertical resize 60
+ redraw
+ call assert_equal([15, 60], term_getsize(buf))
+ call assert_equal(15, winheight(win))
+ call assert_equal(60, winwidth(win))
+
+ resize 7
+ vertical resize 30
+ redraw
+ call assert_equal([10, 50], term_getsize(buf))
+ call assert_equal(7, winheight(win))
+ call assert_equal(30, winwidth(win))
+
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+
+ set termwinsize=0*0
+ let buf = Run_shell_in_terminal({})
+ let win = bufwinid(buf)
+ call assert_equal([winheight(win), winwidth(win)], term_getsize(buf))
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+ exe buf . 'bwipe'
+
+ set termwinsize=
+endfunc
+
+func Test_terminal_termwinkey()
+ call assert_equal(1, winnr('$'))
+ let thiswin = win_getid()
+
+ let buf = Run_shell_in_terminal({})
+ let termwin = bufwinid(buf)
+ set termwinkey=<C-L>
+ call feedkeys("\<C-L>w", 'tx')
+ call assert_equal(thiswin, win_getid())
+ call feedkeys("\<C-W>w", 'tx')
+
+ let job = term_getjob(buf)
+ call feedkeys("\<C-L>\<C-C>", 'tx')
+ call WaitForAssert({-> assert_equal("dead", job_status(job))})
+endfunc
+
+func Test_terminal_out_err()
+ if !has('unix')
+ return
+ endif
+ call writefile([
+ \ '#!/bin/sh',
+ \ 'echo "this is standard error" >&2',
+ \ 'echo "this is standard out" >&1',
+ \ ], 'Xechoerrout.sh')
+ call setfperm('Xechoerrout.sh', 'rwxrwx---')
+
+ let outfile = 'Xtermstdout'
+ let buf = term_start(['./Xechoerrout.sh'], {'out_io': 'file', 'out_name': outfile})
+
+ call WaitFor({-> !empty(readfile(outfile)) && !empty(term_getline(buf, 1))})
+ call assert_equal(['this is standard out'], readfile(outfile))
+ call assert_equal('this is standard error', term_getline(buf, 1))
+
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe'
+ call delete('Xechoerrout.sh')
+ call delete(outfile)
+endfunc
+
+func Test_terminwinscroll()
+ if !has('unix')
+ return
+ endif
+
+ " Let the terminal output more than 'termwinscroll' lines, some at the start
+ " will be dropped.
+ exe 'set termwinscroll=' . &lines
+ let buf = term_start('/bin/sh')
+ for i in range(1, &lines)
+ call feedkeys("echo " . i . "\<CR>", 'xt')
+ call WaitForAssert({-> assert_match(string(i), term_getline(buf, term_getcursor(buf)[0] - 1))})
+ endfor
+ " Go to Terminal-Normal mode to update the buffer.
+ call feedkeys("\<C-W>N", 'xt')
+ call assert_inrange(&lines, &lines * 110 / 100 + winheight(0), line('$'))
+
+ " Every "echo nr" must only appear once
+ let lines = getline(1, line('$'))
+ for i in range(&lines - len(lines) / 2 + 2, &lines)
+ let filtered = filter(copy(lines), {idx, val -> val =~ 'echo ' . i . '\>'})
+ call assert_equal(1, len(filtered), 'for "echo ' . i . '"')
+ endfor
+
+ exe buf . 'bwipe!'
+endfunc
+
+" Resizing the terminal window caused an ml_get error.
+" TODO: This does not reproduce the original problem.
+func Test_terminal_resize()
+ set statusline=x
+ terminal
+ call assert_equal(2, winnr('$'))
+
+ " Fill the terminal with text.
+ if has('win32')
+ call feedkeys("dir\<CR>", 'xt')
+ else
+ call feedkeys("ls\<CR>", 'xt')
+ endif
+ " Go to Terminal-Normal mode for a moment.
+ call feedkeys("\<C-W>N", 'xt')
+ " Open a new window
+ call feedkeys("i\<C-W>n", 'xt')
+ call assert_equal(3, winnr('$'))
+ redraw
+
+ close
+ call assert_equal(2, winnr('$'))
+ call feedkeys("exit\<CR>", 'xt')
+ set statusline&
+endfunc
+
+" must be nearly the last, we can't go back from GUI to terminal
+func Test_zz1_terminal_in_gui()
+ if !CanRunGui()
+ return
+ endif
+
+ " Ignore the "failed to create input context" error.
+ call test_ignore_error('E285:')
+
+ gui -f
+
+ call assert_equal(1, winnr('$'))
+ let buf = Run_shell_in_terminal({'term_finish': 'close'})
+ call Stop_shell_in_terminal(buf)
+ call term_wait(buf)
+
+ " closing window wipes out the terminal buffer a with finished job
+ call WaitForAssert({-> assert_equal(1, winnr('$'))})
+ call assert_equal("", bufname(buf))
+
+ unlet g:job
+endfunc
+
+func Test_zz2_terminal_guioptions_bang()
+ if !has('gui_running')
+ return
+ endif
+ set guioptions+=!
+
+ let filename = 'Xtestscript'
+ if has('win32')
+ let filename .= '.bat'
+ let prefix = ''
+ let contents = ['@echo off', 'exit %1']
+ else
+ let filename .= '.sh'
+ let prefix = './'
+ let contents = ['#!/bin/sh', 'exit $1']
+ endif
+ call writefile(contents, filename)
+ call setfperm(filename, 'rwxrwx---')
+
+ " Check if v:shell_error is equal to the exit status.
+ let exitval = 0
+ execute printf(':!%s%s %d', prefix, filename, exitval)
+ call assert_equal(exitval, v:shell_error)
+
+ let exitval = 9
+ execute printf(':!%s%s %d', prefix, filename, exitval)
+ call assert_equal(exitval, v:shell_error)
+
+ set guioptions&
+ call delete(filename)
+endfunc
+
+func Test_terminal_hidden()
+ if !has('unix')
+ return
+ endif
+ term ++hidden cat
+ let bnr = bufnr('$')
+ call assert_equal('terminal', getbufvar(bnr, '&buftype'))
+ exe 'sbuf ' . bnr
+ call assert_equal('terminal', &buftype)
+ call term_sendkeys(bnr, "asdf\<CR>")
+ call WaitForAssert({-> assert_match('asdf', term_getline(bnr, 2))})
+ call term_sendkeys(bnr, "\<C-D>")
+ call WaitForAssert({-> assert_equal('finished', term_getstatus(bnr))})
+ bwipe!
+endfunc
+
+func Test_terminal_hidden_and_close()
+ if !has('unix')
+ return
+ endif
+ call assert_equal(1, winnr('$'))
+ term ++hidden ++close ls
+ let bnr = bufnr('$')
+ call assert_equal('terminal', getbufvar(bnr, '&buftype'))
+ call WaitForAssert({-> assert_false(bufexists(bnr))})
+ call assert_equal(1, winnr('$'))
+endfunc
+
+func Test_terminal_does_not_truncate_last_newlines()
+ " This test does not pass through ConPTY.
+ if has('conpty')
+ return
+ endif
+ let contents = [
+ \ [ 'One', '', 'X' ],
+ \ [ 'Two', '', '' ],
+ \ [ 'Three' ] + repeat([''], 30)
+ \ ]
+
+ for c in contents
+ call writefile(c, 'Xfile')
+ if has('win32')
+ term cmd /c type Xfile
+ else
+ term cat Xfile
+ endif
+ let bnr = bufnr('$')
+ call assert_equal('terminal', getbufvar(bnr, '&buftype'))
+ call WaitForAssert({-> assert_equal('finished', term_getstatus(bnr))})
+ sleep 100m
+ call assert_equal(c, getline(1, line('$')))
+ quit
+ endfor
+
+ call delete('Xfile')
+endfunc
+
+func Test_terminal_no_job()
+ let term = term_start('false', {'term_finish': 'close'})
+ call WaitForAssert({-> assert_equal(v:null, term_getjob(term)) })
+endfunc
+
+func Test_term_gettitle()
+ if !has('title') || empty(&t_ts)
+ return
+ endif
+ " TODO: this fails on Travis
+ return
+
+ " term_gettitle() returns an empty string for a non-terminal buffer
+ " or for a non-existing buffer.
+ call assert_equal('', term_gettitle(bufnr('%')))
+ call assert_equal('', term_gettitle(bufnr('$') + 1))
+
+ let term = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'])
+ call WaitForAssert({-> assert_equal('[No Name] - VIM', term_gettitle(term)) })
+
+ call term_sendkeys(term, ":e Xfoo\r")
+ call WaitForAssert({-> assert_match('Xfoo (.*[/\\]testdir) - VIM', term_gettitle(term)) })
+
+ call term_sendkeys(term, ":set titlestring=foo\r")
+ call WaitForAssert({-> assert_equal('foo', term_gettitle(term)) })
+
+ exe term . 'bwipe!'
+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()
+ if !has('unix')
+ return
+ endif
+ 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