##// END OF EJS Templates
rust-dirstate: remove test case for DirsMultiset::new(Manifest, Some)...
Yuya Nishihara -
r43071:8ab1ae7f default
parent child Browse files
Show More
@@ -1,338 +1,320
1 // dirs_multiset.rs
1 // dirs_multiset.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! A multiset of directory names.
8 //! A multiset of directory names.
9 //!
9 //!
10 //! Used to counts the references to directories in a manifest or dirstate.
10 //! Used to counts the references to directories in a manifest or dirstate.
11 use crate::{
11 use crate::{
12 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
12 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
13 };
13 };
14 use std::collections::hash_map::Entry;
14 use std::collections::hash_map::Entry;
15 use std::collections::HashMap;
15 use std::collections::HashMap;
16
16
17 #[derive(PartialEq, Debug)]
17 #[derive(PartialEq, Debug)]
18 pub struct DirsMultiset {
18 pub struct DirsMultiset {
19 inner: HashMap<Vec<u8>, u32>,
19 inner: HashMap<Vec<u8>, u32>,
20 }
20 }
21
21
22 impl DirsMultiset {
22 impl DirsMultiset {
23 /// Initializes the multiset from a dirstate.
23 /// Initializes the multiset from a dirstate.
24 ///
24 ///
25 /// If `skip_state` is provided, skips dirstate entries with equal state.
25 /// If `skip_state` is provided, skips dirstate entries with equal state.
26 pub fn from_dirstate(
26 pub fn from_dirstate(
27 vec: &HashMap<Vec<u8>, DirstateEntry>,
27 vec: &HashMap<Vec<u8>, DirstateEntry>,
28 skip_state: Option<EntryState>,
28 skip_state: Option<EntryState>,
29 ) -> Self {
29 ) -> Self {
30 let mut multiset = DirsMultiset {
30 let mut multiset = DirsMultiset {
31 inner: HashMap::new(),
31 inner: HashMap::new(),
32 };
32 };
33
33
34 for (filename, DirstateEntry { state, .. }) in vec {
34 for (filename, DirstateEntry { state, .. }) in vec {
35 // This `if` is optimized out of the loop
35 // This `if` is optimized out of the loop
36 if let Some(skip) = skip_state {
36 if let Some(skip) = skip_state {
37 if skip != *state {
37 if skip != *state {
38 multiset.add_path(filename);
38 multiset.add_path(filename);
39 }
39 }
40 } else {
40 } else {
41 multiset.add_path(filename);
41 multiset.add_path(filename);
42 }
42 }
43 }
43 }
44
44
45 multiset
45 multiset
46 }
46 }
47
47
48 /// Initializes the multiset from a manifest.
48 /// Initializes the multiset from a manifest.
49 pub fn from_manifest(vec: &Vec<Vec<u8>>) -> Self {
49 pub fn from_manifest(vec: &Vec<Vec<u8>>) -> Self {
50 let mut multiset = DirsMultiset {
50 let mut multiset = DirsMultiset {
51 inner: HashMap::new(),
51 inner: HashMap::new(),
52 };
52 };
53
53
54 for filename in vec {
54 for filename in vec {
55 multiset.add_path(filename);
55 multiset.add_path(filename);
56 }
56 }
57
57
58 multiset
58 multiset
59 }
59 }
60
60
61 /// Increases the count of deepest directory contained in the path.
61 /// Increases the count of deepest directory contained in the path.
62 ///
62 ///
63 /// If the directory is not yet in the map, adds its parents.
63 /// If the directory is not yet in the map, adds its parents.
64 pub fn add_path(&mut self, path: &[u8]) {
64 pub fn add_path(&mut self, path: &[u8]) {
65 for subpath in files::find_dirs(path) {
65 for subpath in files::find_dirs(path) {
66 if let Some(val) = self.inner.get_mut(subpath) {
66 if let Some(val) = self.inner.get_mut(subpath) {
67 *val += 1;
67 *val += 1;
68 break;
68 break;
69 }
69 }
70 self.inner.insert(subpath.to_owned(), 1);
70 self.inner.insert(subpath.to_owned(), 1);
71 }
71 }
72 }
72 }
73
73
74 /// Decreases the count of deepest directory contained in the path.
74 /// Decreases the count of deepest directory contained in the path.
75 ///
75 ///
76 /// If it is the only reference, decreases all parents until one is
76 /// If it is the only reference, decreases all parents until one is
77 /// removed.
77 /// removed.
78 /// If the directory is not in the map, something horrible has happened.
78 /// If the directory is not in the map, something horrible has happened.
79 pub fn delete_path(
79 pub fn delete_path(
80 &mut self,
80 &mut self,
81 path: &[u8],
81 path: &[u8],
82 ) -> Result<(), DirstateMapError> {
82 ) -> Result<(), DirstateMapError> {
83 for subpath in files::find_dirs(path) {
83 for subpath in files::find_dirs(path) {
84 match self.inner.entry(subpath.to_owned()) {
84 match self.inner.entry(subpath.to_owned()) {
85 Entry::Occupied(mut entry) => {
85 Entry::Occupied(mut entry) => {
86 let val = entry.get().clone();
86 let val = entry.get().clone();
87 if val > 1 {
87 if val > 1 {
88 entry.insert(val - 1);
88 entry.insert(val - 1);
89 break;
89 break;
90 }
90 }
91 entry.remove();
91 entry.remove();
92 }
92 }
93 Entry::Vacant(_) => {
93 Entry::Vacant(_) => {
94 return Err(DirstateMapError::PathNotFound(
94 return Err(DirstateMapError::PathNotFound(
95 path.to_owned(),
95 path.to_owned(),
96 ))
96 ))
97 }
97 }
98 };
98 };
99 }
99 }
100
100
101 Ok(())
101 Ok(())
102 }
102 }
103
103
104 pub fn contains(&self, key: &[u8]) -> bool {
104 pub fn contains(&self, key: &[u8]) -> bool {
105 self.inner.contains_key(key)
105 self.inner.contains_key(key)
106 }
106 }
107
107
108 pub fn iter(&self) -> impl Iterator<Item = &Vec<u8>> {
108 pub fn iter(&self) -> impl Iterator<Item = &Vec<u8>> {
109 self.inner.keys()
109 self.inner.keys()
110 }
110 }
111
111
112 pub fn len(&self) -> usize {
112 pub fn len(&self) -> usize {
113 self.inner.len()
113 self.inner.len()
114 }
114 }
115 }
115 }
116
116
117 #[cfg(test)]
117 #[cfg(test)]
118 mod tests {
118 mod tests {
119 use super::*;
119 use super::*;
120 use std::collections::HashMap;
120 use std::collections::HashMap;
121
121
122 #[test]
122 #[test]
123 fn test_delete_path_path_not_found() {
123 fn test_delete_path_path_not_found() {
124 let mut map = DirsMultiset::from_manifest(&vec![]);
124 let mut map = DirsMultiset::from_manifest(&vec![]);
125 let path = b"doesnotexist/";
125 let path = b"doesnotexist/";
126 assert_eq!(
126 assert_eq!(
127 Err(DirstateMapError::PathNotFound(path.to_vec())),
127 Err(DirstateMapError::PathNotFound(path.to_vec())),
128 map.delete_path(path)
128 map.delete_path(path)
129 );
129 );
130 }
130 }
131
131
132 #[test]
132 #[test]
133 fn test_delete_path_empty_path() {
133 fn test_delete_path_empty_path() {
134 let mut map = DirsMultiset::from_manifest(&vec![vec![]]);
134 let mut map = DirsMultiset::from_manifest(&vec![vec![]]);
135 let path = b"";
135 let path = b"";
136 assert_eq!(Ok(()), map.delete_path(path));
136 assert_eq!(Ok(()), map.delete_path(path));
137 assert_eq!(
137 assert_eq!(
138 Err(DirstateMapError::PathNotFound(path.to_vec())),
138 Err(DirstateMapError::PathNotFound(path.to_vec())),
139 map.delete_path(path)
139 map.delete_path(path)
140 );
140 );
141 }
141 }
142
142
143 #[test]
143 #[test]
144 fn test_delete_path_successful() {
144 fn test_delete_path_successful() {
145 let mut map = DirsMultiset {
145 let mut map = DirsMultiset {
146 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
146 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
147 .iter()
147 .iter()
148 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
148 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
149 .collect(),
149 .collect(),
150 };
150 };
151
151
152 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
152 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
153 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
153 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
154 assert_eq!(
154 assert_eq!(
155 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
155 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
156 map.delete_path(b"a/b/")
156 map.delete_path(b"a/b/")
157 );
157 );
158
158
159 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
159 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
160 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
160 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
161 eprintln!("{:?}", map);
161 eprintln!("{:?}", map);
162 assert_eq!(Ok(()), map.delete_path(b"a/"));
162 assert_eq!(Ok(()), map.delete_path(b"a/"));
163 eprintln!("{:?}", map);
163 eprintln!("{:?}", map);
164
164
165 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
165 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
166 assert_eq!(
166 assert_eq!(
167 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
167 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
168 map.delete_path(b"a/c/")
168 map.delete_path(b"a/c/")
169 );
169 );
170 }
170 }
171
171
172 #[test]
172 #[test]
173 fn test_add_path_empty_path() {
173 fn test_add_path_empty_path() {
174 let mut map = DirsMultiset::from_manifest(&vec![]);
174 let mut map = DirsMultiset::from_manifest(&vec![]);
175 let path = b"";
175 let path = b"";
176 map.add_path(path);
176 map.add_path(path);
177
177
178 assert_eq!(1, map.len());
178 assert_eq!(1, map.len());
179 }
179 }
180
180
181 #[test]
181 #[test]
182 fn test_add_path_successful() {
182 fn test_add_path_successful() {
183 let mut map = DirsMultiset::from_manifest(&vec![]);
183 let mut map = DirsMultiset::from_manifest(&vec![]);
184
184
185 map.add_path(b"a/");
185 map.add_path(b"a/");
186 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
186 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
187 assert_eq!(1, *map.inner.get(&Vec::new()).unwrap());
187 assert_eq!(1, *map.inner.get(&Vec::new()).unwrap());
188 assert_eq!(2, map.len());
188 assert_eq!(2, map.len());
189
189
190 // Non directory should be ignored
190 // Non directory should be ignored
191 map.add_path(b"a");
191 map.add_path(b"a");
192 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
192 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
193 assert_eq!(2, map.len());
193 assert_eq!(2, map.len());
194
194
195 // Non directory will still add its base
195 // Non directory will still add its base
196 map.add_path(b"a/b");
196 map.add_path(b"a/b");
197 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
197 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
198 assert_eq!(2, map.len());
198 assert_eq!(2, map.len());
199
199
200 // Duplicate path works
200 // Duplicate path works
201 map.add_path(b"a/");
201 map.add_path(b"a/");
202 assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap());
202 assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap());
203
203
204 // Nested dir adds to its base
204 // Nested dir adds to its base
205 map.add_path(b"a/b/");
205 map.add_path(b"a/b/");
206 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
206 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
207 assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap());
207 assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap());
208
208
209 // but not its base's base, because it already existed
209 // but not its base's base, because it already existed
210 map.add_path(b"a/b/c/");
210 map.add_path(b"a/b/c/");
211 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
211 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
212 assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap());
212 assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap());
213
213
214 map.add_path(b"a/c/");
214 map.add_path(b"a/c/");
215 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
215 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
216
216
217 let expected = DirsMultiset {
217 let expected = DirsMultiset {
218 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
218 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
219 .iter()
219 .iter()
220 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
220 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
221 .collect(),
221 .collect(),
222 };
222 };
223 assert_eq!(map, expected);
223 assert_eq!(map, expected);
224 }
224 }
225
225
226 #[test]
226 #[test]
227 fn test_dirsmultiset_new_empty() {
227 fn test_dirsmultiset_new_empty() {
228 let new = DirsMultiset::from_manifest(&vec![]);
228 let new = DirsMultiset::from_manifest(&vec![]);
229 let expected = DirsMultiset {
229 let expected = DirsMultiset {
230 inner: HashMap::new(),
230 inner: HashMap::new(),
231 };
231 };
232 assert_eq!(expected, new);
232 assert_eq!(expected, new);
233
233
234 let new = DirsMultiset::from_dirstate(&HashMap::new(), None);
234 let new = DirsMultiset::from_dirstate(&HashMap::new(), None);
235 let expected = DirsMultiset {
235 let expected = DirsMultiset {
236 inner: HashMap::new(),
236 inner: HashMap::new(),
237 };
237 };
238 assert_eq!(expected, new);
238 assert_eq!(expected, new);
239 }
239 }
240
240
241 #[test]
241 #[test]
242 fn test_dirsmultiset_new_no_skip() {
242 fn test_dirsmultiset_new_no_skip() {
243 let input_vec = ["a/", "b/", "a/c", "a/d/"]
243 let input_vec = ["a/", "b/", "a/c", "a/d/"]
244 .iter()
244 .iter()
245 .map(|e| e.as_bytes().to_vec())
245 .map(|e| e.as_bytes().to_vec())
246 .collect();
246 .collect();
247 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
247 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
248 .iter()
248 .iter()
249 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
249 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
250 .collect();
250 .collect();
251
251
252 let new = DirsMultiset::from_manifest(&input_vec);
252 let new = DirsMultiset::from_manifest(&input_vec);
253 let expected = DirsMultiset {
253 let expected = DirsMultiset {
254 inner: expected_inner,
254 inner: expected_inner,
255 };
255 };
256 assert_eq!(expected, new);
256 assert_eq!(expected, new);
257
257
258 let input_map = ["a/", "b/", "a/c", "a/d/"]
258 let input_map = ["a/", "b/", "a/c", "a/d/"]
259 .iter()
259 .iter()
260 .map(|f| {
260 .map(|f| {
261 (
261 (
262 f.as_bytes().to_vec(),
262 f.as_bytes().to_vec(),
263 DirstateEntry {
263 DirstateEntry {
264 state: EntryState::Normal,
264 state: EntryState::Normal,
265 mode: 0,
265 mode: 0,
266 mtime: 0,
266 mtime: 0,
267 size: 0,
267 size: 0,
268 },
268 },
269 )
269 )
270 })
270 })
271 .collect();
271 .collect();
272 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
272 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
273 .iter()
273 .iter()
274 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
274 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
275 .collect();
275 .collect();
276
276
277 let new = DirsMultiset::from_dirstate(&input_map, None);
277 let new = DirsMultiset::from_dirstate(&input_map, None);
278 let expected = DirsMultiset {
278 let expected = DirsMultiset {
279 inner: expected_inner,
279 inner: expected_inner,
280 };
280 };
281 assert_eq!(expected, new);
281 assert_eq!(expected, new);
282 }
282 }
283
283
284 #[test]
284 #[test]
285 fn test_dirsmultiset_new_skip() {
285 fn test_dirsmultiset_new_skip() {
286 let input_vec = ["a/", "b/", "a/c", "a/d/"]
287 .iter()
288 .map(|e| e.as_bytes().to_vec())
289 .collect();
290 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
291 .iter()
292 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
293 .collect();
294
295 // this was
296 // DirsMultiset::new(Manifest(&input_vec), Some(EntryState::Normal))
297 let new = DirsMultiset::from_manifest(&input_vec);
298 let expected = DirsMultiset {
299 inner: expected_inner,
300 };
301 // Skip does not affect a manifest
302 assert_eq!(expected, new);
303
304 let input_map = [
286 let input_map = [
305 ("a/", EntryState::Normal),
287 ("a/", EntryState::Normal),
306 ("a/b/", EntryState::Normal),
288 ("a/b/", EntryState::Normal),
307 ("a/c", EntryState::Removed),
289 ("a/c", EntryState::Removed),
308 ("a/d/", EntryState::Merged),
290 ("a/d/", EntryState::Merged),
309 ]
291 ]
310 .iter()
292 .iter()
311 .map(|(f, state)| {
293 .map(|(f, state)| {
312 (
294 (
313 f.as_bytes().to_vec(),
295 f.as_bytes().to_vec(),
314 DirstateEntry {
296 DirstateEntry {
315 state: *state,
297 state: *state,
316 mode: 0,
298 mode: 0,
317 mtime: 0,
299 mtime: 0,
318 size: 0,
300 size: 0,
319 },
301 },
320 )
302 )
321 })
303 })
322 .collect();
304 .collect();
323
305
324 // "a" incremented with "a/c" and "a/d/"
306 // "a" incremented with "a/c" and "a/d/"
325 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
307 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
326 .iter()
308 .iter()
327 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
309 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
328 .collect();
310 .collect();
329
311
330 let new =
312 let new =
331 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal));
313 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal));
332 let expected = DirsMultiset {
314 let expected = DirsMultiset {
333 inner: expected_inner,
315 inner: expected_inner,
334 };
316 };
335 assert_eq!(expected, new);
317 assert_eq!(expected, new);
336 }
318 }
337
319
338 }
320 }
General Comments 0
You need to be logged in to leave comments. Login now