1 #!/usr/bin/env -S ucode -S 2 3 import * as fs from 'fs'; 4 5 let testdir = sourcepath(0, true); 6 let topdir = fs.realpath(`${testdir}/../..`); 7 8 let line = '........................................'; 9 let ucode_bin = getenv('UCODE_BIN') || `${topdir}/ucode`; 10 let ucode_lib = getenv('UCODE_LIB') || topdir; 11 12 function mkdir_p(path) { 13 let parts = split(rtrim(path, '/') || '/', /\/+/); 14 let current = ''; 15 16 for (let part in parts) { 17 current += part + '/'; 18 19 let s = fs.stat(current); 20 21 if (s == null) { 22 if (!fs.mkdir(current)) 23 die(`Error creating directory '${current}': ${fs.error()}`); 24 } 25 else if (s.type != 'directory') { 26 die(`Path '${current}' exists but is not a directory`); 27 } 28 } 29 } 30 31 function shellquote(s) { 32 return `'${replace(s, "'", "'\\''")}'`; 33 } 34 35 function getpid() { 36 return +fs.popen('echo $PPID', 'r').read('all'); 37 } 38 39 function has_expectations(testcase) 40 { 41 return (testcase?.stdout != null || testcase?.stderr != null || testcase?.exitcode != null); 42 } 43 44 function parse_testcases(file, dir) { 45 let fp = fs.open(file, 'r') ?? die(`Unable to open ${file}: ${fs.error()}`); 46 let testcases, testcase, section, m; 47 let code_first = false; 48 49 for (let line = fp.read('line'); length(line); line = fp.read('line')) { 50 if (line == '-- Args --\n') { 51 section = [ 'args', [] ]; 52 } 53 else if (line == '-- Vars --\n') { 54 section = [ 'env', {} ]; 55 } 56 else if (line == '-- Testcase --\n') { 57 section = [ 'code', '' ]; 58 } 59 else if ((m = match(line, /^-- Expect (stdout|stderr|exitcode) --$/s)) != null) { 60 section = [ m[1], '' ]; 61 } 62 else if ((m = match(line, /^-- File (.*)--$/s)) != null) { 63 section = [ 'file', `${dir}/files/${trim(m[1]) || 'file'}`, '' ]; 64 } 65 else if ((m = match(line, /^-- End( \(no-eol\))? --$/s)) != null) { 66 if (m[1] != null && type(section[-1]) == 'string') 67 section[-1] = substr(section[-1], 0, -1); 68 69 if (section[0] == 'code') { 70 if (testcases == null && !has_expectations(testcase)) 71 code_first = true; 72 73 if (code_first) { 74 if (testcase?.code != null) { 75 push(testcases ??= [], testcase); 76 testcase = null; 77 } 78 79 (testcase ??= {}).code = section[1]; 80 } 81 else { 82 push(testcases ??= [], { ...testcase, code: section[1] }); 83 testcase = null; 84 } 85 } 86 else if (section[0] == 'file') { 87 ((testcase ??= {}).files ??= {})[section[1]] = section[2]; 88 } 89 else { 90 (testcase ??= {})[section[0]] = section[1]; 91 } 92 93 section = null; 94 } 95 else if (section) { 96 switch (section[0]) { 97 case 'args': 98 if ((m = trim(line)) != '') 99 push(section[1], ...split(m, /[ \t\r\n]+/)); 100 break; 101 102 case 'env': 103 if ((m = match(line, /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s)) != null) 104 section[1][m[1]] = m[2]; 105 break; 106 107 default: 108 section[-1] += line; 109 break; 110 } 111 } 112 } 113 114 if (code_first && testcase.code != null && has_expectations(testcase)) 115 push(testcases ??= [], testcase); 116 117 return testcases; 118 } 119 120 function diff(tag, ...ab) { 121 let cmd = [ 'diff', '-au', '--color=always', `--label=Expected ${tag}`, `--label=Resulting ${tag}` ]; 122 let tmpfiles = []; 123 124 for (let i, f in ab) { 125 if (type(f) != 'resource') { 126 push(tmpfiles, fs.mkstemp()); 127 tmpfiles[-1].write(f); 128 f = tmpfiles[-1]; 129 } 130 131 f.seek(0); 132 push(cmd, `/dev/fd/${f.fileno()}`); 133 } 134 135 system(cmd); 136 } 137 138 function run_testcase(num, dir, testcase) { 139 let fout = fs.mkstemp(`${dir}/stdout.XXXXXX`); 140 let ferr = fs.mkstemp(`${dir}/stderr.XXXXXX`); 141 142 let eout = testcase.stdout ?? ''; 143 let eerr = testcase.stderr ?? ''; 144 let ecode = testcase.exitcode ? +testcase.exitcode : null; 145 146 let cmd = join(' ', [ 147 ...map(keys(testcase.env) ?? [], k => `export ${k}=${shellquote(testcase.env[k])};`), 148 `cd ${shellquote(dir)};`, 149 `exec ${ucode_bin}`, 150 `-T','`, 151 `-L ${shellquote(`${ucode_lib}/*.so`)}`, 152 `-D TESTFILES_PATH=${shellquote(`${fs.realpath(dir)}/files`)}`, 153 `${join(' ', map(testcase.args ?? [], shellquote))} -`, 154 `>/dev/fd/${fout.fileno()} 2>/dev/fd/${ferr.fileno()}` 155 ]); 156 157 let proc = fs.popen(cmd, 'w') ?? die(`Error launching test command "${cmd}": ${fs.error()}\n`); 158 159 if (testcase.code != null) 160 proc.write(testcase.code); 161 162 let exitcode = proc.close(); 163 164 fout.seek(0); 165 ferr.seek(0); 166 167 let ok = true; 168 169 if (replace(ferr.read('all'), dir, '.') != eerr) { 170 if (ok) print('!\n'); 171 printf("Testcase #%d: Expected stderr did not match:\n", num); 172 diff('stderr', eerr, ferr); 173 print("---\n"); 174 ok = false; 175 } 176 177 if (replace(fout.read('all'), dir, '.') != eout) { 178 if (ok) print('!\n'); 179 printf("Testcase #%d: Expected stdout did not match:\n", num); 180 diff('stdout', eout, fout); 181 print("---\n"); 182 ok = false; 183 } 184 185 if (ecode != null && exitcode != ecode) { 186 if (ok) print('!\n'); 187 printf("Testcase #%d: Expected exit code did not match:\n", num); 188 diff('code', `${ecode}\n`, `${exitcode}\n`); 189 print("---\n"); 190 ok = false; 191 } 192 193 return ok; 194 } 195 196 function run_test(file) { 197 let name = fs.basename(file); 198 printf('%s %s ', name, substr(line, length(name))); 199 200 let tmpdir = sprintf('/tmp/test.%d', getpid()); 201 let testcases = parse_testcases(file, tmpdir); 202 let failed = 0; 203 204 fs.mkdir(tmpdir); 205 206 try { 207 for (let i, testcase in testcases) { 208 for (let path, data in testcase.files) { 209 mkdir_p(fs.dirname(path)); 210 fs.writefile(path, data) ?? die(`Error writing testcase file "${path}": ${fs.error()}\n`); 211 } 212 213 failed += !run_testcase(i + 1, tmpdir, testcase); 214 } 215 } 216 catch (e) { 217 warn(`${e.type}: ${e.message}\n${e.stacktrace[0].context}\n`); 218 } 219 220 system(['rm', '-r', tmpdir]); 221 222 if (failed == 0) 223 print('OK\n'); 224 else 225 printf('%s %s FAILED (%d/%d)\n', name, substr(line, length(name)), failed, length(testcases)); 226 227 return failed; 228 } 229 230 let n_tests = 0; 231 let n_fails = 0; 232 let select_tests = filter(map(ARGV, p => fs.realpath(p)), length); 233 234 function use_test(input) { 235 return fs.access(input = fs.realpath(input)) && 236 (!length(select_tests) || filter(select_tests, p => p == input)[0]); 237 } 238 239 for (let catdir in fs.glob(`${testdir}/[0-9][0-9]_*`)) { 240 if (fs.stat(catdir)?.type != 'directory') 241 continue; 242 243 printf('\n##\n## Running %s tests\n##\n\n', substr(fs.basename(catdir), 3)); 244 245 for (let testfile in fs.glob(`${catdir}/[0-9][0-9]_*`)) { 246 if (!use_test(testfile)) continue; 247 248 n_tests++; 249 n_fails += run_test(testfile); 250 } 251 } 252 253 printf('\nRan %d tests, %d okay, %d failures\n', n_tests, n_tests - n_fails, n_fails); 254 exit(n_fails);
This page was automatically generated by LXR 0.3.1. • OpenWrt