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):
644
758
self.new_tree.add('file', 'file-id')
645
759
os.unlink('old-tree/file')
646
760
self.differ.show_diff(None)
647
self.assertContainsRe(self.differ.to_file.getvalue(), '\+contents')
761
self.assertContainsRe(self.differ.to_file.getvalue(), r'\+contents')
649
763
def test_diff_symlink(self):
650
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
764
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
651
765
differ.diff_symlink('old target', None)
652
766
self.assertEqual("=== target was 'old target'\n",
653
767
differ.to_file.getvalue())
655
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
769
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
656
770
differ.diff_symlink(None, 'new target')
657
771
self.assertEqual("=== target is 'new target'\n",
658
772
differ.to_file.getvalue())
660
differ = diff.DiffSymlink(self.old_tree, self.new_tree, StringIO())
774
differ = diff.DiffSymlink(self.old_tree, self.new_tree, BytesIO())
661
775
differ.diff_symlink('old target', 'new target')
662
776
self.assertEqual("=== target changed 'old target' => 'new target'\n",
663
777
differ.to_file.getvalue())
717
831
diff.DiffTree.diff_factories=old_diff_factories[:]
718
832
diff.DiffTree.diff_factories.insert(0, DiffWasIs.from_diff_tree)
720
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO())
834
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO())
722
836
diff.DiffTree.diff_factories = old_diff_factories
723
837
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
724
838
self.assertNotContainsRe(
725
839
differ.to_file.getvalue(),
726
840
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
727
' \@\@\n-old\n\+new\n\n')
841
r' \@\@\n-old\n\+new\n\n')
728
842
self.assertContainsRe(differ.to_file.getvalue(),
729
843
'was: old\nis: new\n')
731
845
def test_extra_factories(self):
732
846
self.create_old_new()
733
differ = diff.DiffTree(self.old_tree, self.new_tree, StringIO(),
847
differ = diff.DiffTree(self.old_tree, self.new_tree, BytesIO(),
734
848
extra_factories=[DiffWasIs.from_diff_tree])
735
849
differ.diff('file-id', 'olddir/oldfile', 'newdir/newfile')
736
850
self.assertNotContainsRe(
737
851
differ.to_file.getvalue(),
738
852
r'--- olddir/oldfile.*\n\+\+\+ newdir/newfile.*\n\@\@ -1,1 \+1,1'
739
' \@\@\n-old\n\+new\n\n')
853
r' \@\@\n-old\n\+new\n\n')
740
854
self.assertContainsRe(differ.to_file.getvalue(),
741
855
'was: old\nis: new\n')
764
878
b = ''.join([unichr(i) for i in range(4300, 4800, 2)])
765
879
sm = self._PatienceSequenceMatcher(None, a, b)
766
880
mb = sm.get_matching_blocks()
767
self.assertEquals(35, len(mb))
881
self.assertEqual(35, len(mb))
769
883
def test_unique_lcs(self):
770
884
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)])
885
self.assertEqual(unique_lcs('', ''), [])
886
self.assertEqual(unique_lcs('', 'a'), [])
887
self.assertEqual(unique_lcs('a', ''), [])
888
self.assertEqual(unique_lcs('a', 'a'), [(0, 0)])
889
self.assertEqual(unique_lcs('a', 'b'), [])
890
self.assertEqual(unique_lcs('ab', 'ab'), [(0, 0), (1, 1)])
891
self.assertEqual(unique_lcs('abcde', 'cdeab'), [(2, 0), (3, 1), (4, 2)])
892
self.assertEqual(unique_lcs('cdeab', 'abcde'), [(0, 2), (1, 3), (2, 4)])
893
self.assertEqual(unique_lcs('abXde', 'abYde'), [(0, 0), (1, 1),
895
self.assertEqual(unique_lcs('acbac', 'abc'), [(2, 1)])
783
897
def test_recurse_matches(self):
784
898
def test_one(a, b, matches):
785
899
test_matches = []
786
900
self._recurse_matches(
787
901
a, b, 0, 0, len(a), len(b), test_matches, 10)
788
self.assertEquals(test_matches, matches)
902
self.assertEqual(test_matches, matches)
790
904
test_one(['a', '', 'b', '', 'c'], ['a', 'a', 'b', 'c', 'c'],
791
905
[(0, 0), (2, 2), (4, 4)])
854
968
# non unique lines surrounded by non-matching lines
856
self.assertDiffBlocks('aBccDe', 'abccde', [(0,0,1), (5,5,1)])
970
self.assertDiffBlocks('aBccDe', 'abccde', [(0, 0, 1), (5, 5, 1)])
858
972
# But they only need to be locally unique
859
self.assertDiffBlocks('aBcDec', 'abcdec', [(0,0,1), (2,2,1), (4,4,2)])
973
self.assertDiffBlocks('aBcDec', 'abcdec', [(0, 0, 1), (2, 2, 1), (4, 4, 2)])
861
975
# non unique blocks won't be matched
862
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0,0,1), (8,8,1)])
976
self.assertDiffBlocks('aBcdEcdFg', 'abcdecdfg', [(0, 0, 1), (8, 8, 1)])
864
978
# 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)])
979
self.assertDiffBlocks('aBcdEeXcdFg', 'abcdecdfg', [(0, 0, 1), (2, 2, 2),
980
(5, 4, 1), (7, 5, 2), (10, 8, 1)])
868
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7,7,1)])
982
self.assertDiffBlocks('abbabbXd', 'cabbabxd', [(7, 7, 1)])
869
983
self.assertDiffBlocks('abbabbbb', 'cabbabbc', [])
870
984
self.assertDiffBlocks('bbbbbbbb', 'cbbbbbbc', [])
896
1010
def test_opcodes(self):
897
1011
def chk_ops(a, b, expected_codes):
898
1012
s = self._PatienceSequenceMatcher(None, a, b)
899
self.assertEquals(expected_codes, s.get_opcodes())
1013
self.assertEqual(expected_codes, s.get_opcodes())
901
1015
chk_ops('', '', [])
902
1016
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),
1017
chk_ops('abc', '', [('delete', 0, 3, 0, 0)])
1018
chk_ops('', 'abc', [('insert', 0, 0, 0, 3)])
1019
chk_ops('abcd', 'abcd', [('equal', 0, 4, 0, 4)])
1020
chk_ops('abcd', 'abce', [('equal', 0, 3, 0, 3),
1021
('replace', 3, 4, 3, 4)
1023
chk_ops('eabc', 'abce', [('delete', 0, 1, 0, 0),
1024
('equal', 1, 4, 0, 3),
1025
('insert', 4, 4, 3, 4)
1027
chk_ops('eabce', 'abce', [('delete', 0, 1, 0, 0),
1028
('equal', 1, 5, 0, 4)
916
chk_ops('abcde', 'abXde', [('equal', 0,2, 0,2),
917
('replace', 2,3, 2,3),
1030
chk_ops('abcde', 'abXde', [('equal', 0, 2, 0, 2),
1031
('replace', 2, 3, 2, 3),
1032
('equal', 3, 5, 3, 5)
920
chk_ops('abcde', 'abXYZde', [('equal', 0,2, 0,2),
921
('replace', 2,3, 2,5),
1034
chk_ops('abcde', 'abXYZde', [('equal', 0, 2, 0, 2),
1035
('replace', 2, 3, 2, 5),
1036
('equal', 3, 5, 5, 7)
924
chk_ops('abde', 'abXYZde', [('equal', 0,2, 0,2),
925
('insert', 2,2, 2,5),
1038
chk_ops('abde', 'abXYZde', [('equal', 0, 2, 0, 2),
1039
('insert', 2, 2, 2, 5),
1040
('equal', 2, 4, 5, 7)
928
1042
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
929
[('equal', 0,6, 0,6),
930
('insert', 6,6, 6,11),
931
('equal', 6,16, 11,21)
1043
[('equal', 0, 6, 0, 6),
1044
('insert', 6, 6, 6, 11),
1045
('equal', 6, 16, 11, 21)
934
1048
[ 'hello there\n'
936
1050
, 'how are you today?\n'],
937
1051
[ 'hello there\n'
938
1052
, 'how are you today?\n'],
939
[('equal', 0,1, 0,1),
940
('delete', 1,2, 1,1),
1053
[('equal', 0, 1, 0, 1),
1054
('delete', 1, 2, 1, 1),
1055
('equal', 2, 3, 1, 2),
943
1057
chk_ops('aBccDe', 'abccde',
944
[('equal', 0,1, 0,1),
945
('replace', 1,5, 1,5),
1058
[('equal', 0, 1, 0, 1),
1059
('replace', 1, 5, 1, 5),
1060
('equal', 5, 6, 5, 6),
948
1062
chk_ops('aBcDec', 'abcdec',
949
[('equal', 0,1, 0,1),
950
('replace', 1,2, 1,2),
952
('replace', 3,4, 3,4),
1063
[('equal', 0, 1, 0, 1),
1064
('replace', 1, 2, 1, 2),
1065
('equal', 2, 3, 2, 3),
1066
('replace', 3, 4, 3, 4),
1067
('equal', 4, 6, 4, 6),
955
1069
chk_ops('aBcdEcdFg', 'abcdecdfg',
956
[('equal', 0,1, 0,1),
957
('replace', 1,8, 1,8),
1070
[('equal', 0, 1, 0, 1),
1071
('replace', 1, 8, 1, 8),
1072
('equal', 8, 9, 8, 9)
960
1074
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)
1075
[('equal', 0, 1, 0, 1),
1076
('replace', 1, 2, 1, 2),
1077
('equal', 2, 4, 2, 4),
1078
('delete', 4, 5, 4, 4),
1079
('equal', 5, 6, 4, 5),
1080
('delete', 6, 7, 5, 5),
1081
('equal', 7, 9, 5, 7),
1082
('replace', 9, 10, 7, 8),
1083
('equal', 10, 11, 8, 9)
972
1086
def test_grouped_opcodes(self):
973
1087
def chk_ops(a, b, expected_codes, n=3):
974
1088
s = self._PatienceSequenceMatcher(None, a, b)
975
self.assertEquals(expected_codes, list(s.get_grouped_opcodes(n)))
1089
self.assertEqual(expected_codes, list(s.get_grouped_opcodes(n)))
977
1091
chk_ops('', '', [])
978
1092
chk_ops([], [], [])
979
chk_ops('abc', '', [[('delete', 0,3, 0,0)]])
980
chk_ops('', 'abc', [[('insert', 0,0, 0,3)]])
1093
chk_ops('abc', '', [[('delete', 0, 3, 0, 0)]])
1094
chk_ops('', 'abc', [[('insert', 0, 0, 0, 3)]])
981
1095
chk_ops('abcd', 'abcd', [])
982
chk_ops('abcd', 'abce', [[('equal', 0,3, 0,3),
983
('replace', 3,4, 3,4)
1096
chk_ops('abcd', 'abce', [[('equal', 0, 3, 0, 3),
1097
('replace', 3, 4, 3, 4)
985
chk_ops('eabc', 'abce', [[('delete', 0,1, 0,0),
1099
chk_ops('eabc', 'abce', [[('delete', 0, 1, 0, 0),
1100
('equal', 1, 4, 0, 3),
1101
('insert', 4, 4, 3, 4)
989
1103
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
990
[[('equal', 3,6, 3,6),
991
('insert', 6,6, 6,11),
992
('equal', 6,9, 11,14)
1104
[[('equal', 3, 6, 3, 6),
1105
('insert', 6, 6, 6, 11),
1106
('equal', 6, 9, 11, 14)
994
1108
chk_ops('abcdefghijklmnop', 'abcdefxydefghijklmnop',
995
[[('equal', 2,6, 2,6),
996
('insert', 6,6, 6,11),
997
('equal', 6,10, 11,15)
1109
[[('equal', 2, 6, 2, 6),
1110
('insert', 6, 6, 6, 11),
1111
('equal', 6, 10, 11, 15)
999
1113
chk_ops('Xabcdef', 'abcdef',
1000
[[('delete', 0,1, 0,0),
1114
[[('delete', 0, 1, 0, 0),
1115
('equal', 1, 4, 0, 3)
1003
1117
chk_ops('abcdef', 'abcdefX',
1004
[[('equal', 3,6, 3,6),
1005
('insert', 6,6, 6,7)
1118
[[('equal', 3, 6, 3, 6),
1119
('insert', 6, 6, 6, 7)
1250
1363
class TestUsingCompiledIfAvailable(tests.TestCase):
1252
1365
def test_PatienceSequenceMatcher(self):
1253
if compiled_patiencediff_feature.available():
1254
from bzrlib._patiencediff_c import PatienceSequenceMatcher_c
1366
if features.compiled_patiencediff_feature.available():
1367
from breezy._patiencediff_c import PatienceSequenceMatcher_c
1255
1368
self.assertIs(PatienceSequenceMatcher_c,
1256
1369
patiencediff.PatienceSequenceMatcher)
1258
from bzrlib._patiencediff_py import PatienceSequenceMatcher_py
1371
from breezy._patiencediff_py import PatienceSequenceMatcher_py
1259
1372
self.assertIs(PatienceSequenceMatcher_py,
1260
1373
patiencediff.PatienceSequenceMatcher)
1262
1375
def test_unique_lcs(self):
1263
if compiled_patiencediff_feature.available():
1264
from bzrlib._patiencediff_c import unique_lcs_c
1376
if features.compiled_patiencediff_feature.available():
1377
from breezy._patiencediff_c import unique_lcs_c
1265
1378
self.assertIs(unique_lcs_c,
1266
1379
patiencediff.unique_lcs)
1268
from bzrlib._patiencediff_py import unique_lcs_py
1381
from breezy._patiencediff_py import unique_lcs_py
1269
1382
self.assertIs(unique_lcs_py,
1270
1383
patiencediff.unique_lcs)
1272
1385
def test_recurse_matches(self):
1273
if compiled_patiencediff_feature.available():
1274
from bzrlib._patiencediff_c import recurse_matches_c
1386
if features.compiled_patiencediff_feature.available():
1387
from breezy._patiencediff_c import recurse_matches_c
1275
1388
self.assertIs(recurse_matches_c,
1276
1389
patiencediff.recurse_matches)
1278
from bzrlib._patiencediff_py import recurse_matches_py
1391
from breezy._patiencediff_py import recurse_matches_py
1279
1392
self.assertIs(recurse_matches_py,
1280
1393
patiencediff.recurse_matches)
1391
1504
diff_obj._prepare_files('file2-id', 'oldname2', 'newname2')
1507
class TestDiffFromToolEncodedFilename(tests.TestCaseWithTransport):
1509
def test_encodable_filename(self):
1510
# Just checks file path for external diff tool.
1511
# We cannot change CPython's internal encoding used by os.exec*.
1512
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1514
for _, scenario in EncodingAdapter.encoding_scenarios:
1515
encoding = scenario['encoding']
1516
dirname = scenario['info']['directory']
1517
filename = scenario['info']['filename']
1519
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1520
relpath = dirname + u'/' + filename
1521
fullpath = diffobj._safe_filename('safe', relpath)
1522
self.assertEqual(fullpath,
1523
fullpath.encode(encoding).decode(encoding))
1524
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1526
def test_unencodable_filename(self):
1527
diffobj = diff.DiffFromTool(['dummy', '@old_path', '@new_path'],
1529
for _, scenario in EncodingAdapter.encoding_scenarios:
1530
encoding = scenario['encoding']
1531
dirname = scenario['info']['directory']
1532
filename = scenario['info']['filename']
1534
if encoding == 'iso-8859-1':
1535
encoding = 'iso-8859-2'
1537
encoding = 'iso-8859-1'
1539
self.overrideAttr(diffobj, '_fenc', lambda: encoding)
1540
relpath = dirname + u'/' + filename
1541
fullpath = diffobj._safe_filename('safe', relpath)
1542
self.assertEqual(fullpath,
1543
fullpath.encode(encoding).decode(encoding))
1544
self.assertTrue(fullpath.startswith(diffobj._root + '/safe'))
1394
1547
class TestGetTreesAndBranchesToDiffLocked(tests.TestCaseWithTransport):
1396
1549
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.
1550
"""Call get_trees_and_branches_to_diff_locked."""
1400
1551
return diff.get_trees_and_branches_to_diff_locked(
1401
1552
path_list, revision_specs, old_url, new_url, self.addCleanup)