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
80pub 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 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}