143 lines
3.3 KiB
Rust
143 lines
3.3 KiB
Rust
use rand::rngs::StdRng;
|
|
use rand::SeedableRng;
|
|
use cagire::model::forth::{Dictionary, Forth, Rng, StepContext, Value, Variables};
|
|
use std::collections::HashMap;
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
pub fn default_ctx() -> StepContext {
|
|
StepContext {
|
|
step: 0,
|
|
beat: 0.0,
|
|
pattern: 0,
|
|
tempo: 120.0,
|
|
phase: 0.0,
|
|
slot: 0,
|
|
runs: 0,
|
|
iter: 0,
|
|
speed: 1.0,
|
|
fill: false,
|
|
}
|
|
}
|
|
|
|
pub fn ctx_with(f: impl FnOnce(&mut StepContext)) -> StepContext {
|
|
let mut ctx = default_ctx();
|
|
f(&mut ctx);
|
|
ctx
|
|
}
|
|
|
|
pub fn new_vars() -> Variables {
|
|
Arc::new(Mutex::new(HashMap::new()))
|
|
}
|
|
|
|
pub fn new_dict() -> Dictionary {
|
|
Arc::new(Mutex::new(HashMap::new()))
|
|
}
|
|
|
|
pub fn seeded_rng(seed: u64) -> Rng {
|
|
Arc::new(Mutex::new(StdRng::seed_from_u64(seed)))
|
|
}
|
|
|
|
pub fn forth() -> Forth {
|
|
Forth::new(new_vars(), new_dict(), seeded_rng(42))
|
|
}
|
|
|
|
pub fn forth_seeded(seed: u64) -> Forth {
|
|
Forth::new(new_vars(), new_dict(), seeded_rng(seed))
|
|
}
|
|
|
|
pub fn run(script: &str) -> Forth {
|
|
let f = forth();
|
|
f.evaluate(script, &default_ctx()).unwrap();
|
|
f
|
|
}
|
|
|
|
pub fn run_ctx(script: &str, ctx: &StepContext) -> Forth {
|
|
let f = forth();
|
|
f.evaluate(script, ctx).unwrap();
|
|
f
|
|
}
|
|
|
|
pub fn stack_top(f: &Forth) -> Value {
|
|
f.stack().pop().expect("stack empty")
|
|
}
|
|
|
|
pub fn stack_int(f: &Forth) -> i64 {
|
|
match stack_top(f) {
|
|
Value::Int(i, _) => i,
|
|
other => panic!("expected Int, got {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn stack_float(f: &Forth) -> f64 {
|
|
match stack_top(f) {
|
|
Value::Float(x, _) => x,
|
|
Value::Int(i, _) => i as f64,
|
|
other => panic!("expected number, got {:?}", other),
|
|
}
|
|
}
|
|
|
|
pub fn expect_stack(script: &str, expected: &[Value]) {
|
|
let f = run(script);
|
|
let stack = f.stack();
|
|
assert_eq!(stack, expected, "script: {}", script);
|
|
}
|
|
|
|
pub fn expect_int(script: &str, expected: i64) {
|
|
expect_stack(script, &[Value::Int(expected, None)]);
|
|
}
|
|
|
|
pub fn expect_float(script: &str, expected: f64) {
|
|
let f = run(script);
|
|
let stack = f.stack();
|
|
assert_eq!(stack.len(), 1, "expected single value on stack");
|
|
let val = stack_float(&f);
|
|
assert!(
|
|
(val - expected).abs() < 1e-9,
|
|
"expected {}, got {}",
|
|
expected,
|
|
val
|
|
);
|
|
}
|
|
|
|
pub fn expect_floats_close(script: &str, expected: f64, epsilon: f64) {
|
|
let f = run(script);
|
|
let val = stack_float(&f);
|
|
assert!(
|
|
(val - expected).abs() < epsilon,
|
|
"expected ~{}, got {}",
|
|
expected,
|
|
val
|
|
);
|
|
}
|
|
|
|
pub fn expect_error(script: &str, expected_substr: &str) {
|
|
let f = forth();
|
|
let result = f.evaluate(script, &default_ctx());
|
|
assert!(result.is_err(), "expected error for '{}'", script);
|
|
let err = result.unwrap_err();
|
|
assert!(
|
|
err.contains(expected_substr),
|
|
"error '{}' does not contain '{}'",
|
|
err,
|
|
expected_substr
|
|
);
|
|
}
|
|
|
|
pub fn expect_outputs(script: &str, count: usize) -> Vec<String> {
|
|
let f = forth();
|
|
let outputs = f.evaluate(script, &default_ctx()).unwrap();
|
|
assert_eq!(outputs.len(), count, "expected {} outputs", count);
|
|
outputs
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn expect_output_contains(script: &str, substr: &str) {
|
|
let outputs = expect_outputs(script, 1);
|
|
assert!(
|
|
outputs[0].contains(substr),
|
|
"output '{}' does not contain '{}'",
|
|
outputs[0],
|
|
substr
|
|
);
|
|
}
|