Record revised release requirements and native scenario oracle
This commit is contained in:
@@ -0,0 +1,672 @@
|
||||
//! Independent B2/B3 native fixture; no desktop implementation is linked.
|
||||
//! generate DIR | check CHECKPOINT ENTRY | same VERIFIED_ENTRY RESTORED_ENTRY | self-check
|
||||
use oa_core::{source_project::*, *};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
fs,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
fn id(n: u128) -> Id {
|
||||
Id::from_u128(0x5ceaa100000040008000000000000000 + n)
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
struct Side {
|
||||
layer: Layer,
|
||||
word: &'static str,
|
||||
base: u128,
|
||||
}
|
||||
const SIDES: [Side; 2] = [
|
||||
Side {
|
||||
layer: Layer::Oa,
|
||||
word: "OA",
|
||||
base: 100,
|
||||
},
|
||||
Side {
|
||||
layer: Layer::Sa,
|
||||
word: "SA",
|
||||
base: 200,
|
||||
},
|
||||
];
|
||||
impl Side {
|
||||
fn id(self, n: u128) -> Id {
|
||||
id(self.base + n)
|
||||
}
|
||||
fn name(self, text: &str) -> String {
|
||||
format!("{} {text}", self.word)
|
||||
}
|
||||
}
|
||||
fn seed(p: &mut Project, side: Side, n: u128, kind: Kind, text: &str, parent: Option<Id>) {
|
||||
let mut element = p
|
||||
.make_element(kind, side.layer, &side.name(text), parent)
|
||||
.unwrap();
|
||||
element.id = side.id(n);
|
||||
element.alias = format!("scenario_{}_{}", side.word.to_lowercase(), n);
|
||||
p.elements.insert(element.id, element);
|
||||
}
|
||||
fn seed_occurrence(
|
||||
p: &mut Project,
|
||||
side: Side,
|
||||
n: u128,
|
||||
text: &str,
|
||||
parent: u128,
|
||||
body: OccurrenceBody,
|
||||
) {
|
||||
seed(p, side, n, Kind::Occurrence, text, Some(side.id(parent)));
|
||||
*p.elements
|
||||
.get_mut(&side.id(n))
|
||||
.unwrap()
|
||||
.shared_mut()
|
||||
.unwrap() = SharedBody::Occurrence {
|
||||
body,
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
};
|
||||
}
|
||||
fn action(side: Side, work: u128, line: u128) -> OccurrenceBody {
|
||||
OccurrenceBody::Action {
|
||||
action: Some(side.id(work)),
|
||||
lifeline: Some(side.id(line)),
|
||||
}
|
||||
}
|
||||
fn reuse(side: Side, target: u128, remote: [u128; 2], local: [u128; 2]) -> OccurrenceBody {
|
||||
OccurrenceBody::ScenarioUse {
|
||||
scenario: Some(side.id(target)),
|
||||
bindings: remote
|
||||
.into_iter()
|
||||
.zip(local)
|
||||
.map(|(referenced, local)| LifelineBinding {
|
||||
referenced: side.id(referenced),
|
||||
local: side.id(local),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn fixture() -> Project {
|
||||
let mut p = Project::empty();
|
||||
p.id = id(1);
|
||||
p.alias = "scenario_authoring".into();
|
||||
p.name = "Scenario authoring contract".into();
|
||||
p.description = "Native clicked-slot and caller-return contract.".into();
|
||||
for side in SIDES {
|
||||
let work = if side.layer == Layer::Oa {
|
||||
Kind::Activity
|
||||
} else {
|
||||
Kind::Function
|
||||
};
|
||||
seed(&mut p, side, 10, work, "Inspect ticket", None);
|
||||
seed(&mut p, side, 11, work, "Record result", None);
|
||||
seed(&mut p, side, 20, Kind::StateMachine, "Ticket modes", None);
|
||||
seed(
|
||||
&mut p,
|
||||
side,
|
||||
21,
|
||||
Kind::Region,
|
||||
"Ticket region",
|
||||
Some(side.id(20)),
|
||||
);
|
||||
seed(&mut p, side, 22, Kind::State, "Ready", Some(side.id(21)));
|
||||
for (n, text) in [(30, "A caller"), (31, "B verification"), (32, "C detail")] {
|
||||
seed(&mut p, side, n, Kind::Scenario, text, None);
|
||||
}
|
||||
for (n, owner, represented, text) in [
|
||||
(40, 30, 10, "A inspector"),
|
||||
(41, 30, 11, "A recorder"),
|
||||
(42, 31, 10, "B inspector"),
|
||||
(43, 31, 11, "B recorder"),
|
||||
(44, 32, 10, "C inspector"),
|
||||
(45, 32, 11, "C recorder"),
|
||||
] {
|
||||
seed(&mut p, side, n, Kind::Lifeline, text, Some(side.id(owner)));
|
||||
*p.elements
|
||||
.get_mut(&side.id(n))
|
||||
.unwrap()
|
||||
.shared_mut()
|
||||
.unwrap() = SharedBody::Lifeline {
|
||||
represented: Some(side.id(represented)),
|
||||
};
|
||||
}
|
||||
seed_occurrence(&mut p, side, 50, "Root entry", 30, action(side, 10, 40));
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
51,
|
||||
"Outer ALT",
|
||||
30,
|
||||
OccurrenceBody::Fragment {
|
||||
operator: FragmentOperator::Alternative,
|
||||
branches: vec![side.id(52), side.id(53)],
|
||||
},
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
52,
|
||||
"Outer chosen operand",
|
||||
51,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "ticket.present".into(),
|
||||
occurrences: vec![side.id(54)],
|
||||
},
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
53,
|
||||
"Outer fallback operand",
|
||||
51,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "else".into(),
|
||||
occurrences: vec![side.id(59)],
|
||||
},
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
54,
|
||||
"Inner ALT",
|
||||
52,
|
||||
OccurrenceBody::Fragment {
|
||||
operator: FragmentOperator::Alternative,
|
||||
branches: vec![side.id(55), side.id(56)],
|
||||
},
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
55,
|
||||
"Inner chosen operand",
|
||||
54,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "ticket.ready".into(),
|
||||
occurrences: vec![side.id(57)],
|
||||
},
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
56,
|
||||
"Inner other operand",
|
||||
54,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "else".into(),
|
||||
occurrences: vec![side.id(58)],
|
||||
},
|
||||
);
|
||||
for (n, text, parent, line) in [
|
||||
(57, "Inner anchor", 55, 40),
|
||||
(58, "Inner other anchor", 56, 40),
|
||||
(59, "Fallback anchor", 53, 41),
|
||||
(62, "Root exit", 30, 41),
|
||||
(63, "B entry", 31, 42),
|
||||
(65, "B exit", 31, 43),
|
||||
(66, "C entry", 32, 44),
|
||||
] {
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
n,
|
||||
text,
|
||||
parent,
|
||||
action(side, if line % 2 == 0 { 10 } else { 11 }, line),
|
||||
);
|
||||
}
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
60,
|
||||
"First use of B",
|
||||
30,
|
||||
reuse(side, 31, [42, 43], [40, 41]),
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
61,
|
||||
"Second use of B",
|
||||
30,
|
||||
reuse(side, 31, [42, 43], [40, 41]),
|
||||
);
|
||||
seed_occurrence(
|
||||
&mut p,
|
||||
side,
|
||||
64,
|
||||
"B use of C",
|
||||
31,
|
||||
reuse(side, 32, [44, 45], [42, 43]),
|
||||
);
|
||||
for (owner, steps) in [
|
||||
(30, vec![50, 51, 60, 61, 62]),
|
||||
(31, vec![63, 64, 65]),
|
||||
(32, vec![66]),
|
||||
] {
|
||||
*p.elements
|
||||
.get_mut(&side.id(owner))
|
||||
.unwrap()
|
||||
.steps_mut()
|
||||
.unwrap() = steps.into_iter().map(|n| side.id(n)).collect();
|
||||
}
|
||||
}
|
||||
execute_batch(&p, &[]).expect("deterministic fixture must satisfy core admission")
|
||||
}
|
||||
fn bundle(p: &Project) -> CompiledSourceProject {
|
||||
let entry = language::format(p).replacen("{\n", "{\n include \"rationale.arcadia\";\n", 1);
|
||||
let raw = SourceBundle {
|
||||
entry: "project.arcadia".into(),
|
||||
files: BTreeMap::from([
|
||||
("project.arcadia".into(), entry),
|
||||
("rationale.arcadia".into(), "// Retain caller/operand evidence: ticket 雨.\r\n// Navigation never edits this note.\r\n".into()),
|
||||
]),
|
||||
};
|
||||
let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(compiled.decoded.project, *p);
|
||||
assert_eq!(compiled.bundle, raw);
|
||||
compiled
|
||||
}
|
||||
fn load(entry: &Path) -> CompiledSourceProject {
|
||||
let files = source_files::load_project(entry, ProjectLimits::default()).unwrap();
|
||||
let raw = files.reachable_bundle();
|
||||
let compiled = compile_bundle(&raw, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(
|
||||
compiled.bundle, raw,
|
||||
"No migration or source normalization permitted"
|
||||
);
|
||||
execute_batch(&compiled.decoded.project, &[]).unwrap();
|
||||
compiled
|
||||
}
|
||||
fn advance(base: &mut CompiledSourceProject, commands: Vec<Command>) {
|
||||
let expected = execute_batch(&base.decoded.project, &[Command::Batch(commands)]).unwrap();
|
||||
*base = update_bundle(base, &expected, ProjectLimits::default()).unwrap();
|
||||
}
|
||||
struct Roles<'a> {
|
||||
actual: &'a Project,
|
||||
baseline: Project,
|
||||
used: BTreeSet<Id>,
|
||||
}
|
||||
impl Roles<'_> {
|
||||
fn occurrence(&mut self, side: Side, text: &str) -> Id {
|
||||
let name = side.name(text);
|
||||
let found: Vec<_> = self
|
||||
.actual
|
||||
.elements
|
||||
.values()
|
||||
.filter(|e| e.kind() == Kind::Occurrence && e.layer() == side.layer && e.name == name)
|
||||
.map(|e| e.id)
|
||||
.collect();
|
||||
assert_eq!(
|
||||
found.len(),
|
||||
1,
|
||||
"Exactly one fresh occurrence for role {name}"
|
||||
);
|
||||
let id = found[0];
|
||||
assert_eq!(
|
||||
id.get_version_num(),
|
||||
4,
|
||||
"Native occurrence needs UUIDv4: {name}"
|
||||
);
|
||||
assert!(!self.baseline.contains(id), "Role reused a seed: {name}");
|
||||
assert!(
|
||||
self.used.insert(id),
|
||||
"Role identities must be distinct: {name}"
|
||||
);
|
||||
id
|
||||
}
|
||||
}
|
||||
fn authored(
|
||||
p: &Project,
|
||||
side: Side,
|
||||
id: Id,
|
||||
text: &str,
|
||||
parent: Id,
|
||||
body: OccurrenceBody,
|
||||
) -> Element {
|
||||
let name = side.name(text);
|
||||
Element {
|
||||
id,
|
||||
alias: p.fresh_alias(&name, "element", id),
|
||||
name,
|
||||
description: String::new(),
|
||||
parent: Some(parent),
|
||||
body: ElementBody::shared_in(
|
||||
side.layer,
|
||||
SharedBody::Occurrence {
|
||||
body,
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
fn insert_before(base: &mut CompiledSourceProject, parent: Id, anchor: Id, elements: Vec<Element>) {
|
||||
let mut steps = base.decoded.project.elements[&parent]
|
||||
.steps()
|
||||
.unwrap()
|
||||
.clone();
|
||||
let position = steps
|
||||
.iter()
|
||||
.position(|id| *id == anchor)
|
||||
.expect("declared insertion anchor");
|
||||
let inserted: Vec<_> = elements
|
||||
.iter()
|
||||
.filter(|e| e.parent == Some(parent))
|
||||
.map(|e| e.id)
|
||||
.collect();
|
||||
steps.splice(position..position, inserted);
|
||||
let mut commands: Vec<_> = elements.into_iter().map(Command::CreateElement).collect();
|
||||
commands.push(Command::SetSteps(parent, steps));
|
||||
advance(base, commands);
|
||||
}
|
||||
fn checkpoint(mode: &str) -> Option<(Side, usize)> {
|
||||
if mode == "baseline" {
|
||||
return None;
|
||||
}
|
||||
let (prefix, stage) = mode
|
||||
.split_once('-')
|
||||
.expect("checkpoint is baseline or oa/sa-stage");
|
||||
let side = match prefix {
|
||||
"oa" => SIDES[0],
|
||||
"sa" => SIDES[1],
|
||||
_ => panic!("Unknown layer"),
|
||||
};
|
||||
let count = match stage {
|
||||
"action" => 1,
|
||||
"state" => 2,
|
||||
"reuse" => 3,
|
||||
"fragment" => 4,
|
||||
_ => panic!("Unknown checkpoint"),
|
||||
};
|
||||
Some((side, count))
|
||||
}
|
||||
fn expected(mode: &str, actual: &Project) -> CompiledSourceProject {
|
||||
let baseline = fixture();
|
||||
let mut base = bundle(&baseline);
|
||||
let Some((side, count)) = checkpoint(mode) else {
|
||||
return base;
|
||||
};
|
||||
let mut roles = Roles {
|
||||
actual,
|
||||
baseline,
|
||||
used: BTreeSet::new(),
|
||||
};
|
||||
let key = roles.occurrence(side, "native action");
|
||||
let e = authored(
|
||||
&base.decoded.project,
|
||||
side,
|
||||
key,
|
||||
"native action",
|
||||
side.id(30),
|
||||
action(side, 10, 40),
|
||||
);
|
||||
insert_before(&mut base, side.id(30), side.id(50), vec![e]);
|
||||
if count == 1 {
|
||||
return base;
|
||||
}
|
||||
let key = roles.occurrence(side, "native state");
|
||||
let e = authored(
|
||||
&base.decoded.project,
|
||||
side,
|
||||
key,
|
||||
"native state",
|
||||
side.id(55),
|
||||
OccurrenceBody::State {
|
||||
state: Some(side.id(22)),
|
||||
lifeline: Some(side.id(40)),
|
||||
},
|
||||
);
|
||||
insert_before(&mut base, side.id(55), side.id(57), vec![e]);
|
||||
if count == 2 {
|
||||
return base;
|
||||
}
|
||||
let key = roles.occurrence(side, "native reuse");
|
||||
let e = authored(
|
||||
&base.decoded.project,
|
||||
side,
|
||||
key,
|
||||
"native reuse",
|
||||
side.id(55),
|
||||
reuse(side, 31, [42, 43], [40, 41]),
|
||||
);
|
||||
insert_before(&mut base, side.id(55), side.id(57), vec![e]);
|
||||
if count == 3 {
|
||||
return base;
|
||||
}
|
||||
let fragment = roles.occurrence(side, "native alternative");
|
||||
let branch1 = roles.occurrence(side, "native alternative · operand 1");
|
||||
let branch2 = roles.occurrence(side, "native alternative · operand 2");
|
||||
let p = &base.decoded.project;
|
||||
let elements = vec![
|
||||
authored(
|
||||
p,
|
||||
side,
|
||||
fragment,
|
||||
"native alternative",
|
||||
side.id(30),
|
||||
OccurrenceBody::Fragment {
|
||||
operator: FragmentOperator::Alternative,
|
||||
branches: vec![],
|
||||
},
|
||||
),
|
||||
authored(
|
||||
p,
|
||||
side,
|
||||
branch1,
|
||||
"native alternative · operand 1",
|
||||
fragment,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "ticket.priority > 2".into(),
|
||||
occurrences: vec![],
|
||||
},
|
||||
),
|
||||
authored(
|
||||
p,
|
||||
side,
|
||||
branch2,
|
||||
"native alternative · operand 2",
|
||||
fragment,
|
||||
OccurrenceBody::Branch {
|
||||
guard: "ticket.priority <= 2".into(),
|
||||
occurrences: vec![],
|
||||
},
|
||||
),
|
||||
];
|
||||
insert_before(&mut base, side.id(30), side.id(62), elements);
|
||||
base
|
||||
}
|
||||
fn same_snapshot(verified: &CompiledSourceProject, restored: &CompiledSourceProject) -> bool {
|
||||
verified.decoded.project == restored.decoded.project && verified.bundle == restored.bundle
|
||||
}
|
||||
fn self_check() {
|
||||
let baseline = fixture();
|
||||
assert_eq!(
|
||||
baseline,
|
||||
fixture(),
|
||||
"seed identities and fields must be deterministic"
|
||||
);
|
||||
let base = bundle(&baseline);
|
||||
assert_eq!(expected("baseline", &baseline).bundle, base.bundle);
|
||||
for side in SIDES {
|
||||
// Role carrier intentionally has wrong parent, alias and occurrence body.
|
||||
// Only its fresh IDs may survive in any prediction.
|
||||
let mut carrier = baseline.clone();
|
||||
for text in [
|
||||
"native action",
|
||||
"native state",
|
||||
"native reuse",
|
||||
"native alternative",
|
||||
"native alternative · operand 1",
|
||||
"native alternative · operand 2",
|
||||
] {
|
||||
let mut e = carrier
|
||||
.make_element(Kind::Occurrence, side.layer, &side.name(text), None)
|
||||
.unwrap();
|
||||
e.alias = "wrong_carrier_alias".into();
|
||||
e.description = "must never enter the prediction".into();
|
||||
carrier.elements.insert(e.id, e);
|
||||
}
|
||||
for stage in ["action", "state", "reuse", "fragment"] {
|
||||
let mode = format!("{}-{stage}", side.word.to_lowercase());
|
||||
let correct = expected(&mode, &carrier);
|
||||
assert_ne!(correct.decoded.project, carrier);
|
||||
let again = expected(&mode, &correct.decoded.project);
|
||||
assert_eq!(again.decoded.project, correct.decoded.project);
|
||||
assert_eq!(
|
||||
again.bundle, correct.bundle,
|
||||
"full source checkpoint fixed point"
|
||||
);
|
||||
let mut wrong = correct.decoded.project.clone();
|
||||
wrong.elements.get_mut(&side.id(10)).unwrap().description =
|
||||
"unrequested seed edit".into();
|
||||
assert_ne!(expected(&mode, &wrong).decoded.project, wrong);
|
||||
let native = wrong
|
||||
.elements
|
||||
.values()
|
||||
.find(|e| e.name == side.name("native action"))
|
||||
.unwrap()
|
||||
.id;
|
||||
let mut wrong = correct.decoded.project.clone();
|
||||
wrong.elements.get_mut(&native).unwrap().parent = Some(side.id(55));
|
||||
assert_ne!(
|
||||
expected(&mode, &wrong).decoded.project,
|
||||
wrong,
|
||||
"parent is predicted, not read back"
|
||||
);
|
||||
let mut wrong = correct.decoded.project.clone();
|
||||
wrong
|
||||
.elements
|
||||
.get_mut(&side.id(30))
|
||||
.unwrap()
|
||||
.steps_mut()
|
||||
.unwrap()
|
||||
.swap(0, 1);
|
||||
assert_ne!(
|
||||
expected(&mode, &wrong).decoded.project,
|
||||
wrong,
|
||||
"clicked order is independently fixed"
|
||||
);
|
||||
}
|
||||
let mode = format!("{}-fragment", side.word.to_lowercase());
|
||||
let correct = expected(&mode, &carrier);
|
||||
// Both semantic checks may legitimately discover fresh native IDs. An
|
||||
// Undo/Redo check must additionally pin them to the earlier snapshot.
|
||||
let mut other_carrier = carrier.clone();
|
||||
let old = other_carrier
|
||||
.elements
|
||||
.values()
|
||||
.find(|e| e.name == side.name("native action"))
|
||||
.unwrap()
|
||||
.id;
|
||||
let mut replaced = other_carrier.elements.remove(&old).unwrap();
|
||||
replaced.id = Id::new_v4();
|
||||
other_carrier.elements.insert(replaced.id, replaced);
|
||||
let recreated = expected(&mode, &other_carrier);
|
||||
let correct_prediction = expected(&mode, &correct.decoded.project);
|
||||
let recreated_prediction = expected(&mode, &recreated.decoded.project);
|
||||
assert!(same_snapshot(&correct_prediction, &correct));
|
||||
assert!(same_snapshot(&recreated_prediction, &recreated));
|
||||
assert!(
|
||||
!same_snapshot(&correct, &recreated),
|
||||
"recreated native IDs pass semantic roles but must fail exact history restoration"
|
||||
);
|
||||
let mut wrong = correct.decoded.project.clone();
|
||||
let e = wrong
|
||||
.elements
|
||||
.values_mut()
|
||||
.find(|e| e.name == side.name("native reuse"))
|
||||
.unwrap();
|
||||
let Some(SharedBody::Occurrence {
|
||||
body: OccurrenceBody::ScenarioUse { bindings, .. },
|
||||
..
|
||||
}) = e.shared_mut()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
bindings[0].local = side.id(41);
|
||||
assert_ne!(
|
||||
expected(&mode, &wrong).decoded.project,
|
||||
wrong,
|
||||
"bindings are never copied from actual"
|
||||
);
|
||||
let mut changed_source = correct.bundle.clone();
|
||||
changed_source
|
||||
.files
|
||||
.get_mut("rationale.arcadia")
|
||||
.unwrap()
|
||||
.push_str("// unrequested note\n");
|
||||
let changed = compile_bundle(&changed_source, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(changed.decoded.project, correct.decoded.project);
|
||||
assert_ne!(
|
||||
changed.bundle,
|
||||
expected(&mode, &changed.decoded.project).bundle
|
||||
);
|
||||
}
|
||||
println!(
|
||||
"PASS self-check: deterministic OA/SA baseline, eight semantic checkpoints, independent fields/source bytes and exact history identity discrimination"
|
||||
);
|
||||
}
|
||||
fn main() {
|
||||
assert_eq!(SCHEMA_VERSION, 7);
|
||||
let args: Vec<_> = std::env::args().collect();
|
||||
if args.get(1).is_some_and(|s| s == "self-check") {
|
||||
assert_eq!(args.len(), 2);
|
||||
self_check();
|
||||
return;
|
||||
}
|
||||
if args.get(1).is_some_and(|s| s == "generate") {
|
||||
assert_eq!(args.len(), 3);
|
||||
let dir = Path::new(&args[2]);
|
||||
assert!(
|
||||
!dir.exists(),
|
||||
"Generate only into a fresh directory; preserve existing evidence"
|
||||
);
|
||||
let p = fixture();
|
||||
let base = bundle(&p);
|
||||
fs::create_dir_all(dir).unwrap();
|
||||
for (name, bytes) in base.bundle.files {
|
||||
fs::write(dir.join(name), bytes).unwrap();
|
||||
}
|
||||
fs::write(
|
||||
dir.join("expected-baseline.json"),
|
||||
serde_json::to_vec_pretty(&p).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
println!(
|
||||
"PASS generated deterministic OA/SA fixture: {} elements, 2 sources; native roles absent",
|
||||
p.elements.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
if args.get(1).is_some_and(|s| s == "same") {
|
||||
assert_eq!(args.len(), 4, "same VERIFIED_SAVED_ENTRY RESTORED_ENTRY");
|
||||
let verified = load(Path::new(&args[2]));
|
||||
let restored = load(Path::new(&args[3]));
|
||||
assert!(
|
||||
same_snapshot(&verified, &restored),
|
||||
"Restored snapshot must preserve every Project identity/field and every SourceBundle byte"
|
||||
);
|
||||
println!(
|
||||
"PASS same: exact saved/restored Project and SourceBundle, including all native identities"
|
||||
);
|
||||
return;
|
||||
}
|
||||
assert_eq!(
|
||||
args.len(),
|
||||
4,
|
||||
"check baseline|oa-action|oa-state|oa-reuse|oa-fragment|sa-action|sa-state|sa-reuse|sa-fragment ENTRY"
|
||||
);
|
||||
assert_eq!(args[1], "check");
|
||||
let actual = load(Path::new(&args[3]));
|
||||
let prediction = expected(&args[2], &actual.decoded.project);
|
||||
assert_eq!(
|
||||
actual.decoded.project, prediction.decoded.project,
|
||||
"Complete Project differs from independent checkpoint"
|
||||
);
|
||||
assert_eq!(
|
||||
actual.bundle, prediction.bundle,
|
||||
"Complete source bundle differs, including retained note bytes"
|
||||
);
|
||||
println!(
|
||||
"PASS {}: whole model and {} exact reachable sources; only unique fresh role IDs borrowed",
|
||||
args[2],
|
||||
actual.bundle.files.len()
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user