##// END OF EJS Templates
tests: make test-manifest finish importing in Python 3...
Augie Fackler -
r32534:0048a852 default
parent child Browse files
Show More
@@ -1,482 +1,486 b''
1 1 from __future__ import absolute_import
2 2
3 3 import binascii
4 4 import itertools
5 5 import silenttestrunner
6 6 import unittest
7 7
8 8 from mercurial import (
9 9 manifest as manifestmod,
10 10 match as matchmod,
11 11 )
12 12
13 13 EMTPY_MANIFEST = ''
14 14 EMTPY_MANIFEST_V2 = '\0\n'
15 15
16 16 HASH_1 = '1' * 40
17 17 BIN_HASH_1 = binascii.unhexlify(HASH_1)
18 18 HASH_2 = 'f' * 40
19 19 BIN_HASH_2 = binascii.unhexlify(HASH_2)
20 20 HASH_3 = '1234567890abcdef0987654321deadbeef0fcafe'
21 21 BIN_HASH_3 = binascii.unhexlify(HASH_3)
22 22 A_SHORT_MANIFEST = (
23 23 'bar/baz/qux.py\0%(hash2)s%(flag2)s\n'
24 24 'foo\0%(hash1)s%(flag1)s\n'
25 25 ) % {'hash1': HASH_1,
26 26 'flag1': '',
27 27 'hash2': HASH_2,
28 28 'flag2': 'l',
29 29 }
30 30
31 31 # Same data as A_SHORT_MANIFEST
32 32 A_SHORT_MANIFEST_V2 = (
33 33 '\0\n'
34 34 '\x00bar/baz/qux.py\0%(flag2)s\n%(hash2)s\n'
35 35 '\x00foo\0%(flag1)s\n%(hash1)s\n'
36 36 ) % {'hash1': BIN_HASH_1,
37 37 'flag1': '',
38 38 'hash2': BIN_HASH_2,
39 39 'flag2': 'l',
40 40 }
41 41
42 42 # Same data as A_SHORT_MANIFEST
43 43 A_METADATA_MANIFEST = (
44 44 '\0foo\0bar\n'
45 45 '\x00bar/baz/qux.py\0%(flag2)s\0foo\0bar\n%(hash2)s\n' # flag and metadata
46 46 '\x00foo\0%(flag1)s\0foo\n%(hash1)s\n' # no flag, but metadata
47 47 ) % {'hash1': BIN_HASH_1,
48 48 'flag1': '',
49 49 'hash2': BIN_HASH_2,
50 50 'flag2': 'l',
51 51 }
52 52
53 53 A_STEM_COMPRESSED_MANIFEST = (
54 54 '\0\n'
55 55 '\x00bar/baz/qux.py\0%(flag2)s\n%(hash2)s\n'
56 56 '\x04qux/foo.py\0%(flag1)s\n%(hash1)s\n' # simple case of 4 stem chars
57 57 '\x0az.py\0%(flag1)s\n%(hash1)s\n' # tricky newline = 10 stem characters
58 58 '\x00%(verylongdir)sx/x\0\n%(hash1)s\n'
59 59 '\xffx/y\0\n%(hash2)s\n' # more than 255 stem chars
60 60 ) % {'hash1': BIN_HASH_1,
61 61 'flag1': '',
62 62 'hash2': BIN_HASH_2,
63 63 'flag2': 'l',
64 64 'verylongdir': 255 * 'x',
65 65 }
66 66
67 67 A_DEEPER_MANIFEST = (
68 68 'a/b/c/bar.py\0%(hash3)s%(flag1)s\n'
69 69 'a/b/c/bar.txt\0%(hash1)s%(flag1)s\n'
70 70 'a/b/c/foo.py\0%(hash3)s%(flag1)s\n'
71 71 'a/b/c/foo.txt\0%(hash2)s%(flag2)s\n'
72 72 'a/b/d/baz.py\0%(hash3)s%(flag1)s\n'
73 73 'a/b/d/qux.py\0%(hash1)s%(flag2)s\n'
74 74 'a/b/d/ten.txt\0%(hash3)s%(flag2)s\n'
75 75 'a/b/dog.py\0%(hash3)s%(flag1)s\n'
76 76 'a/b/fish.py\0%(hash2)s%(flag1)s\n'
77 77 'a/c/london.py\0%(hash3)s%(flag2)s\n'
78 78 'a/c/paper.txt\0%(hash2)s%(flag2)s\n'
79 79 'a/c/paris.py\0%(hash2)s%(flag1)s\n'
80 80 'a/d/apple.py\0%(hash3)s%(flag1)s\n'
81 81 'a/d/pizza.py\0%(hash3)s%(flag2)s\n'
82 82 'a/green.py\0%(hash1)s%(flag2)s\n'
83 83 'a/purple.py\0%(hash2)s%(flag1)s\n'
84 84 'app.py\0%(hash3)s%(flag1)s\n'
85 85 'readme.txt\0%(hash2)s%(flag1)s\n'
86 86 ) % {'hash1': HASH_1,
87 87 'flag1': '',
88 88 'hash2': HASH_2,
89 89 'flag2': 'l',
90 90 'hash3': HASH_3,
91 91 }
92 92
93 93 HUGE_MANIFEST_ENTRIES = 200001
94 94
95 izip = getattr(itertools, 'izip', zip)
96 if 'xrange' not in globals():
97 xrange = range
98
95 99 A_HUGE_MANIFEST = ''.join(sorted(
96 100 'file%d\0%s%s\n' % (i, h, f) for i, h, f in
97 itertools.izip(xrange(200001),
101 izip(xrange(200001),
98 102 itertools.cycle((HASH_1, HASH_2)),
99 103 itertools.cycle(('', 'x', 'l')))))
100 104
101 105 class basemanifesttests(object):
102 106 def parsemanifest(self, text):
103 107 raise NotImplementedError('parsemanifest not implemented by test case')
104 108
105 109 def testEmptyManifest(self):
106 110 m = self.parsemanifest(EMTPY_MANIFEST)
107 111 self.assertEqual(0, len(m))
108 112 self.assertEqual([], list(m))
109 113
110 114 def testEmptyManifestv2(self):
111 115 m = self.parsemanifest(EMTPY_MANIFEST_V2)
112 116 self.assertEqual(0, len(m))
113 117 self.assertEqual([], list(m))
114 118
115 119 def testManifest(self):
116 120 m = self.parsemanifest(A_SHORT_MANIFEST)
117 121 self.assertEqual(['bar/baz/qux.py', 'foo'], list(m))
118 122 self.assertEqual(BIN_HASH_2, m['bar/baz/qux.py'])
119 123 self.assertEqual('l', m.flags('bar/baz/qux.py'))
120 124 self.assertEqual(BIN_HASH_1, m['foo'])
121 125 self.assertEqual('', m.flags('foo'))
122 126 with self.assertRaises(KeyError):
123 127 m['wat']
124 128
125 129 def testParseManifestV2(self):
126 130 m1 = self.parsemanifest(A_SHORT_MANIFEST)
127 131 m2 = self.parsemanifest(A_SHORT_MANIFEST_V2)
128 132 # Should have same content as A_SHORT_MANIFEST
129 133 self.assertEqual(m1.text(), m2.text())
130 134
131 135 def testParseManifestMetadata(self):
132 136 # Metadata is for future-proofing and should be accepted but ignored
133 137 m = self.parsemanifest(A_METADATA_MANIFEST)
134 138 self.assertEqual(A_SHORT_MANIFEST, m.text())
135 139
136 140 def testParseManifestStemCompression(self):
137 141 m = self.parsemanifest(A_STEM_COMPRESSED_MANIFEST)
138 142 self.assertIn('bar/baz/qux.py', m)
139 143 self.assertIn('bar/qux/foo.py', m)
140 144 self.assertIn('bar/qux/foz.py', m)
141 145 self.assertIn(256 * 'x' + '/x', m)
142 146 self.assertIn(256 * 'x' + '/y', m)
143 147 self.assertEqual(A_STEM_COMPRESSED_MANIFEST, m.text(usemanifestv2=True))
144 148
145 149 def testTextV2(self):
146 150 m1 = self.parsemanifest(A_SHORT_MANIFEST)
147 151 v2text = m1.text(usemanifestv2=True)
148 152 self.assertEqual(A_SHORT_MANIFEST_V2, v2text)
149 153
150 154 def testSetItem(self):
151 155 want = BIN_HASH_1
152 156
153 157 m = self.parsemanifest(EMTPY_MANIFEST)
154 158 m['a'] = want
155 159 self.assertIn('a', m)
156 160 self.assertEqual(want, m['a'])
157 161 self.assertEqual('a\0' + HASH_1 + '\n', m.text())
158 162
159 163 m = self.parsemanifest(A_SHORT_MANIFEST)
160 164 m['a'] = want
161 165 self.assertEqual(want, m['a'])
162 166 self.assertEqual('a\0' + HASH_1 + '\n' + A_SHORT_MANIFEST,
163 167 m.text())
164 168
165 169 def testSetFlag(self):
166 170 want = 'x'
167 171
168 172 m = self.parsemanifest(EMTPY_MANIFEST)
169 173 # first add a file; a file-less flag makes no sense
170 174 m['a'] = BIN_HASH_1
171 175 m.setflag('a', want)
172 176 self.assertEqual(want, m.flags('a'))
173 177 self.assertEqual('a\0' + HASH_1 + want + '\n', m.text())
174 178
175 179 m = self.parsemanifest(A_SHORT_MANIFEST)
176 180 # first add a file; a file-less flag makes no sense
177 181 m['a'] = BIN_HASH_1
178 182 m.setflag('a', want)
179 183 self.assertEqual(want, m.flags('a'))
180 184 self.assertEqual('a\0' + HASH_1 + want + '\n' + A_SHORT_MANIFEST,
181 185 m.text())
182 186
183 187 def testCopy(self):
184 188 m = self.parsemanifest(A_SHORT_MANIFEST)
185 189 m['a'] = BIN_HASH_1
186 190 m2 = m.copy()
187 191 del m
188 192 del m2 # make sure we don't double free() anything
189 193
190 194 def testCompaction(self):
191 195 unhex = binascii.unhexlify
192 196 h1, h2 = unhex(HASH_1), unhex(HASH_2)
193 197 m = self.parsemanifest(A_SHORT_MANIFEST)
194 198 m['alpha'] = h1
195 199 m['beta'] = h2
196 200 del m['foo']
197 201 want = 'alpha\0%s\nbar/baz/qux.py\0%sl\nbeta\0%s\n' % (
198 202 HASH_1, HASH_2, HASH_2)
199 203 self.assertEqual(want, m.text())
200 204 self.assertEqual(3, len(m))
201 205 self.assertEqual(['alpha', 'bar/baz/qux.py', 'beta'], list(m))
202 206 self.assertEqual(h1, m['alpha'])
203 207 self.assertEqual(h2, m['bar/baz/qux.py'])
204 208 self.assertEqual(h2, m['beta'])
205 209 self.assertEqual('', m.flags('alpha'))
206 210 self.assertEqual('l', m.flags('bar/baz/qux.py'))
207 211 self.assertEqual('', m.flags('beta'))
208 212 with self.assertRaises(KeyError):
209 213 m['foo']
210 214
211 215 def testSetGetNodeSuffix(self):
212 216 clean = self.parsemanifest(A_SHORT_MANIFEST)
213 217 m = self.parsemanifest(A_SHORT_MANIFEST)
214 218 h = m['foo']
215 219 f = m.flags('foo')
216 220 want = h + 'a'
217 221 # Merge code wants to set 21-byte fake hashes at times
218 222 m['foo'] = want
219 223 self.assertEqual(want, m['foo'])
220 224 self.assertEqual([('bar/baz/qux.py', BIN_HASH_2),
221 225 ('foo', BIN_HASH_1 + 'a')],
222 226 list(m.iteritems()))
223 227 # Sometimes it even tries a 22-byte fake hash, but we can
224 228 # return 21 and it'll work out
225 229 m['foo'] = want + '+'
226 230 self.assertEqual(want, m['foo'])
227 231 # make sure the suffix survives a copy
228 232 match = matchmod.match('', '', ['re:foo'])
229 233 m2 = m.matches(match)
230 234 self.assertEqual(want, m2['foo'])
231 235 self.assertEqual(1, len(m2))
232 236 m2 = m.copy()
233 237 self.assertEqual(want, m2['foo'])
234 238 # suffix with iteration
235 239 self.assertEqual([('bar/baz/qux.py', BIN_HASH_2),
236 240 ('foo', want)],
237 241 list(m.iteritems()))
238 242
239 243 # shows up in diff
240 244 self.assertEqual({'foo': ((want, f), (h, ''))}, m.diff(clean))
241 245 self.assertEqual({'foo': ((h, ''), (want, f))}, clean.diff(m))
242 246
243 247 def testMatchException(self):
244 248 m = self.parsemanifest(A_SHORT_MANIFEST)
245 249 match = matchmod.match('', '', ['re:.*'])
246 250 def filt(path):
247 251 if path == 'foo':
248 252 assert False
249 253 return True
250 254 match.matchfn = filt
251 255 with self.assertRaises(AssertionError):
252 256 m.matches(match)
253 257
254 258 def testRemoveItem(self):
255 259 m = self.parsemanifest(A_SHORT_MANIFEST)
256 260 del m['foo']
257 261 with self.assertRaises(KeyError):
258 262 m['foo']
259 263 self.assertEqual(1, len(m))
260 264 self.assertEqual(1, len(list(m)))
261 265 # now restore and make sure everything works right
262 266 m['foo'] = 'a' * 20
263 267 self.assertEqual(2, len(m))
264 268 self.assertEqual(2, len(list(m)))
265 269
266 270 def testManifestDiff(self):
267 271 MISSING = (None, '')
268 272 addl = 'z-only-in-left\0' + HASH_1 + '\n'
269 273 addr = 'z-only-in-right\0' + HASH_2 + 'x\n'
270 274 left = self.parsemanifest(
271 275 A_SHORT_MANIFEST.replace(HASH_1, HASH_3 + 'x') + addl)
272 276 right = self.parsemanifest(A_SHORT_MANIFEST + addr)
273 277 want = {
274 278 'foo': ((BIN_HASH_3, 'x'),
275 279 (BIN_HASH_1, '')),
276 280 'z-only-in-left': ((BIN_HASH_1, ''), MISSING),
277 281 'z-only-in-right': (MISSING, (BIN_HASH_2, 'x')),
278 282 }
279 283 self.assertEqual(want, left.diff(right))
280 284
281 285 want = {
282 286 'bar/baz/qux.py': (MISSING, (BIN_HASH_2, 'l')),
283 287 'foo': (MISSING, (BIN_HASH_3, 'x')),
284 288 'z-only-in-left': (MISSING, (BIN_HASH_1, '')),
285 289 }
286 290 self.assertEqual(want, self.parsemanifest(EMTPY_MANIFEST).diff(left))
287 291
288 292 want = {
289 293 'bar/baz/qux.py': ((BIN_HASH_2, 'l'), MISSING),
290 294 'foo': ((BIN_HASH_3, 'x'), MISSING),
291 295 'z-only-in-left': ((BIN_HASH_1, ''), MISSING),
292 296 }
293 297 self.assertEqual(want, left.diff(self.parsemanifest(EMTPY_MANIFEST)))
294 298 copy = right.copy()
295 299 del copy['z-only-in-right']
296 300 del right['foo']
297 301 want = {
298 302 'foo': (MISSING, (BIN_HASH_1, '')),
299 303 'z-only-in-right': ((BIN_HASH_2, 'x'), MISSING),
300 304 }
301 305 self.assertEqual(want, right.diff(copy))
302 306
303 307 short = self.parsemanifest(A_SHORT_MANIFEST)
304 308 pruned = short.copy()
305 309 del pruned['foo']
306 310 want = {
307 311 'foo': ((BIN_HASH_1, ''), MISSING),
308 312 }
309 313 self.assertEqual(want, short.diff(pruned))
310 314 want = {
311 315 'foo': (MISSING, (BIN_HASH_1, '')),
312 316 }
313 317 self.assertEqual(want, pruned.diff(short))
314 318 want = {
315 319 'bar/baz/qux.py': None,
316 320 'foo': (MISSING, (BIN_HASH_1, '')),
317 321 }
318 322 self.assertEqual(want, pruned.diff(short, clean=True))
319 323
320 324 def testReversedLines(self):
321 325 backwards = ''.join(
322 326 l + '\n' for l in reversed(A_SHORT_MANIFEST.split('\n')) if l)
323 327 try:
324 328 self.parsemanifest(backwards)
325 329 self.fail('Should have raised ValueError')
326 330 except ValueError as v:
327 331 self.assertIn('Manifest lines not in sorted order.', str(v))
328 332
329 333 def testNoTerminalNewline(self):
330 334 try:
331 335 self.parsemanifest(A_SHORT_MANIFEST + 'wat')
332 336 self.fail('Should have raised ValueError')
333 337 except ValueError as v:
334 338 self.assertIn('Manifest did not end in a newline.', str(v))
335 339
336 340 def testNoNewLineAtAll(self):
337 341 try:
338 342 self.parsemanifest('wat')
339 343 self.fail('Should have raised ValueError')
340 344 except ValueError as v:
341 345 self.assertIn('Manifest did not end in a newline.', str(v))
342 346
343 347 def testHugeManifest(self):
344 348 m = self.parsemanifest(A_HUGE_MANIFEST)
345 349 self.assertEqual(HUGE_MANIFEST_ENTRIES, len(m))
346 350 self.assertEqual(len(m), len(list(m)))
347 351
348 352 def testMatchesMetadata(self):
349 353 '''Tests matches() for a few specific files to make sure that both
350 354 the set of files as well as their flags and nodeids are correct in
351 355 the resulting manifest.'''
352 356 m = self.parsemanifest(A_HUGE_MANIFEST)
353 357
354 358 match = matchmod.match('/', '',
355 359 ['file1', 'file200', 'file300'], exact=True)
356 360 m2 = m.matches(match)
357 361
358 362 w = ('file1\0%sx\n'
359 363 'file200\0%sl\n'
360 364 'file300\0%s\n') % (HASH_2, HASH_1, HASH_1)
361 365 self.assertEqual(w, m2.text())
362 366
363 367 def testMatchesNonexistentFile(self):
364 368 '''Tests matches() for a small set of specific files, including one
365 369 nonexistent file to make sure in only matches against existing files.
366 370 '''
367 371 m = self.parsemanifest(A_DEEPER_MANIFEST)
368 372
369 373 match = matchmod.match('/', '',
370 374 ['a/b/c/bar.txt', 'a/b/d/qux.py', 'readme.txt', 'nonexistent'],
371 375 exact=True)
372 376 m2 = m.matches(match)
373 377
374 378 self.assertEqual(
375 379 ['a/b/c/bar.txt', 'a/b/d/qux.py', 'readme.txt'],
376 380 m2.keys())
377 381
378 382 def testMatchesNonexistentDirectory(self):
379 383 '''Tests matches() for a relpath match on a directory that doesn't
380 384 actually exist.'''
381 385 m = self.parsemanifest(A_DEEPER_MANIFEST)
382 386
383 387 match = matchmod.match('/', '', ['a/f'], default='relpath')
384 388 m2 = m.matches(match)
385 389
386 390 self.assertEqual([], m2.keys())
387 391
388 392 def testMatchesExactLarge(self):
389 393 '''Tests matches() for files matching a large list of exact files.
390 394 '''
391 395 m = self.parsemanifest(A_HUGE_MANIFEST)
392 396
393 397 flist = m.keys()[80:300]
394 398 match = matchmod.match('/', '', flist, exact=True)
395 399 m2 = m.matches(match)
396 400
397 401 self.assertEqual(flist, m2.keys())
398 402
399 403 def testMatchesFull(self):
400 404 '''Tests matches() for what should be a full match.'''
401 405 m = self.parsemanifest(A_DEEPER_MANIFEST)
402 406
403 407 match = matchmod.match('/', '', [''])
404 408 m2 = m.matches(match)
405 409
406 410 self.assertEqual(m.keys(), m2.keys())
407 411
408 412 def testMatchesDirectory(self):
409 413 '''Tests matches() on a relpath match on a directory, which should
410 414 match against all files within said directory.'''
411 415 m = self.parsemanifest(A_DEEPER_MANIFEST)
412 416
413 417 match = matchmod.match('/', '', ['a/b'], default='relpath')
414 418 m2 = m.matches(match)
415 419
416 420 self.assertEqual([
417 421 'a/b/c/bar.py', 'a/b/c/bar.txt', 'a/b/c/foo.py', 'a/b/c/foo.txt',
418 422 'a/b/d/baz.py', 'a/b/d/qux.py', 'a/b/d/ten.txt', 'a/b/dog.py',
419 423 'a/b/fish.py'], m2.keys())
420 424
421 425 def testMatchesExactPath(self):
422 426 '''Tests matches() on an exact match on a directory, which should
423 427 result in an empty manifest because you can't perform an exact match
424 428 against a directory.'''
425 429 m = self.parsemanifest(A_DEEPER_MANIFEST)
426 430
427 431 match = matchmod.match('/', '', ['a/b'], exact=True)
428 432 m2 = m.matches(match)
429 433
430 434 self.assertEqual([], m2.keys())
431 435
432 436 def testMatchesCwd(self):
433 437 '''Tests matches() on a relpath match with the current directory ('.')
434 438 when not in the root directory.'''
435 439 m = self.parsemanifest(A_DEEPER_MANIFEST)
436 440
437 441 match = matchmod.match('/', 'a/b', ['.'], default='relpath')
438 442 m2 = m.matches(match)
439 443
440 444 self.assertEqual([
441 445 'a/b/c/bar.py', 'a/b/c/bar.txt', 'a/b/c/foo.py', 'a/b/c/foo.txt',
442 446 'a/b/d/baz.py', 'a/b/d/qux.py', 'a/b/d/ten.txt', 'a/b/dog.py',
443 447 'a/b/fish.py'], m2.keys())
444 448
445 449 def testMatchesWithPattern(self):
446 450 '''Tests matches() for files matching a pattern that reside
447 451 deeper than the specified directory.'''
448 452 m = self.parsemanifest(A_DEEPER_MANIFEST)
449 453
450 454 match = matchmod.match('/', '', ['a/b/*/*.txt'])
451 455 m2 = m.matches(match)
452 456
453 457 self.assertEqual(
454 458 ['a/b/c/bar.txt', 'a/b/c/foo.txt', 'a/b/d/ten.txt'],
455 459 m2.keys())
456 460
457 461 class testmanifestdict(unittest.TestCase, basemanifesttests):
458 462 def parsemanifest(self, text):
459 463 return manifestmod.manifestdict(text)
460 464
461 465 class testtreemanifest(unittest.TestCase, basemanifesttests):
462 466 def parsemanifest(self, text):
463 467 return manifestmod.treemanifest('', text)
464 468
465 469 def testWalkSubtrees(self):
466 470 m = self.parsemanifest(A_DEEPER_MANIFEST)
467 471
468 472 dirs = [s._dir for s in m.walksubtrees()]
469 473 self.assertEqual(
470 474 sorted(['', 'a/', 'a/c/', 'a/d/', 'a/b/', 'a/b/c/', 'a/b/d/']),
471 475 sorted(dirs)
472 476 )
473 477
474 478 match = matchmod.match('/', '', ['path:a/b/'])
475 479 dirs = [s._dir for s in m.walksubtrees(matcher=match)]
476 480 self.assertEqual(
477 481 sorted(['a/b/', 'a/b/c/', 'a/b/d/']),
478 482 sorted(dirs)
479 483 )
480 484
481 485 if __name__ == '__main__':
482 486 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now