aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/functional/job/job_spec.lua14
-rw-r--r--test/functional/job/tty-test.c88
-rw-r--r--test/functional/terminal/altscreen_spec.lua158
-rw-r--r--test/functional/terminal/buffer_spec.lua159
-rw-r--r--test/functional/terminal/cursor_spec.lua174
-rw-r--r--test/functional/terminal/helpers.lua95
-rw-r--r--test/functional/terminal/highlight_spec.lua163
-rw-r--r--test/functional/terminal/mouse_spec.lua188
-rw-r--r--test/functional/terminal/scrollback_spec.lua348
-rw-r--r--test/functional/terminal/window_spec.lua64
-rw-r--r--test/functional/terminal/window_split_tab_spec.lua138
-rw-r--r--test/functional/ui/screen.lua8
12 files changed, 1553 insertions, 44 deletions
diff --git a/test/functional/job/job_spec.lua b/test/functional/job/job_spec.lua
index c74ae047c5..8981c49744 100644
--- a/test/functional/job/job_spec.lua
+++ b/test/functional/job/job_spec.lua
@@ -174,24 +174,14 @@ describe('jobs', function()
it('echoing input', function()
send('test')
- -- the tty driver will echo input by default
eq('test', next_chunk())
end)
it('resizing window', function()
nvim('command', 'call jobresize(j, 40, 10)')
- eq('screen resized. rows: 10, columns: 40', next_chunk())
+ eq('rows: 10, cols: 40', next_chunk())
nvim('command', 'call jobresize(j, 10, 40)')
- eq('screen resized. rows: 40, columns: 10', next_chunk())
- end)
-
- -- FIXME This test is flawed because there is no telling when the OS will send chunks of data.
- pending('preprocessing ctrl+c with terminal driver', function()
- send('\\<c-c>')
- eq('^Cinterrupt received, press again to exit', next_chunk())
- send('\\<c-c>')
- eq('^Ctty done', next_chunk())
- eq({'notification', 'exit', {0}}, next_message())
+ eq('rows: 40, cols: 10', next_chunk())
end)
end)
end)
diff --git a/test/functional/job/tty-test.c b/test/functional/job/tty-test.c
index b395c9c9a7..40ba131003 100644
--- a/test/functional/job/tty-test.c
+++ b/test/functional/job/tty-test.c
@@ -3,6 +3,8 @@
#include <stdlib.h>
#include <uv.h>
+uv_tty_t tty;
+
#ifdef _WIN32
#include <windows.h>
bool owns_tty(void)
@@ -13,10 +15,10 @@ bool owns_tty(void)
return GetCurrentProcessId() == dwProcessId;
}
#else
+#include <unistd.h>
bool owns_tty(void)
{
- // TODO: Check if the process is the session leader
- return true;
+ return getsid(0) == getpid();
}
#endif
@@ -29,26 +31,20 @@ static void walk_cb(uv_handle_t *handle, void *arg) {
}
}
-static void sigwinch_cb(uv_signal_t *handle, int signum)
+static void sigwinch_handler(int signum)
{
int width, height;
- uv_tty_t *tty = handle->data;
- uv_tty_get_winsize(tty, &width, &height);
- fprintf(stderr, "screen resized. rows: %d, columns: %d\n", height, width);
+ uv_tty_get_winsize(&tty, &width, &height);
+ fprintf(stderr, "rows: %d, cols: %d\n", height, width);
}
-static void sigint_cb(uv_signal_t *handle, int signum)
-{
- bool *interrupted = handle->data;
-
- if (*interrupted) {
- uv_walk(uv_default_loop(), walk_cb, NULL);
- return;
- }
-
- *interrupted = true;
- fprintf(stderr, "interrupt received, press again to exit\n");
-}
+// static void sigwinch_cb(uv_signal_t *handle, int signum)
+// {
+// int width, height;
+// uv_tty_t *tty = handle->data;
+// uv_tty_get_winsize(tty, &width, &height);
+// fprintf(stderr, "rows: %d, cols: %d\n", height, width);
+// }
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
@@ -63,13 +59,20 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
return;
}
- fprintf(stderr, "received data: ");
+ int *interrupted = stream->data;
+
+ for (int i = 0; i < cnt; i++) {
+ if (buf->base[i] == 3) {
+ (*interrupted)++;
+ }
+ }
+
uv_loop_t write_loop;
uv_loop_init(&write_loop);
uv_tty_t out;
uv_tty_init(&write_loop, &out, 1, 0);
uv_write_t req;
- uv_buf_t b = {.base = buf->base, .len = buf->len};
+ uv_buf_t b = {.base = buf->base, .len = (size_t)cnt};
uv_write(&req, (uv_stream_t *)&out, &b, 1, NULL);
uv_run(&write_loop, UV_RUN_DEFAULT);
uv_close((uv_handle_t *)&out, NULL);
@@ -78,6 +81,12 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
abort();
}
free(buf->base);
+
+ if (*interrupted >= 2) {
+ uv_walk(uv_default_loop(), walk_cb, NULL);
+ } else if (*interrupted == 1) {
+ fprintf(stderr, "interrupt received, press again to exit\n");
+ }
}
static void prepare_cb(uv_prepare_t *handle)
@@ -88,6 +97,11 @@ static void prepare_cb(uv_prepare_t *handle)
int main(int argc, char **argv)
{
+ if (!owns_tty()) {
+ fprintf(stderr, "process does not own the terminal\n");
+ exit(2);
+ }
+
if (!is_terminal(stdin)) {
fprintf(stderr, "stdin is not a terminal\n");
exit(2);
@@ -103,20 +117,34 @@ int main(int argc, char **argv)
exit(2);
}
- bool interrupted = false;
+ if (argc > 1) {
+ int count = atoi(argv[1]);
+ for (int i = 0; i < count; ++i) {
+ printf("line%d\n", i);
+ }
+ fflush(stdout);
+ return 0;
+ }
+
+ int interrupted = 0;
uv_prepare_t prepare;
uv_prepare_init(uv_default_loop(), &prepare);
uv_prepare_start(&prepare, prepare_cb);
- uv_tty_t tty;
+ // uv_tty_t tty;
uv_tty_init(uv_default_loop(), &tty, fileno(stderr), 1);
+ uv_tty_set_mode(&tty, UV_TTY_MODE_RAW);
+ tty.data = &interrupted;
uv_read_start((uv_stream_t *)&tty, alloc_cb, read_cb);
- uv_signal_t sigwinch_watcher, sigint_watcher;
- uv_signal_init(uv_default_loop(), &sigwinch_watcher);
- sigwinch_watcher.data = &tty;
- uv_signal_start(&sigwinch_watcher, sigwinch_cb, SIGWINCH);
- uv_signal_init(uv_default_loop(), &sigint_watcher);
- sigint_watcher.data = &interrupted;
- uv_signal_start(&sigint_watcher, sigint_cb, SIGINT);
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sigwinch_handler;
+ sigaction(SIGWINCH, &sa, NULL);
+ // uv_signal_t sigwinch_watcher;
+ // uv_signal_init(uv_default_loop(), &sigwinch_watcher);
+ // sigwinch_watcher.data = &tty;
+ // uv_signal_start(&sigwinch_watcher, sigwinch_cb, SIGWINCH);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
- fprintf(stderr, "tty done\n");
+
+ return 0;
}
diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua
new file mode 100644
index 0000000000..9ec0fc7c5a
--- /dev/null
+++ b/test/functional/terminal/altscreen_spec.lua
@@ -0,0 +1,158 @@
+local helpers = require('test.functional.helpers')
+local thelpers = require('test.functional.terminal.helpers')
+local Screen = require('test.functional.ui.screen')
+local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf
+local feed = helpers.feed
+local feed_data = thelpers.feed_data
+local enter_altscreen = thelpers.enter_altscreen
+local exit_altscreen = thelpers.exit_altscreen
+
+describe('terminal altscreen', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = thelpers.screen_setup()
+ feed_data({'line1', 'line2', 'line3', 'line4', 'line5', 'line6',
+ 'line7', 'line8', ''})
+ screen:expect([[
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ line8 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ enter_altscreen()
+ screen:expect([[
+ |
+ |
+ |
+ |
+ |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ eq(10, curbuf('line_count'))
+ end)
+
+ it('wont clear lines already in the scrollback', function()
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^tty ready |
+ line1 |
+ line2 |
+ line3 |
+ |
+ |
+ |
+ ]])
+ end)
+
+ describe('on exit', function()
+ before_each(exit_altscreen)
+
+ it('restores buffer state', function()
+ screen:expect([[
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ line8 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ line5 |
+ |
+ ]])
+ end)
+ end)
+
+ describe('with lines printed after the screen height limit', function()
+ before_each(function()
+ feed_data({'line9', 'line10', 'line11', 'line12', 'line13',
+ 'line14', 'line15', 'line16', ''})
+ screen:expect([[
+ line12 |
+ line13 |
+ line14 |
+ line15 |
+ line16 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('wont modify line count', function()
+ eq(10, curbuf('line_count'))
+ end)
+
+ it('wont modify lines in the scrollback', function()
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line12 |
+ line13 |
+ |
+ ]])
+ end)
+ end)
+
+ describe('after height is decreased by 2', function()
+ local function wait_removal()
+ screen:try_resize(screen._width, screen._height - 2)
+ screen:expect([[
+ |
+ |
+ rows: 4, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end
+
+ it('removes 2 lines from the bottom of the visible buffer', function()
+ wait_removal()
+ feed('<c-\\><c-n>4k')
+ screen:expect([[
+ ^line3 |
+ |
+ |
+ rows: 4, cols: 50 |
+ |
+ ]])
+ eq(8, curbuf('line_count'))
+ end)
+
+ describe('and after exit', function()
+ before_each(function()
+ wait_removal()
+ exit_altscreen()
+ end)
+
+ it('restore buffer state', function()
+ -- FIXME(tarruda): Note that the last line was lost after restoring the
+ -- screen. This is a libvterm bug: When the main screen is restored it
+ -- seems to "cut" lines that would have been left below the new visible
+ -- screen.
+ screen:expect([[
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+ end)
+end)
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
new file mode 100644
index 0000000000..857679c4b3
--- /dev/null
+++ b/test/functional/terminal/buffer_spec.lua
@@ -0,0 +1,159 @@
+local helpers = require('test.functional.helpers')
+local Screen = require('test.functional.ui.screen')
+local thelpers = require('test.functional.terminal.helpers')
+local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
+local wait, execute, eq = helpers.wait, helpers.execute, helpers.eq
+
+
+describe('terminal buffer', function()
+ local screen
+
+ before_each(function()
+ clear()
+ execute('set modifiable swapfile undolevels=20')
+ wait()
+ screen = thelpers.screen_setup()
+ end)
+
+ describe('when a new file is edited', function()
+ before_each(function()
+ feed('<c-\\><c-n>:set bufhidden=wipe<cr>:enew<cr>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :enew |
+ ]])
+ end)
+
+ it('will hide the buffer, ignoring the bufhidden option', function()
+ feed(':bnext:l<esc>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+ end)
+
+ describe('swap and undo', function()
+ before_each(function()
+ feed('<c-\\><c-n>')
+ screen:expect([[
+ tty ready |
+ {2: } |
+ |
+ |
+ |
+ ^ |
+ |
+ ]])
+ end)
+
+ it('does not create swap files', function()
+ local swapfile = nvim('command_output', 'swapname'):gsub('\n', '')
+ eq(nil, io.open(swapfile))
+ end)
+
+ it('does not create undofiles files', function()
+ local undofile = nvim('eval', 'undofile(bufname("%"))')
+ eq(nil, io.open(undofile))
+ end)
+ end)
+
+ it('cannot be modified directly', function()
+ feed('<c-\\><c-n>dd')
+ screen:expect([[
+ tty ready |
+ {2: } |
+ |
+ |
+ |
+ ^ |
+ E21: Cannot make changes, 'modifiable' is off |
+ ]])
+ end)
+
+ it('sends data to the terminal when the "put" operator is used', function()
+ feed('<c-\\><c-n>gg"ayj')
+ execute('let @a = "appended " . @a')
+ feed('"ap"ap')
+ screen:expect([[
+ ^tty ready |
+ appended tty ready |
+ appended tty ready |
+ {2: } |
+ |
+ |
+ :let @a = "appended " . @a |
+ ]])
+ -- operator count is also taken into consideration
+ feed('3"ap')
+ screen:expect([[
+ ^tty ready |
+ appended tty ready |
+ appended tty ready |
+ appended tty ready |
+ appended tty ready |
+ appended tty ready |
+ :let @a = "appended " . @a |
+ ]])
+ end)
+
+ it('sends data to the terminal when the ":put" command is used', function()
+ feed('<c-\\><c-n>gg"ayj')
+ execute('let @a = "appended " . @a')
+ execute('put a')
+ screen:expect([[
+ ^tty ready |
+ appended tty ready |
+ {2: } |
+ |
+ |
+ |
+ :put a |
+ ]])
+ -- line argument is only used to move the cursor
+ execute('6put a')
+ screen:expect([[
+ tty ready |
+ appended tty ready |
+ appended tty ready |
+ {2: } |
+ |
+ ^ |
+ :6put a |
+ ]])
+ end)
+
+ it('can be deleted', function()
+ feed('<c-\\><c-n>:bd!<cr>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :bd! |
+ ]])
+ execute('bnext')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :bnext |
+ ]])
+ end)
+end)
+
diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua
new file mode 100644
index 0000000000..3e3f9cbf4f
--- /dev/null
+++ b/test/functional/terminal/cursor_spec.lua
@@ -0,0 +1,174 @@
+local helpers = require('test.functional.helpers')
+local Screen = require('test.functional.ui.screen')
+local thelpers = require('test.functional.terminal.helpers')
+local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
+local nvim_dir, execute, eq = helpers.nvim_dir, helpers.execute, helpers.eq
+local hide_cursor = thelpers.hide_cursor
+local show_cursor = thelpers.show_cursor
+
+
+describe('terminal cursor', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = thelpers.screen_setup()
+ end)
+
+
+ it('moves the screen cursor when focused', function()
+ thelpers.feed_data('testing cursor')
+ screen:expect([[
+ tty ready |
+ testing cursor{1: } |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ eq(2, screen._cursor.row)
+ eq(15, screen._cursor.col)
+ end)
+
+ it('is highlighted when not focused', function()
+ feed('<c-\\><c-n>')
+ screen:expect([[
+ tty ready |
+ {2: } |
+ |
+ |
+ |
+ ^ |
+ |
+ ]])
+ end)
+
+ describe('with number column', function()
+ before_each(function()
+ feed('<c-\\><c-n>:set number<cr>')
+ end)
+
+ it('is positioned correctly when unfocused', function()
+ screen:expect([[
+ 1 tty ready |
+ 2 {2: } |
+ 3 |
+ 4 |
+ 5 |
+ 6 ^ |
+ :set number |
+ ]])
+ end)
+
+ it('is positioned correctly when focused', function()
+ feed('i')
+ screen:expect([[
+ 1 tty ready |
+ 2 {1: } |
+ 3 |
+ 4 |
+ 5 |
+ 6 |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+
+ describe('when invisible', function()
+ it('is not highlighted and is detached from screen cursor', function()
+ hide_cursor()
+ screen:expect([[
+ tty ready |
+ |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ show_cursor()
+ screen:expect([[
+ tty ready |
+ {1: } |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ -- same for when the terminal is unfocused
+ feed('<c-\\><c-n>')
+ hide_cursor()
+ screen:expect([[
+ tty ready |
+ |
+ |
+ |
+ |
+ ^ |
+ |
+ ]])
+ show_cursor()
+ screen:expect([[
+ tty ready |
+ {2: } |
+ |
+ |
+ |
+ ^ |
+ |
+ ]])
+ end)
+ end)
+end)
+
+
+describe('cursor with customized highlighting', function()
+ local screen
+
+ before_each(function()
+ clear()
+ nvim('set_var', 'terminal_focused_cursor_highlight', 'CursorFocused')
+ nvim('set_var', 'terminal_unfocused_cursor_highlight', 'CursorUnfocused')
+ nvim('command', 'highlight CursorFocused ctermfg=45 ctermbg=46')
+ nvim('command', 'highlight CursorUnfocused ctermfg=55 ctermbg=56')
+ screen = Screen.new(50, 7)
+ screen:set_default_attr_ids({
+ [1] = {foreground = 45, background = 46},
+ [2] = {foreground = 55, background = 56}
+ })
+ screen:set_default_attr_ignore({
+ [1] = {bold = true},
+ [2] = {foreground = 12},
+ [3] = {bold = true, reverse = true},
+ [5] = {background = 11},
+ [6] = {foreground = 130},
+ })
+ screen:attach(false)
+ execute('term "' ..nvim_dir.. '/tty-test"')
+ end)
+
+ it('overrides the default highlighting', function()
+ screen:expect([[
+ tty ready |
+ {1: } |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ feed('<c-\\><c-n>')
+ screen:expect([[
+ tty ready |
+ {2: } |
+ |
+ |
+ |
+ ^ |
+ |
+ ]])
+ end)
+end)
+
diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua
new file mode 100644
index 0000000000..631dc579d3
--- /dev/null
+++ b/test/functional/terminal/helpers.lua
@@ -0,0 +1,95 @@
+local helpers = require('test.functional.helpers')
+local Screen = require('test.functional.ui.screen')
+local nvim_dir = helpers.nvim_dir
+local execute, nvim = helpers.execute, helpers.nvim
+
+local function feed_data(data)
+ nvim('set_var', 'term_data', data)
+ nvim('command', 'call jobsend(b:terminal_job_id, term_data)')
+end
+
+local function feed_termcode(data)
+ -- feed with the job API
+ nvim('command', 'call jobsend(b:terminal_job_id, "\\x1b'..data..'")')
+end
+-- some helpers for controlling the terminal. the codes were taken from
+-- infocmp xterm-256color which is less what libvterm understands
+-- civis/cnorm
+local function hide_cursor() feed_termcode('[?25l') end
+local function show_cursor() feed_termcode('[?25h') end
+-- smcup/rmcup
+local function enter_altscreen() feed_termcode('[?1049h') end
+local function exit_altscreen() feed_termcode('[?1049l') end
+-- character attributes
+local function set_fg(num) feed_termcode('[38;5;'..num..'m') end
+local function set_bg(num) feed_termcode('[48;5;'..num..'m') end
+local function set_bold() feed_termcode('[1m') end
+local function set_italic() feed_termcode('[3m') end
+local function set_underline() feed_termcode('[4m') end
+local function clear_attrs() feed_termcode('[0;10m') end
+-- mouse
+local function enable_mouse() feed_termcode('[?1002h') end
+local function disable_mouse() feed_termcode('[?1002l') end
+
+
+
+local function screen_setup(extra_height)
+ nvim('set_var', 'terminal_scrollback_buffer_size', 10)
+ if not extra_height then extra_height = 0 end
+ local screen = Screen.new(50, 7 + extra_height)
+ screen:set_default_attr_ids({
+ [1] = {reverse = true}, -- focused cursor
+ [2] = {background = 11}, -- unfocused cursor
+ })
+ screen:set_default_attr_ignore({
+ [1] = {bold = true},
+ [2] = {foreground = 12},
+ [3] = {bold = true, reverse = true},
+ [5] = {background = 11},
+ [6] = {foreground = 130},
+ [7] = {foreground = 15, background = 1}, -- error message
+ })
+
+ screen:attach(false)
+ -- tty-test puts the terminal into raw mode and echoes all input. tests are
+ -- done by feeding it with terminfo codes to control the display and
+ -- verifying output with screen:expect.
+ execute('term ' ..nvim_dir.. '/tty-test')
+ -- wait for "tty ready" to be printed before each test or the terminal may
+ -- still be in canonical mode(will echo characters for example)
+ --
+ local empty_line = ' '
+ local expected = {
+ 'tty ready ',
+ '{1: } ',
+ empty_line,
+ empty_line,
+ empty_line,
+ empty_line,
+ }
+ for i = 1, extra_height do
+ table.insert(expected, empty_line)
+ end
+
+ table.insert(expected, '-- TERMINAL -- ')
+ screen:expect(table.concat(expected, '\n'))
+ return screen
+end
+
+return {
+ feed_data = feed_data,
+ feed_termcode = feed_termcode,
+ hide_cursor = hide_cursor,
+ show_cursor = show_cursor,
+ enter_altscreen = enter_altscreen,
+ exit_altscreen = exit_altscreen,
+ set_fg = set_fg,
+ set_bg = set_bg,
+ set_bold = set_bold,
+ set_italic = set_italic,
+ set_underline = set_underline,
+ clear_attrs = clear_attrs,
+ enable_mouse = enable_mouse,
+ disable_mouse = disable_mouse,
+ screen_setup = screen_setup
+}
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
new file mode 100644
index 0000000000..59b0d2c19d
--- /dev/null
+++ b/test/functional/terminal/highlight_spec.lua
@@ -0,0 +1,163 @@
+local helpers = require('test.functional.helpers')
+local Screen = require('test.functional.ui.screen')
+local thelpers = require('test.functional.terminal.helpers')
+local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
+local nvim_dir, execute = helpers.nvim_dir, helpers.execute
+
+
+describe('terminal window highlighting', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 7)
+ screen:set_default_attr_ids({
+ [1] = {foreground = 45},
+ [2] = {background = 46},
+ [3] = {foreground = 45, background = 46},
+ [4] = {bold = true, italic = true, underline = true}
+ })
+ screen:set_default_attr_ignore({
+ [1] = {bold = true},
+ [2] = {foreground = 12},
+ [3] = {bold = true, reverse = true},
+ [5] = {background = 11},
+ [6] = {foreground = 130},
+ [7] = {reverse = true},
+ [8] = {background = 11}
+ })
+ screen:attach(false)
+ execute('term "' ..nvim_dir.. '/tty-test"')
+ screen:expect([[
+ tty ready |
+ |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ function descr(title, attr_num, set_attrs_fn)
+ local function sub(s)
+ return s:gsub('NUM', attr_num)
+ end
+
+ describe(title, function()
+ before_each(function()
+ set_attrs_fn()
+ thelpers.feed_data('text')
+ thelpers.clear_attrs()
+ thelpers.feed_data('text')
+ end)
+
+ local function pass_attrs()
+ local s = sub([[
+ tty ready |
+ {NUM:text}text |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ screen:expect(s)
+ end
+
+ it('will pass the corresponding attributes', pass_attrs)
+
+ it('will pass the corresponding attributes on scrollback', function()
+ pass_attrs()
+ local lines = {}
+ for i = 1, 8 do
+ table.insert(lines, 'line'..tostring(i))
+ end
+ table.insert(lines, '')
+ thelpers.feed_data(lines)
+ screen:expect([[
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ line8 |
+ |
+ -- TERMINAL -- |
+ ]])
+ feed('<c-\\><c-n>gg')
+ local s = sub([[
+ ^tty ready |
+ {NUM:text}textline1 |
+ line2 |
+ line3 |
+ line4 |
+ line5 |
+ |
+ ]])
+ screen:expect(s)
+ end)
+ end)
+ end
+
+ descr('foreground', 1, function() thelpers.set_fg(45) end)
+ descr('background', 2, function() thelpers.set_bg(46) end)
+ descr('foreground and background', 3, function()
+ thelpers.set_fg(45)
+ thelpers.set_bg(46)
+ end)
+ descr('bold, italics and underline', 4, function()
+ thelpers.set_bold()
+ thelpers.set_italic()
+ thelpers.set_underline()
+ end)
+end)
+
+
+describe('terminal window highlighting with custom palette', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 7)
+ screen:set_default_attr_ids({
+ [1] = {foreground = 1193046}
+ })
+ screen:set_default_attr_ignore({
+ [1] = {bold = true},
+ [2] = {foreground = 12},
+ [3] = {bold = true, reverse = true},
+ [5] = {background = 11},
+ [6] = {foreground = 130},
+ [7] = {reverse = true},
+ [8] = {background = 11}
+ })
+ screen:attach(true)
+ nvim('set_var', 'terminal_color_3', '#123456')
+ execute('term "' ..nvim_dir.. '/tty-test"')
+ screen:expect([[
+ tty ready |
+ |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('will use the custom color', function()
+ thelpers.set_fg(3)
+ thelpers.feed_data('text')
+ thelpers.clear_attrs()
+ thelpers.feed_data('text')
+ screen:expect([[
+ tty ready |
+ {1:text}text |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ end)
+end)
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
new file mode 100644
index 0000000000..b8f6214f8f
--- /dev/null
+++ b/test/functional/terminal/mouse_spec.lua
@@ -0,0 +1,188 @@
+local Screen = require('test.functional.ui.screen')
+local helpers = require('test.functional.helpers')
+local thelpers = require('test.functional.terminal.helpers')
+local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf
+local feed, execute, nvim = helpers.feed, helpers.execute, helpers.nvim
+local feed_data = thelpers.feed_data
+
+describe('terminal mouse', function()
+ local screen
+
+ before_each(function()
+ clear()
+ nvim('set_option', 'statusline', '==========')
+ nvim('command', 'highlight StatusLine cterm=NONE')
+ nvim('command', 'highlight StatusLineNC cterm=NONE')
+ nvim('command', 'highlight VertSplit cterm=NONE')
+ screen = thelpers.screen_setup()
+ local lines = {}
+ for i = 1, 30 do
+ table.insert(lines, 'line'..tostring(i))
+ end
+ table.insert(lines, '')
+ feed_data(lines)
+ screen:expect([[
+ line26 |
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ describe('when the terminal has focus', function()
+ it('will exit focus when scrolled', function()
+ feed('<MouseDown><0,0>')
+ screen:expect([[
+ line23 |
+ line24 |
+ line25 |
+ line26 |
+ line27 |
+ ^line28 |
+ |
+ ]])
+ end)
+
+ describe('with mouse events enabled by the program', function()
+ before_each(function()
+ thelpers.enable_mouse()
+ thelpers.feed_data('mouse enabled\n')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('will forward mouse clicks to the program', function()
+ feed('<LeftMouse><1,2>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ "#{1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('will forward mouse scroll to the program', function()
+ feed('<MouseDown><0,0>')
+ screen:expect([[
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ mouse enabled |
+ `!!{1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+
+ describe('with a split window and other buffer', function()
+ before_each(function()
+ feed('<c-\\><c-n>:vsp<cr>')
+ screen:expect([[
+ line21 |line21 |
+ line22 |line22 |
+ line23 |line23 |
+ line24 |line24 |
+ ^rows: 5, cols: 24 |rows: 5, cols: 24 |
+ ========== ========== |
+ |
+ ]])
+ feed(':enew | set number<cr>')
+ screen:expect([[
+ 1 ^ |line21 |
+ ~ |line22 |
+ ~ |line23 |
+ ~ |line24 |
+ ~ |rows: 5, cols: 24 |
+ ========== ========== |
+ :enew | set number |
+ ]])
+ feed('30iline\n<esc>')
+ screen:expect([[
+ 27 line |line21 |
+ 28 line |line22 |
+ 29 line |line23 |
+ 30 line |line24 |
+ 31 ^ |rows: 5, cols: 24 |
+ ========== ========== |
+ |
+ ]])
+ feed('<c-w>li')
+ screen:expect([[
+ 27 line |line22 |
+ 28 line |line23 |
+ 29 line |line24 |
+ 30 line |rows: 5, cols: 24 |
+ 31 |{1: } |
+ ========== ========== |
+ -- TERMINAL -- |
+ ]])
+ -- enabling mouse won't affect interaction with other windows
+ thelpers.enable_mouse()
+ thelpers.feed_data('mouse enabled\n')
+ screen:expect([[
+ 27 line |line23 |
+ 28 line |line24 |
+ 29 line |rows: 5, cols: 24 |
+ 30 line |mouse enabled |
+ 31 |{1: } |
+ ========== ========== |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('wont lose focus if another window is scrolled', function()
+ feed('<MouseDown><0,0><MouseDown><0,0>')
+ screen:expect([[
+ 21 line |line23 |
+ 22 line |line24 |
+ 23 line |rows: 5, cols: 24 |
+ 24 line |mouse enabled |
+ 25 line |{1: } |
+ ========== ========== |
+ -- TERMINAL -- |
+ ]])
+ feed('<S-MouseUp><0,0>')
+ screen:expect([[
+ 26 line |line23 |
+ 27 line |line24 |
+ 28 line |rows: 5, cols: 24 |
+ 29 line |mouse enabled |
+ 30 line |{1: } |
+ ========== ========== |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('will lose focus if another window is clicked', function()
+ feed('<LeftMouse><5,1>')
+ screen:expect([[
+ 27 line |line23 |
+ 28 l^ine |line24 |
+ 29 line |rows: 5, cols: 24 |
+ 30 line |mouse enabled |
+ 31 |{2: } |
+ ========== ========== |
+ |
+ ]])
+ end)
+ end)
+ end)
+end)
diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua
new file mode 100644
index 0000000000..d9b7534fac
--- /dev/null
+++ b/test/functional/terminal/scrollback_spec.lua
@@ -0,0 +1,348 @@
+local Screen = require('test.functional.ui.screen')
+local helpers = require('test.functional.helpers')
+local thelpers = require('test.functional.terminal.helpers')
+local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf
+local feed, nvim_dir, execute = helpers.feed, helpers.nvim_dir, helpers.execute
+local wait = helpers.wait
+local feed_data = thelpers.feed_data
+
+describe('terminal scrollback', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = thelpers.screen_setup()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ describe('when the limit is crossed', function()
+ before_each(function()
+ local lines = {}
+ for i = 1, 30 do
+ table.insert(lines, 'line'..tostring(i))
+ end
+ table.insert(lines, '')
+ feed_data(lines)
+ screen:expect([[
+ line26 |
+ line27 |
+ line28 |
+ line29 |
+ line30 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('will delete extra lines at the top', function()
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^line16 |
+ line17 |
+ line18 |
+ line19 |
+ line20 |
+ line21 |
+ |
+ ]])
+ end)
+ end)
+
+ describe('with the cursor at the last row', function()
+ before_each(function()
+ feed_data({'line1', 'line2', 'line3', 'line4', ''})
+ screen:expect([[
+ tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ describe('and 1 line is printed', function()
+ before_each(function() feed_data({'line5', ''}) end)
+
+ it('will hide the top line', function()
+ screen:expect([[
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ line5 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ eq(7, curbuf('line_count'))
+ end)
+
+ describe('and then 3 more lines are printed', function()
+ before_each(function() feed_data({'line6', 'line7', 'line8'}) end)
+
+ it('will hide the top 4 lines', function()
+ screen:expect([[
+ line3 |
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ line8{1: } |
+ -- TERMINAL -- |
+ ]])
+
+ feed('<c-\\><c-n>6k')
+ screen:expect([[
+ ^line2 |
+ line3 |
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ |
+ ]])
+
+ feed('gg')
+ screen:expect([[
+ ^tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ line5 |
+ |
+ ]])
+
+ feed('G')
+ screen:expect([[
+ line3 |
+ line4 |
+ line5 |
+ line6 |
+ line7 |
+ ^line8{2: } |
+ |
+ ]])
+ end)
+ end)
+ end)
+
+
+ describe('and the height is decreased by 1', function()
+ local function will_hide_top_line()
+ screen:try_resize(screen._width, screen._height - 1)
+ screen:expect([[
+ line2 |
+ line3 |
+ line4 |
+ rows: 5, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end
+
+ it('will hide top line', will_hide_top_line)
+
+ describe('and then decreased by 2', function()
+ before_each(function()
+ will_hide_top_line()
+ screen:try_resize(screen._width, screen._height - 2)
+ end)
+
+ it('will hide the top 3 lines', function()
+ screen:expect([[
+ rows: 5, cols: 50 |
+ rows: 3, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ eq(8, curbuf('line_count'))
+ feed('<c-\\><c-n>3k')
+ screen:expect([[
+ ^line4 |
+ rows: 5, cols: 50 |
+ rows: 3, cols: 50 |
+ |
+ ]])
+ end)
+ end)
+ end)
+ end)
+
+ describe('with empty lines after the cursor', function()
+ describe('and the height is decreased by 2', function()
+ before_each(function()
+ screen:try_resize(screen._width, screen._height - 2)
+ end)
+
+ local function will_delete_last_two_lines()
+ screen:expect([[
+ tty ready |
+ rows: 4, cols: 50 |
+ {1: } |
+ |
+ -- TERMINAL -- |
+ ]])
+ eq(4, curbuf('line_count'))
+ end
+
+ it('will delete the last two empty lines', will_delete_last_two_lines)
+
+ describe('and then decreased by 1', function()
+ before_each(function()
+ will_delete_last_two_lines()
+ screen:try_resize(screen._width, screen._height - 1)
+ end)
+
+ it('will delete the last line and hide the first', function()
+ screen:expect([[
+ rows: 4, cols: 50 |
+ rows: 3, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ eq(4, curbuf('line_count'))
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^tty ready |
+ rows: 4, cols: 50 |
+ rows: 3, cols: 50 |
+ |
+ ]])
+ feed('a')
+ screen:expect([[
+ rows: 4, cols: 50 |
+ rows: 3, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+ end)
+ end)
+
+ describe('with 4 lines hidden in the scrollback', function()
+ before_each(function()
+ feed_data({'line1', 'line2', 'line3', 'line4', ''})
+ screen:expect([[
+ tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ screen:try_resize(screen._width, screen._height - 3)
+ screen:expect([[
+ line4 |
+ rows: 3, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ eq(7, curbuf('line_count'))
+ end)
+
+ describe('and the height is increased by 1', function()
+ local function pop_then_push()
+ screen:try_resize(screen._width, screen._height + 1)
+ screen:expect([[
+ line4 |
+ rows: 3, cols: 50 |
+ rows: 4, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end
+
+ it('will pop 1 line and then push it back', pop_then_push)
+
+ describe('and then by 3', function()
+ before_each(function()
+ pop_then_push()
+ eq(8, curbuf('line_count'))
+ screen:try_resize(screen._width, screen._height + 3)
+ end)
+
+ local function pop3_then_push1()
+ screen:expect([[
+ line2 |
+ line3 |
+ line4 |
+ rows: 3, cols: 50 |
+ rows: 4, cols: 50 |
+ rows: 7, cols: 50 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ eq(9, curbuf('line_count'))
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ rows: 3, cols: 50 |
+ rows: 4, cols: 50 |
+ |
+ ]])
+ end
+
+ it('will pop 3 lines and then push one back', pop3_then_push1)
+
+ describe('and then by 4', function()
+ before_each(function()
+ pop3_then_push1()
+ feed('Gi')
+ screen:try_resize(screen._width, screen._height + 4)
+ end)
+
+ it('will show all lines and leave a blank one at the end', function()
+ screen:expect([[
+ tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ rows: 3, cols: 50 |
+ rows: 4, cols: 50 |
+ rows: 7, cols: 50 |
+ rows: 11, cols: 50 |
+ {1: } |
+ |
+ -- TERMINAL -- |
+ ]])
+ -- since there's an empty line after the cursor, the buffer line
+ -- count equals the terminal screen height
+ eq(11, curbuf('line_count'))
+ end)
+ end)
+ end)
+ end)
+ end)
+end)
+
+describe('terminal prints more lines than the screen height and exits', function()
+ it('will push extra lines to scrollback', function()
+ clear()
+ local screen = Screen.new(50, 7)
+ screen:attach(false)
+ execute('term ' ..nvim_dir.. '/tty-test 10')
+ wait()
+ screen:expect([[
+ line6 |
+ line7 |
+ line8 |
+ line9 |
+ |
+ [Program exited, press any key to close] |
+ -- TERMINAL -- |
+ ]])
+ end)
+end)
+
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
new file mode 100644
index 0000000000..234950638e
--- /dev/null
+++ b/test/functional/terminal/window_spec.lua
@@ -0,0 +1,64 @@
+local helpers = require('test.functional.helpers')
+local thelpers = require('test.functional.terminal.helpers')
+local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
+local wait, eq = helpers.wait, helpers.eq
+
+
+describe('terminal window', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = thelpers.screen_setup()
+ end)
+
+ describe('with colorcolumn set', function()
+ before_each(function()
+ feed('<c-\\><c-n>:set colorcolumn=20<cr>i')
+ wait()
+ end)
+
+ it('wont show the color column', function()
+ screen:expect([[
+ tty ready |
+ {1: } |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+
+ describe('with fold set', function()
+ before_each(function()
+ feed('<c-\\><c-n>:set foldenable foldmethod=manual<cr>i')
+ thelpers.feed_data({'line1', 'line2', 'line3', 'line4', ''})
+ screen:expect([[
+ tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+
+ it('wont show any folds', function()
+ feed('<c-\\><c-n>ggvGzf')
+ wait()
+ screen:expect([[
+ ^tty ready |
+ line1 |
+ line2 |
+ line3 |
+ line4 |
+ {2: } |
+ |
+ ]])
+ end)
+ end)
+end)
+
diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua
new file mode 100644
index 0000000000..c102b1f133
--- /dev/null
+++ b/test/functional/terminal/window_split_tab_spec.lua
@@ -0,0 +1,138 @@
+local helpers = require('test.functional.helpers')
+local thelpers = require('test.functional.terminal.helpers')
+local clear, eq, curbuf = helpers.clear, helpers.eq, helpers.curbuf
+local feed, nvim = helpers.feed, helpers.nvim
+local feed_data = thelpers.feed_data
+
+describe('terminal', function()
+ local screen
+
+ before_each(function()
+ clear()
+ -- set the statusline to a constant value because of variables like pid
+ -- and current directory and to improve visibility of splits
+ nvim('set_option', 'statusline', '==========')
+ nvim('command', 'highlight StatusLine cterm=NONE')
+ nvim('command', 'highlight StatusLineNC cterm=NONE')
+ nvim('command', 'highlight VertSplit cterm=NONE')
+ screen = thelpers.screen_setup(3)
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ describe('when the screen is resized', function()
+ it('will forward a resize request to the program', function()
+ screen:try_resize(screen._width + 3, screen._height + 5)
+ screen:expect([[
+ tty ready |
+ rows: 14, cols: 53 |
+ {1: } |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ screen:try_resize(screen._width - 6, screen._height - 10)
+ screen:expect([[
+ tty ready |
+ rows: 14, cols: 53 |
+ rows: 4, cols: 47 |
+ {1: } |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+
+ describe('split horizontally', function()
+ before_each(function()
+ nvim('command', 'sp')
+ end)
+
+ local function reduce_height()
+ screen:expect([[
+ tty ready |
+ rows: 3, cols: 50 |
+ {1: } |
+ ~ |
+ ========== |
+ tty ready |
+ rows: 3, cols: 50 |
+ {2: } |
+ ========== |
+ -- TERMINAL -- |
+ ]])
+ end
+
+ it('uses the minimum height of all window displaying it', reduce_height)
+
+ describe('and then vertically', function()
+ before_each(function()
+ reduce_height()
+ nvim('command', 'vsp')
+ end)
+
+ local function reduce_width()
+ screen:expect([[
+ rows: 3, cols: 50 |rows: 3, cols: 50 |
+ rows: 3, cols: 24 |rows: 3, cols: 24 |
+ {1: } |{2: } |
+ ~ |~ |
+ ========== ========== |
+ rows: 3, cols: 50 |
+ rows: 3, cols: 24 |
+ {2: } |
+ ========== |
+ -- TERMINAL -- |
+ ]])
+ feed('<c-\\><c-n>gg')
+ screen:expect([[
+ ^tty ready |rows: 3, cols: 50 |
+ rows: 3, cols: 50 |rows: 3, cols: 24 |
+ rows: 3, cols: 24 |{2: } |
+ {2: } |~ |
+ ========== ========== |
+ rows: 3, cols: 50 |
+ rows: 3, cols: 24 |
+ {2: } |
+ ========== |
+ |
+ ]])
+ end
+
+ it('uses the minimum width of all window displaying it', reduce_width)
+
+ describe('and then closes one of the vertical splits with q:', function()
+ before_each(function()
+ reduce_width()
+ nvim('command', 'q')
+ feed('<c-w>ja')
+ end)
+
+ it('will restore the width', function()
+ screen:expect([[
+ rows: 3, cols: 24 |
+ rows: 3, cols: 50 |
+ {2: } |
+ ~ |
+ ========== |
+ rows: 3, cols: 24 |
+ rows: 3, cols: 50 |
+ {1: } |
+ ========== |
+ -- TERMINAL -- |
+ ]])
+ end)
+ end)
+ end)
+ end)
+end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index c60944bbb0..174538df6e 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -190,8 +190,11 @@ function Screen:set_default_attr_ignore(attr_ignore)
self._default_attr_ignore = attr_ignore
end
-function Screen:attach()
- request('ui_attach', self._width, self._height, true)
+function Screen:attach(rgb)
+ if rgb == nil then
+ rgb = true
+ end
+ request('ui_attach', self._width, self._height, rgb)
end
function Screen:detach()
@@ -500,6 +503,7 @@ function Screen:snapshot_util(attrs, ignore)
else
print( "]], "..attrstr..")\n")
end
+ io.stdout:flush()
end
function pprint_attrs(attrs)