From 3678ddfa364aac3bbc1d6ec142f0926a0cd341e8 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Mon, 30 Sep 2019 16:27:31 -0700 Subject: [PATCH] Break redaction stuff out into separate crate --- Cargo.lock | 12 ++++- Cargo.toml | 3 ++ rojo-insta-ext/Cargo.toml | 14 +++++ rojo-insta-ext/README.md | 2 + rojo-insta-ext/src/lib.rs | 3 ++ rojo-insta-ext/src/redaction_map.rs | 75 ++++++++++++++++++++++++++ rojo-test/Cargo.toml | 2 +- rojo-test/README.md | 8 +++ rojo-test/src/serve_test.rs | 83 ++++------------------------- 9 files changed, 127 insertions(+), 75 deletions(-) create mode 100644 rojo-insta-ext/Cargo.toml create mode 100644 rojo-insta-ext/README.md create mode 100644 rojo-insta-ext/src/lib.rs create mode 100644 rojo-insta-ext/src/redaction_map.rs create mode 100644 rojo-test/README.md diff --git a/Cargo.lock b/Cargo.lock index 0b0e216e..9a8686aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1474,6 +1474,7 @@ dependencies = [ "regex 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", "ritz 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rojo-insta-ext 0.1.0", "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1483,6 +1484,15 @@ dependencies = [ "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rojo-insta-ext" +version = "0.1.0" +dependencies = [ + "insta 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rojo-test" version = "0.1.0" @@ -1494,9 +1504,9 @@ dependencies = [ "rbx_dom_weak 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", "rojo 0.6.0-dev", + "rojo-insta-ext 0.1.0", "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index e16e76d1..b1826785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,13 @@ dev-live-assets = [] [workspace] members = [ "rojo-test", + "rojo-insta-ext", ] default-members = [ ".", "rojo-test", + "rojo-insta-ext", ] [lib] @@ -65,6 +67,7 @@ uuid = { version = "0.7", features = ["v4", "serde"] } winreg = "0.6.2" [dev-dependencies] +rojo-insta-ext = { path = "rojo-insta-ext" } lazy_static = "1.2" paste = "0.1" pretty_assertions = "0.6.1" diff --git a/rojo-insta-ext/Cargo.toml b/rojo-insta-ext/Cargo.toml new file mode 100644 index 00000000..f072ab91 --- /dev/null +++ b/rojo-insta-ext/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rojo-insta-ext" +version = "0.1.0" +authors = ["Lucien Greathouse "] +edition = "2018" +publish = false + +[dependencies] +serde = "1.0.99" +serde_yaml = "0.8.9" + +[dependencies.insta] +version = "0.11.0" +features = ["redactions"] diff --git a/rojo-insta-ext/README.md b/rojo-insta-ext/README.md new file mode 100644 index 00000000..5ffb9dde --- /dev/null +++ b/rojo-insta-ext/README.md @@ -0,0 +1,2 @@ +# Rojo Insta Extensions +This crate has add-ons intended for use with Insta that are useful for snapshot tests. \ No newline at end of file diff --git a/rojo-insta-ext/src/lib.rs b/rojo-insta-ext/src/lib.rs new file mode 100644 index 00000000..bcfd1f20 --- /dev/null +++ b/rojo-insta-ext/src/lib.rs @@ -0,0 +1,3 @@ +mod redaction_map; + +pub use redaction_map::*; diff --git a/rojo-insta-ext/src/redaction_map.rs b/rojo-insta-ext/src/redaction_map.rs new file mode 100644 index 00000000..dce3cb5d --- /dev/null +++ b/rojo-insta-ext/src/redaction_map.rs @@ -0,0 +1,75 @@ +use std::collections::HashMap; + +use serde::Serialize; + +/// Enables redacting any value that serializes as a string. +/// +/// Used for transforming Rojo instance IDs into something deterministic. +pub struct RedactionMap { + ids: HashMap, + last_id: usize, +} + +impl RedactionMap { + pub fn new() -> Self { + Self { + ids: HashMap::new(), + last_id: 0, + } + } + + pub fn intern(&mut self, id: impl ToString) { + let last_id = &mut self.last_id; + + self.ids.entry(id.to_string()).or_insert_with(|| { + *last_id += 1; + *last_id + }); + } + + pub fn intern_iter(&mut self, ids: impl Iterator) { + for id in ids { + self.intern(id.to_string()); + } + } + + pub fn redacted_yaml(&self, value: impl Serialize) -> serde_yaml::Value { + let mut encoded = serde_yaml::to_value(value).expect("Couldn't encode value as YAML"); + + self.redact(&mut encoded); + encoded + } + + pub fn redact(&self, yaml_value: &mut serde_yaml::Value) { + use serde_yaml::{Mapping, Value}; + + match yaml_value { + Value::String(value) => { + if let Some(redacted) = self.ids.get(value) { + *yaml_value = Value::String(format!("id-{}", *redacted)); + } + } + Value::Sequence(sequence) => { + for value in sequence { + self.redact(value); + } + } + Value::Mapping(mapping) => { + // We can't mutate the keys of a map in-place, so we take + // ownership of the map and rebuild it. + + let owned_map = std::mem::replace(mapping, Mapping::new()); + let mut new_map = Mapping::with_capacity(owned_map.len()); + + for (mut key, mut value) in owned_map { + self.redact(&mut key); + self.redact(&mut value); + new_map.insert(key, value); + } + + *mapping = new_map; + } + _ => {} + } + } +} diff --git a/rojo-test/Cargo.toml b/rojo-test/Cargo.toml index 2a9e5c56..173cbb16 100644 --- a/rojo-test/Cargo.toml +++ b/rojo-test/Cargo.toml @@ -13,9 +13,9 @@ rbx_dom_weak = "1.9.0" reqwest = "0.9.20" serde = "1.0.99" serde_json = "1.0.40" -serde_yaml = "0.8.9" tempfile = "3.1.0" walkdir = "2.2.9" +rojo-insta-ext = { path = "../rojo-insta-ext" } # We execute Rojo via std::process::Command, so depend on it so it's built! rojo = { path = ".." } diff --git a/rojo-test/README.md b/rojo-test/README.md new file mode 100644 index 00000000..166044a1 --- /dev/null +++ b/rojo-test/README.md @@ -0,0 +1,8 @@ +# rojo-test +This project does end-to-end testing of Rojo by executing it and checking what side-effects it has. + +rojo-test is meant to be run as a test with: + +```bash +cargo test +``` \ No newline at end of file diff --git a/rojo-test/src/serve_test.rs b/rojo-test/src/serve_test.rs index a5887655..2cdff469 100644 --- a/rojo-test/src/serve_test.rs +++ b/rojo-test/src/serve_test.rs @@ -11,6 +11,7 @@ use rbx_dom_weak::RbxId; use tempfile::{tempdir, TempDir}; use librojo::web_interface::{ReadResponse, ServerInfoResponse}; +use rojo_insta_ext::RedactionMap; use crate::util::{ copy_recursive, get_rojo_path, get_serve_tests_path, get_working_dir_path, KillOnDrop, @@ -18,28 +19,26 @@ use crate::util::{ #[test] fn empty() { - run_serve_test(|session, mut dm| { + run_serve_test(|session, mut redactions| { let info = session.get_api_rojo().unwrap(); let root_id = info.root_instance_id; - let mut info = serde_yaml::to_value(info).unwrap(); - dm.redact(&mut info); + let info = redactions.redacted_yaml(info); assert_yaml_snapshot!(info); let read_result = session.get_api_read(root_id).unwrap(); - dm.intern_iter(read_result.instances.keys().copied()); + redactions.intern_iter(read_result.instances.keys().copied()); - let mut read_result = serde_yaml::to_value(read_result).unwrap(); - dm.redact(&mut read_result); + let read_result = redactions.redacted_yaml(read_result); assert_yaml_snapshot!(read_result); }); } -fn run_serve_test(callback: impl FnOnce(TestServeSession, DeterMap)) { +fn run_serve_test(callback: impl FnOnce(TestServeSession, RedactionMap)) { let _ = env_logger::try_init(); let mut settings = insta::Settings::new(); @@ -47,77 +46,15 @@ fn run_serve_test(callback: impl FnOnce(TestServeSession, DeterMap)) { let snapshot_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("serve-test-snapshots"); settings.set_snapshot_path(snapshot_path); - let mut dm = DeterMap::new(); + let mut redactions = RedactionMap::new(); let mut session = TestServeSession::new("empty"); let info = session.wait_to_come_online(); - dm.intern(info.session_id); - dm.intern(info.root_instance_id); + redactions.intern(info.session_id); + redactions.intern(info.root_instance_id); - settings.bind(move || callback(session, dm)); -} - -struct DeterMap { - ids: HashMap, - last_id: usize, -} - -impl DeterMap { - fn new() -> DeterMap { - DeterMap { - ids: HashMap::new(), - last_id: 0, - } - } - - fn intern(&mut self, id: impl ToString) { - let last_id = &mut self.last_id; - - self.ids.entry(id.to_string()).or_insert_with(|| { - *last_id += 1; - *last_id - }); - } - - fn intern_iter(&mut self, ids: impl Iterator) { - for id in ids { - self.intern(id.to_string()); - } - } - - fn redact(&self, yaml_value: &mut serde_yaml::Value) { - use serde_yaml::{Mapping, Value}; - - match yaml_value { - Value::String(value) => { - if let Some(redacted) = self.ids.get(value) { - *yaml_value = Value::String(format!("id-{}", *redacted)); - } - } - Value::Sequence(sequence) => { - for value in sequence { - self.redact(value); - } - } - Value::Mapping(mapping) => { - // We can't mutate the keys of a map in-place, so we take - // ownership of the map and rebuild it. - - let owned_map = std::mem::replace(mapping, Mapping::new()); - let mut new_map = Mapping::with_capacity(owned_map.len()); - - for (mut key, mut value) in owned_map { - self.redact(&mut key); - self.redact(&mut value); - new_map.insert(key, value); - } - - *mapping = new_map; - } - _ => {} - } - } + settings.bind(move || callback(session, redactions)); } fn get_port_number() -> usize {