mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 22:11:38 +00:00
test fixtures
This commit is contained in:
parent
bf65d8bfa8
commit
bbd9c0e0fa
22 changed files with 2107 additions and 0 deletions
|
|
@ -803,3 +803,196 @@ fn api_write_existing_instance() {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Rojo_Ref_* path-based forward-sync tests
|
||||
//
|
||||
// These tests validate that Rojo_Ref_* attributes in meta/model files
|
||||
// correctly resolve to Variant::Ref properties during forward sync
|
||||
// (filesystem → server → plugin).
|
||||
// ===========================================================================
|
||||
|
||||
/// Initial load: Model with Rojo_Ref_PrimaryPart attribute should have
|
||||
/// PrimaryPart resolved as a Variant::Ref in the read response.
|
||||
#[test]
|
||||
fn ref_path_initial_load() {
|
||||
run_serve_test("ref_forward_sync", |session, _redactions| {
|
||||
let info = session.get_api_rojo().unwrap();
|
||||
|
||||
let read_response = session.get_api_read(info.root_instance_id).unwrap();
|
||||
let instances = &read_response.instances;
|
||||
|
||||
// Find MyModel and check it has a PrimaryPart Ref property
|
||||
let model = instances
|
||||
.values()
|
||||
.find(|inst| inst.name == "MyModel")
|
||||
.expect("MyModel should exist in read response");
|
||||
|
||||
let has_primary_part = model
|
||||
.properties
|
||||
.iter()
|
||||
.any(|(name, _)| name == "PrimaryPart");
|
||||
assert!(
|
||||
has_primary_part,
|
||||
"MyModel should have PrimaryPart property resolved from Rojo_Ref_PrimaryPart"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a Rojo_Ref_PrimaryPart attribute to an existing meta file.
|
||||
/// The forward-sync patch should include a PrimaryPart Ref update.
|
||||
#[test]
|
||||
fn ref_path_add_attribute_to_existing_meta() {
|
||||
run_serve_test("ref_forward_sync", |session, mut redactions| {
|
||||
let _info = session.get_api_rojo().unwrap();
|
||||
|
||||
// Modify MyModel's meta to change PrimaryPart to OtherPart
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/MyModel/init.meta.json5");
|
||||
|
||||
let socket_packet = session
|
||||
.recv_socket_packet(SocketPacketType::Messages, 0, || {
|
||||
fs::write(
|
||||
&meta_path,
|
||||
r#"{
|
||||
"className": "Model",
|
||||
"attributes": {
|
||||
"Rojo_Ref_PrimaryPart": "Workspace/MyModel/OtherPart"
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Verify we got a patch -- the PrimaryPart should be updated
|
||||
let redacted = socket_packet.intern_and_redact(&mut redactions, ());
|
||||
assert_yaml_snapshot!("ref_path_change_target_patch", redacted);
|
||||
});
|
||||
}
|
||||
|
||||
/// Remove a Rojo_Ref_PrimaryPart attribute from a meta file.
|
||||
/// The forward-sync patch should update PrimaryPart to nil.
|
||||
#[test]
|
||||
fn ref_path_remove_attribute() {
|
||||
run_serve_test("ref_forward_sync", |session, mut redactions| {
|
||||
let _info = session.get_api_rojo().unwrap();
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/MyModel/init.meta.json5");
|
||||
|
||||
let socket_packet = session
|
||||
.recv_socket_packet(SocketPacketType::Messages, 0, || {
|
||||
// Remove the Rojo_Ref_PrimaryPart attribute
|
||||
fs::write(
|
||||
&meta_path,
|
||||
r#"{
|
||||
"className": "Model",
|
||||
"attributes": {}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let redacted = socket_packet.intern_and_redact(&mut redactions, ());
|
||||
assert_yaml_snapshot!("ref_path_remove_attr_patch", redacted);
|
||||
});
|
||||
}
|
||||
|
||||
/// Add a new model file with a Rojo_Ref_Value attribute.
|
||||
/// The forward-sync patch should include the new instance with Value resolved.
|
||||
#[test]
|
||||
fn ref_path_new_file_with_ref_attr() {
|
||||
run_serve_test("ref_forward_sync", |session, mut redactions| {
|
||||
let _info = session.get_api_rojo().unwrap();
|
||||
|
||||
let new_file = session
|
||||
.path()
|
||||
.join("src/Workspace/MyModel/RefValue.model.json5");
|
||||
|
||||
let socket_packet = session
|
||||
.recv_socket_packet(SocketPacketType::Messages, 0, || {
|
||||
fs::write(
|
||||
&new_file,
|
||||
r#"{
|
||||
"className": "ObjectValue",
|
||||
"attributes": {
|
||||
"Rojo_Ref_Value": "Workspace/MyModel/Target"
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let redacted = socket_packet.intern_and_redact(&mut redactions, ());
|
||||
assert_yaml_snapshot!("ref_path_new_file_patch", redacted);
|
||||
});
|
||||
}
|
||||
|
||||
/// Rojo_Ref_* pointing to a non-existent path should not crash and
|
||||
/// the property should not be set.
|
||||
#[test]
|
||||
fn ref_path_nonexistent_target_no_crash() {
|
||||
run_serve_test("ref_forward_sync", |session, mut redactions| {
|
||||
let _info = session.get_api_rojo().unwrap();
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/MyModel/init.meta.json5");
|
||||
|
||||
let socket_packet = session
|
||||
.recv_socket_packet(SocketPacketType::Messages, 0, || {
|
||||
fs::write(
|
||||
&meta_path,
|
||||
r#"{
|
||||
"className": "Model",
|
||||
"attributes": {
|
||||
"Rojo_Ref_PrimaryPart": "Workspace/NonExistent/Part"
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Should receive a patch without crashing.
|
||||
// The PrimaryPart might be nil or missing (not a resolved Ref).
|
||||
let redacted = socket_packet.intern_and_redact(&mut redactions, ());
|
||||
assert_yaml_snapshot!("ref_path_nonexistent_patch", redacted);
|
||||
});
|
||||
}
|
||||
|
||||
/// Multiple Rojo_Ref_* attributes on the same instance should all resolve.
|
||||
#[test]
|
||||
fn ref_path_multiple_attributes() {
|
||||
run_serve_test("ref_forward_sync", |session, mut redactions| {
|
||||
let _info = session.get_api_rojo().unwrap();
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/MyModel/init.meta.json5");
|
||||
|
||||
let socket_packet = session
|
||||
.recv_socket_packet(SocketPacketType::Messages, 0, || {
|
||||
fs::write(
|
||||
&meta_path,
|
||||
r#"{
|
||||
"className": "Model",
|
||||
"attributes": {
|
||||
"Rojo_Ref_PrimaryPart": "Workspace/MyModel/Target",
|
||||
"Rojo_Ref_CustomRef": "Workspace/MyModel/OtherPart"
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let redacted = socket_packet.intern_and_redact(&mut redactions, ());
|
||||
assert_yaml_snapshot!("ref_path_multiple_attrs_patch", redacted);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5977,3 +5977,529 @@ fn fuzz_filesystem_chaos() {
|
|||
session.assert_tree_fresh();
|
||||
});
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Ref property two-way sync tests
|
||||
//
|
||||
// These tests validate the full /api/write pipeline for Variant::Ref
|
||||
// properties. The server should convert Ref properties to Rojo_Ref_*
|
||||
// path-based attributes in meta/model files.
|
||||
// ===========================================================================
|
||||
|
||||
/// Helper: get session info and find instances by name in the ref_two_way_sync fixture.
|
||||
fn ref_test_setup(
|
||||
session: &crate::rojo_test::serve_util::TestServeSession,
|
||||
) -> (
|
||||
librojo::SessionId,
|
||||
Ref, // workspace_id
|
||||
Ref, // model_id
|
||||
Ref, // part1_id
|
||||
Ref, // part2_id
|
||||
Ref, // objval_id
|
||||
) {
|
||||
let info = session.get_api_rojo().unwrap();
|
||||
let root_read = session.get_api_read(info.root_instance_id).unwrap();
|
||||
let (workspace_id, _) = find_by_name(&root_read.instances, "Workspace");
|
||||
let ws_read = session.get_api_read(workspace_id).unwrap();
|
||||
|
||||
let (model_id, _) = find_by_name(&ws_read.instances, "TestModel");
|
||||
let model_read = session.get_api_read(model_id).unwrap();
|
||||
let (part1_id, _) = find_by_name(&model_read.instances, "Part1");
|
||||
let (part2_id, _) = find_by_name(&model_read.instances, "Part2");
|
||||
|
||||
let (objval_id, _) = find_by_name(&ws_read.instances, "TestObjectValue");
|
||||
|
||||
(info.session_id, workspace_id, model_id, part1_id, part2_id, objval_id)
|
||||
}
|
||||
|
||||
/// Read a JSON5 file and return its parsed value.
|
||||
fn read_json5_file(path: &Path) -> serde_json::Value {
|
||||
let content = fs::read_to_string(path).unwrap_or_else(|e| {
|
||||
panic!("Failed to read {}: {}", path.display(), e);
|
||||
});
|
||||
json5::from_str(&content).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Failed to parse JSON5 at {}: {}\nContent: {}",
|
||||
path.display(),
|
||||
e,
|
||||
content
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if a JSON5 file's attributes contain a specific Rojo_Ref_* key with expected value.
|
||||
fn assert_meta_has_ref_attr(path: &Path, attr_name: &str, expected_path: &str) {
|
||||
let val = read_json5_file(path);
|
||||
let attr_val = val
|
||||
.get("attributes")
|
||||
.and_then(|a| a.get(attr_name))
|
||||
.and_then(|v| v.as_str());
|
||||
assert_eq!(
|
||||
attr_val,
|
||||
Some(expected_path),
|
||||
"File {} should have attributes.{} = {:?}, got {:?}",
|
||||
path.display(),
|
||||
attr_name,
|
||||
expected_path,
|
||||
attr_val,
|
||||
);
|
||||
}
|
||||
|
||||
/// Check that a JSON5 file does NOT have a specific attribute key.
|
||||
fn assert_meta_no_ref_attr(path: &Path, attr_name: &str) {
|
||||
if !path.exists() {
|
||||
return; // File doesn't exist means no attribute, which is correct.
|
||||
}
|
||||
let val = read_json5_file(path);
|
||||
let has_attr = val
|
||||
.get("attributes")
|
||||
.and_then(|a| a.get(attr_name))
|
||||
.is_some();
|
||||
assert!(
|
||||
!has_attr,
|
||||
"File {} should NOT have attributes.{}",
|
||||
path.display(),
|
||||
attr_name,
|
||||
);
|
||||
}
|
||||
|
||||
/// Poll until a JSON5 file exists and has the expected Rojo_Ref_* attribute.
|
||||
fn poll_meta_has_ref_attr(path: &Path, attr_name: &str, expected_path: &str) {
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if path.exists() {
|
||||
if let Ok(content) = fs::read_to_string(path) {
|
||||
if let Ok(val) = json5::from_str::<serde_json::Value>(&content) {
|
||||
if let Some(attr_val) = val
|
||||
.get("attributes")
|
||||
.and_then(|a| a.get(attr_name))
|
||||
.and_then(|v| v.as_str())
|
||||
{
|
||||
if attr_val == expected_path {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if start.elapsed() > Duration::from_millis(API_POLL_TIMEOUT_MS) {
|
||||
// Final check with detailed error
|
||||
assert_meta_has_ref_attr(path, attr_name, expected_path);
|
||||
return;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Basic Ref write operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ref_set_primary_part() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_set_object_value() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, _, part1_id, _, objval_id) = ref_test_setup(&session);
|
||||
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("Value"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: objval_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestObjectValue.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_Value",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Nil Ref operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ref_set_primary_part_to_nil() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
|
||||
// First set PrimaryPart to Part1
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
|
||||
// Now set PrimaryPart to nil
|
||||
let mut props2 = UstrMap::default();
|
||||
props2.insert(ustr("PrimaryPart"), Some(Variant::Ref(Ref::none())));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props2,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
// Wait for processing then verify attribute removed
|
||||
thread::sleep(Duration::from_millis(300));
|
||||
assert_meta_no_ref_attr(&meta_path, "Rojo_Ref_PrimaryPart");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_nil_when_no_prior_attr() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, _, _, _) = ref_test_setup(&session);
|
||||
|
||||
// Set PrimaryPart to nil without having set it before
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(Ref::none())));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
// Should not crash; meta file may or may not exist
|
||||
thread::sleep(Duration::from_millis(300));
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
assert_meta_no_ref_attr(&meta_path, "Rojo_Ref_PrimaryPart");
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ref target changes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ref_change_target() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, part2_id, _) = ref_test_setup(&session);
|
||||
|
||||
// Set PrimaryPart to Part1
|
||||
let mut props1 = UstrMap::default();
|
||||
props1.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props1,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
|
||||
// Change PrimaryPart to Part2
|
||||
let mut props2 = UstrMap::default();
|
||||
props2.insert(ustr("PrimaryPart"), Some(Variant::Ref(part2_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props2,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part2",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_set_nil_then_set_again() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
|
||||
// Set → nil → set again
|
||||
let mut props1 = UstrMap::default();
|
||||
props1.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props1,
|
||||
changed_metadata: None,
|
||||
});
|
||||
poll_meta_has_ref_attr(&meta_path, "Rojo_Ref_PrimaryPart", "Workspace/TestModel/Part1");
|
||||
|
||||
let mut props2 = UstrMap::default();
|
||||
props2.insert(ustr("PrimaryPart"), Some(Variant::Ref(Ref::none())));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props2,
|
||||
changed_metadata: None,
|
||||
});
|
||||
thread::sleep(Duration::from_millis(300));
|
||||
assert_meta_no_ref_attr(&meta_path, "Rojo_Ref_PrimaryPart");
|
||||
|
||||
let mut props3 = UstrMap::default();
|
||||
props3.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props3,
|
||||
changed_metadata: None,
|
||||
});
|
||||
poll_meta_has_ref_attr(&meta_path, "Rojo_Ref_PrimaryPart", "Workspace/TestModel/Part1");
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ref with other property changes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ref_with_name_change() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: Some("RenamedModel".to_string()),
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
// Both name change and ref change should be processed.
|
||||
// The name change triggers a filesystem rename handled by ChangeProcessor.
|
||||
// The ref change writes Rojo_Ref_PrimaryPart to the meta file.
|
||||
// After rename, the meta file is at the new location.
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
|
||||
// Check the renamed directory has the ref attribute
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/RenamedModel/init.meta.json5");
|
||||
if meta_path.exists() {
|
||||
assert_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/RenamedModel/Part1",
|
||||
);
|
||||
}
|
||||
// Note: the exact path in the ref depends on when the rename is processed.
|
||||
// This test verifies the combination doesn't crash.
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_only_change_creates_meta() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
|
||||
// Sending ONLY a Ref property change (no Source, no Name, no other props)
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ref on different file formats
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ref_on_model_json5_instance() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, _, part1_id, _, objval_id) = ref_test_setup(&session);
|
||||
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("Value"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: objval_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
// ObjectValue is a .model.json5 — Ref attribute should be added
|
||||
// to adjacent .meta.json5 (since model files get adjacent meta)
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestObjectValue.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_Value",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_existing_meta_preserved() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
|
||||
// The Model's init.meta.json5 already has className: "Model".
|
||||
// After adding a Ref attribute, className should still be present.
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
|
||||
// Verify existing content wasn't clobbered
|
||||
let val = read_json5_file(&meta_path);
|
||||
assert_eq!(
|
||||
val.get("className").and_then(|v| v.as_str()),
|
||||
Some("Model"),
|
||||
"Existing className should be preserved after adding Ref attribute"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error cases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn ref_to_nonexistent_instance_no_crash() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, _, _, _) = ref_test_setup(&session);
|
||||
|
||||
// Use a random Ref that doesn't exist in the tree
|
||||
let fake_ref = Ref::new();
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(fake_ref)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
// Should not crash. The Ref is logged as warning and skipped.
|
||||
thread::sleep(Duration::from_millis(300));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_mixed_valid_and_invalid() {
|
||||
run_serve_test("ref_two_way_sync", |session, _| {
|
||||
let (session_id, _, model_id, part1_id, _, _) = ref_test_setup(&session);
|
||||
|
||||
// Send both a valid Ref and a regular property
|
||||
let fake_ref = Ref::new();
|
||||
let mut props = UstrMap::default();
|
||||
props.insert(ustr("PrimaryPart"), Some(Variant::Ref(part1_id)));
|
||||
// Also try to set a non-existent ref property (should be ignored)
|
||||
props.insert(ustr("SomeOtherRef"), Some(Variant::Ref(fake_ref)));
|
||||
send_update(&session, &session_id, InstanceUpdate {
|
||||
id: model_id,
|
||||
changed_name: None,
|
||||
changed_class_name: None,
|
||||
changed_properties: props,
|
||||
changed_metadata: None,
|
||||
});
|
||||
|
||||
// The valid PrimaryPart Ref should still be written
|
||||
let meta_path = session
|
||||
.path()
|
||||
.join("src/Workspace/TestModel/init.meta.json5");
|
||||
poll_meta_has_ref_attr(
|
||||
&meta_path,
|
||||
"Rojo_Ref_PrimaryPart",
|
||||
"Workspace/TestModel/Part1",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue