1 /* Copyright libuv project contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #ifdef _WIN32
23 
24 #include "task.h"
25 #include "uv.h"
26 
27 #include <io.h>
28 #include <windows.h>
29 
30 #include <errno.h>
31 #include <string.h>
32 
33 #define ESC "\033"
34 #define CSI ESC "["
35 #define ST ESC "\\"
36 #define BEL "\x07"
37 #define HELLO "Hello"
38 
39 #define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
40 #define FOREGROUND_BLACK 0
41 #define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN)
42 #define FOREGROUND_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE)
43 #define FOREGROUND_MAGENTA (FOREGROUND_RED | FOREGROUND_BLUE)
44 #define BACKGROUND_WHITE (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
45 #define BACKGROUND_BLACK 0
46 #define BACKGROUND_YELLOW (BACKGROUND_RED | BACKGROUND_GREEN)
47 #define BACKGROUND_CYAN (BACKGROUND_GREEN | BACKGROUND_BLUE)
48 #define BACKGROUND_MAGENTA (BACKGROUND_RED | BACKGROUND_BLUE)
49 
50 #define F_INTENSITY      1
51 #define FB_INTENSITY     2
52 #define B_INTENSITY      5
53 #define INVERSE          7
54 #define F_INTENSITY_OFF1 21
55 #define F_INTENSITY_OFF2 22
56 #define B_INTENSITY_OFF  25
57 #define INVERSE_OFF      27
58 #define F_BLACK          30
59 #define F_RED            31
60 #define F_GREEN          32
61 #define F_YELLOW         33
62 #define F_BLUE           34
63 #define F_MAGENTA        35
64 #define F_CYAN           36
65 #define F_WHITE          37
66 #define F_DEFAULT        39
67 #define B_BLACK          40
68 #define B_RED            41
69 #define B_GREEN          42
70 #define B_YELLOW         43
71 #define B_BLUE           44
72 #define B_MAGENTA        45
73 #define B_CYAN           46
74 #define B_WHITE          47
75 #define B_DEFAULT        49
76 
77 #define CURSOR_SIZE_SMALL     25
78 #define CURSOR_SIZE_MIDDLE    50
79 #define CURSOR_SIZE_LARGE     100
80 
81 struct screen_info {
82   CONSOLE_SCREEN_BUFFER_INFO csbi;
83   int top;
84   int width;
85   int height;
86   int length;
87   WORD default_attr;
88 };
89 
90 struct captured_screen {
91   char* text;
92   WORD* attributes;
93   struct screen_info si;
94 };
95 
get_screen_info(uv_tty_t * tty_out,struct screen_info * si)96 static void get_screen_info(uv_tty_t* tty_out, struct screen_info* si) {
97   ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &(si->csbi)));
98   si->width = si->csbi.dwSize.X;
99   si->height = si->csbi.srWindow.Bottom - si->csbi.srWindow.Top + 1;
100   si->length = si->width * si->height;
101   si->default_attr = si->csbi.wAttributes;
102   si->top = si->csbi.srWindow.Top;
103 }
104 
set_cursor_position(uv_tty_t * tty_out,COORD pos)105 static void set_cursor_position(uv_tty_t* tty_out, COORD pos) {
106   HANDLE handle = tty_out->handle;
107   CONSOLE_SCREEN_BUFFER_INFO info;
108   ASSERT(GetConsoleScreenBufferInfo(handle, &info));
109   pos.X -= 1;
110   pos.Y += info.srWindow.Top - 1;
111   ASSERT(SetConsoleCursorPosition(handle, pos));
112 }
113 
get_cursor_position(uv_tty_t * tty_out,COORD * cursor_position)114 static void get_cursor_position(uv_tty_t* tty_out, COORD* cursor_position) {
115   HANDLE handle = tty_out->handle;
116   CONSOLE_SCREEN_BUFFER_INFO info;
117   ASSERT(GetConsoleScreenBufferInfo(handle, &info));
118   cursor_position->X = info.dwCursorPosition.X + 1;
119   cursor_position->Y = info.dwCursorPosition.Y - info.srWindow.Top + 1;
120 }
121 
set_cursor_to_home(uv_tty_t * tty_out)122 static void set_cursor_to_home(uv_tty_t* tty_out) {
123   COORD origin = {1, 1};
124   set_cursor_position(tty_out, origin);
125 }
126 
get_cursor_info(uv_tty_t * tty_out)127 static CONSOLE_CURSOR_INFO get_cursor_info(uv_tty_t* tty_out) {
128   HANDLE handle = tty_out->handle;
129   CONSOLE_CURSOR_INFO info;
130   ASSERT(GetConsoleCursorInfo(handle, &info));
131   return info;
132 }
133 
set_cursor_size(uv_tty_t * tty_out,DWORD size)134 static void set_cursor_size(uv_tty_t* tty_out, DWORD size) {
135   CONSOLE_CURSOR_INFO info = get_cursor_info(tty_out);
136   info.dwSize = size;
137   ASSERT(SetConsoleCursorInfo(tty_out->handle, &info));
138 }
139 
get_cursor_size(uv_tty_t * tty_out)140 static DWORD get_cursor_size(uv_tty_t* tty_out) {
141   return get_cursor_info(tty_out).dwSize;
142 }
143 
set_cursor_visibility(uv_tty_t * tty_out,BOOL visible)144 static void set_cursor_visibility(uv_tty_t* tty_out, BOOL visible) {
145   CONSOLE_CURSOR_INFO info = get_cursor_info(tty_out);
146   info.bVisible = visible;
147   ASSERT(SetConsoleCursorInfo(tty_out->handle, &info));
148 }
149 
get_cursor_visibility(uv_tty_t * tty_out)150 static BOOL get_cursor_visibility(uv_tty_t* tty_out) {
151   return get_cursor_info(tty_out).bVisible;
152 }
153 
is_scrolling(uv_tty_t * tty_out,struct screen_info si)154 static BOOL is_scrolling(uv_tty_t* tty_out, struct screen_info si) {
155   CONSOLE_SCREEN_BUFFER_INFO info;
156   ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &info));
157   return info.srWindow.Top != si.top;
158 }
159 
write_console(uv_tty_t * tty_out,char * src)160 static void write_console(uv_tty_t* tty_out, char* src) {
161   int r;
162   uv_buf_t buf;
163 
164   buf.base = src;
165   buf.len = strlen(buf.base);
166 
167   r = uv_try_write((uv_stream_t*) tty_out, &buf, 1);
168   ASSERT_GE(r, 0);
169   ASSERT_EQ((unsigned int) r, buf.len);
170 }
171 
setup_screen(uv_tty_t * tty_out)172 static void setup_screen(uv_tty_t* tty_out) {
173   DWORD length, number_of_written;
174   COORD origin;
175   CONSOLE_SCREEN_BUFFER_INFO info;
176   ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &info));
177   length = info.dwSize.X * (info.srWindow.Bottom - info.srWindow.Top + 1);
178   origin.X = 0;
179   origin.Y = info.srWindow.Top;
180   ASSERT(FillConsoleOutputCharacter(
181          tty_out->handle, '.', length, origin, &number_of_written));
182   ASSERT_EQ(length, number_of_written);
183 }
184 
clear_screen(uv_tty_t * tty_out,struct screen_info * si)185 static void clear_screen(uv_tty_t* tty_out, struct screen_info* si) {
186   DWORD length, number_of_written;
187   COORD origin;
188   CONSOLE_SCREEN_BUFFER_INFO info;
189   ASSERT(GetConsoleScreenBufferInfo(tty_out->handle, &info));
190   length = (info.srWindow.Bottom - info.srWindow.Top + 1) * info.dwSize.X - 1;
191   origin.X = 0;
192   origin.Y = info.srWindow.Top;
193   FillConsoleOutputCharacterA(
194       tty_out->handle, ' ', length, origin, &number_of_written);
195   ASSERT_EQ(length, number_of_written);
196   FillConsoleOutputAttribute(
197       tty_out->handle, si->default_attr, length, origin, &number_of_written);
198   ASSERT_EQ(length, number_of_written);
199 }
200 
free_screen(struct captured_screen * cs)201 static void free_screen(struct captured_screen* cs) {
202   free(cs->text);
203   cs->text = NULL;
204   free(cs->attributes);
205   cs->attributes = NULL;
206 }
207 
capture_screen(uv_tty_t * tty_out,struct captured_screen * cs)208 static void capture_screen(uv_tty_t* tty_out, struct captured_screen* cs) {
209   DWORD length;
210   COORD origin;
211   get_screen_info(tty_out, &(cs->si));
212   origin.X = 0;
213   origin.Y = cs->si.csbi.srWindow.Top;
214   cs->text = malloc(cs->si.length * sizeof(*cs->text));
215   ASSERT_NOT_NULL(cs->text);
216   cs->attributes = (WORD*) malloc(cs->si.length * sizeof(*cs->attributes));
217   ASSERT_NOT_NULL(cs->attributes);
218   ASSERT(ReadConsoleOutputCharacter(
219          tty_out->handle, cs->text, cs->si.length, origin, &length));
220   ASSERT_EQ((unsigned int) cs->si.length, length);
221   ASSERT(ReadConsoleOutputAttribute(
222          tty_out->handle, cs->attributes, cs->si.length, origin, &length));
223   ASSERT_EQ((unsigned int) cs->si.length, length);
224 }
225 
make_expect_screen_erase(struct captured_screen * cs,COORD cursor_position,int dir,BOOL entire_screen)226 static void make_expect_screen_erase(struct captured_screen* cs,
227                                      COORD cursor_position,
228                                      int dir,
229                                      BOOL entire_screen) {
230   /* beginning of line */
231   char* start;
232   char* end;
233   start = cs->text + cs->si.width * (cursor_position.Y - 1);
234   if (dir == 0) {
235     if (entire_screen) {
236       /* erase to end of screen */
237       end = cs->text + cs->si.length;
238     } else {
239       /* erase to end of line */
240       end = start + cs->si.width;
241     }
242     /* erase from postition of cursor */
243     start += cursor_position.X - 1;
244   } else if (dir == 1) {
245     /* erase to position of cursor */
246     end = start + cursor_position.X;
247     if (entire_screen) {
248       /* erase form beginning of screen */
249       start = cs->text;
250     }
251   } else if (dir == 2) {
252     if (entire_screen) {
253       /* erase form beginning of screen */
254       start = cs->text;
255       /* erase to end of screen */
256       end = cs->text + cs->si.length;
257     } else {
258       /* erase to end of line */
259       end = start + cs->si.width;
260     }
261   } else {
262     ASSERT(FALSE);
263   }
264   ASSERT_PTR_LT(start, end);
265   ASSERT_LE(end - cs->text, cs->si.length);
266   for (; start < end; start++) {
267     *start = ' ';
268   }
269 }
270 
make_expect_screen_write(struct captured_screen * cs,COORD cursor_position,const char * text)271 static void make_expect_screen_write(struct captured_screen* cs,
272                                      COORD cursor_position,
273                                      const char* text) {
274   /* position of cursor */
275   char* start;
276   start = cs->text + cs->si.width * (cursor_position.Y - 1) +
277                 cursor_position.X - 1;
278   size_t length = strlen(text);
279   size_t remain_length = cs->si.length - (cs->text - start);
280   length = length > remain_length ? remain_length : length;
281   memcpy(start, text, length);
282 }
283 
make_expect_screen_set_attr(struct captured_screen * cs,COORD cursor_position,size_t length,WORD attr)284 static void make_expect_screen_set_attr(struct captured_screen* cs,
285                                         COORD cursor_position,
286                                         size_t length,
287                                         WORD attr) {
288   WORD* start;
289   start = cs->attributes + cs->si.width * (cursor_position.Y - 1) +
290                 cursor_position.X - 1;
291   size_t remain_length = cs->si.length - (cs->attributes - start);
292   length = length > remain_length ? remain_length : length;
293   while (length) {
294     *start = attr;
295     start++;
296     length--;
297   }
298 }
299 
compare_screen(uv_tty_t * tty_out,struct captured_screen * actual,struct captured_screen * expect)300 static BOOL compare_screen(uv_tty_t* tty_out,
301                            struct captured_screen* actual,
302                            struct captured_screen* expect) {
303   int line, col;
304   BOOL result = TRUE;
305   int current = 0;
306   ASSERT(actual->text);
307   ASSERT(actual->attributes);
308   ASSERT(expect->text);
309   ASSERT(expect->attributes);
310   if (actual->si.length != expect->si.length) {
311     return FALSE;
312   }
313   if (actual->si.width != expect->si.width) {
314     return FALSE;
315   }
316   if (actual->si.height != expect->si.height) {
317     return FALSE;
318   }
319   while (current < actual->si.length) {
320     if (*(actual->text + current) != *(expect->text + current)) {
321       line = current / actual->si.width + 1;
322       col = current - actual->si.width * (line - 1) + 1;
323       fprintf(stderr,
324               "line:%d col:%d expected character '%c' but found '%c'\n",
325               line,
326               col,
327               *(expect->text + current),
328               *(actual->text + current));
329       result = FALSE;
330     }
331     if (*(actual->attributes + current) != *(expect->attributes + current)) {
332       line = current / actual->si.width + 1;
333       col = current - actual->si.width * (line - 1) + 1;
334       fprintf(stderr,
335               "line:%d col:%d expected attributes '%u' but found '%u'\n",
336               line,
337               col,
338               *(expect->attributes + current),
339               *(actual->attributes + current));
340       result = FALSE;
341     }
342     current++;
343   }
344   clear_screen(tty_out, &expect->si);
345   free_screen(expect);
346   free_screen(actual);
347   return result;
348 }
349 
initialize_tty(uv_tty_t * tty_out)350 static void initialize_tty(uv_tty_t* tty_out) {
351   int r;
352   int ttyout_fd;
353   /* Make sure we have an FD that refers to a tty */
354   HANDLE handle;
355 
356   uv_tty_set_vterm_state(UV_TTY_UNSUPPORTED);
357 
358   handle = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
359                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
360                                      NULL,
361                                      CONSOLE_TEXTMODE_BUFFER,
362                                      NULL);
363   ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE);
364 
365   ttyout_fd = _open_osfhandle((intptr_t) handle, 0);
366   ASSERT_GE(ttyout_fd, 0);
367   ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd));
368   r = uv_tty_init(uv_default_loop(), tty_out, ttyout_fd, 0); /* Writable. */
369   ASSERT_OK(r);
370 }
371 
terminate_tty(uv_tty_t * tty_out)372 static void terminate_tty(uv_tty_t* tty_out) {
373   set_cursor_to_home(tty_out);
374   uv_close((uv_handle_t*) tty_out, NULL);
375 }
376 
TEST_IMPL(tty_cursor_up)377 TEST_IMPL(tty_cursor_up) {
378   uv_tty_t tty_out;
379   uv_loop_t* loop;
380   COORD cursor_pos, cursor_pos_old;
381   char buffer[1024];
382   struct screen_info si;
383 
384   loop = uv_default_loop();
385 
386   initialize_tty(&tty_out);
387   get_screen_info(&tty_out, &si);
388 
389   cursor_pos_old.X = si.width / 2;
390   cursor_pos_old.Y = si.height / 2;
391   set_cursor_position(&tty_out, cursor_pos_old);
392 
393   /* cursor up one times if omitted arguments */
394   snprintf(buffer, sizeof(buffer), "%sA", CSI);
395   write_console(&tty_out, buffer);
396   get_cursor_position(&tty_out, &cursor_pos);
397   ASSERT_EQ(cursor_pos_old.Y - 1, cursor_pos.Y);
398   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
399 
400   /* cursor up nth times */
401   cursor_pos_old = cursor_pos;
402   snprintf(buffer, sizeof(buffer), "%s%dA", CSI, si.height / 4);
403   write_console(&tty_out, buffer);
404   get_cursor_position(&tty_out, &cursor_pos);
405   ASSERT_EQ(cursor_pos_old.Y - si.height / 4, cursor_pos.Y);
406   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
407 
408   /* cursor up from Window top does nothing */
409   cursor_pos_old.X = 1;
410   cursor_pos_old.Y = 1;
411   set_cursor_position(&tty_out, cursor_pos_old);
412   snprintf(buffer, sizeof(buffer), "%sA", CSI);
413   write_console(&tty_out, buffer);
414   get_cursor_position(&tty_out, &cursor_pos);
415   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
416   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
417   ASSERT(!is_scrolling(&tty_out, si));
418 
419   terminate_tty(&tty_out);
420 
421   uv_run(loop, UV_RUN_DEFAULT);
422 
423   MAKE_VALGRIND_HAPPY(loop);
424   return 0;
425 }
426 
427 
TEST_IMPL(tty_cursor_down)428 TEST_IMPL(tty_cursor_down) {
429   uv_tty_t tty_out;
430   uv_loop_t* loop;
431   COORD cursor_pos, cursor_pos_old;
432   char buffer[1024];
433   struct screen_info si;
434 
435   loop = uv_default_loop();
436 
437   initialize_tty(&tty_out);
438   get_screen_info(&tty_out, &si);
439 
440   cursor_pos_old.X = si.width / 2;
441   cursor_pos_old.Y = si.height / 2;
442   set_cursor_position(&tty_out, cursor_pos_old);
443 
444   /* cursor down one times if omitted arguments */
445   snprintf(buffer, sizeof(buffer), "%sB", CSI);
446   write_console(&tty_out, buffer);
447   get_cursor_position(&tty_out, &cursor_pos);
448   ASSERT_EQ(cursor_pos_old.Y + 1, cursor_pos.Y);
449   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
450 
451   /* cursor down nth times */
452   cursor_pos_old = cursor_pos;
453   snprintf(buffer, sizeof(buffer), "%s%dB", CSI, si.height / 4);
454   write_console(&tty_out, buffer);
455   get_cursor_position(&tty_out, &cursor_pos);
456   ASSERT_EQ(cursor_pos_old.Y + si.height / 4, cursor_pos.Y);
457   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
458 
459   /* cursor down from bottom line does nothing */
460   cursor_pos_old.X = si.width / 2;
461   cursor_pos_old.Y = si.height;
462   set_cursor_position(&tty_out, cursor_pos_old);
463   snprintf(buffer, sizeof(buffer), "%sB", CSI);
464   write_console(&tty_out, buffer);
465   get_cursor_position(&tty_out, &cursor_pos);
466   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
467   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
468   ASSERT(!is_scrolling(&tty_out, si));
469 
470   terminate_tty(&tty_out);
471 
472   uv_run(loop, UV_RUN_DEFAULT);
473 
474   MAKE_VALGRIND_HAPPY(loop);
475   return 0;
476 }
477 
478 
TEST_IMPL(tty_cursor_forward)479 TEST_IMPL(tty_cursor_forward) {
480   uv_tty_t tty_out;
481   uv_loop_t* loop;
482   COORD cursor_pos, cursor_pos_old;
483   char buffer[1024];
484   struct screen_info si;
485 
486   loop = uv_default_loop();
487 
488   initialize_tty(&tty_out);
489   get_screen_info(&tty_out, &si);
490 
491   cursor_pos_old.X = si.width / 2;
492   cursor_pos_old.Y = si.height / 2;
493   set_cursor_position(&tty_out, cursor_pos_old);
494 
495   /* cursor forward one times if omitted arguments */
496   snprintf(buffer, sizeof(buffer), "%sC", CSI);
497   write_console(&tty_out, buffer);
498   get_cursor_position(&tty_out, &cursor_pos);
499   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
500   ASSERT_EQ(cursor_pos_old.X + 1, cursor_pos.X);
501 
502   /* cursor forward nth times */
503   cursor_pos_old = cursor_pos;
504   snprintf(buffer, sizeof(buffer), "%s%dC", CSI, si.width / 4);
505   write_console(&tty_out, buffer);
506   get_cursor_position(&tty_out, &cursor_pos);
507   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
508   ASSERT_EQ(cursor_pos_old.X + si.width / 4, cursor_pos.X);
509 
510   /* cursor forward from end of line does nothing*/
511   cursor_pos_old.X = si.width;
512   cursor_pos_old.Y = si.height / 2;
513   set_cursor_position(&tty_out, cursor_pos_old);
514   snprintf(buffer, sizeof(buffer), "%sC", CSI);
515   write_console(&tty_out, buffer);
516   get_cursor_position(&tty_out, &cursor_pos);
517   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
518   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
519 
520   /* cursor forward from end of screen does nothing */
521   cursor_pos_old.X = si.width;
522   cursor_pos_old.Y = si.height;
523   set_cursor_position(&tty_out, cursor_pos_old);
524   snprintf(buffer, sizeof(buffer), "%sC", CSI);
525   write_console(&tty_out, buffer);
526   get_cursor_position(&tty_out, &cursor_pos);
527   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
528   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
529   ASSERT(!is_scrolling(&tty_out, si));
530 
531   terminate_tty(&tty_out);
532 
533   uv_run(loop, UV_RUN_DEFAULT);
534 
535   MAKE_VALGRIND_HAPPY(loop);
536   return 0;
537 }
538 
539 
TEST_IMPL(tty_cursor_back)540 TEST_IMPL(tty_cursor_back) {
541   uv_tty_t tty_out;
542   uv_loop_t* loop;
543   COORD cursor_pos, cursor_pos_old;
544   char buffer[1024];
545   struct screen_info si;
546 
547   loop = uv_default_loop();
548 
549   initialize_tty(&tty_out);
550   get_screen_info(&tty_out, &si);
551 
552   cursor_pos_old.X = si.width / 2;
553   cursor_pos_old.Y = si.height / 2;
554   set_cursor_position(&tty_out, cursor_pos_old);
555 
556   /* cursor back one times if omitted arguments */
557   snprintf(buffer, sizeof(buffer), "%sD", CSI);
558   write_console(&tty_out, buffer);
559   get_cursor_position(&tty_out, &cursor_pos);
560   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
561   ASSERT_EQ(cursor_pos_old.X - 1, cursor_pos.X);
562 
563   /* cursor back nth times */
564   cursor_pos_old = cursor_pos;
565   snprintf(buffer, sizeof(buffer), "%s%dD", CSI, si.width / 4);
566   write_console(&tty_out, buffer);
567   get_cursor_position(&tty_out, &cursor_pos);
568   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
569   ASSERT_EQ(cursor_pos_old.X - si.width / 4, cursor_pos.X);
570 
571   /* cursor back from beginning of line does nothing */
572   cursor_pos_old.X = 1;
573   cursor_pos_old.Y = si.height / 2;
574   set_cursor_position(&tty_out, cursor_pos_old);
575   snprintf(buffer, sizeof(buffer), "%sD", CSI);
576   write_console(&tty_out, buffer);
577   get_cursor_position(&tty_out, &cursor_pos);
578   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
579   ASSERT_EQ(cursor_pos_old.X, cursor_pos.X);
580 
581   /* cursor back from top of screen does nothing */
582   cursor_pos_old.X = 1;
583   cursor_pos_old.Y = 1;
584   set_cursor_position(&tty_out, cursor_pos_old);
585   snprintf(buffer, sizeof(buffer), "%sD", CSI);
586   write_console(&tty_out, buffer);
587   get_cursor_position(&tty_out, &cursor_pos);
588   ASSERT_EQ(1, cursor_pos.Y);
589   ASSERT_EQ(1, cursor_pos.X);
590   ASSERT(!is_scrolling(&tty_out, si));
591 
592   terminate_tty(&tty_out);
593 
594   uv_run(loop, UV_RUN_DEFAULT);
595 
596   MAKE_VALGRIND_HAPPY(loop);
597   return 0;
598 }
599 
600 
TEST_IMPL(tty_cursor_next_line)601 TEST_IMPL(tty_cursor_next_line) {
602   uv_tty_t tty_out;
603   uv_loop_t* loop;
604   COORD cursor_pos, cursor_pos_old;
605   char buffer[1024];
606   struct screen_info si;
607 
608   loop = uv_default_loop();
609 
610   initialize_tty(&tty_out);
611   get_screen_info(&tty_out, &si);
612 
613   cursor_pos_old.X = si.width / 2;
614   cursor_pos_old.Y = si.height / 2;
615   set_cursor_position(&tty_out, cursor_pos_old);
616 
617   /* cursor next line one times if omitted arguments */
618   snprintf(buffer, sizeof(buffer), "%sE", CSI);
619   write_console(&tty_out, buffer);
620   get_cursor_position(&tty_out, &cursor_pos);
621   ASSERT_EQ(cursor_pos_old.Y + 1, cursor_pos.Y);
622   ASSERT_EQ(1, cursor_pos.X);
623 
624   /* cursor next line nth times */
625   cursor_pos_old = cursor_pos;
626   snprintf(buffer, sizeof(buffer), "%s%dE", CSI, si.height / 4);
627   write_console(&tty_out, buffer);
628   get_cursor_position(&tty_out, &cursor_pos);
629   ASSERT_EQ(cursor_pos_old.Y + si.height / 4, cursor_pos.Y);
630   ASSERT_EQ(1, cursor_pos.X);
631 
632   /* cursor next line from buttom row moves beginning of line */
633   cursor_pos_old.X = si.width / 2;
634   cursor_pos_old.Y = si.height;
635   set_cursor_position(&tty_out, cursor_pos_old);
636   snprintf(buffer, sizeof(buffer), "%sE", CSI);
637   write_console(&tty_out, buffer);
638   get_cursor_position(&tty_out, &cursor_pos);
639   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
640   ASSERT_EQ(1, cursor_pos.X);
641   ASSERT(!is_scrolling(&tty_out, si));
642 
643   terminate_tty(&tty_out);
644 
645   uv_run(loop, UV_RUN_DEFAULT);
646 
647   MAKE_VALGRIND_HAPPY(loop);
648   return 0;
649 }
650 
651 
TEST_IMPL(tty_cursor_previous_line)652 TEST_IMPL(tty_cursor_previous_line) {
653   uv_tty_t tty_out;
654   uv_loop_t* loop;
655   COORD cursor_pos, cursor_pos_old;
656   char buffer[1024];
657   struct screen_info si;
658 
659   loop = uv_default_loop();
660 
661   initialize_tty(&tty_out);
662   get_screen_info(&tty_out, &si);
663 
664   cursor_pos_old.X = si.width / 2;
665   cursor_pos_old.Y = si.height / 2;
666   set_cursor_position(&tty_out, cursor_pos_old);
667 
668   /* cursor previous line one times if omitted arguments */
669   snprintf(buffer, sizeof(buffer), "%sF", CSI);
670   write_console(&tty_out, buffer);
671   get_cursor_position(&tty_out, &cursor_pos);
672   ASSERT_EQ(cursor_pos_old.Y - 1, cursor_pos.Y);
673   ASSERT_EQ(1, cursor_pos.X);
674 
675   /* cursor previous line nth times */
676   cursor_pos_old = cursor_pos;
677   snprintf(buffer, sizeof(buffer), "%s%dF", CSI, si.height / 4);
678   write_console(&tty_out, buffer);
679   get_cursor_position(&tty_out, &cursor_pos);
680   ASSERT_EQ(cursor_pos_old.Y - si.height / 4, cursor_pos.Y);
681   ASSERT_EQ(1, cursor_pos.X);
682 
683   /* cursor previous line from top of screen does nothing */
684   cursor_pos_old.X = 1;
685   cursor_pos_old.Y = 1;
686   set_cursor_position(&tty_out, cursor_pos_old);
687   snprintf(buffer, sizeof(buffer), "%sD", CSI);
688   write_console(&tty_out, buffer);
689   get_cursor_position(&tty_out, &cursor_pos);
690   ASSERT_EQ(1, cursor_pos.Y);
691   ASSERT_EQ(1, cursor_pos.X);
692   ASSERT(!is_scrolling(&tty_out, si));
693 
694   terminate_tty(&tty_out);
695 
696   uv_run(loop, UV_RUN_DEFAULT);
697 
698   MAKE_VALGRIND_HAPPY(loop);
699   return 0;
700 }
701 
702 
TEST_IMPL(tty_cursor_horizontal_move_absolute)703 TEST_IMPL(tty_cursor_horizontal_move_absolute) {
704   uv_tty_t tty_out;
705   uv_loop_t* loop;
706   COORD cursor_pos, cursor_pos_old;
707   char buffer[1024];
708   struct screen_info si;
709 
710   loop = uv_default_loop();
711 
712   initialize_tty(&tty_out);
713   get_screen_info(&tty_out, &si);
714 
715   cursor_pos_old.X = si.width / 2;
716   cursor_pos_old.Y = si.height / 2;
717   set_cursor_position(&tty_out, cursor_pos_old);
718 
719   /* Move to beginning of line if omitted argument */
720   snprintf(buffer, sizeof(buffer), "%sG", CSI);
721   write_console(&tty_out, buffer);
722   get_cursor_position(&tty_out, &cursor_pos);
723   ASSERT_EQ(1, cursor_pos.X);
724   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
725 
726   /* Move cursor to nth character */
727   snprintf(buffer, sizeof(buffer), "%s%dG", CSI, si.width / 4);
728   write_console(&tty_out, buffer);
729   get_cursor_position(&tty_out, &cursor_pos);
730   ASSERT_EQ(si.width / 4, cursor_pos.X);
731   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
732 
733   /* Moving out of screen will fit within screen */
734   snprintf(buffer, sizeof(buffer), "%s%dG", CSI, si.width + 1);
735   write_console(&tty_out, buffer);
736   get_cursor_position(&tty_out, &cursor_pos);
737   ASSERT_EQ(si.width, cursor_pos.X);
738   ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y);
739 
740   terminate_tty(&tty_out);
741 
742   uv_run(loop, UV_RUN_DEFAULT);
743 
744   MAKE_VALGRIND_HAPPY(loop);
745   return 0;
746 }
747 
748 
TEST_IMPL(tty_cursor_move_absolute)749 TEST_IMPL(tty_cursor_move_absolute) {
750   uv_tty_t tty_out;
751   uv_loop_t* loop;
752   COORD cursor_pos;
753   char buffer[1024];
754   struct screen_info si;
755 
756   loop = uv_default_loop();
757 
758   initialize_tty(&tty_out);
759   get_screen_info(&tty_out, &si);
760 
761   cursor_pos.X = si.width / 2;
762   cursor_pos.Y = si.height / 2;
763   set_cursor_position(&tty_out, cursor_pos);
764 
765   /* Move the cursor to home if omitted arguments */
766   snprintf(buffer, sizeof(buffer), "%sH", CSI);
767   write_console(&tty_out, buffer);
768   get_cursor_position(&tty_out, &cursor_pos);
769   ASSERT_EQ(1, cursor_pos.X);
770   ASSERT_EQ(1, cursor_pos.Y);
771 
772   /* Move the cursor to the middle of the screen */
773   snprintf(
774       buffer, sizeof(buffer), "%s%d;%df", CSI, si.height / 2, si.width / 2);
775   write_console(&tty_out, buffer);
776   get_cursor_position(&tty_out, &cursor_pos);
777   ASSERT_EQ(si.width / 2, cursor_pos.X);
778   ASSERT_EQ(si.height / 2, cursor_pos.Y);
779 
780   /* Moving out of screen will fit within screen */
781   snprintf(
782       buffer, sizeof(buffer), "%s%d;%df", CSI, si.height / 2, si.width + 1);
783   write_console(&tty_out, buffer);
784   get_cursor_position(&tty_out, &cursor_pos);
785   ASSERT_EQ(si.width, cursor_pos.X);
786   ASSERT_EQ(si.height / 2, cursor_pos.Y);
787 
788   snprintf(
789       buffer, sizeof(buffer), "%s%d;%df", CSI, si.height + 1, si.width / 2);
790   write_console(&tty_out, buffer);
791   get_cursor_position(&tty_out, &cursor_pos);
792   ASSERT_EQ(si.width / 2, cursor_pos.X);
793   ASSERT_EQ(si.height, cursor_pos.Y);
794   ASSERT(!is_scrolling(&tty_out, si));
795 
796   terminate_tty(&tty_out);
797 
798   uv_run(loop, UV_RUN_DEFAULT);
799 
800   MAKE_VALGRIND_HAPPY(loop);
801   return 0;
802 }
803 
804 
TEST_IMPL(tty_hide_show_cursor)805 TEST_IMPL(tty_hide_show_cursor) {
806   uv_tty_t tty_out;
807   uv_loop_t* loop;
808   char buffer[1024];
809   BOOL saved_cursor_visibility;
810 
811   loop = uv_default_loop();
812 
813   initialize_tty(&tty_out);
814 
815   saved_cursor_visibility = get_cursor_visibility(&tty_out);
816 
817   /* Hide the cursor */
818   set_cursor_visibility(&tty_out, TRUE);
819   snprintf(buffer, sizeof(buffer), "%s?25l", CSI);
820   write_console(&tty_out, buffer);
821   ASSERT(!get_cursor_visibility(&tty_out));
822 
823   /* Show the cursor */
824   set_cursor_visibility(&tty_out, FALSE);
825   snprintf(buffer, sizeof(buffer), "%s?25h", CSI);
826   write_console(&tty_out, buffer);
827   ASSERT(get_cursor_visibility(&tty_out));
828 
829   set_cursor_visibility(&tty_out, saved_cursor_visibility);
830   terminate_tty(&tty_out);
831 
832   uv_run(loop, UV_RUN_DEFAULT);
833 
834   MAKE_VALGRIND_HAPPY(loop);
835   return 0;
836 }
837 
838 
TEST_IMPL(tty_erase)839 TEST_IMPL(tty_erase) {
840   int dir;
841   uv_tty_t tty_out;
842   uv_loop_t* loop;
843   COORD cursor_pos;
844   char buffer[1024];
845   struct captured_screen actual = {0}, expect = {0};
846 
847   loop = uv_default_loop();
848 
849   initialize_tty(&tty_out);
850 
851   /* Erase to below if omitted argument */
852   dir = 0;
853   setup_screen(&tty_out);
854   capture_screen(&tty_out, &expect);
855   cursor_pos.X = expect.si.width / 2;
856   cursor_pos.Y = expect.si.height / 2;
857   make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
858 
859   set_cursor_position(&tty_out, cursor_pos);
860   snprintf(buffer, sizeof(buffer), "%sJ", CSI);
861   write_console(&tty_out, buffer);
862   capture_screen(&tty_out, &actual);
863 
864   ASSERT(compare_screen(&tty_out, &actual, &expect));
865 
866   /* Erase to below(dir = 0) */
867   setup_screen(&tty_out);
868   capture_screen(&tty_out, &expect);
869   make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
870 
871   set_cursor_position(&tty_out, cursor_pos);
872   snprintf(buffer, sizeof(buffer), "%s%dJ", CSI, dir);
873   write_console(&tty_out, buffer);
874   capture_screen(&tty_out, &actual);
875 
876   ASSERT(compare_screen(&tty_out, &actual, &expect));
877 
878   /* Erase to above */
879   dir = 1;
880   setup_screen(&tty_out);
881   capture_screen(&tty_out, &expect);
882   make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
883 
884   set_cursor_position(&tty_out, cursor_pos);
885   snprintf(buffer, sizeof(buffer), "%s%dJ", CSI, dir);
886   write_console(&tty_out, buffer);
887   capture_screen(&tty_out, &actual);
888 
889   ASSERT(compare_screen(&tty_out, &actual, &expect));
890 
891   /* Erase All */
892   dir = 2;
893   setup_screen(&tty_out);
894   capture_screen(&tty_out, &expect);
895   make_expect_screen_erase(&expect, cursor_pos, dir, TRUE);
896 
897   set_cursor_position(&tty_out, cursor_pos);
898   snprintf(buffer, sizeof(buffer), "%s%dJ", CSI, dir);
899   write_console(&tty_out, buffer);
900   capture_screen(&tty_out, &actual);
901 
902   ASSERT(compare_screen(&tty_out, &actual, &expect));
903 
904   terminate_tty(&tty_out);
905 
906   uv_run(loop, UV_RUN_DEFAULT);
907 
908   MAKE_VALGRIND_HAPPY(loop);
909   return 0;
910 }
911 
912 
TEST_IMPL(tty_erase_line)913 TEST_IMPL(tty_erase_line) {
914   int dir;
915   uv_tty_t tty_out;
916   uv_loop_t* loop;
917   COORD cursor_pos;
918   char buffer[1024];
919   struct captured_screen actual = {0}, expect = {0};
920 
921   loop = uv_default_loop();
922 
923   initialize_tty(&tty_out);
924 
925   /* Erase to right if omitted arguments */
926   dir = 0;
927   setup_screen(&tty_out);
928   capture_screen(&tty_out, &expect);
929   cursor_pos.X = expect.si.width / 2;
930   cursor_pos.Y = expect.si.height / 2;
931   make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
932 
933   set_cursor_position(&tty_out, cursor_pos);
934   snprintf(buffer, sizeof(buffer), "%sK", CSI);
935   write_console(&tty_out, buffer);
936   capture_screen(&tty_out, &actual);
937 
938   ASSERT(compare_screen(&tty_out, &actual, &expect));
939 
940   /* Erase to right(dir = 0) */
941   setup_screen(&tty_out);
942   capture_screen(&tty_out, &expect);
943   make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
944 
945   set_cursor_position(&tty_out, cursor_pos);
946   snprintf(buffer, sizeof(buffer), "%s%dK", CSI, dir);
947   write_console(&tty_out, buffer);
948   capture_screen(&tty_out, &actual);
949 
950   ASSERT(compare_screen(&tty_out, &actual, &expect));
951 
952   /* Erase to Left */
953   dir = 1;
954   setup_screen(&tty_out);
955   capture_screen(&tty_out, &expect);
956   make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
957 
958   set_cursor_position(&tty_out, cursor_pos);
959   snprintf(buffer, sizeof(buffer), "%s%dK", CSI, dir);
960   write_console(&tty_out, buffer);
961   capture_screen(&tty_out, &actual);
962 
963   ASSERT(compare_screen(&tty_out, &actual, &expect));
964 
965   /* Erase All */
966   dir = 2;
967   setup_screen(&tty_out);
968   capture_screen(&tty_out, &expect);
969   make_expect_screen_erase(&expect, cursor_pos, dir, FALSE);
970 
971   set_cursor_position(&tty_out, cursor_pos);
972   snprintf(buffer, sizeof(buffer), "%s%dK", CSI, dir);
973   write_console(&tty_out, buffer);
974   capture_screen(&tty_out, &actual);
975 
976   ASSERT(compare_screen(&tty_out, &actual, &expect));
977 
978   terminate_tty(&tty_out);
979 
980   uv_run(loop, UV_RUN_DEFAULT);
981 
982   MAKE_VALGRIND_HAPPY(loop);
983   return 0;
984 }
985 
986 
TEST_IMPL(tty_set_cursor_shape)987 TEST_IMPL(tty_set_cursor_shape) {
988   uv_tty_t tty_out;
989   uv_loop_t* loop;
990   DWORD saved_cursor_size;
991   char buffer[1024];
992 
993   loop = uv_default_loop();
994 
995   initialize_tty(&tty_out);
996 
997   saved_cursor_size = get_cursor_size(&tty_out);
998 
999   /* cursor size large if omitted arguments */
1000   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1001   snprintf(buffer, sizeof(buffer), "%s q", CSI);
1002   write_console(&tty_out, buffer);
1003   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE);
1004 
1005   /* cursor size large */
1006   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1007   snprintf(buffer, sizeof(buffer), "%s1 q", CSI);
1008   write_console(&tty_out, buffer);
1009   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE);
1010   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1011   snprintf(buffer, sizeof(buffer), "%s2 q", CSI);
1012   write_console(&tty_out, buffer);
1013   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE);
1014 
1015   /* cursor size small */
1016   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1017   snprintf(buffer, sizeof(buffer), "%s3 q", CSI);
1018   write_console(&tty_out, buffer);
1019   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_SMALL);
1020   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1021   snprintf(buffer, sizeof(buffer), "%s6 q", CSI);
1022   write_console(&tty_out, buffer);
1023   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_SMALL);
1024 
1025   /* Nothing occurs with arguments outside valid range */
1026   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1027   snprintf(buffer, sizeof(buffer), "%s7 q", CSI);
1028   write_console(&tty_out, buffer);
1029   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE);
1030 
1031   /* restore cursor size if arguments is zero */
1032   snprintf(buffer, sizeof(buffer), "%s0 q", CSI);
1033   write_console(&tty_out, buffer);
1034   ASSERT_EQ(get_cursor_size(&tty_out), saved_cursor_size);
1035 
1036   terminate_tty(&tty_out);
1037 
1038   uv_run(loop, UV_RUN_DEFAULT);
1039 
1040   MAKE_VALGRIND_HAPPY(loop);
1041   return 0;
1042 }
1043 
1044 
TEST_IMPL(tty_set_style)1045 TEST_IMPL(tty_set_style) {
1046 #if _MSC_VER >= 1920 && _MSC_VER <= 1929
1047   RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. "
1048               "See: https://github.com/libuv/libuv/issues/3304");
1049 #else
1050 
1051   uv_tty_t tty_out;
1052   uv_loop_t* loop;
1053   COORD cursor_pos;
1054   char buffer[1024];
1055   struct captured_screen actual = {0}, expect = {0};
1056   WORD fg, bg;
1057   WORD fg_attrs[9][2] = {{F_BLACK, FOREGROUND_BLACK},
1058                          {F_RED, FOREGROUND_RED},
1059                          {F_GREEN, FOREGROUND_GREEN},
1060                          {F_YELLOW, FOREGROUND_YELLOW},
1061                          {F_BLUE, FOREGROUND_BLUE},
1062                          {F_MAGENTA, FOREGROUND_MAGENTA},
1063                          {F_CYAN, FOREGROUND_CYAN},
1064                          {F_WHITE, FOREGROUND_WHITE},
1065                          {F_DEFAULT, 0}};
1066   WORD bg_attrs[9][2] = {{B_DEFAULT, 0},
1067                          {B_BLACK, BACKGROUND_BLACK},
1068                          {B_RED, BACKGROUND_RED},
1069                          {B_GREEN, BACKGROUND_GREEN},
1070                          {B_YELLOW, BACKGROUND_YELLOW},
1071                          {B_BLUE, BACKGROUND_BLUE},
1072                          {B_MAGENTA, BACKGROUND_MAGENTA},
1073                          {B_CYAN, BACKGROUND_CYAN},
1074                          {B_WHITE, BACKGROUND_WHITE}};
1075   WORD attr;
1076   int i, length;
1077 
1078   loop = uv_default_loop();
1079 
1080   initialize_tty(&tty_out);
1081 
1082   capture_screen(&tty_out, &expect);
1083   fg_attrs[8][1] = expect.si.default_attr & FOREGROUND_WHITE;
1084   bg_attrs[0][1] = expect.si.default_attr & BACKGROUND_WHITE;
1085 
1086   /* Set foreground color */
1087   length = ARRAY_SIZE(fg_attrs);
1088   for (i = 0; i < length; i++) {
1089     capture_screen(&tty_out, &expect);
1090     cursor_pos.X = expect.si.width / 2;
1091     cursor_pos.Y = expect.si.height / 2;
1092     attr = (expect.si.default_attr & ~FOREGROUND_WHITE) | fg_attrs[i][1];
1093     make_expect_screen_write(&expect, cursor_pos, HELLO);
1094     make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1095 
1096     set_cursor_position(&tty_out, cursor_pos);
1097     snprintf(
1098         buffer, sizeof(buffer), "%s%dm%s%sm", CSI, fg_attrs[i][0], HELLO, CSI);
1099     write_console(&tty_out, buffer);
1100     capture_screen(&tty_out, &actual);
1101 
1102     ASSERT(compare_screen(&tty_out, &actual, &expect));
1103   }
1104 
1105   /* Set background color */
1106   length = ARRAY_SIZE(bg_attrs);
1107   for (i = 0; i < length; i++) {
1108     capture_screen(&tty_out, &expect);
1109     cursor_pos.X = expect.si.width / 2;
1110     cursor_pos.Y = expect.si.height / 2;
1111     attr = (expect.si.default_attr & ~BACKGROUND_WHITE) | bg_attrs[i][1];
1112     make_expect_screen_write(&expect, cursor_pos, HELLO);
1113     make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1114 
1115     set_cursor_position(&tty_out, cursor_pos);
1116     snprintf(
1117         buffer, sizeof(buffer), "%s%dm%s%sm", CSI, bg_attrs[i][0], HELLO, CSI);
1118     write_console(&tty_out, buffer);
1119     capture_screen(&tty_out, &actual);
1120 
1121     ASSERT(compare_screen(&tty_out, &actual, &expect));
1122   }
1123 
1124   /* Set foreground and background color */
1125   ASSERT_EQ(ARRAY_SIZE(fg_attrs), ARRAY_SIZE(bg_attrs));
1126   length = ARRAY_SIZE(bg_attrs);
1127   for (i = 0; i < length; i++) {
1128     capture_screen(&tty_out, &expect);
1129     cursor_pos.X = expect.si.width / 2;
1130     cursor_pos.Y = expect.si.height / 2;
1131     attr = expect.si.default_attr & ~FOREGROUND_WHITE & ~BACKGROUND_WHITE;
1132     attr |= fg_attrs[i][1] | bg_attrs[i][1];
1133     make_expect_screen_write(&expect, cursor_pos, HELLO);
1134     make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1135 
1136     set_cursor_position(&tty_out, cursor_pos);
1137     snprintf(buffer,
1138              sizeof(buffer),
1139              "%s%d;%dm%s%sm",
1140              CSI,
1141              bg_attrs[i][0],
1142              fg_attrs[i][0],
1143              HELLO,
1144              CSI);
1145     write_console(&tty_out, buffer);
1146     capture_screen(&tty_out, &actual);
1147 
1148     ASSERT(compare_screen(&tty_out, &actual, &expect));
1149   }
1150 
1151   /* Set foreground bright on */
1152   capture_screen(&tty_out, &expect);
1153   cursor_pos.X = expect.si.width / 2;
1154   cursor_pos.Y = expect.si.height / 2;
1155   set_cursor_position(&tty_out, cursor_pos);
1156   attr = expect.si.default_attr;
1157   attr |= FOREGROUND_INTENSITY;
1158   make_expect_screen_write(&expect, cursor_pos, HELLO);
1159   make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1160   cursor_pos.X += strlen(HELLO);
1161   make_expect_screen_write(&expect, cursor_pos, HELLO);
1162   make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1163 
1164   snprintf(buffer,
1165            sizeof(buffer),
1166            "%s%dm%s%s%dm%s%dm%s%s%dm",
1167            CSI,
1168            F_INTENSITY,
1169            HELLO,
1170            CSI,
1171            F_INTENSITY_OFF1,
1172            CSI,
1173            F_INTENSITY,
1174            HELLO,
1175            CSI,
1176            F_INTENSITY_OFF2);
1177   write_console(&tty_out, buffer);
1178   capture_screen(&tty_out, &actual);
1179 
1180   ASSERT(compare_screen(&tty_out, &actual, &expect));
1181 
1182   /* Set background bright on */
1183   capture_screen(&tty_out, &expect);
1184   cursor_pos.X = expect.si.width / 2;
1185   cursor_pos.Y = expect.si.height / 2;
1186   set_cursor_position(&tty_out, cursor_pos);
1187   attr = expect.si.default_attr;
1188   attr |= BACKGROUND_INTENSITY;
1189   make_expect_screen_write(&expect, cursor_pos, HELLO);
1190   make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1191 
1192   snprintf(buffer,
1193            sizeof(buffer),
1194            "%s%dm%s%s%dm",
1195            CSI,
1196            B_INTENSITY,
1197            HELLO,
1198            CSI,
1199            B_INTENSITY_OFF);
1200   write_console(&tty_out, buffer);
1201   capture_screen(&tty_out, &actual);
1202 
1203   ASSERT(compare_screen(&tty_out, &actual, &expect));
1204 
1205   /* Inverse */
1206   capture_screen(&tty_out, &expect);
1207   cursor_pos.X = expect.si.width / 2;
1208   cursor_pos.Y = expect.si.height / 2;
1209   set_cursor_position(&tty_out, cursor_pos);
1210   attr = expect.si.default_attr;
1211   fg = attr & FOREGROUND_WHITE;
1212   bg = attr & BACKGROUND_WHITE;
1213   attr &= (~FOREGROUND_WHITE & ~BACKGROUND_WHITE);
1214   attr |= COMMON_LVB_REVERSE_VIDEO;
1215   attr |= fg << 4;
1216   attr |= bg >> 4;
1217   make_expect_screen_write(&expect, cursor_pos, HELLO);
1218   make_expect_screen_set_attr(&expect, cursor_pos, strlen(HELLO), attr);
1219   cursor_pos.X += strlen(HELLO);
1220   make_expect_screen_write(&expect, cursor_pos, HELLO);
1221 
1222   snprintf(buffer,
1223            sizeof(buffer),
1224            "%s%dm%s%s%dm%s",
1225            CSI,
1226            INVERSE,
1227            HELLO,
1228            CSI,
1229            INVERSE_OFF,
1230            HELLO);
1231   write_console(&tty_out, buffer);
1232   capture_screen(&tty_out, &actual);
1233 
1234   ASSERT(compare_screen(&tty_out, &actual, &expect));
1235 
1236   terminate_tty(&tty_out);
1237 
1238   uv_run(loop, UV_RUN_DEFAULT);
1239 
1240   MAKE_VALGRIND_HAPPY(loop);
1241   return 0;
1242 #endif
1243 }
1244 
1245 
TEST_IMPL(tty_save_restore_cursor_position)1246 TEST_IMPL(tty_save_restore_cursor_position) {
1247   uv_tty_t tty_out;
1248   uv_loop_t* loop;
1249   COORD cursor_pos, cursor_pos_old;
1250   char buffer[1024];
1251   struct screen_info si;
1252 
1253   loop = uv_default_loop();
1254 
1255   initialize_tty(&tty_out);
1256   get_screen_info(&tty_out, &si);
1257 
1258   cursor_pos_old.X = si.width / 2;
1259   cursor_pos_old.Y = si.height / 2;
1260   set_cursor_position(&tty_out, cursor_pos_old);
1261 
1262   /* save the cursor position */
1263   snprintf(buffer, sizeof(buffer), "%ss", CSI);
1264   write_console(&tty_out, buffer);
1265 
1266   cursor_pos.X = si.width / 4;
1267   cursor_pos.Y = si.height / 4;
1268   set_cursor_position(&tty_out, cursor_pos);
1269 
1270   /* restore the cursor position */
1271   snprintf(buffer, sizeof(buffer), "%su", CSI);
1272   write_console(&tty_out, buffer);
1273   get_cursor_position(&tty_out, &cursor_pos);
1274   ASSERT_EQ(cursor_pos.X, cursor_pos_old.X);
1275   ASSERT_EQ(cursor_pos.Y, cursor_pos_old.Y);
1276 
1277   cursor_pos_old.X = si.width / 2;
1278   cursor_pos_old.Y = si.height / 2;
1279   set_cursor_position(&tty_out, cursor_pos_old);
1280 
1281   /* save the cursor position */
1282   snprintf(buffer, sizeof(buffer), "%s7", ESC);
1283   write_console(&tty_out, buffer);
1284 
1285   cursor_pos.X = si.width / 4;
1286   cursor_pos.Y = si.height / 4;
1287   set_cursor_position(&tty_out, cursor_pos);
1288 
1289   /* restore the cursor position */
1290   snprintf(buffer, sizeof(buffer), "%s8", ESC);
1291   write_console(&tty_out, buffer);
1292   get_cursor_position(&tty_out, &cursor_pos);
1293   ASSERT_EQ(cursor_pos.X, cursor_pos_old.X);
1294   ASSERT_EQ(cursor_pos.Y, cursor_pos_old.Y);
1295 
1296   terminate_tty(&tty_out);
1297 
1298   uv_run(loop, UV_RUN_DEFAULT);
1299 
1300   MAKE_VALGRIND_HAPPY(loop);
1301   return 0;
1302 }
1303 
1304 
TEST_IMPL(tty_full_reset)1305 TEST_IMPL(tty_full_reset) {
1306   uv_tty_t tty_out;
1307   uv_loop_t* loop;
1308   char buffer[1024];
1309   struct captured_screen actual = {0}, expect = {0};
1310   COORD cursor_pos;
1311   DWORD saved_cursor_size;
1312   BOOL saved_cursor_visibility;
1313 
1314   loop = uv_default_loop();
1315 
1316   initialize_tty(&tty_out);
1317 
1318   capture_screen(&tty_out, &expect);
1319   setup_screen(&tty_out);
1320   cursor_pos.X = expect.si.width;
1321   cursor_pos.Y = expect.si.height;
1322   set_cursor_position(&tty_out, cursor_pos);
1323   snprintf(buffer, sizeof(buffer), "%s%d;%dm%s", CSI, F_CYAN, B_YELLOW, HELLO);
1324   saved_cursor_size = get_cursor_size(&tty_out);
1325   set_cursor_size(&tty_out,
1326                   saved_cursor_size == CURSOR_SIZE_LARGE ? CURSOR_SIZE_SMALL
1327                                                          : CURSOR_SIZE_LARGE);
1328   saved_cursor_visibility = get_cursor_visibility(&tty_out);
1329   set_cursor_visibility(&tty_out, saved_cursor_visibility ? FALSE : TRUE);
1330   write_console(&tty_out, buffer);
1331   snprintf(buffer, sizeof(buffer), "%sc", ESC);
1332   write_console(&tty_out, buffer);
1333   capture_screen(&tty_out, &actual);
1334   ASSERT(compare_screen(&tty_out, &actual, &expect));
1335   ASSERT_EQ(get_cursor_size(&tty_out), saved_cursor_size);
1336   ASSERT_EQ(get_cursor_visibility(&tty_out), saved_cursor_visibility);
1337   ASSERT_OK(actual.si.csbi.srWindow.Top);
1338 
1339   terminate_tty(&tty_out);
1340 
1341   uv_run(loop, UV_RUN_DEFAULT);
1342 
1343   MAKE_VALGRIND_HAPPY(loop);
1344   return 0;
1345 }
1346 
1347 
TEST_IMPL(tty_escape_sequence_processing)1348 TEST_IMPL(tty_escape_sequence_processing) {
1349 #if _MSC_VER >= 1920 && _MSC_VER <= 1929
1350   RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. "
1351               "See: https://github.com/libuv/libuv/issues/3304");
1352 #else
1353   uv_tty_t tty_out;
1354   uv_loop_t* loop;
1355   COORD cursor_pos, cursor_pos_old;
1356   DWORD saved_cursor_size;
1357   char buffer[1024];
1358   struct captured_screen actual = {0}, expect = {0};
1359   int dir;
1360 
1361   loop = uv_default_loop();
1362 
1363   initialize_tty(&tty_out);
1364 
1365   /* CSI + finally byte does not output anything */
1366   cursor_pos.X = 1;
1367   cursor_pos.Y = 1;
1368   set_cursor_position(&tty_out, cursor_pos);
1369   capture_screen(&tty_out, &expect);
1370   make_expect_screen_write(&expect, cursor_pos, HELLO);
1371   cursor_pos.X += strlen(HELLO);
1372   make_expect_screen_write(&expect, cursor_pos, HELLO);
1373   snprintf(buffer, sizeof(buffer), "%s@%s%s~%s", CSI, HELLO, CSI, HELLO);
1374   write_console(&tty_out, buffer);
1375   capture_screen(&tty_out, &actual);
1376   ASSERT(compare_screen(&tty_out, &actual, &expect));
1377 
1378   /* CSI(C1) + finally byte does not output anything */
1379   cursor_pos.X = 1;
1380   cursor_pos.Y = 1;
1381   set_cursor_position(&tty_out, cursor_pos);
1382   capture_screen(&tty_out, &expect);
1383   make_expect_screen_write(&expect, cursor_pos, HELLO);
1384   cursor_pos.X += strlen(HELLO);
1385   make_expect_screen_write(&expect, cursor_pos, HELLO);
1386   snprintf(buffer, sizeof(buffer), "\xC2\x9B@%s\xC2\x9B~%s", HELLO, HELLO);
1387   write_console(&tty_out, buffer);
1388   capture_screen(&tty_out, &actual);
1389   ASSERT(compare_screen(&tty_out, &actual, &expect));
1390 
1391   /* CSI + intermediate byte + finally byte does not output anything */
1392   cursor_pos.X = 1;
1393   cursor_pos.Y = 1;
1394   set_cursor_position(&tty_out, cursor_pos);
1395   capture_screen(&tty_out, &expect);
1396   make_expect_screen_write(&expect, cursor_pos, HELLO);
1397   cursor_pos.X += strlen(HELLO);
1398   make_expect_screen_write(&expect, cursor_pos, HELLO);
1399   snprintf(buffer, sizeof(buffer), "%s @%s%s/~%s", CSI, HELLO, CSI, HELLO);
1400   write_console(&tty_out, buffer);
1401   capture_screen(&tty_out, &actual);
1402   ASSERT(compare_screen(&tty_out, &actual, &expect));
1403 
1404   /* CSI + parameter byte + finally byte does not output anything */
1405   cursor_pos.X = 1;
1406   cursor_pos.Y = 1;
1407   set_cursor_position(&tty_out, cursor_pos);
1408   capture_screen(&tty_out, &expect);
1409   snprintf(buffer,
1410            sizeof(buffer),
1411            "%s0@%s%s>~%s%s?~%s",
1412            CSI,
1413            HELLO,
1414            CSI,
1415            HELLO,
1416            CSI,
1417            HELLO);
1418   make_expect_screen_write(&expect, cursor_pos, HELLO);
1419   cursor_pos.X += strlen(HELLO);
1420   make_expect_screen_write(&expect, cursor_pos, HELLO);
1421   cursor_pos.X += strlen(HELLO);
1422   make_expect_screen_write(&expect, cursor_pos, HELLO);
1423   write_console(&tty_out, buffer);
1424   capture_screen(&tty_out, &actual);
1425   ASSERT(compare_screen(&tty_out, &actual, &expect));
1426 
1427   /* ESC Single-char control does not output anyghing */
1428   cursor_pos.X = 1;
1429   cursor_pos.Y = 1;
1430   set_cursor_position(&tty_out, cursor_pos);
1431   capture_screen(&tty_out, &expect);
1432   make_expect_screen_write(&expect, cursor_pos, HELLO);
1433   cursor_pos.X += strlen(HELLO);
1434   make_expect_screen_write(&expect, cursor_pos, HELLO);
1435   snprintf(buffer, sizeof(buffer), "%s @%s%s/~%s", CSI, HELLO, CSI, HELLO);
1436   write_console(&tty_out, buffer);
1437   capture_screen(&tty_out, &actual);
1438   ASSERT(compare_screen(&tty_out, &actual, &expect));
1439 
1440   /* Nothing is output from ESC + ^, _, P, ] to BEL or ESC \ */
1441   /* Operaging System Command */
1442   cursor_pos.X = 1;
1443   cursor_pos.Y = 1;
1444   set_cursor_position(&tty_out, cursor_pos);
1445   capture_screen(&tty_out, &expect);
1446   make_expect_screen_write(&expect, cursor_pos, HELLO);
1447   snprintf(buffer, sizeof(buffer), "%s]0;%s%s%s", ESC, HELLO, BEL, HELLO);
1448   write_console(&tty_out, buffer);
1449   capture_screen(&tty_out, &actual);
1450   ASSERT(compare_screen(&tty_out, &actual, &expect));
1451   /* Device Control Sequence */
1452   cursor_pos.X = 1;
1453   cursor_pos.Y = 1;
1454   set_cursor_position(&tty_out, cursor_pos);
1455   capture_screen(&tty_out, &expect);
1456   make_expect_screen_write(&expect, cursor_pos, HELLO);
1457   snprintf(buffer, sizeof(buffer), "%sP$m%s%s", ESC, ST, HELLO);
1458   write_console(&tty_out, buffer);
1459   capture_screen(&tty_out, &actual);
1460   ASSERT(compare_screen(&tty_out, &actual, &expect));
1461   /* Privacy Message */
1462   cursor_pos.X = 1;
1463   cursor_pos.Y = 1;
1464   set_cursor_position(&tty_out, cursor_pos);
1465   capture_screen(&tty_out, &expect);
1466   make_expect_screen_write(&expect, cursor_pos, HELLO);
1467   snprintf(buffer,
1468            sizeof(buffer),
1469            "%s^\"%s\\\"%s\"%s%s",
1470            ESC,
1471            HELLO,
1472            HELLO,
1473            ST,
1474            HELLO);
1475   write_console(&tty_out, buffer);
1476   capture_screen(&tty_out, &actual);
1477   ASSERT(compare_screen(&tty_out, &actual, &expect));
1478   /* Application Program Command */
1479   cursor_pos.X = 1;
1480   cursor_pos.Y = 1;
1481   set_cursor_position(&tty_out, cursor_pos);
1482   capture_screen(&tty_out, &expect);
1483   make_expect_screen_write(&expect, cursor_pos, HELLO);
1484   snprintf(buffer,
1485            sizeof(buffer),
1486            "%s_\"%s%s%s\"%s%s",
1487            ESC,
1488            HELLO,
1489            ST,
1490            HELLO,
1491            BEL,
1492            HELLO);
1493   write_console(&tty_out, buffer);
1494   capture_screen(&tty_out, &actual);
1495   ASSERT(compare_screen(&tty_out, &actual, &expect));
1496 
1497   /* Ignore double escape */
1498   cursor_pos.X = 1;
1499   cursor_pos.Y = 1;
1500   set_cursor_position(&tty_out, cursor_pos);
1501   capture_screen(&tty_out, &expect);
1502   make_expect_screen_write(&expect, cursor_pos, HELLO);
1503   cursor_pos.X += strlen(HELLO);
1504   make_expect_screen_write(&expect, cursor_pos, HELLO);
1505   snprintf(buffer,
1506            sizeof(buffer),
1507            "%s%s@%s%s%s~%s",
1508            ESC,
1509            CSI,
1510            HELLO,
1511            ESC,
1512            CSI,
1513            HELLO);
1514   write_console(&tty_out, buffer);
1515   capture_screen(&tty_out, &actual);
1516   ASSERT(compare_screen(&tty_out, &actual, &expect));
1517 
1518   /* Ignored if argument overflow */
1519   set_cursor_to_home(&tty_out);
1520   snprintf(buffer, sizeof(buffer), "%s1;%dH", CSI, UINT16_MAX + 1);
1521   write_console(&tty_out, buffer);
1522   get_cursor_position(&tty_out, &cursor_pos);
1523   ASSERT_EQ(1, cursor_pos.X);
1524   ASSERT_EQ(1, cursor_pos.Y);
1525 
1526   /* Too many argument are ignored */
1527   cursor_pos.X = 1;
1528   cursor_pos.Y = 1;
1529   set_cursor_position(&tty_out, cursor_pos);
1530   capture_screen(&tty_out, &expect);
1531   make_expect_screen_write(&expect, cursor_pos, HELLO);
1532   snprintf(buffer,
1533            sizeof(buffer),
1534            "%s%d;%d;%d;%d;%dm%s%sm",
1535            CSI,
1536            F_RED,
1537            F_INTENSITY,
1538            INVERSE,
1539            B_CYAN,
1540            B_INTENSITY_OFF,
1541            HELLO,
1542            CSI);
1543   write_console(&tty_out, buffer);
1544   capture_screen(&tty_out, &actual);
1545   ASSERT(compare_screen(&tty_out, &actual, &expect));
1546 
1547   /* In the case of DECSCUSR, the others are ignored */
1548   set_cursor_to_home(&tty_out);
1549   snprintf(buffer,
1550            sizeof(buffer),
1551            "%s%d;%d H",
1552            CSI,
1553            expect.si.height / 2,
1554            expect.si.width / 2);
1555   write_console(&tty_out, buffer);
1556   get_cursor_position(&tty_out, &cursor_pos);
1557   ASSERT_EQ(1, cursor_pos.X);
1558   ASSERT_EQ(1, cursor_pos.Y);
1559 
1560   /* Invalid sequence are ignored */
1561   saved_cursor_size = get_cursor_size(&tty_out);
1562   set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE);
1563   snprintf(buffer, sizeof(buffer), "%s 1q", CSI);
1564   write_console(&tty_out, buffer);
1565   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE);
1566   snprintf(buffer, sizeof(buffer), "%s 1 q", CSI);
1567   write_console(&tty_out, buffer);
1568   ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE);
1569   set_cursor_size(&tty_out, saved_cursor_size);
1570 
1571   /* #1874 2. */
1572   snprintf(buffer, sizeof(buffer), "%s??25l", CSI);
1573   write_console(&tty_out, buffer);
1574   ASSERT(get_cursor_visibility(&tty_out));
1575   snprintf(buffer, sizeof(buffer), "%s25?l", CSI);
1576   write_console(&tty_out, buffer);
1577   ASSERT(get_cursor_visibility(&tty_out));
1578   cursor_pos_old.X = expect.si.width / 2;
1579   cursor_pos_old.Y = expect.si.height / 2;
1580   set_cursor_position(&tty_out, cursor_pos_old);
1581   snprintf(buffer,
1582            sizeof(buffer),
1583            "%s??%d;%df",
1584            CSI,
1585            expect.si.height / 4,
1586            expect.si.width / 4);
1587   write_console(&tty_out, buffer);
1588   get_cursor_position(&tty_out, &cursor_pos);
1589   ASSERT(cursor_pos.X = cursor_pos_old.X);
1590   ASSERT(cursor_pos.Y = cursor_pos_old.Y);
1591   set_cursor_to_home(&tty_out);
1592 
1593   /* CSI 25 l does nothing (#1874 4.) */
1594   snprintf(buffer, sizeof(buffer), "%s25l", CSI);
1595   write_console(&tty_out, buffer);
1596   ASSERT(get_cursor_visibility(&tty_out));
1597 
1598   /* Unsupported sequences are ignored(#1874 5.) */
1599   dir = 2;
1600   setup_screen(&tty_out);
1601   capture_screen(&tty_out, &expect);
1602   set_cursor_position(&tty_out, cursor_pos);
1603   snprintf(buffer, sizeof(buffer), "%s?%dJ", CSI, dir);
1604   write_console(&tty_out, buffer);
1605   capture_screen(&tty_out, &actual);
1606   ASSERT(compare_screen(&tty_out, &actual, &expect));
1607 
1608   /* Finally byte immedately after CSI [ are also output(#1874 1.) */
1609   cursor_pos.X = expect.si.width / 2;
1610   cursor_pos.Y = expect.si.height / 2;
1611   set_cursor_position(&tty_out, cursor_pos);
1612   capture_screen(&tty_out, &expect);
1613   make_expect_screen_write(&expect, cursor_pos, HELLO);
1614   snprintf(buffer, sizeof(buffer), "%s[%s", CSI, HELLO);
1615   write_console(&tty_out, buffer);
1616   capture_screen(&tty_out, &actual);
1617   ASSERT(compare_screen(&tty_out, &actual, &expect));
1618 
1619   terminate_tty(&tty_out);
1620 
1621   uv_run(loop, UV_RUN_DEFAULT);
1622 
1623   MAKE_VALGRIND_HAPPY(loop);
1624   return 0;
1625 #endif
1626 }
1627 
1628 #else
1629 
1630 typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */
1631 
1632 #endif  /* ifdef _WIN32 */
1633