##// END OF EJS Templates
test-manifest: extract constants for binary hashes...
Martin von Zweigbergk -
r24570:487245cb default
parent child Browse files
Show More
@@ -1,391 +1,394 b''
1 1 import binascii
2 2 import unittest
3 3 import itertools
4 4
5 5 import silenttestrunner
6 6
7 7 from mercurial import manifest as manifestmod
8 8 from mercurial import match as matchmod
9 9
10 10 EMTPY_MANIFEST = ''
11 11
12 12 HASH_1 = '1' * 40
13 BIN_HASH_1 = binascii.unhexlify(HASH_1)
13 14 HASH_2 = 'f' * 40
15 BIN_HASH_2 = binascii.unhexlify(HASH_2)
14 16 HASH_3 = '1234567890abcdef0987654321deadbeef0fcafe'
17 BIN_HASH_3 = binascii.unhexlify(HASH_3)
15 18 A_SHORT_MANIFEST = (
16 19 'bar/baz/qux.py\0%(hash2)s%(flag2)s\n'
17 20 'foo\0%(hash1)s%(flag1)s\n'
18 21 ) % {'hash1': HASH_1,
19 22 'flag1': '',
20 23 'hash2': HASH_2,
21 24 'flag2': 'l',
22 25 }
23 26
24 27 A_DEEPER_MANIFEST = (
25 28 'a/b/c/bar.py\0%(hash3)s%(flag1)s\n'
26 29 'a/b/c/bar.txt\0%(hash1)s%(flag1)s\n'
27 30 'a/b/c/foo.py\0%(hash3)s%(flag1)s\n'
28 31 'a/b/c/foo.txt\0%(hash2)s%(flag2)s\n'
29 32 'a/b/d/baz.py\0%(hash3)s%(flag1)s\n'
30 33 'a/b/d/qux.py\0%(hash1)s%(flag2)s\n'
31 34 'a/b/d/ten.txt\0%(hash3)s%(flag2)s\n'
32 35 'a/b/dog.py\0%(hash3)s%(flag1)s\n'
33 36 'a/b/fish.py\0%(hash2)s%(flag1)s\n'
34 37 'a/c/london.py\0%(hash3)s%(flag2)s\n'
35 38 'a/c/paper.txt\0%(hash2)s%(flag2)s\n'
36 39 'a/c/paris.py\0%(hash2)s%(flag1)s\n'
37 40 'a/d/apple.py\0%(hash3)s%(flag1)s\n'
38 41 'a/d/pizza.py\0%(hash3)s%(flag2)s\n'
39 42 'a/green.py\0%(hash1)s%(flag2)s\n'
40 43 'a/purple.py\0%(hash2)s%(flag1)s\n'
41 44 'app.py\0%(hash3)s%(flag1)s\n'
42 45 'readme.txt\0%(hash2)s%(flag1)s\n'
43 46 ) % {'hash1': HASH_1,
44 47 'flag1': '',
45 48 'hash2': HASH_2,
46 49 'flag2': 'l',
47 50 'hash3': HASH_3,
48 51 }
49 52
50 53 HUGE_MANIFEST_ENTRIES = 200001
51 54
52 55 A_HUGE_MANIFEST = ''.join(sorted(
53 56 'file%d\0%s%s\n' % (i, h, f) for i, h, f in
54 57 itertools.izip(xrange(200001),
55 58 itertools.cycle((HASH_1, HASH_2)),
56 59 itertools.cycle(('', 'x', 'l')))))
57 60
58 61 def parsemanifest(text):
59 62 return manifestmod.manifestdict(text)
60 63
61 64 class testmanifest(unittest.TestCase):
62 65
63 66 def assertIn(self, thing, container, msg=None):
64 67 # assertIn new in 2.7, use it if available, otherwise polyfill
65 68 sup = getattr(unittest.TestCase, 'assertIn', False)
66 69 if sup:
67 70 return sup(self, thing, container, msg=msg)
68 71 if not msg:
69 72 msg = 'Expected %r in %r' % (thing, container)
70 73 self.assert_(thing in container, msg)
71 74
72 75 def testEmptyManifest(self):
73 76 m = parsemanifest(EMTPY_MANIFEST)
74 77 self.assertEqual(0, len(m))
75 78 self.assertEqual([], list(m))
76 79
77 80 def testManifest(self):
78 81 m = parsemanifest(A_SHORT_MANIFEST)
79 82 self.assertEqual(['bar/baz/qux.py', 'foo'], list(m))
80 self.assertEqual(binascii.unhexlify(HASH_2), m['bar/baz/qux.py'])
83 self.assertEqual(BIN_HASH_2, m['bar/baz/qux.py'])
81 84 self.assertEqual('l', m.flags('bar/baz/qux.py'))
82 self.assertEqual(binascii.unhexlify(HASH_1), m['foo'])
85 self.assertEqual(BIN_HASH_1, m['foo'])
83 86 self.assertEqual('', m.flags('foo'))
84 87 self.assertRaises(KeyError, lambda : m['wat'])
85 88
86 89 def testSetItem(self):
87 want = binascii.unhexlify(HASH_1)
90 want = BIN_HASH_1
88 91
89 92 m = parsemanifest(EMTPY_MANIFEST)
90 93 m['a'] = want
91 94 self.assertIn('a', m)
92 95 self.assertEqual(want, m['a'])
93 96 self.assertEqual('a\0' + HASH_1 + '\n', m.text())
94 97
95 98 m = parsemanifest(A_SHORT_MANIFEST)
96 99 m['a'] = want
97 100 self.assertEqual(want, m['a'])
98 101 self.assertEqual('a\0' + HASH_1 + '\n' + A_SHORT_MANIFEST,
99 102 m.text())
100 103
101 104 def testSetFlag(self):
102 105 want = 'x'
103 106
104 107 m = parsemanifest(EMTPY_MANIFEST)
105 108 # first add a file; a file-less flag makes no sense
106 m['a'] = binascii.unhexlify(HASH_1)
109 m['a'] = BIN_HASH_1
107 110 m.setflag('a', want)
108 111 self.assertEqual(want, m.flags('a'))
109 112 self.assertEqual('a\0' + HASH_1 + want + '\n', m.text())
110 113
111 114 m = parsemanifest(A_SHORT_MANIFEST)
112 115 # first add a file; a file-less flag makes no sense
113 m['a'] = binascii.unhexlify(HASH_1)
116 m['a'] = BIN_HASH_1
114 117 m.setflag('a', want)
115 118 self.assertEqual(want, m.flags('a'))
116 119 self.assertEqual('a\0' + HASH_1 + want + '\n' + A_SHORT_MANIFEST,
117 120 m.text())
118 121
119 122 def testCopy(self):
120 123 m = parsemanifest(A_SHORT_MANIFEST)
121 m['a'] = binascii.unhexlify(HASH_1)
124 m['a'] = BIN_HASH_1
122 125 m2 = m.copy()
123 126 del m
124 127 del m2 # make sure we don't double free() anything
125 128
126 129 def testCompaction(self):
127 130 unhex = binascii.unhexlify
128 131 h1, h2 = unhex(HASH_1), unhex(HASH_2)
129 132 m = parsemanifest(A_SHORT_MANIFEST)
130 133 m['alpha'] = h1
131 134 m['beta'] = h2
132 135 del m['foo']
133 136 want = 'alpha\0%s\nbar/baz/qux.py\0%sl\nbeta\0%s\n' % (
134 137 HASH_1, HASH_2, HASH_2)
135 138 self.assertEqual(want, m.text())
136 139 self.assertEqual(3, len(m))
137 140 self.assertEqual(['alpha', 'bar/baz/qux.py', 'beta'], list(m))
138 141 self.assertEqual(h1, m['alpha'])
139 142 self.assertEqual(h2, m['bar/baz/qux.py'])
140 143 self.assertEqual(h2, m['beta'])
141 144 self.assertEqual('', m.flags('alpha'))
142 145 self.assertEqual('l', m.flags('bar/baz/qux.py'))
143 146 self.assertEqual('', m.flags('beta'))
144 147 self.assertRaises(KeyError, lambda : m['foo'])
145 148
146 149 def testSetGetNodeSuffix(self):
147 150 clean = parsemanifest(A_SHORT_MANIFEST)
148 151 m = parsemanifest(A_SHORT_MANIFEST)
149 152 h = m['foo']
150 153 f = m.flags('foo')
151 154 want = h + 'a'
152 155 # Merge code wants to set 21-byte fake hashes at times
153 156 m['foo'] = want
154 157 self.assertEqual(want, m['foo'])
155 self.assertEqual([('bar/baz/qux.py', binascii.unhexlify(HASH_2)),
156 ('foo', binascii.unhexlify(HASH_1) + 'a')],
158 self.assertEqual([('bar/baz/qux.py', BIN_HASH_2),
159 ('foo', BIN_HASH_1 + 'a')],
157 160 list(m.iteritems()))
158 161 # Sometimes it even tries a 22-byte fake hash, but we can
159 162 # return 21 and it'll work out
160 163 m['foo'] = want + '+'
161 164 self.assertEqual(want, m['foo'])
162 165 # make sure the suffix survives a copy
163 166 match = matchmod.match('', '', ['re:foo'])
164 167 m2 = m.matches(match)
165 168 self.assertEqual(want, m2['foo'])
166 169 self.assertEqual(1, len(m2))
167 170 m2 = m.copy()
168 171 self.assertEqual(want, m2['foo'])
169 172 # suffix with iteration
170 self.assertEqual([('bar/baz/qux.py', binascii.unhexlify(HASH_2)),
173 self.assertEqual([('bar/baz/qux.py', BIN_HASH_2),
171 174 ('foo', want)],
172 175 list(m.iteritems()))
173 176
174 177 # shows up in diff
175 178 self.assertEqual({'foo': ((want, f), (h, ''))}, m.diff(clean))
176 179 self.assertEqual({'foo': ((h, ''), (want, f))}, clean.diff(m))
177 180
178 181 def testMatchException(self):
179 182 m = parsemanifest(A_SHORT_MANIFEST)
180 183 match = matchmod.match('', '', ['re:.*'])
181 184 def filt(path):
182 185 if path == 'foo':
183 186 assert False
184 187 return True
185 188 match.matchfn = filt
186 189 self.assertRaises(AssertionError, m.matches, match)
187 190
188 191 def testRemoveItem(self):
189 192 m = parsemanifest(A_SHORT_MANIFEST)
190 193 del m['foo']
191 194 self.assertRaises(KeyError, lambda : m['foo'])
192 195 self.assertEqual(1, len(m))
193 196 self.assertEqual(1, len(list(m)))
194 197 # now restore and make sure everything works right
195 198 m['foo'] = 'a' * 20
196 199 self.assertEqual(2, len(m))
197 200 self.assertEqual(2, len(list(m)))
198 201
199 202 def testManifestDiff(self):
200 203 MISSING = (None, '')
201 204 addl = 'z-only-in-left\0' + HASH_1 + '\n'
202 205 addr = 'z-only-in-right\0' + HASH_2 + 'x\n'
203 206 left = parsemanifest(
204 207 A_SHORT_MANIFEST.replace(HASH_1, HASH_3 + 'x') + addl)
205 208 right = parsemanifest(A_SHORT_MANIFEST + addr)
206 209 want = {
207 'foo': ((binascii.unhexlify(HASH_3), 'x'),
208 (binascii.unhexlify(HASH_1), '')),
209 'z-only-in-left': ((binascii.unhexlify(HASH_1), ''), MISSING),
210 'z-only-in-right': (MISSING, (binascii.unhexlify(HASH_2), 'x')),
210 'foo': ((BIN_HASH_3, 'x'),
211 (BIN_HASH_1, '')),
212 'z-only-in-left': ((BIN_HASH_1, ''), MISSING),
213 'z-only-in-right': (MISSING, (BIN_HASH_2, 'x')),
211 214 }
212 215 self.assertEqual(want, left.diff(right))
213 216
214 217 want = {
215 'bar/baz/qux.py': (MISSING, (binascii.unhexlify(HASH_2), 'l')),
216 'foo': (MISSING, (binascii.unhexlify(HASH_3), 'x')),
217 'z-only-in-left': (MISSING, (binascii.unhexlify(HASH_1), '')),
218 'bar/baz/qux.py': (MISSING, (BIN_HASH_2, 'l')),
219 'foo': (MISSING, (BIN_HASH_3, 'x')),
220 'z-only-in-left': (MISSING, (BIN_HASH_1, '')),
218 221 }
219 222 self.assertEqual(want, parsemanifest(EMTPY_MANIFEST).diff(left))
220 223
221 224 want = {
222 'bar/baz/qux.py': ((binascii.unhexlify(HASH_2), 'l'), MISSING),
223 'foo': ((binascii.unhexlify(HASH_3), 'x'), MISSING),
224 'z-only-in-left': ((binascii.unhexlify(HASH_1), ''), MISSING),
225 'bar/baz/qux.py': ((BIN_HASH_2, 'l'), MISSING),
226 'foo': ((BIN_HASH_3, 'x'), MISSING),
227 'z-only-in-left': ((BIN_HASH_1, ''), MISSING),
225 228 }
226 229 self.assertEqual(want, left.diff(parsemanifest(EMTPY_MANIFEST)))
227 230 copy = right.copy()
228 231 del copy['z-only-in-right']
229 232 del right['foo']
230 233 want = {
231 'foo': (MISSING, (binascii.unhexlify(HASH_1), '')),
232 'z-only-in-right': ((binascii.unhexlify(HASH_2), 'x'), MISSING),
234 'foo': (MISSING, (BIN_HASH_1, '')),
235 'z-only-in-right': ((BIN_HASH_2, 'x'), MISSING),
233 236 }
234 237 self.assertEqual(want, right.diff(copy))
235 238
236 239 short = parsemanifest(A_SHORT_MANIFEST)
237 240 pruned = short.copy()
238 241 del pruned['foo']
239 242 want = {
240 'foo': ((binascii.unhexlify(HASH_1), ''), MISSING),
243 'foo': ((BIN_HASH_1, ''), MISSING),
241 244 }
242 245 self.assertEqual(want, short.diff(pruned))
243 246 want = {
244 'foo': (MISSING, (binascii.unhexlify(HASH_1), '')),
247 'foo': (MISSING, (BIN_HASH_1, '')),
245 248 }
246 249 self.assertEqual(want, pruned.diff(short))
247 250 want = {
248 251 'bar/baz/qux.py': None,
249 'foo': (MISSING, (binascii.unhexlify(HASH_1), '')),
252 'foo': (MISSING, (BIN_HASH_1, '')),
250 253 }
251 254 self.assertEqual(want, pruned.diff(short, True))
252 255
253 256 def testReversedLines(self):
254 257 backwards = ''.join(
255 258 l + '\n' for l in reversed(A_SHORT_MANIFEST.split('\n')) if l)
256 259 try:
257 260 parsemanifest(backwards)
258 261 self.fail('Should have raised ValueError')
259 262 except ValueError, v:
260 263 self.assertIn('Manifest lines not in sorted order.', str(v))
261 264
262 265 def testNoTerminalNewline(self):
263 266 try:
264 267 parsemanifest(A_SHORT_MANIFEST + 'wat')
265 268 self.fail('Should have raised ValueError')
266 269 except ValueError, v:
267 270 self.assertIn('Manifest did not end in a newline.', str(v))
268 271
269 272 def testNoNewLineAtAll(self):
270 273 try:
271 274 parsemanifest('wat')
272 275 self.fail('Should have raised ValueError')
273 276 except ValueError, v:
274 277 self.assertIn('Manifest did not end in a newline.', str(v))
275 278
276 279 def testHugeManifest(self):
277 280 m = parsemanifest(A_HUGE_MANIFEST)
278 281 self.assertEqual(HUGE_MANIFEST_ENTRIES, len(m))
279 282 self.assertEqual(len(m), len(list(m)))
280 283
281 284 def testMatchesMetadata(self):
282 285 '''Tests matches() for a few specific files to make sure that both
283 286 the set of files as well as their flags and nodeids are correct in
284 287 the resulting manifest.'''
285 288 m = parsemanifest(A_HUGE_MANIFEST)
286 289
287 290 match = matchmod.match('/', '',
288 291 ['file1', 'file200', 'file300'], exact=True)
289 292 m2 = m.matches(match)
290 293
291 294 w = ('file1\0%sx\n'
292 295 'file200\0%sl\n'
293 296 'file300\0%s\n') % (HASH_2, HASH_1, HASH_1)
294 297 self.assertEqual(w, m2.text())
295 298
296 299 def testMatchesNonexistentFile(self):
297 300 '''Tests matches() for a small set of specific files, including one
298 301 nonexistent file to make sure in only matches against existing files.
299 302 '''
300 303 m = parsemanifest(A_DEEPER_MANIFEST)
301 304
302 305 match = matchmod.match('/', '',
303 306 ['a/b/c/bar.txt', 'a/b/d/qux.py', 'readme.txt', 'nonexistent'],
304 307 exact=True)
305 308 m2 = m.matches(match)
306 309
307 310 self.assertEqual(
308 311 ['a/b/c/bar.txt', 'a/b/d/qux.py', 'readme.txt'],
309 312 m2.keys())
310 313
311 314 def testMatchesNonexistentDirectory(self):
312 315 '''Tests matches() for a relpath match on a directory that doesn't
313 316 actually exist.'''
314 317 m = parsemanifest(A_DEEPER_MANIFEST)
315 318
316 319 match = matchmod.match('/', '', ['a/f'], default='relpath')
317 320 m2 = m.matches(match)
318 321
319 322 self.assertEqual([], m2.keys())
320 323
321 324 def testMatchesExactLarge(self):
322 325 '''Tests matches() for files matching a large list of exact files.
323 326 '''
324 327 m = parsemanifest(A_HUGE_MANIFEST)
325 328
326 329 flist = m.keys()[80:300]
327 330 match = matchmod.match('/', '', flist, exact=True)
328 331 m2 = m.matches(match)
329 332
330 333 self.assertEqual(flist, m2.keys())
331 334
332 335 def testMatchesFull(self):
333 336 '''Tests matches() for what should be a full match.'''
334 337 m = parsemanifest(A_DEEPER_MANIFEST)
335 338
336 339 match = matchmod.match('/', '', [''])
337 340 m2 = m.matches(match)
338 341
339 342 self.assertEqual(m.keys(), m2.keys())
340 343
341 344 def testMatchesDirectory(self):
342 345 '''Tests matches() on a relpath match on a directory, which should
343 346 match against all files within said directory.'''
344 347 m = parsemanifest(A_DEEPER_MANIFEST)
345 348
346 349 match = matchmod.match('/', '', ['a/b'], default='relpath')
347 350 m2 = m.matches(match)
348 351
349 352 self.assertEqual([
350 353 'a/b/c/bar.py', 'a/b/c/bar.txt', 'a/b/c/foo.py', 'a/b/c/foo.txt',
351 354 'a/b/d/baz.py', 'a/b/d/qux.py', 'a/b/d/ten.txt', 'a/b/dog.py',
352 355 'a/b/fish.py'], m2.keys())
353 356
354 357 def testMatchesExactPath(self):
355 358 '''Tests matches() on an exact match on a directory, which should
356 359 result in an empty manifest because you can't perform an exact match
357 360 against a directory.'''
358 361 m = parsemanifest(A_DEEPER_MANIFEST)
359 362
360 363 match = matchmod.match('/', '', ['a/b'], exact=True)
361 364 m2 = m.matches(match)
362 365
363 366 self.assertEqual([], m2.keys())
364 367
365 368 def testMatchesCwd(self):
366 369 '''Tests matches() on a relpath match with the current directory ('.')
367 370 when not in the root directory.'''
368 371 m = parsemanifest(A_DEEPER_MANIFEST)
369 372
370 373 match = matchmod.match('/', 'a/b', ['.'], default='relpath')
371 374 m2 = m.matches(match)
372 375
373 376 self.assertEqual([
374 377 'a/b/c/bar.py', 'a/b/c/bar.txt', 'a/b/c/foo.py', 'a/b/c/foo.txt',
375 378 'a/b/d/baz.py', 'a/b/d/qux.py', 'a/b/d/ten.txt', 'a/b/dog.py',
376 379 'a/b/fish.py'], m2.keys())
377 380
378 381 def testMatchesWithPattern(self):
379 382 '''Tests matches() for files matching a pattern that reside
380 383 deeper than the specified directory.'''
381 384 m = parsemanifest(A_DEEPER_MANIFEST)
382 385
383 386 match = matchmod.match('/', '', ['a/b/*/*.txt'])
384 387 m2 = m.matches(match)
385 388
386 389 self.assertEqual(
387 390 ['a/b/c/bar.txt', 'a/b/c/foo.txt', 'a/b/d/ten.txt'],
388 391 m2.keys())
389 392
390 393 if __name__ == '__main__':
391 394 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now