use super::harness::*; #[test] fn basic_emit() { let outputs = expect_outputs(r#""kick" sound ."#, 1); assert!(outputs[0].contains("sound/kick")); } #[test] fn alias_s() { let outputs = expect_outputs(r#""snare" s ."#, 1); assert!(outputs[0].contains("sound/snare")); } #[test] fn with_params() { let outputs = expect_outputs(r#""kick" s 440 freq 0.5 gain ."#, 1); assert!(outputs[0].contains("sound/kick")); assert!(outputs[0].contains("freq/440")); assert!(outputs[0].contains("gain/0.5")); } #[test] fn auto_dur() { let outputs = expect_outputs(r#""kick" s ."#, 1); assert!(outputs[0].contains("dur/")); } #[test] fn auto_delaytime() { let outputs = expect_outputs(r#""kick" s ."#, 1); assert!(outputs[0].contains("delaytime/")); } #[test] fn emit_no_sound() { expect_error(".", "nothing to emit"); } #[test] fn multiple_emits() { let outputs = expect_outputs(r#""kick" s . "snare" s ."#, 2); assert!(outputs[0].contains("sound/kick")); assert!(outputs[1].contains("sound/snare")); } #[test] fn envelope_params() { // Values are tempo-scaled: 0.01 * step_duration(0.125) = 0.00125, etc. let outputs = expect_outputs( r#""synth" s 0.01 attack 0.1 decay 0.7 sustain 0.3 release ."#, 1, ); assert!(outputs[0].contains("attack/0.00125")); assert!(outputs[0].contains("decay/0.0125")); assert!(outputs[0].contains("sustain/0.7")); assert!(outputs[0].contains("release/0.0375")); } #[test] fn filter_params() { let outputs = expect_outputs(r#""synth" s 2000 lpf 0.5 lpq ."#, 1); assert!(outputs[0].contains("lpf/2000")); assert!(outputs[0].contains("lpq/0.5")); } #[test] fn adsr_sets_all_envelope_params() { let outputs = expect_outputs(r#""synth" s 0.01 0.1 0.5 0.3 adsr ."#, 1); assert!(outputs[0].contains("attack/0.00125")); assert!(outputs[0].contains("decay/0.0125")); assert!(outputs[0].contains("sustain/0.5")); assert!(outputs[0].contains("release/0.0375")); } #[test] fn ad_sets_attack_decay_sustain_zero() { let outputs = expect_outputs(r#""synth" s 0.01 0.1 ad ."#, 1); assert!(outputs[0].contains("attack/0.00125")); assert!(outputs[0].contains("decay/0.0125")); assert!(outputs[0].contains("sustain/0")); } #[test] fn bank_param() { let outputs = expect_outputs(r#""loop" s "a" bank ."#, 1); assert!(outputs[0].contains("sound/loop")); assert!(outputs[0].contains("bank/a")); } #[test] fn param_only_emit() { let outputs = expect_outputs(r#"0 voice 880 freq ."#, 1); assert!(outputs[0].contains("voice/0")); assert!(outputs[0].contains("freq/880")); assert!(!outputs[0].contains("sound/")); assert!(outputs[0].contains("dur/")); assert!(!outputs[0].contains("delaytime/")); } #[test] fn param_only_multiple_params() { let outputs = expect_outputs(r#"0 voice 440 freq 0.5 gain ."#, 1); assert!(outputs[0].contains("voice/0")); assert!(outputs[0].contains("freq/440")); assert!(outputs[0].contains("gain/0.5")); assert!(!outputs[0].contains("sound/")); } #[test] fn polyphonic_notes() { let outputs = expect_outputs(r#"60 64 67 note sine s ."#, 3); assert!(outputs[0].contains("note/60")); assert!(outputs[1].contains("note/64")); assert!(outputs[2].contains("note/67")); } #[test] fn polyphonic_sounds() { let outputs = expect_outputs(r#"440 freq kick hat s ."#, 2); assert!(outputs[0].contains("sound/kick")); assert!(outputs[1].contains("sound/hat")); } #[test] fn polyphonic_cycling() { let outputs = expect_outputs(r#"60 64 67 note 0.5 1.0 gain sine s ."#, 3); assert!(outputs[0].contains("note/60")); assert!(outputs[0].contains("gain/0.5")); assert!(outputs[1].contains("note/64")); assert!(outputs[1].contains("gain/1")); assert!(outputs[2].contains("note/67")); assert!(outputs[2].contains("gain/0.5")); } #[test] fn polyphonic_with_at() { let outputs = expect_outputs(r#"0 0.5 at 60 64 note sine s ."#, 4); assert_eq!(outputs.len(), 4); } #[test] fn explicit_dur_zero_is_infinite() { let outputs = expect_outputs("880 freq 0 dur .", 1); assert!(outputs[0].contains("dur/0")); } #[test] fn all_before_sounds() { let outputs = expect_outputs( r#"500 lpf 0.5 verb all "kick" s 60 note . "hat" s 70 note ."#, 2, ); assert!(outputs[0].contains("sound/kick")); assert!(outputs[0].contains("lpf/500")); assert!(outputs[0].contains("verb/0.5")); assert!(outputs[1].contains("sound/hat")); assert!(outputs[1].contains("lpf/500")); assert!(outputs[1].contains("verb/0.5")); } #[test] fn all_after_sounds() { let outputs = expect_outputs( r#""kick" s 60 note . "hat" s 70 note . 500 lpf 0.5 verb all"#, 2, ); assert!(outputs[0].contains("sound/kick")); assert!(outputs[0].contains("lpf/500")); assert!(outputs[0].contains("verb/0.5")); assert!(outputs[1].contains("sound/hat")); assert!(outputs[1].contains("lpf/500")); assert!(outputs[1].contains("verb/0.5")); } #[test] fn noall_clears_global_params() { let outputs = expect_outputs( r#"500 lpf all "kick" s 60 note . noall "hat" s 70 note ."#, 2, ); assert!(outputs[0].contains("lpf/500")); assert!(!outputs[1].contains("lpf/500")); } #[test] fn all_with_tempo_scaled_params() { // attack is tempo-scaled: 0.01 * step_duration(0.125) = 0.00125 let outputs = expect_outputs( r#"0.01 attack all "kick" s 60 note ."#, 1, ); assert!(outputs[0].contains("attack/0.00125")); } #[test] fn all_per_sound_override() { let outputs = expect_outputs( r#"500 lpf all "kick" s 2000 lpf . "hat" s ."#, 2, ); // kick has both global lpf=500 and per-sound lpf=2000; per-sound wins (comes last) assert!(outputs[0].contains("lpf/2000")); // hat only has global lpf=500 assert!(outputs[1].contains("lpf/500")); } #[test] fn all_persists_across_evaluations() { let f = forth(); let ctx = default_ctx(); f.evaluate(r#"500 lpf 0.5 verb all"#, &ctx).unwrap(); let outputs = f.evaluate(r#""kick" s 60 note ."#, &ctx).unwrap(); assert_eq!(outputs.len(), 1); assert!(outputs[0].contains("lpf/500"), "global lpf missing: {}", outputs[0]); assert!(outputs[0].contains("verb/0.5"), "global verb missing: {}", outputs[0]); } #[test] fn noall_clears_across_evaluations() { let f = forth(); let ctx = default_ctx(); f.evaluate(r#"500 lpf all"#, &ctx).unwrap(); f.evaluate(r#"noall"#, &ctx).unwrap(); let outputs = f.evaluate(r#""kick" s 60 note ."#, &ctx).unwrap(); assert_eq!(outputs.len(), 1); assert!(!outputs[0].contains("lpf"), "lpf should be cleared: {}", outputs[0]); } #[test] fn rec() { let outputs = expect_outputs(r#""loop1" rec"#, 1); assert_eq!(outputs[0], "/doux/rec/sound/loop1"); } #[test] fn overdub() { let outputs = expect_outputs(r#""loop1" overdub"#, 1); assert_eq!(outputs[0], "/doux/rec/sound/loop1/overdub/1"); } #[test] fn overdub_alias_dub() { let outputs = expect_outputs(r#""loop1" dub"#, 1); assert_eq!(outputs[0], "/doux/rec/sound/loop1/overdub/1"); } #[test] fn all_replaces_previous_global() { let f = forth(); let ctx = default_ctx(); f.evaluate(r#"500 lpf 0.5 verb all"#, &ctx).unwrap(); f.evaluate(r#"2000 lpf all"#, &ctx).unwrap(); let outputs = f.evaluate(r#""kick" s ."#, &ctx).unwrap(); assert_eq!(outputs.len(), 1); assert!(outputs[0].contains("lpf/2000"), "latest lpf should be 2000: {}", outputs[0]); } #[test] fn slice_param() { let outputs = expect_outputs(r#""break" s 8 slice ."#, 1); assert!(outputs[0].contains("slice/8")); } #[test] fn pick_param() { let outputs = expect_outputs(r#""break" s 8 slice 3 pick ."#, 1); assert!(outputs[0].contains("slice/8")); assert!(outputs[0].contains("pick/3")); }