test-remotefilelog-histpack.py
313 lines
| 10.2 KiB
| text/x-python
|
PythonLexer
/ tests / test-remotefilelog-histpack.py
r48294 | #!/usr/bin/env python | |||
|
r40530 | from __future__ import absolute_import | ||
import hashlib | ||||
import os | ||||
import random | ||||
import shutil | ||||
import stat | ||||
import struct | ||||
import sys | ||||
import tempfile | ||||
import unittest | ||||
import silenttestrunner | ||||
|
r47771 | from mercurial.node import sha1nodeconstants | ||
|
r40530 | from mercurial import ( | ||
|
r40764 | pycompat, | ||
|
r40530 | ui as uimod, | ||
) | ||||
|
r43346 | |||
|
r40530 | # Load the local remotefilelog, not the system one | ||
sys.path[0:0] = [os.path.join(os.path.dirname(__file__), '..')] | ||||
from hgext.remotefilelog import ( | ||||
basepack, | ||||
historypack, | ||||
) | ||||
|
r43346 | |||
|
r40530 | class histpacktests(unittest.TestCase): | ||
def setUp(self): | ||||
self.tempdirs = [] | ||||
def tearDown(self): | ||||
for d in self.tempdirs: | ||||
shutil.rmtree(d) | ||||
def makeTempDir(self): | ||||
tempdir = tempfile.mkdtemp() | ||||
self.tempdirs.append(tempdir) | ||||
|
r40764 | return pycompat.fsencode(tempdir) | ||
|
r40530 | |||
def getHash(self, content): | ||||
return hashlib.sha1(content).digest() | ||||
def getFakeHash(self): | ||||
|
r43346 | return b''.join( | ||
pycompat.bytechr(random.randint(0, 255)) for _ in range(20) | ||||
) | ||||
|
r40530 | |||
def createPack(self, revisions=None): | ||||
"""Creates and returns a historypack containing the specified revisions. | ||||
`revisions` is a list of tuples, where each tuple contains a filanem, | ||||
node, p1node, p2node, and linknode. | ||||
""" | ||||
if revisions is None: | ||||
|
r43346 | revisions = [ | ||
( | ||||
b"filename", | ||||
self.getFakeHash(), | ||||
|
r47771 | sha1nodeconstants.nullid, | ||
sha1nodeconstants.nullid, | ||||
|
r43346 | self.getFakeHash(), | ||
None, | ||||
) | ||||
] | ||||
|
r40530 | |||
|
r40764 | packdir = pycompat.fsencode(self.makeTempDir()) | ||
|
r43346 | packer = historypack.mutablehistorypack(uimod.ui(), packdir, version=2) | ||
|
r40530 | |||
for filename, node, p1, p2, linknode, copyfrom in revisions: | ||||
packer.add(filename, node, p1, p2, linknode, copyfrom) | ||||
path = packer.close() | ||||
return historypack.historypack(path) | ||||
def testAddSingle(self): | ||||
|
r46554 | """Test putting a single entry into a pack and reading it out.""" | ||
|
r41352 | filename = b"foo" | ||
|
r40530 | node = self.getFakeHash() | ||
p1 = self.getFakeHash() | ||||
p2 = self.getFakeHash() | ||||
linknode = self.getFakeHash() | ||||
revisions = [(filename, node, p1, p2, linknode, None)] | ||||
pack = self.createPack(revisions) | ||||
actual = pack.getancestors(filename, node)[node] | ||||
|
r41351 | self.assertEqual(p1, actual[0]) | ||
self.assertEqual(p2, actual[1]) | ||||
self.assertEqual(linknode, actual[2]) | ||||
|
r40530 | |||
def testAddMultiple(self): | ||||
"""Test putting multiple unrelated revisions into a pack and reading | ||||
them out. | ||||
""" | ||||
revisions = [] | ||||
for i in range(10): | ||||
|
r41352 | filename = b"foo-%d" % i | ||
|
r40530 | node = self.getFakeHash() | ||
p1 = self.getFakeHash() | ||||
p2 = self.getFakeHash() | ||||
linknode = self.getFakeHash() | ||||
revisions.append((filename, node, p1, p2, linknode, None)) | ||||
pack = self.createPack(revisions) | ||||
for filename, node, p1, p2, linknode, copyfrom in revisions: | ||||
actual = pack.getancestors(filename, node)[node] | ||||
|
r41351 | self.assertEqual(p1, actual[0]) | ||
self.assertEqual(p2, actual[1]) | ||||
self.assertEqual(linknode, actual[2]) | ||||
self.assertEqual(copyfrom, actual[3]) | ||||
|
r40530 | |||
def testAddAncestorChain(self): | ||||
"""Test putting multiple revisions in into a pack and read the ancestor | ||||
chain. | ||||
""" | ||||
revisions = [] | ||||
|
r40764 | filename = b"foo" | ||
|
r47771 | lastnode = sha1nodeconstants.nullid | ||
|
r40530 | for i in range(10): | ||
node = self.getFakeHash() | ||||
|
r47771 | revisions.append( | ||
( | ||||
filename, | ||||
node, | ||||
lastnode, | ||||
sha1nodeconstants.nullid, | ||||
sha1nodeconstants.nullid, | ||||
None, | ||||
) | ||||
) | ||||
|
r40530 | lastnode = node | ||
# revisions must be added in topological order, newest first | ||||
revisions = list(reversed(revisions)) | ||||
pack = self.createPack(revisions) | ||||
# Test that the chain has all the entries | ||||
ancestors = pack.getancestors(revisions[0][0], revisions[0][1]) | ||||
for filename, node, p1, p2, linknode, copyfrom in revisions: | ||||
ap1, ap2, alinknode, acopyfrom = ancestors[node] | ||||
|
r41351 | self.assertEqual(ap1, p1) | ||
self.assertEqual(ap2, p2) | ||||
self.assertEqual(alinknode, linknode) | ||||
self.assertEqual(acopyfrom, copyfrom) | ||||
|
r40530 | |||
def testPackMany(self): | ||||
|
r46554 | """Pack many related and unrelated ancestors.""" | ||
|
r40530 | # Build a random pack file | ||
allentries = {} | ||||
ancestorcounts = {} | ||||
revisions = [] | ||||
random.seed(0) | ||||
for i in range(100): | ||||
|
r40764 | filename = b"filename-%d" % i | ||
|
r40530 | entries = [] | ||
|
r47771 | p2 = sha1nodeconstants.nullid | ||
linknode = sha1nodeconstants.nullid | ||||
|
r40530 | for j in range(random.randint(1, 100)): | ||
node = self.getFakeHash() | ||||
|
r47771 | p1 = sha1nodeconstants.nullid | ||
|
r40530 | if len(entries) > 0: | ||
p1 = entries[random.randint(0, len(entries) - 1)] | ||||
entries.append(node) | ||||
revisions.append((filename, node, p1, p2, linknode, None)) | ||||
allentries[(filename, node)] = (p1, p2, linknode) | ||||
|
r47771 | if p1 == sha1nodeconstants.nullid: | ||
|
r40530 | ancestorcounts[(filename, node)] = 1 | ||
else: | ||||
newcount = ancestorcounts[(filename, p1)] + 1 | ||||
ancestorcounts[(filename, node)] = newcount | ||||
# Must add file entries in reverse topological order | ||||
revisions = list(reversed(revisions)) | ||||
pack = self.createPack(revisions) | ||||
# Verify the pack contents | ||||
|
r41401 | for (filename, node) in allentries: | ||
|
r40530 | ancestors = pack.getancestors(filename, node) | ||
|
r43346 | self.assertEqual(ancestorcounts[(filename, node)], len(ancestors)) | ||
|
r40765 | for anode, (ap1, ap2, alinknode, copyfrom) in ancestors.items(): | ||
|
r40530 | ep1, ep2, elinknode = allentries[(filename, anode)] | ||
|
r41351 | self.assertEqual(ap1, ep1) | ||
self.assertEqual(ap2, ep2) | ||||
self.assertEqual(alinknode, elinknode) | ||||
self.assertEqual(copyfrom, None) | ||||
|
r40530 | |||
def testGetNodeInfo(self): | ||||
revisions = [] | ||||
|
r40764 | filename = b"foo" | ||
|
r47771 | lastnode = sha1nodeconstants.nullid | ||
|
r40530 | for i in range(10): | ||
node = self.getFakeHash() | ||||
|
r47771 | revisions.append( | ||
( | ||||
filename, | ||||
node, | ||||
lastnode, | ||||
sha1nodeconstants.nullid, | ||||
sha1nodeconstants.nullid, | ||||
None, | ||||
) | ||||
) | ||||
|
r40530 | lastnode = node | ||
pack = self.createPack(revisions) | ||||
# Test that getnodeinfo returns the expected results | ||||
for filename, node, p1, p2, linknode, copyfrom in revisions: | ||||
ap1, ap2, alinknode, acopyfrom = pack.getnodeinfo(filename, node) | ||||
|
r41351 | self.assertEqual(ap1, p1) | ||
self.assertEqual(ap2, p2) | ||||
self.assertEqual(alinknode, linknode) | ||||
self.assertEqual(acopyfrom, copyfrom) | ||||
|
r40530 | |||
def testGetMissing(self): | ||||
|
r46554 | """Test the getmissing() api.""" | ||
|
r40530 | revisions = [] | ||
|
r40764 | filename = b"foo" | ||
|
r40530 | for i in range(10): | ||
node = self.getFakeHash() | ||||
p1 = self.getFakeHash() | ||||
p2 = self.getFakeHash() | ||||
linknode = self.getFakeHash() | ||||
revisions.append((filename, node, p1, p2, linknode, None)) | ||||
pack = self.createPack(revisions) | ||||
missing = pack.getmissing([(filename, revisions[0][1])]) | ||||
self.assertFalse(missing) | ||||
|
r43346 | missing = pack.getmissing( | ||
[(filename, revisions[0][1]), (filename, revisions[1][1])] | ||||
) | ||||
|
r40530 | self.assertFalse(missing) | ||
fakenode = self.getFakeHash() | ||||
|
r43346 | missing = pack.getmissing( | ||
[(filename, revisions[0][1]), (filename, fakenode)] | ||||
) | ||||
|
r41351 | self.assertEqual(missing, [(filename, fakenode)]) | ||
|
r40530 | |||
# Test getmissing on a non-existant filename | ||||
|
r41352 | missing = pack.getmissing([(b"bar", fakenode)]) | ||
self.assertEqual(missing, [(b"bar", fakenode)]) | ||||
|
r40530 | |||
def testAddThrows(self): | ||||
pack = self.createPack() | ||||
try: | ||||
|
r47771 | pack.add( | ||
b'filename', | ||||
sha1nodeconstants.nullid, | ||||
sha1nodeconstants.nullid, | ||||
sha1nodeconstants.nullid, | ||||
sha1nodeconstants.nullid, | ||||
None, | ||||
) | ||||
|
r40530 | self.assertTrue(False, "historypack.add should throw") | ||
except RuntimeError: | ||||
pass | ||||
def testBadVersionThrows(self): | ||||
pack = self.createPack() | ||||
|
r41353 | path = pack.path + b'.histpack' | ||
with open(path, 'rb') as f: | ||||
|
r40530 | raw = f.read() | ||
raw = struct.pack('!B', 255) + raw[1:] | ||||
os.chmod(path, os.stat(path).st_mode | stat.S_IWRITE) | ||||
|
r41353 | with open(path, 'wb+') as f: | ||
|
r40530 | f.write(raw) | ||
try: | ||||
|
r44442 | historypack.historypack(pack.path) | ||
|
r40530 | self.assertTrue(False, "bad version number should have thrown") | ||
except RuntimeError: | ||||
pass | ||||
def testLargePack(self): | ||||
"""Test creating and reading from a large pack with over X entries. | ||||
This causes it to use a 2^16 fanout table instead.""" | ||||
total = basepack.SMALLFANOUTCUTOFF + 1 | ||||
revisions = [] | ||||
|
r40780 | for i in pycompat.xrange(total): | ||
|
r40764 | filename = b"foo-%d" % i | ||
|
r40530 | node = self.getFakeHash() | ||
p1 = self.getFakeHash() | ||||
p2 = self.getFakeHash() | ||||
linknode = self.getFakeHash() | ||||
revisions.append((filename, node, p1, p2, linknode, None)) | ||||
pack = self.createPack(revisions) | ||||
|
r41351 | self.assertEqual(pack.params.fanoutprefix, basepack.LARGEFANOUTPREFIX) | ||
|
r40530 | |||
for filename, node, p1, p2, linknode, copyfrom in revisions: | ||||
actual = pack.getancestors(filename, node)[node] | ||||
|
r41351 | self.assertEqual(p1, actual[0]) | ||
self.assertEqual(p2, actual[1]) | ||||
self.assertEqual(linknode, actual[2]) | ||||
self.assertEqual(copyfrom, actual[3]) | ||||
|
r43346 | |||
|
r40530 | # TODO: | ||
# histpack store: | ||||
# - repack two packs into one | ||||
if __name__ == '__main__': | ||||
|
r41304 | if pycompat.iswindows: | ||
|
r43346 | sys.exit(80) # Skip on Windows | ||
|
r40530 | silenttestrunner.main(__name__) | ||