##// END OF EJS Templates
py3: alias long to int and xrange to range in test-ancestor.py on Python 3
Pulkit Goyal -
r32861:20d70df6 default
parent child Browse files
Show More
@@ -1,262 +1,267
1 1 from __future__ import absolute_import, print_function
2 2
3 3 import binascii
4 4 import getopt
5 5 import math
6 6 import os
7 7 import random
8 8 import sys
9 9 import time
10 10
11 11 from mercurial.node import nullrev
12 12 from mercurial import (
13 13 ancestor,
14 14 debugcommands,
15 15 hg,
16 pycompat,
16 17 ui as uimod,
17 18 util,
18 19 )
19 20
21 if pycompat.ispy3:
22 long = int
23 xrange = range
24
20 25 def buildgraph(rng, nodes=100, rootprob=0.05, mergeprob=0.2, prevprob=0.7):
21 26 '''nodes: total number of nodes in the graph
22 27 rootprob: probability that a new node (not 0) will be a root
23 28 mergeprob: probability that, excluding a root a node will be a merge
24 29 prevprob: probability that p1 will be the previous node
25 30
26 31 return value is a graph represented as an adjacency list.
27 32 '''
28 33 graph = [None] * nodes
29 34 for i in xrange(nodes):
30 35 if i == 0 or rng.random() < rootprob:
31 36 graph[i] = [nullrev]
32 37 elif i == 1:
33 38 graph[i] = [0]
34 39 elif rng.random() < mergeprob:
35 40 if i == 2 or rng.random() < prevprob:
36 41 # p1 is prev
37 42 p1 = i - 1
38 43 else:
39 44 p1 = rng.randrange(i - 1)
40 45 p2 = rng.choice(range(0, p1) + range(p1 + 1, i))
41 46 graph[i] = [p1, p2]
42 47 elif rng.random() < prevprob:
43 48 graph[i] = [i - 1]
44 49 else:
45 50 graph[i] = [rng.randrange(i - 1)]
46 51
47 52 return graph
48 53
49 54 def buildancestorsets(graph):
50 55 ancs = [None] * len(graph)
51 56 for i in xrange(len(graph)):
52 57 ancs[i] = {i}
53 58 if graph[i] == [nullrev]:
54 59 continue
55 60 for p in graph[i]:
56 61 ancs[i].update(ancs[p])
57 62 return ancs
58 63
59 64 class naiveincrementalmissingancestors(object):
60 65 def __init__(self, ancs, bases):
61 66 self.ancs = ancs
62 67 self.bases = set(bases)
63 68 def addbases(self, newbases):
64 69 self.bases.update(newbases)
65 70 def removeancestorsfrom(self, revs):
66 71 for base in self.bases:
67 72 if base != nullrev:
68 73 revs.difference_update(self.ancs[base])
69 74 revs.discard(nullrev)
70 75 def missingancestors(self, revs):
71 76 res = set()
72 77 for rev in revs:
73 78 if rev != nullrev:
74 79 res.update(self.ancs[rev])
75 80 for base in self.bases:
76 81 if base != nullrev:
77 82 res.difference_update(self.ancs[base])
78 83 return sorted(res)
79 84
80 85 def test_missingancestors(seed, rng):
81 86 # empirically observed to take around 1 second
82 87 graphcount = 100
83 88 testcount = 10
84 89 inccount = 10
85 90 nerrs = [0]
86 91 # the default mu and sigma give us a nice distribution of mostly
87 92 # single-digit counts (including 0) with some higher ones
88 93 def lognormrandom(mu, sigma):
89 94 return int(math.floor(rng.lognormvariate(mu, sigma)))
90 95
91 96 def samplerevs(nodes, mu=1.1, sigma=0.8):
92 97 count = min(lognormrandom(mu, sigma), len(nodes))
93 98 return rng.sample(nodes, count)
94 99
95 100 def err(seed, graph, bases, seq, output, expected):
96 101 if nerrs[0] == 0:
97 102 print('seed:', hex(seed)[:-1], file=sys.stderr)
98 103 if gerrs[0] == 0:
99 104 print('graph:', graph, file=sys.stderr)
100 105 print('* bases:', bases, file=sys.stderr)
101 106 print('* seq: ', seq, file=sys.stderr)
102 107 print('* output: ', output, file=sys.stderr)
103 108 print('* expected:', expected, file=sys.stderr)
104 109 nerrs[0] += 1
105 110 gerrs[0] += 1
106 111
107 112 for g in xrange(graphcount):
108 113 graph = buildgraph(rng)
109 114 ancs = buildancestorsets(graph)
110 115 gerrs = [0]
111 116 for _ in xrange(testcount):
112 117 # start from nullrev to include it as a possibility
113 118 graphnodes = range(nullrev, len(graph))
114 119 bases = samplerevs(graphnodes)
115 120
116 121 # fast algorithm
117 122 inc = ancestor.incrementalmissingancestors(graph.__getitem__, bases)
118 123 # reference slow algorithm
119 124 naiveinc = naiveincrementalmissingancestors(ancs, bases)
120 125 seq = []
121 126 revs = []
122 127 for _ in xrange(inccount):
123 128 if rng.random() < 0.2:
124 129 newbases = samplerevs(graphnodes)
125 130 seq.append(('addbases', newbases))
126 131 inc.addbases(newbases)
127 132 naiveinc.addbases(newbases)
128 133 if rng.random() < 0.4:
129 134 # larger set so that there are more revs to remove from
130 135 revs = samplerevs(graphnodes, mu=1.5)
131 136 seq.append(('removeancestorsfrom', revs))
132 137 hrevs = set(revs)
133 138 rrevs = set(revs)
134 139 inc.removeancestorsfrom(hrevs)
135 140 naiveinc.removeancestorsfrom(rrevs)
136 141 if hrevs != rrevs:
137 142 err(seed, graph, bases, seq, sorted(hrevs),
138 143 sorted(rrevs))
139 144 else:
140 145 revs = samplerevs(graphnodes)
141 146 seq.append(('missingancestors', revs))
142 147 h = inc.missingancestors(revs)
143 148 r = naiveinc.missingancestors(revs)
144 149 if h != r:
145 150 err(seed, graph, bases, seq, h, r)
146 151
147 152 # graph is a dict of child->parent adjacency lists for this graph:
148 153 # o 13
149 154 # |
150 155 # | o 12
151 156 # | |
152 157 # | | o 11
153 158 # | | |\
154 159 # | | | | o 10
155 160 # | | | | |
156 161 # | o---+ | 9
157 162 # | | | | |
158 163 # o | | | | 8
159 164 # / / / /
160 165 # | | o | 7
161 166 # | | | |
162 167 # o---+ | 6
163 168 # / / /
164 169 # | | o 5
165 170 # | |/
166 171 # | o 4
167 172 # | |
168 173 # o | 3
169 174 # | |
170 175 # | o 2
171 176 # |/
172 177 # o 1
173 178 # |
174 179 # o 0
175 180
176 181 graph = {0: [-1], 1: [0], 2: [1], 3: [1], 4: [2], 5: [4], 6: [4],
177 182 7: [4], 8: [-1], 9: [6, 7], 10: [5], 11: [3, 7], 12: [9],
178 183 13: [8]}
179 184
180 185 def genlazyancestors(revs, stoprev=0, inclusive=False):
181 186 print(("%% lazy ancestor set for %s, stoprev = %s, inclusive = %s" %
182 187 (revs, stoprev, inclusive)))
183 188 return ancestor.lazyancestors(graph.get, revs, stoprev=stoprev,
184 189 inclusive=inclusive)
185 190
186 191 def printlazyancestors(s, l):
187 192 print('membership: %r' % [n for n in l if n in s])
188 193 print('iteration: %r' % list(s))
189 194
190 195 def test_lazyancestors():
191 196 # Empty revs
192 197 s = genlazyancestors([])
193 198 printlazyancestors(s, [3, 0, -1])
194 199
195 200 # Standard example
196 201 s = genlazyancestors([11, 13])
197 202 printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
198 203
199 204 # Standard with ancestry in the initial set (1 is ancestor of 3)
200 205 s = genlazyancestors([1, 3])
201 206 printlazyancestors(s, [1, -1, 0])
202 207
203 208 # Including revs
204 209 s = genlazyancestors([11, 13], inclusive=True)
205 210 printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
206 211
207 212 # Test with stoprev
208 213 s = genlazyancestors([11, 13], stoprev=6)
209 214 printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
210 215 s = genlazyancestors([11, 13], stoprev=6, inclusive=True)
211 216 printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
212 217
213 218
214 219 # The C gca algorithm requires a real repo. These are textual descriptions of
215 220 # DAGs that have been known to be problematic.
216 221 dagtests = [
217 222 '+2*2*2/*3/2',
218 223 '+3*3/*2*2/*4*4/*4/2*4/2*2',
219 224 ]
220 225 def test_gca():
221 226 u = uimod.ui.load()
222 227 for i, dag in enumerate(dagtests):
223 228 repo = hg.repository(u, 'gca%d' % i, create=1)
224 229 cl = repo.changelog
225 230 if not util.safehasattr(cl.index, 'ancestors'):
226 231 # C version not available
227 232 return
228 233
229 234 debugcommands.debugbuilddag(u, repo, dag)
230 235 # Compare the results of the Python and C versions. This does not
231 236 # include choosing a winner when more than one gca exists -- we make
232 237 # sure both return exactly the same set of gcas.
233 238 for a in cl:
234 239 for b in cl:
235 240 cgcas = sorted(cl.index.ancestors(a, b))
236 241 pygcas = sorted(ancestor.ancestors(cl.parentrevs, a, b))
237 242 if cgcas != pygcas:
238 243 print("test_gca: for dag %s, gcas for %d, %d:"
239 244 % (dag, a, b))
240 245 print(" C returned: %s" % cgcas)
241 246 print(" Python returned: %s" % pygcas)
242 247
243 248 def main():
244 249 seed = None
245 250 opts, args = getopt.getopt(sys.argv[1:], 's:', ['seed='])
246 251 for o, a in opts:
247 252 if o in ('-s', '--seed'):
248 253 seed = long(a, base=0) # accepts base 10 or 16 strings
249 254
250 255 if seed is None:
251 256 try:
252 257 seed = long(binascii.hexlify(os.urandom(16)), 16)
253 258 except AttributeError:
254 259 seed = long(time.time() * 1000)
255 260
256 261 rng = random.Random(seed)
257 262 test_missingancestors(seed, rng)
258 263 test_lazyancestors()
259 264 test_gca()
260 265
261 266 if __name__ == '__main__':
262 267 main()
General Comments 0
You need to be logged in to leave comments. Login now