Files
ARCADIA-Studio/crates/oa-core/tests/r6_source.rs
T

216 lines
9.3 KiB
Rust

use oa_core::{language, source_project::*, *};
use std::collections::BTreeMap;
fn id(n: u128) -> Id {
Id::from_u128(n)
}
#[test]
fn authored_v3_bundle_upgrade_preserves_includes_nested_ownership_comments_and_identity() {
let original = include_str!("fixtures/v3-data-contract.arcadia");
let header = original.find(" sa_function").unwrap();
let close = original.rfind('}').unwrap();
let entry = format!(
"{} // Contracts stay in their original module.\n include \"contracts.arcadia\";\n}}\n",
&original[..header]
);
let fragment = original[header..close].replace(
"items [@packet];",
"items [@ // interface contract decision\npacket];",
);
let bundle = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([
("project.arcadia".into(), entry),
("contracts.arcadia".into(), fragment),
]),
};
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
assert_eq!(compiled.bundle.files.len(), 2);
assert!(compiled.bundle.files["project.arcadia"].contains("arcadia 4;"));
assert!(compiled.bundle.files["project.arcadia"].contains("include \"contracts.arcadia\";"));
for text in [
"// Contracts stay in their original module.",
"// Reused record parameters retain their own identities.",
"// interface contract decision",
] {
assert!(
compiled
.bundle
.files
.values()
.any(|source| source.contains(text)),
"Lost {text}"
);
}
assert_eq!(compiled.index.lexical_owners[&id(6)], id(5));
assert_eq!(compiled.index.lexical_owners[&id(7)], id(4));
assert_eq!(compiled.index.lexical_owners[&id(8)], id(4));
assert_eq!(compiled.index.lexical_owners[&id(17)], id(14));
let report = compiled.decoded.migration.as_ref().unwrap();
assert_eq!(report.from_schema, 3);
for allocation in &report.generated_interface_allocations {
assert_eq!(
compiled.index.declarations[&allocation.id].path,
"contracts.arcadia"
);
}
assert!(
compiled
.diagnostics
.iter()
.any(|d| d.diagnostic.code == "message_value_ambiguous_path")
);
let second = compile_bundle(&compiled.bundle, ProjectLimits::default()).unwrap();
assert_eq!(second.bundle, compiled.bundle);
assert_eq!(second.decoded.project, compiled.decoded.project);
assert!(second.decoded.migration.is_none());
let mut renamed = compiled.decoded.project.clone();
renamed.elements.get_mut(&id(6)).unwrap().name = "Latitude degrees".into();
let updated = update_bundle(&compiled, &renamed, ProjectLimits::default()).unwrap();
assert_eq!(updated.index.lexical_owners, compiled.index.lexical_owners);
assert!(updated.bundle.files["contracts.arcadia"].contains("Latitude degrees"));
assert!(
bundle.files["project.arcadia"].contains("arcadia 3;"),
"Migration does not mutate the caller's draft"
);
}
#[test]
fn schema4_typed_values_supertype_paths_and_protocols_roundtrip() {
let source = r#"arcadia 4; project data_contract "Data contract" {
sa_data_type bounded "Bounded" { definition { Integer { minimum -9223372036854775808; maximum 9223372036854775807; }; }; default { Integer -9223372036854775808; }; unit "m"; }
sa_data_type label_type "Label" { definition { String { minimum_length 1; maximum_length 4; }; }; default { String "航海"; }; }
sa_data_type base "Base" { definition { Record { supertype none; }; }; }
sa_data_type derived "Derived" { definition { Record { supertype @base; }; }; }
sa_exchange_item operation "Operation" { mechanism Operation;
sa_data_field value "Value" { type_ref { Named @bounded; }; direction In; storage Value; default [{ Integer 42; }]; }
}
sa_interface contract "Contract" { supertype none; }
interface_item_allocation member "Operation member" { source @contract; target @operation; transmission Synchronous; acquisition Accept; order 3; }
data_association association "Related records" { source @base; target @derived; source_role "parent"; target_role "children"; source_minimum 0; source_maximum 1; target_minimum 0; target_maximum none; source_navigable true; target_navigable true; }
}"#;
let parsed = language::parse(source).unwrap();
let formatted = language::format(&parsed);
assert!(formatted.contains("supertype @base"));
assert!(formatted.contains("transmission Synchronous"));
assert_eq!(language::parse(&formatted).unwrap(), parsed);
let compiled = compile_bundle(&format_bundle(&parsed), ProjectLimits::default()).unwrap();
assert_eq!(compiled.decoded.project, parsed);
assert!(compiled.decoded.migration.is_none());
}
#[test]
fn omitted_ids_are_stabilized_before_allocation_identity_generation() {
let bundle=SourceBundle{entry:"project.arcadia".into(),files:BTreeMap::from([("project.arcadia".into(),"arcadia 3; project old_model \"Old\" { sa_exchange_item event_item \"Event\" { kind Event; } sa_interface contract \"Contract\" { items [@event_item]; } }".into())])};
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
let report = compiled.decoded.migration.as_ref().unwrap();
let allocation = &report.generated_interface_allocations[0];
assert_eq!(
allocation.id,
Id::new_v5(
&compiled.decoded.project.id,
format!(
"v2-interface-item:{}:{}",
allocation.source, allocation.target
)
.as_bytes()
)
);
let reopened = compile_bundle(&compiled.bundle, ProjectLimits::default()).unwrap();
assert_eq!(reopened.decoded.project, compiled.decoded.project);
assert_eq!(reopened.bundle, compiled.bundle);
}
#[test]
fn inferring_a_legacy_path_keeps_rationale_inside_its_argument() {
let source = include_str!("fixtures/v3-data-contract.arcadia")
.replace(
"type_ref { Named @coordinate; }; }\n }",
"type_ref String; }\n }",
)
.replace(
"field @latitude; expression",
"field @latitude; // original argument rationale\nexpression",
);
let bundle = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([("project.arcadia".into(), source)]),
};
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
let source = &compiled.bundle.files["project.arcadia"];
let argument = &source[source.find("values [{").unwrap()..];
let argument = &argument[..argument.find('}').unwrap()];
assert!(
argument.contains("// original argument rationale"),
"{source}"
);
assert!(argument.contains("path [@origin]"), "{source}");
assert_eq!(compiled.index.lexical_owners[&id(17)], id(14));
}
#[test]
fn qualified_value_reordering_and_path_reidentification_keep_each_arguments_comments() {
let mut project = language::parse(include_str!("fixtures/v3-data-contract.arcadia")).unwrap();
let Some(SharedBody::Occurrence {
body: OccurrenceBody::Exchange { values, .. },
..
}) = project.elements.get_mut(&id(17)).unwrap().shared_mut()
else {
panic!()
};
*values = vec![
MessageValue {
path: vec![id(7)],
field: id(6),
expression: "origin value".into(),
},
MessageValue {
path: vec![id(8)],
field: id(6),
expression: "destination value".into(),
},
];
let mut bundle = format_bundle(&project);
for source in bundle.files.values_mut() {
*source = source
.replace(
"expression \"origin value\";",
"expression \"origin value\"; // origin argument rationale",
)
.replace(
"expression \"destination value\";",
"expression \"destination value\"; // destination argument rationale",
);
}
let current = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
let Some(SharedBody::Occurrence {
body: OccurrenceBody::Exchange { values, .. },
..
}) = project.elements.get_mut(&id(17)).unwrap().shared_mut()
else {
panic!()
};
values.reverse();
let renamed = Id::from_u128(100);
let mut identities: BTreeMap<_, _> = project
.identities()
.into_iter()
.map(|(id, _)| (id, id))
.collect();
identities.insert(id(7), renamed);
reidentify(&mut project, id(7), renamed).unwrap();
project.elements.get_mut(&renamed).unwrap().alias = "revised_origin".into();
let result =
update_bundle_with_identity_map(&current, &project, &identities, ProjectLimits::default())
.unwrap();
let source = &result.bundle.files[&result.index.declarations[&id(17)].path];
let destination = source.find("// destination argument rationale").unwrap();
let origin = source.find("// origin argument rationale").unwrap();
assert!(
destination < origin,
"Distinct uses of the same terminal field must reorder as complete argument annotations: {source}"
);
assert!(source[origin..].contains("path [@revised_origin]"));
assert_eq!(result.decoded.project, project);
}