use std::collections::BTreeMap;
use rete_core::{
build_pyramid_meta, eval_sparql, write_dataset, write_file, DictionaryBuilder,
GraphIndexBuilder, Rete, SliceReader, DEFAULT_TILE_BUDGET,
};
fn exercise(rete: &Rete) {
let _ = rete.dump(None);
let _ = rete.graph_names();
for g in rete.graph_names().to_vec() {
let _ = rete.dump(Some(g));
}
let _ = rete.query(None, None, None);
let _ = eval_sparql(rete, "SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10");
let _ = eval_sparql(
rete,
"PREFIX ex: <http://ex/> SELECT ?x WHERE { ?x ex:knows+ ?y }",
);
}
fn iri(s: &str) -> String {
format!("<http://ex/{s}>")
}
fn valid_image() -> Vec<u8> {
let triples = [
("Alice", "type", "Person", None),
("Bob", "type", "Person", None),
("Alice", "knows", "Bob", Some("g")),
("Bob", "knows", "Carol", Some("g")),
];
let mut db = DictionaryBuilder::new();
for (s, p, o, _) in triples {
db.observe(&iri(s), &iri(p), &iri(o));
}
let dict = db.build();
let mut def = GraphIndexBuilder::new();
let mut named: BTreeMap<String, GraphIndexBuilder> = BTreeMap::new();
for (s, p, o, g) in triples {
let t = dict.encode(&iri(s), &iri(p), &iri(o)).unwrap();
match g {
None => def.push(t),
Some(name) => named.entry(iri(name)).or_default().push(t),
}
}
let named_idx: Vec<(String, _)> = named.into_iter().map(|(g, b)| (g, b.build())).collect();
write_dataset(&dict, &def.build(), &named_idx, true, &[], 0)
}
fn valid_image_with_pyramid() -> Vec<u8> {
let mut edges: Vec<(u32, u32)> = Vec::new();
for c in 0..2u32 {
let base = c * 8;
for i in 0..8u32 {
for j in 0..8u32 {
if i != j {
edges.push((base + i, base + j));
}
}
}
}
edges.push((0, 8));
let node = |n: u32| iri(&format!("n{n}"));
let knows = iri("knows");
let mut db = DictionaryBuilder::new();
for &(s, o) in &edges {
db.observe(&node(s), &knows, &node(o));
}
let dict = db.build();
let ids: Vec<_> = edges
.iter()
.map(|&(s, o)| dict.encode(&node(s), &knows, &node(o)).unwrap())
.collect();
let mut ib = GraphIndexBuilder::new();
for &t in &ids {
ib.push(t);
}
let (meta, levels) = build_pyramid_meta(&dict, &ids, DEFAULT_TILE_BUDGET);
write_file(&dict, &ib.build(), false, &meta, levels)
}
#[test]
fn pyramid_image_corruption_never_panics() {
let image = valid_image_with_pyramid();
assert!(Rete::open(&image).unwrap().pyramid().is_some());
for len in 0..image.len() {
if let Ok(r) = Rete::open(&image[..len]) {
exercise(&r);
}
}
let probes: [u8; 4] = [0x00, 0xff, 0x7f, 0x80];
for i in 0..image.len() {
for &v in &probes {
let mut bad = image.clone();
if bad[i] == v {
continue;
}
bad[i] = v;
if let Ok(r) = Rete::open(&bad) {
exercise(&r);
}
}
}
}
#[test]
fn truncation_never_panics() {
let image = valid_image();
assert!(Rete::open(&image).is_ok());
for len in 0..image.len() {
let prefix = &image[..len];
if let Ok(r) = Rete::open(prefix) {
exercise(&r);
}
let _ = Rete::open_ranged(&SliceReader::new(prefix));
}
}
#[test]
fn corruption_never_panics() {
let image = valid_image();
let probes: [u8; 5] = [0x00, 0x01, 0xff, 0x7f, 0x80];
for i in 0..image.len() {
for &v in &probes {
let mut bad = image.clone();
if bad[i] == v {
continue;
}
bad[i] = v;
if let Ok(r) = Rete::open(&bad) {
exercise(&r);
}
let _ = Rete::open_ranged(&SliceReader::new(&bad));
}
}
}
#[test]
fn arbitrary_bytes_never_panic() {
for len in 0..600usize {
let bytes: Vec<u8> = (0..len).map(|k| ((k * 31 + 7) & 0xff) as u8).collect();
if let Ok(r) = Rete::open(&bytes) {
exercise(&r);
}
let _ = Rete::open_ranged(&SliceReader::new(&bytes));
}
}