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