#[cfg(test)]
mod parse_and_lower {
use legato::{
dsl::ir::{IRGraph, Port, Value},
dsl::parse::legato_parser,
dsl::pipeline::Pipeline,
};
fn parse_and_lower(src: &str) -> IRGraph {
let ast = legato_parser(src).expect("Parse failed");
Pipeline::default().run_from_ast(ast)
}
fn get_param(graph: &IRGraph, alias: &str, key: &str) -> Value {
graph
.find_node_by_alias(alias)
.unwrap_or_else(|| panic!("node '{}' not found in graph", alias))
.params
.get(key)
.cloned()
.unwrap_or_else(|| panic!("param '{}' not found on node '{}'", key, alias))
}
#[test]
fn test_e2e_simple_patch_instantiation() {
let src = r#"
patch voice(freq = 440.0, attack = 100.0, release = 500.0) {
in gate freq_in
audio {
sine: osc { freq: $freq },
adsr: env { attack: $attack, release: $release }
}
freq_in >> osc.freq
gate >> env.gate
osc >> env[1]
{ env }
}
patches {
voice: v1 { freq: 880.0, attack: 200.0, release: 300.0 }
}
{ v1 }
"#;
let graph = parse_and_lower(src);
assert!(
graph.find_node_by_alias("v1.osc").is_some(),
"missing v1.osc"
);
assert!(
graph.find_node_by_alias("v1.env").is_some(),
"missing v1.env"
);
assert_eq!(get_param(&graph, "v1.osc", "freq"), Value::F32(880.0));
assert_eq!(get_param(&graph, "v1.env", "attack"), Value::F32(200.0));
assert_eq!(get_param(&graph, "v1.env", "release"), Value::F32(300.0));
assert_eq!(graph.edge_count(), 1);
let edges = graph.find_edges_between("v1.osc", "v1.env");
assert_eq!(edges.len(), 1, "expected exactly one osc->env edge");
assert_eq!(edges[0].sink_port, Port::Index(1));
let env_id = graph.find_node_by_alias("v1.env").unwrap().id;
assert_eq!(graph.sink, Some(env_id), "graph sink should be v1.env");
}
#[test]
fn test_e2e_external_connections_through_virtual_ports() {
let src = r#"
patch voice(freq = 440.0, attack = 100.0) {
in gate freq_in
audio {
sine: osc { freq: $freq },
adsr: env { attack: $attack }
}
freq_in >> osc.freq
gate >> env.gate
osc >> env[1]
{ env }
}
patches {
voice: v1 { freq: 440.0 }
}
midi {
poly_voice { chan: 0 }
}
poly_voice.freq >> v1.freq_in
poly_voice.gate >> v1.gate
{ v1 }
"#;
let graph = parse_and_lower(src);
assert_eq!(graph.edge_count(), 3);
let osc = graph.find_node_by_alias("v1.osc").expect("v1.osc missing");
let env = graph.find_node_by_alias("v1.env").expect("v1.env missing");
let poly_edges = graph.find_edges_from("poly_voice");
let freq_edge = poly_edges
.iter()
.find(|e| e.source_port == Port::Named("freq".into()))
.expect("poly_voice.freq edge not found");
assert_eq!(
freq_edge.sink, osc.id,
"poly_voice.freq should route to v1.osc"
);
assert_eq!(freq_edge.sink_port, Port::Named("freq".into()));
let gate_edge = poly_edges
.iter()
.find(|e| e.source_port == Port::Named("gate".into()))
.expect("poly_voice.gate edge not found");
assert_eq!(
gate_edge.sink, env.id,
"poly_voice.gate should route to v1.env"
);
assert_eq!(gate_edge.sink_port, Port::Named("gate".into()));
}
#[test]
fn test_e2e_multiple_instances_distinct_fqns_and_params() {
let src = r#"
patch voice(freq = 440.0) {
audio {
sine: osc { freq: $freq }
}
{ osc }
}
patches {
voice: v1 { freq: 110.0 },
voice: v2 { freq: 220.0 },
voice: v3 { freq: 440.0 }
}
{ v1 }
"#;
let graph = parse_and_lower(src);
assert_eq!(get_param(&graph, "v1.osc", "freq"), Value::F32(110.0));
assert_eq!(get_param(&graph, "v2.osc", "freq"), Value::F32(220.0));
assert_eq!(get_param(&graph, "v3.osc", "freq"), Value::F32(440.0));
}
#[test]
fn test_e2e_nested_patch_virtual_port_resolution() {
let src = r#"
patch fm_osc(freq = 440.0, mod_freq = 880.0) {
in freq_in
audio {
sine: modulator { freq: $mod_freq },
sine: carrier { freq: $freq }
}
freq_in >> carrier.freq
modulator >> carrier[0]
{ carrier }
}
patch voice(freq = 440.0, attack = 100.0) {
in gate voice_freq
audio {
fm_osc: osc_inst { freq: $freq },
adsr: env { attack: $attack }
}
voice_freq >> osc_inst.freq_in
gate >> env.gate
osc_inst >> env[1]
{ env }
}
patches {
voice: lead { freq: 880.0, attack: 200.0 }
}
midi {
poly_voice { chan: 0 }
}
poly_voice.freq >> lead.voice_freq
poly_voice.gate >> lead.gate
{ lead }
"#;
let graph = parse_and_lower(src);
assert_eq!(graph.node_count(), 4);
for alias in [
"lead.osc_inst.modulator",
"lead.osc_inst.carrier",
"lead.env",
] {
assert!(
graph.find_node_by_alias(alias).is_some(),
"missing {}",
alias
);
}
assert_eq!(
get_param(&graph, "lead.osc_inst.carrier", "freq"),
Value::F32(880.0)
);
assert_eq!(get_param(&graph, "lead.env", "attack"), Value::F32(200.0));
let carrier = graph.find_node_by_alias("lead.osc_inst.carrier").unwrap();
let env = graph.find_node_by_alias("lead.env").unwrap();
let mod_to_carrier =
graph.find_edges_between("lead.osc_inst.modulator", "lead.osc_inst.carrier");
assert_eq!(mod_to_carrier.len(), 1, "expected modulator->carrier edge");
assert_eq!(mod_to_carrier[0].sink_port, Port::Index(0));
let osc_to_env = graph.find_edges_between("lead.osc_inst.carrier", "lead.env");
assert_eq!(osc_to_env.len(), 1, "expected carrier->env edge");
assert_eq!(osc_to_env[0].sink_port, Port::Index(1));
let poly_edges = graph.find_edges_from("poly_voice");
let freq_edge = poly_edges
.iter()
.find(|e| e.source_port == Port::Named("freq".into()))
.expect("poly_voice.freq edge missing");
assert_eq!(
freq_edge.sink, carrier.id,
"poly_voice.freq should route to lead.osc_inst.carrier"
);
assert_eq!(freq_edge.sink_port, Port::Named("freq".into()));
let gate_edge = poly_edges
.iter()
.find(|e| e.source_port == Port::Named("gate".into()))
.expect("poly_voice.gate edge missing");
assert_eq!(
gate_edge.sink, env.id,
"poly_voice.gate should route to lead.env"
);
assert_eq!(gate_edge.sink_port, Port::Named("gate".into()));
assert_eq!(graph.edge_count(), 4);
}
#[test]
fn test_e2e_passthrough_via_sink_no_virtual_port() {
let src = r#"
patch voice(freq = 440.0) {
audio {
sine: osc { freq: $freq },
adsr: env { attack: 100.0 }
}
osc >> env[1]
{ env }
}
patches {
voice: v1 {}
}
audio {
track_mixer: mixer { tracks: 1 }
}
v1 >> mixer
{ mixer }
"#;
let graph = parse_and_lower(src);
let edges_to_mixer = graph.find_edges_to("mixer");
assert_eq!(
edges_to_mixer.len(),
1,
"expected exactly one edge into mixer"
);
let env = graph.find_node_by_alias("v1.env").expect("v1.env missing");
assert_eq!(
edges_to_mixer[0].source, env.id,
"passthrough should originate from v1.env (the voice sink leaf), not the macro alias"
);
}
#[test]
fn test_e2e_default_params_used_when_not_overridden() {
let src = r#"
patch osc_unit(freq = 220.0, gain = 0.5) {
audio {
sine { freq: $freq }
}
{ sine }
}
patches {
osc_unit: a { freq: 880.0 }, // overrides freq
osc_unit: b {} // use defaults
}
{ a }
"#;
let graph = parse_and_lower(src);
assert_eq!(get_param(&graph, "a.sine", "freq"), Value::F32(880.0));
assert_eq!(get_param(&graph, "b.sine", "freq"), Value::F32(220.0));
}
#[test]
fn test_e2e_multi_nodes() {
let src = r#"
patch fm(freq = 220.0) {
audio {
sine: carrier { freq: $freq },
sine: mod { freq: $freq }
}
carrier >> mod.freq
{ mod }
}
// Fake shitty reverb patch
patch reverb(
chans = 2,
gain = 1.0
) {
in audio_in
audio {
allpass * 4 { delay_length: 20, feedback: 0.5, chans: $chans },
gain { val: $gain, chans: $chans }
}
audio_in >> allpass(0)
allpass(0) >> allpass(1)
allpass(1) >> allpass(2)
allpass(2) >> allpass(3)
// TODO: Return sink by node selector index
allpass(3) >> gain
{ gain }
}
audio {
fm * 8 { freq: 880.0 },
track_mixer { tracks: 8, chans_per_track: 1 },
reverb { gain: 1.0, chans: 2 }
}
fm(*) >> track_mixer >> reverb
{ reverb }
"#;
let graph = parse_and_lower(src);
let nodes: Vec<_> = graph.topological_sort();
dbg!(&nodes);
assert_eq!(nodes.iter().len(), 22);
let mixer_edges = graph.find_edges_from("track_mixer");
assert_eq!(
mixer_edges.len(),
1,
"expected exactly one edge out of track_mixer"
);
let allpass_0 = graph
.find_node_by_alias("reverb.allpass.0")
.expect("reverb.allpass.0 missing");
assert_eq!(
mixer_edges[0].sink, allpass_0.id,
"track_mixer should connect to reverb.allpass.0 via the audio_in virtual port"
);
let reverb_gain = graph.find_node_by_alias("reverb.gain").unwrap();
assert_eq!(
graph.sink,
Some(reverb_gain.id),
"graph sink should be reverb.gain"
);
assert_eq!(graph.edge_count(), 21);
}
#[test]
fn test_e2e_kitchen_sink() {
let src = r#"
// Level 1: leaf wrapper
patch osc(freq = 440.0) {
audio {
sine { freq: $freq }
}
{ sine }
}
// Level 2: three osc macros panned into a mixer
patch triad(root = 220.0) {
audio {
osc: r { freq: $root },
osc: f { freq: $root },
osc: o { freq: $root },
mixer { tracks: 3 }
}
r >> mixer[0]
f >> mixer[1]
o >> mixer[2]
{ mixer }
}
// Top level: two named triads + four spawned triads -> master gain
patches {
triad: chord_lo { root: 110.0 },
triad: chord_hi { root: 880.0 },
triad * 4 { root: 440.0 }
}
audio {
gain { val: 0.5 }
}
chord_lo >> gain
chord_hi >> gain
triad(*) >> gain
{ gain }
"#;
let graph = parse_and_lower(src);
assert_eq!(graph.node_count(), 25);
for prefix in ["chord_lo", "chord_hi"] {
for leaf in ["r.sine", "f.sine", "o.sine", "mixer"] {
let alias = format!("{prefix}.{leaf}");
assert!(
graph.find_node_by_alias(&alias).is_some(),
"missing {alias}"
);
}
}
for i in 0..4 {
for leaf in ["r.sine", "f.sine", "o.sine", "mixer"] {
let alias = format!("triad.{i}.{leaf}");
assert!(
graph.find_node_by_alias(&alias).is_some(),
"missing {alias}"
);
}
}
assert!(graph.find_node_by_alias("gain").is_some(), "missing gain");
assert_eq!(
get_param(&graph, "chord_lo.r.sine", "freq"),
Value::F32(110.0)
);
assert_eq!(
get_param(&graph, "chord_lo.f.sine", "freq"),
Value::F32(110.0)
);
assert_eq!(
get_param(&graph, "chord_hi.r.sine", "freq"),
Value::F32(880.0)
);
for i in 0..4 {
assert_eq!(
get_param(&graph, &format!("triad.{i}.r.sine"), "freq"),
Value::F32(440.0),
"triad.{i}.r.sine freq wrong"
);
}
for prefix in ["chord_lo", "chord_hi"] {
for (osc, slot) in [("r", 0usize), ("f", 1), ("o", 2)] {
let edges = graph.find_edges_between(
&format!("{prefix}.{osc}.sine"),
&format!("{prefix}.mixer"),
);
assert_eq!(
edges.len(),
1,
"expected {prefix}.{osc}.sine -> {prefix}.mixer"
);
assert_eq!(edges[0].sink_port, Port::Index(slot));
}
}
for i in 0..4 {
for (osc, slot) in [("r", 0usize), ("f", 1), ("o", 2)] {
let edges = graph.find_edges_between(
&format!("triad.{i}.{osc}.sine"),
&format!("triad.{i}.mixer"),
);
assert_eq!(
edges.len(),
1,
"expected triad.{i}.{osc}.sine -> triad.{i}.mixer"
);
assert_eq!(edges[0].sink_port, Port::Index(slot));
}
}
let gain_edges = graph.find_edges_to("gain");
assert_eq!(gain_edges.len(), 6, "expected 6 edges into gain");
let gain_id = graph.find_node_by_alias("gain").unwrap().id;
for prefix in ["chord_lo", "chord_hi"] {
let mixer = graph
.find_node_by_alias(&format!("{prefix}.mixer"))
.unwrap();
assert!(
gain_edges.iter().any(|e| e.source == mixer.id),
"{prefix}.mixer -> gain edge missing"
);
}
for i in 0..4 {
let mixer = graph
.find_node_by_alias(&format!("triad.{i}.mixer"))
.unwrap();
assert!(
gain_edges.iter().any(|e| e.source == mixer.id),
"triad.{i}.mixer -> gain edge missing"
);
}
assert_eq!(graph.edge_count(), 24);
assert_eq!(graph.sink, Some(gain_id), "graph sink should be gain");
}
#[test]
fn test_e2e_complex_ports() {
let src = r#"
patch channel(gain = 1.0) {
in audio_in
audio {
amp { val: $gain }
}
audio_in >> amp
{ amp }
}
audio {
sine * 6 { freq: 440.0 },
sine: lfo { freq: 0.5 },
channel: ch_a { gain: 0.8 },
channel: ch_b { gain: 0.6 },
track_mixer: mixer { tracks: 6, chans_per_track: 1 }
}
// Named virtual port + Index source selector:
// only sine instance 0 feeds ch_a, only instance 1 feeds ch_b.
sine(0) >> ch_a.audio_in
sine(1) >> ch_b.audio_in
// Single source broadcast to a Range of sinks via named port:
// lfo modulates only sine.2 through sine.5, not sine.0 or sine.1.
lfo >> sine(2..6).freq
// Slice: range-selected sources -> contiguous indexed mixer inputs.
// sine(2..6) gives 4 sources; mixer[0..4] gives 4 slots -> zip.
sine(2..6) >> mixer[0..4]
// Port::Index on outgoing macro edges:
// ch_a and ch_b sinks wire to specific mixer slots.
ch_a >> mixer[4]
ch_b >> mixer[5]
{ mixer }
"#;
let graph = parse_and_lower(src);
assert_eq!(graph.node_count(), 10);
for i in 0..6 {
assert!(
graph.find_node_by_alias(&format!("sine.{i}")).is_some(),
"missing sine.{i}"
);
}
assert!(graph.find_node_by_alias("lfo").is_some(), "missing lfo");
assert!(
graph.find_node_by_alias("ch_a.amp").is_some(),
"missing ch_a.amp"
);
assert!(
graph.find_node_by_alias("ch_b.amp").is_some(),
"missing ch_b.amp"
);
assert!(graph.find_node_by_alias("mixer").is_some(), "missing mixer");
let edges = graph.find_edges_between("sine.0", "ch_a.amp");
assert_eq!(edges.len(), 1, "expected sine.0 -> ch_a.amp");
assert_eq!(edges[0].source_port, Port::None);
assert_eq!(edges[0].sink_port, Port::None);
let edges = graph.find_edges_between("sine.1", "ch_b.amp");
assert_eq!(edges.len(), 1, "expected sine.1 -> ch_b.amp");
for i in 2..6 {
assert!(
graph
.find_edges_between(&format!("sine.{i}"), "ch_a.amp")
.is_empty(),
"sine.{i} should not connect to ch_a.amp"
);
assert!(
graph
.find_edges_between(&format!("sine.{i}"), "ch_b.amp")
.is_empty(),
"sine.{i} should not connect to ch_b.amp"
);
}
for i in 2..6 {
let edges = graph.find_edges_between("lfo", &format!("sine.{i}"));
assert_eq!(edges.len(), 1, "expected lfo -> sine.{i}");
assert_eq!(
edges[0].sink_port,
Port::Named("freq".into()),
"lfo -> sine.{i} should target port 'freq'"
);
}
assert!(
graph.find_edges_between("lfo", "sine.0").is_empty(),
"lfo should not reach sine.0"
);
assert!(
graph.find_edges_between("lfo", "sine.1").is_empty(),
"lfo should not reach sine.1"
);
let mixer_edges = graph.find_edges_to("mixer");
for (slot, src_i) in (2..6usize).enumerate() {
let src_alias = format!("sine.{src_i}");
let src_id = graph.find_node_by_alias(&src_alias).unwrap().id;
let edge = mixer_edges
.iter()
.find(|e| e.source == src_id && e.sink_port == Port::Index(slot))
.unwrap_or_else(|| panic!("{src_alias} -> mixer[{slot}] missing"));
assert_eq!(edge.source_port, Port::None);
}
let ch_a_edges = graph.find_edges_between("ch_a.amp", "mixer");
assert_eq!(ch_a_edges.len(), 1, "expected ch_a.amp -> mixer");
assert_eq!(ch_a_edges[0].sink_port, Port::Index(4));
let ch_b_edges = graph.find_edges_between("ch_b.amp", "mixer");
assert_eq!(ch_b_edges.len(), 1, "expected ch_b.amp -> mixer");
assert_eq!(ch_b_edges[0].sink_port, Port::Index(5));
let mixer_id = graph.find_node_by_alias("mixer").unwrap().id;
assert_eq!(graph.sink, Some(mixer_id), "graph sink should be mixer");
assert_eq!(graph.edge_count(), 12);
}
#[test]
fn test_e2e_stride_and_slice_port_resolution() {
let src = r#"
patch voice(freq = 440.0) {
in freq gate
audio {
sine { freq: $freq },
adsr { attack: 100.0, chans: 1 }
}
freq >> sine.freq
gate >> adsr.gate
sine >> adsr[1]
{ adsr }
}
patches {
voice * 5 {}
}
audio {
track_mixer: osc_mixer { tracks: 5, chans_per_track: 1 },
track_mixer: out_mixer { tracks: 2, chans_per_track: 1 }
}
midi {
poly_voice { chan: 0, voices: 5 }
}
// Stride: poly_voice[0], [3], [6], [9], [12] → voice.0..4.gate
// i.e. start=0, end=12, stride=3 zipped against 5 instances
poly_voice[0:12:3] >> voice(*).gate
// Stride: poly_voice[1], [4], [7], [10], [13] → voice.0..4.freq
poly_voice[1:13:3] >> voice(*).freq
// Slice: voice.0..4 adsr sinks → osc_mixer[0..5]
voice(*) >> osc_mixer[0..5]
// Stride on outgoing: osc_mixer sinks → out_mixer[0], [2] (stride=2)
// (contrived but exercises the stride outgoing path)
osc_mixer >> out_mixer
{ out_mixer }
"#;
let graph = parse_and_lower(src);
assert_eq!(graph.node_count(), 13);
let gate_port_indices = [0usize, 3, 6, 9, 12];
for (i, &port_index) in gate_port_indices.iter().enumerate() {
let edges = graph.find_edges_between("poly_voice", &format!("voice.{i}.adsr"));
let gate_edge = edges
.iter()
.find(|e| e.sink_port == Port::Named("gate".into()))
.unwrap_or_else(|| panic!("poly_voice → voice.{i}.adsr gate edge missing"));
assert_eq!(
gate_edge.source_port,
Port::Index(port_index),
"poly_voice → voice.{i}.adsr should use source Port::Index({port_index})"
);
}
let freq_port_indices = [1usize, 4, 7, 10, 13];
for (i, &port_index) in freq_port_indices.iter().enumerate() {
let edges = graph.find_edges_between("poly_voice", &format!("voice.{i}.sine"));
let freq_edge = edges
.iter()
.find(|e| e.sink_port == Port::Named("freq".into()))
.unwrap_or_else(|| panic!("poly_voice → voice.{i}.sine freq edge missing"));
assert_eq!(
freq_edge.source_port,
Port::Index(port_index),
"poly_voice → voice.{i}.sine should use source Port::Index({port_index})"
);
}
for (i, &port_index) in gate_port_indices.iter().enumerate() {
for j in 0..5usize {
if i == j {
continue;
}
let edges = graph.find_edges_between("poly_voice", &format!("voice.{j}.adsr"));
assert!(
!edges
.iter()
.any(|e| e.source_port == Port::Index(port_index)
&& e.sink_port == Port::Named("gate".into())),
"Port::Index({port_index}) should only reach voice.{i}.adsr, not voice.{j}.adsr"
);
}
}
for i in 0..5usize {
let edges = graph.find_edges_between(&format!("voice.{i}.adsr"), "osc_mixer");
assert_eq!(edges.len(), 1, "expected voice.{i}.adsr → osc_mixer");
assert_eq!(
edges[0].sink_port,
Port::Index(i),
"voice.{i}.adsr should connect to osc_mixer[{i}]"
);
}
let mixer_edges = graph.find_edges_between("osc_mixer", "out_mixer");
assert_eq!(
mixer_edges.len(),
1,
"expected exactly one osc_mixer → out_mixer edge"
);
let out_mixer_id = graph.find_node_by_alias("out_mixer").unwrap().id;
assert_eq!(
graph.sink,
Some(out_mixer_id),
"graph sink should be out_mixer"
);
assert_eq!(graph.edge_count(), 21);
}
#[test]
fn test_e2e_virtual_port_fans_out_to_multiple_internal_nodes() {
let src = r#"
patch fm_voice(freq = 440.0) {
in freq
audio {
mult: freq_mult,
add: fm_add,
sine: carrier { freq: $freq }
}
freq >> freq_mult[0]
freq >> fm_add[0]
freq_mult >> carrier.freq
fm_add >> carrier[0]
{ carrier }
}
patches {
fm_voice: v { freq: 880.0 }
}
audio {
sine: lfo { freq: 2.0 }
}
lfo >> v.freq
{ v }
"#;
let graph = parse_and_lower(src);
let to_freq_mult = graph.find_edges_between("lfo", "v.freq_mult");
assert_eq!(to_freq_mult.len(), 1, "lfo -> v.freq_mult missing");
assert_eq!(to_freq_mult[0].sink_port, Port::Index(0));
let to_fm_add = graph.find_edges_between("lfo", "v.fm_add");
assert_eq!(to_fm_add.len(), 1, "lfo -> v.fm_add missing");
assert_eq!(to_fm_add[0].sink_port, Port::Index(0));
let freq_mult_to_carrier = graph.find_edges_between("v.freq_mult", "v.carrier");
assert_eq!(freq_mult_to_carrier.len(), 1);
assert_eq!(
freq_mult_to_carrier[0].sink_port,
Port::Named("freq".into())
);
let fm_add_to_carrier = graph.find_edges_between("v.fm_add", "v.carrier");
assert_eq!(fm_add_to_carrier.len(), 1);
assert_eq!(fm_add_to_carrier[0].sink_port, Port::Index(0));
assert_eq!(graph.edge_count(), 4);
}
#[test]
fn test_e2e_two_virtual_ports_each_fan_out_without_cross_contamination() {
let src = r#"
patch dual(freq = 440.0, attack = 100.0) {
in freq gate
audio {
mult: freq_mult,
add: fm_add,
adsr: env { attack: $attack },
mult: env_scale
}
freq >> freq_mult[0]
freq >> fm_add[0]
gate >> env.gate
gate >> env_scale[0]
freq_mult >> env[1]
{ env }
}
patches {
dual * 3 { freq: 220.0, attack: 50.0 }
}
midi {
poly_voice { chan: 0, voices: 3 }
}
poly_voice[0:6:2] >> dual(*).gate
poly_voice[1:7:2] >> dual(*).freq
{ dual }
"#;
let graph = parse_and_lower(src);
assert_eq!(graph.node_count(), 13);
for i in 0..3usize {
let prefix = format!("dual.{i}");
let to_freq_mult =
graph.find_edges_between("poly_voice", &format!("{prefix}.freq_mult"));
assert_eq!(
to_freq_mult.len(),
1,
"poly_voice -> {prefix}.freq_mult missing"
);
assert_eq!(to_freq_mult[0].sink_port, Port::Index(0));
let to_fm_add = graph.find_edges_between("poly_voice", &format!("{prefix}.fm_add"));
assert_eq!(to_fm_add.len(), 1, "poly_voice -> {prefix}.fm_add missing");
assert_eq!(to_fm_add[0].sink_port, Port::Index(0));
let to_env = graph.find_edges_between("poly_voice", &format!("{prefix}.env"));
let gate_edge = to_env
.iter()
.find(|e| e.sink_port == Port::Named("gate".into()))
.unwrap_or_else(|| panic!("poly_voice -> {prefix}.env gate edge missing"));
assert_eq!(gate_edge.source_port, Port::Index(i * 2));
let to_env_scale =
graph.find_edges_between("poly_voice", &format!("{prefix}.env_scale"));
assert_eq!(
to_env_scale.len(),
1,
"poly_voice -> {prefix}.env_scale missing"
);
assert_eq!(to_env_scale[0].sink_port, Port::Index(0));
assert!(
graph
.find_edges_between("poly_voice", &format!("{prefix}.env"))
.iter()
.all(|e| e.sink_port == Port::Named("gate".into())),
"freq port bled into {prefix}.env via wrong sink_port"
);
assert!(
graph
.find_edges_between("poly_voice", &format!("{prefix}.freq_mult"))
.iter()
.all(|e| e.sink_port == Port::Index(0)),
"gate port bled into {prefix}.freq_mult"
);
let interior =
graph.find_edges_between(&format!("{prefix}.freq_mult"), &format!("{prefix}.env"));
assert_eq!(
interior.len(),
1,
"{prefix}.freq_mult -> {prefix}.env missing"
);
assert_eq!(interior[0].sink_port, Port::Index(1));
assert_eq!(
get_param(&graph, &format!("{prefix}.env"), "attack"),
Value::F32(50.0)
);
}
assert_eq!(graph.edge_count(), 15);
}
}