char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
688e96e
main
1mod common; 2 3use common::{ run, spawn, wait}; 4 5# [ test ] 6fn binary_piping_and_byob_preserve_bytes () { 7let input: Vec < u8 > =( 0 ..160_003 ). map ( |index|( index %256 ) as u8 ). collect (); 8for sourcein [ 9r#" 10import { stdin, stdout } from "avril:std"; 11await stdin.pipeThrough(new TransformStream()).pipeTo(stdout); 12"# , 13r#" 14import { stdin, stdout } from "avril:std"; 15const reader = stdin.getReader({ mode: "byob" }); 16const writer = stdout.getWriter(); 17while (true) { 18const view = new Uint8Array(new ArrayBuffer(1033), 7, 1024); 19const { value, done } = await reader.read(view); 20if (done) break; 21await writer.write(value); 22} 23await writer.close(); 24"# , 25] { 26assert_eq! ( run ( source, & input). stdout , input); 27} 28} 29 30# [ test ] 31fn stdio_is_not_exposed_on_the_global_and_output_is_ordered () { 32let output =run ( 33r#" 34const opsSymbol = Symbol.for("__avril.ops"); 35if (opsSymbol in globalThis || globalThis[opsSymbol] !== undefined 36|| Reflect.ownKeys(globalThis).includes(opsSymbol)) { 37throw new Error("Native op registry exposed"); 38} 39if (Symbol.for("__avril.stdio") in globalThis) { 40throw new Error("Stdio registry exposed"); 41} 42import { stdout, stderr } from "avril:std"; 43const Bytes = Uint8Array; 44const writer = stdout.getWriter(); 45const errors = stderr.getWriter(); 46globalThis.Uint8Array = () => { throw new Error("Poisoned constructor"); }; 47Promise.resolve = () => { throw new Error("Poisoned resolve"); }; 48Array.prototype.map = () => { throw new Error("Poisoned map"); }; 49writer.write(new Bytes([1, 2])); 50writer.write(new Bytes([3, 4])); 51await writer.close(); 52errors.write(new Bytes([5, 6])); 53"# , 54& [], 55); 56assert_eq! ( output. stdout , [ 1 , 2 , 3 , 4 ]); 57assert_eq! ( output. stderr , [ 5 , 6 ]); 58} 59 60# [ test ] 61fn canceling_a_pending_stdin_read_does_not_hold_the_process_open () { 62let ( _dir, mut child) =spawn ( 63r#" 64import { stdin } from "avril:std"; 65const reader = stdin.getReader(); 66const pending = reader.read(); 67await Promise.resolve(); 68await reader.cancel(); 69await pending; 70"# , 71); 72let stdin = child. stdin . take (). unwrap (); 73let output =wait ( child); 74drop ( stdin); 75assert! ( 76 output. status . success (), 77"{}" , 78String :: from_utf8_lossy ( & output. stderr ) 79); 80} 81 82# [ test ] 83fn output_failures_reject_writes_and_fail_the_cli () { 84let ( _dir, mut child) =spawn ( 85r#"import { stdout } from "avril:std"; await stdout 86.getWriter().write(new Uint8Array([1]));"# , 87); 88drop ( child. stdout . take ()); 89let output =wait ( child); 90assert! ( !output. status . success ()); 91assert! ( !output. stderr . is_empty ()); 92} 93 94# [ test ] 95fn default_stdin_reads_do_not_retain_oversized_buffers () { 96let output =run ( 97r#" 98import { stdin, stdout } from "avril:std"; 99const reader = stdin.getReader(); 100const writer = stdout.getWriter(); 101while (true) { 102const { value, done } = await reader.read(); 103if (done) break; 104if (value.buffer.byteLength !== value.byteLength) { 105throw new Error("Input chunk retained an oversized buffer"); 106} 107await writer.write(value); 108} 109await writer.close(); 110"# , 111b"hi\n" , 112); 113assert_eq! ( output. stdout , b"hi\n" ); 114} 115 116# [ test ] 117fn concurrent_stdin_reads_cancel_cleanly () { 118let ( _dir, mut child) =spawn ( 119r#" 120import { stdin } from "avril:std"; 121const reader = stdin.getReader(); 122const reads = [reader.read(), reader.read()]; 123await reader.cancel(); 124const results = await Promise.all(reads); 125if (results.some(({done}) => !done)) throw new Error("Cancellation did not close input"); 126"# , 127); 128let stdin = child. stdin . take (). unwrap (); 129let output =wait ( child); 130drop ( stdin); 131assert! ( 132 output. status . success (), 133"{}" , 134String :: from_utf8_lossy ( & output. stderr ) 135); 136} 137 138# [ test ] 139fn queued_stream_output_does_not_block_console_or_the_event_loop () { 140let output =run ( 141r#" 142import { stdout } from "avril:std"; 143const writer = stdout.getWriter(); 144const writes = []; 145for (let value = 65; value < 73; value++) { 146writes.push(writer.write(new Uint8Array(128 * 1024).fill(value))); 147} 148writes[0].then(() => console.log("done")); 149await Promise.all(writes); 150await writer.close(); 151"# , 152& [], 153); 154let done = output 155. stdout 156. windows ( b"done\n" . len ()) 157. position ( |window| window ==b"done\n" ) 158. expect ( "console output should progress while stream writes are queued" ); 159let mut stream_output = output. stdout ; 160 stream_output. drain ( done..done +b"done\n" . len ()); 161let expected: Vec < _ > =( 65 ..73 ) 162. flat_map ( |byte| std:: iter:: repeat_n ( byte, 128 * 1024 )) 163. collect (); 164assert_eq! ( stream_output, expected); 165} 166 167# [ test ] 168fn output_accepts_shared_and_resizable_byte_views () { 169let output =run ( 170r#" 171import { stdout } from "avril:std"; 172const writer = stdout.getWriter(); 173const shared = new Uint8Array(new SharedArrayBuffer(4)); 174shared.set([0, 65, 66, 0]); 175const sharedView = new Uint8Array(shared.buffer, 1, 2); 176const resizable = new ArrayBuffer(4, { maxByteLength: 8 }); 177const fixedView = new Uint8Array(resizable, 1, 2); 178fixedView.set([67, 68]); 179const tracking = new Uint8Array(resizable, 3); 180resizable.resize(7); 181tracking.set([69, 70, 71, 72]); 182const growable = new SharedArrayBuffer(1, { maxByteLength: 4 }); 183const growingView = new Uint8Array(growable); 184growable.grow(2); 185growingView.set([73, 74]); 186globalThis.Uint8Array = () => { throw new Error("Poisoned constructor"); }; 187Object.defineProperty(ArrayBuffer.prototype, "resizable", { 188get() { throw new Error("Poisoned getter"); }, 189}); 190await writer.write(sharedView); 191await writer.write(fixedView); 192await writer.write(tracking); 193await writer.write(growingView); 194await writer.close(); 195"# , 196& [], 197); 198assert_eq! ( output. stdout , b"ABCDEFGHIJ" ); 199} 200 201# [ test ] 202fn accepted_stream_writes_precede_console_output () { 203let output =run ( 204r#" 205import { stdout } from "avril:std"; 206const writer = stdout.getWriter(); 207await writer.ready; 208writer.write(new Uint8Array([65, 10])); 209console.log("B"); 210await writer.close(); 211"# , 212& [], 213); 214assert_eq! ( output. stdout , b"A\nB\n" ); 215} 216 217# [ test ] 218fn accepted_output_is_flushed_even_when_the_module_fails () { 219let ( _dir, child) =spawn ( 220r#" 221import { stdout } from "avril:std"; 222const writer = stdout.getWriter(); 223const bytes = new Uint8Array(8 * 128 * 1024); 224for (let value = 65; value < 73; value++) { 225bytes.fill(value, (value - 65) * 128 * 1024, (value - 64) * 128 * 1024); 226} 227writer.write(bytes); 228throw new Error("after accepting output"); 229"# , 230); 231let output =wait ( child); 232let expected: Vec < _ > =( 65 ..73 ) 233. flat_map ( |byte| std:: iter:: repeat_n ( byte, 128 * 1024 )) 234. collect (); 235assert! ( !output. status . success ()); 236assert_eq! ( output. stdout , expected); 237assert! ( String :: from_utf8_lossy ( & output. stderr ). contains ( "after accepting output" )); 238}