char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
688e96e
main
1#! [ cfg ( windows )] 2mod common; 3 4const PRELUDE : & str =r#" 5const assert=(value,message)=>{if(!value)throw Error(message)}; 6async function rejects(action,name){try{await action()}catch(error){assert(error.name===name,'Expected '+name+', got '+error);return error}throw Error('Expected '+name)} 7"# ; 8 9fn pass ( source : & str ) { 10 common:: run ( & format! ( "{PRELUDE}\n{source}" ), b"" ); 11} 12 13# [ test ] 14fn console_programs_run_with_exit_codes_env_and_input () { 15pass ( 16r#" 17import {run,runSync} from 'avril:std'; 18for (const method of [run, runSync]) { 19const hello = await method('cmd', {args:['/d','/c','echo hello'], encoding:'utf-8'}); 20assert(hello.stdout === 'hello\r\n', 'PATH search appends .exe: ' + JSON.stringify(hello.stdout)); 21const failed = await method('cmd.exe', {args:['/d','/c','exit 3'], check:false}); 22assert(failed.code === 3 && failed.signal === null, 'exit code'); 23const env = await method('cmd.exe', {args:['/d','/c','echo %AVRIL_PROCESS_VALUE%'], env:{AVRIL_PROCESS_VALUE:'set'}, encoding:'utf-8'}); 24assert(env.stdout === 'set\r\n', 'explicit environment'); 25const found = await method('findstr', {args:['x'], input:'axb\r\nnope\r\n', encoding:'utf-8'}); 26assert(found.stdout === 'axb\r\n', 'piped stdin'); 27} 28"# , 29); 30} 31 32# [ test ] 33fn batch_files_need_an_explicit_shell () { 34let dir = tempfile:: tempdir (). unwrap (); 35 std:: fs:: write ( dir. path (). join ( "tool.cmd" ), "@echo implicit\r\n" ). unwrap (); 36// Only a verbatim path keeps the trailing dot and space literal. 37let literal = dir. path (). canonicalize (). unwrap (). join ( "tool.cmd. " ); 38 std:: fs:: write ( literal, "@echo implicit\r\n" ). unwrap (); 39let source =format! ( 40r#" 41import {{run}} from 'avril:std'; 42const cwd = {}; 43await rejects(()=>run('./tool.cmd', {{cwd}}), 'NotSupported'); 44await rejects(()=>run('./tool.cmd. ', {{cwd}}), 'NotSupported'); 45const explicit = await run('cmd.exe', {{args:['/d','/c','tool.cmd'], cwd, encoding:'utf-8'}}); 46assert(explicit.stdout === 'implicit\r\n', 'explicit cmd.exe'); 47"# , 48 serde_json:: to_string ( dir. path ()). unwrap () 49); 50pass ( & source); 51} 52 53# [ test ] 54fn group_termination_covers_the_job () { 55// The grandchild holds the output pipe; only terminating the whole job 56// lets the pipes close well before ping would finish. 57pass ( 58r#" 59import {run} from 'avril:std'; 60const started = Date.now(); 61try { 62await run('cmd.exe', {args:['/d','/c','ping -n 30 127.0.0.1'], timeoutMs:300}); 63throw Error('expected timeout'); 64} catch (error) { 65assert(error.name === 'ProcessTimeoutError', 'timeout: ' + error); 66} 67assert(Date.now() - started < 4000, 'grandchild kept the pipe open'); 68"# , 69); 70} 71 72# [ test ] 73fn only_terminating_signals_and_no_terminals () { 74pass ( 75r#" 76import {spawn} from 'avril:std'; 77await rejects(()=>spawn('cmd.exe', {termination:{signal:'SIGUSR1'}}), 'NotSupported'); 78await rejects(()=>spawn('cmd.exe', {terminal:{}}), 'NotSupported'); 79const child = spawn('ping', {args:['-n','30','127.0.0.1'], stdout:'null'}); 80await rejects(()=>child.kill('SIGHUP'), 'NotSupported'); 81assert(child.kill('SIGTERM') === true, 'terminated'); 82const status = await child.status; 83assert(status.code === 1 && status.signal === null, 'terminated exit status'); 84"# , 85); 86} 87 88# [ test ] 89fn respelled_entries_meet_the_same_grants () { 90let system = std:: path:: Path :: new ( & std:: env:: var_os ( "SystemRoot" ). unwrap ()). join ( "System32" ); 91let dir = tempfile:: tempdir (). unwrap (); 92let source =format! ( 93r#" 94import {{run}} from 'avril:std'; 95await rejects(()=>run('CMD', {{args:['/d','/c','exit']}}), 'PermissionDenied'); 96await rejects(()=>run({}, {{args:['/d','/c','exit']}}), 'PermissionDenied'); 97"# , 98 serde_json:: to_string ( & system. join ( "Cmd.Exe" )). unwrap () 99); 100 std:: fs:: write ( dir. path (). join ( "main.js" ), format! ( "{PRELUDE}\n{source}" )). unwrap (); 101let output = common:: wait ( 102 std:: process:: Command :: new ( env! ( "CARGO_BIN_EXE_avril" )) 103. arg ( "run" ) 104. arg ( format! ( "--deny-run={}" , system. join ( "CMD.EXE" ). display ())) 105. arg ( "main.js" ) 106. current_dir ( dir. path ()) 107. stdin ( std:: process:: Stdio :: null ()) 108. stdout ( std:: process:: Stdio :: piped ()) 109. stderr ( std:: process:: Stdio :: piped ()) 110. spawn () 111. unwrap (), 112); 113assert! ( 114 output. status . success (), 115"{}" , 116String :: from_utf8_lossy ( & output. stderr ) 117); 118} 119 120# [ test ] 121fn signal_listeners_are_not_supported () { 122pass ( 123r#" 124import {addSignalListener, removeSignalListener} from 'avril:std'; 125await rejects(() => addSignalListener('SIGUSR1', () => {}), 'NotSupported'); 126removeSignalListener('SIGUSR1', () => {}); 127"# , 128); 129}