char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somsnapshot: embed shared main and worker startup in the CLI688e96e

main
4.4 KiB122 linesraw
1mod common;
2
3use std::fs;
4use std::process::{Command, Stdio};
5use std::time::{SystemTime, UNIX_EPOCH};
6
7#[test]
8fn embedded_startup_uses_live_state_in_main_and_nested_workers() {
9    let directory = tempfile::tempdir().unwrap();
10    let root = directory.path();
11    fs::write(
12        root.join("state.js"),
13        r#"
14        import {env, process} from 'avril:std';
15        export async function state() {
16            let value, denied;
17            try { value = env.get('AVRIL_STARTUP_VALUE'); }
18            catch (error) { value = error.name; }
19            try { env.get('AVRIL_STARTUP_UNGRANTED'); }
20            catch (error) { denied = error.name; }
21            await new Promise(resolve => setTimeout(resolve, 1));
22            return {
23                pid: process.pid, args: process.args, value, denied,
24                origin: performance.timeOrigin,
25                storage: typeof Storage !== 'undefined',
26                name: globalThis.name ?? '', url: globalThis.location?.href ?? '',
27                random: Array.from({length: 4}, () => Math.random()),
28                uuid: crypto.randomUUID(),
29            };
30        }
31        export function child(name) {
32            const worker = new Worker('./worker.js', {type: 'module', name});
33            return new Promise((resolve, reject) => {
34                worker.onmessage = event => resolve(event.data);
35                worker.onerror = event => reject(Error(event.message));
36            });
37        }
38    "#,
39    )
40    .unwrap();
41    fs::write(
42        root.join("worker.js"),
43        r#"
44        import {env} from 'avril:std';
45        import {state, child} from './state.js';
46        const result = await state();
47        if (name === 'branch') {
48            env.set('AVRIL_STARTUP_VALUE', 'nested value');
49            result.nested = await child('leaf');
50        }
51        postMessage(result);
52        close();
53    "#,
54    )
55    .unwrap();
56    fs::write(
57        root.join("main.js"),
58        r#"
59        import {env, pledge} from 'avril:std';
60        import {state, child} from './state.js';
61        const result = await state();
62        result.worker = await child('branch');
63        if (env.get('AVRIL_STARTUP_VALUE') !== result.value) throw Error('Shared environment');
64        pledge({env: false});
65        result.narrowed = await child('denied');
66        console.log(JSON.stringify(result));
67    "#,
68    )
69    .unwrap();
70    let worker_url = url::Url::from_file_path(root.join("worker.js"))
71        .unwrap()
72        .to_string();
73    let mut random_sequences = std::collections::HashSet::new();
74    let mut uuids = std::collections::HashSet::new();
75    for label in ["first process", "second process"] {
76        let started = SystemTime::now()
77            .duration_since(UNIX_EPOCH)
78            .unwrap()
79            .as_millis();
80        let child = Command::new(env!("CARGO_BIN_EXE_avril"))
81            .current_dir(root)
82            .env("AVRIL_STARTUP_VALUE", label)
83            .args([
84                "run",
85                "-0",
86                "--allow-js-exec=.",
87                "--allow-env=AVRIL_STARTUP_VALUE",
88                "main.js",
89                label,
90            ])
91            .stdin(Stdio::null())
92            .stdout(Stdio::piped())
93            .stderr(Stdio::piped())
94            .spawn()
95            .unwrap();
96        let pid = child.id();
97        let output = common::wait(child);
98        assert!(
99            output.status.success(),
100            "{}",
101            String::from_utf8_lossy(&output.stderr)
102        );
103        let result: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
104        for (state, name, value) in [
105            (&result, "", label),
106            (&result["worker"], "branch", label),
107            (&result["worker"]["nested"], "leaf", "nested value"),
108            (&result["narrowed"], "denied", "PermissionDenied"),
109        ] {
110            assert!(random_sequences.insert(state["random"].to_string()));
111            assert!(uuids.insert(state["uuid"].as_str().unwrap().to_owned()));
112            assert_eq!(state["pid"], pid);
113            assert_eq!(state["args"], serde_json::json!([label]));
114            assert_eq!(state["value"], value);
115            assert_eq!(state["denied"], "PermissionDenied");
116            assert_eq!(state["name"], name);
117            assert_eq!(state["storage"], name.is_empty());
118            assert_eq!(state["url"], if name.is_empty() { "" } else { &worker_url });
119            assert!(state["origin"].as_f64().unwrap() >= started as f64 - 1000.0);
120        }
121    }
122}