35
from bzrlib.symbol_versioning import deprecated_in
36
from bzrlib.tests import test_win32utils
39
class _AttribFeature(tests.Feature):
42
if (sys.platform not in ('cygwin', 'win32')):
45
proc = subprocess.Popen(['attrib', '.'], stdout=subprocess.PIPE)
48
return (0 == proc.wait())
50
def feature_name(self):
51
return 'attrib Windows command-line tool'
53
AttribFeature = _AttribFeature()
56
compiled_patiencediff_feature = tests.ModuleAvailableFeature(
57
'bzrlib._patiencediff_c')
33
from ..sixish import (
40
from ..tests.blackbox.test_diff import subst_dates
41
from ..tests.scenarios import load_tests_apply_scenarios
44
load_tests = load_tests_apply_scenarios
60
47
def udiff_lines(old, new, allow_binary=False):
62
49
diff.internal_diff('old', old, 'new', new, output, allow_binary)
64
51
return output.readlines()
71
"""Simple file-like object that allows writes with any type and records."""
74
self.write_record = []
76
def write(self, data):
77
self.write_record.append(data)
79
def check_types(self, testcase, expected_type):
81
any(not isinstance(o, expected_type) for o in self.write_record),
82
"Not all writes of type %s: %r" % (
83
expected_type.__name__, self.write_record))
86
class TestDiffOptions(tests.TestCase):
88
def test_unified_added(self):
89
"""Check for default style '-u' only if no other style specified
92
# Verify that style defaults to unified, id est '-u' appended
93
# to option list, in the absence of an alternative style.
94
self.assertEqual(['-a', '-u'], diff.default_style_unified(['-a']))
97
class TestDiffOptionsScenarios(tests.TestCase):
99
scenarios = [(s, dict(style=s)) for s in diff.style_option_list]
100
style = None # Set by load_tests_apply_scenarios from scenarios
102
def test_unified_not_added(self):
103
# Verify that for all valid style options, '-u' is not
104
# appended to option list.
105
ret_opts = diff.default_style_unified(diff_opts=["%s" % (self.style,)])
106
self.assertEqual(["%s" % (self.style,)], ret_opts)
83
109
class TestDiff(tests.TestCase):
85
111
def test_add_nl(self):
86
112
"""diff generates a valid diff for patches that add a newline"""
87
113
lines = udiff_lines(['boo'], ['boo\n'])
88
114
self.check_patch(lines)
89
self.assertEquals(lines[4], '\\ No newline at end of file\n')
115
self.assertEqual(lines[4], '\\ No newline at end of file\n')
90
116
## "expected no-nl, got %r" % lines[4]
92
118
def test_add_nl_2(self):
105
131
lines = udiff_lines(['boo\n'], ['boo'])
106
132
self.check_patch(lines)
107
self.assertEquals(lines[5], '\\ No newline at end of file\n')
133
self.assertEqual(lines[5], '\\ No newline at end of file\n')
108
134
## "expected no-nl, got %r" % lines[5]
110
136
def check_patch(self, lines):
111
self.assert_(len(lines) > 1)
137
self.assertTrue(len(lines) > 1)
112
138
## "Not enough lines for a file header for patch:\n%s" % "".join(lines)
113
self.assert_(lines[0].startswith ('---'))
139
self.assertTrue(lines[0].startswith ('---'))
114
140
## 'No orig line for patch:\n%s' % "".join(lines)
115
self.assert_(lines[1].startswith ('+++'))
141
self.assertTrue(lines[1].startswith ('+++'))
116
142
## 'No mod line for patch:\n%s' % "".join(lines)
117
self.assert_(len(lines) > 2)
143
self.assertTrue(len(lines) > 2)
118
144
## "No hunks for patch:\n%s" % "".join(lines)
119
self.assert_(lines[2].startswith('@@'))
145
self.assertTrue(lines[2].startswith('@@'))
120
146
## "No hunk header for patch:\n%s" % "".join(lines)
121
self.assert_('@@' in lines[2][2:])
147
self.assertTrue('@@' in lines[2][2:])
122
148
## "Unterminated hunk header for patch:\n%s" % "".join(lines)
124
150
def test_binary_lines(self):
126
152
uni_lines = [1023 * 'a' + '\x00']
127
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines , empty)
153
self.assertRaises(errors.BinaryFile, udiff_lines, uni_lines, empty)
128
154
self.assertRaises(errors.BinaryFile, udiff_lines, empty, uni_lines)
129
udiff_lines(uni_lines , empty, allow_binary=True)
155
udiff_lines(uni_lines, empty, allow_binary=True)
130
156
udiff_lines(empty, uni_lines, allow_binary=True)
132
158
def test_external_diff(self):
143
169
self.check_patch(lines)
145
171
def test_external_diff_binary_lang_c(self):
147
172
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
148
old_env[lang] = osutils.set_or_unset_env(lang, 'C')
150
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
151
# Older versions of diffutils say "Binary files", newer
152
# versions just say "Files".
153
self.assertContainsRe(lines[0],
154
'(Binary f|F)iles old and new differ\n')
155
self.assertEquals(lines[1:], ['\n'])
157
for lang, old_val in old_env.iteritems():
158
osutils.set_or_unset_env(lang, old_val)
173
self.overrideEnv(lang, 'C')
174
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
175
# Older versions of diffutils say "Binary files", newer
176
# versions just say "Files".
177
self.assertContainsRe(lines[0], '(Binary f|F)iles old and new differ\n')
178
self.assertEqual(lines[1:], ['\n'])
160
180
def test_no_external_diff(self):
161
181
"""Check that NoDiff is raised when diff is not available"""
162
# Use os.environ['PATH'] to make sure no 'diff' command is available
163
orig_path = os.environ['PATH']
165
os.environ['PATH'] = ''
166
self.assertRaises(errors.NoDiff, diff.external_diff,
167
'old', ['boo\n'], 'new', ['goo\n'],
168
StringIO(), diff_opts=['-u'])
170
os.environ['PATH'] = orig_path
182
# Make sure no 'diff' command is available
183
# XXX: Weird, using None instead of '' breaks the test -- vila 20101216
184
self.overrideEnv('PATH', '')
185
self.assertRaises(errors.NoDiff, diff.external_diff,
186
'old', ['boo\n'], 'new', ['goo\n'],
187
BytesIO(), diff_opts=['-u'])
172
189
def test_internal_diff_default(self):
173
190
# Default internal diff encoding is utf8
175
192
diff.internal_diff(u'old_\xb5', ['old_text\n'],
176
193
u'new_\xe5', ['new_text\n'], output)
177
194
lines = output.getvalue().splitlines(True)
178
195
self.check_patch(lines)
179
self.assertEquals(['--- old_\xc2\xb5\n',
196
self.assertEqual(['--- old_\xc2\xb5\n',
180
197
'+++ new_\xc3\xa5\n',
181
198
'@@ -1,1 +1,1 @@\n',
220
237
def test_internal_diff_no_content(self):
222
239
diff.internal_diff(u'old', [], u'new', [], output)
223
240
self.assertEqual('', output.getvalue())
225
242
def test_internal_diff_no_changes(self):
227
244
diff.internal_diff(u'old', ['text\n', 'contents\n'],
228
245
u'new', ['text\n', 'contents\n'],
230
247
self.assertEqual('', output.getvalue())
232
249
def test_internal_diff_returns_bytes(self):
234
output = StringIO.StringIO()
235
251
diff.internal_diff(u'old_\xb5', ['old_text\n'],
236
252
u'new_\xe5', ['new_text\n'], output)
237
self.failUnless(isinstance(output.getvalue(), str),
238
'internal_diff should return bytestrings')
253
output.check_types(self, bytes)
255
def test_internal_diff_default_context(self):
257
diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
258
'same_text\n', 'same_text\n', 'old_text\n'],
259
'new', ['same_text\n', 'same_text\n', 'same_text\n',
260
'same_text\n', 'same_text\n', 'new_text\n'], output)
261
lines = output.getvalue().splitlines(True)
262
self.check_patch(lines)
263
self.assertEqual(['--- old\n',
275
def test_internal_diff_no_context(self):
277
diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
278
'same_text\n', 'same_text\n', 'old_text\n'],
279
'new', ['same_text\n', 'same_text\n', 'same_text\n',
280
'same_text\n', 'same_text\n', 'new_text\n'], output,
282
lines = output.getvalue().splitlines(True)
283
self.check_patch(lines)
284
self.assertEqual(['--- old\n',
293
def test_internal_diff_more_context(self):
295
diff.internal_diff('old', ['same_text\n', 'same_text\n', 'same_text\n',
296
'same_text\n', 'same_text\n', 'old_text\n'],
297
'new', ['same_text\n', 'same_text\n', 'same_text\n',
298
'same_text\n', 'same_text\n', 'new_text\n'], output,
300
lines = output.getvalue().splitlines(True)
301
self.check_patch(lines)
302
self.assertEqual(['--- old\n',
241
319
class TestDiffFiles(tests.TestCaseInTempDir):
243
321
def test_external_diff_binary(self):
244
322
"""The output when using external diff should use diff's i18n error"""
323
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
324
self.overrideEnv(lang, 'C')
245
325
# Make sure external_diff doesn't fail in the current LANG
246
326
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
248
328
cmd = ['diff', '-u', '--binary', 'old', 'new']
249
open('old', 'wb').write('\x00foobar\n')
250
open('new', 'wb').write('foo\x00bar\n')
329
with open('old', 'wb') as f: f.write('\x00foobar\n')
330
with open('new', 'wb') as f: f.write('foo\x00bar\n')
251
331
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252
332
stdin=subprocess.PIPE)
253
333
out, err = pipe.communicate()
254
# Diff returns '2' on Binary files.
255
self.assertEqual(2, pipe.returncode)
256
334
# We should output whatever diff tells us, plus a trailing newline
257
335
self.assertEqual(out.splitlines(True) + ['\n'], lines)
260
class TestShowDiffTreesHelper(tests.TestCaseWithTransport):
261
"""Has a helper for running show_diff_trees"""
263
def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
265
if working_tree is not None:
266
extra_trees = (working_tree,)
269
diff.show_diff_trees(tree1, tree2, output,
270
specific_files=specific_files,
271
extra_trees=extra_trees, old_label='old/',
273
return output.getvalue()
276
class TestDiffDates(TestShowDiffTreesHelper):
338
def get_diff_as_string(tree1, tree2, specific_files=None, working_tree=None):
340
if working_tree is not None:
341
extra_trees = (working_tree,)
344
diff.show_diff_trees(tree1, tree2, output,
345
specific_files=specific_files,
346
extra_trees=extra_trees, old_label='old/',
348
return output.getvalue()
351
class TestDiffDates(tests.TestCaseWithTransport):
279
354
super(TestDiffDates, self).setUp()
394
469
self.wt.rename_one('file1', 'dir1/file1')
395
470
old_tree = self.b.repository.revision_tree('rev-1')
396
471
new_tree = self.b.repository.revision_tree('rev-4')
397
out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
472
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir1'],
398
473
working_tree=self.wt)
399
474
self.assertContainsRe(out, 'file1\t')
400
out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
475
out = get_diff_as_string(old_tree, new_tree, specific_files=['dir2'],
401
476
working_tree=self.wt)
402
477
self.assertNotContainsRe(out, 'file1\t')
406
class TestShowDiffTrees(TestShowDiffTreesHelper):
480
class TestShowDiffTrees(tests.TestCaseWithTransport):
407
481
"""Direct tests for show_diff_trees"""
409
483
def test_modified_file(self):
508
582
tree.rename_one('c', 'new-c')
509
583
tree.rename_one('d', 'new-d')
511
d = self.get_diff(tree.basis_tree(), tree)
585
d = get_diff_as_string(tree.basis_tree(), tree)
513
587
self.assertContainsRe(d, r"file 'a'.*\(properties changed:"
515
589
self.assertContainsRe(d, r"file 'b'.*\(properties changed:"
517
591
self.assertContainsRe(d, r"file 'c'.*\(properties changed:"
519
593
self.assertContainsRe(d, r"file 'd'.*\(properties changed:"
521
595
self.assertNotContainsRe(d, r"file 'e'")
522
596
self.assertNotContainsRe(d, r"file 'f'")
525
598
def test_binary_unicode_filenames(self):
526
599
"""Test that contents of files are *not* encoded in UTF-8 when there
527
600
is a binary file in the diff.
529
602
# See https://bugs.launchpad.net/bugs/110092.
530
self.requireFeature(tests.UnicodeFilenameFeature)
603
self.requireFeature(features.UnicodeFilenameFeature)
532
# This bug isn't triggered with cStringIO.
533
from StringIO import StringIO
534
605
tree = self.make_branch_and_tree('tree')
535
606
alpha, omega = u'\u03b1', u'\u03c9'
536
607
alpha_utf8, omega_utf8 = alpha.encode('utf8'), omega.encode('utf8')
573
645
tree.add(['add_'+alpha], ['file-id'])
574
646
self.build_tree_contents([('tree/mod_'+alpha, 'contents_mod\n')])
576
d = self.get_diff(tree.basis_tree(), tree)
648
d = get_diff_as_string(tree.basis_tree(), tree)
577
649
self.assertContainsRe(d,
578
650
"=== renamed file 'ren_%s' => 'ren_%s'\n"%(autf8, outf8))
579
651
self.assertContainsRe(d, "=== added file 'add_%s'"%autf8)
580
652
self.assertContainsRe(d, "=== modified file 'mod_%s'"%autf8)
581
653
self.assertContainsRe(d, "=== removed file 'del_%s'"%autf8)
655
def test_unicode_filename_path_encoding(self):
656
"""Test for bug #382699: unicode filenames on Windows should be shown
659
self.requireFeature(features.UnicodeFilenameFeature)
660
# The word 'test' in Russian
661
_russian_test = u'\u0422\u0435\u0441\u0442'
662
directory = _russian_test + u'/'
663
test_txt = _russian_test + u'.txt'
664
u1234 = u'\u1234.txt'
666
tree = self.make_branch_and_tree('.')
667
self.build_tree_contents([
672
tree.add([test_txt, u1234, directory])
675
diff.show_diff_trees(tree.basis_tree(), tree, sio,
676
path_encoding='cp1251')
678
output = subst_dates(sio.getvalue())
680
=== added directory '%(directory)s'
681
=== added file '%(test_txt)s'
682
--- a/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
683
+++ b/%(test_txt)s\tYYYY-MM-DD HH:MM:SS +ZZZZ
687
=== added file '?.txt'
688
--- a/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
689
+++ b/?.txt\tYYYY-MM-DD HH:MM:SS +ZZZZ
693
''' % {'directory': _russian_test.encode('cp1251'),
694
'test_txt': test_txt.encode('cp1251'),
696
self.assertEqualDiff(output, shouldbe)
584
699
class DiffWasIs(diff.DiffPath):
586
701
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
587
702
self.to_file.write('was: ')
588
self.to_file.write(self.old_tree.get_file(file_id).read())
703
self.to_file.write(self.old_tree.get_file(old_path).read())
589
704
self.to_file.write('is: ')
590
self.to_file.write(self.new_tree.get_file(file_id).read())
705
self.to_file.write(self.new_tree.get_file(new_path).read())
594
708
class TestDiffTree(tests.TestCaseWithTransport):
612
726
('new-tree/newdir/newfile', 'new\n')])
613
727
self.new_tree.add('newdir')
614
728
self.new_tree.add('newdir/newfile', 'file-id')
615
differ = diff.DiffText(self.old_tree, self.new_tree, StringIO())
616
differ.diff_text('file-id', None, 'old label', 'new label')
729
differ = diff.DiffText(self.old_tree, self.new_tree, BytesIO())
730
differ.diff_text('olddir/oldfile', None, 'old label',
731
'new label', 'file-id', None)
617
732
self.assertEqual(
618
733
'--- old label\n+++ new label\n@@ -1,1 +0,0 @@\n-old\n\n',
619
734
differ.to_file.getvalue())
620
735
differ.to_file.seek(0)
621
differ.diff_text(None, 'file-id', 'old label', 'new label')
736
differ.diff_text(None, 'newdir/newfile',
737
'old label', 'new label', None, 'file-id')
622
738
self.assertEqual(
623
739
'--- old label\n+++ new label\n@@ -0,0 +1,1 @@\n+new\n\n',
624
740
differ.to_file.getvalue())
625
741
differ.to_file.seek(0)
626
differ.diff_text('file-id', 'file-id', 'old label', 'new label')
742
differ.diff_text('olddir/oldfile', 'newdir/newfile',
743
'old label', 'new label', 'file-id', 'file-id')
627
744
self.assertEqual(
628
745
'--- old label\n+++ new label\n@@ -1,1 +1,1 @@\n-old\n+new\n\n',
629
746
differ.to_file.getvalue())
644
761
self.new_tree.add('file', 'file-id')
645
762
os.unlink('old-tree/file')
646
763
self.differ.show_diff(None)
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
764
self.assertContainsRe(self.differ.to_file.getvalue(), r'\+contents')
649
766
def test_diff_symlink(self):
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
767
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
768
differ.diff_symlink('old target', None)
652
769
self.assertEqual("=== target was 'old target'\n",
653
770
differ.to_file.getvalue())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
772
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
773
differ.diff_symlink(None, 'new target')
657
774
self.assertEqual("=== target is 'new target'\n",
658
775
differ.to_file.getvalue())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
777
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
778
differ.diff_symlink('old target', 'new target')
662
779
self.assertEqual("=== target changed 'old target' => 'new target'\n",
663
780
differ.to_file.getvalue())
717
834
diff.DiffTree.diff_factories=old_diff_factories[:]
718
835
diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
720
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
837
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
722
839
diff.DiffTree.diff_factories = old_diff_factories
723
840
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
724
841
self.assertNotContainsRe(
725
842
differ.to_file.getvalue(),
726
843
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
727
' \@\@\n-old\n\+new\n\n')
844
r' \@\@\n-old\n\+new\n\n')
728
845
self.assertContainsRe(differ.to_file.getvalue(),
729
846
'was: old\nis: new\n')
731
848
def test_extra_factories(self):
732
849
self.create_old_new()
733
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
850
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
734
851
extra_factories=[DiffWasIs.from_diff_tree])
735
852
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
736
853
self.assertNotContainsRe(
737
854
differ.to_file.getvalue(),
738
855
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
739
' \@\@\n-old\n\+new\n\n')
856
r' \@\@\n-old\n\+new\n\n')
740
857
self.assertContainsRe(differ.to_file.getvalue(),
741
858
'was: old\nis: new\n')
764
881
b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
882
sm = self._PatienceSequenceMatcher(None, a, b)
766
883
mb = sm.get_matching_blocks()
767
self.assertEquals(35, len(mb))
884
self.assertEqual(35, len(mb))
769
886
def test_unique_lcs(self):
770
887
unique_lcs = self._unique_lcs
771
self.assertEquals(unique_lcs('', ''), [])
772
self.assertEquals(unique_lcs('', 'a'), [])
773
self.assertEquals(unique_lcs('a', ''), [])
774
self.assertEquals(unique_lcs('a', 'a'), [(0,0)])
775
self.assertEquals(unique_lcs('a', 'b'), [])
776
self.assertEquals(unique_lcs('ab', 'ab'), [(0,0), (1,1)])
777
self.assertEquals(unique_lcs('abcde', 'cdeab'), [(2,0), (3,1), (4,2)])
778
self.assertEquals(unique_lcs('cdeab', 'abcde'), [(0,2), (1,3), (2,4)])
779
self.assertEquals(unique_lcs('abXde', 'abYde'), [(0,0), (1,1),
781
self.assertEquals(unique_lcs('acbac', 'abc'), [(2,1)])
888
self.assertEqual(unique_lcs('', ''), [])
889
self.assertEqual(unique_lcs('', 'a'), [])
890
self.assertEqual(unique_lcs('a', ''), [])
891
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
892
self.assertEqual(unique_lcs('a', 'b'), [])
893
self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
894
self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
895
self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0, 2), (1, 3), (2, 4)])
896
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
898
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
783
900
def test_recurse_matches(self):
784
901
def test_one(a, b, matches):
785
902
test_matches = []
786
903
self._recurse_matches(
787
904
a, b, 0, 0, len(a), len(b), test_matches, 10)
788
self.assertEquals(test_matches, matches)
905
self.assertEqual(test_matches, matches)
790
907
test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
908
[(0, 0), (2, 2), (4, 4)])
854
971
# non unique lines surrounded by non-matching lines
856
self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
973
self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
858
975
# But they only need to be locally unique
859
self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
976
self.assertDiffBlocks('aBcDec', 'abcdec', [(0, 0, 1), (2, 2, 1), (4, 4, 2)])
861
978
# non unique blocks won't be matched
862
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
979
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
864
981
# but locally unique ones will
865
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0,0,1), (2,2,2),
866
(5,4,1), (7,5,2), (10,8,1)])
982
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
983
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
868
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
985
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
869
986
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
870
987
self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
896
1013
def test_opcodes(self):
897
1014
def chk_ops(a, b, expected_codes):
898
1015
s = self._PatienceSequenceMatcher(None, a, b)
899
self.assertEquals(expected_codes, s.get_opcodes())
1016
self.assertEqual(expected_codes, s.get_opcodes())
901
1018
chk_ops('', '', [])
902
1019
chk_ops([], [], [])
903
chk_ops('abc', '', [('delete', 0,3, 0,0)])
904
chk_ops('', 'abc', [('insert', 0,0, 0,3)])
905
chk_ops('abcd', 'abcd', [('equal', 0,4, 0,4)])
906
chk_ops('abcd', 'abce', [('equal', 0,3, 0,3),
907
('replace', 3,4, 3,4)
909
chk_ops('eabc', 'abce', [('delete', 0,1, 0,0),
913
chk_ops('eabce', 'abce', [('delete', 0,1, 0,0),
1020
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1021
chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
1022
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1023
chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
1024
('replace', 3, 4, 3, 4)
1026
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1027
('equal', 1, 4, 0, 3),
1028
('insert', 4, 4, 3, 4)
1030
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1031
('equal', 1, 5, 0, 4)
916
chk_ops('abcde', 'abXde', [('equal', 0,2, 0,2),
917
('replace', 2,3, 2,3),
1033
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1034
('replace', 2, 3, 2, 3),
1035
('equal', 3, 5, 3, 5)
920
chk_ops('abcde', 'abXYZde', [('equal', 0,2, 0,2),
921
('replace', 2,3, 2,5),
1037
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1038
('replace', 2, 3, 2, 5),
1039
('equal', 3, 5, 5, 7)
924
chk_ops('abde', 'abXYZde', [('equal', 0,2, 0,2),
925
('insert', 2,2, 2,5),
1041
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1042
('insert', 2, 2, 2, 5),
1043
('equal', 2, 4, 5, 7)
928
1045
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
929
[('equal', 0,6, 0,6),
930
('insert', 6,6, 6,11),
931
('equal', 6,16, 11,21)
1046
[('equal', 0, 6, 0, 6),
1047
('insert', 6, 6, 6, 11),
1048
('equal', 6, 16, 11, 21)
934
1051
[ 'hello there\n'
936
1053
, 'how are you today?\n'],
937
1054
[ 'hello there\n'
938
1055
, 'how are you today?\n'],
939
[('equal', 0,1, 0,1),
940
('delete', 1,2, 1,1),
1056
[('equal', 0, 1, 0, 1),
1057
('delete', 1, 2, 1, 1),
1058
('equal', 2, 3, 1, 2),
943
1060
chk_ops('aBccDe', 'abccde',
944
[('equal', 0,1, 0,1),
945
('replace', 1,5, 1,5),
1061
[('equal', 0, 1, 0, 1),
1062
('replace', 1, 5, 1, 5),
1063
('equal', 5, 6, 5, 6),
948
1065
chk_ops('aBcDec', 'abcdec',
949
[('equal', 0,1, 0,1),
950
('replace', 1,2, 1,2),
952
('replace', 3,4, 3,4),
1066
[('equal', 0, 1, 0, 1),
1067
('replace', 1, 2, 1, 2),
1068
('equal', 2, 3, 2, 3),
1069
('replace', 3, 4, 3, 4),
1070
('equal', 4, 6, 4, 6),
955
1072
chk_ops('aBcdEcdFg', 'abcdecdfg',
956
[('equal', 0,1, 0,1),
957
('replace', 1,8, 1,8),
1073
[('equal', 0, 1, 0, 1),
1074
('replace', 1, 8, 1, 8),
1075
('equal', 8, 9, 8, 9)
960
1077
chk_ops('aBcdEeXcdFg', 'abcdecdfg',
961
[('equal', 0,1, 0,1),
962
('replace', 1,2, 1,2),
964
('delete', 4,5, 4,4),
966
('delete', 6,7, 5,5),
968
('replace', 9,10, 7,8),
969
('equal', 10,11, 8,9)
1078
[('equal', 0, 1, 0, 1),
1079
('replace', 1, 2, 1, 2),
1080
('equal', 2, 4, 2, 4),
1081
('delete', 4, 5, 4, 4),
1082
('equal', 5, 6, 4, 5),
1083
('delete', 6, 7, 5, 5),
1084
('equal', 7, 9, 5, 7),
1085
('replace', 9, 10, 7, 8),
1086
('equal', 10, 11, 8, 9)
972
1089
def test_grouped_opcodes(self):
973
1090
def chk_ops(a, b, expected_codes, n=3):
974
1091
s = self._PatienceSequenceMatcher(None, a, b)
975
self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
1092
self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
977
1094
chk_ops('', '', [])
978
1095
chk_ops([], [], [])
979
chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
980
chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
1096
chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
1097
chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
981
1098
chk_ops('abcd', 'abcd', [])
982
chk_ops('abcd', 'abce', [[('equal', 0,3, 0,3),
983
('replace', 3,4, 3,4)
1099
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1100
('replace', 3, 4, 3, 4)
985
chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
1102
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1103
('equal', 1, 4, 0, 3),
1104
('insert', 4, 4, 3, 4)
989
1106
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
990
[[('equal', 3,6, 3,6),
991
('insert', 6,6, 6,11),
992
('equal', 6,9, 11,14)
1107
[[('equal', 3, 6, 3, 6),
1108
('insert', 6, 6, 6, 11),
1109
('equal', 6, 9, 11, 14)
994
1111
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
995
[[('equal', 2,6, 2,6),
996
('insert', 6,6, 6,11),
997
('equal', 6,10, 11,15)
1112
[[('equal', 2, 6, 2, 6),
1113
('insert', 6, 6, 6, 11),
1114
('equal', 6, 10, 11, 15)
999
1116
chk_ops('Xabcdef', 'abcdef',
1000
[[('delete', 0,1, 0,0),
1117
[[('delete', 0, 1, 0, 0),
1118
('equal', 1, 4, 0, 3)
1003
1120
chk_ops('abcdef', 'abcdefX',
1004
[[('equal', 3,6, 3,6),
1005
('insert', 6,6, 6,7)
1121
[[('equal', 3, 6, 3, 6),
1122
('insert', 6, 6, 6, 7)
1250
1366
class TestUsingCompiledIfAvailable(tests.TestCase):
1252
1368
def test_PatienceSequenceMatcher(self):
1253
if compiled_patiencediff_feature.available():
1254
from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1369
if features.compiled_patiencediff_feature.available():
1370
from breezy._patiencediff_c import PatienceSequenceMatcher_c
1255
1371
self.assertIs(PatienceSequenceMatcher_c,
1256
1372
patiencediff.PatienceSequenceMatcher)
1258
from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1374
from breezy._patiencediff_py import PatienceSequenceMatcher_py
1259
1375
self.assertIs(PatienceSequenceMatcher_py,
1260
1376
patiencediff.PatienceSequenceMatcher)
1262
1378
def test_unique_lcs(self):
1263
if compiled_patiencediff_feature.available():
1264
from bzrlib._patiencediff_c import unique_lcs_c
1379
if features.compiled_patiencediff_feature.available():
1380
from breezy._patiencediff_c import unique_lcs_c
1265
1381
self.assertIs(unique_lcs_c,
1266
1382
patiencediff.unique_lcs)
1268
from bzrlib._patiencediff_py import unique_lcs_py
1384
from breezy._patiencediff_py import unique_lcs_py
1269
1385
self.assertIs(unique_lcs_py,
1270
1386
patiencediff.unique_lcs)
1272
1388
def test_recurse_matches(self):
1273
if compiled_patiencediff_feature.available():
1274
from bzrlib._patiencediff_c import recurse_matches_c
1389
if features.compiled_patiencediff_feature.available():
1390
from breezy._patiencediff_c import recurse_matches_c
1275
1391
self.assertIs(recurse_matches_c,
1276
1392
patiencediff.recurse_matches)
1278
from bzrlib._patiencediff_py import recurse_matches_py
1394
from breezy._patiencediff_py import recurse_matches_py
1279
1395
self.assertIs(recurse_matches_py,
1280
1396
patiencediff.recurse_matches)
1388
1504
if osutils.host_os_dereferences_symlinks():
1389
1505
self.assertTrue(os.path.samefile('tree/newname', new_path))
1390
1506
# make sure we can create files with the same parent directories
1391
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1507
diff_obj._prepare_files('oldname2', 'newname2', file_id='file2-id')
1510
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1512
def test_encodable_filename(self):
1513
# Just checks file path for external diff tool.
1514
# We cannot change CPython's internal encoding used by os.exec*.
1515
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1517
for _, scenario in EncodingAdapter.encoding_scenarios:
1518
encoding = scenario['encoding']
1519
dirname = scenario['info']['directory']
1520
filename = scenario['info']['filename']
1522
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1523
relpath = dirname + u'/' + filename
1524
fullpath = diffobj._safe_filename('safe', relpath)
1525
self.assertEqual(fullpath,
1526
fullpath.encode(encoding).decode(encoding))
1527
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1529
def test_unencodable_filename(self):
1530
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1532
for _, scenario in EncodingAdapter.encoding_scenarios:
1533
encoding = scenario['encoding']
1534
dirname = scenario['info']['directory']
1535
filename = scenario['info']['filename']
1537
if encoding == 'iso-8859-1':
1538
encoding = 'iso-8859-2'
1540
encoding = 'iso-8859-1'
1542
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1543
relpath = dirname + u'/' + filename
1544
fullpath = diffobj._safe_filename('safe', relpath)
1545
self.assertEqual(fullpath,
1546
fullpath.encode(encoding).decode(encoding))
1547
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1394
1550
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1396
1552
def call_gtabtd(self, path_list, revision_specs, old_url, new_url):
1397
"""Call get_trees_and_branches_to_diff_locked. Overridden by
1398
TestGetTreesAndBranchesToDiff.
1553
"""Call get_trees_and_branches_to_diff_locked."""
1400
1554
return diff.get_trees_and_branches_to_diff_locked(
1401
1555
path_list, revision_specs, old_url, new_url, self.addCleanup)