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