mimium_test/
lib.rs

1extern crate mimium_lang;
2use std::{collections::HashMap, path::PathBuf};
3
4use mimium_audiodriver::{backends::local_buffer::LocalBufferDriver, driver::Driver};
5use mimium_lang::{
6    interner::{Symbol, ToSymbol},
7    plugin::Plugin,
8    runtime::{self, vm},
9    utils::{
10        error::{report, ReportableError},
11        fileloader,
12    },
13    ExecContext,
14};
15
16pub fn run_bytecode_test(
17    machine: &mut vm::Machine,
18    n: usize,
19) -> Result<&[f64], Vec<Box<dyn ReportableError>>> {
20    let retcode = machine.execute_entry(&"dsp".to_symbol());
21    if retcode >= 0 {
22        Ok(vm::Machine::get_as_array::<f64>(machine.get_top_n(n)))
23    } else {
24        Err(vec![Box::new(runtime::Error(
25            runtime::ErrorKind::Unknown,
26            0..0,
27        ))])
28    }
29}
30
31pub fn run_bytecode_test_multiple(
32    bytecodes: vm::Program,
33    times: u64,
34    stereo: bool,
35) -> Result<Vec<f64>, Vec<Box<dyn ReportableError>>> {
36    let mut ctx = ExecContext::new([].into_iter(), None);
37    let _ = ctx.prepare_machine_with_bytecode(bytecodes);
38    let mut machine = ctx.vm.unwrap();
39    let _retcode = machine.execute_main();
40    let n = if stereo { 2 } else { 1 };
41    let mut ret = Vec::with_capacity(times as usize * n);
42    for i in 0..times {
43        let res = run_bytecode_test(&mut machine, n)?;
44        ret.extend_from_slice(res);
45        println!("time:{}, res: {:?}", i, res)
46    }
47    Ok(ret)
48}
49
50pub fn run_source_with_plugins(
51    src: &str,
52    path: Option<&str>,
53    times: u64,
54    plugins: impl Iterator<Item = Box<dyn Plugin>>,
55    with_scheduler: bool,
56) -> Result<Vec<f64>, Vec<Box<dyn ReportableError>>> {
57    let mut driver = LocalBufferDriver::new(times as _);
58    let audiodriverplug: Box<dyn Plugin> = Box::new(driver.get_as_plugin());
59    let mut ctx = ExecContext::new(
60        plugins.chain([audiodriverplug]),
61        path.map(|s| s.to_symbol()),
62    );
63    if with_scheduler {
64        ctx.add_system_plugin(mimium_scheduler::get_default_scheduler_plugin());
65    }
66    ctx.prepare_machine(src).unwrap();
67    let _ = ctx.run_main();
68    driver.init(ctx, None);
69    driver.play();
70    Ok(driver.get_generated_samples().to_vec())
71}
72
73pub fn run_source_with_scheduler(
74    src: &str,
75    times: u64,
76) -> Result<Vec<f64>, Vec<Box<dyn ReportableError>>> {
77    run_source_with_plugins(src, None, times, [].into_iter(), true)
78}
79
80// if stereo, this returns values in flattened form [L1, R1, L2, R2, ...]
81pub fn run_source_test(
82    src: &str,
83    times: u64,
84    stereo: bool,
85    path: Option<Symbol>,
86) -> Result<Vec<f64>, Vec<Box<dyn ReportableError>>> {
87    let mut ctx = ExecContext::new([].into_iter(), path);
88
89    ctx.prepare_machine(src)?;
90    let bytecode = ctx.vm.unwrap().prog;
91    run_bytecode_test_multiple(bytecode, times, stereo)
92}
93
94pub fn run_file_with_plugins(
95    path: &str,
96    times: u64,
97    plugins: impl Iterator<Item = Box<dyn Plugin>>,
98    with_scheduler: bool,
99) -> Result<Vec<f64>, ()> {
100    let (file, src) = load_src(path);
101    let res = run_source_with_plugins(
102        &src,
103        Some(&file.to_string_lossy()),
104        times,
105        plugins,
106        with_scheduler,
107    );
108    match res {
109        Ok(res) => Ok(res),
110        Err(errs) => {
111            report(&src, file, &errs);
112            Err(())
113        }
114    }
115}
116pub fn run_file_with_scheduler(path: &str, times: u64) -> Result<Vec<f64>, ()> {
117    run_file_with_plugins(path, times, [].into_iter(), true)
118}
119pub fn run_file_test(path: &str, times: u64, stereo: bool) -> Result<Vec<f64>, ()> {
120    let (file, src) = load_src(path);
121    let path_sym = file.to_string_lossy().to_symbol();
122    let res = run_source_test(&src, times, stereo, Some(path_sym));
123    match res {
124        Ok(res) => Ok(res),
125        Err(errs) => {
126            report(&src, file, &errs);
127            Err(())
128        }
129    }
130}
131
132pub fn load_src(path: &str) -> (PathBuf, String) {
133    let crate_root = std::env::var("TEST_ROOT").expect(
134        r#"You must set TEST_ROOT environment variable to run test.
135You should put the line like below to your build.rs.
136fn main() {
137    println!("cargo:rustc-env=TEST_ROOT={}", env!("CARGO_MANIFEST_DIR"));
138}
139"#,
140    );
141    let file = [crate_root.as_str(), "tests/mmm", path]
142        .iter()
143        .collect::<PathBuf>()
144        .canonicalize()
145        .unwrap();
146    let file_str = file.to_str().unwrap();
147    println!("{}", file_str);
148    let src = fileloader::load(file_str).unwrap();
149    (file, src)
150}
151
152pub fn run_file_test_mono(path: &str, times: u64) -> Result<Vec<f64>, ()> {
153    run_file_test(path, times, false)
154}
155
156pub fn run_file_test_stereo(path: &str, times: u64) -> Result<Vec<f64>, ()> {
157    run_file_test(path, times, true)
158}
159
160pub fn test_state_sizes<T: IntoIterator<Item = (&'static str, u64)>>(path: &str, ans: T) {
161    let state_sizes: HashMap<&str, u64> = HashMap::from_iter(ans);
162    let (file, src) = load_src(path);
163    let mut ctx = ExecContext::new([].into_iter(), Some(file.to_str().unwrap().to_symbol()));
164    ctx.prepare_machine(&src).unwrap();
165    let bytecode = ctx.vm.expect("failed to emit bytecode").prog;
166    // let bytecode = match ctx.compiler.emit_bytecode(&src) {
167    //     Ok(res) => res,
168    //     Err(errs) => {
169    //         report(&src, file, &errs);
170    //         panic!("failed to emit bytecode");
171    //     }
172    // };
173
174    for (sym, proto) in bytecode.global_fn_table {
175        let fn_name = sym.as_str();
176
177        if fn_name == "_mimium_global" {
178            continue;
179        }
180
181        let actual = proto.state_size;
182        match state_sizes.get(fn_name) {
183            Some(&expected) => {
184                assert_eq!(
185                    actual, expected,
186                    "state size of function `{fn_name}` is wrong"
187                );
188            }
189            None => panic!("no such function: {fn_name}"),
190        };
191    }
192}