#![allow(dead_code)]
use nord_format::formats::nsmp;
use nord_format::util::{peek, FileType};
use nord_format::{Entity, Sample};
use std::collections::BTreeSet;
use std::fs;
use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
pub fn root() -> PathBuf {
std::env::var_os("NORD_CORPUS_ROOT")
.map(PathBuf::from)
.expect("set NORD_CORPUS_ROOT to a nord-corpus checkout for --features corpus")
}
pub fn wanted(path: &Path) -> bool {
let name = path.file_name().unwrap().to_string_lossy();
if name.contains(".skip.") || name.ends_with(".oracle.json") {
return false;
}
let Ok(mut file) = fs::File::open(path) else {
return false;
};
matches!(
peek(&mut file).map(|p| p.file_type),
Ok(FileType::Cbin
| FileType::Cne3
| FileType::Midi
| FileType::SampleProject
| FileType::Sysex
| FileType::Zip)
)
}
pub type Shape = (Vec<u8>, u8);
pub fn shape(path: &Path) -> Option<Shape> {
use std::io::Read;
let mut head = [0u8; 12];
fs::File::open(path)
.and_then(|mut f| f.read_exact(&mut head))
.ok()?;
head.starts_with(b"CBIN")
.then(|| (head[8..12].to_vec(), head[4]))
}
pub fn sampled(path: &Path, seen: &mut BTreeSet<Shape>) -> bool {
crate::sidecar::sidecar_of(path).exists() || shape(path).is_none_or(|s| seen.insert(s))
}
pub fn unwritten(body: &[u8]) -> bool {
!body.is_empty() && body.iter().all(|&byte| byte == 0xff)
}
fn visit(root: &Path, each: &mut impl FnMut(PathBuf)) {
let mut stack = vec![root.to_path_buf()];
while let Some(dir) = stack.pop() {
for entry in fs::read_dir(&dir).unwrap_or_else(|e| panic!("{}: {e}", dir.display())) {
let path = entry.unwrap().path();
if path.file_name().unwrap().to_string_lossy().starts_with('.') {
continue;
}
if path.is_dir() {
stack.push(path);
} else {
each(path);
}
}
}
}
pub fn walk(root: &Path) -> (Vec<PathBuf>, Vec<PathBuf>) {
let mut specimens = Vec::new();
let mut sidecars = Vec::new();
visit(root, &mut |path| {
if path
.file_name()
.unwrap()
.to_string_lossy()
.ends_with(".oracle.json")
{
sidecars.push(path);
} else if wanted(&path) {
specimens.push(path);
}
});
specimens.sort();
sidecars.sort();
(specimens, sidecars)
}
pub struct Specimen {
pub path: PathBuf,
pub bytes: Vec<u8>,
pub entity: Entity,
}
pub struct Tree {
pub specimens: Vec<Specimen>,
pub unparsed: Vec<(PathBuf, String)>,
}
pub fn read_tree(root: &Path) -> Tree {
let (paths, _) = walk(root);
assert!(!paths.is_empty(), "no specimen under {}", root.display());
let mut tree = Tree {
specimens: Vec::new(),
unparsed: Vec::new(),
};
for path in paths {
let bytes = fs::read(&path).unwrap();
match nord_format::from_stream(&mut Cursor::new(&bytes)) {
Ok(entity) => tree.specimens.push(Specimen {
path,
bytes,
entity,
}),
Err(e) => tree.unparsed.push((path, e.to_string())),
}
}
tree
}
fn parsed(root: &Path) -> Vec<Specimen> {
let tree = read_tree(root);
if !tree.unparsed.is_empty() {
use std::io::Write;
let mut err = std::io::stderr().lock();
let _ = writeln!(
err,
"warning: {} of {} files under {} did not parse and are left out of this suite; \
`--test corpus` reports each one as a failing trial",
tree.unparsed.len(),
tree.unparsed.len() + tree.specimens.len(),
root.display()
);
for (path, error) in &tree.unparsed {
let _ = writeln!(err, " {}: {error}", path.display());
}
}
tree.specimens
}
pub fn corpus() -> &'static [Specimen] {
static CORPUS: OnceLock<Vec<Specimen>> = OnceLock::new();
CORPUS.get_or_init(|| parsed(&root()))
}
pub fn fixtures() -> &'static [Specimen] {
static FIXTURES: OnceLock<Vec<Specimen>> = OnceLock::new();
FIXTURES
.get_or_init(|| parsed(&PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")))
}
pub fn named(name: &str) -> &'static Specimen {
let mut hits = corpus()
.iter()
.filter(|s| s.path.file_name().is_some_and(|n| n == name));
let found = hits
.next()
.unwrap_or_else(|| panic!("no specimen named {name}"));
assert!(hits.next().is_none(), "more than one specimen named {name}");
found
}
pub fn named_skipped(name: &str) -> PathBuf {
let mut hits = Vec::new();
visit(&root(), &mut |path| {
if path.file_name().is_some_and(|found| found == name) {
hits.push(path);
}
});
assert_eq!(hits.len(), 1, "corpus files named {name}: {hits:?}");
hits.pop().unwrap()
}
pub fn v2_samples() -> impl Iterator<
Item = (
&'static Specimen,
&'static nord_format::cbin::Cbin<nsmp::Sample>,
),
> {
corpus()
.iter()
.filter_map(|specimen| match &specimen.entity {
Entity::Sample(Sample::V2(sample)) => Some((specimen, sample)),
_ => None,
})
}
pub fn v2_named(name: &str) -> nord_format::cbin::Cbin<nsmp::Sample> {
match nord_format::from_stream(&mut Cursor::new(&named(name).bytes)).unwrap() {
Entity::Sample(Sample::V2(sample)) => sample,
other => panic!("{name} decoded as {other:?}"),
}
}