Show More
@@ -92,11 +92,19 def _updatesample(revs, heads, sample, p | |||
|
92 | 92 | dist.setdefault(p, d + 1) |
|
93 | 93 | visit.append(p) |
|
94 | 94 | |
|
95 | def _limitsample(sample, desiredlen): | |
|
96 |
"""return a random subset of sample of at most desiredlen item |
|
|
97 | if len(sample) > desiredlen: | |
|
98 | sample = set(random.sample(sample, desiredlen)) | |
|
99 | return sample | |
|
95 | def _limitsample(sample, desiredlen, randomize=True): | |
|
96 | """return a random subset of sample of at most desiredlen item. | |
|
97 | ||
|
98 | If randomize is False, though, a deterministic subset is returned. | |
|
99 | This is meant for integration tests. | |
|
100 | """ | |
|
101 | if len(sample) <= desiredlen: | |
|
102 | return sample | |
|
103 | if randomize: | |
|
104 | return set(random.sample(sample, desiredlen)) | |
|
105 | sample = list(sample) | |
|
106 | sample.sort() | |
|
107 | return set(sample[:desiredlen]) | |
|
100 | 108 | |
|
101 | 109 | class partialdiscovery(object): |
|
102 | 110 | """an object representing ongoing discovery |
@@ -110,7 +118,7 class partialdiscovery(object): | |||
|
110 | 118 | (all tracked revisions are known locally) |
|
111 | 119 | """ |
|
112 | 120 | |
|
113 | def __init__(self, repo, targetheads, respectsize): | |
|
121 | def __init__(self, repo, targetheads, respectsize, randomize=True): | |
|
114 | 122 | self._repo = repo |
|
115 | 123 | self._targetheads = targetheads |
|
116 | 124 | self._common = repo.changelog.incrementalmissingrevs() |
@@ -118,6 +126,7 class partialdiscovery(object): | |||
|
118 | 126 | self.missing = set() |
|
119 | 127 | self._childrenmap = None |
|
120 | 128 | self._respectsize = respectsize |
|
129 | self.randomize = randomize | |
|
121 | 130 | |
|
122 | 131 | def addcommons(self, commons): |
|
123 | 132 | """register nodes known as common""" |
@@ -222,7 +231,7 class partialdiscovery(object): | |||
|
222 | 231 | sample = set(self._repo.revs('heads(%ld)', revs)) |
|
223 | 232 | |
|
224 | 233 | if len(sample) >= size: |
|
225 | return _limitsample(sample, size) | |
|
234 | return _limitsample(sample, size, randomize=self.randomize) | |
|
226 | 235 | |
|
227 | 236 | _updatesample(None, headrevs, sample, self._parentsgetter(), |
|
228 | 237 | quicksamplesize=size) |
@@ -249,10 +258,15 class partialdiscovery(object): | |||
|
249 | 258 | if not self._respectsize: |
|
250 | 259 | size = max(size, min(len(revsroots), len(revsheads))) |
|
251 | 260 | |
|
252 | sample = _limitsample(sample, size) | |
|
261 | sample = _limitsample(sample, size, randomize=self.randomize) | |
|
253 | 262 | if len(sample) < size: |
|
254 | 263 | more = size - len(sample) |
|
255 |
|
|
|
264 | takefrom = list(revs - sample) | |
|
265 | if self.randomize: | |
|
266 | sample.update(random.sample(takefrom, more)) | |
|
267 | else: | |
|
268 | takefrom.sort() | |
|
269 | sample.update(takefrom[:more]) | |
|
256 | 270 | return sample |
|
257 | 271 | |
|
258 | 272 | def findcommonheads(ui, local, remote, |
@@ -31,6 +31,7 pub struct PartialDiscovery<G: Graph + C | |||
|
31 | 31 | missing: HashSet<Revision>, |
|
32 | 32 | rng: Rng, |
|
33 | 33 | respect_size: bool, |
|
34 | randomize: bool, | |
|
34 | 35 | } |
|
35 | 36 | |
|
36 | 37 | pub struct DiscoveryStats { |
@@ -151,14 +152,26 impl<G: Graph + Clone> PartialDiscovery< | |||
|
151 | 152 | /// will interpret the size argument requested by the caller. If it's |
|
152 | 153 | /// `false`, they are allowed to produce a sample whose size is more |
|
153 | 154 | /// appropriate to the situation (typically bigger). |
|
155 | /// | |
|
156 | /// The `randomize` boolean affects sampling, and specifically how | |
|
157 | /// limiting or last-minute expanding is been done: | |
|
158 | /// | |
|
159 | /// If `true`, both will perform random picking from `self.undecided`. | |
|
160 | /// This is currently the best for actual discoveries. | |
|
161 | /// | |
|
162 | /// If `false`, a reproductible picking strategy is performed. This is | |
|
163 | /// useful for integration tests. | |
|
154 | 164 | pub fn new( |
|
155 | 165 | graph: G, |
|
156 | 166 | target_heads: Vec<Revision>, |
|
157 | 167 | respect_size: bool, |
|
168 | randomize: bool, | |
|
158 | 169 | ) -> Self { |
|
159 | 170 | let mut seed: [u8; 16] = [0; 16]; |
|
160 | thread_rng().fill_bytes(&mut seed); | |
|
161 | Self::new_with_seed(graph, target_heads, seed, respect_size) | |
|
171 | if randomize { | |
|
172 | thread_rng().fill_bytes(&mut seed); | |
|
173 | } | |
|
174 | Self::new_with_seed(graph, target_heads, seed, respect_size, randomize) | |
|
162 | 175 | } |
|
163 | 176 | |
|
164 | 177 | pub fn new_with_seed( |
@@ -166,6 +179,7 impl<G: Graph + Clone> PartialDiscovery< | |||
|
166 | 179 | target_heads: Vec<Revision>, |
|
167 | 180 | seed: [u8; 16], |
|
168 | 181 | respect_size: bool, |
|
182 | randomize: bool, | |
|
169 | 183 | ) -> Self { |
|
170 | 184 | PartialDiscovery { |
|
171 | 185 | undecided: None, |
@@ -176,6 +190,7 impl<G: Graph + Clone> PartialDiscovery< | |||
|
176 | 190 | missing: HashSet::new(), |
|
177 | 191 | rng: Rng::from_seed(seed), |
|
178 | 192 | respect_size: respect_size, |
|
193 | randomize: randomize, | |
|
179 | 194 | } |
|
180 | 195 | } |
|
181 | 196 | |
@@ -186,6 +201,11 impl<G: Graph + Clone> PartialDiscovery< | |||
|
186 | 201 | mut sample: Vec<Revision>, |
|
187 | 202 | size: usize, |
|
188 | 203 | ) -> Vec<Revision> { |
|
204 | if !self.randomize { | |
|
205 | sample.sort(); | |
|
206 | sample.truncate(size); | |
|
207 | return sample; | |
|
208 | } | |
|
189 | 209 | let sample_len = sample.len(); |
|
190 | 210 | if sample_len <= size { |
|
191 | 211 | return sample; |
@@ -436,13 +456,15 mod tests { | |||
|
436 | 456 | |
|
437 | 457 | /// A PartialDiscovery as for pushing all the heads of `SampleGraph` |
|
438 | 458 | /// |
|
439 |
/// To avoid actual randomness in tests, we give it a fixed |
|
|
459 | /// To avoid actual randomness in these tests, we give it a fixed | |
|
460 | /// random seed, but by default we'll test the random version. | |
|
440 | 461 | fn full_disco() -> PartialDiscovery<SampleGraph> { |
|
441 | 462 | PartialDiscovery::new_with_seed( |
|
442 | 463 | SampleGraph, |
|
443 | 464 | vec![10, 11, 12, 13], |
|
444 | 465 | [0; 16], |
|
445 | 466 | true, |
|
467 | true, | |
|
446 | 468 | ) |
|
447 | 469 | } |
|
448 | 470 | |
@@ -450,7 +472,13 mod tests { | |||
|
450 | 472 | /// |
|
451 | 473 | /// To avoid actual randomness in tests, we give it a fixed random seed. |
|
452 | 474 | fn disco12() -> PartialDiscovery<SampleGraph> { |
|
453 |
PartialDiscovery::new_with_seed( |
|
|
475 | PartialDiscovery::new_with_seed( | |
|
476 | SampleGraph, | |
|
477 | vec![12], | |
|
478 | [0; 16], | |
|
479 | true, | |
|
480 | true, | |
|
481 | ) | |
|
454 | 482 | } |
|
455 | 483 | |
|
456 | 484 | fn sorted_undecided( |
@@ -535,6 +563,16 mod tests { | |||
|
535 | 563 | } |
|
536 | 564 | |
|
537 | 565 | #[test] |
|
566 | fn test_limit_sample_no_random() { | |
|
567 | let mut disco = full_disco(); | |
|
568 | disco.randomize = false; | |
|
569 | assert_eq!( | |
|
570 | disco.limit_sample(vec![1, 8, 13, 5, 7, 3], 4), | |
|
571 | vec![1, 3, 5, 7] | |
|
572 | ); | |
|
573 | } | |
|
574 | ||
|
575 | #[test] | |
|
538 | 576 | fn test_quick_sample_enough_undecided_heads() -> Result<(), GraphError> { |
|
539 | 577 | let mut disco = full_disco(); |
|
540 | 578 | disco.undecided = Some((1..=13).collect()); |
@@ -36,7 +36,8 py_class!(pub class PartialDiscovery |py | |||
|
36 | 36 | _cls, |
|
37 | 37 | repo: PyObject, |
|
38 | 38 | targetheads: PyObject, |
|
39 | respectsize: bool | |
|
39 | respectsize: bool, | |
|
40 | randomize: bool = true | |
|
40 | 41 | ) -> PyResult<PartialDiscovery> { |
|
41 | 42 | let index = repo.getattr(py, "changelog")?.getattr(py, "index")?; |
|
42 | 43 | Self::create_instance( |
@@ -44,7 +45,8 py_class!(pub class PartialDiscovery |py | |||
|
44 | 45 | RefCell::new(Box::new(CorePartialDiscovery::new( |
|
45 | 46 | Index::new(py, index)?, |
|
46 | 47 | rev_pyiter_collect(py, &targetheads)?, |
|
47 | respectsize | |
|
48 | respectsize, | |
|
49 | randomize, | |
|
48 | 50 | ))) |
|
49 | 51 | ) |
|
50 | 52 | } |
@@ -103,6 +103,9 class rustdiscoverytest(unittest.TestCas | |||
|
103 | 103 | self.assertTrue(disco.iscomplete()) |
|
104 | 104 | self.assertEqual(disco.commonheads(), {1}) |
|
105 | 105 | |
|
106 | def testinitnorandom(self): | |
|
107 | PartialDiscovery(self.repo(), [3], True, randomize=False) | |
|
108 | ||
|
106 | 109 | if __name__ == '__main__': |
|
107 | 110 | import silenttestrunner |
|
108 | 111 | silenttestrunner.main(__name__) |
General Comments 0
You need to be logged in to leave comments.
Login now