##// END OF EJS Templates
py3: replace `pycompat.xrange` by `range`
py3: replace `pycompat.xrange` by `range`

File last commit:

r49801:642e31cb default
r50179:d44e3c45 default
Show More
test-ancestor.py
471 lines | 12.8 KiB | text/x-python | PythonLexer
Gregory Szorc
tests/test-ancestor: use absolute_import
r27280 import binascii
import getopt
import math
import os
import random
import sys
import time
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 from mercurial.node import nullrev
Gregory Szorc
tests/test-ancestor: use absolute_import
r27280 from mercurial import (
ancestor,
Gregory Szorc
debugcommands: move debugbuilddag...
r30402 debugcommands,
Gregory Szorc
tests/test-ancestor: use absolute_import
r27280 hg,
Pulkit Goyal
py3: alias long to int and xrange to range in test-ancestor.py on Python 3
r32861 pycompat,
Yuya Nishihara
tests: alias ui as uimod in test-ancestor
r28774 ui as uimod,
Gregory Szorc
tests/test-ancestor: use absolute_import
r27280 util,
)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331
Pulkit Goyal
py3: alias long to int and xrange to range in test-ancestor.py on Python 3
r32861 if pycompat.ispy3:
long = int
xrange = range
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 def buildgraph(rng, nodes=100, rootprob=0.05, mergeprob=0.2, prevprob=0.7):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """nodes: total number of nodes in the graph
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 rootprob: probability that a new node (not 0) will be a root
mergeprob: probability that, excluding a root a node will be a merge
prevprob: probability that p1 will be the previous node
return value is a graph represented as an adjacency list.
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 graph = [None] * nodes
for i in xrange(nodes):
if i == 0 or rng.random() < rootprob:
graph[i] = [nullrev]
elif i == 1:
graph[i] = [0]
elif rng.random() < mergeprob:
if i == 2 or rng.random() < prevprob:
# p1 is prev
p1 = i - 1
else:
p1 = rng.randrange(i - 1)
Pulkit Goyal
py3: pass range() into list() to get one explicitly...
r32893 p2 = rng.choice(list(range(0, p1)) + list(range(p1 + 1, i)))
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 graph[i] = [p1, p2]
elif rng.random() < prevprob:
graph[i] = [i - 1]
else:
graph[i] = [rng.randrange(i - 1)]
return graph
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 def buildancestorsets(graph):
ancs = [None] * len(graph)
for i in xrange(len(graph)):
Martin von Zweigbergk
cleanup: use set literals...
r32291 ancs[i] = {i}
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 if graph[i] == [nullrev]:
continue
for p in graph[i]:
ancs[i].update(ancs[p])
return ancs
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class naiveincrementalmissingancestors:
Siddharth Agarwal
test-ancestor: move naive missing ancestor algorithm into a class...
r23335 def __init__(self, ancs, bases):
self.ancs = ancs
self.bases = set(bases)
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor: add a way to add to bases of a missing ancestor object...
r23341 def addbases(self, newbases):
self.bases.update(newbases)
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor: add a way to remove ancestors of bases from a given set...
r23342 def removeancestorsfrom(self, revs):
for base in self.bases:
if base != nullrev:
revs.difference_update(self.ancs[base])
revs.discard(nullrev)
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
test-ancestor: move naive missing ancestor algorithm into a class...
r23335 def missingancestors(self, revs):
res = set()
for rev in revs:
if rev != nullrev:
res.update(self.ancs[rev])
for base in self.bases:
if base != nullrev:
res.difference_update(self.ancs[base])
return sorted(res)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 def test_missingancestors(seed, rng):
# empirically observed to take around 1 second
graphcount = 100
Siddharth Agarwal
test-ancestor: add support for multiple tests against one incremental object...
r23336 testcount = 10
inccount = 10
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 nerrs = [0]
# the default mu and sigma give us a nice distribution of mostly
# single-digit counts (including 0) with some higher ones
def lognormrandom(mu, sigma):
return int(math.floor(rng.lognormvariate(mu, sigma)))
def samplerevs(nodes, mu=1.1, sigma=0.8):
count = min(lognormrandom(mu, sigma), len(nodes))
return rng.sample(nodes, count)
Siddharth Agarwal
test-ancestor: add support for multiple tests against one incremental object...
r23336 def err(seed, graph, bases, seq, output, expected):
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 if nerrs[0] == 0:
Robert Stanca
py3: use print_function in test-ancestor.py
r28723 print('seed:', hex(seed)[:-1], file=sys.stderr)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 if gerrs[0] == 0:
Robert Stanca
py3: use print_function in test-ancestor.py
r28723 print('graph:', graph, file=sys.stderr)
print('* bases:', bases, file=sys.stderr)
print('* seq: ', seq, file=sys.stderr)
print('* output: ', output, file=sys.stderr)
print('* expected:', expected, file=sys.stderr)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 nerrs[0] += 1
gerrs[0] += 1
for g in xrange(graphcount):
graph = buildgraph(rng)
ancs = buildancestorsets(graph)
gerrs = [0]
for _ in xrange(testcount):
# start from nullrev to include it as a possibility
graphnodes = range(nullrev, len(graph))
bases = samplerevs(graphnodes)
# fast algorithm
Siddharth Agarwal
ancestor.missingancestors: turn into a state-keeping class...
r23334 inc = ancestor.incrementalmissingancestors(graph.__getitem__, bases)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 # reference slow algorithm
Siddharth Agarwal
test-ancestor: move naive missing ancestor algorithm into a class...
r23335 naiveinc = naiveincrementalmissingancestors(ancs, bases)
Siddharth Agarwal
test-ancestor: add support for multiple tests against one incremental object...
r23336 seq = []
for _ in xrange(inccount):
Siddharth Agarwal
ancestor: add a way to add to bases of a missing ancestor object...
r23341 if rng.random() < 0.2:
newbases = samplerevs(graphnodes)
seq.append(('addbases', newbases))
inc.addbases(newbases)
naiveinc.addbases(newbases)
Siddharth Agarwal
ancestor: add a way to remove ancestors of bases from a given set...
r23342 if rng.random() < 0.4:
# larger set so that there are more revs to remove from
revs = samplerevs(graphnodes, mu=1.5)
seq.append(('removeancestorsfrom', revs))
hrevs = set(revs)
rrevs = set(revs)
inc.removeancestorsfrom(hrevs)
naiveinc.removeancestorsfrom(rrevs)
if hrevs != rrevs:
Augie Fackler
formatting: blacken the codebase...
r43346 err(
seed,
graph,
bases,
seq,
sorted(hrevs),
sorted(rrevs),
)
Siddharth Agarwal
ancestor: add a way to remove ancestors of bases from a given set...
r23342 else:
revs = samplerevs(graphnodes)
seq.append(('missingancestors', revs))
h = inc.missingancestors(revs)
r = naiveinc.missingancestors(revs)
if h != r:
err(seed, graph, bases, seq, h, r)
Siddharth Agarwal
ancestor: move missingancestors doctest out into a separate file...
r18079
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor: move missingancestors doctest out into a separate file...
r18079 # graph is a dict of child->parent adjacency lists for this graph:
# o 13
# |
# | o 12
# | |
# | | o 11
# | | |\
# | | | | o 10
# | | | | |
# | o---+ | 9
# | | | | |
# o | | | | 8
# / / / /
# | | o | 7
# | | | |
# o---+ | 6
# / / /
# | | o 5
# | |/
# | o 4
# | |
# o | 3
# | |
# | o 2
# |/
# o 1
# |
# o 0
Augie Fackler
formatting: blacken the codebase...
r43346 graph = {
0: [-1, -1],
1: [0, -1],
2: [1, -1],
3: [1, -1],
4: [2, -1],
5: [4, -1],
6: [4, -1],
7: [4, -1],
8: [-1, -1],
9: [6, 7],
10: [5, -1],
11: [3, 7],
12: [9, -1],
13: [8, -1],
}
Siddharth Agarwal
ancestor: move missingancestors doctest out into a separate file...
r18079
Georges Racinet
rust: translation of missingancestors...
r40995 def test_missingancestors_explicit():
"""A few explicit cases, easier to check for catching errors in refactors.
The bigger graph at the end has been produced by the random generator
above, and we have some evidence that the other tests don't cover it.
"""
Augie Fackler
formatting: blacken the codebase...
r43346 for i, (bases, revs) in enumerate(
(
({1, 2, 3, 4, 7}, set(xrange(10))),
({10}, set({11, 12, 13, 14})),
({7}, set({1, 2, 3, 4, 5})),
)
):
Georges Racinet
rust: translation of missingancestors...
r40995 print("%% removeancestorsfrom(), example %d" % (i + 1))
missanc = ancestor.incrementalmissingancestors(graph.get, bases)
missanc.removeancestorsfrom(revs)
print("remaining (sorted): %s" % sorted(list(revs)))
Augie Fackler
formatting: blacken the codebase...
r43346 for i, (bases, revs) in enumerate(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 (
({10}, {11}),
({11}, {10}),
({7}, {9, 11}),
)
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Georges Racinet
rust: translation of missingancestors...
r40995 print("%% missingancestors(), example %d" % (i + 1))
missanc = ancestor.incrementalmissingancestors(graph.get, bases)
print("return %s" % missanc.missingancestors(revs))
print("% removeancestorsfrom(), bigger graph")
vecgraph = [
Augie Fackler
formatting: blacken the codebase...
r43346 [-1, -1],
[0, -1],
[1, 0],
[2, 1],
[3, -1],
[4, -1],
[5, 1],
[2, -1],
[7, -1],
[8, -1],
[9, -1],
[10, 1],
[3, -1],
[12, -1],
[13, -1],
[14, -1],
[4, -1],
[16, -1],
[17, -1],
[18, -1],
[19, 11],
[20, -1],
[21, -1],
[22, -1],
[23, -1],
[2, -1],
[3, -1],
[26, 24],
[27, -1],
[28, -1],
[12, -1],
[1, -1],
[1, 9],
[32, -1],
[33, -1],
[34, 31],
[35, -1],
[36, 26],
[37, -1],
[38, -1],
[39, -1],
[40, -1],
[41, -1],
[42, 26],
[0, -1],
[44, -1],
[45, 4],
[40, -1],
[47, -1],
[36, 0],
[49, -1],
[-1, -1],
[51, -1],
[52, -1],
[53, -1],
[14, -1],
[55, -1],
[15, -1],
[23, -1],
[58, -1],
[59, -1],
[2, -1],
[61, 59],
[62, -1],
[63, -1],
[-1, -1],
[65, -1],
[66, -1],
[67, -1],
[68, -1],
[37, 28],
[69, 25],
[71, -1],
[72, -1],
[50, 2],
[74, -1],
[12, -1],
[18, -1],
[77, -1],
[78, -1],
[79, -1],
[43, 33],
[81, -1],
[82, -1],
[83, -1],
[84, 45],
[85, -1],
[86, -1],
[-1, -1],
[88, -1],
[-1, -1],
[76, 83],
[44, -1],
[92, -1],
[93, -1],
[9, -1],
[95, 67],
[96, -1],
[97, -1],
[-1, -1],
]
Georges Racinet
rust: translation of missingancestors...
r40995 problem_rev = 28
problem_base = 70
# problem_rev is a parent of problem_base, but a faulty implementation
# could forget to remove it.
bases = {60, 26, 70, 3, 96, 19, 98, 49, 97, 47, 1, 6}
if problem_rev not in vecgraph[problem_base] or problem_base not in bases:
print("Conditions have changed")
missanc = ancestor.incrementalmissingancestors(vecgraph.__getitem__, bases)
revs = {4, 12, 41, 28, 68, 38, 1, 30, 56, 44}
missanc.removeancestorsfrom(revs)
if 28 in revs:
print("Failed!")
else:
print("Ok")
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor: add lazy membership testing to lazyancestors...
r18091 def genlazyancestors(revs, stoprev=0, inclusive=False):
Augie Fackler
formatting: blacken the codebase...
r43346 print(
(
"%% lazy ancestor set for %s, stoprev = %s, inclusive = %s"
% (revs, stoprev, inclusive)
)
)
return ancestor.lazyancestors(
graph.get, revs, stoprev=stoprev, inclusive=inclusive
)
Siddharth Agarwal
ancestor: add lazy membership testing to lazyancestors...
r18091
def printlazyancestors(s, l):
Robert Stanca
py3: use print_function in test-ancestor.py
r28723 print('membership: %r' % [n for n in l if n in s])
print('iteration: %r' % list(s))
Siddharth Agarwal
ancestor: add lazy membership testing to lazyancestors...
r18091
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor: add lazy membership testing to lazyancestors...
r18091 def test_lazyancestors():
# Empty revs
s = genlazyancestors([])
printlazyancestors(s, [3, 0, -1])
# Standard example
s = genlazyancestors([11, 13])
printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
Pierre-Yves David
test-ancestor: add a test for `ancestor` with ancestry within the initset...
r22355 # Standard with ancestry in the initial set (1 is ancestor of 3)
s = genlazyancestors([1, 3])
printlazyancestors(s, [1, -1, 0])
Siddharth Agarwal
ancestor: add lazy membership testing to lazyancestors...
r18091 # Including revs
s = genlazyancestors([11, 13], inclusive=True)
printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
# Test with stoprev
s = genlazyancestors([11, 13], stoprev=6)
printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
s = genlazyancestors([11, 13], stoprev=6, inclusive=True)
printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
Yuya Nishihara
ancestor: add test showing inconsistency between __iter__ and __contains__
r39511 # Test with stoprev >= min(initrevs)
s = genlazyancestors([11, 13], stoprev=11, inclusive=True)
printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
s = genlazyancestors([11, 13], stoprev=12, inclusive=True)
printlazyancestors(s, [11, 13, 7, 9, 8, 3, 6, 4, 1, -1, 0])
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504
Yuya Nishihara
ancestor: optimize _lazyancestorsiter() for contiguous chains...
r39572 # Contiguous chains: 5->4, 2->1 (where 1 is in seen set), 1->0
s = genlazyancestors([10, 1], inclusive=True)
printlazyancestors(s, [2, 10, 4, 5, -1, 0, 1])
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 # The C gca algorithm requires a real repo. These are textual descriptions of
Sune Foldager
parsers: fix invariant bug in find_deepest (issue5623)...
r33475 # DAGs that have been known to be problematic, and, optionally, known pairs
# of revisions and their expected ancestor list.
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 dagtests = [
Yuya Nishihara
py3: make test-ancestors.py pass on Python 3 with C extensions...
r36644 (b'+2*2*2/*3/2', {}),
(b'+3*3/*2*2/*4*4/*4/2*4/2*2', {}),
(b'+2*2*/2*4*/4*/3*2/4', {(6, 7): [3, 5]}),
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 ]
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 def test_gca():
Yuya Nishihara
ui: factor out ui.load() to create a ui without loading configs (API)...
r30559 u = uimod.ui.load()
Sune Foldager
parsers: fix invariant bug in find_deepest (issue5623)...
r33475 for i, (dag, tests) in enumerate(dagtests):
Pulkit Goyal
py3: pass the path in hg.repository() as bytes...
r32894 repo = hg.repository(u, b'gca%d' % i, create=1)
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 cl = repo.changelog
if not util.safehasattr(cl.index, 'ancestors'):
# C version not available
return
Gregory Szorc
debugcommands: move debugbuilddag...
r30402 debugcommands.debugbuilddag(u, repo, dag)
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 # Compare the results of the Python and C versions. This does not
# include choosing a winner when more than one gca exists -- we make
# sure both return exactly the same set of gcas.
Sune Foldager
parsers: fix invariant bug in find_deepest (issue5623)...
r33475 # Also compare against expected results, if available.
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 for a in cl:
for b in cl:
cgcas = sorted(cl.index.ancestors(a, b))
pygcas = sorted(ancestor.ancestors(cl.parentrevs, a, b))
Sune Foldager
parsers: fix invariant bug in find_deepest (issue5623)...
r33475 expected = None
if (a, b) in tests:
expected = tests[(a, b)]
if cgcas != pygcas or (expected and cgcas != expected):
Augie Fackler
formatting: blacken the codebase...
r43346 print(
"test_gca: for dag %s, gcas for %d, %d:" % (dag, a, b)
)
Robert Stanca
py3: use print_function in test-ancestor.py
r28723 print(" C returned: %s" % cgcas)
print(" Python returned: %s" % pygcas)
Sune Foldager
parsers: fix invariant bug in find_deepest (issue5623)...
r33475 if expected:
print(" expected: %s" % expected)
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
test-ancestor: define a main function...
r23330 def main():
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 seed = None
opts, args = getopt.getopt(sys.argv[1:], 's:', ['seed='])
for o, a in opts:
if o in ('-s', '--seed'):
Augie Fackler
formatting: blacken the codebase...
r43346 seed = long(a, base=0) # accepts base 10 or 16 strings
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331
if seed is None:
try:
seed = long(binascii.hexlify(os.urandom(16)), 16)
except AttributeError:
seed = long(time.time() * 1000)
rng = random.Random(seed)
Georges Racinet
rust: translation of missingancestors...
r40995 test_missingancestors_explicit()
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 test_missingancestors(seed, rng)
Siddharth Agarwal
ancestor: add lazy membership testing to lazyancestors...
r18091 test_lazyancestors()
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 test_gca()
Siddharth Agarwal
test-ancestor: define a main function...
r23330
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
test-ancestor: define a main function...
r23330 if __name__ == '__main__':
main()