gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / dulwich / tests / test_diff_tree.py @ 959
History | View | Annotate | Download (40.2 KB)
1 |
# test_diff_tree.py -- Tests for file and tree diff utilities.
|
---|---|
2 |
# Copyright (C) 2010 Google, Inc.
|
3 |
#
|
4 |
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
|
5 |
# General Public License as public by the Free Software Foundation; version 2.0
|
6 |
# or (at your option) any later version. You can redistribute it and/or
|
7 |
# modify it under the terms of either of these two licenses.
|
8 |
#
|
9 |
# Unless required by applicable law or agreed to in writing, software
|
10 |
# distributed under the License is distributed on an "AS IS" BASIS,
|
11 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 |
# See the License for the specific language governing permissions and
|
13 |
# limitations under the License.
|
14 |
#
|
15 |
# You should have received a copy of the licenses; if not, see
|
16 |
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
|
17 |
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
|
18 |
# License, Version 2.0.
|
19 |
#
|
20 |
|
21 |
"""Tests for file and tree diff utilities."""
|
22 |
|
23 |
from itertools import permutations |
24 |
from dulwich.diff_tree import ( |
25 |
CHANGE_MODIFY, |
26 |
CHANGE_RENAME, |
27 |
CHANGE_COPY, |
28 |
CHANGE_UNCHANGED, |
29 |
TreeChange, |
30 |
_merge_entries, |
31 |
_merge_entries_py, |
32 |
tree_changes, |
33 |
tree_changes_for_merge, |
34 |
_count_blocks, |
35 |
_count_blocks_py, |
36 |
_similarity_score, |
37 |
_tree_change_key, |
38 |
RenameDetector, |
39 |
_is_tree, |
40 |
_is_tree_py |
41 |
) |
42 |
from dulwich.index import ( |
43 |
commit_tree, |
44 |
) |
45 |
from dulwich.object_store import ( |
46 |
MemoryObjectStore, |
47 |
) |
48 |
from dulwich.objects import ( |
49 |
ShaFile, |
50 |
Blob, |
51 |
TreeEntry, |
52 |
Tree, |
53 |
) |
54 |
from dulwich.tests import ( |
55 |
TestCase, |
56 |
) |
57 |
from dulwich.tests.utils import ( |
58 |
F, |
59 |
make_object, |
60 |
functest_builder, |
61 |
ext_functest_builder, |
62 |
) |
63 |
|
64 |
|
65 |
class DiffTestCase(TestCase): |
66 |
|
67 |
def setUp(self): |
68 |
super(DiffTestCase, self).setUp() |
69 |
self.store = MemoryObjectStore()
|
70 |
self.empty_tree = self.commit_tree([]) |
71 |
|
72 |
def commit_tree(self, entries): |
73 |
commit_blobs = [] |
74 |
for entry in entries: |
75 |
if len(entry) == 2: |
76 |
path, obj = entry |
77 |
mode = F |
78 |
else:
|
79 |
path, obj, mode = entry |
80 |
if isinstance(obj, Blob): |
81 |
self.store.add_object(obj)
|
82 |
sha = obj.id |
83 |
else:
|
84 |
sha = obj |
85 |
commit_blobs.append((path, sha, mode)) |
86 |
return self.store[commit_tree(self.store, commit_blobs)] |
87 |
|
88 |
|
89 |
class TreeChangesTest(DiffTestCase): |
90 |
|
91 |
def setUp(self): |
92 |
super(TreeChangesTest, self).setUp() |
93 |
self.detector = RenameDetector(self.store) |
94 |
|
95 |
def assertMergeFails(self, merge_entries, name, mode, sha): |
96 |
t = Tree() |
97 |
t[name] = (mode, sha) |
98 |
self.assertRaises((TypeError, ValueError), merge_entries, '', t, t) |
99 |
|
100 |
def _do_test_merge_entries(self, merge_entries): |
101 |
blob_a1 = make_object(Blob, data=b'a1')
|
102 |
blob_a2 = make_object(Blob, data=b'a2')
|
103 |
blob_b1 = make_object(Blob, data=b'b1')
|
104 |
blob_c2 = make_object(Blob, data=b'c2')
|
105 |
tree1 = self.commit_tree([(b'a', blob_a1, 0o100644), |
106 |
(b'b', blob_b1, 0o100755)]) |
107 |
tree2 = self.commit_tree([(b'a', blob_a2, 0o100644), |
108 |
(b'c', blob_c2, 0o100755)]) |
109 |
|
110 |
self.assertEqual([], merge_entries(b'', self.empty_tree, |
111 |
self.empty_tree))
|
112 |
self.assertEqual(
|
113 |
[((None, None, None), (b'a', 0o100644, blob_a1.id)), |
114 |
((None, None, None), (b'b', 0o100755, blob_b1.id)), ], |
115 |
merge_entries(b'', self.empty_tree, tree1)) |
116 |
self.assertEqual(
|
117 |
[((None, None, None), (b'x/a', 0o100644, blob_a1.id)), |
118 |
((None, None, None), (b'x/b', 0o100755, blob_b1.id)), ], |
119 |
merge_entries(b'x', self.empty_tree, tree1)) |
120 |
|
121 |
self.assertEqual(
|
122 |
[((b'a', 0o100644, blob_a2.id), (None, None, None)), |
123 |
((b'c', 0o100755, blob_c2.id), (None, None, None)), ], |
124 |
merge_entries(b'', tree2, self.empty_tree)) |
125 |
|
126 |
self.assertEqual(
|
127 |
[((b'a', 0o100644, blob_a1.id), (b'a', 0o100644, blob_a2.id)), |
128 |
((b'b', 0o100755, blob_b1.id), (None, None, None)), |
129 |
((None, None, None), (b'c', 0o100755, blob_c2.id)), ], |
130 |
merge_entries(b'', tree1, tree2))
|
131 |
|
132 |
self.assertEqual(
|
133 |
[((b'a', 0o100644, blob_a2.id), (b'a', 0o100644, blob_a1.id)), |
134 |
((None, None, None), (b'b', 0o100755, blob_b1.id)), |
135 |
((b'c', 0o100755, blob_c2.id), (None, None, None)), ], |
136 |
merge_entries(b'', tree2, tree1))
|
137 |
|
138 |
self.assertMergeFails(merge_entries, 0xdeadbeef, 0o100644, '1' * 40) |
139 |
self.assertMergeFails(merge_entries, b'a', b'deadbeef', '1' * 40) |
140 |
self.assertMergeFails(merge_entries, b'a', 0o100644, 0xdeadbeef) |
141 |
|
142 |
test_merge_entries = functest_builder(_do_test_merge_entries, |
143 |
_merge_entries_py) |
144 |
test_merge_entries_extension = ext_functest_builder(_do_test_merge_entries, |
145 |
_merge_entries) |
146 |
|
147 |
def _do_test_is_tree(self, is_tree): |
148 |
self.assertFalse(is_tree(TreeEntry(None, None, None))) |
149 |
self.assertFalse(is_tree(TreeEntry(b'a', 0o100644, b'a' * 40))) |
150 |
self.assertFalse(is_tree(TreeEntry(b'a', 0o100755, b'a' * 40))) |
151 |
self.assertFalse(is_tree(TreeEntry(b'a', 0o120000, b'a' * 40))) |
152 |
self.assertTrue(is_tree(TreeEntry(b'a', 0o040000, b'a' * 40))) |
153 |
self.assertRaises(TypeError, is_tree, TreeEntry(b'a', b'x', b'a' * 40)) |
154 |
self.assertRaises(AttributeError, is_tree, 1234) |
155 |
|
156 |
test_is_tree = functest_builder(_do_test_is_tree, _is_tree_py) |
157 |
test_is_tree_extension = ext_functest_builder(_do_test_is_tree, _is_tree) |
158 |
|
159 |
def assertChangesEqual(self, expected, tree1, tree2, **kwargs): |
160 |
actual = list(tree_changes(self.store, tree1.id, tree2.id, **kwargs)) |
161 |
self.assertEqual(expected, actual)
|
162 |
|
163 |
# For brevity, the following tests use tuples instead of TreeEntry objects.
|
164 |
|
165 |
def test_tree_changes_empty(self): |
166 |
self.assertChangesEqual([], self.empty_tree, self.empty_tree) |
167 |
|
168 |
def test_tree_changes_no_changes(self): |
169 |
blob = make_object(Blob, data=b'blob')
|
170 |
tree = self.commit_tree([(b'a', blob), (b'b/c', blob)]) |
171 |
self.assertChangesEqual([], self.empty_tree, self.empty_tree) |
172 |
self.assertChangesEqual([], tree, tree)
|
173 |
self.assertChangesEqual(
|
174 |
[TreeChange(CHANGE_UNCHANGED, (b'a', F, blob.id), (b'a', F, blob.id)), |
175 |
TreeChange(CHANGE_UNCHANGED, (b'b/c', F, blob.id),
|
176 |
(b'b/c', F, blob.id))],
|
177 |
tree, tree, want_unchanged=True)
|
178 |
|
179 |
def test_tree_changes_add_delete(self): |
180 |
blob_a = make_object(Blob, data=b'a')
|
181 |
blob_b = make_object(Blob, data=b'b')
|
182 |
tree = self.commit_tree([(b'a', blob_a, 0o100644), |
183 |
(b'x/b', blob_b, 0o100755)]) |
184 |
self.assertChangesEqual(
|
185 |
[TreeChange.add((b'a', 0o100644, blob_a.id)), |
186 |
TreeChange.add((b'x/b', 0o100755, blob_b.id))], |
187 |
self.empty_tree, tree)
|
188 |
self.assertChangesEqual(
|
189 |
[TreeChange.delete((b'a', 0o100644, blob_a.id)), |
190 |
TreeChange.delete((b'x/b', 0o100755, blob_b.id))], |
191 |
tree, self.empty_tree)
|
192 |
|
193 |
def test_tree_changes_modify_contents(self): |
194 |
blob_a1 = make_object(Blob, data=b'a1')
|
195 |
blob_a2 = make_object(Blob, data=b'a2')
|
196 |
tree1 = self.commit_tree([(b'a', blob_a1)]) |
197 |
tree2 = self.commit_tree([(b'a', blob_a2)]) |
198 |
self.assertChangesEqual(
|
199 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id),
|
200 |
(b'a', F, blob_a2.id))],
|
201 |
tree1, tree2) |
202 |
|
203 |
def test_tree_changes_modify_mode(self): |
204 |
blob_a = make_object(Blob, data=b'a')
|
205 |
tree1 = self.commit_tree([(b'a', blob_a, 0o100644)]) |
206 |
tree2 = self.commit_tree([(b'a', blob_a, 0o100755)]) |
207 |
self.assertChangesEqual(
|
208 |
[TreeChange(CHANGE_MODIFY, (b'a', 0o100644, blob_a.id), |
209 |
(b'a', 0o100755, blob_a.id))], |
210 |
tree1, tree2) |
211 |
|
212 |
def test_tree_changes_change_type(self): |
213 |
blob_a1 = make_object(Blob, data=b'a')
|
214 |
blob_a2 = make_object(Blob, data=b'/foo/bar')
|
215 |
tree1 = self.commit_tree([(b'a', blob_a1, 0o100644)]) |
216 |
tree2 = self.commit_tree([(b'a', blob_a2, 0o120000)]) |
217 |
self.assertChangesEqual(
|
218 |
[TreeChange.delete((b'a', 0o100644, blob_a1.id)), |
219 |
TreeChange.add((b'a', 0o120000, blob_a2.id))], |
220 |
tree1, tree2) |
221 |
|
222 |
def test_tree_changes_to_tree(self): |
223 |
blob_a = make_object(Blob, data=b'a')
|
224 |
blob_x = make_object(Blob, data=b'x')
|
225 |
tree1 = self.commit_tree([(b'a', blob_a)]) |
226 |
tree2 = self.commit_tree([(b'a/x', blob_x)]) |
227 |
self.assertChangesEqual(
|
228 |
[TreeChange.delete((b'a', F, blob_a.id)),
|
229 |
TreeChange.add((b'a/x', F, blob_x.id))],
|
230 |
tree1, tree2) |
231 |
|
232 |
def test_tree_changes_complex(self): |
233 |
blob_a_1 = make_object(Blob, data=b'a1_1')
|
234 |
blob_bx1_1 = make_object(Blob, data=b'bx1_1')
|
235 |
blob_bx2_1 = make_object(Blob, data=b'bx2_1')
|
236 |
blob_by1_1 = make_object(Blob, data=b'by1_1')
|
237 |
blob_by2_1 = make_object(Blob, data=b'by2_1')
|
238 |
tree1 = self.commit_tree([
|
239 |
(b'a', blob_a_1),
|
240 |
(b'b/x/1', blob_bx1_1),
|
241 |
(b'b/x/2', blob_bx2_1),
|
242 |
(b'b/y/1', blob_by1_1),
|
243 |
(b'b/y/2', blob_by2_1),
|
244 |
]) |
245 |
|
246 |
blob_a_2 = make_object(Blob, data=b'a1_2')
|
247 |
blob_bx1_2 = blob_bx1_1 |
248 |
blob_by_2 = make_object(Blob, data=b'by_2')
|
249 |
blob_c_2 = make_object(Blob, data=b'c_2')
|
250 |
tree2 = self.commit_tree([
|
251 |
(b'a', blob_a_2),
|
252 |
(b'b/x/1', blob_bx1_2),
|
253 |
(b'b/y', blob_by_2),
|
254 |
(b'c', blob_c_2),
|
255 |
]) |
256 |
|
257 |
self.assertChangesEqual(
|
258 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob_a_1.id),
|
259 |
(b'a', F, blob_a_2.id)),
|
260 |
TreeChange.delete((b'b/x/2', F, blob_bx2_1.id)),
|
261 |
TreeChange.add((b'b/y', F, blob_by_2.id)),
|
262 |
TreeChange.delete((b'b/y/1', F, blob_by1_1.id)),
|
263 |
TreeChange.delete((b'b/y/2', F, blob_by2_1.id)),
|
264 |
TreeChange.add((b'c', F, blob_c_2.id))],
|
265 |
tree1, tree2) |
266 |
|
267 |
def test_tree_changes_name_order(self): |
268 |
blob = make_object(Blob, data=b'a')
|
269 |
tree1 = self.commit_tree([(b'a', blob), (b'a.', blob), (b'a..', blob)]) |
270 |
# Tree order is the reverse of this, so if we used tree order, 'a..'
|
271 |
# would not be merged.
|
272 |
tree2 = self.commit_tree([(b'a/x', blob), (b'a./x', blob), (b'a..', blob)]) |
273 |
|
274 |
self.assertChangesEqual(
|
275 |
[TreeChange.delete((b'a', F, blob.id)),
|
276 |
TreeChange.add((b'a/x', F, blob.id)),
|
277 |
TreeChange.delete((b'a.', F, blob.id)),
|
278 |
TreeChange.add((b'a./x', F, blob.id))],
|
279 |
tree1, tree2) |
280 |
|
281 |
def test_tree_changes_prune(self): |
282 |
blob_a1 = make_object(Blob, data=b'a1')
|
283 |
blob_a2 = make_object(Blob, data=b'a2')
|
284 |
blob_x = make_object(Blob, data=b'x')
|
285 |
tree1 = self.commit_tree([(b'a', blob_a1), (b'b/x', blob_x)]) |
286 |
tree2 = self.commit_tree([(b'a', blob_a2), (b'b/x', blob_x)]) |
287 |
# Remove identical items so lookups will fail unless we prune.
|
288 |
subtree = self.store[tree1[b'b'][1]] |
289 |
for entry in subtree.items(): |
290 |
del self.store[entry.sha] |
291 |
del self.store[subtree.id] |
292 |
|
293 |
self.assertChangesEqual(
|
294 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id),
|
295 |
(b'a', F, blob_a2.id))],
|
296 |
tree1, tree2) |
297 |
|
298 |
def test_tree_changes_rename_detector(self): |
299 |
blob_a1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
300 |
blob_a2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
301 |
blob_b = make_object(Blob, data=b'b')
|
302 |
tree1 = self.commit_tree([(b'a', blob_a1), (b'b', blob_b)]) |
303 |
tree2 = self.commit_tree([(b'c', blob_a2), (b'b', blob_b)]) |
304 |
detector = RenameDetector(self.store)
|
305 |
|
306 |
self.assertChangesEqual(
|
307 |
[TreeChange.delete((b'a', F, blob_a1.id)),
|
308 |
TreeChange.add((b'c', F, blob_a2.id))],
|
309 |
tree1, tree2) |
310 |
self.assertChangesEqual(
|
311 |
[TreeChange.delete((b'a', F, blob_a1.id)),
|
312 |
TreeChange(CHANGE_UNCHANGED, (b'b', F, blob_b.id),
|
313 |
(b'b', F, blob_b.id)),
|
314 |
TreeChange.add((b'c', F, blob_a2.id))],
|
315 |
tree1, tree2, want_unchanged=True)
|
316 |
self.assertChangesEqual(
|
317 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id),
|
318 |
(b'c', F, blob_a2.id))],
|
319 |
tree1, tree2, rename_detector=detector) |
320 |
self.assertChangesEqual(
|
321 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id),
|
322 |
(b'c', F, blob_a2.id)),
|
323 |
TreeChange(CHANGE_UNCHANGED, (b'b', F, blob_b.id),
|
324 |
(b'b', F, blob_b.id))],
|
325 |
tree1, tree2, rename_detector=detector, want_unchanged=True)
|
326 |
|
327 |
def assertChangesForMergeEqual(self, expected, parent_trees, merge_tree, |
328 |
**kwargs): |
329 |
parent_tree_ids = [t.id for t in parent_trees] |
330 |
actual = list(tree_changes_for_merge(
|
331 |
self.store, parent_tree_ids, merge_tree.id, **kwargs))
|
332 |
self.assertEqual(expected, actual)
|
333 |
|
334 |
parent_tree_ids.reverse() |
335 |
expected = [list(reversed(cs)) for cs in expected] |
336 |
actual = list(tree_changes_for_merge(
|
337 |
self.store, parent_tree_ids, merge_tree.id, **kwargs))
|
338 |
self.assertEqual(expected, actual)
|
339 |
|
340 |
def test_tree_changes_for_merge_add_no_conflict(self): |
341 |
blob = make_object(Blob, data=b'blob')
|
342 |
parent1 = self.commit_tree([])
|
343 |
parent2 = merge = self.commit_tree([(b'a', blob)]) |
344 |
self.assertChangesForMergeEqual([], [parent1, parent2], merge)
|
345 |
self.assertChangesForMergeEqual([], [parent2, parent2], merge)
|
346 |
|
347 |
def test_tree_changes_for_merge_add_modify_conflict(self): |
348 |
blob1 = make_object(Blob, data=b'1')
|
349 |
blob2 = make_object(Blob, data=b'2')
|
350 |
parent1 = self.commit_tree([])
|
351 |
parent2 = self.commit_tree([(b'a', blob1)]) |
352 |
merge = self.commit_tree([(b'a', blob2)]) |
353 |
self.assertChangesForMergeEqual(
|
354 |
[[TreeChange.add((b'a', F, blob2.id)),
|
355 |
TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob2.id))]], |
356 |
[parent1, parent2], merge) |
357 |
|
358 |
def test_tree_changes_for_merge_modify_modify_conflict(self): |
359 |
blob1 = make_object(Blob, data=b'1')
|
360 |
blob2 = make_object(Blob, data=b'2')
|
361 |
blob3 = make_object(Blob, data=b'3')
|
362 |
parent1 = self.commit_tree([(b'a', blob1)]) |
363 |
parent2 = self.commit_tree([(b'a', blob2)]) |
364 |
merge = self.commit_tree([(b'a', blob3)]) |
365 |
self.assertChangesForMergeEqual(
|
366 |
[[TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob3.id)), |
367 |
TreeChange(CHANGE_MODIFY, (b'a', F, blob2.id), (b'a', F, blob3.id))]], |
368 |
[parent1, parent2], merge) |
369 |
|
370 |
def test_tree_changes_for_merge_modify_no_conflict(self): |
371 |
blob1 = make_object(Blob, data=b'1')
|
372 |
blob2 = make_object(Blob, data=b'2')
|
373 |
parent1 = self.commit_tree([(b'a', blob1)]) |
374 |
parent2 = merge = self.commit_tree([(b'a', blob2)]) |
375 |
self.assertChangesForMergeEqual([], [parent1, parent2], merge)
|
376 |
|
377 |
def test_tree_changes_for_merge_delete_delete_conflict(self): |
378 |
blob1 = make_object(Blob, data=b'1')
|
379 |
blob2 = make_object(Blob, data=b'2')
|
380 |
parent1 = self.commit_tree([(b'a', blob1)]) |
381 |
parent2 = self.commit_tree([(b'a', blob2)]) |
382 |
merge = self.commit_tree([])
|
383 |
self.assertChangesForMergeEqual(
|
384 |
[[TreeChange.delete((b'a', F, blob1.id)),
|
385 |
TreeChange.delete((b'a', F, blob2.id))]],
|
386 |
[parent1, parent2], merge) |
387 |
|
388 |
def test_tree_changes_for_merge_delete_no_conflict(self): |
389 |
blob = make_object(Blob, data=b'blob')
|
390 |
has = self.commit_tree([(b'a', blob)]) |
391 |
doesnt_have = self.commit_tree([])
|
392 |
self.assertChangesForMergeEqual([], [has, has], doesnt_have)
|
393 |
self.assertChangesForMergeEqual([], [has, doesnt_have], doesnt_have)
|
394 |
|
395 |
def test_tree_changes_for_merge_octopus_no_conflict(self): |
396 |
r = list(range(5)) |
397 |
blobs = [make_object(Blob, data=bytes(i)) for i in r] |
398 |
parents = [self.commit_tree([(b'a', blobs[i])]) for i in r] |
399 |
for i in r: |
400 |
# Take the SHA from each of the parents.
|
401 |
self.assertChangesForMergeEqual([], parents, parents[i])
|
402 |
|
403 |
def test_tree_changes_for_merge_octopus_modify_conflict(self): |
404 |
# Because the octopus merge strategy is limited, I doubt it's possible
|
405 |
# to create this with the git command line. But the output is well-
|
406 |
# defined, so test it anyway.
|
407 |
r = list(range(5)) |
408 |
parent_blobs = [make_object(Blob, data=bytes(i)) for i in r] |
409 |
merge_blob = make_object(Blob, data=b'merge')
|
410 |
parents = [self.commit_tree([(b'a', parent_blobs[i])]) for i in r] |
411 |
merge = self.commit_tree([(b'a', merge_blob)]) |
412 |
expected = [[TreeChange(CHANGE_MODIFY, (b'a', F, parent_blobs[i].id),
|
413 |
(b'a', F, merge_blob.id)) for i in r]] |
414 |
self.assertChangesForMergeEqual(expected, parents, merge)
|
415 |
|
416 |
def test_tree_changes_for_merge_octopus_delete(self): |
417 |
blob1 = make_object(Blob, data=b'1')
|
418 |
blob2 = make_object(Blob, data=b'3')
|
419 |
parent1 = self.commit_tree([(b'a', blob1)]) |
420 |
parent2 = self.commit_tree([(b'a', blob2)]) |
421 |
parent3 = merge = self.commit_tree([])
|
422 |
self.assertChangesForMergeEqual([], [parent1, parent1, parent1], merge)
|
423 |
self.assertChangesForMergeEqual([], [parent1, parent1, parent3], merge)
|
424 |
self.assertChangesForMergeEqual([], [parent1, parent3, parent3], merge)
|
425 |
self.assertChangesForMergeEqual(
|
426 |
[[TreeChange.delete((b'a', F, blob1.id)),
|
427 |
TreeChange.delete((b'a', F, blob2.id)),
|
428 |
None]],
|
429 |
[parent1, parent2, parent3], merge) |
430 |
|
431 |
def test_tree_changes_for_merge_add_add_same_conflict(self): |
432 |
blob = make_object(Blob, data=b'a\nb\nc\nd\n')
|
433 |
parent1 = self.commit_tree([(b'a', blob)]) |
434 |
parent2 = self.commit_tree([])
|
435 |
merge = self.commit_tree([(b'b', blob)]) |
436 |
add = TreeChange.add((b'b', F, blob.id))
|
437 |
self.assertChangesForMergeEqual([[add, add]], [parent1, parent2], merge)
|
438 |
|
439 |
def test_tree_changes_for_merge_add_exact_rename_conflict(self): |
440 |
blob = make_object(Blob, data=b'a\nb\nc\nd\n')
|
441 |
parent1 = self.commit_tree([(b'a', blob)]) |
442 |
parent2 = self.commit_tree([])
|
443 |
merge = self.commit_tree([(b'b', blob)]) |
444 |
self.assertChangesForMergeEqual(
|
445 |
[[TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'b', F, blob.id)), |
446 |
TreeChange.add((b'b', F, blob.id))]],
|
447 |
[parent1, parent2], merge, rename_detector=self.detector)
|
448 |
|
449 |
def test_tree_changes_for_merge_add_content_rename_conflict(self): |
450 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
451 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
452 |
parent1 = self.commit_tree([(b'a', blob1)]) |
453 |
parent2 = self.commit_tree([])
|
454 |
merge = self.commit_tree([(b'b', blob2)]) |
455 |
self.assertChangesForMergeEqual(
|
456 |
[[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id)), |
457 |
TreeChange.add((b'b', F, blob2.id))]],
|
458 |
[parent1, parent2], merge, rename_detector=self.detector)
|
459 |
|
460 |
def test_tree_changes_for_merge_modify_rename_conflict(self): |
461 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
462 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
463 |
parent1 = self.commit_tree([(b'a', blob1)]) |
464 |
parent2 = self.commit_tree([(b'b', blob1)]) |
465 |
merge = self.commit_tree([(b'b', blob2)]) |
466 |
self.assertChangesForMergeEqual(
|
467 |
[[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id)), |
468 |
TreeChange(CHANGE_MODIFY, (b'b', F, blob1.id), (b'b', F, blob2.id))]], |
469 |
[parent1, parent2], merge, rename_detector=self.detector)
|
470 |
|
471 |
|
472 |
class RenameDetectionTest(DiffTestCase): |
473 |
|
474 |
def _do_test_count_blocks(self, count_blocks): |
475 |
blob = make_object(Blob, data=b'a\nb\na\n')
|
476 |
self.assertEqual({hash(b'a\n'): 4, hash(b'b\n'): 2}, count_blocks(blob)) |
477 |
|
478 |
test_count_blocks = functest_builder(_do_test_count_blocks, |
479 |
_count_blocks_py) |
480 |
test_count_blocks_extension = ext_functest_builder(_do_test_count_blocks, |
481 |
_count_blocks) |
482 |
|
483 |
def _do_test_count_blocks_no_newline(self, count_blocks): |
484 |
blob = make_object(Blob, data=b'a\na')
|
485 |
self.assertEqual({hash(b'a\n'): 2, hash(b'a'): 1}, _count_blocks(blob)) |
486 |
|
487 |
test_count_blocks_no_newline = functest_builder( |
488 |
_do_test_count_blocks_no_newline, _count_blocks_py) |
489 |
test_count_blocks_no_newline_extension = ext_functest_builder( |
490 |
_do_test_count_blocks_no_newline, _count_blocks) |
491 |
|
492 |
def _do_test_count_blocks_chunks(self, count_blocks): |
493 |
blob = ShaFile.from_raw_chunks(Blob.type_num, [b'a\nb', b'\na\n']) |
494 |
self.assertEqual({hash(b'a\n'): 4, hash(b'b\n'): 2}, _count_blocks(blob)) |
495 |
|
496 |
test_count_blocks_chunks = functest_builder(_do_test_count_blocks_chunks, |
497 |
_count_blocks_py) |
498 |
test_count_blocks_chunks_extension = ext_functest_builder( |
499 |
_do_test_count_blocks_chunks, _count_blocks) |
500 |
|
501 |
def _do_test_count_blocks_long_lines(self, count_blocks): |
502 |
a = b'a' * 64 |
503 |
data = a + b'xxx\ny\n' + a + b'zzz\n' |
504 |
blob = make_object(Blob, data=data) |
505 |
self.assertEqual({hash(b'a' * 64): 128, hash(b'xxx\n'): 4, hash(b'y\n'): 2, |
506 |
hash(b'zzz\n'): 4}, |
507 |
_count_blocks(blob)) |
508 |
|
509 |
test_count_blocks_long_lines = functest_builder( |
510 |
_do_test_count_blocks_long_lines, _count_blocks_py) |
511 |
test_count_blocks_long_lines_extension = ext_functest_builder( |
512 |
_do_test_count_blocks_long_lines, _count_blocks) |
513 |
|
514 |
def assertSimilar(self, expected_score, blob1, blob2): |
515 |
self.assertEqual(expected_score, _similarity_score(blob1, blob2))
|
516 |
self.assertEqual(expected_score, _similarity_score(blob2, blob1))
|
517 |
|
518 |
def test_similarity_score(self): |
519 |
blob0 = make_object(Blob, data=b'')
|
520 |
blob1 = make_object(Blob, data=b'ab\ncd\ncd\n')
|
521 |
blob2 = make_object(Blob, data=b'ab\n')
|
522 |
blob3 = make_object(Blob, data=b'cd\n')
|
523 |
blob4 = make_object(Blob, data=b'cd\ncd\n')
|
524 |
|
525 |
self.assertSimilar(100, blob0, blob0) |
526 |
self.assertSimilar(0, blob0, blob1) |
527 |
self.assertSimilar(33, blob1, blob2) |
528 |
self.assertSimilar(33, blob1, blob3) |
529 |
self.assertSimilar(66, blob1, blob4) |
530 |
self.assertSimilar(0, blob2, blob3) |
531 |
self.assertSimilar(50, blob3, blob4) |
532 |
|
533 |
def test_similarity_score_cache(self): |
534 |
blob1 = make_object(Blob, data=b'ab\ncd\n')
|
535 |
blob2 = make_object(Blob, data=b'ab\n')
|
536 |
|
537 |
block_cache = {} |
538 |
self.assertEqual(
|
539 |
50, _similarity_score(blob1, blob2, block_cache=block_cache))
|
540 |
self.assertEqual(set([blob1.id, blob2.id]), set(block_cache)) |
541 |
|
542 |
def fail_chunks(): |
543 |
self.fail('Unexpected call to as_raw_chunks()') |
544 |
|
545 |
blob1.as_raw_chunks = blob2.as_raw_chunks = fail_chunks |
546 |
blob1.raw_length = lambda: 6 |
547 |
blob2.raw_length = lambda: 3 |
548 |
self.assertEqual(
|
549 |
50, _similarity_score(blob1, blob2, block_cache=block_cache))
|
550 |
|
551 |
def test_tree_entry_sort(self): |
552 |
sha = 'abcd' * 10 |
553 |
expected_entries = [ |
554 |
TreeChange.add(TreeEntry(b'aaa', F, sha)),
|
555 |
TreeChange(CHANGE_COPY, TreeEntry(b'bbb', F, sha),
|
556 |
TreeEntry(b'aab', F, sha)),
|
557 |
TreeChange(CHANGE_MODIFY, TreeEntry(b'bbb', F, sha),
|
558 |
TreeEntry(b'bbb', F, b'dabc' * 10)), |
559 |
TreeChange(CHANGE_RENAME, TreeEntry(b'bbc', F, sha),
|
560 |
TreeEntry(b'ddd', F, sha)),
|
561 |
TreeChange.delete(TreeEntry(b'ccc', F, sha)),
|
562 |
] |
563 |
|
564 |
for perm in permutations(expected_entries): |
565 |
self.assertEqual(expected_entries,
|
566 |
sorted(perm, key=_tree_change_key))
|
567 |
|
568 |
def detect_renames(self, tree1, tree2, want_unchanged=False, **kwargs): |
569 |
detector = RenameDetector(self.store, **kwargs)
|
570 |
return detector.changes_with_renames(tree1.id, tree2.id,
|
571 |
want_unchanged=want_unchanged) |
572 |
|
573 |
def test_no_renames(self): |
574 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
575 |
blob2 = make_object(Blob, data=b'a\nb\ne\nf\n')
|
576 |
blob3 = make_object(Blob, data=b'a\nb\ng\nh\n')
|
577 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
578 |
tree2 = self.commit_tree([(b'a', blob1), (b'b', blob3)]) |
579 |
self.assertEqual(
|
580 |
[TreeChange(CHANGE_MODIFY, (b'b', F, blob2.id), (b'b', F, blob3.id))], |
581 |
self.detect_renames(tree1, tree2))
|
582 |
|
583 |
def test_exact_rename_one_to_one(self): |
584 |
blob1 = make_object(Blob, data=b'1')
|
585 |
blob2 = make_object(Blob, data=b'2')
|
586 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
587 |
tree2 = self.commit_tree([(b'c', blob1), (b'd', blob2)]) |
588 |
self.assertEqual(
|
589 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob1.id)), |
590 |
TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'd', F, blob2.id))], |
591 |
self.detect_renames(tree1, tree2))
|
592 |
|
593 |
def test_exact_rename_split_different_type(self): |
594 |
blob = make_object(Blob, data=b'/foo')
|
595 |
tree1 = self.commit_tree([(b'a', blob, 0o100644)]) |
596 |
tree2 = self.commit_tree([(b'a', blob, 0o120000)]) |
597 |
self.assertEqual(
|
598 |
[TreeChange.add((b'a', 0o120000, blob.id)), |
599 |
TreeChange.delete((b'a', 0o100644, blob.id))], |
600 |
self.detect_renames(tree1, tree2))
|
601 |
|
602 |
def test_exact_rename_and_different_type(self): |
603 |
blob1 = make_object(Blob, data=b'1')
|
604 |
blob2 = make_object(Blob, data=b'2')
|
605 |
tree1 = self.commit_tree([(b'a', blob1)]) |
606 |
tree2 = self.commit_tree([(b'a', blob2, 0o120000), (b'b', blob1)]) |
607 |
self.assertEqual(
|
608 |
[TreeChange.add((b'a', 0o120000, blob2.id)), |
609 |
TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob1.id))], |
610 |
self.detect_renames(tree1, tree2))
|
611 |
|
612 |
def test_exact_rename_one_to_many(self): |
613 |
blob = make_object(Blob, data=b'1')
|
614 |
tree1 = self.commit_tree([(b'a', blob)]) |
615 |
tree2 = self.commit_tree([(b'b', blob), (b'c', blob)]) |
616 |
self.assertEqual(
|
617 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'b', F, blob.id)), |
618 |
TreeChange(CHANGE_COPY, (b'a', F, blob.id), (b'c', F, blob.id))], |
619 |
self.detect_renames(tree1, tree2))
|
620 |
|
621 |
def test_exact_rename_many_to_one(self): |
622 |
blob = make_object(Blob, data=b'1')
|
623 |
tree1 = self.commit_tree([(b'a', blob), (b'b', blob)]) |
624 |
tree2 = self.commit_tree([(b'c', blob)]) |
625 |
self.assertEqual(
|
626 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'c', F, blob.id)), |
627 |
TreeChange.delete((b'b', F, blob.id))],
|
628 |
self.detect_renames(tree1, tree2))
|
629 |
|
630 |
def test_exact_rename_many_to_many(self): |
631 |
blob = make_object(Blob, data=b'1')
|
632 |
tree1 = self.commit_tree([(b'a', blob), (b'b', blob)]) |
633 |
tree2 = self.commit_tree([(b'c', blob), (b'd', blob), (b'e', blob)]) |
634 |
self.assertEqual(
|
635 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'c', F, blob.id)), |
636 |
TreeChange(CHANGE_COPY, (b'a', F, blob.id), (b'e', F, blob.id)), |
637 |
TreeChange(CHANGE_RENAME, (b'b', F, blob.id), (b'd', F, blob.id))], |
638 |
self.detect_renames(tree1, tree2))
|
639 |
|
640 |
def test_exact_copy_modify(self): |
641 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
642 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
643 |
tree1 = self.commit_tree([(b'a', blob1)]) |
644 |
tree2 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) |
645 |
self.assertEqual(
|
646 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob2.id)), |
647 |
TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob1.id))], |
648 |
self.detect_renames(tree1, tree2))
|
649 |
|
650 |
def test_exact_copy_change_mode(self): |
651 |
blob = make_object(Blob, data=b'a\nb\nc\nd\n')
|
652 |
tree1 = self.commit_tree([(b'a', blob)]) |
653 |
tree2 = self.commit_tree([(b'a', blob, 0o100755), (b'b', blob)]) |
654 |
self.assertEqual(
|
655 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob.id),
|
656 |
(b'a', 0o100755, blob.id)), |
657 |
TreeChange(CHANGE_COPY, (b'a', F, blob.id), (b'b', F, blob.id))], |
658 |
self.detect_renames(tree1, tree2))
|
659 |
|
660 |
def test_rename_threshold(self): |
661 |
blob1 = make_object(Blob, data=b'a\nb\nc\n')
|
662 |
blob2 = make_object(Blob, data=b'a\nb\nd\n')
|
663 |
tree1 = self.commit_tree([(b'a', blob1)]) |
664 |
tree2 = self.commit_tree([(b'b', blob2)]) |
665 |
self.assertEqual(
|
666 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id))], |
667 |
self.detect_renames(tree1, tree2, rename_threshold=50)) |
668 |
self.assertEqual(
|
669 |
[TreeChange.delete((b'a', F, blob1.id)),
|
670 |
TreeChange.add((b'b', F, blob2.id))],
|
671 |
self.detect_renames(tree1, tree2, rename_threshold=75)) |
672 |
|
673 |
def test_content_rename_max_files(self): |
674 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd')
|
675 |
blob4 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
676 |
blob2 = make_object(Blob, data=b'e\nf\ng\nh\n')
|
677 |
blob3 = make_object(Blob, data=b'e\nf\ng\ni\n')
|
678 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
679 |
tree2 = self.commit_tree([(b'c', blob3), (b'd', blob4)]) |
680 |
self.assertEqual(
|
681 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'd', F, blob4.id)), |
682 |
TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'c', F, blob3.id))], |
683 |
self.detect_renames(tree1, tree2))
|
684 |
self.assertEqual(
|
685 |
[TreeChange.delete((b'a', F, blob1.id)),
|
686 |
TreeChange.delete((b'b', F, blob2.id)),
|
687 |
TreeChange.add((b'c', F, blob3.id)),
|
688 |
TreeChange.add((b'd', F, blob4.id))],
|
689 |
self.detect_renames(tree1, tree2, max_files=1)) |
690 |
|
691 |
def test_content_rename_one_to_one(self): |
692 |
b11 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
693 |
b12 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
694 |
b21 = make_object(Blob, data=b'e\nf\ng\n\h')
|
695 |
b22 = make_object(Blob, data=b'e\nf\ng\n\i')
|
696 |
tree1 = self.commit_tree([(b'a', b11), (b'b', b21)]) |
697 |
tree2 = self.commit_tree([(b'c', b12), (b'd', b22)]) |
698 |
self.assertEqual(
|
699 |
[TreeChange(CHANGE_RENAME, (b'a', F, b11.id), (b'c', F, b12.id)), |
700 |
TreeChange(CHANGE_RENAME, (b'b', F, b21.id), (b'd', F, b22.id))], |
701 |
self.detect_renames(tree1, tree2))
|
702 |
|
703 |
def test_content_rename_one_to_one_ordering(self): |
704 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\ne\nf\n')
|
705 |
blob2 = make_object(Blob, data=b'a\nb\nc\nd\ng\nh\n')
|
706 |
# 6/10 match to blob1, 8/10 match to blob2
|
707 |
blob3 = make_object(Blob, data=b'a\nb\nc\nd\ng\ni\n')
|
708 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
709 |
tree2 = self.commit_tree([(b'c', blob3)]) |
710 |
self.assertEqual(
|
711 |
[TreeChange.delete((b'a', F, blob1.id)),
|
712 |
TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'c', F, blob3.id))], |
713 |
self.detect_renames(tree1, tree2))
|
714 |
|
715 |
tree3 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) |
716 |
tree4 = self.commit_tree([(b'c', blob3)]) |
717 |
self.assertEqual(
|
718 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob2.id), (b'c', F, blob3.id)), |
719 |
TreeChange.delete((b'b', F, blob1.id))],
|
720 |
self.detect_renames(tree3, tree4))
|
721 |
|
722 |
def test_content_rename_one_to_many(self): |
723 |
blob1 = make_object(Blob, data=b'aa\nb\nc\nd\ne\n')
|
724 |
blob2 = make_object(Blob, data=b'ab\nb\nc\nd\ne\n') # 8/11 match |
725 |
blob3 = make_object(Blob, data=b'aa\nb\nc\nd\nf\n') # 9/11 match |
726 |
tree1 = self.commit_tree([(b'a', blob1)]) |
727 |
tree2 = self.commit_tree([(b'b', blob2), (b'c', blob3)]) |
728 |
self.assertEqual(
|
729 |
[TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob2.id)), |
730 |
TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id))], |
731 |
self.detect_renames(tree1, tree2))
|
732 |
|
733 |
def test_content_rename_many_to_one(self): |
734 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
735 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
736 |
blob3 = make_object(Blob, data=b'a\nb\nc\nf\n')
|
737 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
738 |
tree2 = self.commit_tree([(b'c', blob3)]) |
739 |
self.assertEqual(
|
740 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id)), |
741 |
TreeChange.delete((b'b', F, blob2.id))],
|
742 |
self.detect_renames(tree1, tree2))
|
743 |
|
744 |
def test_content_rename_many_to_many(self): |
745 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
746 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
747 |
blob3 = make_object(Blob, data=b'a\nb\nc\nf\n')
|
748 |
blob4 = make_object(Blob, data=b'a\nb\nc\ng\n')
|
749 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
750 |
tree2 = self.commit_tree([(b'c', blob3), (b'd', blob4)]) |
751 |
# TODO(dborowitz): Distribute renames rather than greedily choosing
|
752 |
# copies.
|
753 |
self.assertEqual(
|
754 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id)), |
755 |
TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'd', F, blob4.id)), |
756 |
TreeChange.delete((b'b', F, blob2.id))],
|
757 |
self.detect_renames(tree1, tree2))
|
758 |
|
759 |
def test_content_rename_with_more_deletions(self): |
760 |
blob1 = make_object(Blob, data=b'')
|
761 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob1), (b'c', blob1), |
762 |
(b'd', blob1)])
|
763 |
tree2 = self.commit_tree([(b'e', blob1), (b'f', blob1), (b'g', blob1)]) |
764 |
self.maxDiff = None |
765 |
self.assertEqual(
|
766 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'e', F, blob1.id)), |
767 |
TreeChange(CHANGE_RENAME, (b'b', F, blob1.id), (b'f', F, blob1.id)), |
768 |
TreeChange(CHANGE_RENAME, (b'c', F, blob1.id), (b'g', F, blob1.id)), |
769 |
TreeChange.delete((b'd', F, blob1.id))],
|
770 |
self.detect_renames(tree1, tree2))
|
771 |
|
772 |
def test_content_rename_gitlink(self): |
773 |
blob1 = make_object(Blob, data=b'blob1')
|
774 |
blob2 = make_object(Blob, data=b'blob2')
|
775 |
link1 = b'1' * 40 |
776 |
link2 = b'2' * 40 |
777 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', link1, 0o160000)]) |
778 |
tree2 = self.commit_tree([(b'c', blob2), (b'd', link2, 0o160000)]) |
779 |
self.assertEqual(
|
780 |
[TreeChange.delete((b'a', 0o100644, blob1.id)), |
781 |
TreeChange.delete((b'b', 0o160000, link1)), |
782 |
TreeChange.add((b'c', 0o100644, blob2.id)), |
783 |
TreeChange.add((b'd', 0o160000, link2))], |
784 |
self.detect_renames(tree1, tree2))
|
785 |
|
786 |
def test_exact_rename_swap(self): |
787 |
blob1 = make_object(Blob, data=b'1')
|
788 |
blob2 = make_object(Blob, data=b'2')
|
789 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
790 |
tree2 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) |
791 |
self.assertEqual(
|
792 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob2.id)), |
793 |
TreeChange(CHANGE_MODIFY, (b'b', F, blob2.id), (b'b', F, blob1.id))], |
794 |
self.detect_renames(tree1, tree2))
|
795 |
self.assertEqual(
|
796 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob1.id)), |
797 |
TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'a', F, blob2.id))], |
798 |
self.detect_renames(tree1, tree2, rewrite_threshold=50)) |
799 |
|
800 |
def test_content_rename_swap(self): |
801 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
802 |
blob2 = make_object(Blob, data=b'e\nf\ng\nh\n')
|
803 |
blob3 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
804 |
blob4 = make_object(Blob, data=b'e\nf\ng\ni\n')
|
805 |
tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
806 |
tree2 = self.commit_tree([(b'a', blob4), (b'b', blob3)]) |
807 |
self.assertEqual(
|
808 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob3.id)), |
809 |
TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'a', F, blob4.id))], |
810 |
self.detect_renames(tree1, tree2, rewrite_threshold=60)) |
811 |
|
812 |
def test_rewrite_threshold(self): |
813 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
814 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
815 |
blob3 = make_object(Blob, data=b'a\nb\nf\ng\n')
|
816 |
|
817 |
tree1 = self.commit_tree([(b'a', blob1)]) |
818 |
tree2 = self.commit_tree([(b'a', blob3), (b'b', blob2)]) |
819 |
|
820 |
no_renames = [ |
821 |
TreeChange(CHANGE_MODIFY, (b'a', F, blob1.id), (b'a', F, blob3.id)), |
822 |
TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob2.id))] |
823 |
self.assertEqual(
|
824 |
no_renames, self.detect_renames(tree1, tree2))
|
825 |
self.assertEqual(
|
826 |
no_renames, self.detect_renames(tree1, tree2, rewrite_threshold=40)) |
827 |
self.assertEqual(
|
828 |
[TreeChange.add((b'a', F, blob3.id)),
|
829 |
TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id))], |
830 |
self.detect_renames(tree1, tree2, rewrite_threshold=80)) |
831 |
|
832 |
def test_find_copies_harder_exact(self): |
833 |
blob = make_object(Blob, data=b'blob')
|
834 |
tree1 = self.commit_tree([(b'a', blob)]) |
835 |
tree2 = self.commit_tree([(b'a', blob), (b'b', blob)]) |
836 |
self.assertEqual([TreeChange.add((b'b', F, blob.id))], |
837 |
self.detect_renames(tree1, tree2))
|
838 |
self.assertEqual(
|
839 |
[TreeChange(CHANGE_COPY, (b'a', F, blob.id), (b'b', F, blob.id))], |
840 |
self.detect_renames(tree1, tree2, find_copies_harder=True)) |
841 |
|
842 |
def test_find_copies_harder_content(self): |
843 |
blob1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
844 |
blob2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
845 |
tree1 = self.commit_tree([(b'a', blob1)]) |
846 |
tree2 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) |
847 |
self.assertEqual([TreeChange.add((b'b', F, blob2.id))], |
848 |
self.detect_renames(tree1, tree2))
|
849 |
self.assertEqual(
|
850 |
[TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'b', F, blob2.id))], |
851 |
self.detect_renames(tree1, tree2, find_copies_harder=True)) |
852 |
|
853 |
def test_find_copies_harder_with_rewrites(self): |
854 |
blob_a1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
855 |
blob_a2 = make_object(Blob, data=b'f\ng\nh\ni\n')
|
856 |
blob_b2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
857 |
tree1 = self.commit_tree([(b'a', blob_a1)]) |
858 |
tree2 = self.commit_tree([(b'a', blob_a2), (b'b', blob_b2)]) |
859 |
self.assertEqual(
|
860 |
[TreeChange(CHANGE_MODIFY, (b'a', F, blob_a1.id),
|
861 |
(b'a', F, blob_a2.id)),
|
862 |
TreeChange(CHANGE_COPY, (b'a', F, blob_a1.id), (b'b', F, blob_b2.id))], |
863 |
self.detect_renames(tree1, tree2, find_copies_harder=True)) |
864 |
self.assertEqual(
|
865 |
[TreeChange.add((b'a', F, blob_a2.id)),
|
866 |
TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id),
|
867 |
(b'b', F, blob_b2.id))],
|
868 |
self.detect_renames(tree1, tree2, rewrite_threshold=50, |
869 |
find_copies_harder=True))
|
870 |
|
871 |
def test_reuse_detector(self): |
872 |
blob = make_object(Blob, data=b'blob')
|
873 |
tree1 = self.commit_tree([(b'a', blob)]) |
874 |
tree2 = self.commit_tree([(b'b', blob)]) |
875 |
detector = RenameDetector(self.store)
|
876 |
changes = [TreeChange(CHANGE_RENAME, (b'a', F, blob.id),
|
877 |
(b'b', F, blob.id))]
|
878 |
self.assertEqual(changes,
|
879 |
detector.changes_with_renames(tree1.id, tree2.id)) |
880 |
self.assertEqual(changes,
|
881 |
detector.changes_with_renames(tree1.id, tree2.id)) |
882 |
|
883 |
def test_want_unchanged(self): |
884 |
blob_a1 = make_object(Blob, data=b'a\nb\nc\nd\n')
|
885 |
blob_b = make_object(Blob, data=b'b')
|
886 |
blob_c2 = make_object(Blob, data=b'a\nb\nc\ne\n')
|
887 |
tree1 = self.commit_tree([(b'a', blob_a1), (b'b', blob_b)]) |
888 |
tree2 = self.commit_tree([(b'c', blob_c2), (b'b', blob_b)]) |
889 |
self.assertEqual(
|
890 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id),
|
891 |
(b'c', F, blob_c2.id))],
|
892 |
self.detect_renames(tree1, tree2))
|
893 |
self.assertEqual(
|
894 |
[TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id),
|
895 |
(b'c', F, blob_c2.id)),
|
896 |
TreeChange(CHANGE_UNCHANGED, (b'b', F, blob_b.id),
|
897 |
(b'b', F, blob_b.id))],
|
898 |
self.detect_renames(tree1, tree2, want_unchanged=True)) |