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