Add desktop reset history checks and native wiring fixtures
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
//! Native UI wiring fixture/oracle. Expected output comes from candidate Layout,
|
||||
//! not from an independent geometry algorithm. Run only after parent source freeze.
|
||||
use oa_core::*;
|
||||
use serde_json::{Value, json};
|
||||
use std::{collections::BTreeMap, error::Error, fs, path::PathBuf};
|
||||
|
||||
fn id(n: u128) -> Id {
|
||||
Id::from_u128(700_000 + n)
|
||||
}
|
||||
fn at(x: f32, y: f32) -> Position {
|
||||
Position { x, y }
|
||||
}
|
||||
|
||||
fn function(p: &mut Project, n: u128, name: &str, parent: Option<Id>) -> Id {
|
||||
let old = p
|
||||
.add_element(Kind::Function, Layer::Sa, name, parent)
|
||||
.unwrap();
|
||||
reidentify(p, old, id(n)).unwrap();
|
||||
id(n)
|
||||
}
|
||||
|
||||
fn place(p: &mut Project, d: Id, n: u128, element: Id, position: Position, size: Size) {
|
||||
let old = p.place(d, element, position);
|
||||
reidentify(p, old, id(n)).unwrap();
|
||||
let node = p
|
||||
.diagrams
|
||||
.get_mut(&d)
|
||||
.unwrap()
|
||||
.nodes
|
||||
.get_mut(&id(n))
|
||||
.unwrap();
|
||||
node.alias = format!("placement_{n}");
|
||||
node.size = size;
|
||||
}
|
||||
|
||||
fn exchange(p: &mut Project, n: u128, name: &str, source: Id, target: Id) -> Id {
|
||||
let request = FunctionalExchangeRequest {
|
||||
id: id(n),
|
||||
alias: format!("exchange_{n}"),
|
||||
name: name.into(),
|
||||
description: String::new(),
|
||||
source,
|
||||
target,
|
||||
source_port: PortChoice::Create,
|
||||
target_port: PortChoice::Create,
|
||||
items: vec![],
|
||||
};
|
||||
let commands = ports::plan_functional_exchange(p, &request).unwrap();
|
||||
*p = execute_batch(p, &commands).unwrap();
|
||||
id(n)
|
||||
}
|
||||
|
||||
fn route(edge: Id, waypoints: Vec<Position>, fraction: f32, offset: Position) -> EdgeRoute {
|
||||
EdgeRoute {
|
||||
edge,
|
||||
source_attachment: None,
|
||||
target_attachment: None,
|
||||
waypoints,
|
||||
label: Some(EdgeLabel { fraction, offset }),
|
||||
}
|
||||
}
|
||||
|
||||
fn original() -> (Project, Id, Id, Value) {
|
||||
let mut p = Project::empty();
|
||||
p.id = id(0);
|
||||
p.alias = "arrange_native_fixture".into();
|
||||
p.name = "Arrange route reset trial".into();
|
||||
let old = p.ensure_diagram(Layer::Sa);
|
||||
let main = id(1);
|
||||
reidentify(&mut p, old, main).unwrap();
|
||||
{
|
||||
let d = p.diagrams.get_mut(&main).unwrap();
|
||||
d.alias = "main_dispatch".into();
|
||||
d.name = "Main dispatch".into();
|
||||
d.projection = ProjectionKind::FunctionalDataflow;
|
||||
}
|
||||
let group = function(&mut p, 10, "Prepare report", None);
|
||||
let send = function(&mut p, 11, "Send report", Some(group));
|
||||
let check = function(&mut p, 12, "Check report", Some(group));
|
||||
let monitor = function(&mut p, 13, "Monitor report", None);
|
||||
let archive = function(&mut p, 14, "Archive report", None);
|
||||
let card = Size {
|
||||
width: 224.,
|
||||
height: 94.,
|
||||
};
|
||||
place(
|
||||
&mut p,
|
||||
main,
|
||||
20,
|
||||
group,
|
||||
at(40., 80.),
|
||||
Size {
|
||||
width: 400.,
|
||||
height: 420.,
|
||||
},
|
||||
);
|
||||
for (n, element, x, y) in [
|
||||
(21, send, 80., 160.),
|
||||
(22, check, 80., 330.),
|
||||
(23, monitor, 740., 80.),
|
||||
(24, archive, 740., 380.),
|
||||
] {
|
||||
place(&mut p, main, n, element, at(x, y), card);
|
||||
}
|
||||
let authored = exchange(&mut p, 100, "Verified report", send, monitor);
|
||||
exchange(&mut p, 101, "Report for checking", send, check);
|
||||
exchange(&mut p, 102, "Checked report archive", check, archive);
|
||||
let hidden = exchange(
|
||||
&mut p,
|
||||
103,
|
||||
"Filtered archive acknowledgement",
|
||||
monitor,
|
||||
archive,
|
||||
);
|
||||
let RelationBody::FunctionalExchange {
|
||||
source_port,
|
||||
target_port,
|
||||
..
|
||||
} = p.relations[&authored].body
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
for (node, port, side, offset) in [
|
||||
(id(21), source_port, PortAnchorSide::Right, 0.25),
|
||||
(id(23), target_port, PortAnchorSide::Left, 0.75),
|
||||
] {
|
||||
p = prepare_command(
|
||||
&p,
|
||||
&Command::SetPortAnchor {
|
||||
diagram: main,
|
||||
node,
|
||||
anchor: PortAnchor { port, side, offset },
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.project;
|
||||
}
|
||||
let mut visible = route(
|
||||
authored,
|
||||
vec![at(610., 210.), at(610., 420.)],
|
||||
0.5,
|
||||
at(0., -32.),
|
||||
);
|
||||
// Real semantic ports remain authoritative; these stored preferences are dormant.
|
||||
visible.source_attachment = Some(EdgeAttachment {
|
||||
side: PortAnchorSide::Bottom,
|
||||
offset: 0.5,
|
||||
});
|
||||
visible.target_attachment = Some(EdgeAttachment {
|
||||
side: PortAnchorSide::Top,
|
||||
offset: 0.25,
|
||||
});
|
||||
let filtered = route(hidden, vec![at(1020., 270.)], 0.75, at(16., -16.));
|
||||
p.diagrams.get_mut(&main).unwrap().routes = vec![visible, filtered];
|
||||
p.diagrams.get_mut(&main).unwrap().view_options.hidden_edges = vec![hidden];
|
||||
let second = id(2);
|
||||
p.diagrams.insert(
|
||||
second,
|
||||
Diagram {
|
||||
id: second,
|
||||
alias: "review_sentinel".into(),
|
||||
name: "Review sentinel".into(),
|
||||
description:
|
||||
"Independent other-view route; Arrange Main dispatch must retain this entire view"
|
||||
.into(),
|
||||
layer: Layer::Sa,
|
||||
projection: ProjectionKind::FunctionalDataflow,
|
||||
view_options: Default::default(),
|
||||
nodes: BTreeMap::new(),
|
||||
layout: None,
|
||||
routes: vec![route(authored, vec![at(420., 40.)], 0.25, at(8., -8.))],
|
||||
},
|
||||
);
|
||||
place(&mut p, second, 30, send, at(40., 120.), card);
|
||||
place(&mut p, second, 31, monitor, at(660., 120.), card);
|
||||
validate_candidate(&p).unwrap();
|
||||
(
|
||||
p,
|
||||
main,
|
||||
second,
|
||||
json!({"authored_edge":authored,"authored_edge_name":"Verified report",
|
||||
"filtered_edge":hidden,"container_placement":id(20),"source_port":source_port,"target_port":target_port,
|
||||
"main_diagram":main,"second_diagram":second,"authored_pin_count":2,
|
||||
"source_anchor":{"side":"Right","offset":0.25},"target_anchor":{"side":"Left","offset":0.75},
|
||||
"dormant_source_attachment":{"side":"Bottom","offset":0.5},
|
||||
"dormant_target_attachment":{"side":"Top","offset":0.25}}),
|
||||
)
|
||||
}
|
||||
|
||||
fn json_bytes(p: &Project) -> Vec<u8> {
|
||||
let mut bytes = serde_json::to_vec_pretty(p).unwrap();
|
||||
if !bytes.ends_with(b"\n") {
|
||||
bytes.push(b'\n');
|
||||
}
|
||||
assert_eq!(persistence::decode(&bytes).unwrap(), *p);
|
||||
bytes
|
||||
}
|
||||
|
||||
fn source_bytes(p: &Project) -> Vec<u8> {
|
||||
let source = language::format(p);
|
||||
assert_eq!(language::parse(&source).unwrap(), *p);
|
||||
let mut bytes = source.into_bytes();
|
||||
if !bytes.ends_with(b"\n") {
|
||||
bytes.push(b'\n');
|
||||
}
|
||||
bytes
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut args = std::env::args_os().skip(1);
|
||||
let output = PathBuf::from(
|
||||
args.next()
|
||||
.ok_or("Usage: arrange_reset_native_probe NEW_DIRECTORY")?,
|
||||
);
|
||||
if args.next().is_some() || output.exists() {
|
||||
return Err("Require exactly one output directory that does not already exist".into());
|
||||
}
|
||||
let (before, main, second, identities) = original();
|
||||
let mut folded = before.clone();
|
||||
folded
|
||||
.diagrams
|
||||
.get_mut(&main)
|
||||
.unwrap()
|
||||
.nodes
|
||||
.get_mut(&id(20))
|
||||
.unwrap()
|
||||
.collapsed = true;
|
||||
let mut files = Vec::new();
|
||||
let mut cases = Vec::new();
|
||||
for (name, project) in [("expanded", before), ("folded", folded)] {
|
||||
validate_candidate(&project)?;
|
||||
// Explicitly candidate-derived UI oracle: this is not an independent layout proof.
|
||||
let expected = execute_batch(&project, &[Command::Layout(main)])?;
|
||||
assert_eq!(project.elements, expected.elements);
|
||||
assert_eq!(project.relations, expected.relations);
|
||||
assert_eq!(project.diagrams[&second], expected.diagrams[&second]);
|
||||
for (stage, p) in [("original", &project), ("expected-after", &expected)] {
|
||||
files.push((format!("{name}-{stage}.json"), json_bytes(p)));
|
||||
files.push((format!("{name}-{stage}.arcadia"), source_bytes(p)));
|
||||
}
|
||||
cases.push(json!({"name":name,"diagram":main,
|
||||
"original_json":format!("{name}-original.json"),
|
||||
"expected_json":format!("{name}-expected-after.json"),
|
||||
"original_source":format!("{name}-original.arcadia"),
|
||||
"expected_source":format!("{name}-expected-after.arcadia"),
|
||||
"original_routes":project.diagrams[&main].routes,"expected_routes":expected.diagrams[&main].routes,
|
||||
"expected_origin":"Candidate Command::Layout; UI-wiring oracle only"}));
|
||||
}
|
||||
let manifest = json!({"schema":1,"cases":cases,"identities":identities,
|
||||
"definition":"Source-authored five-Function/two-view fixture; original geometry never generated by Layout.",
|
||||
"comparison":"Decode saved JSON with production persistence and compare complete Project to immutable original/expected file. Prefer direct serializer bytes for byte checks; do not reserialize Project through generic JSON Value as a float oracle.",
|
||||
"performed":"Generation and core checks only when this program is actually run; no native UI results recorded here."});
|
||||
files.push((
|
||||
"manifest.json".into(),
|
||||
serde_json::to_vec_pretty(&manifest)?,
|
||||
));
|
||||
fs::create_dir(&output)?;
|
||||
for (name, bytes) in files {
|
||||
fs::write(output.join(name), bytes)?;
|
||||
}
|
||||
println!("{}", output.join("manifest.json").display());
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user