##// END OF EJS Templates
rust-warnings: fix warnings in tests...
Raphaël Gomès -
r44342:d8a96ceb default
parent child Browse files
Show More
@@ -1,346 +1,346 b''
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::utils::hg_path::{HgPath, HgPathBuf};
11 use crate::utils::hg_path::{HgPath, HgPathBuf};
12 use crate::{
12 use crate::{
13 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
13 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
14 FastHashMap,
14 FastHashMap,
15 };
15 };
16 use std::collections::hash_map::{self, Entry};
16 use std::collections::hash_map::{self, Entry};
17
17
18 // could be encapsulated if we care API stability more seriously
18 // could be encapsulated if we care API stability more seriously
19 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>;
19 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>;
20
20
21 #[derive(PartialEq, Debug)]
21 #[derive(PartialEq, Debug)]
22 pub struct DirsMultiset {
22 pub struct DirsMultiset {
23 inner: FastHashMap<HgPathBuf, u32>,
23 inner: FastHashMap<HgPathBuf, u32>,
24 }
24 }
25
25
26 impl DirsMultiset {
26 impl DirsMultiset {
27 /// Initializes the multiset from a dirstate.
27 /// Initializes the multiset from a dirstate.
28 ///
28 ///
29 /// If `skip_state` is provided, skips dirstate entries with equal state.
29 /// If `skip_state` is provided, skips dirstate entries with equal state.
30 pub fn from_dirstate(
30 pub fn from_dirstate(
31 dirstate: &FastHashMap<HgPathBuf, DirstateEntry>,
31 dirstate: &FastHashMap<HgPathBuf, DirstateEntry>,
32 skip_state: Option<EntryState>,
32 skip_state: Option<EntryState>,
33 ) -> Result<Self, DirstateMapError> {
33 ) -> Result<Self, DirstateMapError> {
34 let mut multiset = DirsMultiset {
34 let mut multiset = DirsMultiset {
35 inner: FastHashMap::default(),
35 inner: FastHashMap::default(),
36 };
36 };
37
37
38 for (filename, DirstateEntry { state, .. }) in dirstate {
38 for (filename, DirstateEntry { state, .. }) in dirstate {
39 // This `if` is optimized out of the loop
39 // This `if` is optimized out of the loop
40 if let Some(skip) = skip_state {
40 if let Some(skip) = skip_state {
41 if skip != *state {
41 if skip != *state {
42 multiset.add_path(filename)?;
42 multiset.add_path(filename)?;
43 }
43 }
44 } else {
44 } else {
45 multiset.add_path(filename)?;
45 multiset.add_path(filename)?;
46 }
46 }
47 }
47 }
48
48
49 Ok(multiset)
49 Ok(multiset)
50 }
50 }
51
51
52 /// Initializes the multiset from a manifest.
52 /// Initializes the multiset from a manifest.
53 pub fn from_manifest(
53 pub fn from_manifest(
54 manifest: &[impl AsRef<HgPath>],
54 manifest: &[impl AsRef<HgPath>],
55 ) -> Result<Self, DirstateMapError> {
55 ) -> Result<Self, DirstateMapError> {
56 let mut multiset = DirsMultiset {
56 let mut multiset = DirsMultiset {
57 inner: FastHashMap::default(),
57 inner: FastHashMap::default(),
58 };
58 };
59
59
60 for filename in manifest {
60 for filename in manifest {
61 multiset.add_path(filename.as_ref())?;
61 multiset.add_path(filename.as_ref())?;
62 }
62 }
63
63
64 Ok(multiset)
64 Ok(multiset)
65 }
65 }
66
66
67 /// Increases the count of deepest directory contained in the path.
67 /// Increases the count of deepest directory contained in the path.
68 ///
68 ///
69 /// If the directory is not yet in the map, adds its parents.
69 /// If the directory is not yet in the map, adds its parents.
70 pub fn add_path(
70 pub fn add_path(
71 &mut self,
71 &mut self,
72 path: impl AsRef<HgPath>,
72 path: impl AsRef<HgPath>,
73 ) -> Result<(), DirstateMapError> {
73 ) -> Result<(), DirstateMapError> {
74 for subpath in files::find_dirs(path.as_ref()) {
74 for subpath in files::find_dirs(path.as_ref()) {
75 if subpath.as_bytes().last() == Some(&b'/') {
75 if subpath.as_bytes().last() == Some(&b'/') {
76 // TODO Remove this once PathAuditor is certified
76 // TODO Remove this once PathAuditor is certified
77 // as the only entrypoint for path data
77 // as the only entrypoint for path data
78 return Err(DirstateMapError::ConsecutiveSlashes);
78 return Err(DirstateMapError::ConsecutiveSlashes);
79 }
79 }
80 if let Some(val) = self.inner.get_mut(subpath) {
80 if let Some(val) = self.inner.get_mut(subpath) {
81 *val += 1;
81 *val += 1;
82 break;
82 break;
83 }
83 }
84 self.inner.insert(subpath.to_owned(), 1);
84 self.inner.insert(subpath.to_owned(), 1);
85 }
85 }
86 Ok(())
86 Ok(())
87 }
87 }
88
88
89 /// Decreases the count of deepest directory contained in the path.
89 /// Decreases the count of deepest directory contained in the path.
90 ///
90 ///
91 /// If it is the only reference, decreases all parents until one is
91 /// If it is the only reference, decreases all parents until one is
92 /// removed.
92 /// removed.
93 /// If the directory is not in the map, something horrible has happened.
93 /// If the directory is not in the map, something horrible has happened.
94 pub fn delete_path(
94 pub fn delete_path(
95 &mut self,
95 &mut self,
96 path: impl AsRef<HgPath>,
96 path: impl AsRef<HgPath>,
97 ) -> Result<(), DirstateMapError> {
97 ) -> Result<(), DirstateMapError> {
98 for subpath in files::find_dirs(path.as_ref()) {
98 for subpath in files::find_dirs(path.as_ref()) {
99 match self.inner.entry(subpath.to_owned()) {
99 match self.inner.entry(subpath.to_owned()) {
100 Entry::Occupied(mut entry) => {
100 Entry::Occupied(mut entry) => {
101 let val = entry.get().clone();
101 let val = entry.get().clone();
102 if val > 1 {
102 if val > 1 {
103 entry.insert(val - 1);
103 entry.insert(val - 1);
104 break;
104 break;
105 }
105 }
106 entry.remove();
106 entry.remove();
107 }
107 }
108 Entry::Vacant(_) => {
108 Entry::Vacant(_) => {
109 return Err(DirstateMapError::PathNotFound(
109 return Err(DirstateMapError::PathNotFound(
110 path.as_ref().to_owned(),
110 path.as_ref().to_owned(),
111 ))
111 ))
112 }
112 }
113 };
113 };
114 }
114 }
115
115
116 Ok(())
116 Ok(())
117 }
117 }
118
118
119 pub fn contains(&self, key: impl AsRef<HgPath>) -> bool {
119 pub fn contains(&self, key: impl AsRef<HgPath>) -> bool {
120 self.inner.contains_key(key.as_ref())
120 self.inner.contains_key(key.as_ref())
121 }
121 }
122
122
123 pub fn iter(&self) -> DirsMultisetIter {
123 pub fn iter(&self) -> DirsMultisetIter {
124 self.inner.keys()
124 self.inner.keys()
125 }
125 }
126
126
127 pub fn len(&self) -> usize {
127 pub fn len(&self) -> usize {
128 self.inner.len()
128 self.inner.len()
129 }
129 }
130 }
130 }
131
131
132 #[cfg(test)]
132 #[cfg(test)]
133 mod tests {
133 mod tests {
134 use super::*;
134 use super::*;
135
135
136 #[test]
136 #[test]
137 fn test_delete_path_path_not_found() {
137 fn test_delete_path_path_not_found() {
138 let manifest: Vec<HgPathBuf> = vec![];
138 let manifest: Vec<HgPathBuf> = vec![];
139 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
139 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
140 let path = HgPathBuf::from_bytes(b"doesnotexist/");
140 let path = HgPathBuf::from_bytes(b"doesnotexist/");
141 assert_eq!(
141 assert_eq!(
142 Err(DirstateMapError::PathNotFound(path.to_owned())),
142 Err(DirstateMapError::PathNotFound(path.to_owned())),
143 map.delete_path(&path)
143 map.delete_path(&path)
144 );
144 );
145 }
145 }
146
146
147 #[test]
147 #[test]
148 fn test_delete_path_empty_path() {
148 fn test_delete_path_empty_path() {
149 let mut map =
149 let mut map =
150 DirsMultiset::from_manifest(&vec![HgPathBuf::new()]).unwrap();
150 DirsMultiset::from_manifest(&vec![HgPathBuf::new()]).unwrap();
151 let path = HgPath::new(b"");
151 let path = HgPath::new(b"");
152 assert_eq!(Ok(()), map.delete_path(path));
152 assert_eq!(Ok(()), map.delete_path(path));
153 assert_eq!(
153 assert_eq!(
154 Err(DirstateMapError::PathNotFound(path.to_owned())),
154 Err(DirstateMapError::PathNotFound(path.to_owned())),
155 map.delete_path(path)
155 map.delete_path(path)
156 );
156 );
157 }
157 }
158
158
159 #[test]
159 #[test]
160 fn test_delete_path_successful() {
160 fn test_delete_path_successful() {
161 let mut map = DirsMultiset {
161 let mut map = DirsMultiset {
162 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
162 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
163 .iter()
163 .iter()
164 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
164 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
165 .collect(),
165 .collect(),
166 };
166 };
167
167
168 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
168 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
169 eprintln!("{:?}", map);
169 eprintln!("{:?}", map);
170 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
170 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
171 eprintln!("{:?}", map);
171 eprintln!("{:?}", map);
172 assert_eq!(
172 assert_eq!(
173 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
173 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
174 b"a/b/"
174 b"a/b/"
175 ))),
175 ))),
176 map.delete_path(HgPath::new(b"a/b/"))
176 map.delete_path(HgPath::new(b"a/b/"))
177 );
177 );
178
178
179 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
179 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
180 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
180 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
181 eprintln!("{:?}", map);
181 eprintln!("{:?}", map);
182 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
182 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
183 eprintln!("{:?}", map);
183 eprintln!("{:?}", map);
184
184
185 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
185 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
186 assert_eq!(
186 assert_eq!(
187 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
187 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
188 b"a/c/"
188 b"a/c/"
189 ))),
189 ))),
190 map.delete_path(HgPath::new(b"a/c/"))
190 map.delete_path(HgPath::new(b"a/c/"))
191 );
191 );
192 }
192 }
193
193
194 #[test]
194 #[test]
195 fn test_add_path_empty_path() {
195 fn test_add_path_empty_path() {
196 let manifest: Vec<HgPathBuf> = vec![];
196 let manifest: Vec<HgPathBuf> = vec![];
197 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
197 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
198 let path = HgPath::new(b"");
198 let path = HgPath::new(b"");
199 map.add_path(path);
199 map.add_path(path).unwrap();
200
200
201 assert_eq!(1, map.len());
201 assert_eq!(1, map.len());
202 }
202 }
203
203
204 #[test]
204 #[test]
205 fn test_add_path_successful() {
205 fn test_add_path_successful() {
206 let manifest: Vec<HgPathBuf> = vec![];
206 let manifest: Vec<HgPathBuf> = vec![];
207 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
207 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
208
208
209 map.add_path(HgPath::new(b"a/"));
209 map.add_path(HgPath::new(b"a/")).unwrap();
210 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
210 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
211 assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
211 assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
212 assert_eq!(2, map.len());
212 assert_eq!(2, map.len());
213
213
214 // Non directory should be ignored
214 // Non directory should be ignored
215 map.add_path(HgPath::new(b"a"));
215 map.add_path(HgPath::new(b"a")).unwrap();
216 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
216 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
217 assert_eq!(2, map.len());
217 assert_eq!(2, map.len());
218
218
219 // Non directory will still add its base
219 // Non directory will still add its base
220 map.add_path(HgPath::new(b"a/b"));
220 map.add_path(HgPath::new(b"a/b")).unwrap();
221 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
221 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
222 assert_eq!(2, map.len());
222 assert_eq!(2, map.len());
223
223
224 // Duplicate path works
224 // Duplicate path works
225 map.add_path(HgPath::new(b"a/"));
225 map.add_path(HgPath::new(b"a/")).unwrap();
226 assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
226 assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
227
227
228 // Nested dir adds to its base
228 // Nested dir adds to its base
229 map.add_path(HgPath::new(b"a/b/"));
229 map.add_path(HgPath::new(b"a/b/")).unwrap();
230 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
230 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
231 assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
231 assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
232
232
233 // but not its base's base, because it already existed
233 // but not its base's base, because it already existed
234 map.add_path(HgPath::new(b"a/b/c/"));
234 map.add_path(HgPath::new(b"a/b/c/")).unwrap();
235 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
235 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
236 assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
236 assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
237
237
238 map.add_path(HgPath::new(b"a/c/"));
238 map.add_path(HgPath::new(b"a/c/")).unwrap();
239 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
239 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
240
240
241 let expected = DirsMultiset {
241 let expected = DirsMultiset {
242 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
242 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
243 .iter()
243 .iter()
244 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
244 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
245 .collect(),
245 .collect(),
246 };
246 };
247 assert_eq!(map, expected);
247 assert_eq!(map, expected);
248 }
248 }
249
249
250 #[test]
250 #[test]
251 fn test_dirsmultiset_new_empty() {
251 fn test_dirsmultiset_new_empty() {
252 let manifest: Vec<HgPathBuf> = vec![];
252 let manifest: Vec<HgPathBuf> = vec![];
253 let new = DirsMultiset::from_manifest(&manifest).unwrap();
253 let new = DirsMultiset::from_manifest(&manifest).unwrap();
254 let expected = DirsMultiset {
254 let expected = DirsMultiset {
255 inner: FastHashMap::default(),
255 inner: FastHashMap::default(),
256 };
256 };
257 assert_eq!(expected, new);
257 assert_eq!(expected, new);
258
258
259 let new = DirsMultiset::from_dirstate(&FastHashMap::default(), None)
259 let new = DirsMultiset::from_dirstate(&FastHashMap::default(), None)
260 .unwrap();
260 .unwrap();
261 let expected = DirsMultiset {
261 let expected = DirsMultiset {
262 inner: FastHashMap::default(),
262 inner: FastHashMap::default(),
263 };
263 };
264 assert_eq!(expected, new);
264 assert_eq!(expected, new);
265 }
265 }
266
266
267 #[test]
267 #[test]
268 fn test_dirsmultiset_new_no_skip() {
268 fn test_dirsmultiset_new_no_skip() {
269 let input_vec: Vec<HgPathBuf> = ["a/", "b/", "a/c", "a/d/"]
269 let input_vec: Vec<HgPathBuf> = ["a/", "b/", "a/c", "a/d/"]
270 .iter()
270 .iter()
271 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
271 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
272 .collect();
272 .collect();
273 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
273 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
274 .iter()
274 .iter()
275 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
275 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
276 .collect();
276 .collect();
277
277
278 let new = DirsMultiset::from_manifest(&input_vec).unwrap();
278 let new = DirsMultiset::from_manifest(&input_vec).unwrap();
279 let expected = DirsMultiset {
279 let expected = DirsMultiset {
280 inner: expected_inner,
280 inner: expected_inner,
281 };
281 };
282 assert_eq!(expected, new);
282 assert_eq!(expected, new);
283
283
284 let input_map = ["a/", "b/", "a/c", "a/d/"]
284 let input_map = ["a/", "b/", "a/c", "a/d/"]
285 .iter()
285 .iter()
286 .map(|f| {
286 .map(|f| {
287 (
287 (
288 HgPathBuf::from_bytes(f.as_bytes()),
288 HgPathBuf::from_bytes(f.as_bytes()),
289 DirstateEntry {
289 DirstateEntry {
290 state: EntryState::Normal,
290 state: EntryState::Normal,
291 mode: 0,
291 mode: 0,
292 mtime: 0,
292 mtime: 0,
293 size: 0,
293 size: 0,
294 },
294 },
295 )
295 )
296 })
296 })
297 .collect();
297 .collect();
298 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
298 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
299 .iter()
299 .iter()
300 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
300 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
301 .collect();
301 .collect();
302
302
303 let new = DirsMultiset::from_dirstate(&input_map, None).unwrap();
303 let new = DirsMultiset::from_dirstate(&input_map, None).unwrap();
304 let expected = DirsMultiset {
304 let expected = DirsMultiset {
305 inner: expected_inner,
305 inner: expected_inner,
306 };
306 };
307 assert_eq!(expected, new);
307 assert_eq!(expected, new);
308 }
308 }
309
309
310 #[test]
310 #[test]
311 fn test_dirsmultiset_new_skip() {
311 fn test_dirsmultiset_new_skip() {
312 let input_map = [
312 let input_map = [
313 ("a/", EntryState::Normal),
313 ("a/", EntryState::Normal),
314 ("a/b/", EntryState::Normal),
314 ("a/b/", EntryState::Normal),
315 ("a/c", EntryState::Removed),
315 ("a/c", EntryState::Removed),
316 ("a/d/", EntryState::Merged),
316 ("a/d/", EntryState::Merged),
317 ]
317 ]
318 .iter()
318 .iter()
319 .map(|(f, state)| {
319 .map(|(f, state)| {
320 (
320 (
321 HgPathBuf::from_bytes(f.as_bytes()),
321 HgPathBuf::from_bytes(f.as_bytes()),
322 DirstateEntry {
322 DirstateEntry {
323 state: *state,
323 state: *state,
324 mode: 0,
324 mode: 0,
325 mtime: 0,
325 mtime: 0,
326 size: 0,
326 size: 0,
327 },
327 },
328 )
328 )
329 })
329 })
330 .collect();
330 .collect();
331
331
332 // "a" incremented with "a/c" and "a/d/"
332 // "a" incremented with "a/c" and "a/d/"
333 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
333 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
334 .iter()
334 .iter()
335 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
335 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
336 .collect();
336 .collect();
337
337
338 let new =
338 let new =
339 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal))
339 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal))
340 .unwrap();
340 .unwrap();
341 let expected = DirsMultiset {
341 let expected = DirsMultiset {
342 inner: expected_inner,
342 inner: expected_inner,
343 };
343 };
344 assert_eq!(expected, new);
344 assert_eq!(expected, new);
345 }
345 }
346 }
346 }
@@ -1,435 +1,436 b''
1 // dirstate_map.rs
1 // dirstate_map.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 use crate::{
8 use crate::{
9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
10 pack_dirstate, parse_dirstate,
10 pack_dirstate, parse_dirstate,
11 utils::{
11 utils::{
12 files::normalize_case,
12 files::normalize_case,
13 hg_path::{HgPath, HgPathBuf},
13 hg_path::{HgPath, HgPathBuf},
14 },
14 },
15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
17 };
17 };
18 use core::borrow::Borrow;
18 use core::borrow::Borrow;
19 use std::collections::HashSet;
19 use std::collections::HashSet;
20 use std::convert::TryInto;
20 use std::convert::TryInto;
21 use std::iter::FromIterator;
21 use std::iter::FromIterator;
22 use std::ops::Deref;
22 use std::ops::Deref;
23 use std::time::Duration;
23 use std::time::Duration;
24
24
25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
26
26
27 const NULL_ID: [u8; 20] = [0; 20];
27 const NULL_ID: [u8; 20] = [0; 20];
28 const MTIME_UNSET: i32 = -1;
28 const MTIME_UNSET: i32 = -1;
29
29
30 #[derive(Default)]
30 #[derive(Default)]
31 pub struct DirstateMap {
31 pub struct DirstateMap {
32 state_map: StateMap,
32 state_map: StateMap,
33 pub copy_map: CopyMap,
33 pub copy_map: CopyMap,
34 file_fold_map: Option<FileFoldMap>,
34 file_fold_map: Option<FileFoldMap>,
35 pub dirs: Option<DirsMultiset>,
35 pub dirs: Option<DirsMultiset>,
36 pub all_dirs: Option<DirsMultiset>,
36 pub all_dirs: Option<DirsMultiset>,
37 non_normal_set: HashSet<HgPathBuf>,
37 non_normal_set: HashSet<HgPathBuf>,
38 other_parent_set: HashSet<HgPathBuf>,
38 other_parent_set: HashSet<HgPathBuf>,
39 parents: Option<DirstateParents>,
39 parents: Option<DirstateParents>,
40 dirty_parents: bool,
40 dirty_parents: bool,
41 }
41 }
42
42
43 /// Should only really be used in python interface code, for clarity
43 /// Should only really be used in python interface code, for clarity
44 impl Deref for DirstateMap {
44 impl Deref for DirstateMap {
45 type Target = StateMap;
45 type Target = StateMap;
46
46
47 fn deref(&self) -> &Self::Target {
47 fn deref(&self) -> &Self::Target {
48 &self.state_map
48 &self.state_map
49 }
49 }
50 }
50 }
51
51
52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 iter: I,
54 iter: I,
55 ) -> Self {
55 ) -> Self {
56 Self {
56 Self {
57 state_map: iter.into_iter().collect(),
57 state_map: iter.into_iter().collect(),
58 ..Self::default()
58 ..Self::default()
59 }
59 }
60 }
60 }
61 }
61 }
62
62
63 impl DirstateMap {
63 impl DirstateMap {
64 pub fn new() -> Self {
64 pub fn new() -> Self {
65 Self::default()
65 Self::default()
66 }
66 }
67
67
68 pub fn clear(&mut self) {
68 pub fn clear(&mut self) {
69 self.state_map.clear();
69 self.state_map.clear();
70 self.copy_map.clear();
70 self.copy_map.clear();
71 self.file_fold_map = None;
71 self.file_fold_map = None;
72 self.non_normal_set.clear();
72 self.non_normal_set.clear();
73 self.other_parent_set.clear();
73 self.other_parent_set.clear();
74 self.set_parents(&DirstateParents {
74 self.set_parents(&DirstateParents {
75 p1: NULL_ID,
75 p1: NULL_ID,
76 p2: NULL_ID,
76 p2: NULL_ID,
77 })
77 })
78 }
78 }
79
79
80 /// Add a tracked file to the dirstate
80 /// Add a tracked file to the dirstate
81 pub fn add_file(
81 pub fn add_file(
82 &mut self,
82 &mut self,
83 filename: &HgPath,
83 filename: &HgPath,
84 old_state: EntryState,
84 old_state: EntryState,
85 entry: DirstateEntry,
85 entry: DirstateEntry,
86 ) -> Result<(), DirstateMapError> {
86 ) -> Result<(), DirstateMapError> {
87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 {
88 {
89 if let Some(ref mut dirs) = self.dirs {
89 if let Some(ref mut dirs) = self.dirs {
90 dirs.add_path(filename)?;
90 dirs.add_path(filename)?;
91 }
91 }
92 }
92 }
93 if old_state == EntryState::Unknown {
93 if old_state == EntryState::Unknown {
94 if let Some(ref mut all_dirs) = self.all_dirs {
94 if let Some(ref mut all_dirs) = self.all_dirs {
95 all_dirs.add_path(filename)?;
95 all_dirs.add_path(filename)?;
96 }
96 }
97 }
97 }
98 self.state_map.insert(filename.to_owned(), entry.to_owned());
98 self.state_map.insert(filename.to_owned(), entry.to_owned());
99
99
100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 self.non_normal_set.insert(filename.to_owned());
101 self.non_normal_set.insert(filename.to_owned());
102 }
102 }
103
103
104 if entry.size == SIZE_FROM_OTHER_PARENT {
104 if entry.size == SIZE_FROM_OTHER_PARENT {
105 self.other_parent_set.insert(filename.to_owned());
105 self.other_parent_set.insert(filename.to_owned());
106 }
106 }
107 Ok(())
107 Ok(())
108 }
108 }
109
109
110 /// Mark a file as removed in the dirstate.
110 /// Mark a file as removed in the dirstate.
111 ///
111 ///
112 /// The `size` parameter is used to store sentinel values that indicate
112 /// The `size` parameter is used to store sentinel values that indicate
113 /// the file's previous state. In the future, we should refactor this
113 /// the file's previous state. In the future, we should refactor this
114 /// to be more explicit about what that state is.
114 /// to be more explicit about what that state is.
115 pub fn remove_file(
115 pub fn remove_file(
116 &mut self,
116 &mut self,
117 filename: &HgPath,
117 filename: &HgPath,
118 old_state: EntryState,
118 old_state: EntryState,
119 size: i32,
119 size: i32,
120 ) -> Result<(), DirstateMapError> {
120 ) -> Result<(), DirstateMapError> {
121 if old_state != EntryState::Unknown && old_state != EntryState::Removed
121 if old_state != EntryState::Unknown && old_state != EntryState::Removed
122 {
122 {
123 if let Some(ref mut dirs) = self.dirs {
123 if let Some(ref mut dirs) = self.dirs {
124 dirs.delete_path(filename)?;
124 dirs.delete_path(filename)?;
125 }
125 }
126 }
126 }
127 if old_state == EntryState::Unknown {
127 if old_state == EntryState::Unknown {
128 if let Some(ref mut all_dirs) = self.all_dirs {
128 if let Some(ref mut all_dirs) = self.all_dirs {
129 all_dirs.add_path(filename)?;
129 all_dirs.add_path(filename)?;
130 }
130 }
131 }
131 }
132
132
133 if let Some(ref mut file_fold_map) = self.file_fold_map {
133 if let Some(ref mut file_fold_map) = self.file_fold_map {
134 file_fold_map.remove(&normalize_case(filename));
134 file_fold_map.remove(&normalize_case(filename));
135 }
135 }
136 self.state_map.insert(
136 self.state_map.insert(
137 filename.to_owned(),
137 filename.to_owned(),
138 DirstateEntry {
138 DirstateEntry {
139 state: EntryState::Removed,
139 state: EntryState::Removed,
140 mode: 0,
140 mode: 0,
141 size,
141 size,
142 mtime: 0,
142 mtime: 0,
143 },
143 },
144 );
144 );
145 self.non_normal_set.insert(filename.to_owned());
145 self.non_normal_set.insert(filename.to_owned());
146 Ok(())
146 Ok(())
147 }
147 }
148
148
149 /// Remove a file from the dirstate.
149 /// Remove a file from the dirstate.
150 /// Returns `true` if the file was previously recorded.
150 /// Returns `true` if the file was previously recorded.
151 pub fn drop_file(
151 pub fn drop_file(
152 &mut self,
152 &mut self,
153 filename: &HgPath,
153 filename: &HgPath,
154 old_state: EntryState,
154 old_state: EntryState,
155 ) -> Result<bool, DirstateMapError> {
155 ) -> Result<bool, DirstateMapError> {
156 let exists = self.state_map.remove(filename).is_some();
156 let exists = self.state_map.remove(filename).is_some();
157
157
158 if exists {
158 if exists {
159 if old_state != EntryState::Removed {
159 if old_state != EntryState::Removed {
160 if let Some(ref mut dirs) = self.dirs {
160 if let Some(ref mut dirs) = self.dirs {
161 dirs.delete_path(filename)?;
161 dirs.delete_path(filename)?;
162 }
162 }
163 }
163 }
164 if let Some(ref mut all_dirs) = self.all_dirs {
164 if let Some(ref mut all_dirs) = self.all_dirs {
165 all_dirs.delete_path(filename)?;
165 all_dirs.delete_path(filename)?;
166 }
166 }
167 }
167 }
168 if let Some(ref mut file_fold_map) = self.file_fold_map {
168 if let Some(ref mut file_fold_map) = self.file_fold_map {
169 file_fold_map.remove(&normalize_case(filename));
169 file_fold_map.remove(&normalize_case(filename));
170 }
170 }
171 self.non_normal_set.remove(filename);
171 self.non_normal_set.remove(filename);
172
172
173 Ok(exists)
173 Ok(exists)
174 }
174 }
175
175
176 pub fn clear_ambiguous_times(
176 pub fn clear_ambiguous_times(
177 &mut self,
177 &mut self,
178 filenames: Vec<HgPathBuf>,
178 filenames: Vec<HgPathBuf>,
179 now: i32,
179 now: i32,
180 ) {
180 ) {
181 for filename in filenames {
181 for filename in filenames {
182 let mut changed = false;
182 let mut changed = false;
183 self.state_map
183 self.state_map
184 .entry(filename.to_owned())
184 .entry(filename.to_owned())
185 .and_modify(|entry| {
185 .and_modify(|entry| {
186 if entry.state == EntryState::Normal && entry.mtime == now
186 if entry.state == EntryState::Normal && entry.mtime == now
187 {
187 {
188 changed = true;
188 changed = true;
189 *entry = DirstateEntry {
189 *entry = DirstateEntry {
190 mtime: MTIME_UNSET,
190 mtime: MTIME_UNSET,
191 ..*entry
191 ..*entry
192 };
192 };
193 }
193 }
194 });
194 });
195 if changed {
195 if changed {
196 self.non_normal_set.insert(filename.to_owned());
196 self.non_normal_set.insert(filename.to_owned());
197 }
197 }
198 }
198 }
199 }
199 }
200
200
201 pub fn non_normal_other_parent_entries(
201 pub fn non_normal_other_parent_entries(
202 &self,
202 &self,
203 ) -> (HashSet<HgPathBuf>, HashSet<HgPathBuf>) {
203 ) -> (HashSet<HgPathBuf>, HashSet<HgPathBuf>) {
204 let mut non_normal = HashSet::new();
204 let mut non_normal = HashSet::new();
205 let mut other_parent = HashSet::new();
205 let mut other_parent = HashSet::new();
206
206
207 for (
207 for (
208 filename,
208 filename,
209 DirstateEntry {
209 DirstateEntry {
210 state, size, mtime, ..
210 state, size, mtime, ..
211 },
211 },
212 ) in self.state_map.iter()
212 ) in self.state_map.iter()
213 {
213 {
214 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
214 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
215 non_normal.insert(filename.to_owned());
215 non_normal.insert(filename.to_owned());
216 }
216 }
217 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
217 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
218 {
218 {
219 other_parent.insert(filename.to_owned());
219 other_parent.insert(filename.to_owned());
220 }
220 }
221 }
221 }
222
222
223 (non_normal, other_parent)
223 (non_normal, other_parent)
224 }
224 }
225
225
226 /// Both of these setters and their uses appear to be the simplest way to
226 /// Both of these setters and their uses appear to be the simplest way to
227 /// emulate a Python lazy property, but it is ugly and unidiomatic.
227 /// emulate a Python lazy property, but it is ugly and unidiomatic.
228 /// TODO One day, rewriting this struct using the typestate might be a
228 /// TODO One day, rewriting this struct using the typestate might be a
229 /// good idea.
229 /// good idea.
230 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
230 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
231 if self.all_dirs.is_none() {
231 if self.all_dirs.is_none() {
232 self.all_dirs =
232 self.all_dirs =
233 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
233 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
234 }
234 }
235 Ok(())
235 Ok(())
236 }
236 }
237
237
238 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
238 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
239 if self.dirs.is_none() {
239 if self.dirs.is_none() {
240 self.dirs = Some(DirsMultiset::from_dirstate(
240 self.dirs = Some(DirsMultiset::from_dirstate(
241 &self.state_map,
241 &self.state_map,
242 Some(EntryState::Removed),
242 Some(EntryState::Removed),
243 )?);
243 )?);
244 }
244 }
245 Ok(())
245 Ok(())
246 }
246 }
247
247
248 pub fn has_tracked_dir(
248 pub fn has_tracked_dir(
249 &mut self,
249 &mut self,
250 directory: &HgPath,
250 directory: &HgPath,
251 ) -> Result<bool, DirstateMapError> {
251 ) -> Result<bool, DirstateMapError> {
252 self.set_dirs()?;
252 self.set_dirs()?;
253 Ok(self.dirs.as_ref().unwrap().contains(directory))
253 Ok(self.dirs.as_ref().unwrap().contains(directory))
254 }
254 }
255
255
256 pub fn has_dir(
256 pub fn has_dir(
257 &mut self,
257 &mut self,
258 directory: &HgPath,
258 directory: &HgPath,
259 ) -> Result<bool, DirstateMapError> {
259 ) -> Result<bool, DirstateMapError> {
260 self.set_all_dirs()?;
260 self.set_all_dirs()?;
261 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
261 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
262 }
262 }
263
263
264 pub fn parents(
264 pub fn parents(
265 &mut self,
265 &mut self,
266 file_contents: &[u8],
266 file_contents: &[u8],
267 ) -> Result<&DirstateParents, DirstateError> {
267 ) -> Result<&DirstateParents, DirstateError> {
268 if let Some(ref parents) = self.parents {
268 if let Some(ref parents) = self.parents {
269 return Ok(parents);
269 return Ok(parents);
270 }
270 }
271 let parents;
271 let parents;
272 if file_contents.len() == PARENT_SIZE * 2 {
272 if file_contents.len() == PARENT_SIZE * 2 {
273 parents = DirstateParents {
273 parents = DirstateParents {
274 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
274 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
275 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
275 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
276 .try_into()
276 .try_into()
277 .unwrap(),
277 .unwrap(),
278 };
278 };
279 } else if file_contents.is_empty() {
279 } else if file_contents.is_empty() {
280 parents = DirstateParents {
280 parents = DirstateParents {
281 p1: NULL_ID,
281 p1: NULL_ID,
282 p2: NULL_ID,
282 p2: NULL_ID,
283 };
283 };
284 } else {
284 } else {
285 return Err(DirstateError::Parse(DirstateParseError::Damaged));
285 return Err(DirstateError::Parse(DirstateParseError::Damaged));
286 }
286 }
287
287
288 self.parents = Some(parents);
288 self.parents = Some(parents);
289 Ok(self.parents.as_ref().unwrap())
289 Ok(self.parents.as_ref().unwrap())
290 }
290 }
291
291
292 pub fn set_parents(&mut self, parents: &DirstateParents) {
292 pub fn set_parents(&mut self, parents: &DirstateParents) {
293 self.parents = Some(parents.clone());
293 self.parents = Some(parents.clone());
294 self.dirty_parents = true;
294 self.dirty_parents = true;
295 }
295 }
296
296
297 pub fn read(
297 pub fn read(
298 &mut self,
298 &mut self,
299 file_contents: &[u8],
299 file_contents: &[u8],
300 ) -> Result<Option<DirstateParents>, DirstateError> {
300 ) -> Result<Option<DirstateParents>, DirstateError> {
301 if file_contents.is_empty() {
301 if file_contents.is_empty() {
302 return Ok(None);
302 return Ok(None);
303 }
303 }
304
304
305 let parents = parse_dirstate(
305 let parents = parse_dirstate(
306 &mut self.state_map,
306 &mut self.state_map,
307 &mut self.copy_map,
307 &mut self.copy_map,
308 file_contents,
308 file_contents,
309 )?;
309 )?;
310
310
311 if !self.dirty_parents {
311 if !self.dirty_parents {
312 self.set_parents(&parents);
312 self.set_parents(&parents);
313 }
313 }
314
314
315 Ok(Some(parents))
315 Ok(Some(parents))
316 }
316 }
317
317
318 pub fn pack(
318 pub fn pack(
319 &mut self,
319 &mut self,
320 parents: DirstateParents,
320 parents: DirstateParents,
321 now: Duration,
321 now: Duration,
322 ) -> Result<Vec<u8>, DirstateError> {
322 ) -> Result<Vec<u8>, DirstateError> {
323 let packed =
323 let packed =
324 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
324 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
325
325
326 self.dirty_parents = false;
326 self.dirty_parents = false;
327
327
328 let result = self.non_normal_other_parent_entries();
328 let result = self.non_normal_other_parent_entries();
329 self.non_normal_set = result.0;
329 self.non_normal_set = result.0;
330 self.other_parent_set = result.1;
330 self.other_parent_set = result.1;
331 Ok(packed)
331 Ok(packed)
332 }
332 }
333
333
334 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
334 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
335 if let Some(ref file_fold_map) = self.file_fold_map {
335 if let Some(ref file_fold_map) = self.file_fold_map {
336 return file_fold_map;
336 return file_fold_map;
337 }
337 }
338 let mut new_file_fold_map = FileFoldMap::default();
338 let mut new_file_fold_map = FileFoldMap::default();
339 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
339 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
340 {
340 {
341 if *state == EntryState::Removed {
341 if *state == EntryState::Removed {
342 new_file_fold_map
342 new_file_fold_map
343 .insert(normalize_case(filename), filename.to_owned());
343 .insert(normalize_case(filename), filename.to_owned());
344 }
344 }
345 }
345 }
346 self.file_fold_map = Some(new_file_fold_map);
346 self.file_fold_map = Some(new_file_fold_map);
347 self.file_fold_map.as_ref().unwrap()
347 self.file_fold_map.as_ref().unwrap()
348 }
348 }
349 }
349 }
350
350
351 #[cfg(test)]
351 #[cfg(test)]
352 mod tests {
352 mod tests {
353 use super::*;
353 use super::*;
354
354
355 #[test]
355 #[test]
356 fn test_dirs_multiset() {
356 fn test_dirs_multiset() {
357 let mut map = DirstateMap::new();
357 let mut map = DirstateMap::new();
358 assert!(map.dirs.is_none());
358 assert!(map.dirs.is_none());
359 assert!(map.all_dirs.is_none());
359 assert!(map.all_dirs.is_none());
360
360
361 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
361 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
362 assert!(map.all_dirs.is_some());
362 assert!(map.all_dirs.is_some());
363 assert!(map.dirs.is_none());
363 assert!(map.dirs.is_none());
364
364
365 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
365 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
366 assert!(map.dirs.is_some());
366 assert!(map.dirs.is_some());
367 }
367 }
368
368
369 #[test]
369 #[test]
370 fn test_add_file() {
370 fn test_add_file() {
371 let mut map = DirstateMap::new();
371 let mut map = DirstateMap::new();
372
372
373 assert_eq!(0, map.len());
373 assert_eq!(0, map.len());
374
374
375 map.add_file(
375 map.add_file(
376 HgPath::new(b"meh"),
376 HgPath::new(b"meh"),
377 EntryState::Normal,
377 EntryState::Normal,
378 DirstateEntry {
378 DirstateEntry {
379 state: EntryState::Normal,
379 state: EntryState::Normal,
380 mode: 1337,
380 mode: 1337,
381 mtime: 1337,
381 mtime: 1337,
382 size: 1337,
382 size: 1337,
383 },
383 },
384 );
384 )
385 .unwrap();
385
386
386 assert_eq!(1, map.len());
387 assert_eq!(1, map.len());
387 assert_eq!(0, map.non_normal_set.len());
388 assert_eq!(0, map.non_normal_set.len());
388 assert_eq!(0, map.other_parent_set.len());
389 assert_eq!(0, map.other_parent_set.len());
389 }
390 }
390
391
391 #[test]
392 #[test]
392 fn test_non_normal_other_parent_entries() {
393 fn test_non_normal_other_parent_entries() {
393 let map: DirstateMap = [
394 let map: DirstateMap = [
394 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
395 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
395 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
396 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
396 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
397 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
397 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
398 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
398 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
399 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
399 (b"f6", (EntryState::Added, 1337, 1337, -1)),
400 (b"f6", (EntryState::Added, 1337, 1337, -1)),
400 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
401 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
401 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
402 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
402 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
403 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
403 (b"fa", (EntryState::Added, 1337, -2, 1337)),
404 (b"fa", (EntryState::Added, 1337, -2, 1337)),
404 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
405 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
405 ]
406 ]
406 .iter()
407 .iter()
407 .map(|(fname, (state, mode, size, mtime))| {
408 .map(|(fname, (state, mode, size, mtime))| {
408 (
409 (
409 HgPathBuf::from_bytes(fname.as_ref()),
410 HgPathBuf::from_bytes(fname.as_ref()),
410 DirstateEntry {
411 DirstateEntry {
411 state: *state,
412 state: *state,
412 mode: *mode,
413 mode: *mode,
413 size: *size,
414 size: *size,
414 mtime: *mtime,
415 mtime: *mtime,
415 },
416 },
416 )
417 )
417 })
418 })
418 .collect();
419 .collect();
419
420
420 let non_normal = [
421 let non_normal = [
421 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
422 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
422 ]
423 ]
423 .iter()
424 .iter()
424 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
425 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
425 .collect();
426 .collect();
426
427
427 let mut other_parent = HashSet::new();
428 let mut other_parent = HashSet::new();
428 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
429 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
429
430
430 assert_eq!(
431 assert_eq!(
431 (non_normal, other_parent),
432 (non_normal, other_parent),
432 map.non_normal_other_parent_entries()
433 map.non_normal_other_parent_entries()
433 );
434 );
434 }
435 }
435 }
436 }
General Comments 0
You need to be logged in to leave comments. Login now