Mercurial > hg
comparison rust/hg-core/tests/test_missing_ancestors.rs @ 50979:4c5f6e95df84
rust: make `Revision` a newtype
This change is the one we've been building towards during this series.
The aim is to make `Revision` mean more than a simple integer, holding
the information that it is valid for a given revlog index.
While this still allows for programmer error, since creating a revision
directly and querying a different index with a "checked" revision are
still possible, the friction created by the newtype will hopefully make
us think twice about which type to use.
Enough of the Rust ecosystem relies on the newtype pattern to be
efficiently optimized away (even compiler in codegen testsĀ¹), so I'm not
worried about this being a fundamental problem.
[1] https://github.com/rust-lang/rust/blob/7a70647f195f6b0a0f1ebd72b1542ba91a32f43a/tests/codegen/vec-in-place.rs#L47
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Fri, 18 Aug 2023 14:34:29 +0200 |
parents | e98fd81bb151 |
children | ec7171748350 |
comparison
equal
deleted
inserted
replaced
50978:27e773aa607d | 50979:4c5f6e95df84 |
---|---|
24 let mut vg: VecGraph = Vec::with_capacity(nodes); | 24 let mut vg: VecGraph = Vec::with_capacity(nodes); |
25 for i in 0..nodes { | 25 for i in 0..nodes { |
26 if i == 0 || rng.gen_bool(rootprob) { | 26 if i == 0 || rng.gen_bool(rootprob) { |
27 vg.push([NULL_REVISION, NULL_REVISION]) | 27 vg.push([NULL_REVISION, NULL_REVISION]) |
28 } else if i == 1 { | 28 } else if i == 1 { |
29 vg.push([0, NULL_REVISION]) | 29 vg.push([Revision(0), NULL_REVISION]) |
30 } else if rng.gen_bool(mergeprob) { | 30 } else if rng.gen_bool(mergeprob) { |
31 let p1 = { | 31 let p1 = { |
32 if i == 2 || rng.gen_bool(prevprob) { | 32 if i == 2 || rng.gen_bool(prevprob) { |
33 (i - 1) as Revision | 33 Revision((i - 1) as BaseRevision) |
34 } else { | 34 } else { |
35 rng.gen_range(0..i - 1) as Revision | 35 Revision(rng.gen_range(0..i - 1) as BaseRevision) |
36 } | 36 } |
37 }; | 37 }; |
38 // p2 is a random revision lower than i and different from p1 | 38 // p2 is a random revision lower than i and different from p1 |
39 let mut p2 = rng.gen_range(0..i - 1) as Revision; | 39 let mut p2 = Revision(rng.gen_range(0..i - 1) as BaseRevision); |
40 if p2 >= p1 { | 40 if p2 >= p1 { |
41 p2 += 1; | 41 p2.0 += 1; |
42 } | 42 } |
43 vg.push([p1, p2]); | 43 vg.push([p1, p2]); |
44 } else if rng.gen_bool(prevprob) { | 44 } else if rng.gen_bool(prevprob) { |
45 vg.push([(i - 1) as Revision, NULL_REVISION]) | 45 vg.push([Revision((i - 1) as BaseRevision), NULL_REVISION]) |
46 } else { | 46 } else { |
47 vg.push([rng.gen_range(0..i - 1) as Revision, NULL_REVISION]) | 47 vg.push([ |
48 Revision(rng.gen_range(0..i - 1) as BaseRevision), | |
49 NULL_REVISION, | |
50 ]) | |
48 } | 51 } |
49 } | 52 } |
50 vg | 53 vg |
51 } | 54 } |
52 | 55 |
53 /// Compute the ancestors set of all revisions of a VecGraph | 56 /// Compute the ancestors set of all revisions of a VecGraph |
54 fn ancestors_sets(vg: &VecGraph) -> Vec<HashSet<Revision>> { | 57 fn ancestors_sets(vg: &VecGraph) -> Vec<HashSet<Revision>> { |
55 let mut ancs: Vec<HashSet<Revision>> = Vec::new(); | 58 let mut ancs: Vec<HashSet<Revision>> = Vec::new(); |
56 (0..vg.len()).for_each(|i| { | 59 (0..vg.len()).for_each(|i| { |
57 let mut ancs_i = HashSet::new(); | 60 let mut ancs_i = HashSet::new(); |
58 ancs_i.insert(i as Revision); | 61 ancs_i.insert(Revision(i as BaseRevision)); |
59 for p in vg[i].iter().cloned() { | 62 for p in vg[i].iter().cloned() { |
60 if p != NULL_REVISION { | 63 if p != NULL_REVISION { |
61 ancs_i.extend(&ancs[p as usize]); | 64 ancs_i.extend(&ancs[p.0 as usize]); |
62 } | 65 } |
63 } | 66 } |
64 ancs.push(ancs_i); | 67 ancs.push(ancs_i); |
65 }); | 68 }); |
66 ancs | 69 ancs |
113 revs.remove(&NULL_REVISION); | 116 revs.remove(&NULL_REVISION); |
114 self.history | 117 self.history |
115 .push(MissingAncestorsAction::RemoveAncestorsFrom(revs.clone())); | 118 .push(MissingAncestorsAction::RemoveAncestorsFrom(revs.clone())); |
116 for base in self.bases.iter().cloned() { | 119 for base in self.bases.iter().cloned() { |
117 if base != NULL_REVISION { | 120 if base != NULL_REVISION { |
118 for rev in &self.ancestors_sets[base as usize] { | 121 for rev in &self.ancestors_sets[base.0 as usize] { |
119 revs.remove(rev); | 122 revs.remove(rev); |
120 } | 123 } |
121 } | 124 } |
122 } | 125 } |
123 } | 126 } |
129 let revs_as_set: HashSet<Revision> = revs.into_iter().collect(); | 132 let revs_as_set: HashSet<Revision> = revs.into_iter().collect(); |
130 | 133 |
131 let mut missing: HashSet<Revision> = HashSet::new(); | 134 let mut missing: HashSet<Revision> = HashSet::new(); |
132 for rev in revs_as_set.iter().cloned() { | 135 for rev in revs_as_set.iter().cloned() { |
133 if rev != NULL_REVISION { | 136 if rev != NULL_REVISION { |
134 missing.extend(&self.ancestors_sets[rev as usize]) | 137 missing.extend(&self.ancestors_sets[rev.0 as usize]) |
135 } | 138 } |
136 } | 139 } |
137 self.history | 140 self.history |
138 .push(MissingAncestorsAction::MissingAncestors(revs_as_set)); | 141 .push(MissingAncestorsAction::MissingAncestors(revs_as_set)); |
139 | 142 |
140 for base in self.bases.iter().cloned() { | 143 for base in self.bases.iter().cloned() { |
141 if base != NULL_REVISION { | 144 if base != NULL_REVISION { |
142 for rev in &self.ancestors_sets[base as usize] { | 145 for rev in &self.ancestors_sets[base.0 as usize] { |
143 missing.remove(rev); | 146 missing.remove(rev); |
144 } | 147 } |
145 } | 148 } |
146 } | 149 } |
147 let mut res: Vec<Revision> = missing.iter().cloned().collect(); | 150 let mut res: Vec<Revision> = missing.iter().cloned().collect(); |
191 ) -> HashSet<Revision> { | 194 ) -> HashSet<Revision> { |
192 let mu = mu_opt.unwrap_or(1.1); | 195 let mu = mu_opt.unwrap_or(1.1); |
193 let sigma = sigma_opt.unwrap_or(0.8); | 196 let sigma = sigma_opt.unwrap_or(0.8); |
194 | 197 |
195 let log_normal = LogNormal::new(mu, sigma).unwrap(); | 198 let log_normal = LogNormal::new(mu, sigma).unwrap(); |
196 let nb = min(maxrev as usize, log_normal.sample(rng).floor() as usize); | 199 let nb = min(maxrev.0 as usize, log_normal.sample(rng).floor() as usize); |
197 | 200 |
198 let dist = Uniform::from(NULL_REVISION..maxrev); | 201 let dist = Uniform::from(NULL_REVISION.0..maxrev.0); |
199 rng.sample_iter(&dist).take(nb).collect() | 202 rng.sample_iter(&dist).take(nb).map(Revision).collect() |
200 } | 203 } |
201 | 204 |
202 /// Produces the hexadecimal representation of a slice of bytes | 205 /// Produces the hexadecimal representation of a slice of bytes |
203 fn hex_bytes(bytes: &[u8]) -> String { | 206 fn hex_bytes(bytes: &[u8]) -> String { |
204 let mut s = String::with_capacity(bytes.len() * 2); | 207 let mut s = String::with_capacity(bytes.len() * 2); |
292 for g in 0..graphcount { | 295 for g in 0..graphcount { |
293 if g != 0 && g % 100 == 0 { | 296 if g != 0 && g % 100 == 0 { |
294 eprintln!("Tested with {} graphs", g); | 297 eprintln!("Tested with {} graphs", g); |
295 } | 298 } |
296 let graph = build_random_graph(None, None, None, None); | 299 let graph = build_random_graph(None, None, None, None); |
297 let graph_len = graph.len() as Revision; | 300 let graph_len = Revision(graph.len() as BaseRevision); |
298 let ancestors_sets = ancestors_sets(&graph); | 301 let ancestors_sets = ancestors_sets(&graph); |
299 for _testno in 0..testcount { | 302 for _testno in 0..testcount { |
300 let bases: HashSet<Revision> = | 303 let bases: HashSet<Revision> = |
301 sample_revs(&mut rng, graph_len, None, None); | 304 sample_revs(&mut rng, graph_len, None, None); |
302 let mut inc = MissingAncestors::<VecGraph>::new( | 305 let mut inc = MissingAncestors::<VecGraph>::new( |