##// END OF EJS Templates
revlog: subclass the new `repository.iverifyproblem` Protocol class...
revlog: subclass the new `repository.iverifyproblem` Protocol class This is the same transformation as 3a90a6fd710d did for dirstate, but the CamelCase naming was already cleaned up here. We shouldn't have to explicitly subclass, but I'm doing so to test the interplay of regular attributes and the `attrs` class. Also, PyCharm has a nifty feature that puts a jump point in the gutter to navigate back and forth between the base class and subclasses (and override functions and base class functions) when there's an explicit subclassing. Additionally, PyCharm will immediately flag signature mismatches without a 40m pytype run.

File last commit:

r52596:ca7bde5d default
r53365:4ef6dbc2 default
Show More
test-ancestor.py
466 lines | 12.7 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,
Yuya Nishihara
tests: alias ui as uimod in test-ancestor
r28774 ui as uimod,
Gregory Szorc
tests/test-ancestor: use absolute_import
r27280 )
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 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
Manuel Jacob
py3: remove xrange() compatibility code...
r50180 for i in range(nodes):
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 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)
Manuel Jacob
py3: remove xrange() compatibility code...
r50180 for i in range(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]
Raphaël Gomès
black: format the codebase with 23.3.0...
r52596
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 # 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
Manuel Jacob
py3: remove xrange() compatibility code...
r50180 for g in range(graphcount):
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 graph = buildgraph(rng)
ancs = buildancestorsets(graph)
gerrs = [0]
Manuel Jacob
py3: remove xrange() compatibility code...
r50180 for _ in range(testcount):
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 # 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 = []
Manuel Jacob
py3: remove xrange() compatibility code...
r50180 for _ in range(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(
(
Manuel Jacob
py3: remove xrange() compatibility code...
r50180 ({1, 2, 3, 4, 7}, set(range(10))),
Augie Fackler
formatting: blacken the codebase...
r43346 ({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
safehasattr: drop usage in favor of hasattr...
r51821 if not hasattr(cl.index, 'ancestors'):
Siddharth Agarwal
ancestor.deepest: ignore ninteresting while building result (issue3984)...
r19504 # 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'):
Manuel Jacob
py3: remove long() compatibility code
r50189 seed = int(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:
Manuel Jacob
py3: remove long() compatibility code
r50189 seed = int(binascii.hexlify(os.urandom(16)), 16)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331 except AttributeError:
Manuel Jacob
py3: remove long() compatibility code
r50189 seed = int(time.time() * 1000)
Siddharth Agarwal
test-ancestor: use random testing for missing ancestors...
r23331
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()