use std::fs;
use std::path::Path;
use alice_zip::container::{Container, ContainerError, ContainerView, Tag};
use alice_zip::law::{Provenance, SignalLaw, SEMANTICS_ID};
const SEMANTICS_ID_0_4_0: [u8; 32] = [
0xd2, 0x20, 0x9b, 0x30, 0xf6, 0xf1, 0xf4, 0x5b, 0xaa, 0x1b, 0x63, 0x8b, 0xcd, 0xfe, 0xe3, 0x4a,
0xc6, 0x47, 0x73, 0xb2, 0xe6, 0x3b, 0x9c, 0x08, 0x3b, 0x2e, 0x77, 0xaf, 0xc6, 0x91, 0x39, 0x8e,
];
const FIXTURE_SEMANTICS_ID: [u8; 32] = [0x11; 32];
fn law() -> SignalLaw {
let points: Vec<(f64, f64)> = (0..16)
.map(|i| (f64::from(i), 2.0 * f64::from(i) + 1.0))
.collect();
SignalLaw::fit_polynomial(&points, 1, Provenance::new("test", "linear")).unwrap()
}
fn lids_payload(rows: &[(u64, [u8; 32], [u8; 32])]) -> Vec<u8> {
let mut p = (rows.len() as u64).to_le_bytes().to_vec();
for (section, id, sem) in rows {
p.extend_from_slice(§ion.to_le_bytes());
p.extend_from_slice(id);
p.extend_from_slice(sem);
}
p
}
fn stamped(semantics: [u8; 32]) -> Vec<u8> {
let l = law();
let mut c = Container::new(semantics);
c.push(Tag::LAW, true, b"law-bytes".to_vec());
c.push(
Tag::LIDS,
true,
lids_payload(&[(0, l.law_id(&semantics), semantics)]),
);
c.to_bytes()
}
fn recompute(_: usize, _: &[u8], sem: &[u8; 32]) -> Option<[u8; 32]> {
Some(law().law_id(sem))
}
#[test]
fn a_container_stamped_with_the_running_arithmetic_is_trusted() {
let bytes = stamped(SEMANTICS_ID);
let view = ContainerView::open(&bytes).unwrap();
let rows = view.law_ids_for_this_arithmetic().unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].semantics_id, SEMANTICS_ID);
assert_eq!(view.verify_law_ids(recompute), Ok(()));
}
#[test]
fn a_container_stamped_by_0_4_0_opens_but_its_law_ids_are_not_trusted() {
let bytes = stamped(SEMANTICS_ID_0_4_0);
let refused = ContainerError::NotThisArithmetic {
container: SEMANTICS_ID_0_4_0,
running: SEMANTICS_ID,
};
let c = Container::from_bytes(&bytes).unwrap();
assert_eq!(c.semantics_id, SEMANTICS_ID_0_4_0);
let view = ContainerView::open(&bytes).unwrap();
assert_eq!(view.law_ids().unwrap().len(), 1);
assert_eq!(view.law_ids_unchecked_semantics().unwrap().len(), 1);
assert_eq!(view.law_ids_for_this_arithmetic(), Err(refused));
assert_eq!(view.verify_law_ids(recompute), Err(refused));
let mut called = false;
let _ = view.verify_law_ids(|_, _, _| {
called = true;
None
});
assert!(
!called,
"verify_law_ids consulted the callback before refusing"
);
}
#[test]
fn the_reference_fixture_opens_and_is_not_trusted_either() {
let bytes = stamped(FIXTURE_SEMANTICS_ID);
let view = ContainerView::open(&bytes).unwrap();
assert!(view.law_ids().is_ok());
assert_eq!(
view.law_ids_for_this_arithmetic(),
Err(ContainerError::NotThisArithmetic {
container: FIXTURE_SEMANTICS_ID,
running: SEMANTICS_ID,
})
);
}
fn law_id_readers(src: &str) -> Vec<String> {
let src = src.replace("\r\n", "\n");
let mut found = Vec::new();
let mut rest = src.as_str();
while let Some(at) = rest.find("pub fn ") {
let after = &rest[at + "pub fn ".len()..];
let end = after.find(['{', ';']).unwrap_or(after.len());
let signature = &after[..end];
if signature.contains("LawIdEntry") {
let name: String = signature
.chars()
.take_while(|c| c.is_ascii_alphanumeric() || *c == '_')
.collect();
found.push(name);
}
rest = &after[end..];
}
found.sort();
found
}
fn method_body(src: &str, name: &str) -> String {
let src = src.replace("\r\n", "\n");
let from = src
.find(&format!("pub fn {name}"))
.unwrap_or_else(|| panic!("`pub fn {name}` exists"));
let tail = &src[from..];
let end = tail.find("\n }\n").expect("end of the fn");
tail[..end].to_owned()
}
#[test]
fn the_reader_scan_sees_one_line_and_multi_line_signatures_with_crlf() {
let src = "impl V {\r\n pub fn one(&self) -> Vec<LawIdEntry> {\r\n }\r\n pub fn \
two(\r\n &self,\r\n ) -> Result<\r\n Vec<LawIdEntry>,\r\n E,\r\n > {\r\n }\r\n \
pub fn other(&self) -> u8 {\r\n }\r\n}\r\n";
assert_eq!(
law_id_readers(src),
vec!["one".to_owned(), "two".to_owned()]
);
assert!(method_body(src, "one").contains("Vec<LawIdEntry>"));
}
#[test]
fn every_reader_of_law_ids_is_classified() {
let src = fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join("src/container.rs"))
.expect("read src/container.rs");
let found = law_id_readers(&src);
let classified = [
("law_ids", false),
("law_ids_for_this_arithmetic", true),
("law_ids_unchecked_semantics", false),
];
let names: Vec<String> = classified.iter().map(|(n, _)| (*n).to_owned()).collect();
assert!(
!found.is_empty(),
"compared nothing: no reader of law ids found"
);
assert_eq!(
found, names,
"classify every pub fn that returns LawIdEntry"
);
for (name, trusts) in classified {
if trusts {
assert!(
method_body(&src, name).contains("check_semantics"),
"{name} trusts law ids but does not call check_semantics"
);
}
}
assert!(
method_body(&src, "verify_law_ids").contains("self.law_ids_for_this_arithmetic()"),
"verify_law_ids must read the rows through the trusting reader"
);
}