45 b'a/d/pizza.py\0%(hash3)s%(flag2)s\n' |
40 b'a/d/pizza.py\0%(hash3)s%(flag2)s\n' |
46 b'a/green.py\0%(hash1)s%(flag2)s\n' |
41 b'a/green.py\0%(hash1)s%(flag2)s\n' |
47 b'a/purple.py\0%(hash2)s%(flag1)s\n' |
42 b'a/purple.py\0%(hash2)s%(flag1)s\n' |
48 b'app.py\0%(hash3)s%(flag1)s\n' |
43 b'app.py\0%(hash3)s%(flag1)s\n' |
49 b'readme.txt\0%(hash2)s%(flag1)s\n' |
44 b'readme.txt\0%(hash2)s%(flag1)s\n' |
50 ) % {b'hash1': HASH_1, |
45 ) % { |
51 b'flag1': b'', |
46 b'hash1': HASH_1, |
52 b'hash2': HASH_2, |
47 b'flag1': b'', |
53 b'flag2': b'l', |
48 b'hash2': HASH_2, |
54 b'hash3': HASH_3, |
49 b'flag2': b'l', |
55 } |
50 b'hash3': HASH_3, |
|
51 } |
56 |
52 |
57 HUGE_MANIFEST_ENTRIES = 200001 |
53 HUGE_MANIFEST_ENTRIES = 200001 |
58 |
54 |
59 izip = getattr(itertools, 'izip', zip) |
55 izip = getattr(itertools, 'izip', zip) |
60 if 'xrange' not in globals(): |
56 if 'xrange' not in globals(): |
61 xrange = range |
57 xrange = range |
62 |
58 |
63 A_HUGE_MANIFEST = b''.join(sorted( |
59 A_HUGE_MANIFEST = b''.join( |
64 b'file%d\0%s%s\n' % (i, h, f) for i, h, f in |
60 sorted( |
65 izip(xrange(200001), |
61 b'file%d\0%s%s\n' % (i, h, f) |
66 itertools.cycle((HASH_1, HASH_2)), |
62 for i, h, f in izip( |
67 itertools.cycle((b'', b'x', b'l'))))) |
63 xrange(200001), |
|
64 itertools.cycle((HASH_1, HASH_2)), |
|
65 itertools.cycle((b'', b'x', b'l')), |
|
66 ) |
|
67 ) |
|
68 ) |
|
69 |
68 |
70 |
69 class basemanifesttests(object): |
71 class basemanifesttests(object): |
70 def parsemanifest(self, text): |
72 def parsemanifest(self, text): |
71 raise NotImplementedError('parsemanifest not implemented by test case') |
73 raise NotImplementedError('parsemanifest not implemented by test case') |
72 |
74 |
113 m = self.parsemanifest(A_SHORT_MANIFEST) |
114 m = self.parsemanifest(A_SHORT_MANIFEST) |
114 # first add a file; a file-less flag makes no sense |
115 # first add a file; a file-less flag makes no sense |
115 m[b'a'] = BIN_HASH_1 |
116 m[b'a'] = BIN_HASH_1 |
116 m.setflag(b'a', want) |
117 m.setflag(b'a', want) |
117 self.assertEqual(want, m.flags(b'a')) |
118 self.assertEqual(want, m.flags(b'a')) |
118 self.assertEqual(b'a\0' + HASH_1 + want + b'\n' + A_SHORT_MANIFEST, |
119 self.assertEqual( |
119 m.text()) |
120 b'a\0' + HASH_1 + want + b'\n' + A_SHORT_MANIFEST, m.text() |
|
121 ) |
120 |
122 |
121 def testCopy(self): |
123 def testCopy(self): |
122 m = self.parsemanifest(A_SHORT_MANIFEST) |
124 m = self.parsemanifest(A_SHORT_MANIFEST) |
123 m[b'a'] = BIN_HASH_1 |
125 m[b'a'] = BIN_HASH_1 |
124 m2 = m.copy() |
126 m2 = m.copy() |
125 del m |
127 del m |
126 del m2 # make sure we don't double free() anything |
128 del m2 # make sure we don't double free() anything |
127 |
129 |
128 def testCompaction(self): |
130 def testCompaction(self): |
129 unhex = binascii.unhexlify |
131 unhex = binascii.unhexlify |
130 h1, h2 = unhex(HASH_1), unhex(HASH_2) |
132 h1, h2 = unhex(HASH_1), unhex(HASH_2) |
131 m = self.parsemanifest(A_SHORT_MANIFEST) |
133 m = self.parsemanifest(A_SHORT_MANIFEST) |
132 m[b'alpha'] = h1 |
134 m[b'alpha'] = h1 |
133 m[b'beta'] = h2 |
135 m[b'beta'] = h2 |
134 del m[b'foo'] |
136 del m[b'foo'] |
135 want = b'alpha\0%s\nbar/baz/qux.py\0%sl\nbeta\0%s\n' % ( |
137 want = b'alpha\0%s\nbar/baz/qux.py\0%sl\nbeta\0%s\n' % ( |
136 HASH_1, HASH_2, HASH_2) |
138 HASH_1, |
|
139 HASH_2, |
|
140 HASH_2, |
|
141 ) |
137 self.assertEqual(want, m.text()) |
142 self.assertEqual(want, m.text()) |
138 self.assertEqual(3, len(m)) |
143 self.assertEqual(3, len(m)) |
139 self.assertEqual([b'alpha', b'bar/baz/qux.py', b'beta'], list(m)) |
144 self.assertEqual([b'alpha', b'bar/baz/qux.py', b'beta'], list(m)) |
140 self.assertEqual(h1, m[b'alpha']) |
145 self.assertEqual(h1, m[b'alpha']) |
141 self.assertEqual(h2, m[b'bar/baz/qux.py']) |
146 self.assertEqual(h2, m[b'bar/baz/qux.py']) |
168 self.assertEqual(want, m2[b'foo']) |
174 self.assertEqual(want, m2[b'foo']) |
169 self.assertEqual(1, len(m2)) |
175 self.assertEqual(1, len(m2)) |
170 m2 = m.copy() |
176 m2 = m.copy() |
171 self.assertEqual(want, m2[b'foo']) |
177 self.assertEqual(want, m2[b'foo']) |
172 # suffix with iteration |
178 # suffix with iteration |
173 self.assertEqual([(b'bar/baz/qux.py', BIN_HASH_2), |
179 self.assertEqual( |
174 (b'foo', want)], |
180 [(b'bar/baz/qux.py', BIN_HASH_2), (b'foo', want)], list(m.items()) |
175 list(m.items())) |
181 ) |
176 |
182 |
177 # shows up in diff |
183 # shows up in diff |
178 self.assertEqual({b'foo': ((want, f), (h, b''))}, m.diff(clean)) |
184 self.assertEqual({b'foo': ((want, f), (h, b''))}, m.diff(clean)) |
179 self.assertEqual({b'foo': ((h, b''), (want, f))}, clean.diff(m)) |
185 self.assertEqual({b'foo': ((h, b''), (want, f))}, clean.diff(m)) |
180 |
186 |
181 def testMatchException(self): |
187 def testMatchException(self): |
182 m = self.parsemanifest(A_SHORT_MANIFEST) |
188 m = self.parsemanifest(A_SHORT_MANIFEST) |
183 match = matchmod.match(b'', b'', [b're:.*']) |
189 match = matchmod.match(b'', b'', [b're:.*']) |
|
190 |
184 def filt(path): |
191 def filt(path): |
185 if path == b'foo': |
192 if path == b'foo': |
186 assert False |
193 assert False |
187 return True |
194 return True |
|
195 |
188 match.matchfn = filt |
196 match.matchfn = filt |
189 with self.assertRaises(AssertionError): |
197 with self.assertRaises(AssertionError): |
190 m.matches(match) |
198 m.matches(match) |
191 |
199 |
192 def testRemoveItem(self): |
200 def testRemoveItem(self): |
204 def testManifestDiff(self): |
212 def testManifestDiff(self): |
205 MISSING = (None, b'') |
213 MISSING = (None, b'') |
206 addl = b'z-only-in-left\0' + HASH_1 + b'\n' |
214 addl = b'z-only-in-left\0' + HASH_1 + b'\n' |
207 addr = b'z-only-in-right\0' + HASH_2 + b'x\n' |
215 addr = b'z-only-in-right\0' + HASH_2 + b'x\n' |
208 left = self.parsemanifest( |
216 left = self.parsemanifest( |
209 A_SHORT_MANIFEST.replace(HASH_1, HASH_3 + b'x') + addl) |
217 A_SHORT_MANIFEST.replace(HASH_1, HASH_3 + b'x') + addl |
|
218 ) |
210 right = self.parsemanifest(A_SHORT_MANIFEST + addr) |
219 right = self.parsemanifest(A_SHORT_MANIFEST + addr) |
211 want = { |
220 want = { |
212 b'foo': ((BIN_HASH_3, b'x'), |
221 b'foo': ((BIN_HASH_3, b'x'), (BIN_HASH_1, b'')), |
213 (BIN_HASH_1, b'')), |
|
214 b'z-only-in-left': ((BIN_HASH_1, b''), MISSING), |
222 b'z-only-in-left': ((BIN_HASH_1, b''), MISSING), |
215 b'z-only-in-right': (MISSING, (BIN_HASH_2, b'x')), |
223 b'z-only-in-right': (MISSING, (BIN_HASH_2, b'x')), |
216 } |
224 } |
217 self.assertEqual(want, left.diff(right)) |
225 self.assertEqual(want, left.diff(right)) |
218 |
226 |
219 want = { |
227 want = { |
220 b'bar/baz/qux.py': (MISSING, (BIN_HASH_2, b'l')), |
228 b'bar/baz/qux.py': (MISSING, (BIN_HASH_2, b'l')), |
221 b'foo': (MISSING, (BIN_HASH_3, b'x')), |
229 b'foo': (MISSING, (BIN_HASH_3, b'x')), |
222 b'z-only-in-left': (MISSING, (BIN_HASH_1, b'')), |
230 b'z-only-in-left': (MISSING, (BIN_HASH_1, b'')), |
223 } |
231 } |
224 self.assertEqual(want, self.parsemanifest(EMTPY_MANIFEST).diff(left)) |
232 self.assertEqual(want, self.parsemanifest(EMTPY_MANIFEST).diff(left)) |
225 |
233 |
226 want = { |
234 want = { |
227 b'bar/baz/qux.py': ((BIN_HASH_2, b'l'), MISSING), |
235 b'bar/baz/qux.py': ((BIN_HASH_2, b'l'), MISSING), |
228 b'foo': ((BIN_HASH_3, b'x'), MISSING), |
236 b'foo': ((BIN_HASH_3, b'x'), MISSING), |
229 b'z-only-in-left': ((BIN_HASH_1, b''), MISSING), |
237 b'z-only-in-left': ((BIN_HASH_1, b''), MISSING), |
230 } |
238 } |
231 self.assertEqual(want, left.diff(self.parsemanifest(EMTPY_MANIFEST))) |
239 self.assertEqual(want, left.diff(self.parsemanifest(EMTPY_MANIFEST))) |
232 copy = right.copy() |
240 copy = right.copy() |
233 del copy[b'z-only-in-right'] |
241 del copy[b'z-only-in-right'] |
234 del right[b'foo'] |
242 del right[b'foo'] |
235 want = { |
243 want = { |
236 b'foo': (MISSING, (BIN_HASH_1, b'')), |
244 b'foo': (MISSING, (BIN_HASH_1, b'')), |
237 b'z-only-in-right': ((BIN_HASH_2, b'x'), MISSING), |
245 b'z-only-in-right': ((BIN_HASH_2, b'x'), MISSING), |
238 } |
246 } |
239 self.assertEqual(want, right.diff(copy)) |
247 self.assertEqual(want, right.diff(copy)) |
240 |
248 |
241 short = self.parsemanifest(A_SHORT_MANIFEST) |
249 short = self.parsemanifest(A_SHORT_MANIFEST) |
242 pruned = short.copy() |
250 pruned = short.copy() |
243 del pruned[b'foo'] |
251 del pruned[b'foo'] |
244 want = { |
252 want = { |
245 b'foo': ((BIN_HASH_1, b''), MISSING), |
253 b'foo': ((BIN_HASH_1, b''), MISSING), |
246 } |
254 } |
247 self.assertEqual(want, short.diff(pruned)) |
255 self.assertEqual(want, short.diff(pruned)) |
248 want = { |
256 want = { |
249 b'foo': (MISSING, (BIN_HASH_1, b'')), |
257 b'foo': (MISSING, (BIN_HASH_1, b'')), |
250 } |
258 } |
251 self.assertEqual(want, pruned.diff(short)) |
259 self.assertEqual(want, pruned.diff(short)) |
252 want = { |
260 want = { |
253 b'bar/baz/qux.py': None, |
261 b'bar/baz/qux.py': None, |
254 b'foo': (MISSING, (BIN_HASH_1, b'')), |
262 b'foo': (MISSING, (BIN_HASH_1, b'')), |
255 } |
263 } |
256 self.assertEqual(want, pruned.diff(short, clean=True)) |
264 self.assertEqual(want, pruned.diff(short, clean=True)) |
257 |
265 |
258 def testReversedLines(self): |
266 def testReversedLines(self): |
259 backwards = b''.join( |
267 backwards = b''.join( |
260 l + b'\n' for l in reversed(A_SHORT_MANIFEST.split(b'\n')) if l) |
268 l + b'\n' for l in reversed(A_SHORT_MANIFEST.split(b'\n')) if l |
|
269 ) |
261 try: |
270 try: |
262 self.parsemanifest(backwards) |
271 self.parsemanifest(backwards) |
263 self.fail('Should have raised ValueError') |
272 self.fail('Should have raised ValueError') |
264 except ValueError as v: |
273 except ValueError as v: |
265 self.assertIn('Manifest lines not in sorted order.', str(v)) |
274 self.assertIn('Manifest lines not in sorted order.', str(v)) |
290 m = self.parsemanifest(A_HUGE_MANIFEST) |
299 m = self.parsemanifest(A_HUGE_MANIFEST) |
291 |
300 |
292 match = matchmod.exact([b'file1', b'file200', b'file300']) |
301 match = matchmod.exact([b'file1', b'file200', b'file300']) |
293 m2 = m.matches(match) |
302 m2 = m.matches(match) |
294 |
303 |
295 w = (b'file1\0%sx\n' |
304 w = (b'file1\0%sx\n' b'file200\0%sl\n' b'file300\0%s\n') % ( |
296 b'file200\0%sl\n' |
305 HASH_2, |
297 b'file300\0%s\n') % (HASH_2, HASH_1, HASH_1) |
306 HASH_1, |
|
307 HASH_1, |
|
308 ) |
298 self.assertEqual(w, m2.text()) |
309 self.assertEqual(w, m2.text()) |
299 |
310 |
300 def testMatchesNonexistentFile(self): |
311 def testMatchesNonexistentFile(self): |
301 '''Tests matches() for a small set of specific files, including one |
312 '''Tests matches() for a small set of specific files, including one |
302 nonexistent file to make sure in only matches against existing files. |
313 nonexistent file to make sure in only matches against existing files. |
303 ''' |
314 ''' |
304 m = self.parsemanifest(A_DEEPER_MANIFEST) |
315 m = self.parsemanifest(A_DEEPER_MANIFEST) |
305 |
316 |
306 match = matchmod.exact([b'a/b/c/bar.txt', b'a/b/d/qux.py', |
317 match = matchmod.exact( |
307 b'readme.txt', b'nonexistent']) |
318 [b'a/b/c/bar.txt', b'a/b/d/qux.py', b'readme.txt', b'nonexistent'] |
308 m2 = m.matches(match) |
319 ) |
309 |
320 m2 = m.matches(match) |
310 self.assertEqual( |
321 |
311 [b'a/b/c/bar.txt', b'a/b/d/qux.py', b'readme.txt'], |
322 self.assertEqual( |
312 m2.keys()) |
323 [b'a/b/c/bar.txt', b'a/b/d/qux.py', b'readme.txt'], m2.keys() |
|
324 ) |
313 |
325 |
314 def testMatchesNonexistentDirectory(self): |
326 def testMatchesNonexistentDirectory(self): |
315 '''Tests matches() for a relpath match on a directory that doesn't |
327 '''Tests matches() for a relpath match on a directory that doesn't |
316 actually exist.''' |
328 actually exist.''' |
317 m = self.parsemanifest(A_DEEPER_MANIFEST) |
329 m = self.parsemanifest(A_DEEPER_MANIFEST) |
372 m = self.parsemanifest(A_DEEPER_MANIFEST) |
393 m = self.parsemanifest(A_DEEPER_MANIFEST) |
373 |
394 |
374 match = matchmod.match(b'/', b'a/b', [b'.'], default=b'relpath') |
395 match = matchmod.match(b'/', b'a/b', [b'.'], default=b'relpath') |
375 m2 = m.matches(match) |
396 m2 = m.matches(match) |
376 |
397 |
377 self.assertEqual([ |
398 self.assertEqual( |
378 b'a/b/c/bar.py', b'a/b/c/bar.txt', b'a/b/c/foo.py', |
399 [ |
379 b'a/b/c/foo.txt', b'a/b/d/baz.py', b'a/b/d/qux.py', |
400 b'a/b/c/bar.py', |
380 b'a/b/d/ten.txt', b'a/b/dog.py', b'a/b/fish.py'], m2.keys()) |
401 b'a/b/c/bar.txt', |
|
402 b'a/b/c/foo.py', |
|
403 b'a/b/c/foo.txt', |
|
404 b'a/b/d/baz.py', |
|
405 b'a/b/d/qux.py', |
|
406 b'a/b/d/ten.txt', |
|
407 b'a/b/dog.py', |
|
408 b'a/b/fish.py', |
|
409 ], |
|
410 m2.keys(), |
|
411 ) |
381 |
412 |
382 def testMatchesWithPattern(self): |
413 def testMatchesWithPattern(self): |
383 '''Tests matches() for files matching a pattern that reside |
414 '''Tests matches() for files matching a pattern that reside |
384 deeper than the specified directory.''' |
415 deeper than the specified directory.''' |
385 m = self.parsemanifest(A_DEEPER_MANIFEST) |
416 m = self.parsemanifest(A_DEEPER_MANIFEST) |
386 |
417 |
387 match = matchmod.match(b'/', b'', [b'a/b/*/*.txt']) |
418 match = matchmod.match(b'/', b'', [b'a/b/*/*.txt']) |
388 m2 = m.matches(match) |
419 m2 = m.matches(match) |
389 |
420 |
390 self.assertEqual( |
421 self.assertEqual( |
391 [b'a/b/c/bar.txt', b'a/b/c/foo.txt', b'a/b/d/ten.txt'], |
422 [b'a/b/c/bar.txt', b'a/b/c/foo.txt', b'a/b/d/ten.txt'], m2.keys() |
392 m2.keys()) |
423 ) |
|
424 |
393 |
425 |
394 class testmanifestdict(unittest.TestCase, basemanifesttests): |
426 class testmanifestdict(unittest.TestCase, basemanifesttests): |
395 def parsemanifest(self, text): |
427 def parsemanifest(self, text): |
396 return manifestmod.manifestdict(text) |
428 return manifestmod.manifestdict(text) |
397 |
429 |
412 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
444 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
413 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
445 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
414 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
446 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
415 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
447 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
416 b'\x00\x00\xc0\x8aey\x1d}\x01\xd8\xe0\xb9\xf3\xde\x1b\xcf\x17' |
448 b'\x00\x00\xc0\x8aey\x1d}\x01\xd8\xe0\xb9\xf3\xde\x1b\xcf\x17' |
417 b'\xac\xbe') |
449 b'\xac\xbe' |
|
450 ) |
418 with self.assertRaises(ValueError): |
451 with self.assertRaises(ValueError): |
419 self.parsemanifest(data) |
452 self.parsemanifest(data) |
|
453 |
420 |
454 |
421 class testtreemanifest(unittest.TestCase, basemanifesttests): |
455 class testtreemanifest(unittest.TestCase, basemanifesttests): |
422 def parsemanifest(self, text): |
456 def parsemanifest(self, text): |
423 return manifestmod.treemanifest(b'', text) |
457 return manifestmod.treemanifest(b'', text) |
424 |
458 |
425 def testWalkSubtrees(self): |
459 def testWalkSubtrees(self): |
426 m = self.parsemanifest(A_DEEPER_MANIFEST) |
460 m = self.parsemanifest(A_DEEPER_MANIFEST) |
427 |
461 |
428 dirs = [s._dir for s in m.walksubtrees()] |
462 dirs = [s._dir for s in m.walksubtrees()] |
429 self.assertEqual( |
463 self.assertEqual( |
430 sorted([ |
464 sorted( |
431 b'', b'a/', b'a/c/', b'a/d/', b'a/b/', b'a/b/c/', b'a/b/d/']), |
465 [b'', b'a/', b'a/c/', b'a/d/', b'a/b/', b'a/b/c/', b'a/b/d/'] |
432 sorted(dirs) |
466 ), |
|
467 sorted(dirs), |
433 ) |
468 ) |
434 |
469 |
435 match = matchmod.match(b'/', b'', [b'path:a/b/']) |
470 match = matchmod.match(b'/', b'', [b'path:a/b/']) |
436 dirs = [s._dir for s in m.walksubtrees(matcher=match)] |
471 dirs = [s._dir for s in m.walksubtrees(matcher=match)] |
437 self.assertEqual( |
472 self.assertEqual(sorted([b'a/b/', b'a/b/c/', b'a/b/d/']), sorted(dirs)) |
438 sorted([b'a/b/', b'a/b/c/', b'a/b/d/']), |
473 |
439 sorted(dirs) |
|
440 ) |
|
441 |
474 |
442 if __name__ == '__main__': |
475 if __name__ == '__main__': |
443 silenttestrunner.main(__name__) |
476 silenttestrunner.main(__name__) |