9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
from cStringIO import StringIO
21
from tempfile import TemporaryFile
23
from bzrlib.diff import internal_diff, external_diff, show_diff_trees
24
from bzrlib.errors import BinaryFile, NoDiff
25
import bzrlib.osutils as osutils
20
from bzrlib.diff import internal_diff, show_diff_trees
21
from bzrlib.errors import BinaryFile
26
22
import bzrlib.patiencediff
27
from bzrlib.tests import (TestCase, TestCaseWithTransport,
28
TestCaseInTempDir, TestSkipped)
23
from bzrlib.tests import TestCase, TestCaseWithTransport, TestCaseInTempDir
24
from bzrlib.tests import TestCase, TestCaseInTempDir
31
27
def udiff_lines(old, new, allow_binary=False):
98
78
udiff_lines([1023 * 'a' + '\x00'], [], allow_binary=True)
99
79
udiff_lines([], [1023 * 'a' + '\x00'], allow_binary=True)
101
def test_external_diff(self):
102
lines = external_udiff_lines(['boo\n'], ['goo\n'])
103
self.check_patch(lines)
104
self.assertEqual('\n', lines[-1])
106
def test_external_diff_no_fileno(self):
107
# Make sure that we can handle not having a fileno, even
108
# if the diff is large
109
lines = external_udiff_lines(['boo\n']*10000,
112
self.check_patch(lines)
114
def test_external_diff_binary_lang_c(self):
116
for lang in ('LANG', 'LC_ALL', 'LANGUAGE'):
117
old_env[lang] = osutils.set_or_unset_env(lang, 'C')
119
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
120
# Older versions of diffutils say "Binary files", newer
121
# versions just say "Files".
122
self.assertContainsRe(lines[0],
123
'(Binary f|F)iles old and new differ\n')
124
self.assertEquals(lines[1:], ['\n'])
126
for lang, old_val in old_env.iteritems():
127
osutils.set_or_unset_env(lang, old_val)
129
def test_no_external_diff(self):
130
"""Check that NoDiff is raised when diff is not available"""
131
# Use os.environ['PATH'] to make sure no 'diff' command is available
132
orig_path = os.environ['PATH']
134
os.environ['PATH'] = ''
135
self.assertRaises(NoDiff, external_diff,
136
'old', ['boo\n'], 'new', ['goo\n'],
137
StringIO(), diff_opts=['-u'])
139
os.environ['PATH'] = orig_path
141
81
def test_internal_diff_default(self):
142
82
# Default internal diff encoding is utf8
143
83
output = StringIO()
195
135
'internal_diff should return bytestrings')
198
class TestDiffFiles(TestCaseInTempDir):
200
def test_external_diff_binary(self):
201
"""The output when using external diff should use diff's i18n error"""
202
# Make sure external_diff doesn't fail in the current LANG
203
lines = external_udiff_lines(['\x00foobar\n'], ['foo\x00bar\n'])
205
cmd = ['diff', '-u', '--binary', 'old', 'new']
206
open('old', 'wb').write('\x00foobar\n')
207
open('new', 'wb').write('foo\x00bar\n')
208
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE,
209
stdin=subprocess.PIPE)
210
out, err = pipe.communicate()
211
# Diff returns '2' on Binary files.
212
self.assertEqual(2, pipe.returncode)
213
# We should output whatever diff tells us, plus a trailing newline
214
self.assertEqual(out.splitlines(True) + ['\n'], lines)
217
class TestShowDiffTreesHelper(TestCaseWithTransport):
218
"""Has a helper for running show_diff_trees"""
220
def get_diff(self, tree1, tree2, specific_files=None, working_tree=None):
222
if working_tree is not None:
223
extra_trees = (working_tree,)
226
show_diff_trees(tree1, tree2, output, specific_files=specific_files,
227
extra_trees=extra_trees, old_label='old/',
229
return output.getvalue()
232
class TestDiffDates(TestShowDiffTreesHelper):
138
class TestDiffDates(TestCaseWithTransport):
235
141
super(TestDiffDates, self).setUp()
336
def test_show_diff_specified(self):
337
"""A working tree filename can be used to identify a file"""
338
self.wt.rename_one('file1', 'file1b')
339
old_tree = self.b.repository.revision_tree('rev-1')
340
new_tree = self.b.repository.revision_tree('rev-4')
341
out = self.get_diff(old_tree, new_tree, specific_files=['file1b'],
342
working_tree=self.wt)
343
self.assertContainsRe(out, 'file1\t')
345
def test_recursive_diff(self):
346
"""Children of directories are matched"""
349
self.wt.add(['dir1', 'dir2'])
350
self.wt.rename_one('file1', 'dir1/file1')
351
old_tree = self.b.repository.revision_tree('rev-1')
352
new_tree = self.b.repository.revision_tree('rev-4')
353
out = self.get_diff(old_tree, new_tree, specific_files=['dir1'],
354
working_tree=self.wt)
355
self.assertContainsRe(out, 'file1\t')
356
out = self.get_diff(old_tree, new_tree, specific_files=['dir2'],
357
working_tree=self.wt)
358
self.assertNotContainsRe(out, 'file1\t')
362
class TestShowDiffTrees(TestShowDiffTreesHelper):
363
"""Direct tests for show_diff_trees"""
365
def test_modified_file(self):
366
"""Test when a file is modified."""
367
tree = self.make_branch_and_tree('tree')
368
self.build_tree_contents([('tree/file', 'contents\n')])
369
tree.add(['file'], ['file-id'])
370
tree.commit('one', rev_id='rev-1')
372
self.build_tree_contents([('tree/file', 'new contents\n')])
373
diff = self.get_diff(tree.basis_tree(), tree)
374
self.assertContainsRe(diff, "=== modified file 'file'\n")
375
self.assertContainsRe(diff, '--- old/file\t')
376
self.assertContainsRe(diff, '\\+\\+\\+ new/file\t')
377
self.assertContainsRe(diff, '-contents\n'
380
def test_modified_file_in_renamed_dir(self):
381
"""Test when a file is modified in a renamed directory."""
382
tree = self.make_branch_and_tree('tree')
383
self.build_tree(['tree/dir/'])
384
self.build_tree_contents([('tree/dir/file', 'contents\n')])
385
tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
386
tree.commit('one', rev_id='rev-1')
388
tree.rename_one('dir', 'other')
389
self.build_tree_contents([('tree/other/file', 'new contents\n')])
390
diff = self.get_diff(tree.basis_tree(), tree)
391
self.assertContainsRe(diff, "=== renamed directory 'dir' => 'other'\n")
392
self.assertContainsRe(diff, "=== modified file 'other/file'\n")
393
# XXX: This is technically incorrect, because it used to be at another
394
# location. What to do?
395
self.assertContainsRe(diff, '--- old/dir/file\t')
396
self.assertContainsRe(diff, '\\+\\+\\+ new/other/file\t')
397
self.assertContainsRe(diff, '-contents\n'
400
def test_renamed_directory(self):
401
"""Test when only a directory is only renamed."""
402
tree = self.make_branch_and_tree('tree')
403
self.build_tree(['tree/dir/'])
404
self.build_tree_contents([('tree/dir/file', 'contents\n')])
405
tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
406
tree.commit('one', rev_id='rev-1')
408
tree.rename_one('dir', 'newdir')
409
diff = self.get_diff(tree.basis_tree(), tree)
410
# Renaming a directory should be a single "you renamed this dir" even
411
# when there are files inside.
412
self.assertEqual("=== renamed directory 'dir' => 'newdir'\n", diff)
414
def test_renamed_file(self):
415
"""Test when a file is only renamed."""
416
tree = self.make_branch_and_tree('tree')
417
self.build_tree_contents([('tree/file', 'contents\n')])
418
tree.add(['file'], ['file-id'])
419
tree.commit('one', rev_id='rev-1')
421
tree.rename_one('file', 'newname')
422
diff = self.get_diff(tree.basis_tree(), tree)
423
self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
424
# We shouldn't have a --- or +++ line, because there is no content
426
self.assertNotContainsRe(diff, '---')
428
def test_renamed_and_modified_file(self):
429
"""Test when a file is only renamed."""
430
tree = self.make_branch_and_tree('tree')
431
self.build_tree_contents([('tree/file', 'contents\n')])
432
tree.add(['file'], ['file-id'])
433
tree.commit('one', rev_id='rev-1')
435
tree.rename_one('file', 'newname')
436
self.build_tree_contents([('tree/newname', 'new contents\n')])
437
diff = self.get_diff(tree.basis_tree(), tree)
438
self.assertContainsRe(diff, "=== renamed file 'file' => 'newname'\n")
439
self.assertContainsRe(diff, '--- old/file\t')
440
self.assertContainsRe(diff, '\\+\\+\\+ new/newname\t')
441
self.assertContainsRe(diff, '-contents\n'
445
249
class TestPatienceDiffLib(TestCase):