Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file terminal_coverage_harness.c
6 : * @brief Harness binary that exercises terminal.c paths for coverage.
7 : *
8 : * Each mode exercises a specific area of src/platform/posix/terminal.c.
9 : * The test runner (test_terminal_coverage.c) drives this binary through
10 : * a PTY or via piped stdin to reach all uncovered lines.
11 : *
12 : * Modes:
13 : * cols_rows - call terminal_cols() / terminal_rows() and print results
14 : * raw_enter_exit - enter/exit raw mode, print result
15 : * read_key - raw mode: read one key, print name, exit
16 : * wait_key - call terminal_wait_key(500), print READY/TIMEOUT
17 : * install_handlers - enter raw mode, install cleanup handlers, then exit 0
18 : * resize_notify - enable resize notifications, loop until resize or 'q'
19 : * passwd_nontty - call terminal_read_password when stdin is a pipe (non-TTY)
20 : */
21 :
22 : #include "platform/terminal.h"
23 :
24 : #include <stdio.h>
25 : #include <string.h>
26 : #include <stdlib.h>
27 : #include <unistd.h>
28 : #include <signal.h>
29 :
30 : /* ---- cols_rows --------------------------------------------------------- */
31 :
32 1 : static int mode_cols_rows(void) {
33 1 : int c = terminal_cols();
34 1 : int r = terminal_rows();
35 : /* On a real PTY both should be > 0; on non-TTY fallback values apply. */
36 1 : printf("COLS:%d\n", c);
37 1 : printf("ROWS:%d\n", r);
38 1 : fflush(stdout);
39 1 : return 0;
40 : }
41 :
42 : /* ---- raw_enter_exit ---------------------------------------------------- */
43 :
44 1 : static int mode_raw_enter_exit(void) {
45 1 : TermRawState *st = terminal_raw_enter();
46 1 : if (!st) {
47 0 : printf("RAW_ENTER:FAIL\n");
48 0 : fflush(stdout);
49 0 : return 1;
50 : }
51 1 : printf("RAW_ENTER:OK\n");
52 1 : fflush(stdout);
53 1 : terminal_raw_exit(&st);
54 1 : if (st != NULL) {
55 0 : printf("RAW_EXIT:FAIL\n");
56 0 : fflush(stdout);
57 0 : return 1;
58 : }
59 1 : printf("RAW_EXIT:OK\n");
60 1 : fflush(stdout);
61 1 : return 0;
62 : }
63 :
64 : /* ---- read_key ---------------------------------------------------------- */
65 :
66 : /**
67 : * Enter raw mode, print "READY", then read keys until 'q' or a count limit.
68 : * For each key print its name so the test can assert receipt.
69 : */
70 3 : static int mode_read_key(void) {
71 3 : TermRawState *st = terminal_raw_enter();
72 3 : if (!st) {
73 0 : printf("RAW:FAIL\n");
74 0 : fflush(stdout);
75 0 : return 1;
76 : }
77 3 : printf("READY\n");
78 3 : fflush(stdout);
79 :
80 : /* Read up to 32 keystrokes, exit on 'q' (printable) or TERM_KEY_QUIT */
81 28 : for (int i = 0; i < 32; i++) {
82 : /* Use terminal_wait_key so that path is also hit. */
83 28 : int avail = terminal_wait_key(3000);
84 28 : if (avail <= 0) {
85 0 : printf("TIMEOUT\n");
86 0 : fflush(stdout);
87 0 : break;
88 : }
89 28 : TermKey k = terminal_read_key();
90 28 : int lp = terminal_last_printable();
91 28 : switch (k) {
92 1 : case TERM_KEY_QUIT: printf("KEY:QUIT\n"); fflush(stdout); goto done;
93 1 : case TERM_KEY_ENTER: printf("KEY:ENTER\n"); fflush(stdout); break;
94 1 : case TERM_KEY_ESC: printf("KEY:ESC\n"); fflush(stdout); break;
95 1 : case TERM_KEY_BACK: printf("KEY:BACK\n"); fflush(stdout); break;
96 1 : case TERM_KEY_LEFT: printf("KEY:LEFT\n"); fflush(stdout); break;
97 1 : case TERM_KEY_RIGHT: printf("KEY:RIGHT\n"); fflush(stdout); break;
98 1 : case TERM_KEY_PREV_LINE: printf("KEY:UP\n"); fflush(stdout); break;
99 1 : case TERM_KEY_NEXT_LINE: printf("KEY:DOWN\n"); fflush(stdout); break;
100 4 : case TERM_KEY_HOME: printf("KEY:HOME\n"); fflush(stdout); break;
101 4 : case TERM_KEY_END: printf("KEY:END\n"); fflush(stdout); break;
102 1 : case TERM_KEY_DELETE: printf("KEY:DELETE\n"); fflush(stdout); break;
103 1 : case TERM_KEY_PREV_PAGE: printf("KEY:PGUP\n"); fflush(stdout); break;
104 1 : case TERM_KEY_NEXT_PAGE: printf("KEY:PGDN\n"); fflush(stdout); break;
105 1 : case TERM_KEY_CTRL_A: printf("KEY:CTRL_A\n"); fflush(stdout); break;
106 1 : case TERM_KEY_CTRL_E: printf("KEY:CTRL_E\n"); fflush(stdout); break;
107 1 : case TERM_KEY_CTRL_K: printf("KEY:CTRL_K\n"); fflush(stdout); break;
108 1 : case TERM_KEY_CTRL_W: printf("KEY:CTRL_W\n"); fflush(stdout); break;
109 1 : case TERM_KEY_CTRL_D: printf("KEY:CTRL_D\n"); fflush(stdout); goto done;
110 4 : case TERM_KEY_IGNORE:
111 4 : if (lp) {
112 2 : if (lp == 'q') {
113 1 : printf("KEY:q\n");
114 1 : fflush(stdout);
115 1 : goto done;
116 : }
117 1 : printf("KEY:CHAR:%c\n", (char)lp);
118 1 : fflush(stdout);
119 : } else {
120 2 : printf("KEY:IGNORE\n");
121 2 : fflush(stdout);
122 : }
123 3 : break;
124 : }
125 : }
126 0 : done:
127 3 : terminal_raw_exit(&st);
128 3 : return 0;
129 : }
130 :
131 : /* ---- wait_key ---------------------------------------------------------- */
132 :
133 1 : static int mode_wait_key(void) {
134 : /* Signal readiness so the test can send a byte after seeing READY. */
135 1 : printf("READY\n");
136 1 : fflush(stdout);
137 1 : int rc = terminal_wait_key(3000);
138 1 : if (rc > 0)
139 1 : printf("WAIT_KEY:READY\n");
140 0 : else if (rc == 0)
141 0 : printf("WAIT_KEY:TIMEOUT\n");
142 : else
143 0 : printf("WAIT_KEY:INTR\n");
144 1 : fflush(stdout);
145 1 : return 0;
146 : }
147 :
148 : /* ---- install_handlers -------------------------------------------------- */
149 :
150 1 : static int mode_install_handlers(void) {
151 1 : TermRawState *st = terminal_raw_enter();
152 1 : if (!st) {
153 0 : printf("HANDLERS:FAIL\n");
154 0 : fflush(stdout);
155 0 : return 1;
156 : }
157 : /* Install the handlers — this exercises terminal_install_cleanup_handlers
158 : * and exposes g_saved_termios. */
159 1 : terminal_install_cleanup_handlers(st);
160 1 : printf("HANDLERS:OK\n");
161 1 : fflush(stdout);
162 : /* Clean exit: restore terminal before exiting so the PTY isn't stuck. */
163 1 : terminal_raw_exit(&st);
164 1 : return 0;
165 : }
166 :
167 : /* ---- resize_notify ----------------------------------------------------- */
168 :
169 1 : static int mode_resize_notify(void) {
170 1 : terminal_enable_resize_notifications();
171 : /* Call a second time to cover the "already installed" guard. */
172 1 : terminal_enable_resize_notifications();
173 :
174 1 : printf("RESIZE_READY\n");
175 1 : fflush(stdout);
176 :
177 : /* Poll for a resize event or 'q' for up to 3 s. */
178 1 : TermRawState *st = terminal_raw_enter();
179 1 : if (!st) {
180 0 : printf("RAW:FAIL\n");
181 0 : fflush(stdout);
182 0 : return 1;
183 : }
184 2 : for (int i = 0; i < 30; i++) {
185 2 : if (terminal_consume_resize()) {
186 1 : printf("RESIZE_DETECTED\n");
187 1 : fflush(stdout);
188 1 : break;
189 : }
190 1 : int avail = terminal_wait_key(100);
191 1 : if (avail > 0) {
192 0 : TermKey k = terminal_read_key();
193 0 : int lp = terminal_last_printable();
194 0 : if (k == TERM_KEY_QUIT || (k == TERM_KEY_IGNORE && lp == 'q')) {
195 0 : printf("QUIT\n");
196 0 : fflush(stdout);
197 0 : break;
198 : }
199 : }
200 : }
201 1 : terminal_raw_exit(&st);
202 1 : return 0;
203 : }
204 :
205 : /* ---- passwd_nontty ----------------------------------------------------- */
206 :
207 : /**
208 : * Call terminal_read_password when stdin is NOT a TTY (piped).
209 : * This exercises the else-branch in terminal_read_password (lines 328-346).
210 : */
211 2 : static int mode_passwd_nontty(void) {
212 : char buf[64];
213 2 : int rc = terminal_read_password("TestPrompt", buf, sizeof(buf));
214 2 : if (rc < 0) {
215 1 : printf("PASSWD:ERROR\n");
216 1 : fflush(stdout);
217 1 : return 1;
218 : }
219 1 : printf("PASSWD:%s\n", buf);
220 1 : printf("PASSWD_LEN:%d\n", rc);
221 1 : fflush(stdout);
222 1 : return 0;
223 : }
224 :
225 : /* ---- main -------------------------------------------------------------- */
226 :
227 10 : int main(int argc, char **argv) {
228 10 : if (argc < 2) {
229 0 : fprintf(stderr, "usage: terminal_coverage_harness <mode>\n");
230 0 : return 2;
231 : }
232 :
233 10 : if (strcmp(argv[1], "cols_rows") == 0) return mode_cols_rows();
234 9 : if (strcmp(argv[1], "raw_enter_exit") == 0) return mode_raw_enter_exit();
235 8 : if (strcmp(argv[1], "read_key") == 0) return mode_read_key();
236 5 : if (strcmp(argv[1], "wait_key") == 0) return mode_wait_key();
237 4 : if (strcmp(argv[1], "install_handlers") == 0) return mode_install_handlers();
238 3 : if (strcmp(argv[1], "resize_notify") == 0) return mode_resize_notify();
239 2 : if (strcmp(argv[1], "passwd_nontty") == 0) return mode_passwd_nontty();
240 :
241 0 : fprintf(stderr, "unknown mode: %s\n", argv[1]);
242 0 : return 2;
243 : }
|