Skip to content

Commit

Permalink
test: Add terminal tests
Browse files Browse the repository at this point in the history
- Modify tty-test to allow easier control over the terminal
- Add a new directory with various terminal tests/specifications
- Remove a pending job/pty test.
- Flush stdout in Screen:snapshot_util() (avoid waiting for the test to finish)
- Replace libuv sigwinch watcher by a sigaction handler. libuv randomly fails to
  deliver signals on OSX. Might be related to the problem fixed by
  @bbcddc55ee1e5605657592644be0102ed3a5f104 (under the hoods, libuv uses a pipe
  to deliver signals to the main thread, which might be blocking in some
  situations)
  • Loading branch information
tarruda committed Mar 25, 2015
1 parent 710002c commit 2aa2513
Show file tree
Hide file tree
Showing 12 changed files with 1,553 additions and 44 deletions.
14 changes: 2 additions & 12 deletions test/functional/job/job_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
88 changes: 58 additions & 30 deletions test/functional/job/tty-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <stdlib.h>
#include <uv.h>

uv_tty_t tty;

#ifdef _WIN32
#include <windows.h>
bool owns_tty(void)
Expand All @@ -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

Expand All @@ -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)
{
Expand All @@ -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);
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -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;
}
158 changes: 158 additions & 0 deletions test/functional/terminal/altscreen_spec.lua
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit 2aa2513

Please sign in to comment.