use anyhow::Result; use oxigraph::model::{Dataset, Graph, NamedNode}; use std::fmt::Write; use text_diff::{diff, Difference}; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; #[derive(Debug)] pub struct TestResult { pub test: NamedNode, pub outcome: Result<()>, pub date: OffsetDateTime, } pub(super) fn dataset_diff(expected: &Dataset, actual: &Dataset) -> String { format_diff( &normalize_dataset_text(expected), &normalize_dataset_text(actual), "quads", ) } fn normalize_dataset_text(store: &Dataset) -> String { let mut quads: Vec<_> = store.iter().map(|q| q.to_string()).collect(); quads.sort(); quads.join("\n") } pub(super) fn graph_diff(expected: &Graph, actual: &Graph) -> String { format_diff( &normalize_graph_text(expected), &normalize_graph_text(actual), "triples", ) } fn normalize_graph_text(store: &Graph) -> String { let mut triples: Vec<_> = store.iter().map(|q| q.to_string()).collect(); triples.sort(); triples.join("\n") } pub(super) fn format_diff(expected: &str, actual: &str, kind: &str) -> String { let (_, changeset) = diff(expected, actual, "\n"); let mut ret = String::new(); writeln!( &mut ret, "Note: missing {kind} in yellow and extra {kind} in blue" ) .unwrap(); for seq in changeset { match seq { Difference::Same(x) => { writeln!(&mut ret, "{x}").unwrap(); } Difference::Add(x) => { writeln!(&mut ret, "\x1B[94m{x}\x1B[0m").unwrap(); } Difference::Rem(x) => { writeln!(&mut ret, "\x1B[93m{x}\x1B[0m").unwrap(); } } } ret } #[allow(unused_must_use)] pub fn build_report(results: impl IntoIterator) -> String { let mut buffer = String::new(); writeln!(&mut buffer, "@prefix dc: ."); writeln!( &mut buffer, "@prefix doap: ." ); writeln!(&mut buffer, "@prefix earl: ."); writeln!(&mut buffer, "@prefix foaf: ."); writeln!( &mut buffer, "@prefix rdf: ." ); writeln!( &mut buffer, "@prefix xsd: ." ); writeln!(&mut buffer); writeln!(&mut buffer, "<> foaf:primaryTopic ;"); writeln!( &mut buffer, "\tdc:issued \"{}\"^^xsd:dateTime ;", OffsetDateTime::now_utc().format(&Rfc3339).unwrap() ); writeln!( &mut buffer, "\tfoaf:maker ." ); writeln!(&mut buffer); writeln!( &mut buffer, " a doap:Project, earl:TestSubject, earl:Software ;" ); writeln!(&mut buffer, "\tdoap:name \"Oxigraph\" ;"); writeln!(&mut buffer, "\tdoap:release ["); writeln!( &mut buffer, "\t\tdoap:name \"Oxigraph {}\";", env!("CARGO_PKG_VERSION") ); writeln!( &mut buffer, "\t\tdoap:revision \"{}\" ;", env!("CARGO_PKG_VERSION") ); writeln!(&mut buffer, "\t] ;"); writeln!( &mut buffer, "\tdoap:developer ;" ); writeln!(&mut buffer, "\tdoap:homepage ;"); writeln!( &mut buffer, "\tdoap:description \"Oxigraph is an embedded triple store.\"@en ;" ); writeln!(&mut buffer, "\tdoap:programming-language \"Rust\" ."); writeln!(&mut buffer); writeln!( &mut buffer, " a foaf:Person, earl:Assertor ;" ); writeln!(&mut buffer, "\tfoaf:name \"Thomas Tanon\"; "); writeln!( &mut buffer, "\tfoaf:homepage ." ); writeln!(&mut buffer); for result in results { writeln!(&mut buffer); writeln!(&mut buffer, "["); writeln!(&mut buffer, "\ta earl:Assertion ;"); writeln!( &mut buffer, "\tearl:assertedBy ;" ); writeln!(&mut buffer, "\tearl:subject ;"); writeln!(&mut buffer, "\tearl:test {} ;", result.test); writeln!(&mut buffer, "\tearl:result ["); writeln!(&mut buffer, "\t\ta earl:TestResult ;"); writeln!( &mut buffer, "\t\tearl:outcome earl:{} ;", if result.outcome.is_ok() { "passed" } else { "failed" } ); writeln!( &mut buffer, "\t\tdc:date \"{}\"^^xsd:dateTime", result.date.format(&Rfc3339).unwrap() ); writeln!(&mut buffer, "\t] ;"); writeln!(&mut buffer, "\tearl:mode earl:automatic"); writeln!(&mut buffer, "] ."); } buffer }