Skip to content

Commit f9b340a

Browse files
committed
Add a test for libtty
1 parent 8e4eee8 commit f9b340a

File tree

4 files changed

+273
-2
lines changed

4 files changed

+273
-2
lines changed

src/lib/libtty.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ addToLibrary({
5454
},
5555
close(stream) {
5656
// flush any pending line data
57-
stream.tty.ops.fsync(stream.tty);
57+
stream.tty.ops.fsync?.(stream.tty);
5858
},
5959
fsync(stream) {
60-
stream.tty.ops.fsync(stream.tty);
60+
stream.tty.ops.fsync?.(stream.tty);
6161
},
6262
read(stream, buffer, offset, length, pos /* ignored */) {
6363
if (!stream.tty || !stream.tty.ops.get_char) {

test/other/tty.c

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#include <emscripten.h>
2+
#include <errno.h>
3+
#include <fcntl.h>
4+
#include <stdio.h>
5+
#include <string.h>
6+
#include <sys/ioctl.h>
7+
#include <termios.h>
8+
#include <unistd.h>
9+
10+
EM_JS_DEPS(main, "$TTY");
11+
12+
// clang-format off
13+
EM_JS(void, init, (void), {
14+
var major = 100;
15+
var tty_ops = {
16+
get_char: function (tty) {
17+
if (tty.input.length > 0) {
18+
return tty.input.shift();
19+
}
20+
return undefined;
21+
},
22+
put_char: function (tty, val) {
23+
if (val !== 0 && val !== 10) {
24+
tty.output.push(val);
25+
}
26+
},
27+
fsync: function (tty) {
28+
console.log('fsync called');
29+
tty.output = [];
30+
},
31+
ioctl_tcgets: function (tty) {
32+
return {
33+
c_iflag: 0,
34+
c_oflag: 0,
35+
c_cflag: 0,
36+
c_lflag: 0,
37+
c_cc: new Array(32).fill(0),
38+
};
39+
},
40+
ioctl_tcsets: function (tty, optional_actions, data) {
41+
return 0;
42+
},
43+
ioctl_tiocgwinsz: function (tty) {
44+
return [25, 80];
45+
},
46+
};
47+
var device = FS.makedev(major, 0);
48+
TTY.register(device, tty_ops);
49+
FS.mkdev('/custom_tty', device);
50+
// Populate the TTY input buffer with test data "ABCD"
51+
TTY.ttys[device].input = [65, 66, 67, 68];
52+
53+
// TTY without get_char - should cause ENXIO on read
54+
var tty_no_getchar = {put_char: function (tty, val) {}};
55+
var device_no_getchar = FS.makedev(major + 1, 0);
56+
TTY.register(device_no_getchar, tty_no_getchar);
57+
FS.mkdev('/tty_no_getchar', device_no_getchar);
58+
59+
// TTY without put_char - should cause ENXIO on write
60+
var tty_no_putchar = {
61+
get_char: function (tty) {
62+
return 0;
63+
},
64+
};
65+
var device_no_putchar = FS.makedev(major + 2, 0);
66+
TTY.register(device_no_putchar, tty_no_putchar);
67+
FS.mkdev('/tty_no_putchar', device_no_putchar);
68+
69+
// TTY with throwing get_char - should cause EIO on read
70+
var tty_throw_getchar = {
71+
get_char: function (tty) {
72+
throw new Error('get_char error');
73+
},
74+
put_char: function (tty, val) {},
75+
};
76+
var device_throw_getchar = FS.makedev(major + 3, 0);
77+
TTY.register(device_throw_getchar, tty_throw_getchar);
78+
FS.mkdev('/tty_throw_getchar', device_throw_getchar);
79+
80+
// TTY with throwing put_char - should cause EIO on write
81+
var tty_throw_putchar = {
82+
get_char: function (tty) {
83+
return 0;
84+
},
85+
put_char: function (tty, val) {
86+
throw new Error('put_char error');
87+
},
88+
};
89+
var device_throw_putchar = FS.makedev(major + 4, 0);
90+
TTY.register(device_throw_putchar, tty_throw_putchar);
91+
FS.mkdev('/tty_throw_putchar', device_throw_putchar);
92+
93+
// TTY with empty input (returns undefined immediately) - should cause EAGAIN on
94+
// read
95+
var tty_empty = {
96+
get_char: function (tty) {
97+
return undefined;
98+
},
99+
put_char: function (tty, val) {},
100+
};
101+
var device_empty = FS.makedev(major + 5, 0);
102+
TTY.register(device_empty, tty_empty);
103+
FS.mkdev('/tty_empty', device_empty);
104+
});
105+
// clang-format on
106+
107+
int main() {
108+
init();
109+
char readBuffer[256] = {0};
110+
char writeBuffer[] = "Test";
111+
struct winsize ws;
112+
struct termios term;
113+
114+
printf("\nTest 1: open custom TTY device and check isatty\n");
115+
int fd = open("/custom_tty", O_RDWR);
116+
printf("open custom_tty: %d\n", fd >= 0 ? 1 : 0);
117+
printf("isatty: %d\n", isatty(fd));
118+
printf("errno after open: %s\n", strerror(errno));
119+
errno = 0;
120+
121+
printf("\nTest 2: read from TTY with data\n");
122+
ssize_t bytesRead = read(fd, readBuffer, sizeof(readBuffer));
123+
printf("read bytes: %zd\n", bytesRead);
124+
printf("read data: %s\n", bytesRead > 0 ? readBuffer : "(none)");
125+
printf("errno after read: %s\n", strerror(errno));
126+
errno = 0;
127+
128+
printf("\nTest 3: write to TTY\n");
129+
ssize_t bytesWritten = write(fd, writeBuffer, strlen(writeBuffer));
130+
printf("write bytes: %zd\n", bytesWritten);
131+
printf("errno after write: %s\n", strerror(errno));
132+
errno = 0;
133+
134+
printf("\nTest 4: ioctl TIOCGWINSZ\n");
135+
int result = ioctl(fd, TIOCGWINSZ, &ws);
136+
printf("ioctl TIOCGWINSZ: %d\n", result);
137+
printf("ws_row: %d ws_col: %d\n", ws.ws_row, ws.ws_col);
138+
printf("errno after ioctl: %s\n", strerror(errno));
139+
errno = 0;
140+
141+
printf("\nTest 5: ioctl TCGETS\n");
142+
result = ioctl(fd, TCGETS, &term);
143+
printf("ioctl TCGETS: %d\n", result);
144+
printf("errno after TCGETS: %s\n", strerror(errno));
145+
errno = 0;
146+
147+
printf("\nTest 6: ioctl TCSETS\n");
148+
result = ioctl(fd, TCSETS, &term);
149+
printf("ioctl TCSETS: %d\n", result);
150+
printf("errno after TCSETS: %s\n", strerror(errno));
151+
errno = 0;
152+
153+
printf("\nTest 7: fsync\n");
154+
result = fsync(fd);
155+
printf("fsync: %d\n", result);
156+
printf("errno after fsync: %s\n", strerror(errno));
157+
errno = 0;
158+
159+
close(fd);
160+
161+
printf("\nTest 8: no put_char\n");
162+
fd = open("/tty_no_putchar", O_WRONLY);
163+
printf("open: %d\n", fd >= 0 ? 1 : 0);
164+
bytesWritten = write(fd, writeBuffer, strlen(writeBuffer));
165+
printf("write: %zd\n", bytesWritten);
166+
printf("errno: %s\n", strerror(errno));
167+
errno = 0;
168+
close(fd);
169+
170+
printf("\nTest 9: no get_char\n");
171+
fd = open("/tty_no_getchar", O_RDONLY);
172+
printf("open: %d\n", fd >= 0 ? 1 : 0);
173+
bytesRead = read(fd, readBuffer, sizeof(readBuffer));
174+
printf("read: %zd\n", bytesRead);
175+
printf("errno: %s\n", strerror(errno));
176+
errno = 0;
177+
close(fd);
178+
179+
printf("\nTest 10: put_char throws\n");
180+
fd = open("/tty_throw_putchar", O_WRONLY);
181+
printf("open: %d\n", fd >= 0 ? 1 : 0);
182+
bytesWritten = write(fd, writeBuffer, strlen(writeBuffer));
183+
printf("write: %zd\n", bytesWritten);
184+
printf("errno: %s\n", strerror(errno));
185+
errno = 0;
186+
close(fd);
187+
188+
printf("\nTest 11: get_char throws\n");
189+
fd = open("/tty_throw_getchar", O_RDONLY);
190+
printf("open: %d\n", fd >= 0 ? 1 : 0);
191+
bytesRead = read(fd, readBuffer, sizeof(readBuffer));
192+
printf("read: %zd\n", bytesRead);
193+
printf("errno: %s\n", strerror(errno));
194+
errno = 0;
195+
close(fd);
196+
197+
printf("\nTest 12: get_char returns undefined\n");
198+
fd = open("/tty_empty", O_RDONLY);
199+
printf("open: %d\n", fd >= 0 ? 1 : 0);
200+
bytesRead = read(fd, readBuffer, sizeof(readBuffer));
201+
printf("read: %zd\n", bytesRead);
202+
printf("errno: %s\n", strerror(errno));
203+
errno = 0;
204+
close(fd);
205+
206+
printf("\ndone\n");
207+
return 0;
208+
}

test/other/tty.out

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
Test 1: open custom TTY device and check isatty
3+
open custom_tty: 1
4+
isatty: 1
5+
errno after open: Success
6+
7+
Test 2: read from TTY with data
8+
read bytes: 4
9+
read data: ABCD
10+
errno after read: Success
11+
12+
Test 3: write to TTY
13+
write bytes: 4
14+
errno after write: Success
15+
16+
Test 4: ioctl TIOCGWINSZ
17+
ioctl TIOCGWINSZ: 0
18+
ws_row: 25 ws_col: 80
19+
errno after ioctl: Success
20+
21+
Test 5: ioctl TCGETS
22+
ioctl TCGETS: 0
23+
errno after TCGETS: Success
24+
25+
Test 6: ioctl TCSETS
26+
ioctl TCSETS: 0
27+
errno after TCSETS: Success
28+
29+
Test 7: fsync
30+
fsync called
31+
fsync: 0
32+
errno after fsync: Success
33+
fsync called
34+
35+
Test 8: no put_char
36+
open: 1
37+
write: -1
38+
errno: No such device or address
39+
40+
Test 9: no get_char
41+
open: 1
42+
read: -1
43+
errno: No such device or address
44+
45+
Test 10: put_char throws
46+
open: 1
47+
write: -1
48+
errno: I/O error
49+
50+
Test 11: get_char throws
51+
open: 1
52+
read: -1
53+
errno: I/O error
54+
55+
Test 12: get_char returns undefined
56+
open: 1
57+
read: -1
58+
errno: Resource temporarily unavailable
59+
60+
done

test/test_other.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13175,6 +13175,9 @@ def test_unistd_isatty(self):
1317513175
self.skipTest('depends on /dev filesystem')
1317613176
self.do_runf('unistd/isatty.c', 'success')
1317713177

13178+
def test_libtty(self):
13179+
self.do_other_test('tty.c')
13180+
1317813181
def test_unistd_login(self):
1317913182
self.do_run_in_out_file_test('unistd/login.c')
1318013183

0 commit comments

Comments
 (0)