Home | History | Annotate | Line # | Download | only in test
test-tty.c revision 1.1.1.2
      1 /* Copyright Joyent, Inc. and other Node 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 #include "uv.h"
     23 #include "task.h"
     24 
     25 #ifdef _WIN32
     26 # include <io.h>
     27 # include <windows.h>
     28 #else /*  Unix */
     29 # include <fcntl.h>
     30 # include <unistd.h>
     31 # if (defined(__linux__) || defined(__GLIBC__)) && !defined(__ANDROID__)
     32 #  include <pty.h>
     33 # elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
     34 #  include <util.h>
     35 # elif defined(__FreeBSD__) || defined(__DragonFly__)
     36 #  include <libutil.h>
     37 # endif
     38 #endif
     39 
     40 #include <string.h>
     41 #include <errno.h>
     42 
     43 
     44 TEST_IMPL(tty) {
     45   int r, width, height;
     46   int ttyin_fd, ttyout_fd;
     47   uv_tty_t tty_in, tty_out;
     48   uv_loop_t* loop = uv_default_loop();
     49 
     50   /* Make sure we have an FD that refers to a tty */
     51 #ifdef _WIN32
     52   HANDLE handle;
     53   handle = CreateFileA("conin$",
     54                        GENERIC_READ | GENERIC_WRITE,
     55                        FILE_SHARE_READ | FILE_SHARE_WRITE,
     56                        NULL,
     57                        OPEN_EXISTING,
     58                        FILE_ATTRIBUTE_NORMAL,
     59                        NULL);
     60   ASSERT(handle != INVALID_HANDLE_VALUE);
     61   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
     62 
     63   handle = CreateFileA("conout$",
     64                        GENERIC_READ | GENERIC_WRITE,
     65                        FILE_SHARE_READ | FILE_SHARE_WRITE,
     66                        NULL,
     67                        OPEN_EXISTING,
     68                        FILE_ATTRIBUTE_NORMAL,
     69                        NULL);
     70   ASSERT(handle != INVALID_HANDLE_VALUE);
     71   ttyout_fd = _open_osfhandle((intptr_t) handle, 0);
     72 
     73 #else /* unix */
     74   ttyin_fd = open("/dev/tty", O_RDONLY, 0);
     75   if (ttyin_fd < 0) {
     76     fprintf(stderr, "Cannot open /dev/tty as read-only: %s\n", strerror(errno));
     77     fflush(stderr);
     78     return TEST_SKIP;
     79   }
     80 
     81   ttyout_fd = open("/dev/tty", O_WRONLY, 0);
     82   if (ttyout_fd < 0) {
     83     fprintf(stderr, "Cannot open /dev/tty as write-only: %s\n", strerror(errno));
     84     fflush(stderr);
     85     return TEST_SKIP;
     86   }
     87 #endif
     88 
     89   ASSERT(ttyin_fd >= 0);
     90   ASSERT(ttyout_fd >= 0);
     91 
     92   ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1));
     93 
     94   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
     95   ASSERT(UV_TTY == uv_guess_handle(ttyout_fd));
     96 
     97   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
     98   ASSERT(r == 0);
     99   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
    100   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
    101 
    102   r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0);  /* Writable. */
    103   ASSERT(r == 0);
    104   ASSERT(!uv_is_readable((uv_stream_t*) &tty_out));
    105   ASSERT(uv_is_writable((uv_stream_t*) &tty_out));
    106 
    107   r = uv_tty_get_winsize(&tty_out, &width, &height);
    108   ASSERT(r == 0);
    109 
    110   printf("width=%d height=%d\n", width, height);
    111 
    112   if (width == 0 && height == 0) {
    113    /* Some environments such as containers or Jenkins behave like this
    114     * sometimes */
    115     MAKE_VALGRIND_HAPPY();
    116     return TEST_SKIP;
    117   }
    118 
    119   /*
    120    * Is it a safe assumption that most people have terminals larger than
    121    * 10x10?
    122    */
    123   ASSERT(width > 10);
    124   ASSERT(height > 10);
    125 
    126   /* Turn on raw mode. */
    127   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
    128   ASSERT(r == 0);
    129 
    130   /* Turn off raw mode. */
    131   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_NORMAL);
    132   ASSERT(r == 0);
    133 
    134   /* Calling uv_tty_reset_mode() repeatedly should not clobber errno. */
    135   errno = 0;
    136   ASSERT(0 == uv_tty_reset_mode());
    137   ASSERT(0 == uv_tty_reset_mode());
    138   ASSERT(0 == uv_tty_reset_mode());
    139   ASSERT(0 == errno);
    140 
    141   /* TODO check the actual mode! */
    142 
    143   uv_close((uv_handle_t*) &tty_in, NULL);
    144   uv_close((uv_handle_t*) &tty_out, NULL);
    145 
    146   uv_run(loop, UV_RUN_DEFAULT);
    147 
    148   MAKE_VALGRIND_HAPPY();
    149   return 0;
    150 }
    151 
    152 
    153 #ifdef _WIN32
    154 static void tty_raw_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
    155   buf->base = malloc(size);
    156   buf->len = size;
    157 }
    158 
    159 static void tty_raw_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) {
    160   if (nread > 0) {
    161     ASSERT(nread  == 1);
    162     ASSERT(buf->base[0] == ' ');
    163     uv_close((uv_handle_t*) tty_in, NULL);
    164   } else {
    165     ASSERT(nread == 0);
    166   }
    167 }
    168 
    169 TEST_IMPL(tty_raw) {
    170   int r;
    171   int ttyin_fd;
    172   uv_tty_t tty_in;
    173   uv_loop_t* loop = uv_default_loop();
    174   HANDLE handle;
    175   INPUT_RECORD record;
    176   DWORD written;
    177 
    178   /* Make sure we have an FD that refers to a tty */
    179   handle = CreateFileA("conin$",
    180                        GENERIC_READ | GENERIC_WRITE,
    181                        FILE_SHARE_READ | FILE_SHARE_WRITE,
    182                        NULL,
    183                        OPEN_EXISTING,
    184                        FILE_ATTRIBUTE_NORMAL,
    185                        NULL);
    186   ASSERT(handle != INVALID_HANDLE_VALUE);
    187   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
    188   ASSERT(ttyin_fd >= 0);
    189   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
    190 
    191   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
    192   ASSERT(r == 0);
    193   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
    194   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
    195 
    196   r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read);
    197   ASSERT(r == 0);
    198 
    199   /* Give uv_tty_line_read_thread time to block on ReadConsoleW */
    200   Sleep(100);
    201 
    202   /* Turn on raw mode. */
    203   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
    204   ASSERT(r == 0);
    205 
    206   /* Write ' ' that should be read in raw mode */
    207   record.EventType = KEY_EVENT;
    208   record.Event.KeyEvent.bKeyDown = TRUE;
    209   record.Event.KeyEvent.wRepeatCount = 1;
    210   record.Event.KeyEvent.wVirtualKeyCode = VK_SPACE;
    211   record.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyW(VK_SPACE, MAPVK_VK_TO_VSC);
    212   record.Event.KeyEvent.uChar.UnicodeChar = L' ';
    213   record.Event.KeyEvent.dwControlKeyState = 0;
    214   WriteConsoleInputW(handle, &record, 1, &written);
    215 
    216   uv_run(loop, UV_RUN_DEFAULT);
    217 
    218   MAKE_VALGRIND_HAPPY();
    219   return 0;
    220 }
    221 
    222 TEST_IMPL(tty_empty_write) {
    223   int r;
    224   int ttyout_fd;
    225   uv_tty_t tty_out;
    226   char dummy[1];
    227   uv_buf_t bufs[1];
    228   uv_loop_t* loop;
    229 
    230   /* Make sure we have an FD that refers to a tty */
    231   HANDLE handle;
    232 
    233   loop = uv_default_loop();
    234 
    235   handle = CreateFileA("conout$",
    236                        GENERIC_READ | GENERIC_WRITE,
    237                        FILE_SHARE_READ | FILE_SHARE_WRITE,
    238                        NULL,
    239                        OPEN_EXISTING,
    240                        FILE_ATTRIBUTE_NORMAL,
    241                        NULL);
    242   ASSERT(handle != INVALID_HANDLE_VALUE);
    243   ttyout_fd = _open_osfhandle((intptr_t) handle, 0);
    244 
    245   ASSERT(ttyout_fd >= 0);
    246 
    247   ASSERT(UV_TTY == uv_guess_handle(ttyout_fd));
    248 
    249   r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0);  /* Writable. */
    250   ASSERT(r == 0);
    251   ASSERT(!uv_is_readable((uv_stream_t*) &tty_out));
    252   ASSERT(uv_is_writable((uv_stream_t*) &tty_out));
    253 
    254   bufs[0].len = 0;
    255   bufs[0].base = &dummy[0];
    256 
    257   r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1);
    258   ASSERT(r == 0);
    259 
    260   uv_close((uv_handle_t*) &tty_out, NULL);
    261 
    262   uv_run(loop, UV_RUN_DEFAULT);
    263 
    264   MAKE_VALGRIND_HAPPY();
    265   return 0;
    266 }
    267 
    268 TEST_IMPL(tty_large_write) {
    269   int r;
    270   int ttyout_fd;
    271   uv_tty_t tty_out;
    272   char dummy[10000];
    273   uv_buf_t bufs[1];
    274   uv_loop_t* loop;
    275 
    276   /* Make sure we have an FD that refers to a tty */
    277   HANDLE handle;
    278 
    279   loop = uv_default_loop();
    280 
    281   handle = CreateFileA("conout$",
    282                        GENERIC_READ | GENERIC_WRITE,
    283                        FILE_SHARE_READ | FILE_SHARE_WRITE,
    284                        NULL,
    285                        OPEN_EXISTING,
    286                        FILE_ATTRIBUTE_NORMAL,
    287                        NULL);
    288   ASSERT(handle != INVALID_HANDLE_VALUE);
    289   ttyout_fd = _open_osfhandle((intptr_t) handle, 0);
    290 
    291   ASSERT(ttyout_fd >= 0);
    292 
    293   ASSERT(UV_TTY == uv_guess_handle(ttyout_fd));
    294 
    295   r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0);  /* Writable. */
    296   ASSERT(r == 0);
    297 
    298   memset(dummy, '.', sizeof(dummy) - 1);
    299   dummy[sizeof(dummy) - 1] = '\n';
    300 
    301   bufs[0] = uv_buf_init(dummy, sizeof(dummy));
    302 
    303   r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1);
    304   ASSERT(r == 10000);
    305 
    306   uv_close((uv_handle_t*) &tty_out, NULL);
    307 
    308   uv_run(loop, UV_RUN_DEFAULT);
    309 
    310   MAKE_VALGRIND_HAPPY();
    311   return 0;
    312 }
    313 
    314 TEST_IMPL(tty_raw_cancel) {
    315   int r;
    316   int ttyin_fd;
    317   uv_tty_t tty_in;
    318   HANDLE handle;
    319 
    320   /* Make sure we have an FD that refers to a tty */
    321   handle = CreateFileA("conin$",
    322                        GENERIC_READ | GENERIC_WRITE,
    323                        FILE_SHARE_READ | FILE_SHARE_WRITE,
    324                        NULL,
    325                        OPEN_EXISTING,
    326                        FILE_ATTRIBUTE_NORMAL,
    327                        NULL);
    328   ASSERT(handle != INVALID_HANDLE_VALUE);
    329   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
    330   ASSERT(ttyin_fd >= 0);
    331   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
    332 
    333   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
    334   ASSERT(r == 0);
    335   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
    336   ASSERT(r == 0);
    337   r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read);
    338   ASSERT(r == 0);
    339 
    340   r = uv_read_stop((uv_stream_t*) &tty_in);
    341   ASSERT(r == 0);
    342 
    343   MAKE_VALGRIND_HAPPY();
    344   return 0;
    345 }
    346 #endif
    347 
    348 
    349 TEST_IMPL(tty_file) {
    350 #ifndef _WIN32
    351   uv_loop_t loop;
    352   uv_tty_t tty;
    353   uv_tty_t tty_ro;
    354   uv_tty_t tty_wo;
    355   int fd;
    356 
    357   ASSERT(0 == uv_loop_init(&loop));
    358 
    359   fd = open("test/fixtures/empty_file", O_RDONLY);
    360   if (fd != -1) {
    361     ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1));
    362     ASSERT(0 == close(fd));
    363     /* test EBADF handling */
    364     ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1));
    365   }
    366 
    367 /* Bug on AIX where '/dev/random' returns 1 from isatty() */
    368 #ifndef _AIX
    369   fd = open("/dev/random", O_RDONLY);
    370   if (fd != -1) {
    371     ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1));
    372     ASSERT(0 == close(fd));
    373   }
    374 #endif /* _AIX */
    375 
    376   fd = open("/dev/zero", O_RDONLY);
    377   if (fd != -1) {
    378     ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1));
    379     ASSERT(0 == close(fd));
    380   }
    381 
    382   fd = open("/dev/tty", O_RDWR);
    383   if (fd != -1) {
    384     ASSERT(0 == uv_tty_init(&loop, &tty, fd, 1));
    385     ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */
    386     ASSERT(uv_is_readable((uv_stream_t*) &tty));
    387     ASSERT(uv_is_writable((uv_stream_t*) &tty));
    388     uv_close((uv_handle_t*) &tty, NULL);
    389     ASSERT(!uv_is_readable((uv_stream_t*) &tty));
    390     ASSERT(!uv_is_writable((uv_stream_t*) &tty));
    391   }
    392 
    393   fd = open("/dev/tty", O_RDONLY);
    394   if (fd != -1) {
    395     ASSERT(0 == uv_tty_init(&loop, &tty_ro, fd, 1));
    396     ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */
    397     ASSERT(uv_is_readable((uv_stream_t*) &tty_ro));
    398     ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro));
    399     uv_close((uv_handle_t*) &tty_ro, NULL);
    400     ASSERT(!uv_is_readable((uv_stream_t*) &tty_ro));
    401     ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro));
    402   }
    403 
    404   fd = open("/dev/tty", O_WRONLY);
    405   if (fd != -1) {
    406     ASSERT(0 == uv_tty_init(&loop, &tty_wo, fd, 0));
    407     ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */
    408     ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo));
    409     ASSERT(uv_is_writable((uv_stream_t*) &tty_wo));
    410     uv_close((uv_handle_t*) &tty_wo, NULL);
    411     ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo));
    412     ASSERT(!uv_is_writable((uv_stream_t*) &tty_wo));
    413   }
    414 
    415 
    416   ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
    417   ASSERT(0 == uv_loop_close(&loop));
    418 
    419   MAKE_VALGRIND_HAPPY();
    420 #endif
    421   return 0;
    422 }
    423 
    424 TEST_IMPL(tty_pty) {
    425 /* TODO(gengjiawen): Fix test on QEMU. */
    426 #if defined(__QEMU__)
    427   RETURN_SKIP("Test does not currently work in QEMU");
    428 #endif
    429 #if defined(__ASAN__)
    430   RETURN_SKIP("Test does not currently work in ASAN");
    431 #endif
    432 
    433 #if defined(__APPLE__)                            || \
    434     defined(__DragonFly__)                        || \
    435     defined(__FreeBSD__)                          || \
    436     defined(__FreeBSD_kernel__)                   || \
    437     (defined(__linux__) && !defined(__ANDROID__)) || \
    438     defined(__NetBSD__)                           || \
    439     defined(__OpenBSD__)
    440   int master_fd, slave_fd, r;
    441   struct winsize w;
    442   uv_loop_t loop;
    443   uv_tty_t master_tty, slave_tty;
    444 
    445   ASSERT(0 == uv_loop_init(&loop));
    446 
    447   r = openpty(&master_fd, &slave_fd, NULL, NULL, &w);
    448   if (r != 0)
    449     RETURN_SKIP("No pty available, skipping.");
    450 
    451   ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0));
    452   ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0));
    453   ASSERT(uv_is_readable((uv_stream_t*) &slave_tty));
    454   ASSERT(uv_is_writable((uv_stream_t*) &slave_tty));
    455   ASSERT(uv_is_readable((uv_stream_t*) &master_tty));
    456   ASSERT(uv_is_writable((uv_stream_t*) &master_tty));
    457   /* Check if the file descriptor was reopened. If it is,
    458    * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags.
    459    */
    460   ASSERT(0 == (slave_tty.flags & 0x100000));
    461   /* The master_fd of a pty should never be reopened.
    462    */
    463   ASSERT(master_tty.flags & 0x100000);
    464   ASSERT(0 == close(slave_fd));
    465   uv_close((uv_handle_t*) &slave_tty, NULL);
    466   ASSERT(0 == close(master_fd));
    467   uv_close((uv_handle_t*) &master_tty, NULL);
    468 
    469   ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
    470 
    471   MAKE_VALGRIND_HAPPY();
    472 #endif
    473   return 0;
    474 }
    475