##// END OF EJS Templates
tests: various Python 3 ports for test-remotefilelog-datapack.py...
Gregory Szorc -
r41614:90b3898c default
parent child Browse files
Show More
@@ -1,379 +1,380
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 from __future__ import absolute_import, print_function
2 from __future__ import absolute_import, print_function
3
3
4 import hashlib
4 import hashlib
5 import os
5 import os
6 import random
6 import random
7 import shutil
7 import shutil
8 import stat
8 import stat
9 import struct
9 import struct
10 import sys
10 import sys
11 import tempfile
11 import tempfile
12 import time
12 import time
13 import unittest
13 import unittest
14
14
15 import silenttestrunner
15 import silenttestrunner
16
16
17 # Load the local remotefilelog, not the system one
17 # Load the local remotefilelog, not the system one
18 sys.path[0:0] = [os.path.join(os.path.dirname(__file__), '..')]
18 sys.path[0:0] = [os.path.join(os.path.dirname(__file__), '..')]
19 from mercurial.node import nullid
19 from mercurial.node import nullid
20 from mercurial import (
20 from mercurial import (
21 pycompat,
21 pycompat,
22 ui as uimod,
22 ui as uimod,
23 )
23 )
24 from hgext.remotefilelog import (
24 from hgext.remotefilelog import (
25 basepack,
25 basepack,
26 constants,
26 constants,
27 datapack,
27 datapack,
28 )
28 )
29
29
30 class datapacktestsbase(object):
30 class datapacktestsbase(object):
31 def __init__(self, datapackreader, paramsavailable):
31 def __init__(self, datapackreader, paramsavailable):
32 self.datapackreader = datapackreader
32 self.datapackreader = datapackreader
33 self.paramsavailable = paramsavailable
33 self.paramsavailable = paramsavailable
34
34
35 def setUp(self):
35 def setUp(self):
36 self.tempdirs = []
36 self.tempdirs = []
37
37
38 def tearDown(self):
38 def tearDown(self):
39 for d in self.tempdirs:
39 for d in self.tempdirs:
40 shutil.rmtree(d)
40 shutil.rmtree(d)
41
41
42 def makeTempDir(self):
42 def makeTempDir(self):
43 tempdir = tempfile.mkdtemp()
43 tempdir = pycompat.bytestr(tempfile.mkdtemp())
44 self.tempdirs.append(tempdir)
44 self.tempdirs.append(tempdir)
45 return tempdir
45 return tempdir
46
46
47 def getHash(self, content):
47 def getHash(self, content):
48 return hashlib.sha1(content).digest()
48 return hashlib.sha1(content).digest()
49
49
50 def getFakeHash(self):
50 def getFakeHash(self):
51 return ''.join(chr(random.randint(0, 255)) for _ in range(20))
51 return b''.join(pycompat.bytechr(random.randint(0, 255))
52 for _ in range(20))
52
53
53 def createPack(self, revisions=None, packdir=None):
54 def createPack(self, revisions=None, packdir=None):
54 if revisions is None:
55 if revisions is None:
55 revisions = [(b"filename", self.getFakeHash(), nullid, b"content")]
56 revisions = [(b"filename", self.getFakeHash(), nullid, b"content")]
56
57
57 if packdir is None:
58 if packdir is None:
58 packdir = self.makeTempDir()
59 packdir = self.makeTempDir()
59
60
60 packer = datapack.mutabledatapack(uimod.ui(), packdir, version=2)
61 packer = datapack.mutabledatapack(uimod.ui(), packdir, version=2)
61
62
62 for args in revisions:
63 for args in revisions:
63 filename, node, base, content = args[0:4]
64 filename, node, base, content = args[0:4]
64 # meta is optional
65 # meta is optional
65 meta = None
66 meta = None
66 if len(args) > 4:
67 if len(args) > 4:
67 meta = args[4]
68 meta = args[4]
68 packer.add(filename, node, base, content, metadata=meta)
69 packer.add(filename, node, base, content, metadata=meta)
69
70
70 path = packer.close()
71 path = packer.close()
71 return self.datapackreader(path)
72 return self.datapackreader(path)
72
73
73 def _testAddSingle(self, content):
74 def _testAddSingle(self, content):
74 """Test putting a simple blob into a pack and reading it out.
75 """Test putting a simple blob into a pack and reading it out.
75 """
76 """
76 filename = b"foo"
77 filename = b"foo"
77 node = self.getHash(content)
78 node = self.getHash(content)
78
79
79 revisions = [(filename, node, nullid, content)]
80 revisions = [(filename, node, nullid, content)]
80 pack = self.createPack(revisions)
81 pack = self.createPack(revisions)
81 if self.paramsavailable:
82 if self.paramsavailable:
82 self.assertEqual(pack.params.fanoutprefix,
83 self.assertEqual(pack.params.fanoutprefix,
83 basepack.SMALLFANOUTPREFIX)
84 basepack.SMALLFANOUTPREFIX)
84
85
85 chain = pack.getdeltachain(filename, node)
86 chain = pack.getdeltachain(filename, node)
86 self.assertEqual(content, chain[0][4])
87 self.assertEqual(content, chain[0][4])
87
88
88 def testAddSingle(self):
89 def testAddSingle(self):
89 self._testAddSingle(b'')
90 self._testAddSingle(b'')
90
91
91 def testAddSingleEmpty(self):
92 def testAddSingleEmpty(self):
92 self._testAddSingle(b'abcdef')
93 self._testAddSingle(b'abcdef')
93
94
94 def testAddMultiple(self):
95 def testAddMultiple(self):
95 """Test putting multiple unrelated blobs into a pack and reading them
96 """Test putting multiple unrelated blobs into a pack and reading them
96 out.
97 out.
97 """
98 """
98 revisions = []
99 revisions = []
99 for i in range(10):
100 for i in range(10):
100 filename = b"foo%d" % i
101 filename = b"foo%d" % i
101 content = b"abcdef%d" % i
102 content = b"abcdef%d" % i
102 node = self.getHash(content)
103 node = self.getHash(content)
103 revisions.append((filename, node, self.getFakeHash(), content))
104 revisions.append((filename, node, self.getFakeHash(), content))
104
105
105 pack = self.createPack(revisions)
106 pack = self.createPack(revisions)
106
107
107 for filename, node, base, content in revisions:
108 for filename, node, base, content in revisions:
108 entry = pack.getdelta(filename, node)
109 entry = pack.getdelta(filename, node)
109 self.assertEqual((content, filename, base, {}), entry)
110 self.assertEqual((content, filename, base, {}), entry)
110
111
111 chain = pack.getdeltachain(filename, node)
112 chain = pack.getdeltachain(filename, node)
112 self.assertEqual(content, chain[0][4])
113 self.assertEqual(content, chain[0][4])
113
114
114 def testAddDeltas(self):
115 def testAddDeltas(self):
115 """Test putting multiple delta blobs into a pack and read the chain.
116 """Test putting multiple delta blobs into a pack and read the chain.
116 """
117 """
117 revisions = []
118 revisions = []
118 filename = b"foo"
119 filename = b"foo"
119 lastnode = nullid
120 lastnode = nullid
120 for i in range(10):
121 for i in range(10):
121 content = b"abcdef%d" % i
122 content = b"abcdef%d" % i
122 node = self.getHash(content)
123 node = self.getHash(content)
123 revisions.append((filename, node, lastnode, content))
124 revisions.append((filename, node, lastnode, content))
124 lastnode = node
125 lastnode = node
125
126
126 pack = self.createPack(revisions)
127 pack = self.createPack(revisions)
127
128
128 entry = pack.getdelta(filename, revisions[0][1])
129 entry = pack.getdelta(filename, revisions[0][1])
129 realvalue = (revisions[0][3], filename, revisions[0][2], {})
130 realvalue = (revisions[0][3], filename, revisions[0][2], {})
130 self.assertEqual(entry, realvalue)
131 self.assertEqual(entry, realvalue)
131
132
132 # Test that the chain for the final entry has all the others
133 # Test that the chain for the final entry has all the others
133 chain = pack.getdeltachain(filename, node)
134 chain = pack.getdeltachain(filename, node)
134 for i in range(10):
135 for i in range(10):
135 content = b"abcdef%d" % i
136 content = b"abcdef%d" % i
136 self.assertEqual(content, chain[-i - 1][4])
137 self.assertEqual(content, chain[-i - 1][4])
137
138
138 def testPackMany(self):
139 def testPackMany(self):
139 """Pack many related and unrelated objects.
140 """Pack many related and unrelated objects.
140 """
141 """
141 # Build a random pack file
142 # Build a random pack file
142 revisions = []
143 revisions = []
143 blobs = {}
144 blobs = {}
144 random.seed(0)
145 random.seed(0)
145 for i in range(100):
146 for i in range(100):
146 filename = b"filename-%d" % i
147 filename = b"filename-%d" % i
147 filerevs = []
148 filerevs = []
148 for j in range(random.randint(1, 100)):
149 for j in range(random.randint(1, 100)):
149 content = b"content-%d" % j
150 content = b"content-%d" % j
150 node = self.getHash(content)
151 node = self.getHash(content)
151 lastnode = nullid
152 lastnode = nullid
152 if len(filerevs) > 0:
153 if len(filerevs) > 0:
153 lastnode = filerevs[random.randint(0, len(filerevs) - 1)]
154 lastnode = filerevs[random.randint(0, len(filerevs) - 1)]
154 filerevs.append(node)
155 filerevs.append(node)
155 blobs[(filename, node, lastnode)] = content
156 blobs[(filename, node, lastnode)] = content
156 revisions.append((filename, node, lastnode, content))
157 revisions.append((filename, node, lastnode, content))
157
158
158 pack = self.createPack(revisions)
159 pack = self.createPack(revisions)
159
160
160 # Verify the pack contents
161 # Verify the pack contents
161 for (filename, node, lastnode), content in sorted(blobs.items()):
162 for (filename, node, lastnode), content in sorted(blobs.items()):
162 chain = pack.getdeltachain(filename, node)
163 chain = pack.getdeltachain(filename, node)
163 for entry in chain:
164 for entry in chain:
164 expectedcontent = blobs[(entry[0], entry[1], entry[3])]
165 expectedcontent = blobs[(entry[0], entry[1], entry[3])]
165 self.assertEqual(entry[4], expectedcontent)
166 self.assertEqual(entry[4], expectedcontent)
166
167
167 def testPackMetadata(self):
168 def testPackMetadata(self):
168 revisions = []
169 revisions = []
169 for i in range(100):
170 for i in range(100):
170 filename = b'%d.txt' % i
171 filename = b'%d.txt' % i
171 content = b'put-something-here \n' * i
172 content = b'put-something-here \n' * i
172 node = self.getHash(content)
173 node = self.getHash(content)
173 meta = {constants.METAKEYFLAG: i ** 4,
174 meta = {constants.METAKEYFLAG: i ** 4,
174 constants.METAKEYSIZE: len(content),
175 constants.METAKEYSIZE: len(content),
175 b'Z': b'random_string',
176 b'Z': b'random_string',
176 b'_': b'\0' * i}
177 b'_': b'\0' * i}
177 revisions.append((filename, node, nullid, content, meta))
178 revisions.append((filename, node, nullid, content, meta))
178 pack = self.createPack(revisions)
179 pack = self.createPack(revisions)
179 for name, node, x, content, origmeta in revisions:
180 for name, node, x, content, origmeta in revisions:
180 parsedmeta = pack.getmeta(name, node)
181 parsedmeta = pack.getmeta(name, node)
181 # flag == 0 should be optimized out
182 # flag == 0 should be optimized out
182 if origmeta[constants.METAKEYFLAG] == 0:
183 if origmeta[constants.METAKEYFLAG] == 0:
183 del origmeta[constants.METAKEYFLAG]
184 del origmeta[constants.METAKEYFLAG]
184 self.assertEqual(parsedmeta, origmeta)
185 self.assertEqual(parsedmeta, origmeta)
185
186
186 def testGetMissing(self):
187 def testGetMissing(self):
187 """Test the getmissing() api.
188 """Test the getmissing() api.
188 """
189 """
189 revisions = []
190 revisions = []
190 filename = b"foo"
191 filename = b"foo"
191 lastnode = nullid
192 lastnode = nullid
192 for i in range(10):
193 for i in range(10):
193 content = b"abcdef%d" % i
194 content = b"abcdef%d" % i
194 node = self.getHash(content)
195 node = self.getHash(content)
195 revisions.append((filename, node, lastnode, content))
196 revisions.append((filename, node, lastnode, content))
196 lastnode = node
197 lastnode = node
197
198
198 pack = self.createPack(revisions)
199 pack = self.createPack(revisions)
199
200
200 missing = pack.getmissing([(b"foo", revisions[0][1])])
201 missing = pack.getmissing([(b"foo", revisions[0][1])])
201 self.assertFalse(missing)
202 self.assertFalse(missing)
202
203
203 missing = pack.getmissing([(b"foo", revisions[0][1]),
204 missing = pack.getmissing([(b"foo", revisions[0][1]),
204 (b"foo", revisions[1][1])])
205 (b"foo", revisions[1][1])])
205 self.assertFalse(missing)
206 self.assertFalse(missing)
206
207
207 fakenode = self.getFakeHash()
208 fakenode = self.getFakeHash()
208 missing = pack.getmissing([(b"foo", revisions[0][1]),
209 missing = pack.getmissing([(b"foo", revisions[0][1]),
209 (b"foo", fakenode)])
210 (b"foo", fakenode)])
210 self.assertEqual(missing, [(b"foo", fakenode)])
211 self.assertEqual(missing, [(b"foo", fakenode)])
211
212
212 def testAddThrows(self):
213 def testAddThrows(self):
213 pack = self.createPack()
214 pack = self.createPack()
214
215
215 try:
216 try:
216 pack.add(b'filename', nullid, b'contents')
217 pack.add(b'filename', nullid, b'contents')
217 self.assertTrue(False, "datapack.add should throw")
218 self.assertTrue(False, "datapack.add should throw")
218 except RuntimeError:
219 except RuntimeError:
219 pass
220 pass
220
221
221 def testBadVersionThrows(self):
222 def testBadVersionThrows(self):
222 pack = self.createPack()
223 pack = self.createPack()
223 path = pack.path + b'.datapack'
224 path = pack.path + b'.datapack'
224 with open(path) as f:
225 with open(path, 'rb') as f:
225 raw = f.read()
226 raw = f.read()
226 raw = struct.pack('!B', 255) + raw[1:]
227 raw = struct.pack('!B', 255) + raw[1:]
227 os.chmod(path, os.stat(path).st_mode | stat.S_IWRITE)
228 os.chmod(path, os.stat(path).st_mode | stat.S_IWRITE)
228 with open(path, 'w+') as f:
229 with open(path, 'wb+') as f:
229 f.write(raw)
230 f.write(raw)
230
231
231 try:
232 try:
232 pack = self.datapackreader(pack.path)
233 pack = self.datapackreader(pack.path)
233 self.assertTrue(False, "bad version number should have thrown")
234 self.assertTrue(False, "bad version number should have thrown")
234 except RuntimeError:
235 except RuntimeError:
235 pass
236 pass
236
237
237 def testMissingDeltabase(self):
238 def testMissingDeltabase(self):
238 fakenode = self.getFakeHash()
239 fakenode = self.getFakeHash()
239 revisions = [(b"filename", fakenode, self.getFakeHash(), b"content")]
240 revisions = [(b"filename", fakenode, self.getFakeHash(), b"content")]
240 pack = self.createPack(revisions)
241 pack = self.createPack(revisions)
241 chain = pack.getdeltachain(b"filename", fakenode)
242 chain = pack.getdeltachain(b"filename", fakenode)
242 self.assertEqual(len(chain), 1)
243 self.assertEqual(len(chain), 1)
243
244
244 def testLargePack(self):
245 def testLargePack(self):
245 """Test creating and reading from a large pack with over X entries.
246 """Test creating and reading from a large pack with over X entries.
246 This causes it to use a 2^16 fanout table instead."""
247 This causes it to use a 2^16 fanout table instead."""
247 revisions = []
248 revisions = []
248 blobs = {}
249 blobs = {}
249 total = basepack.SMALLFANOUTCUTOFF + 1
250 total = basepack.SMALLFANOUTCUTOFF + 1
250 for i in pycompat.xrange(total):
251 for i in pycompat.xrange(total):
251 filename = b"filename-%d" % i
252 filename = b"filename-%d" % i
252 content = filename
253 content = filename
253 node = self.getHash(content)
254 node = self.getHash(content)
254 blobs[(filename, node)] = content
255 blobs[(filename, node)] = content
255 revisions.append((filename, node, nullid, content))
256 revisions.append((filename, node, nullid, content))
256
257
257 pack = self.createPack(revisions)
258 pack = self.createPack(revisions)
258 if self.paramsavailable:
259 if self.paramsavailable:
259 self.assertEqual(pack.params.fanoutprefix,
260 self.assertEqual(pack.params.fanoutprefix,
260 basepack.LARGEFANOUTPREFIX)
261 basepack.LARGEFANOUTPREFIX)
261
262
262 for (filename, node), content in blobs.items():
263 for (filename, node), content in blobs.items():
263 actualcontent = pack.getdeltachain(filename, node)[0][4]
264 actualcontent = pack.getdeltachain(filename, node)[0][4]
264 self.assertEqual(actualcontent, content)
265 self.assertEqual(actualcontent, content)
265
266
266 def testPacksCache(self):
267 def testPacksCache(self):
267 """Test that we remember the most recent packs while fetching the delta
268 """Test that we remember the most recent packs while fetching the delta
268 chain."""
269 chain."""
269
270
270 packdir = self.makeTempDir()
271 packdir = self.makeTempDir()
271 deltachains = []
272 deltachains = []
272
273
273 numpacks = 10
274 numpacks = 10
274 revisionsperpack = 100
275 revisionsperpack = 100
275
276
276 for i in range(numpacks):
277 for i in range(numpacks):
277 chain = []
278 chain = []
278 revision = (b'%d' % i, self.getFakeHash(), nullid, b"content")
279 revision = (b'%d' % i, self.getFakeHash(), nullid, b"content")
279
280
280 for _ in range(revisionsperpack):
281 for _ in range(revisionsperpack):
281 chain.append(revision)
282 chain.append(revision)
282 revision = (
283 revision = (
283 b'%d' % i,
284 b'%d' % i,
284 self.getFakeHash(),
285 self.getFakeHash(),
285 revision[1],
286 revision[1],
286 self.getFakeHash()
287 self.getFakeHash()
287 )
288 )
288
289
289 self.createPack(chain, packdir)
290 self.createPack(chain, packdir)
290 deltachains.append(chain)
291 deltachains.append(chain)
291
292
292 class testdatapackstore(datapack.datapackstore):
293 class testdatapackstore(datapack.datapackstore):
293 # Ensures that we are not keeping everything in the cache.
294 # Ensures that we are not keeping everything in the cache.
294 DEFAULTCACHESIZE = numpacks / 2
295 DEFAULTCACHESIZE = numpacks / 2
295
296
296 store = testdatapackstore(uimod.ui(), packdir)
297 store = testdatapackstore(uimod.ui(), packdir)
297
298
298 random.shuffle(deltachains)
299 random.shuffle(deltachains)
299 for randomchain in deltachains:
300 for randomchain in deltachains:
300 revision = random.choice(randomchain)
301 revision = random.choice(randomchain)
301 chain = store.getdeltachain(revision[0], revision[1])
302 chain = store.getdeltachain(revision[0], revision[1])
302
303
303 mostrecentpack = next(iter(store.packs), None)
304 mostrecentpack = next(iter(store.packs), None)
304 self.assertEqual(
305 self.assertEqual(
305 mostrecentpack.getdeltachain(revision[0], revision[1]),
306 mostrecentpack.getdeltachain(revision[0], revision[1]),
306 chain
307 chain
307 )
308 )
308
309
309 self.assertEqual(randomchain.index(revision) + 1, len(chain))
310 self.assertEqual(randomchain.index(revision) + 1, len(chain))
310
311
311 # perf test off by default since it's slow
312 # perf test off by default since it's slow
312 def _testIndexPerf(self):
313 def _testIndexPerf(self):
313 random.seed(0)
314 random.seed(0)
314 print("Multi-get perf test")
315 print("Multi-get perf test")
315 packsizes = [
316 packsizes = [
316 100,
317 100,
317 10000,
318 10000,
318 100000,
319 100000,
319 500000,
320 500000,
320 1000000,
321 1000000,
321 3000000,
322 3000000,
322 ]
323 ]
323 lookupsizes = [
324 lookupsizes = [
324 10,
325 10,
325 100,
326 100,
326 1000,
327 1000,
327 10000,
328 10000,
328 100000,
329 100000,
329 1000000,
330 1000000,
330 ]
331 ]
331 for packsize in packsizes:
332 for packsize in packsizes:
332 revisions = []
333 revisions = []
333 for i in pycompat.xrange(packsize):
334 for i in pycompat.xrange(packsize):
334 filename = b"filename-%d" % i
335 filename = b"filename-%d" % i
335 content = b"content-%d" % i
336 content = b"content-%d" % i
336 node = self.getHash(content)
337 node = self.getHash(content)
337 revisions.append((filename, node, nullid, content))
338 revisions.append((filename, node, nullid, content))
338
339
339 path = self.createPack(revisions).path
340 path = self.createPack(revisions).path
340
341
341 # Perf of large multi-get
342 # Perf of large multi-get
342 import gc
343 import gc
343 gc.disable()
344 gc.disable()
344 pack = self.datapackreader(path)
345 pack = self.datapackreader(path)
345 for lookupsize in lookupsizes:
346 for lookupsize in lookupsizes:
346 if lookupsize > packsize:
347 if lookupsize > packsize:
347 continue
348 continue
348 random.shuffle(revisions)
349 random.shuffle(revisions)
349 findnodes = [(rev[0], rev[1]) for rev in revisions]
350 findnodes = [(rev[0], rev[1]) for rev in revisions]
350
351
351 start = time.time()
352 start = time.time()
352 pack.getmissing(findnodes[:lookupsize])
353 pack.getmissing(findnodes[:lookupsize])
353 elapsed = time.time() - start
354 elapsed = time.time() - start
354 print ("%s pack %d lookups = %0.04f" %
355 print ("%s pack %d lookups = %0.04f" %
355 (('%d' % packsize).rjust(7),
356 (('%d' % packsize).rjust(7),
356 ('%d' % lookupsize).rjust(7),
357 ('%d' % lookupsize).rjust(7),
357 elapsed))
358 elapsed))
358
359
359 print("")
360 print("")
360 gc.enable()
361 gc.enable()
361
362
362 # The perf test is meant to produce output, so we always fail the test
363 # The perf test is meant to produce output, so we always fail the test
363 # so the user sees the output.
364 # so the user sees the output.
364 raise RuntimeError("perf test always fails")
365 raise RuntimeError("perf test always fails")
365
366
366 class datapacktests(datapacktestsbase, unittest.TestCase):
367 class datapacktests(datapacktestsbase, unittest.TestCase):
367 def __init__(self, *args, **kwargs):
368 def __init__(self, *args, **kwargs):
368 datapacktestsbase.__init__(self, datapack.datapack, True)
369 datapacktestsbase.__init__(self, datapack.datapack, True)
369 unittest.TestCase.__init__(self, *args, **kwargs)
370 unittest.TestCase.__init__(self, *args, **kwargs)
370
371
371 # TODO:
372 # TODO:
372 # datapack store:
373 # datapack store:
373 # - getmissing
374 # - getmissing
374 # - GC two packs into one
375 # - GC two packs into one
375
376
376 if __name__ == '__main__':
377 if __name__ == '__main__':
377 if pycompat.iswindows:
378 if pycompat.iswindows:
378 sys.exit(80) # Skip on Windows
379 sys.exit(80) # Skip on Windows
379 silenttestrunner.main(__name__)
380 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now