#![forbid(unsafe_code)]
use std::fs;
use std::panic;
use std::path::PathBuf;
use std::thread::sleep;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use fsci_runtime::{assert_close_matrix, assert_close_slice, casp_now_unix_ms};
use serde::Serialize;
const PACKET_ID: &str = "FSCI-P2C-007";
#[derive(Debug, Clone, Serialize)]
struct CaseDiff {
case_id: String,
pass: bool,
note: String,
}
#[derive(Debug, Clone, Serialize)]
struct DiffLog {
test_id: String,
category: String,
case_count: usize,
pass: bool,
timestamp_ms: u128,
duration_ns: u128,
cases: Vec<CaseDiff>,
}
fn output_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("fixtures/artifacts/{PACKET_ID}/diff"))
}
fn ensure_output_dir() {
fs::create_dir_all(output_dir()).expect("create assert_helpers diff dir");
}
fn timestamp_ms() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_millis())
}
fn emit_log(log: &DiffLog) {
ensure_output_dir();
let path = output_dir().join(format!("{}.json", log.test_id));
let json = serde_json::to_string_pretty(log).expect("serialize log");
fs::write(path, json).expect("write log");
}
fn catch(f: impl FnOnce() + panic::UnwindSafe) -> Result<(), String> {
let prev = panic::take_hook();
panic::set_hook(Box::new(|_| {}));
let r = panic::catch_unwind(f);
panic::set_hook(prev);
r.map_err(|e| {
e.downcast_ref::<String>()
.cloned()
.or_else(|| e.downcast_ref::<&'static str>().map(|s| (*s).to_string()))
.unwrap_or_else(|| "panic (unknown payload)".into())
})
}
#[test]
fn diff_runtime_assert_close_helpers() {
let start = Instant::now();
let mut diffs: Vec<CaseDiff> = Vec::new();
{
let a = vec![1.0_f64, 2.0, 3.0];
let e = vec![1.0 + 1e-12, 2.0, 3.0 - 1e-12];
let r = catch(move || assert_close_slice(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "slice_within_tol".into(),
pass: r.is_ok(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![1.0_f64, 2.0];
let e = vec![1.0, 2.0, 3.0];
let r = catch(move || assert_close_slice(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "slice_len_mismatch".into(),
pass: r.is_err(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![1.0_f64, 2.0, 3.0];
let e = vec![1.0, 2.0, 3.5];
let r = catch(move || assert_close_slice(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "slice_value_outside_tol".into(),
pass: r.is_err(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![f64::NAN, f64::INFINITY, f64::NEG_INFINITY];
let e = vec![f64::NAN, f64::INFINITY, f64::NEG_INFINITY];
let r = catch(move || assert_close_slice(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "slice_nan_inf_match".into(),
pass: r.is_ok(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![f64::NAN];
let e = vec![0.0];
let r = catch(move || assert_close_slice(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "slice_nan_vs_finite_panics".into(),
pass: r.is_err(),
note: r.err().unwrap_or_default(),
});
}
{
let a: Vec<f64> = vec![];
let e: Vec<f64> = vec![];
let r = catch(move || assert_close_slice(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "slice_empty_ok".into(),
pass: r.is_ok(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![vec![1.0_f64, 2.0], vec![3.0, 4.0]];
let e = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
let r = catch(move || assert_close_matrix(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "matrix_match".into(),
pass: r.is_ok(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![vec![1.0_f64, 2.0]];
let e = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
let r = catch(move || assert_close_matrix(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "matrix_row_mismatch".into(),
pass: r.is_err(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![vec![1.0_f64, 2.0], vec![3.0]];
let e = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
let r = catch(move || assert_close_matrix(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "matrix_col_mismatch".into(),
pass: r.is_err(),
note: r.err().unwrap_or_default(),
});
}
{
let a = vec![vec![1.0_f64, 2.0], vec![3.0, 4.0]];
let e = vec![vec![1.0, 2.0], vec![3.0, 99.0]];
let r = catch(move || assert_close_matrix(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "matrix_value_mismatch".into(),
pass: r.is_err(),
note: r.err().unwrap_or_default(),
});
}
{
let a: Vec<Vec<f64>> = vec![];
let e: Vec<Vec<f64>> = vec![];
let r = catch(move || assert_close_matrix(&a, &e, 1e-9, 1e-9));
diffs.push(CaseDiff {
case_id: "matrix_empty_ok".into(),
pass: r.is_ok(),
note: r.err().unwrap_or_default(),
});
}
let now_t = casp_now_unix_ms();
let sys_t = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_millis() as u64);
let drift_ms = now_t.abs_diff(sys_t);
diffs.push(CaseDiff {
case_id: "casp_now_nonzero_and_aligned".into(),
pass: now_t > 1_700_000_000_000 && drift_ms < 5_000,
note: format!("now_t={now_t} sys_t={sys_t} drift_ms={drift_ms}"),
});
let t1 = casp_now_unix_ms();
sleep(Duration::from_millis(10));
let t2 = casp_now_unix_ms();
diffs.push(CaseDiff {
case_id: "casp_now_monotonic_after_sleep".into(),
pass: t2 >= t1,
note: format!("t1={t1} t2={t2}"),
});
let all_pass = diffs.iter().all(|d| d.pass);
let log = DiffLog {
test_id: "diff_runtime_assert_close_helpers".into(),
category: "fsci_runtime::{assert_close_slice, assert_close_matrix, casp_now_unix_ms}"
.into(),
case_count: diffs.len(),
pass: all_pass,
timestamp_ms: timestamp_ms(),
duration_ns: start.elapsed().as_nanos(),
cases: diffs.clone(),
};
emit_log(&log);
for d in &diffs {
if !d.pass {
eprintln!("assert_helpers mismatch: {} — {}", d.case_id, d.note);
}
}
assert!(
all_pass,
"assert_close helper coverage failed: {} cases",
diffs.len(),
);
}