conhost/tests: Add tty output tests.
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
3038674eef
commit
4100c43576
|
@ -115,6 +115,14 @@ static BOOL skip_byte_(unsigned int line, char ch)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
#define expect_hide_cursor() expect_hide_cursor_(__LINE__)
|
||||
static void expect_hide_cursor_(unsigned int line)
|
||||
{
|
||||
if (!console_output_count) fetch_console_output_(line);
|
||||
ok_(__FILE__,line)(skip_sequence_(line, "\x1b[25l") || broken(skip_sequence_(line, "\x1b[?25l")),
|
||||
"expected hide cursor escape\n");
|
||||
}
|
||||
|
||||
#define skip_hide_cursor() skip_hide_cursor_(__LINE__)
|
||||
static BOOL skip_hide_cursor_(unsigned int line)
|
||||
{
|
||||
|
@ -122,9 +130,25 @@ static BOOL skip_hide_cursor_(unsigned int line)
|
|||
return skip_sequence_(line, "\x1b[25l") || broken(skip_sequence_(line, "\x1b[?25l"));
|
||||
}
|
||||
|
||||
#define expect_erase_line(a) expect_erase_line_(__LINE__,a)
|
||||
static BOOL expect_erase_line_(unsigned line, unsigned int cnt)
|
||||
{
|
||||
char buf[16];
|
||||
if (skip_sequence("\x1b[K")) return FALSE;
|
||||
ok(broken(1), "expected erase line\n");
|
||||
sprintf(buf, "\x1b[%uX", cnt);
|
||||
expect_output_sequence(buf); /* erase the rest of the line */
|
||||
sprintf(buf, "\x1b[%uC", cnt);
|
||||
expect_output_sequence(buf); /* move cursor to the end of the line */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
enum req_type
|
||||
{
|
||||
REQ_SET_CURSOR,
|
||||
REQ_SET_TITLE,
|
||||
REQ_WRITE_CHARACTERS,
|
||||
REQ_WRITE_OUTPUT,
|
||||
};
|
||||
|
||||
struct pseudoconsole_req
|
||||
|
@ -133,6 +157,20 @@ struct pseudoconsole_req
|
|||
union
|
||||
{
|
||||
WCHAR string[1];
|
||||
COORD coord;
|
||||
struct
|
||||
{
|
||||
COORD coord;
|
||||
unsigned int len;
|
||||
WCHAR buf[1];
|
||||
} write_characters;
|
||||
struct
|
||||
{
|
||||
COORD size;
|
||||
COORD coord;
|
||||
SMALL_RECT region;
|
||||
CHAR_INFO buf[1];
|
||||
} write_output;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
@ -151,11 +189,287 @@ static void child_string_request(enum req_type type, const WCHAR *title)
|
|||
ok(ret, "WriteFile failed: %u\n", GetLastError());
|
||||
}
|
||||
|
||||
static void child_write_characters(const WCHAR *buf, unsigned int x, unsigned int y)
|
||||
{
|
||||
char req_buf[4096];
|
||||
struct pseudoconsole_req *req = (void *)req_buf;
|
||||
size_t len = lstrlenW(buf);
|
||||
DWORD count;
|
||||
BOOL ret;
|
||||
|
||||
req->type = REQ_WRITE_CHARACTERS;
|
||||
req->u.write_characters.coord.X = x;
|
||||
req->u.write_characters.coord.Y = y;
|
||||
req->u.write_characters.len = len;
|
||||
memcpy(req->u.write_characters.buf, buf, len * sizeof(WCHAR));
|
||||
ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.write_characters.buf[len + 1]),
|
||||
&count, NULL);
|
||||
ok(ret, "WriteFile failed: %u\n", GetLastError());
|
||||
}
|
||||
|
||||
static void child_set_cursor(const unsigned int x, unsigned int y)
|
||||
{
|
||||
struct pseudoconsole_req req;
|
||||
DWORD count;
|
||||
BOOL ret;
|
||||
|
||||
req.type = REQ_SET_CURSOR;
|
||||
req.u.coord.X = x;
|
||||
req.u.coord.Y = y;
|
||||
ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
|
||||
ok(ret, "WriteFile failed: %u\n", GetLastError());
|
||||
}
|
||||
|
||||
#define child_write_output(a,b,c,d,e,f,g,h,j,k,l,m,n) child_write_output_(__LINE__,a,b,c,d,e,f,g,h,j,k,l,m,n)
|
||||
static void child_write_output_(unsigned int line, CHAR_INFO *buf, unsigned int size_x, unsigned int size_y,
|
||||
unsigned int coord_x, unsigned int coord_y, unsigned int left,
|
||||
unsigned int top, unsigned int right, unsigned int bottom, unsigned int out_left,
|
||||
unsigned int out_top, unsigned int out_right, unsigned int out_bottom)
|
||||
{
|
||||
char req_buf[4096];
|
||||
struct pseudoconsole_req *req = (void *)req_buf;
|
||||
SMALL_RECT region;
|
||||
DWORD count;
|
||||
BOOL ret;
|
||||
|
||||
req->type = REQ_WRITE_OUTPUT;
|
||||
req->u.write_output.size.X = size_x;
|
||||
req->u.write_output.size.Y = size_y;
|
||||
req->u.write_output.coord.X = coord_x;
|
||||
req->u.write_output.coord.Y = coord_y;
|
||||
req->u.write_output.region.Left = left;
|
||||
req->u.write_output.region.Top = top;
|
||||
req->u.write_output.region.Right = right;
|
||||
req->u.write_output.region.Bottom = bottom;
|
||||
memcpy(req->u.write_output.buf, buf, size_x * size_y * sizeof(*buf));
|
||||
ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.write_output.buf[size_x * size_y]), &count, NULL);
|
||||
ok_(__FILE__,line)(ret, "WriteFile failed: %u\n", GetLastError());
|
||||
ret = ReadFile(child_pipe, ®ion, sizeof(region), &count, NULL);
|
||||
ok_(__FILE__,line)(ret, "WriteFile failed: %u\n", GetLastError());
|
||||
ok_(__FILE__,line)(region.Left == out_left, "Left = %u\n", region.Left);
|
||||
ok_(__FILE__,line)(region.Top == out_top, "Top = %u\n", region.Top);
|
||||
ok_(__FILE__,line)(region.Right == out_right, "Right = %u\n", region.Right);
|
||||
ok_(__FILE__,line)(region.Bottom == out_bottom, "Bottom = %u\n", region.Bottom);
|
||||
}
|
||||
|
||||
static void test_tty_output(void)
|
||||
{
|
||||
CHAR_INFO char_info_buf[2048], char_info;
|
||||
unsigned int i;
|
||||
|
||||
/* simple write chars */
|
||||
child_write_characters(L"child", 3, 4);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[5;4H"); /* set cursor */
|
||||
expect_output_sequence("child");
|
||||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
/* wrapped write chars */
|
||||
child_write_characters(L"bound", 28, 6);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[7;1H"); /* set cursor */
|
||||
expect_output_sequence(" bo\r\nund");
|
||||
expect_erase_line(27);
|
||||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
/* fill line 4 with a few simple writes */
|
||||
child_write_characters(L"xxx", 13, 4);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[5;14H"); /* set cursor */
|
||||
expect_output_sequence("xxx");
|
||||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
/* write one char at the end of row */
|
||||
child_write_characters(L"y", 29, 4);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[5;30H"); /* set cursor */
|
||||
expect_output_sequence("y");
|
||||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
/* wrapped write chars */
|
||||
child_write_characters(L"zz", 29, 4);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[5;1H"); /* set cursor */
|
||||
expect_output_sequence(" child xxx z");
|
||||
expect_output_sequence("\r\nz");
|
||||
expect_erase_line(29);
|
||||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
/* trailing spaces */
|
||||
child_write_characters(L"child ", 3, 4);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[5;4H"); /* set cursor */
|
||||
expect_output_sequence("child ");
|
||||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_set_cursor(2, 3);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_string_request(REQ_SET_TITLE, L"new title");
|
||||
fetch_console_output();
|
||||
skip_sequence("\x1b[?25l"); /* hide cursor */
|
||||
expect_output_sequence("\x1b]0;new title\x07"); /* set title */
|
||||
skip_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(char_info_buf); i++)
|
||||
{
|
||||
char_info_buf[i].Char.UnicodeChar = '0' + i % 10;
|
||||
char_info_buf[i].Attributes = 0;
|
||||
}
|
||||
|
||||
child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 1, 2,
|
||||
/* region */ 3, 7, 5, 9, /* out region */ 3, 7, 5, 9);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[30m"); /* foreground black */
|
||||
expect_output_sequence("\x1b[8;4H"); /* set cursor */
|
||||
expect_output_sequence("567");
|
||||
expect_output_sequence("\x1b[9;4H"); /* set cursor */
|
||||
expect_output_sequence("234");
|
||||
expect_output_sequence("\x1b[10;4H"); /* set cursor */
|
||||
expect_output_sequence("901");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_write_output(char_info_buf, /* size */ 2, 3, /* coord */ 1, 2,
|
||||
/* region */ 3, 8, 15, 19, /* out region */ 3, 8, 3, 8);
|
||||
expect_hide_cursor();
|
||||
if (skip_sequence("\x1b[m")) /* default attr */
|
||||
expect_output_sequence("\x1b[30m");/* foreground black */
|
||||
expect_output_sequence("\x1b[9;4H"); /* set cursor */
|
||||
expect_output_sequence("5");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_write_output(char_info_buf, /* size */ 3, 4, /* coord */ 1, 2,
|
||||
/* region */ 3, 8, 15, 19, /* out region */ 3, 8, 4, 9);
|
||||
expect_hide_cursor();
|
||||
if (skip_sequence("\x1b[m")) /* default attr */
|
||||
expect_output_sequence("\x1b[30m");/* foreground black */
|
||||
expect_output_sequence("\x1b[9;4H"); /* set cursor */
|
||||
expect_output_sequence("78");
|
||||
expect_output_sequence("\x1b[10;4H"); /* set cursor */
|
||||
expect_output_sequence("01");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 2, 3,
|
||||
/* region */ 28, 38, 31, 60, /* out region */ 28, 38, 29, 39);
|
||||
expect_hide_cursor();
|
||||
if (skip_sequence("\x1b[m")) /* default attr */
|
||||
expect_output_sequence("\x1b[30m");/* foreground black */
|
||||
expect_output_sequence("\x1b[39;29H"); /* set cursor */
|
||||
expect_output_sequence("34");
|
||||
expect_output_sequence("\x1b[40;29H"); /* set cursor */
|
||||
expect_output_sequence("01");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 1, 2,
|
||||
/* region */ 0, 7, 5, 9, /* out region */ 0, 7, 5, 9);
|
||||
expect_hide_cursor();
|
||||
if (skip_sequence("\x1b[m")) /* default attr */
|
||||
expect_output_sequence("\x1b[30m");/* foreground black */
|
||||
expect_output_sequence("\x1b[8;1H"); /* set cursor */
|
||||
expect_output_sequence("567890\r\n");
|
||||
expect_output_sequence("234567\r\n");
|
||||
expect_output_sequence("901234");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
child_write_characters(L"xxx", 3, 10);
|
||||
expect_hide_cursor();
|
||||
expect_output_sequence("\x1b[m"); /* default attributes */
|
||||
expect_output_sequence("\x1b[11;4H"); /* set cursor */
|
||||
expect_output_sequence("xxx");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
/* test attributes */
|
||||
for (i = 0; i < 0x100 - 0xff; i++)
|
||||
{
|
||||
unsigned int expect;
|
||||
char expect_buf[16];
|
||||
char_info.Char.UnicodeChar = 'a';
|
||||
char_info.Attributes = i;
|
||||
child_write_output(&char_info, /* size */ 1, 1, /* coord */ 0, 0,
|
||||
/* region */ 12, 3, 12, 3, /* out region */ 12, 3, 12, 3);
|
||||
expect_hide_cursor();
|
||||
if (i != 0x190 && i && ((i & 0xff) != 8)) expect_output_sequence_ctx(i, "\x1b[m");
|
||||
if ((i & 0x0f) != 7)
|
||||
{
|
||||
expect = 30;
|
||||
if (i & FOREGROUND_BLUE) expect += 4;
|
||||
if (i & FOREGROUND_GREEN) expect += 2;
|
||||
if (i & FOREGROUND_RED) expect += 1;
|
||||
if (i & FOREGROUND_INTENSITY) expect += 60;
|
||||
sprintf(expect_buf, "\x1b[%um", expect);
|
||||
expect_output_sequence_ctx(i, expect_buf);
|
||||
}
|
||||
if (i & 0xf0)
|
||||
{
|
||||
expect = 40;
|
||||
if (i & BACKGROUND_BLUE) expect += 4;
|
||||
if (i & BACKGROUND_GREEN) expect += 2;
|
||||
if (i & BACKGROUND_RED) expect += 1;
|
||||
if (i & BACKGROUND_INTENSITY) expect += 60;
|
||||
sprintf(expect_buf, "\x1b[%um", expect);
|
||||
expect_output_sequence_ctx(i, expect_buf);
|
||||
}
|
||||
if (!skip_sequence("\x1b[10C"))
|
||||
expect_output_sequence_ctx(i, "\x1b[4;13H"); /* set cursor */
|
||||
expect_output_sequence("a");
|
||||
if (!skip_sequence("\x1b[11D"))
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
}
|
||||
|
||||
char_info_buf[0].Attributes = FOREGROUND_GREEN;
|
||||
char_info_buf[1].Attributes = FOREGROUND_GREEN | BACKGROUND_RED;
|
||||
char_info_buf[2].Attributes = BACKGROUND_RED;
|
||||
child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 0, 0,
|
||||
/* region */ 7, 0, 9, 0, /* out region */ 7, 0, 9, 0);
|
||||
expect_hide_cursor();
|
||||
skip_sequence("\x1b[m"); /* default attr */
|
||||
expect_output_sequence("\x1b[32m"); /* foreground black */
|
||||
expect_output_sequence("\x1b[1;8H"); /* set cursor */
|
||||
expect_output_sequence("0");
|
||||
expect_output_sequence("\x1b[41m"); /* backgorund red */
|
||||
expect_output_sequence("1");
|
||||
expect_output_sequence("\x1b[30m"); /* foreground black */
|
||||
expect_output_sequence("2");
|
||||
expect_output_sequence("\x1b[4;3H"); /* set cursor */
|
||||
expect_output_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
}
|
||||
|
||||
static void child_process(HANDLE pipe)
|
||||
{
|
||||
HANDLE output, input;
|
||||
DWORD size, count;
|
||||
char buf[4096];
|
||||
DWORD size;
|
||||
BOOL ret;
|
||||
|
||||
output = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
|
||||
|
@ -169,11 +483,33 @@ static void child_process(HANDLE pipe)
|
|||
const struct pseudoconsole_req *req = (void *)buf;
|
||||
switch (req->type)
|
||||
{
|
||||
case REQ_SET_CURSOR:
|
||||
ret = SetConsoleCursorPosition(output, req->u.coord);
|
||||
ok(ret, "SetConsoleCursorPosition failed: %u\n", GetLastError());
|
||||
break;
|
||||
|
||||
case REQ_SET_TITLE:
|
||||
ret = SetConsoleTitleW(req->u.string);
|
||||
ok(ret, "SetConsoleTitleW failed: %u\n", GetLastError());
|
||||
break;
|
||||
|
||||
case REQ_WRITE_CHARACTERS:
|
||||
ret = WriteConsoleOutputCharacterW(output, req->u.write_characters.buf,
|
||||
req->u.write_characters.len,
|
||||
req->u.write_characters.coord, &count);
|
||||
ok(ret, "WriteConsoleOutputCharacterW failed: %u\n", GetLastError());
|
||||
break;
|
||||
|
||||
case REQ_WRITE_OUTPUT:
|
||||
{
|
||||
SMALL_RECT region = req->u.write_output.region;
|
||||
ret = WriteConsoleOutputW(output, req->u.write_output.buf, req->u.write_output.size, req->u.write_output.coord, ®ion);
|
||||
ok(ret, "WriteConsoleOutput failed: %u\n", GetLastError());
|
||||
ret = WriteFile(pipe, ®ion, sizeof(region), &count, NULL);
|
||||
ok(ret, "WriteFile failed: %u\n", GetLastError());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ok(0, "unexpected request type %u\n", req->type);
|
||||
};
|
||||
|
@ -250,6 +586,7 @@ static HPCON create_pseudo_console(HANDLE *console_pipe_end, HANDLE *child_proce
|
|||
static void test_pseudoconsole(void)
|
||||
{
|
||||
HANDLE console_pipe_end, child_process;
|
||||
BOOL broken_version;
|
||||
HPCON console;
|
||||
|
||||
console = create_pseudo_console(&console_pipe_end, &child_process);
|
||||
|
@ -261,11 +598,14 @@ static void test_pseudoconsole(void)
|
|||
expect_output_sequence("\x1b[H"); /* set cursor */
|
||||
skip_sequence("\x1b[H"); /* some windows versions emit it twice */
|
||||
expect_output_sequence("\x1b]0;test title"); /* set title */
|
||||
skip_byte(0); /* some win versions emit nullbyte */
|
||||
broken_version = skip_byte(0); /* some win versions emit nullbyte */
|
||||
expect_output_sequence("\x07");
|
||||
skip_sequence("\x1b[?25h"); /* show cursor */
|
||||
expect_empty_output();
|
||||
|
||||
if (!broken_version) test_tty_output();
|
||||
else win_skip("Skipping tty output tests on broken Windows version\n");
|
||||
|
||||
pClosePseudoConsole(console);
|
||||
CloseHandle(console_pipe_end);
|
||||
CloseHandle(console_pipe);
|
||||
|
|
Loading…
Reference in New Issue