rdml-qpcr 0.1.1

Read, write, and validate RDML (Real-time PCR Data Markup Language) qPCR data files
Documentation
//! Completeness pin: every element and attribute declared in the 1.4 XSD
//! must appear in the kitchen-sink fixture's emitted output.
//!
//! Together with the `xsd_validation` test (libxml2 enforces `xs:sequence`
//! order on those same elements) this pins the writer's element order
//! against the schema: an element the writer forgot, misnamed, or emitted
//! in the wrong place fails one of the two tests.

mod common;

use std::collections::BTreeSet;

use common::kitchen_sink;
use rdml_qpcr::RdmlVersion;

fn xsd_declared_names(xsd: &str, what: &str) -> BTreeSet<String> {
    // The vendored XSDs are formatted one declaration per line; a light
    // scan is enough and keeps the schema itself the source of truth.
    let needle = format!("<xs:{what} name=\"");
    xsd.lines()
        .filter_map(|line| {
            let start = line.find(&needle)? + needle.len();
            line[start..].split('"').next().map(str::to_string)
        })
        .collect()
}

#[test]
fn kitchen_sink_exercises_every_1_4_element_and_attribute() {
    let xsd = std::fs::read_to_string(format!(
        "{}/schema/RDML_v1_4_CR.xsd",
        env!("CARGO_MANIFEST_DIR")
    ))
    .unwrap();
    let (xml, _) = kitchen_sink().to_xml(RdmlVersion::V1_4).unwrap();

    let elements = xsd_declared_names(&xsd, "element");
    let missing: Vec<&String> = elements
        .iter()
        .filter(|name| !xml.contains(&format!("<{name}")))
        .collect();
    assert!(
        missing.is_empty(),
        "schema elements never emitted by the kitchen sink: {missing:?}"
    );

    let attributes = xsd_declared_names(&xsd, "attribute");
    let missing: Vec<&String> = attributes
        .iter()
        .filter(|name| !xml.contains(&format!("{name}=\"")))
        .collect();
    assert!(
        missing.is_empty(),
        "schema attributes never emitted by the kitchen sink: {missing:?}"
    );
}