LCOV - code coverage report
Current view: top level - tests/functional/pty - test_terminal_coverage.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 89.4 % 226 202
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 12 12

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file test_terminal_coverage.c
       6              :  * @brief Coverage-boosting PTY tests for src/platform/posix/terminal.c.
       7              :  *
       8              :  * Drives terminal_coverage_harness through various modes to exercise the
       9              :  * lines not reached by the existing password-prompt and readline PTY tests:
      10              :  *
      11              :  *   - terminal_cols() / terminal_rows() success path (ioctl returns real size)
      12              :  *   - terminal_raw_enter() / terminal_raw_exit()
      13              :  *   - terminal_read_key() for all key codes (arrows, ESC sequences, Ctrl keys)
      14              :  *   - terminal_wait_key()
      15              :  *   - terminal_install_cleanup_handlers()
      16              :  *   - terminal_enable_resize_notifications() / terminal_consume_resize()
      17              :  *   - terminal_read_password() non-TTY (piped stdin) path
      18              :  */
      19              : 
      20              : #include "ptytest.h"
      21              : #include "pty_assert.h"
      22              : 
      23              : #include <signal.h>
      24              : #include <stdio.h>
      25              : #include <stdlib.h>
      26              : #include <string.h>
      27              : #include <unistd.h>
      28              : #include <sys/types.h>
      29              : #include <sys/wait.h>
      30              : 
      31              : /* ── Globals ─────────────────────────────────────────────────────────── */
      32              : 
      33              : static int g_tests_run    = 0;
      34              : static int g_tests_failed = 0;
      35              : 
      36              : #ifndef TERMINAL_COVERAGE_HARNESS
      37              : #define TERMINAL_COVERAGE_HARNESS "terminal_coverage_harness"
      38              : #endif
      39              : 
      40              : /* ── Helpers ─────────────────────────────────────────────────────────── */
      41              : 
      42              : #define RUN_TEST(fn) do { \
      43              :     printf("  running %s ...\n", #fn); \
      44              :     fn(); \
      45              : } while(0)
      46              : 
      47              : #define CHECK_WAIT_FOR(s, text, timeout_ms, label) do { \
      48              :     g_tests_run++; \
      49              :     if (pty_wait_for((s), (text), (timeout_ms)) != 0) { \
      50              :         printf("  [FAIL] %s:%d: wait_for(\"%s\", %d ms) timed out\n", \
      51              :                __FILE__, __LINE__, (text), (timeout_ms)); \
      52              :         g_tests_failed++; \
      53              :         goto label; \
      54              :     } \
      55              : } while(0)
      56              : 
      57              : #define CHECK(cond, msg, label) do { \
      58              :     g_tests_run++; \
      59              :     if (!(cond)) { \
      60              :         printf("  [FAIL] %s:%d: %s\n", __FILE__, __LINE__, (msg)); \
      61              :         g_tests_failed++; \
      62              :         goto label; \
      63              :     } \
      64              : } while(0)
      65              : 
      66              : /** Open 80x24 PTY running harness in the given mode. */
      67           35 : static PtySession *open_harness(const char *mode) {
      68           35 :     PtySession *s = pty_open(80, 24);
      69           35 :     if (!s) return NULL;
      70           35 :     const char *argv[] = { TERMINAL_COVERAGE_HARNESS, mode, NULL };
      71           35 :     if (pty_run(s, argv) != 0) {
      72            0 :         pty_close(s);
      73            0 :         return NULL;
      74              :     }
      75           28 :     return s;
      76              : }
      77              : 
      78              : /* ── Test: terminal_cols / terminal_rows ─────────────────────────────── */
      79              : 
      80              : /**
      81              :  * @brief terminal_cols() / terminal_rows() return the PTY size (not fallback).
      82              :  *
      83              :  * The PTY is opened 80×24 so ioctl(TIOCGWINSZ) should return ws_col=80 and
      84              :  * ws_row=24. That covers the success-return branches on lines 30 and 37.
      85              :  */
      86            9 : static void test_cols_rows_on_pty(void) {
      87            9 :     PtySession *s = pty_open(80, 24);
      88            9 :     CHECK(s != NULL, "pty_open should succeed", done_no_session);
      89              : 
      90            9 :     const char *argv[] = { TERMINAL_COVERAGE_HARNESS, "cols_rows", NULL };
      91            9 :     CHECK(pty_run(s, argv) == 0, "pty_run should succeed", done);
      92              : 
      93              :     /* Should report real PTY dimensions — not the 80 / 0 fallback. */
      94            8 :     CHECK_WAIT_FOR(s, "COLS:", 3000, done);
      95            8 :     CHECK_WAIT_FOR(s, "ROWS:", 3000, done);
      96              : 
      97            8 :     int exit_code = pty_wait_exit(s, 3000);
      98            8 :     CHECK(exit_code == 0, "harness must exit 0 for cols_rows mode", done);
      99            8 : done:
     100            8 :     pty_close(s);
     101            8 : done_no_session:
     102            8 :     return;
     103              : }
     104              : 
     105              : /* ── Test: terminal_raw_enter / terminal_raw_exit ────────────────────── */
     106              : 
     107              : /**
     108              :  * @brief terminal_raw_enter() succeeds and terminal_raw_exit() cleans up.
     109              :  *
     110              :  * Covers lines 54-60 (raw_enter) and 65-68 (raw_exit).
     111              :  */
     112            8 : static void test_raw_enter_exit(void) {
     113            8 :     PtySession *s = open_harness("raw_enter_exit");
     114            7 :     CHECK(s != NULL, "open_harness(raw_enter_exit) should succeed",
     115              :           done_no_session);
     116              : 
     117            7 :     CHECK_WAIT_FOR(s, "RAW_ENTER:OK", 3000, done);
     118            7 :     CHECK_WAIT_FOR(s, "RAW_EXIT:OK",  3000, done);
     119              : 
     120            7 :     int exit_code = pty_wait_exit(s, 3000);
     121            7 :     CHECK(exit_code == 0, "harness must exit 0 for raw_enter_exit mode", done);
     122            7 : done:
     123            7 :     pty_close(s);
     124            7 : done_no_session:
     125            7 :     return;
     126              : }
     127              : 
     128              : /* ── Test: terminal_read_key — basic printable and control keys ──────── */
     129              : 
     130              : /**
     131              :  * @brief Exercise terminal_read_key paths.
     132              :  *
     133              :  * Sends a representative mix of keys through the PTY and asserts the harness
     134              :  * echoes the expected KEY:* lines.  This covers:
     135              :  *   - read_byte() and its caller (lines 83–96)
     136              :  *   - printable ASCII branch (lines 173–175)
     137              :  *   - \n/\r → TERM_KEY_ENTER (line 157)
     138              :  *   - Ctrl-A/E/K/W/D (lines 163–169)
     139              :  *   - Backspace (line 171)
     140              :  *   - terminal_last_printable() (line 91)
     141              :  *   - terminal_wait_key() (lines 187–195)
     142              :  */
     143            7 : static void test_read_key_basic(void) {
     144            7 :     PtySession *s = open_harness("read_key");
     145            6 :     CHECK(s != NULL, "open_harness(read_key) should succeed", done_no_session);
     146              : 
     147            6 :     CHECK_WAIT_FOR(s, "READY", 3000, done);
     148              : 
     149              :     /* Printable 'a' → KEY:CHAR:a */
     150            6 :     pty_send_str(s, "a");
     151            6 :     CHECK_WAIT_FOR(s, "KEY:CHAR:a", 3000, done);
     152              : 
     153              :     /* Enter (\r) → KEY:ENTER */
     154            6 :     pty_send_key(s, PTY_KEY_ENTER);
     155            6 :     CHECK_WAIT_FOR(s, "KEY:ENTER", 3000, done);
     156              : 
     157              :     /* Backspace → KEY:BACK */
     158            6 :     pty_send_key(s, PTY_KEY_BACK);
     159            6 :     CHECK_WAIT_FOR(s, "KEY:BACK", 3000, done);
     160              : 
     161              :     /* Ctrl-A (0x01) → KEY:CTRL_A */
     162            6 :     pty_send(s, "\x01", 1);
     163            6 :     CHECK_WAIT_FOR(s, "KEY:CTRL_A", 3000, done);
     164              : 
     165              :     /* Ctrl-E (0x05) → KEY:CTRL_E */
     166            6 :     pty_send(s, "\x05", 1);
     167            6 :     CHECK_WAIT_FOR(s, "KEY:CTRL_E", 3000, done);
     168              : 
     169              :     /* Ctrl-K (0x0b) → KEY:CTRL_K */
     170            6 :     pty_send(s, "\x0b", 1);
     171            6 :     CHECK_WAIT_FOR(s, "KEY:CTRL_K", 3000, done);
     172              : 
     173              :     /* Ctrl-W (0x17) → KEY:CTRL_W */
     174            6 :     pty_send(s, "\x17", 1);
     175            6 :     CHECK_WAIT_FOR(s, "KEY:CTRL_W", 3000, done);
     176              : 
     177              :     /* 'q' (printable) causes the harness to exit cleanly */
     178            6 :     pty_send_str(s, "q");
     179            6 :     CHECK_WAIT_FOR(s, "KEY:q", 3000, done);
     180              : 
     181            6 :     int exit_code = pty_wait_exit(s, 3000);
     182            6 :     CHECK(exit_code == 0, "harness must exit 0 after 'q'", done);
     183            6 : done:
     184            6 :     pty_close(s);
     185            6 : done_no_session:
     186            6 :     return;
     187              : }
     188              : 
     189              : /* ── Test: terminal_read_key — escape sequences ──────────────────────── */
     190              : 
     191              : /**
     192              :  * @brief Exercise ESC-sequence paths in terminal_read_key.
     193              :  *
     194              :  * Covers lines 99–156 (the ESC prefix, CSI sequences, SS3 sequences, bare ESC).
     195              :  */
     196            6 : static void test_read_key_escape_sequences(void) {
     197            6 :     PtySession *s = open_harness("read_key");
     198            5 :     CHECK(s != NULL, "open_harness(read_key) should succeed", done_no_session);
     199              : 
     200            5 :     CHECK_WAIT_FOR(s, "READY", 3000, done);
     201              : 
     202              :     /* Up arrow → ESC [ A → KEY:UP */
     203            5 :     pty_send_key(s, PTY_KEY_UP);
     204            5 :     CHECK_WAIT_FOR(s, "KEY:UP", 3000, done);
     205              : 
     206              :     /* Down arrow → ESC [ B → KEY:DOWN */
     207            5 :     pty_send_key(s, PTY_KEY_DOWN);
     208            5 :     CHECK_WAIT_FOR(s, "KEY:DOWN", 3000, done);
     209              : 
     210              :     /* Right arrow → ESC [ C → KEY:RIGHT */
     211            5 :     pty_send_key(s, PTY_KEY_RIGHT);
     212            5 :     CHECK_WAIT_FOR(s, "KEY:RIGHT", 3000, done);
     213              : 
     214              :     /* Left arrow → ESC [ D → KEY:LEFT */
     215            5 :     pty_send_key(s, PTY_KEY_LEFT);
     216            5 :     CHECK_WAIT_FOR(s, "KEY:LEFT", 3000, done);
     217              : 
     218              :     /* Home (ESC [ H) → KEY:HOME */
     219            5 :     pty_send(s, "\033[H", 3);
     220            5 :     CHECK_WAIT_FOR(s, "KEY:HOME", 3000, done);
     221              : 
     222              :     /* End (ESC [ F) → KEY:END */
     223            5 :     pty_send(s, "\033[F", 3);
     224            5 :     CHECK_WAIT_FOR(s, "KEY:END", 3000, done);
     225              : 
     226              :     /* PgUp (ESC [ 5 ~) → KEY:PGUP */
     227            5 :     pty_send_key(s, PTY_KEY_PGUP);
     228            5 :     CHECK_WAIT_FOR(s, "KEY:PGUP", 3000, done);
     229              : 
     230              :     /* PgDn (ESC [ 6 ~) → KEY:PGDN */
     231            5 :     pty_send_key(s, PTY_KEY_PGDN);
     232            5 :     CHECK_WAIT_FOR(s, "KEY:PGDN", 3000, done);
     233              : 
     234              :     /* Delete (ESC [ 3 ~) → KEY:DELETE */
     235            5 :     pty_send(s, "\033[3~", 4);
     236            5 :     CHECK_WAIT_FOR(s, "KEY:DELETE", 3000, done);
     237              : 
     238              :     /* Home via ESC [ 1 ~ → KEY:HOME */
     239            5 :     pty_send(s, "\033[1~", 4);
     240            5 :     CHECK_WAIT_FOR(s, "KEY:HOME", 3000, done);
     241              : 
     242              :     /* End via ESC [ 4 ~ → KEY:END */
     243            5 :     pty_send(s, "\033[4~", 4);
     244            5 :     CHECK_WAIT_FOR(s, "KEY:END", 3000, done);
     245              : 
     246              :     /* Home via ESC [ 7 ~ → KEY:HOME */
     247            5 :     pty_send(s, "\033[7~", 4);
     248            5 :     CHECK_WAIT_FOR(s, "KEY:HOME", 3000, done);
     249              : 
     250              :     /* End via ESC [ 8 ~ → KEY:END */
     251            5 :     pty_send(s, "\033[8~", 4);
     252            5 :     CHECK_WAIT_FOR(s, "KEY:END", 3000, done);
     253              : 
     254              :     /* Home via ESC O H (SS3 sequence) → KEY:HOME */
     255            5 :     pty_send(s, "\033OH", 3);
     256            5 :     CHECK_WAIT_FOR(s, "KEY:HOME", 3000, done);
     257              : 
     258              :     /* End via ESC O F (SS3 sequence) → KEY:END */
     259            5 :     pty_send(s, "\033OF", 3);
     260            5 :     CHECK_WAIT_FOR(s, "KEY:END", 3000, done);
     261              : 
     262              :     /* Bare ESC followed by timeout → KEY:ESC */
     263            5 :     pty_send(s, "\033", 1);
     264            5 :     CHECK_WAIT_FOR(s, "KEY:ESC", 3000, done);
     265              : 
     266              :     /* Unknown CSI sequence (ESC [ 2 0 m) → IGNORE (drains to letter) */
     267            5 :     pty_send(s, "\033[20m", 5);
     268            5 :     CHECK_WAIT_FOR(s, "KEY:IGNORE", 3000, done);
     269              : 
     270              :     /* Unknown SS3 (ESC O Z) → IGNORE */
     271            5 :     pty_send(s, "\033OZ", 3);
     272            5 :     CHECK_WAIT_FOR(s, "KEY:IGNORE", 3000, done);
     273              : 
     274              :     /* Ctrl-D → exits harness */
     275            5 :     pty_send_key(s, PTY_KEY_CTRL_D);
     276            5 :     CHECK_WAIT_FOR(s, "KEY:CTRL_D", 3000, done);
     277              : 
     278            5 :     int exit_code = pty_wait_exit(s, 3000);
     279            5 :     CHECK(exit_code == 0, "harness must exit 0 after Ctrl-D", done);
     280            5 : done:
     281            5 :     pty_close(s);
     282            5 : done_no_session:
     283            5 :     return;
     284              : }
     285              : 
     286              : /* ── Test: terminal_wait_key ─────────────────────────────────────────── */
     287              : 
     288              : /**
     289              :  * @brief terminal_wait_key() returns 1 when input is available.
     290              :  *
     291              :  * Covers lines 187-195.
     292              :  */
     293            5 : static void test_wait_key(void) {
     294            5 :     PtySession *s = open_harness("wait_key");
     295            4 :     CHECK(s != NULL, "open_harness(wait_key) should succeed", done_no_session);
     296              : 
     297            4 :     CHECK_WAIT_FOR(s, "READY", 3000, done);
     298              : 
     299              :     /* Send a complete line so wait_key's poll() sees POLLIN in canonical mode. */
     300            4 :     pty_send_str(s, "x");
     301            4 :     pty_send_key(s, PTY_KEY_ENTER);
     302            4 :     CHECK_WAIT_FOR(s, "WAIT_KEY:READY", 3000, done);
     303              : 
     304            4 :     int exit_code = pty_wait_exit(s, 3000);
     305            4 :     CHECK(exit_code == 0, "harness must exit 0 for wait_key mode", done);
     306            4 : done:
     307            4 :     pty_close(s);
     308            4 : done_no_session:
     309            4 :     return;
     310              : }
     311              : 
     312              : /* ── Test: terminal_install_cleanup_handlers ─────────────────────────── */
     313              : 
     314              : /**
     315              :  * @brief terminal_install_cleanup_handlers() installs signal handlers.
     316              :  *
     317              :  * Covers lines 240-253 (the function itself). The signal handler code
     318              :  * (lines 217-238) is reached by delivering SIGTERM to the harness.
     319              :  */
     320            4 : static void test_install_cleanup_handlers(void) {
     321            4 :     PtySession *s = open_harness("install_handlers");
     322            3 :     CHECK(s != NULL, "open_harness(install_handlers) should succeed",
     323              :           done_no_session);
     324              : 
     325            3 :     CHECK_WAIT_FOR(s, "HANDLERS:OK", 3000, done);
     326              : 
     327            3 :     int exit_code = pty_wait_exit(s, 3000);
     328            3 :     CHECK(exit_code == 0, "harness must exit 0 for install_handlers mode",
     329              :           done);
     330            3 : done:
     331            3 :     pty_close(s);
     332            3 : done_no_session:
     333            3 :     return;
     334              : }
     335              : 
     336              : /* ── Test: terminal_enable_resize_notifications / consume_resize ─────── */
     337              : 
     338              : /**
     339              :  * @brief Resize notifications are detected via SIGWINCH.
     340              :  *
     341              :  * Covers lines 261-285: terminal_enable_resize_notifications(),
     342              :  * the resize_handler signal handler, and terminal_consume_resize().
     343              :  */
     344            3 : static void test_resize_notifications(void) {
     345            3 :     PtySession *s = open_harness("resize_notify");
     346            2 :     CHECK(s != NULL, "open_harness(resize_notify) should succeed",
     347              :           done_no_session);
     348              : 
     349            2 :     CHECK_WAIT_FOR(s, "RESIZE_READY", 3000, done);
     350              : 
     351              :     /* Issue a PTY resize — ptytest sends TIOCSWINSZ + SIGWINCH. */
     352            2 :     int rc = pty_resize(s, 100, 30);
     353            2 :     CHECK(rc == 0, "pty_resize should succeed", done);
     354              : 
     355            2 :     CHECK_WAIT_FOR(s, "RESIZE_DETECTED", 5000, done);
     356              : 
     357            2 :     int exit_code = pty_wait_exit(s, 3000);
     358            2 :     CHECK(exit_code == 0, "harness must exit 0 after resize", done);
     359            2 : done:
     360            2 :     pty_close(s);
     361            2 : done_no_session:
     362            2 :     return;
     363              : }
     364              : 
     365              : /* ── Test: terminal_read_password non-TTY path ───────────────────────── */
     366              : 
     367              : /**
     368              :  * @brief terminal_read_password() reads from piped stdin (non-TTY path).
     369              :  *
     370              :  * Runs the harness via fork+exec with stdin replaced by a pipe.
     371              :  * Covers lines 328-346 (the else-branch in terminal_read_password).
     372              :  */
     373            2 : static void test_passwd_nontty(void) {
     374              :     int pipefd[2];
     375            2 :     g_tests_run++;
     376            2 :     if (pipe(pipefd) != 0) {
     377            0 :         printf("  [FAIL] %s:%d: pipe() failed\n", __FILE__, __LINE__);
     378            0 :         g_tests_failed++;
     379            0 :         return;
     380              :     }
     381              : 
     382            2 :     pid_t pid = fork();
     383            3 :     if (pid < 0) {
     384            0 :         printf("  [FAIL] %s:%d: fork() failed\n", __FILE__, __LINE__);
     385            0 :         g_tests_failed++;
     386            0 :         close(pipefd[0]);
     387            0 :         close(pipefd[1]);
     388            0 :         return;
     389              :     }
     390              : 
     391            3 :     if (pid == 0) {
     392              :         /* Child: replace stdin with read end of the pipe. */
     393            1 :         close(pipefd[1]);
     394            1 :         dup2(pipefd[0], STDIN_FILENO);
     395            1 :         close(pipefd[0]);
     396            1 :         const char *argv[] = {
     397              :             TERMINAL_COVERAGE_HARNESS, "passwd_nontty", NULL
     398              :         };
     399            1 :         execv(argv[0], (char *const *)argv);
     400            1 :         _exit(127);
     401              :     }
     402              : 
     403              :     /* Parent: write a password line then close the write end. */
     404            2 :     close(pipefd[0]);
     405            2 :     const char *pw = "piped_secret\n";
     406            2 :     ssize_t written = write(pipefd[1], pw, strlen(pw));
     407            2 :     close(pipefd[1]);
     408              :     (void)written;
     409              : 
     410              :     /* Collect the child's stdout by reading from its pipe-backed stdout.
     411              :      * Since we can't easily capture stdout here, we just check exit status
     412              :      * and trust GCOV records the lines.  An exit 0 means no error. */
     413            2 :     int status = 0;
     414            2 :     waitpid(pid, &status, 0);
     415            2 :     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
     416            0 :         printf("  [FAIL] %s:%d: passwd_nontty harness exited with %d\n",
     417            0 :                __FILE__, __LINE__, WEXITSTATUS(status));
     418            0 :         g_tests_failed++;
     419              :     }
     420              : }
     421              : 
     422              : /**
     423              :  * @brief Non-TTY path with EOF immediately → returns -1.
     424              :  *
     425              :  * Covers the getline-returns-(-1) branch inside the else block.
     426              :  */
     427            2 : static void test_passwd_nontty_eof(void) {
     428              :     int pipefd[2];
     429            2 :     g_tests_run++;
     430            2 :     if (pipe(pipefd) != 0) {
     431            0 :         printf("  [FAIL] %s:%d: pipe() failed\n", __FILE__, __LINE__);
     432            0 :         g_tests_failed++;
     433            0 :         return;
     434              :     }
     435              : 
     436            2 :     pid_t pid = fork();
     437            3 :     if (pid < 0) {
     438            0 :         printf("  [FAIL] %s:%d: fork() failed\n", __FILE__, __LINE__);
     439            0 :         g_tests_failed++;
     440            0 :         close(pipefd[0]);
     441            0 :         close(pipefd[1]);
     442            0 :         return;
     443              :     }
     444              : 
     445            3 :     if (pid == 0) {
     446            1 :         close(pipefd[1]);
     447            1 :         dup2(pipefd[0], STDIN_FILENO);
     448            1 :         close(pipefd[0]);
     449            1 :         const char *argv[] = {
     450              :             TERMINAL_COVERAGE_HARNESS, "passwd_nontty", NULL
     451              :         };
     452            1 :         execv(argv[0], (char *const *)argv);
     453            1 :         _exit(127);
     454              :     }
     455              : 
     456              :     /* Close write end immediately → child sees EOF → harness exits 1. */
     457            2 :     close(pipefd[0]);
     458            2 :     close(pipefd[1]);
     459              : 
     460            2 :     int status = 0;
     461            2 :     waitpid(pid, &status, 0);
     462              :     /* Exit 1 means terminal_read_password returned -1 as expected. */
     463            2 :     if (!WIFEXITED(status) || WEXITSTATUS(status) != 1) {
     464            0 :         printf("  [FAIL] %s:%d: expected exit 1 on EOF, got %d\n",
     465            0 :                __FILE__, __LINE__, WEXITSTATUS(status));
     466            0 :         g_tests_failed++;
     467              :     }
     468              : }
     469              : 
     470              : /* ── Test: Ctrl-C triggers signal handler path ───────────────────────── */
     471              : 
     472              : /**
     473              :  * @brief SIGTERM exercices the cleanup_signal_handler.
     474              :  *
     475              :  * Opens install_handlers mode, waits for it to install the handlers,
     476              :  * then sends SIGTERM via pty_close (which will SIGTERM the child). This
     477              :  * exercises the cleanup_signal_handler code (lines 217-238).
     478              :  * We use a separate PTY session so the signal is sent to the child
     479              :  * while it is in raw mode.
     480              :  */
     481            2 : static void test_signal_handler_path(void) {
     482            2 :     PtySession *s = open_harness("read_key");
     483            1 :     CHECK(s != NULL, "open_harness(read_key) should succeed", done_no_session);
     484              : 
     485            1 :     CHECK_WAIT_FOR(s, "READY", 3000, done);
     486              : 
     487              :     /* Send Ctrl-C (0x03). In raw mode (ISIG cleared) this arrives as
     488              :      * TERM_KEY_QUIT rather than raising SIGINT. The harness will print
     489              :      * KEY:QUIT and exit cleanly. This exercises the Ctrl-C branch (line 159). */
     490            1 :     pty_send_key(s, PTY_KEY_CTRL_C);
     491            1 :     CHECK_WAIT_FOR(s, "KEY:QUIT", 3000, done);
     492              : 
     493            1 :     int exit_code = pty_wait_exit(s, 3000);
     494            1 :     CHECK(exit_code == 0, "harness must exit 0 after QUIT key", done);
     495            1 : done:
     496            1 :     pty_close(s);
     497            1 : done_no_session:
     498            1 :     return;
     499              : }
     500              : 
     501              : /* ── Entry point ─────────────────────────────────────────────────────── */
     502              : 
     503            9 : int main(void) {
     504            9 :     printf("Terminal coverage PTY tests (%s)\n", TERMINAL_COVERAGE_HARNESS);
     505              : 
     506            9 :     RUN_TEST(test_cols_rows_on_pty);
     507            8 :     RUN_TEST(test_raw_enter_exit);
     508            7 :     RUN_TEST(test_read_key_basic);
     509            6 :     RUN_TEST(test_read_key_escape_sequences);
     510            5 :     RUN_TEST(test_wait_key);
     511            4 :     RUN_TEST(test_install_cleanup_handlers);
     512            3 :     RUN_TEST(test_resize_notifications);
     513            2 :     RUN_TEST(test_passwd_nontty);
     514            2 :     RUN_TEST(test_passwd_nontty_eof);
     515            2 :     RUN_TEST(test_signal_handler_path);
     516              : 
     517            1 :     printf("\n%d tests run, %d failed\n", g_tests_run, g_tests_failed);
     518            1 :     return g_tests_failed > 0 ? 1 : 0;
     519              : }
        

Generated by: LCOV version 2.0-1