From b64d97e808c7fd9521c3b7223712e03547707f1e Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Mon, 21 Oct 2019 16:25:34 -0700 Subject: [PATCH] Force PathMap to have deterministic ordering by using BTreeSet instead of HashSet --- src/path_map.rs | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/path_map.rs b/src/path_map.rs index 86f6a02f..19201add 100644 --- a/src/path_map.rs +++ b/src/path_map.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeSet, HashMap}, path::{Path, PathBuf}, }; @@ -9,7 +9,7 @@ use serde::Serialize; #[derive(Debug, Serialize)] struct PathMapNode { value: T, - children: HashSet, + children: BTreeSet, } /// A map from paths to another type, like instance IDs, with a bit of @@ -25,7 +25,7 @@ pub struct PathMap { /// Note that these paths may have other _ancestors_ in the tree, but if an /// orphan's parent path is ever inserted, it will stop being an orphan. It /// will be... adopted! - orphan_paths: HashSet, + orphan_paths: BTreeSet, } impl Default for PathMap { @@ -38,7 +38,7 @@ impl PathMap { pub fn new() -> PathMap { PathMap { nodes: HashMap::new(), - orphan_paths: HashSet::new(), + orphan_paths: BTreeSet::new(), } } @@ -67,7 +67,7 @@ impl PathMap { // Collect any children that are currently marked as orphaned paths, but // are actually children of this new node. - let mut children = HashSet::new(); + let mut children = BTreeSet::new(); for orphan_path in &self.orphan_paths { if orphan_path.parent() == Some(&path) { children.insert(orphan_path.clone()); @@ -158,7 +158,7 @@ impl PathMap { mod test { use super::*; - use maplit::hashset; + use maplit::btreeset; #[test] fn smoke_test() { @@ -179,10 +179,10 @@ mod test { let mut map = PathMap::new(); map.insert("/foo/bar", 5); - assert_eq!(map.orphan_paths, hashset!["/foo/bar".into()]); + assert_eq!(map.orphan_paths, btreeset!["/foo/bar".into()]); map.insert("/foo", 6); - assert_eq!(map.orphan_paths, hashset!["/foo".into()]); + assert_eq!(map.orphan_paths, btreeset!["/foo".into()]); } #[test] @@ -246,4 +246,37 @@ mod test { assert_eq!(map.get("/foo"), None); assert_eq!(map.get("/foo/bar/baz"), Some(&12)); } + + // Makes sure that regardless of addition order, paths are always sorted + // when asking for children. + #[test] + fn add_order_sorted() { + let mut map = PathMap::new(); + + map.insert("/foo", 5); + map.insert("/foo/b", 2); + map.insert("/foo/d", 0); + map.insert("/foo/c", 3); + + assert_eq!( + map.children("/foo"), + Some(vec![ + Path::new("/foo/b"), + Path::new("/foo/c"), + Path::new("/foo/d"), + ]) + ); + + map.insert("/foo/a", 1); + + assert_eq!( + map.children("/foo"), + Some(vec![ + Path::new("/foo/a"), + Path::new("/foo/b"), + Path::new("/foo/c"), + Path::new("/foo/d"), + ]) + ); + } }