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