aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/api/vim_spec.lua129
-rw-r--r--test/functional/terminal/helpers.lua3
-rw-r--r--test/functional/terminal/tui_spec.lua366
-rw-r--r--test/functional/ui/input_spec.lua71
4 files changed, 466 insertions, 103 deletions
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 0cd81619c1..647fab5c43 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -5,6 +5,7 @@ local NIL = helpers.NIL
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
local command = helpers.command
local eval = helpers.eval
+local expect = helpers.expect
local funcs = helpers.funcs
local iswin = helpers.iswin
local meth_pcall = helpers.meth_pcall
@@ -365,6 +366,126 @@ describe('API', function()
end)
end)
+ describe('nvim_paste', function()
+ it('validates args', function()
+ expect_err('Invalid phase: %-2', request,
+ 'nvim_paste', 'foo', -2)
+ expect_err('Invalid phase: 4', request,
+ 'nvim_paste', 'foo', 4)
+ end)
+ it('non-streaming', function()
+ -- With final "\n".
+ nvim('paste', 'line 1\nline 2\nline 3\n', -1)
+ expect([[
+ line 1
+ line 2
+ line 3
+ ]])
+ -- Cursor follows the paste.
+ eq({0,4,1,0}, funcs.getpos('.'))
+ eq(false, nvim('get_option', 'paste'))
+ command('%delete _')
+ -- Without final "\n".
+ nvim('paste', 'line 1\nline 2\nline 3', -1)
+ expect([[
+ line 1
+ line 2
+ line 3]])
+ -- Cursor follows the paste.
+ eq({0,3,6,0}, funcs.getpos('.'))
+ eq(false, nvim('get_option', 'paste'))
+ end)
+ it('vim.paste() failure', function()
+ nvim('execute_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {})
+ expect_err([[Error executing lua: %[string "%<nvim>"]:1: fake fail]],
+ request, 'nvim_paste', 'line 1\nline 2\nline 3', 1)
+ end)
+ end)
+
+ describe('nvim_put', function()
+ it('validates args', function()
+ expect_err('Invalid lines %(expected array of strings%)', request,
+ 'nvim_put', {42}, 'l', false, false)
+ expect_err("Invalid type: 'x'", request,
+ 'nvim_put', {'foo'}, 'x', false, false)
+ end)
+ it("fails if 'nomodifiable'", function()
+ command('set nomodifiable')
+ expect_err([[Vim:E21: Cannot make changes, 'modifiable' is off]], request,
+ 'nvim_put', {'a','b'}, 'l', true, true)
+ end)
+ it('inserts text', function()
+ -- linewise
+ nvim('put', {'line 1','line 2','line 3'}, 'l', true, true)
+ expect([[
+
+ line 1
+ line 2
+ line 3]])
+ eq({0,4,1,0}, funcs.getpos('.'))
+ command('%delete _')
+ -- charwise
+ nvim('put', {'line 1','line 2','line 3'}, 'c', true, false)
+ expect([[
+ line 1
+ line 2
+ line 3]])
+ eq({0,1,1,0}, funcs.getpos('.')) -- follow=false
+ -- blockwise
+ nvim('put', {'AA','BB'}, 'b', true, true)
+ expect([[
+ lAAine 1
+ lBBine 2
+ line 3]])
+ eq({0,2,4,0}, funcs.getpos('.'))
+ command('%delete _')
+ -- Empty lines list.
+ nvim('put', {}, 'c', true, true)
+ eq({0,1,1,0}, funcs.getpos('.'))
+ expect([[]])
+ -- Single empty line.
+ nvim('put', {''}, 'c', true, true)
+ eq({0,1,1,0}, funcs.getpos('.'))
+ expect([[
+ ]])
+ nvim('put', {'AB'}, 'c', true, true)
+ -- after=false, follow=true
+ nvim('put', {'line 1','line 2'}, 'c', false, true)
+ expect([[
+ Aline 1
+ line 2B]])
+ eq({0,2,7,0}, funcs.getpos('.'))
+ command('%delete _')
+ nvim('put', {'AB'}, 'c', true, true)
+ -- after=false, follow=false
+ nvim('put', {'line 1','line 2'}, 'c', false, false)
+ expect([[
+ Aline 1
+ line 2B]])
+ eq({0,1,2,0}, funcs.getpos('.'))
+ eq('', nvim('eval', 'v:errmsg'))
+ end)
+
+ it('detects charwise/linewise text (empty {type})', function()
+ -- linewise (final item is empty string)
+ nvim('put', {'line 1','line 2','line 3',''}, '', true, true)
+ expect([[
+
+ line 1
+ line 2
+ line 3]])
+ eq({0,4,1,0}, funcs.getpos('.'))
+ command('%delete _')
+ -- charwise (final item is non-empty)
+ nvim('put', {'line 1','line 2','line 3'}, '', true, true)
+ expect([[
+ line 1
+ line 2
+ line 3]])
+ eq({0,3,6,0}, funcs.getpos('.'))
+ end)
+ end)
+
describe('nvim_strwidth', function()
it('works', function()
eq(3, nvim('strwidth', 'abc'))
@@ -626,12 +747,12 @@ describe('API', function()
-- Make any RPC request (can be non-async: op-pending does not block).
nvim('get_current_buf')
-- Buffer should not change.
- helpers.expect([[
+ expect([[
FIRST LINE
SECOND LINE]])
-- Now send input to complete the operator.
nvim('input', 'j')
- helpers.expect([[
+ expect([[
first line
second line]])
end)
@@ -664,7 +785,7 @@ describe('API', function()
nvim('get_api_info')
-- Send input to complete the mapping.
nvim('input', 'd')
- helpers.expect([[
+ expect([[
FIRST LINE
SECOND LINE]])
eq('it worked...', helpers.eval('g:foo'))
@@ -680,7 +801,7 @@ describe('API', function()
nvim('get_api_info')
-- Send input to complete the mapping.
nvim('input', 'x')
- helpers.expect([[
+ expect([[
FIRST LINE
SECOND LINfooE]])
end)
diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua
index 18f0b9e4c1..2d99a08614 100644
--- a/test/functional/terminal/helpers.lua
+++ b/test/functional/terminal/helpers.lua
@@ -1,3 +1,6 @@
+-- To test tui/input.c, this module spawns `nvim` inside :terminal and sends
+-- bytes via jobsend(). Note: the functional/helpers.lua test-session methods
+-- operate on the _host_ session, _not_ the child session.
local helpers = require('test.functional.helpers')(nil)
local Screen = require('test.functional.ui.screen')
local nvim_dir = helpers.nvim_dir
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index af55ec1555..5445ff0127 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -1,5 +1,9 @@
-- TUI acceptance tests.
-- Uses :terminal as a way to send keys and assert screen state.
+--
+-- "bracketed paste" terminal feature:
+-- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
+
local helpers = require('test.functional.helpers')(after_each)
local uname = helpers.uname
local thelpers = require('test.functional.terminal.helpers')
@@ -21,11 +25,14 @@ if helpers.pending_win32(pending) then return end
describe('TUI', function()
local screen
+ local child_session
before_each(function()
clear()
- screen = thelpers.screen_setup(0, '["'..nvim_prog
- ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler undodir=. directory=. viewdir=. backupdir=."]')
+ local child_server = helpers.new_pipename()
+ screen = thelpers.screen_setup(0,
+ string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
+ nvim_prog, child_server, nvim_set))
screen:expect([[
{1: } |
{4:~ }|
@@ -35,12 +42,31 @@ describe('TUI', function()
|
{3:-- TERMINAL --} |
]])
+ child_session = helpers.connect(child_server)
end)
after_each(function()
screen:detach()
end)
+ -- Wait for mode in the child Nvim (avoid "typeahead race" #10826).
+ local function wait_for_mode(mode)
+ retry(nil, nil, function()
+ local _, m = child_session:request('nvim_get_mode')
+ eq(mode, m.mode)
+ end)
+ end
+
+ -- Assert buffer contents in the child Nvim.
+ local function expect_child_buf_lines(expected)
+ assert(type({}) == type(expected))
+ retry(nil, nil, function()
+ local _, buflines = child_session:request(
+ 'nvim_buf_get_lines', 0, 0, -1, false)
+ eq(expected, buflines)
+ end)
+ end
+
it('rapid resize #7572 #7628', function()
-- Need buffer rows to provoke the behavior.
feed_data(":edit test/functional/fixtures/bigfile.txt:")
@@ -128,7 +154,7 @@ describe('TUI', function()
]])
end)
- it('accepts ascii control sequences', function()
+ it('accepts ASCII control sequences', function()
feed_data('i')
feed_data('\022\007') -- ctrl+g
feed_data('\022\022') -- ctrl+v
@@ -146,49 +172,264 @@ describe('TUI', function()
]], attrs)
end)
- it('automatically sends <Paste> for bracketed paste sequences', function()
- -- Pasting can be really slow in the TUI, specially in ASAN.
- -- This will be fixed later but for now we require a high timeout.
- screen.timeout = 60000
- feed_data('i\027[200~')
+ it('paste: Insert mode', function()
+ -- "bracketed paste"
+ feed_data('i""\027i\027[200~')
screen:expect([[
- {1: } |
+ "{1:"} |
{4:~ }|
{4:~ }|
{4:~ }|
- {5:[No Name] }|
- {3:-- INSERT (paste) --} |
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
{3:-- TERMINAL --} |
]])
feed_data('pasted from terminal')
+ expect_child_buf_lines({'"pasted from terminal"'})
screen:expect([[
- pasted from terminal{1: } |
+ "pasted from terminal{1:"} |
{4:~ }|
{4:~ }|
{4:~ }|
{5:[No Name] [+] }|
- {3:-- INSERT (paste) --} |
+ {3:-- INSERT --} |
{3:-- TERMINAL --} |
]])
- feed_data('\027[201~')
+ feed_data('\027[201~') -- End paste.
+ feed_data('\027\000') -- ESC: go to Normal mode.
+ wait_for_mode('n')
screen:expect([[
- pasted from terminal{1: } |
+ "pasted from termina{1:l}" |
{4:~ }|
{4:~ }|
{4:~ }|
{5:[No Name] [+] }|
- {3:-- INSERT --} |
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ -- Dot-repeat/redo.
+ feed_data('2.')
+ expect_child_buf_lines(
+ {'"pasted from terminapasted from terminalpasted from terminall"'})
+ screen:expect([[
+ "pasted from terminapasted from terminalpasted fro|
+ m termina{1:l}l" |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
{3:-- TERMINAL --} |
]])
+ -- Undo.
+ feed_data('u')
+ expect_child_buf_lines({'"pasted from terminal"'})
+ feed_data('u')
+ expect_child_buf_lines({''})
+ end)
+
+ it('paste: normal-mode', function()
+ feed_data(':set ruler')
+ wait_for_mode('c')
+ feed_data('\n')
+ wait_for_mode('n')
+ local expected = {'line 1', ' line 2', 'ESC:\027 / CR: \013'}
+ local expected_attr = {
+ [3] = {bold = true},
+ [4] = {foreground = tonumber('0x00000c')},
+ [5] = {bold = true, reverse = true},
+ [11] = {foreground = tonumber('0x000051')},
+ [12] = {reverse = true, foreground = tonumber('0x000051')},
+ }
+ -- "bracketed paste"
+ feed_data('\027[200~'..table.concat(expected,'\n')..'\027[201~')
+ screen:expect{
+ grid=[[
+ line 1 |
+ line 2 |
+ ESC:{11:^[} / CR: {12:^}{11:M} |
+ {4:~ }|
+ {5:[No Name] [+] 3,13-14 All}|
+ |
+ {3:-- TERMINAL --} |
+ ]],
+ attr_ids=expected_attr}
+ -- Dot-repeat/redo.
+ feed_data('.')
+ screen:expect{
+ grid=[[
+ line 2 |
+ ESC:{11:^[} / CR: {11:^M}line 1 |
+ line 2 |
+ ESC:{11:^[} / CR: {12:^}{11:M} |
+ {5:[No Name] [+] 5,13-14 Bot}|
+ |
+ {3:-- TERMINAL --} |
+ ]],
+ attr_ids=expected_attr}
+ -- Undo.
+ feed_data('u')
+ expect_child_buf_lines(expected)
+ feed_data('u')
+ expect_child_buf_lines({''})
+ end)
+
+ it('paste: cmdline-mode inserts 1 line', function()
+ feed_data('ifoo\n') -- Insert some text (for dot-repeat later).
+ feed_data('\027:""') -- Enter Cmdline-mode.
+ feed_data('\027[D') -- <Left> to place cursor between quotes.
+ wait_for_mode('c')
+ -- "bracketed paste"
+ feed_data('\027[200~line 1\nline 2\n\027[201~')
+ screen:expect{grid=[[
+ foo |
+ |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ :"line 1{1:"} |
+ {3:-- TERMINAL --} |
+ ]]}
+ -- Dot-repeat/redo.
+ feed_data('\027\000')
+ wait_for_mode('n')
+ feed_data('.')
+ screen:expect{grid=[[
+ foo |
+ foo |
+ {1: } |
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ end)
+
+ it('paste: cmdline-mode collects chunks of unfinished line', function()
+ local function expect_cmdline(expected)
+ retry(nil, nil, function()
+ local _, cmdline = child_session:request(
+ 'nvim_call_function', 'getcmdline', {})
+ eq(expected, cmdline)
+ end)
+ end
+ feed_data('\027:""') -- Enter Cmdline-mode.
+ feed_data('\027[D') -- <Left> to place cursor between quotes.
+ wait_for_mode('c')
+ feed_data('\027[200~stuff 1 ')
+ expect_cmdline('"stuff 1 "')
+ -- Discards everything after the first line.
+ feed_data('more\nstuff 2\nstuff 3\n')
+ expect_cmdline('"stuff 1 more"')
+ feed_data('stuff 3')
+ expect_cmdline('"stuff 1 more"')
+ -- End the paste sequence.
+ feed_data('\027[201~')
+ feed_data(' typed')
+ expect_cmdline('"stuff 1 more typed"')
+ end)
+
+ it('paste: recovers from vim.paste() failure', function()
+ child_session:request('nvim_execute_lua', [[
+ _G.save_paste_fn = vim.paste
+ vim.paste = function(lines, phase) error("fake fail") end
+ ]], {})
+ -- Prepare something for dot-repeat/redo.
+ feed_data('ifoo\n\027\000')
+ wait_for_mode('n')
+ screen:expect{grid=[[
+ foo |
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ -- Start pasting...
+ feed_data('\027[200~line 1\nline 2\n')
+ wait_for_mode('n')
+ screen:expect{any='paste: Error executing lua'}
+ -- Remaining chunks are discarded after vim.paste() failure.
+ feed_data('line 3\nline 4\n')
+ feed_data('line 5\nline 6\n')
+ feed_data('line 7\nline 8\n')
+ -- Stop paste.
+ feed_data('\027[201~')
+ feed_data('\n') -- <Enter>
+ --Dot-repeat/redo is not modified by failed paste.
+ feed_data('.')
+ screen:expect{grid=[[
+ foo |
+ foo |
+ {1: } |
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ -- Editor should still work after failed/drained paste.
+ feed_data('ityped input...\027\000')
+ screen:expect{grid=[[
+ foo |
+ foo |
+ typed input..{1:.} |
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ -- Paste works if vim.paste() succeeds.
+ child_session:request('nvim_execute_lua', [[
+ vim.paste = _G.save_paste_fn
+ ]], {})
+ feed_data('\027[200~line A\nline B\n\027[201~')
+ feed_data('\n') -- <Enter>
+ screen:expect{grid=[[
+ foo |
+ typed input...line A |
+ line B |
+ {1: } |
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
end)
- it('handles pasting a specific amount of text', function()
- -- Need extra time for this test, specially in ASAN.
- screen.timeout = 60000
- feed_data('i\027[200~'..string.rep('z', 64)..'\027[201~')
+ it("paste: 'nomodifiable' buffer", function()
+ child_session:request('nvim_command', 'set nomodifiable')
+ feed_data('\027[200~fail 1\nfail 2\n\027[201~')
+ screen:expect{any='Vim:E21'}
+ feed_data('\n') -- <Enter>
+ child_session:request('nvim_command', 'set modifiable')
+ feed_data('\027[200~success 1\nsuccess 2\n\027[201~')
+ screen:expect{grid=[[
+ success 1 |
+ success 2 |
+ {1: } |
+ {4:~ }|
+ {5:[No Name] [+] }|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ end)
+
+ -- TODO
+ it('paste: other modes', function()
+ -- Other modes act like CTRL-C + paste.
+ end)
+
+ it('paste: exactly 64 bytes #10311', function()
+ local expected = string.rep('z', 64)
+ feed_data('i')
+ wait_for_mode('i')
+ -- "bracketed paste"
+ feed_data('\027[200~'..expected..'\027[201~')
+ feed_data(' end')
+ expected = expected..' end'
+ expect_child_buf_lines({expected})
screen:expect([[
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz|
- zzzzzzzzzzzzzz{1: } |
+ zzzzzzzzzzzzzz end{1: } |
{4:~ }|
{4:~ }|
{5:[No Name] [+] }|
@@ -197,26 +438,95 @@ describe('TUI', function()
]])
end)
- it('can handle arbitrarily long bursts of input', function()
- -- Need extra time for this test, specially in ASAN.
- screen.timeout = 60000
- feed_command('set ruler')
+ it('paste: big burst of input', function()
+ feed_data(':set ruler\n')
local t = {}
for i = 1, 3000 do
t[i] = 'item ' .. tostring(i)
end
- feed_data('i\027[200~'..table.concat(t, '\n')..'\027[201~')
+ feed_data('i')
+ wait_for_mode('i')
+ -- "bracketed paste"
+ feed_data('\027[200~'..table.concat(t, '\n')..'\027[201~')
+ expect_child_buf_lines(t)
+ feed_data(' end')
+ screen:expect([[
+ item 2997 |
+ item 2998 |
+ item 2999 |
+ item 3000 end{1: } |
+ {5:[No Name] [+] 3000,14 Bot}|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]])
+ feed_data('\027\000') -- ESC: go to Normal mode.
+ wait_for_mode('n')
+ -- Dot-repeat/redo.
+ feed_data('.')
screen:expect([[
item 2997 |
item 2998 |
item 2999 |
- item 3000{1: } |
- {5:[No Name] [+] 3000,10 Bot}|
+ item 3000 en{1:d}d |
+ {5:[No Name] [+] 5999,13 Bot}|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
+ it('paste: forwards spurious "start paste" code', function()
+ -- If multiple "start paste" sequences are sent without a corresponding
+ -- "stop paste" sequence, only the first occurrence should be consumed.
+
+ -- Send the "start paste" sequence.
+ feed_data('i\027[200~')
+ feed_data('\npasted from terminal (1)\n')
+ -- Send spurious "start paste" sequence.
+ feed_data('\027[200~')
+ feed_data('\n')
+ -- Send the "stop paste" sequence.
+ feed_data('\027[201~')
+
+ screen:expect{grid=[[
+ |
+ pasted from terminal (1) |
+ {6:^[}[200~ |
+ {1: } |
+ {5:[No Name] [+] }|
+ {3:-- INSERT --} |
+ {3:-- TERMINAL --} |
+ ]],
+ attr_ids={
+ [1] = {reverse = true},
+ [2] = {background = tonumber('0x00000b')},
+ [3] = {bold = true},
+ [4] = {foreground = tonumber('0x00000c')},
+ [5] = {bold = true, reverse = true},
+ [6] = {foreground = tonumber('0x000051')},
+ }}
+ end)
+
+ it('paste: ignores spurious "stop paste" code', function()
+ -- If "stop paste" sequence is received without a preceding "start paste"
+ -- sequence, it should be ignored.
+ feed_data('i')
+ -- Send "stop paste" sequence.
+ feed_data('\027[201~')
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {5:[No Name] }|
{3:-- INSERT --} |
{3:-- TERMINAL --} |
]])
end)
+ -- TODO
+ it('paste: handles missing "stop paste" code', function()
+ end)
+
it('allows termguicolors to be set at runtime', function()
screen:set_option('rgb', true)
screen:set_default_attr_ids({
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 0009f2c31b..12d0e4f40b 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -110,77 +110,6 @@ describe('mappings', function()
end)
end)
-describe('feeding large chunks of input with <Paste>', function()
- local screen
- before_each(function()
- clear()
- screen = Screen.new()
- screen:attach()
- feed_command('set ruler')
- end)
-
- it('ok', function()
- if helpers.skip_fragile(pending) then
- return
- end
- local t = {}
- for i = 1, 20000 do
- t[i] = 'item ' .. tostring(i)
- end
- feed('i<Paste>')
- screen:expect([[
- ^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- -- INSERT (paste) -- |
- ]])
- feed(table.concat(t, '<Enter>'))
- screen:expect([[
- item 19988 |
- item 19989 |
- item 19990 |
- item 19991 |
- item 19992 |
- item 19993 |
- item 19994 |
- item 19995 |
- item 19996 |
- item 19997 |
- item 19998 |
- item 19999 |
- item 20000^ |
- -- INSERT (paste) -- |
- ]])
- feed('<Paste>')
- screen:expect([[
- item 19988 |
- item 19989 |
- item 19990 |
- item 19991 |
- item 19992 |
- item 19993 |
- item 19994 |
- item 19995 |
- item 19996 |
- item 19997 |
- item 19998 |
- item 19999 |
- item 20000^ |
- -- INSERT -- 20000,11 Bot |
- ]])
- end)
-end)
-
describe('input utf sequences that contain CSI/K_SPECIAL', function()
before_each(clear)
it('ok', function()