|
|
from __future__ import absolute_import, print_function
|
|
|
|
|
|
import difflib
|
|
|
import random
|
|
|
import unittest
|
|
|
|
|
|
from mercurial import linelog
|
|
|
|
|
|
vecratio = 3 # number of replacelines / number of replacelines_vec
|
|
|
maxlinenum = 0xFFFFFF
|
|
|
maxb1 = 0xFFFFFF
|
|
|
maxdeltaa = 10
|
|
|
maxdeltab = 10
|
|
|
|
|
|
|
|
|
def _genedits(seed, endrev):
|
|
|
lines = []
|
|
|
random.seed(seed)
|
|
|
for rev in range(0, endrev):
|
|
|
n = len(lines)
|
|
|
a1 = random.randint(0, n)
|
|
|
a2 = random.randint(a1, min(n, a1 + maxdeltaa))
|
|
|
b1 = random.randint(0, maxb1)
|
|
|
b2 = random.randint(b1, b1 + maxdeltab)
|
|
|
usevec = not bool(random.randint(0, vecratio))
|
|
|
if usevec:
|
|
|
blines = [
|
|
|
(random.randint(0, rev), random.randint(0, maxlinenum))
|
|
|
for _ in range(b1, b2)
|
|
|
]
|
|
|
else:
|
|
|
blines = [(rev, bidx) for bidx in range(b1, b2)]
|
|
|
lines[a1:a2] = blines
|
|
|
yield lines, rev, a1, a2, b1, b2, blines, usevec
|
|
|
|
|
|
|
|
|
class linelogtests(unittest.TestCase):
|
|
|
def testlinelogencodedecode(self):
|
|
|
program = [
|
|
|
linelog._eof(0, 0),
|
|
|
linelog._jge(41, 42),
|
|
|
linelog._jump(0, 43),
|
|
|
linelog._eof(0, 0),
|
|
|
linelog._jl(44, 45),
|
|
|
linelog._line(46, 47),
|
|
|
]
|
|
|
ll = linelog.linelog(program, maxrev=100)
|
|
|
enc = ll.encode()
|
|
|
# round-trips okay
|
|
|
self.assertEqual(linelog.linelog.fromdata(enc)._program, ll._program)
|
|
|
self.assertEqual(linelog.linelog.fromdata(enc), ll)
|
|
|
# This encoding matches the encoding used by hg-experimental's
|
|
|
# linelog file, or is supposed to if it doesn't.
|
|
|
self.assertEqual(
|
|
|
enc,
|
|
|
(
|
|
|
b'\x00\x00\x01\x90\x00\x00\x00\x06'
|
|
|
b'\x00\x00\x00\xa4\x00\x00\x00*'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00+'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
|
b'\x00\x00\x00\xb1\x00\x00\x00-'
|
|
|
b'\x00\x00\x00\xba\x00\x00\x00/'
|
|
|
),
|
|
|
)
|
|
|
|
|
|
def testsimpleedits(self):
|
|
|
ll = linelog.linelog()
|
|
|
# Initial revision: add lines 0, 1, and 2
|
|
|
ll.replacelines(1, 0, 0, 0, 3)
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in ll.annotate(1)],
|
|
|
[(1, 0), (1, 1), (1, 2),],
|
|
|
)
|
|
|
# Replace line 1 with a new line
|
|
|
ll.replacelines(2, 1, 2, 1, 2)
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in ll.annotate(2)],
|
|
|
[(1, 0), (2, 1), (1, 2),],
|
|
|
)
|
|
|
# delete a line out of 2
|
|
|
ll.replacelines(3, 1, 2, 0, 0)
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in ll.annotate(3)], [(1, 0), (1, 2),]
|
|
|
)
|
|
|
# annotation of 1 is unchanged
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in ll.annotate(1)],
|
|
|
[(1, 0), (1, 1), (1, 2),],
|
|
|
)
|
|
|
ll.annotate(3) # set internal state to revision 3
|
|
|
start = ll.getoffset(0)
|
|
|
end = ll.getoffset(1)
|
|
|
self.assertEqual(ll.getalllines(start, end), [(1, 0), (2, 1), (1, 1),])
|
|
|
self.assertEqual(ll.getalllines(), [(1, 0), (2, 1), (1, 1), (1, 2),])
|
|
|
|
|
|
def testparseclinelogfile(self):
|
|
|
# This data is what the replacements in testsimpleedits
|
|
|
# produce when fed to the original linelog.c implementation.
|
|
|
data = (
|
|
|
b'\x00\x00\x00\x0c\x00\x00\x00\x0f'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
|
|
b'\x00\x00\x00\x05\x00\x00\x00\x06'
|
|
|
b'\x00\x00\x00\x06\x00\x00\x00\x00'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x07'
|
|
|
b'\x00\x00\x00\x06\x00\x00\x00\x02'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
|
b'\x00\x00\x00\t\x00\x00\x00\t'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x0c'
|
|
|
b'\x00\x00\x00\x08\x00\x00\x00\x05'
|
|
|
b'\x00\x00\x00\x06\x00\x00\x00\x01'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x05'
|
|
|
b'\x00\x00\x00\x0c\x00\x00\x00\x05'
|
|
|
b'\x00\x00\x00\n\x00\x00\x00\x01'
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\t'
|
|
|
)
|
|
|
llc = linelog.linelog.fromdata(data)
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in llc.annotate(1)],
|
|
|
[(1, 0), (1, 1), (1, 2),],
|
|
|
)
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in llc.annotate(2)],
|
|
|
[(1, 0), (2, 1), (1, 2),],
|
|
|
)
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in llc.annotate(3)], [(1, 0), (1, 2),]
|
|
|
)
|
|
|
# Check we emit the same bytecode.
|
|
|
ll = linelog.linelog()
|
|
|
# Initial revision: add lines 0, 1, and 2
|
|
|
ll.replacelines(1, 0, 0, 0, 3)
|
|
|
# Replace line 1 with a new line
|
|
|
ll.replacelines(2, 1, 2, 1, 2)
|
|
|
# delete a line out of 2
|
|
|
ll.replacelines(3, 1, 2, 0, 0)
|
|
|
diff = '\n ' + '\n '.join(
|
|
|
difflib.unified_diff(
|
|
|
ll.debugstr().splitlines(),
|
|
|
llc.debugstr().splitlines(),
|
|
|
'python',
|
|
|
'c',
|
|
|
lineterm='',
|
|
|
)
|
|
|
)
|
|
|
self.assertEqual(ll._program, llc._program, 'Program mismatch: ' + diff)
|
|
|
# Done as a secondary step so we get a better result if the
|
|
|
# program is where the mismatch is.
|
|
|
self.assertEqual(ll, llc)
|
|
|
self.assertEqual(ll.encode(), data)
|
|
|
|
|
|
def testanothersimplecase(self):
|
|
|
ll = linelog.linelog()
|
|
|
ll.replacelines(3, 0, 0, 0, 2)
|
|
|
ll.replacelines(4, 0, 2, 0, 0)
|
|
|
self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(4)], [])
|
|
|
self.assertEqual(
|
|
|
[(l.rev, l.linenum) for l in ll.annotate(3)], [(3, 0), (3, 1)]
|
|
|
)
|
|
|
# rev 2 is empty because contents were only ever introduced in rev 3
|
|
|
self.assertEqual([(l.rev, l.linenum) for l in ll.annotate(2)], [])
|
|
|
|
|
|
def testrandomedits(self):
|
|
|
# Inspired by original linelog tests.
|
|
|
seed = random.random()
|
|
|
numrevs = 2000
|
|
|
ll = linelog.linelog()
|
|
|
# Populate linelog
|
|
|
for lines, rev, a1, a2, b1, b2, blines, usevec in _genedits(
|
|
|
seed, numrevs
|
|
|
):
|
|
|
if usevec:
|
|
|
ll.replacelines_vec(rev, a1, a2, blines)
|
|
|
else:
|
|
|
ll.replacelines(rev, a1, a2, b1, b2)
|
|
|
ll.annotate(rev)
|
|
|
self.assertEqual(ll.annotateresult, lines)
|
|
|
# Verify we can get back these states by annotating each rev
|
|
|
for lines, rev, a1, a2, b1, b2, blines, usevec in _genedits(
|
|
|
seed, numrevs
|
|
|
):
|
|
|
ar = ll.annotate(rev)
|
|
|
self.assertEqual([(l.rev, l.linenum) for l in ar], lines)
|
|
|
|
|
|
def testinfinitebadprogram(self):
|
|
|
ll = linelog.linelog.fromdata(
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x02' # header
|
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x01' # JUMP to self
|
|
|
)
|
|
|
with self.assertRaises(linelog.LineLogError):
|
|
|
# should not be an infinite loop and raise
|
|
|
ll.annotate(1)
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
import silenttestrunner
|
|
|
|
|
|
silenttestrunner.main(__name__)
|
|
|
|