Files
T

183 lines
8.8 KiB
Rust

//! Incident-workspace saved-source oracle. No desktop/spacing producer is linked.
//! self-check BASE OUT | same BASE ACTUAL OUT | repair-c BASE ACTUAL OUT
//! lane BASE ACTUAL OUT VIEW LIFELINE X | message BASE ACTUAL OUT VIEW OCCURRENCE SEND_Y
use oa_core::{
DiagramLayout, Id, MAX_COORDINATE, Project, ScenarioLayout, execute_batch, source_files,
source_project::{ProjectLimits, compile_bundle},
validation::Severity,
};
use serde_json::{Value, json};
use std::{fs, io::Write, path::Path};
const ORIGINAL: &str = include_str!("../incident-workspace.arcadia");
fn id(last: u128) -> Id {
Id::from_u128(0x70200000000040008000000000000000 | last)
}
fn load(path: &Path) -> Project {
assert!(fs::symlink_metadata(path).unwrap().file_type().is_file());
let loaded = source_files::load_project(path, ProjectLimits::default()).unwrap();
assert!(
!loaded.diagnostics.iter().any(|d| d.diagnostic.severity == Severity::Error),
"source loader errors: {:?}", loaded.diagnostics
);
let raw = loaded.reachable_bundle();
assert_eq!(raw.files.len(), 1, "This incident oracle admits one source file only");
let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
assert_eq!(compiled.bundle, raw, "Saved source must need no migration/identity insertion");
execute_batch(&compiled.decoded.project, &[]).unwrap();
let project = compiled.decoded.project;
assert_eq!(project.id, id(1), "Wrong incident fixture Project");
assert_eq!(project.diagrams.len(), 3, "Expected the incident fixture's three Saved views");
project
}
fn layout(project: &mut Project, view: Id) -> &mut ScenarioLayout {
let diagram = project.diagrams.get_mut(&view).expect("Missing chosen saved view");
assert_eq!(diagram.projection.scenario(), Some(id(0x10)), "Wrong view owner");
let Some(DiagramLayout::Scenario(layout)) = &mut diagram.layout else {
panic!("This fixture comparison requires an existing Scenario payload")
};
layout
}
fn lane(project: &mut Project, view: Id, target: Id, x: f32) -> f32 {
assert!(x.is_finite() && x.abs() <= MAX_COORDINATE);
let rows: Vec<_> = layout(project, view).lanes.iter_mut()
.filter(|row| row.lifeline == target).collect();
assert_eq!(rows.len(), 1, "Exactly one existing owned lane override required");
let row = rows.into_iter().next().unwrap();
std::mem::replace(&mut row.x, x)
}
fn message(project: &mut Project, view: Id, target: Id, y: f32) -> f32 {
assert!(y.is_finite() && y.abs() <= MAX_COORDINATE);
let rows: Vec<_> = layout(project, view).messages.iter_mut()
.filter(|row| row.address.references.is_empty() && row.address.occurrence == target).collect();
assert_eq!(rows.len(), 1, "Exactly one existing direct occurrence override required");
let row = rows.into_iter().next().unwrap();
std::mem::replace(&mut row.send_y, y)
}
// Recursively sort object keys. Array order and every serialized scalar remain exact.
// Comparing serialized scalars also distinguishes -0.0 from 0.0, unlike float PartialEq.
fn canonical(value: &Value) -> String {
match value {
Value::Object(map) => {
let mut keys: Vec<_> = map.keys().collect();
keys.sort();
format!("{{{}}}", keys.into_iter().map(|key| {
format!("{}:{}", serde_json::to_string(key).unwrap(), canonical(&map[key]))
}).collect::<Vec<_>>().join(","))
}
Value::Array(items) => format!("[{}]", items.iter().map(canonical).collect::<Vec<_>>().join(",")),
_ => serde_json::to_string(value).unwrap(),
}
}
fn full(project: &Project) -> String {
canonical(&serde_json::to_value(project).unwrap())
}
fn put(path: &Path, bytes: &[u8]) {
let mut file = fs::OpenOptions::new().write(true).create_new(true).open(path).unwrap();
file.write_all(bytes).unwrap();
file.sync_all().unwrap();
}
fn self_check(base: &Project, out: &Path) {
let mut expected = base.clone();
assert_eq!(message(&mut expected, id(0x42), id(0x23), 424.), 300.);
assert_ne!(full(base), full(&expected));
assert_eq!(message(&mut expected, id(0x42), id(0x23), 300.), 424.);
assert_eq!(full(base), full(&expected));
assert_eq!(lane(&mut expected, id(0x40), id(0x12), 700.), 640.);
let lane_expected = full(&expected);
let mut wrong_view = expected.clone();
lane(&mut wrong_view, id(0x41), id(0x12), 700.);
assert_ne!(lane_expected, full(&wrong_view));
let mut wrong_semantics = expected.clone();
wrong_semantics.elements.get_mut(&id(0x21)).unwrap().name.push_str(" changed");
assert_ne!(lane_expected, full(&wrong_semantics));
let reordered_text = ORIGINAL.replacen("steps [@initial_report, @updated_report,", "steps [@updated_report, @initial_report,", 1);
assert_ne!(reordered_text, ORIGINAL);
let reordered = oa_core::language::parse(&reordered_text).unwrap();
assert_ne!(full(base), full(&reordered));
assert_eq!(lane(&mut expected, id(0x40), id(0x12), 640.), 700.);
assert_eq!(full(base), full(&expected));
assert_eq!(message(&mut expected, id(0x40), id(0x21), 272.), 252.);
let mut wrong_occurrence = expected.clone();
message(&mut wrong_occurrence, id(0x40), id(0x20), 272.);
assert_ne!(full(&expected), full(&wrong_occurrence));
let mut signed_zero = base.clone();
lane(&mut signed_zero, id(0x40), id(0x12), -0.0);
let negative = full(&signed_zero);
lane(&mut signed_zero, id(0x40), id(0x12), 0.0);
assert_ne!(negative, full(&signed_zero));
put(&out.join("self-check.json"), serde_json::to_string_pretty(&json!({
"status": "passed", "scope": "Oracle admission only; no native result",
"checks": ["repair_C_exact_300_to_424_and_inverse", "lane_A_only_and_inverse",
"other_saved_view_mutation_rejected", "semantic_name_mutation_rejected", "source_steps_reorder_rejected",
"repeated_exchange_occurrence_distinguished", "signed_zero_distinguished"]
})).unwrap().as_bytes());
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let mode = args.get(1).expect("Expected self-check, same, repair-c, lane or message");
let self_check_mode = mode == "self-check";
let count = match mode.as_str() {
"self-check" => 4, "same" | "repair-c" => 5, "lane" | "message" => 8,
_ => panic!("Unknown comparison mode"),
};
assert_eq!(args.len(), count, "Wrong exact argument count");
let base_path = Path::new(&args[2]);
let out = Path::new(&args[if self_check_mode {3} else {4}]);
fs::create_dir(out).expect("Output directory must be new; preserve previous evidence");
let base = load(base_path);
put(&out.join("baseline.project.json"), full(&base).as_bytes());
if self_check_mode {
assert_eq!(fs::read_to_string(base_path).unwrap(), ORIGINAL, "Self-check requires exact embedded original");
self_check(&base, out);
println!("Oracle self-check passed; no native acceptance");
return;
}
let actual = load(Path::new(&args[3]));
let mut expected = base.clone();
let change = match mode.as_str() {
"same" => json!({"kind": "none"}),
"repair-c" => {
let old = message(&mut expected, id(0x42), id(0x23), 424.);
assert_eq!(old.to_bits(), 300_f32.to_bits(), "Repair must begin with C's exact 300");
json!({"kind": "message", "view": id(0x42), "target": id(0x23), "before": old, "after": 424.0})
}
"lane" | "message" => {
let view = args[5].parse::<Id>().unwrap();
let target = args[6].parse::<Id>().unwrap();
let value = args[7].parse::<f32>().unwrap();
let old = if mode == "lane" {lane(&mut expected, view, target, value)}
else {message(&mut expected, view, target, value)};
assert_ne!(old.to_bits(), value.to_bits(), "Use same for an unchanged/no-op check");
json!({"kind": mode, "view": view, "target": target, "before": old, "after": value,
"before_f32_bits": old.to_bits(), "after_f32_bits": value.to_bits()})
}
_ => unreachable!(),
};
execute_batch(&expected, &[]).unwrap();
let expected_json = full(&expected);
let actual_json = full(&actual);
put(&out.join("expected.project.json"), expected_json.as_bytes());
put(&out.join("actual.project.json"), actual_json.as_bytes());
let equal = expected_json == actual_json;
put(&out.join("comparison.json"), serde_json::to_string_pretty(&json!({
"status": if equal {"passed"} else {"failed"}, "mode": mode,
"allowed_change": change, "full_project_exact": equal,
"scope": "Complete parsed saved Project; no runtime Scene, editor buffer, camera or history-stack claim"
})).unwrap().as_bytes());
assert!(equal, "Unexpected complete Project difference; inspect expected.project.json and actual.project.json");
println!("Full saved Project comparison passed: {mode}");
}