use verovio::Toolkit;
const TWO_STAFF_MEI: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
<meiHead>
<fileDesc>
<titleStmt><title>two-staff smoke</title></titleStmt>
<pubStmt/>
</fileDesc>
</meiHead>
<music>
<body>
<mdiv>
<score>
<scoreDef>
<staffGrp>
<staffDef n="1" lines="5" clef.shape="G" clef.line="2"/>
<staffDef n="2" lines="5" clef.shape="F" clef.line="4"/>
</staffGrp>
</scoreDef>
<section>
<measure>
<staff n="1">
<layer>
<note pname="g" oct="4" dur="4" xml:id="treble-1"/>
<note pname="g" oct="4" dur="4" xml:id="treble-2"/>
<note pname="g" oct="4" dur="4" xml:id="treble-3"/>
<note pname="g" oct="4" dur="4" xml:id="treble-4"/>
</layer>
</staff>
<staff n="2">
<layer>
<note pname="c" oct="3" dur="4" xml:id="bass-1"/>
<note pname="c" oct="3" dur="4" xml:id="bass-2"/>
<note pname="c" oct="3" dur="4" xml:id="bass-3"/>
<note pname="c" oct="3" dur="4" xml:id="bass-4"/>
</layer>
</staff>
</measure>
</section>
</score>
</mdiv>
</body>
</music>
</mei>
"#;
fn parse_smf_header(bytes: &[u8]) -> (u16, u16, u16) {
assert!(bytes.len() >= 14, "SMF must be at least 14 bytes");
assert_eq!(&bytes[..4], b"MThd", "missing MThd magic");
let format = u16::from_be_bytes([bytes[8], bytes[9]]);
let ntrks = u16::from_be_bytes([bytes[10], bytes[11]]);
let division = u16::from_be_bytes([bytes[12], bytes[13]]);
(format, ntrks, division)
}
#[test]
fn two_staff_mei_produces_multi_track_smf() {
let mut tk = Toolkit::from_data(TWO_STAFF_MEI).expect("MEI parse");
let smf = tk.render_to_midi_bytes().expect("midi bytes");
let (format, ntrks, division) = parse_smf_header(&smf);
assert_eq!(
format, 1,
"expected SMF format 1 (multi-track simultaneous), got {format}"
);
assert!(
ntrks >= 2,
"expected at least 2 SMF tracks for a 2-staff score, got {ntrks}"
);
assert!(
division & 0x8000 == 0 && division > 0,
"expected metric division, got {division:#x}"
);
}
#[test]
fn two_staff_mei_renders_svg_with_both_staves() {
let mut tk = Toolkit::from_data(TWO_STAFF_MEI).expect("MEI parse");
let svg = tk.render_to_svg(1).expect("render page 1");
assert!(
svg.contains("treble-1") || svg.contains("treble-4"),
"treble notes missing from SVG"
);
assert!(
svg.contains("bass-1") || svg.contains("bass-4"),
"bass notes missing from SVG"
);
}
#[test]
fn timemap_does_not_distinguish_staves() {
let mut tk = Toolkit::from_data(TWO_STAFF_MEI).expect("MEI parse");
let timemap = tk.timemap().expect("timemap parse");
let first = &timemap[0];
assert!(
first.on.contains(&"treble-1".to_string()),
"first event missing treble-1: {first:?}"
);
assert!(
first.on.contains(&"bass-1".to_string()),
"first event missing bass-1 (timemap should fold both staves together): {first:?}"
);
}
#[test]
fn elements_at_time_zero_returns_both_staff_notes() {
let mut tk = Toolkit::from_data(TWO_STAFF_MEI).expect("MEI parse");
let elements = tk.elements_at(0).expect("elements parse");
assert!(
elements.notes.contains(&"treble-1".to_string())
&& elements.notes.contains(&"bass-1".to_string()),
"elements_at(0) should report both staves' first notes, got {elements:?}"
);
}