1import * as std from "std";
2import * as os from "os";
3
4function assert(actual, expected, message) {
5    if (arguments.length == 1)
6        expected = true;
7
8    if (actual === expected)
9        return;
10
11    if (actual !== null && expected !== null
12    &&  typeof actual == 'object' && typeof expected == 'object'
13    &&  actual.toString() === expected.toString())
14        return;
15
16    throw Error("assertion failed: got |" + actual + "|" +
17                ", expected |" + expected + "|" +
18                (message ? " (" + message + ")" : ""));
19}
20
21// load more elaborate version of assert if available
22try { std.loadScript("test_assert.js"); } catch(e) {}
23
24/*----------------*/
25
26function test_printf()
27{
28    assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc");
29    assert(std.sprintf("%010d", 123), "0000000123");
30    assert(std.sprintf("%x", -2), "fffffffe");
31    assert(std.sprintf("%lx", -2), "fffffffffffffffe");
32    assert(std.sprintf("%10.1f", 2.1), "       2.1");
33    assert(std.sprintf("%*.*f", 10, 2, -2.13), "     -2.13");
34    assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff");
35}
36
37function test_file1()
38{
39    var f, len, str, size, buf, ret, i, str1;
40
41    f = std.tmpfile();
42    str = "hello world\n";
43    f.puts(str);
44
45    f.seek(0, std.SEEK_SET);
46    str1 = f.readAsString();
47    assert(str1 === str);
48
49    f.seek(0, std.SEEK_END);
50    size = f.tell();
51    assert(size === str.length);
52
53    f.seek(0, std.SEEK_SET);
54
55    buf = new Uint8Array(size);
56    ret = f.read(buf.buffer, 0, size);
57    assert(ret === size);
58    for(i = 0; i < size; i++)
59        assert(buf[i] === str.charCodeAt(i));
60
61    f.close();
62}
63
64function test_file2()
65{
66    var f, str, i, size;
67    f = std.tmpfile();
68    str = "hello world\n";
69    size = str.length;
70    for(i = 0; i < size; i++)
71        f.putByte(str.charCodeAt(i));
72    f.seek(0, std.SEEK_SET);
73    for(i = 0; i < size; i++) {
74        assert(str.charCodeAt(i) === f.getByte());
75    }
76    assert(f.getByte() === -1);
77    f.close();
78}
79
80function test_getline()
81{
82    var f, line, line_count, lines, i;
83
84    lines = ["hello world", "line 1", "line 2" ];
85    f = std.tmpfile();
86    for(i = 0; i < lines.length; i++) {
87        f.puts(lines[i], "\n");
88    }
89
90    f.seek(0, std.SEEK_SET);
91    assert(!f.eof());
92    line_count = 0;
93    for(;;) {
94        line = f.getline();
95        if (line === null)
96            break;
97        assert(line == lines[line_count]);
98        line_count++;
99    }
100    assert(f.eof());
101    assert(line_count === lines.length);
102
103    f.close();
104}
105
106function test_popen()
107{
108    var str, f, fname = "tmp_file.txt";
109    var content = "hello world";
110
111    f = std.open(fname, "w");
112    f.puts(content);
113    f.close();
114
115    /* test loadFile */
116    assert(std.loadFile(fname), content);
117
118    /* execute the 'cat' shell command */
119    f = std.popen("cat " + fname, "r");
120    str = f.readAsString();
121    f.close();
122
123    assert(str, content);
124
125    os.remove(fname);
126}
127
128function test_ext_json()
129{
130    var expected, input, obj;
131    expected = '{"x":false,"y":true,"z2":null,"a":[1,8,160],"s":"str"}';
132    input = `{ "x":false, /*comments are allowed */
133               "y":true,  // also a comment
134               z2:null, // unquoted property names
135               "a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal
136               "s":"str",} // trailing comma in objects and arrays
137            `;
138    obj = std.parseExtJSON(input);
139    assert(JSON.stringify(obj), expected);
140}
141
142function test_os()
143{
144    var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path;
145
146    assert(os.isatty(0));
147
148    fdir = "test_tmp_dir";
149    fname = "tmp_file.txt";
150    fpath = fdir + "/" + fname;
151    link_path = fdir + "/test_link";
152
153    os.remove(link_path);
154    os.remove(fpath);
155    os.remove(fdir);
156
157    err = os.mkdir(fdir, 0o755);
158    assert(err === 0);
159
160    fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC);
161    assert(fd >= 0);
162
163    buf = new Uint8Array(10);
164    for(i = 0; i < buf.length; i++)
165        buf[i] = i;
166    assert(os.write(fd, buf.buffer, 0, buf.length) === buf.length);
167
168    assert(os.seek(fd, 0, std.SEEK_SET) === 0);
169    buf2 = new Uint8Array(buf.length);
170    assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length);
171
172    for(i = 0; i < buf.length; i++)
173        assert(buf[i] == buf2[i]);
174
175    if (typeof BigInt !== "undefined") {
176        assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6));
177        assert(os.read(fd, buf2.buffer, 0, 1) === 1);
178        assert(buf[6] == buf2[0]);
179    }
180
181    assert(os.close(fd) === 0);
182
183    [files, err] = os.readdir(fdir);
184    assert(err, 0);
185    assert(files.indexOf(fname) >= 0);
186
187    fdate = 10000;
188
189    err = os.utimes(fpath, fdate, fdate);
190    assert(err, 0);
191
192    [st, err] = os.stat(fpath);
193    assert(err, 0);
194    assert(st.mode & os.S_IFMT, os.S_IFREG);
195    assert(st.mtime, fdate);
196
197    err = os.symlink(fname, link_path);
198    assert(err === 0);
199
200    [st, err] = os.lstat(link_path);
201    assert(err, 0);
202    assert(st.mode & os.S_IFMT, os.S_IFLNK);
203
204    [buf, err] = os.readlink(link_path);
205    assert(err, 0);
206    assert(buf, fname);
207
208    assert(os.remove(link_path) === 0);
209
210    [buf, err] = os.getcwd();
211    assert(err, 0);
212
213    [buf2, err] = os.realpath(".");
214    assert(err, 0);
215
216    assert(buf, buf2);
217
218    assert(os.remove(fpath) === 0);
219
220    fd = os.open(fpath, os.O_RDONLY);
221    assert(fd < 0);
222
223    assert(os.remove(fdir) === 0);
224}
225
226function test_os_exec()
227{
228    var ret, fds, pid, f, status;
229
230    ret = os.exec(["true"]);
231    assert(ret, 0);
232
233    ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false });
234    assert(ret, 1);
235
236    fds = os.pipe();
237    pid = os.exec(["sh", "-c", "echo $FOO"], {
238        stdout: fds[1],
239        block: false,
240        env: { FOO: "hello" },
241    } );
242    assert(pid >= 0);
243    os.close(fds[1]); /* close the write end (as it is only in the child)  */
244    f = std.fdopen(fds[0], "r");
245    assert(f.getline(), "hello");
246    assert(f.getline(), null);
247    f.close();
248    [ret, status] = os.waitpid(pid, 0);
249    assert(ret, pid);
250    assert(status & 0x7f, 0); /* exited */
251    assert(status >> 8, 0); /* exit code */
252
253    pid = os.exec(["cat"], { block: false } );
254    assert(pid >= 0);
255    os.kill(pid, os.SIGQUIT);
256    [ret, status] = os.waitpid(pid, 0);
257    assert(ret, pid);
258    assert(status & 0x7f, os.SIGQUIT);
259}
260
261function test_timer()
262{
263    var th, i;
264
265    /* just test that a timer can be inserted and removed */
266    th = [];
267    for(i = 0; i < 3; i++)
268        th[i] = os.setTimeout(function () { }, 1000);
269    for(i = 0; i < 3; i++)
270        os.clearTimeout(th[i]);
271}
272
273test_printf();
274test_file1();
275test_file2();
276test_getline();
277test_popen();
278test_os();
279test_os_exec();
280test_timer();
281test_ext_json();
282