forked from rojo-rbx/rojo
Support nested partitions and partitions directly targeting services (#122)
* Do the nested partition thing * Tidy up touched code * Add nested partition test project, not fully functional * Clean up variable names, move path_metadata mutation strictly into snapshot_reconciler * Remove path_metadata, snapshotting is now pure * Factor out snapshot metadata storage to fix a missing case * Pull instance_name out of per_path_metadata, closer to what we need * Refactor to make metadata make more sense, part one * All appears to be well * Cull 'metadata_per_path' in favor of 'instances_per_path' * Remove SnapshotContext * InstanceMetadata -> PublicInstanceMetadata in web module * Build in snapshot testing system for testing... snapshots? * Remove pretty_assertions to see if it fixes a snapshot comparison bug * Reintroduce pretty assertions, it's not the cause of inequality * Fix snapshot tests with custom relative path serializer
This commit is contained in:
committed by
GitHub
parent
38e3c198f2
commit
ecb9b5e28f
@@ -1,16 +1,15 @@
|
||||
#[macro_use] extern crate lazy_static;
|
||||
|
||||
extern crate librojo;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use rbx_tree::RbxValue;
|
||||
|
||||
use librojo::{
|
||||
project::{Project, ProjectNode, InstanceProjectNode, SyncPointProjectNode},
|
||||
project::{Project, ProjectNode},
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
@@ -44,54 +43,52 @@ fn empty_fuzzy_folder() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_sync_point() {
|
||||
let project_file_location = TEST_PROJECTS_ROOT.join("single-sync-point/default.project.json");
|
||||
let project = Project::load_exact(&project_file_location).unwrap();
|
||||
fn single_partition_game() {
|
||||
let project_location = TEST_PROJECTS_ROOT.join("single_partition_game");
|
||||
let project = Project::load_fuzzy(&project_location).unwrap();
|
||||
|
||||
let expected_project = {
|
||||
let foo = ProjectNode::SyncPoint(SyncPointProjectNode {
|
||||
path: project_file_location.parent().unwrap().join("lib"),
|
||||
});
|
||||
let foo = ProjectNode {
|
||||
path: Some(project_location.join("lib")),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut replicated_storage_children = HashMap::new();
|
||||
replicated_storage_children.insert("Foo".to_string(), foo);
|
||||
|
||||
let replicated_storage = ProjectNode::Instance(InstanceProjectNode {
|
||||
class_name: "ReplicatedStorage".to_string(),
|
||||
let replicated_storage = ProjectNode {
|
||||
class_name: Some(String::from("ReplicatedStorage")),
|
||||
children: replicated_storage_children,
|
||||
properties: HashMap::new(),
|
||||
metadata: Default::default(),
|
||||
});
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut http_service_properties = HashMap::new();
|
||||
http_service_properties.insert("HttpEnabled".to_string(), RbxValue::Bool {
|
||||
value: true,
|
||||
});
|
||||
|
||||
let http_service = ProjectNode::Instance(InstanceProjectNode {
|
||||
class_name: "HttpService".to_string(),
|
||||
children: HashMap::new(),
|
||||
let http_service = ProjectNode {
|
||||
class_name: Some(String::from("HttpService")),
|
||||
properties: http_service_properties,
|
||||
metadata: Default::default(),
|
||||
});
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut root_children = HashMap::new();
|
||||
root_children.insert("ReplicatedStorage".to_string(), replicated_storage);
|
||||
root_children.insert("HttpService".to_string(), http_service);
|
||||
|
||||
let root_node = ProjectNode::Instance(InstanceProjectNode {
|
||||
class_name: "DataModel".to_string(),
|
||||
let root_node = ProjectNode {
|
||||
class_name: Some(String::from("DataModel")),
|
||||
children: root_children,
|
||||
properties: HashMap::new(),
|
||||
metadata: Default::default(),
|
||||
});
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Project {
|
||||
name: "single-sync-point".to_string(),
|
||||
tree: root_node,
|
||||
serve_port: None,
|
||||
serve_place_ids: None,
|
||||
file_location: project_file_location.clone(),
|
||||
file_location: project_location.join("default.project.json"),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,9 +96,17 @@ fn single_sync_point() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_model() {
|
||||
let project_file_location = TEST_PROJECTS_ROOT.join("test-model/default.project.json");
|
||||
let project = Project::load_exact(&project_file_location).unwrap();
|
||||
fn single_partition_model() {
|
||||
let project_file_location = TEST_PROJECTS_ROOT.join("single_partition_model");
|
||||
let project = Project::load_fuzzy(&project_file_location).unwrap();
|
||||
|
||||
assert_eq!(project.name, "test-model");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn composing_models() {
|
||||
let project_file_location = TEST_PROJECTS_ROOT.join("composing_models");
|
||||
let project = Project::load_fuzzy(&project_file_location).unwrap();
|
||||
|
||||
assert_eq!(project.name, "composing-models");
|
||||
}
|
||||
124
server/tests/snapshots.rs
Normal file
124
server/tests/snapshots.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use librojo::{
|
||||
imfs::Imfs,
|
||||
project::{Project, ProjectNode},
|
||||
rbx_snapshot::snapshot_project_tree,
|
||||
snapshot_reconciler::{RbxSnapshotInstance},
|
||||
};
|
||||
|
||||
macro_rules! generate_snapshot_tests {
|
||||
($($name: ident),*) => {
|
||||
$(
|
||||
paste::item! {
|
||||
#[test]
|
||||
fn [<snapshot_ $name>]() {
|
||||
let tests_folder = Path::new(env!("CARGO_MANIFEST_DIR")).join("../test-projects");
|
||||
let project_folder = tests_folder.join(stringify!($name));
|
||||
run_snapshot_test(&project_folder);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
generate_snapshot_tests!(
|
||||
empty,
|
||||
nested_partitions,
|
||||
single_partition_game,
|
||||
single_partition_model,
|
||||
transmute_partition
|
||||
);
|
||||
|
||||
const SNAPSHOT_EXPECTED_NAME: &str = "expected-snapshot.json";
|
||||
|
||||
fn run_snapshot_test(path: &Path) {
|
||||
println!("Running snapshot from project: {}", path.display());
|
||||
|
||||
let project = Project::load_fuzzy(path)
|
||||
.expect("Couldn't load project file for snapshot test");
|
||||
|
||||
let mut imfs = Imfs::new();
|
||||
imfs.add_roots_from_project(&project)
|
||||
.expect("Could not add IMFS roots to snapshot project");
|
||||
|
||||
let mut snapshot = snapshot_project_tree(&imfs, &project)
|
||||
.expect("Could not generate snapshot for snapshot test");
|
||||
|
||||
if let Some(snapshot) = snapshot.as_mut() {
|
||||
anonymize_snapshot(path, snapshot);
|
||||
}
|
||||
|
||||
match read_expected_snapshot(path) {
|
||||
Some(expected_snapshot) => assert_eq!(snapshot, expected_snapshot),
|
||||
None => write_expected_snapshot(path, &snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
/// Snapshots contain absolute paths, which simplifies much of Rojo.
|
||||
///
|
||||
/// For saving snapshots to the disk, we should strip off the project folder
|
||||
/// path to make them machine-independent. This doesn't work for paths that fall
|
||||
/// outside of the project folder, but that's okay here.
|
||||
///
|
||||
/// We also need to sort children, since Rojo tends to enumerate the filesystem
|
||||
/// in an unpredictable order.
|
||||
fn anonymize_snapshot(project_folder_path: &Path, snapshot: &mut RbxSnapshotInstance) {
|
||||
match snapshot.metadata.source_path.as_mut() {
|
||||
Some(path) => *path = anonymize_path(project_folder_path, path),
|
||||
None => {},
|
||||
}
|
||||
|
||||
match snapshot.metadata.project_definition.as_mut() {
|
||||
Some((_, project_node)) => anonymize_project_node(project_folder_path, project_node),
|
||||
None => {},
|
||||
}
|
||||
|
||||
snapshot.children.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
|
||||
for child in snapshot.children.iter_mut() {
|
||||
anonymize_snapshot(project_folder_path, child);
|
||||
}
|
||||
}
|
||||
|
||||
fn anonymize_project_node(project_folder_path: &Path, project_node: &mut ProjectNode) {
|
||||
match project_node.path.as_mut() {
|
||||
Some(path) => *path = anonymize_path(project_folder_path, path),
|
||||
None => {},
|
||||
}
|
||||
|
||||
for child_node in project_node.children.values_mut() {
|
||||
anonymize_project_node(project_folder_path, child_node);
|
||||
}
|
||||
}
|
||||
|
||||
fn anonymize_path(project_folder_path: &Path, path: &Path) -> PathBuf {
|
||||
if path.is_absolute() {
|
||||
path.strip_prefix(project_folder_path)
|
||||
.expect("Could not anonymize absolute path")
|
||||
.to_path_buf()
|
||||
} else {
|
||||
path.to_path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
fn read_expected_snapshot(path: &Path) -> Option<Option<RbxSnapshotInstance<'static>>> {
|
||||
let contents = fs::read(path.join(SNAPSHOT_EXPECTED_NAME)).ok()?;
|
||||
let snapshot: Option<RbxSnapshotInstance<'static>> = serde_json::from_slice(&contents)
|
||||
.expect("Could not deserialize snapshot");
|
||||
|
||||
Some(snapshot)
|
||||
}
|
||||
|
||||
fn write_expected_snapshot(path: &Path, snapshot: &Option<RbxSnapshotInstance>) {
|
||||
let mut file = File::create(path.join(SNAPSHOT_EXPECTED_NAME))
|
||||
.expect("Could not open file to write snapshot");
|
||||
|
||||
serde_json::to_writer_pretty(&mut file, snapshot)
|
||||
.expect("Could not serialize snapshot to file");
|
||||
}
|
||||
Reference in New Issue
Block a user