--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKitTools/Scripts/webkitpy/common/checkout/scm_unittest.py Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,1197 @@
+# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2009 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from __future__ import with_statement
+
+import base64
+import codecs
+import getpass
+import os
+import os.path
+import re
+import stat
+import subprocess
+import tempfile
+import unittest
+import urllib
+import shutil
+
+from datetime import date
+from webkitpy.common.checkout.api import Checkout
+from webkitpy.common.checkout.scm import detect_scm_system, SCM, SVN, CheckoutNeedsUpdate, commit_error_handler, AuthenticationError, AmbiguousCommitError
+from webkitpy.common.config.committers import Committer # FIXME: This should not be needed
+from webkitpy.common.net.bugzilla import Attachment # FIXME: This should not be needed
+from webkitpy.common.system.executive import Executive, run_command, ScriptError
+
+# Eventually we will want to write tests which work for both scms. (like update_webkit, changed_files, etc.)
+# Perhaps through some SCMTest base-class which both SVNTest and GitTest inherit from.
+
+# FIXME: This should be unified into one of the executive.py commands!
+# Callers could use run_and_throw_if_fail(args, cwd=cwd, quiet=True)
+def run_silent(args, cwd=None):
+ # Note: Not thread safe: http://bugs.python.org/issue2320
+ process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
+ process.communicate() # ignore output
+ exit_code = process.wait()
+ if exit_code:
+ raise ScriptError('Failed to run "%s" exit_code: %d cwd: %s' % (args, exit_code, cwd))
+
+
+def write_into_file_at_path(file_path, contents, encoding="utf-8"):
+ if encoding:
+ with codecs.open(file_path, "w", encoding) as file:
+ file.write(contents)
+ else:
+ with open(file_path, "w") as file:
+ file.write(contents)
+
+
+def read_from_path(file_path, encoding="utf-8"):
+ with codecs.open(file_path, "r", encoding) as file:
+ return file.read()
+
+
+def _make_diff(command, *args):
+ # We use this wrapper to disable output decoding. diffs should be treated as
+ # binary files since they may include text files of multiple differnet encodings.
+ return run_command([command, "diff"] + list(args), decode_output=False)
+
+
+def _svn_diff(*args):
+ return _make_diff("svn", *args)
+
+
+def _git_diff(*args):
+ return _make_diff("git", *args)
+
+
+# Exists to share svn repository creation code between the git and svn tests
+class SVNTestRepository:
+ @classmethod
+ def _svn_add(cls, path):
+ run_command(["svn", "add", path])
+
+ @classmethod
+ def _svn_commit(cls, message):
+ run_command(["svn", "commit", "--quiet", "--message", message])
+
+ @classmethod
+ def _setup_test_commits(cls, test_object):
+ # Add some test commits
+ os.chdir(test_object.svn_checkout_path)
+
+ write_into_file_at_path("test_file", "test1")
+ cls._svn_add("test_file")
+ cls._svn_commit("initial commit")
+
+ write_into_file_at_path("test_file", "test1test2")
+ # This used to be the last commit, but doing so broke
+ # GitTest.test_apply_git_patch which use the inverse diff of the last commit.
+ # svn-apply fails to remove directories in Git, see:
+ # https://bugs.webkit.org/show_bug.cgi?id=34871
+ os.mkdir("test_dir")
+ # Slash should always be the right path separator since we use cygwin on Windows.
+ test_file3_path = "test_dir/test_file3"
+ write_into_file_at_path(test_file3_path, "third file")
+ cls._svn_add("test_dir")
+ cls._svn_commit("second commit")
+
+ write_into_file_at_path("test_file", "test1test2test3\n")
+ write_into_file_at_path("test_file2", "second file")
+ cls._svn_add("test_file2")
+ cls._svn_commit("third commit")
+
+ # This 4th commit is used to make sure that our patch file handling
+ # code correctly treats patches as binary and does not attempt to
+ # decode them assuming they're utf-8.
+ write_into_file_at_path("test_file", u"latin1 test: \u00A0\n", "latin1")
+ write_into_file_at_path("test_file2", u"utf-8 test: \u00A0\n", "utf-8")
+ cls._svn_commit("fourth commit")
+
+ # svn does not seem to update after commit as I would expect.
+ run_command(['svn', 'update'])
+
+ @classmethod
+ def setup(cls, test_object):
+ # Create an test SVN repository
+ test_object.svn_repo_path = tempfile.mkdtemp(suffix="svn_test_repo")
+ test_object.svn_repo_url = "file://%s" % test_object.svn_repo_path # Not sure this will work on windows
+ # git svn complains if we don't pass --pre-1.5-compatible, not sure why:
+ # Expected FS format '2'; found format '3' at /usr/local/libexec/git-core//git-svn line 1477
+ run_command(['svnadmin', 'create', '--pre-1.5-compatible', test_object.svn_repo_path])
+
+ # Create a test svn checkout
+ test_object.svn_checkout_path = tempfile.mkdtemp(suffix="svn_test_checkout")
+ run_command(['svn', 'checkout', '--quiet', test_object.svn_repo_url, test_object.svn_checkout_path])
+
+ # Create and checkout a trunk dir to match the standard svn configuration to match git-svn's expectations
+ os.chdir(test_object.svn_checkout_path)
+ os.mkdir('trunk')
+ cls._svn_add('trunk')
+ # We can add tags and branches as well if we ever need to test those.
+ cls._svn_commit('add trunk')
+
+ # Change directory out of the svn checkout so we can delete the checkout directory.
+ # _setup_test_commits will CD back to the svn checkout directory.
+ os.chdir('/')
+ run_command(['rm', '-rf', test_object.svn_checkout_path])
+ run_command(['svn', 'checkout', '--quiet', test_object.svn_repo_url + '/trunk', test_object.svn_checkout_path])
+
+ cls._setup_test_commits(test_object)
+
+ @classmethod
+ def tear_down(cls, test_object):
+ run_command(['rm', '-rf', test_object.svn_repo_path])
+ run_command(['rm', '-rf', test_object.svn_checkout_path])
+
+ # Now that we've deleted the checkout paths, cwddir may be invalid
+ # Change back to a valid directory so that later calls to os.getcwd() do not fail.
+ os.chdir(detect_scm_system(os.path.dirname(__file__)).checkout_root)
+
+# For testing the SCM baseclass directly.
+class SCMClassTests(unittest.TestCase):
+ def setUp(self):
+ self.dev_null = open(os.devnull, "w") # Used to make our Popen calls quiet.
+
+ def tearDown(self):
+ self.dev_null.close()
+
+ def test_run_command_with_pipe(self):
+ input_process = subprocess.Popen(['echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null)
+ self.assertEqual(run_command(['grep', 'bar'], input=input_process.stdout), "bar\n")
+
+ # Test the non-pipe case too:
+ self.assertEqual(run_command(['grep', 'bar'], input="foo\nbar"), "bar\n")
+
+ command_returns_non_zero = ['/bin/sh', '--invalid-option']
+ # Test when the input pipe process fails.
+ input_process = subprocess.Popen(command_returns_non_zero, stdout=subprocess.PIPE, stderr=self.dev_null)
+ self.assertTrue(input_process.poll() != 0)
+ self.assertRaises(ScriptError, run_command, ['grep', 'bar'], input=input_process.stdout)
+
+ # Test when the run_command process fails.
+ input_process = subprocess.Popen(['echo', 'foo\nbar'], stdout=subprocess.PIPE, stderr=self.dev_null) # grep shows usage and calls exit(2) when called w/o arguments.
+ self.assertRaises(ScriptError, run_command, command_returns_non_zero, input=input_process.stdout)
+
+ def test_error_handlers(self):
+ git_failure_message="Merge conflict during commit: Your file or directory 'WebCore/ChangeLog' is probably out-of-date: resource out of date; try updating at /usr/local/libexec/git-core//git-svn line 469"
+ svn_failure_message="""svn: Commit failed (details follow):
+svn: File or directory 'ChangeLog' is out of date; try updating
+svn: resource out of date; try updating
+"""
+ command_does_not_exist = ['does_not_exist', 'invalid_option']
+ self.assertRaises(OSError, run_command, command_does_not_exist)
+ self.assertRaises(OSError, run_command, command_does_not_exist, error_handler=Executive.ignore_error)
+
+ command_returns_non_zero = ['/bin/sh', '--invalid-option']
+ self.assertRaises(ScriptError, run_command, command_returns_non_zero)
+ # Check if returns error text:
+ self.assertTrue(run_command(command_returns_non_zero, error_handler=Executive.ignore_error))
+
+ self.assertRaises(CheckoutNeedsUpdate, commit_error_handler, ScriptError(output=git_failure_message))
+ self.assertRaises(CheckoutNeedsUpdate, commit_error_handler, ScriptError(output=svn_failure_message))
+ self.assertRaises(ScriptError, commit_error_handler, ScriptError(output='blah blah blah'))
+
+
+# GitTest and SVNTest inherit from this so any test_ methods here will be run once for this class and then once for each subclass.
+class SCMTest(unittest.TestCase):
+ def _create_patch(self, patch_contents):
+ # FIXME: This code is brittle if the Attachment API changes.
+ attachment = Attachment({"bug_id": 12345}, None)
+ attachment.contents = lambda: patch_contents
+
+ joe_cool = Committer(name="Joe Cool", email_or_emails=None)
+ attachment.reviewer = lambda: joe_cool
+
+ return attachment
+
+ def _setup_webkittools_scripts_symlink(self, local_scm):
+ webkit_scm = detect_scm_system(os.path.dirname(os.path.abspath(__file__)))
+ webkit_scripts_directory = webkit_scm.scripts_directory()
+ local_scripts_directory = local_scm.scripts_directory()
+ os.mkdir(os.path.dirname(local_scripts_directory))
+ os.symlink(webkit_scripts_directory, local_scripts_directory)
+
+ # Tests which both GitTest and SVNTest should run.
+ # FIXME: There must be a simpler way to add these w/o adding a wrapper method to both subclasses
+
+ def _shared_test_changed_files(self):
+ write_into_file_at_path("test_file", "changed content")
+ self.assertEqual(self.scm.changed_files(), ["test_file"])
+ write_into_file_at_path("test_dir/test_file3", "new stuff")
+ self.assertEqual(self.scm.changed_files(), ["test_dir/test_file3", "test_file"])
+ old_cwd = os.getcwd()
+ os.chdir("test_dir")
+ # Validate that changed_files does not change with our cwd, see bug 37015.
+ self.assertEqual(self.scm.changed_files(), ["test_dir/test_file3", "test_file"])
+ os.chdir(old_cwd)
+
+ def _shared_test_added_files(self):
+ write_into_file_at_path("test_file", "changed content")
+ self.assertEqual(self.scm.added_files(), [])
+
+ write_into_file_at_path("added_file", "new stuff")
+ self.scm.add("added_file")
+
+ os.mkdir("added_dir")
+ write_into_file_at_path("added_dir/added_file2", "new stuff")
+ self.scm.add("added_dir")
+
+ # SVN reports directory changes, Git does not.
+ added_files = self.scm.added_files()
+ if "added_dir" in added_files:
+ added_files.remove("added_dir")
+ self.assertEqual(added_files, ["added_dir/added_file2", "added_file"])
+
+ # Test also to make sure clean_working_directory removes added files
+ self.scm.clean_working_directory()
+ self.assertEqual(self.scm.added_files(), [])
+ self.assertFalse(os.path.exists("added_file"))
+ self.assertFalse(os.path.exists("added_dir"))
+
+ def _shared_test_changed_files_for_revision(self):
+ # SVN reports directory changes, Git does not.
+ changed_files = self.scm.changed_files_for_revision(3)
+ if "test_dir" in changed_files:
+ changed_files.remove("test_dir")
+ self.assertEqual(changed_files, ["test_dir/test_file3", "test_file"])
+ self.assertEqual(sorted(self.scm.changed_files_for_revision(4)), sorted(["test_file", "test_file2"])) # Git and SVN return different orders.
+ self.assertEqual(self.scm.changed_files_for_revision(2), ["test_file"])
+
+ def _shared_test_contents_at_revision(self):
+ self.assertEqual(self.scm.contents_at_revision("test_file", 3), "test1test2")
+ self.assertEqual(self.scm.contents_at_revision("test_file", 4), "test1test2test3\n")
+
+ # Verify that contents_at_revision returns a byte array, aka str():
+ self.assertEqual(self.scm.contents_at_revision("test_file", 5), u"latin1 test: \u00A0\n".encode("latin1"))
+ self.assertEqual(self.scm.contents_at_revision("test_file2", 5), u"utf-8 test: \u00A0\n".encode("utf-8"))
+
+ self.assertEqual(self.scm.contents_at_revision("test_file2", 4), "second file")
+ # Files which don't exist:
+ # Currently we raise instead of returning None because detecting the difference between
+ # "file not found" and any other error seems impossible with svn (git seems to expose such through the return code).
+ self.assertRaises(ScriptError, self.scm.contents_at_revision, "test_file2", 2)
+ self.assertRaises(ScriptError, self.scm.contents_at_revision, "does_not_exist", 2)
+
+ def _shared_test_committer_email_for_revision(self):
+ self.assertEqual(self.scm.committer_email_for_revision(3), getpass.getuser()) # Committer "email" will be the current user
+
+ def _shared_test_reverse_diff(self):
+ self._setup_webkittools_scripts_symlink(self.scm) # Git's apply_reverse_diff uses resolve-ChangeLogs
+ # Only test the simple case, as any other will end up with conflict markers.
+ self.scm.apply_reverse_diff('5')
+ self.assertEqual(read_from_path('test_file'), "test1test2test3\n")
+
+ def _shared_test_diff_for_revision(self):
+ # Patch formats are slightly different between svn and git, so just regexp for things we know should be there.
+ r3_patch = self.scm.diff_for_revision(4)
+ self.assertTrue(re.search('test3', r3_patch))
+ self.assertFalse(re.search('test4', r3_patch))
+ self.assertTrue(re.search('test2', r3_patch))
+ self.assertTrue(re.search('test2', self.scm.diff_for_revision(3)))
+
+ def _shared_test_svn_apply_git_patch(self):
+ self._setup_webkittools_scripts_symlink(self.scm)
+ git_binary_addition = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
+new file mode 100644
+index 0000000000000000000000000000000000000000..64a9532e7794fcd791f6f12157406d90
+60151690
+GIT binary patch
+literal 512
+zcmZ?wbhEHbRAx|MU|?iW{Kxc~?KofD;ckY;H+&5HnHl!!GQMD7h+sU{_)e9f^V3c?
+zhJP##HdZC#4K}7F68@!1jfWQg2daCm-gs#3|JREDT>c+pG4L<_2;w##WMO#ysPPap
+zLqpAf1OE938xAsSp4!5f-o><?VKe(#0jEcwfHGF4%M1^kRs14oVBp2ZEL{E1N<-zJ
+zsfLmOtKta;2_;2c#^S1-8cf<nb!QnGl>c!Xe6RXvrEtAWBvSDTgTO1j3vA31Puw!A
+zs(87q)j_mVDTqBo-P+03-P5mHCEnJ+x}YdCuS7#bCCyePUe(ynK+|4b-3qK)T?Z&)
+zYG+`tl4h?GZv_$t82}X4*DTE|$;{DEiPyF@)U-1+FaX++T9H{&%cag`W1|zVP@`%b
+zqiSkp6{BTpWTkCr!=<C6Q=?#~R8^JfrliAF6Q^gV9Iup8RqCXqqhqC`qsyhk<-nlB
+z00f{QZvfK&|Nm#oZ0TQl`Yr$BIa6A@16O26ud7H<QM=xl`toLKnz-3h@9c9q&wm|X
+z{89I|WPyD!*M?gv?q`;L=2YFeXrJQNti4?}s!zFo=5CzeBxC69xA<zrjP<wUcCRh4
+ptUl-ZG<%a~#LwkIWv&q!KSCH7tQ8cJDiw+|GV?MN)RjY50RTb-xvT&H
+
+literal 0
+HcmV?d00001
+
+"""
+ self.checkout.apply_patch(self._create_patch(git_binary_addition))
+ added = read_from_path('fizzbuzz7.gif', encoding=None)
+ self.assertEqual(512, len(added))
+ self.assertTrue(added.startswith('GIF89a'))
+ self.assertTrue('fizzbuzz7.gif' in self.scm.changed_files())
+
+ # The file already exists.
+ self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_addition))
+
+ git_binary_modification = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
+index 64a9532e7794fcd791f6f12157406d9060151690..323fae03f4606ea9991df8befbb2fca7
+GIT binary patch
+literal 7
+OcmYex&reD$;sO8*F9L)B
+
+literal 512
+zcmZ?wbhEHbRAx|MU|?iW{Kxc~?KofD;ckY;H+&5HnHl!!GQMD7h+sU{_)e9f^V3c?
+zhJP##HdZC#4K}7F68@!1jfWQg2daCm-gs#3|JREDT>c+pG4L<_2;w##WMO#ysPPap
+zLqpAf1OE938xAsSp4!5f-o><?VKe(#0jEcwfHGF4%M1^kRs14oVBp2ZEL{E1N<-zJ
+zsfLmOtKta;2_;2c#^S1-8cf<nb!QnGl>c!Xe6RXvrEtAWBvSDTgTO1j3vA31Puw!A
+zs(87q)j_mVDTqBo-P+03-P5mHCEnJ+x}YdCuS7#bCCyePUe(ynK+|4b-3qK)T?Z&)
+zYG+`tl4h?GZv_$t82}X4*DTE|$;{DEiPyF@)U-1+FaX++T9H{&%cag`W1|zVP@`%b
+zqiSkp6{BTpWTkCr!=<C6Q=?#~R8^JfrliAF6Q^gV9Iup8RqCXqqhqC`qsyhk<-nlB
+z00f{QZvfK&|Nm#oZ0TQl`Yr$BIa6A@16O26ud7H<QM=xl`toLKnz-3h@9c9q&wm|X
+z{89I|WPyD!*M?gv?q`;L=2YFeXrJQNti4?}s!zFo=5CzeBxC69xA<zrjP<wUcCRh4
+ptUl-ZG<%a~#LwkIWv&q!KSCH7tQ8cJDiw+|GV?MN)RjY50RTb-xvT&H
+
+"""
+ self.checkout.apply_patch(self._create_patch(git_binary_modification))
+ modified = read_from_path('fizzbuzz7.gif', encoding=None)
+ self.assertEqual('foobar\n', modified)
+ self.assertTrue('fizzbuzz7.gif' in self.scm.changed_files())
+
+ # Applying the same modification should fail.
+ self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_modification))
+
+ git_binary_deletion = """diff --git a/fizzbuzz7.gif b/fizzbuzz7.gif
+deleted file mode 100644
+index 323fae0..0000000
+GIT binary patch
+literal 0
+HcmV?d00001
+
+literal 7
+OcmYex&reD$;sO8*F9L)B
+
+"""
+ self.checkout.apply_patch(self._create_patch(git_binary_deletion))
+ self.assertFalse(os.path.exists('fizzbuzz7.gif'))
+ self.assertFalse('fizzbuzz7.gif' in self.scm.changed_files())
+
+ # Cannot delete again.
+ self.assertRaises(ScriptError, self.checkout.apply_patch, self._create_patch(git_binary_deletion))
+
+ def _shared_test_add_recursively(self):
+ os.mkdir("added_dir")
+ write_into_file_at_path("added_dir/added_file", "new stuff")
+ self.scm.add("added_dir/added_file")
+ self.assertTrue("added_dir/added_file" in self.scm.added_files())
+
+class SVNTest(SCMTest):
+
+ @staticmethod
+ def _set_date_and_reviewer(changelog_entry):
+ # Joe Cool matches the reviewer set in SCMTest._create_patch
+ changelog_entry = changelog_entry.replace('REVIEWER_HERE', 'Joe Cool')
+ # svn-apply will update ChangeLog entries with today's date.
+ return changelog_entry.replace('DATE_HERE', date.today().isoformat())
+
+ def test_svn_apply(self):
+ first_entry = """2009-10-26 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Foo Bar.
+
+ Most awesome change ever.
+
+ * scm_unittest.py:
+"""
+ intermediate_entry = """2009-10-27 Eric Seidel <eric@webkit.org>
+
+ Reviewed by Baz Bar.
+
+ A more awesomer change yet!
+
+ * scm_unittest.py:
+"""
+ one_line_overlap_patch = """Index: ChangeLog
+===================================================================
+--- ChangeLog (revision 5)
++++ ChangeLog (working copy)
+@@ -1,5 +1,13 @@
+ 2009-10-26 Eric Seidel <eric@webkit.org>
+
++ Reviewed by NOBODY (OOPS!).
++
++ Second most awesome change ever.
++
++ * scm_unittest.py:
++
++2009-10-26 Eric Seidel <eric@webkit.org>
++
+ Reviewed by Foo Bar.
+
+ Most awesome change ever.
+"""
+ one_line_overlap_entry = """DATE_HERE Eric Seidel <eric@webkit.org>
+
+ Reviewed by REVIEWER_HERE.
+
+ Second most awesome change ever.
+
+ * scm_unittest.py:
+"""
+ two_line_overlap_patch = """Index: ChangeLog
+===================================================================
+--- ChangeLog (revision 5)
++++ ChangeLog (working copy)
+@@ -2,6 +2,14 @@
+
+ Reviewed by Foo Bar.
+
++ Second most awesome change ever.
++
++ * scm_unittest.py:
++
++2009-10-26 Eric Seidel <eric@webkit.org>
++
++ Reviewed by Foo Bar.
++
+ Most awesome change ever.
+
+ * scm_unittest.py:
+"""
+ two_line_overlap_entry = """DATE_HERE Eric Seidel <eric@webkit.org>
+
+ Reviewed by Foo Bar.
+
+ Second most awesome change ever.
+
+ * scm_unittest.py:
+"""
+ write_into_file_at_path('ChangeLog', first_entry)
+ run_command(['svn', 'add', 'ChangeLog'])
+ run_command(['svn', 'commit', '--quiet', '--message', 'ChangeLog commit'])
+
+ # Patch files were created against just 'first_entry'.
+ # Add a second commit to make svn-apply have to apply the patches with fuzz.
+ changelog_contents = "%s\n%s" % (intermediate_entry, first_entry)
+ write_into_file_at_path('ChangeLog', changelog_contents)
+ run_command(['svn', 'commit', '--quiet', '--message', 'Intermediate commit'])
+
+ self._setup_webkittools_scripts_symlink(self.scm)
+ self.checkout.apply_patch(self._create_patch(one_line_overlap_patch))
+ expected_changelog_contents = "%s\n%s" % (self._set_date_and_reviewer(one_line_overlap_entry), changelog_contents)
+ self.assertEquals(read_from_path('ChangeLog'), expected_changelog_contents)
+
+ self.scm.revert_files(['ChangeLog'])
+ self.checkout.apply_patch(self._create_patch(two_line_overlap_patch))
+ expected_changelog_contents = "%s\n%s" % (self._set_date_and_reviewer(two_line_overlap_entry), changelog_contents)
+ self.assertEquals(read_from_path('ChangeLog'), expected_changelog_contents)
+
+ def setUp(self):
+ SVNTestRepository.setup(self)
+ os.chdir(self.svn_checkout_path)
+ self.scm = detect_scm_system(self.svn_checkout_path)
+ # For historical reasons, we test some checkout code here too.
+ self.checkout = Checkout(self.scm)
+
+ def tearDown(self):
+ SVNTestRepository.tear_down(self)
+
+ def test_detect_scm_system_relative_url(self):
+ scm = detect_scm_system(".")
+ # I wanted to assert that we got the right path, but there was some
+ # crazy magic with temp folder names that I couldn't figure out.
+ self.assertTrue(scm.checkout_root)
+
+ def test_create_patch_is_full_patch(self):
+ test_dir_path = os.path.join(self.svn_checkout_path, "test_dir2")
+ os.mkdir(test_dir_path)
+ test_file_path = os.path.join(test_dir_path, 'test_file2')
+ write_into_file_at_path(test_file_path, 'test content')
+ run_command(['svn', 'add', 'test_dir2'])
+
+ # create_patch depends on 'svn-create-patch', so make a dummy version.
+ scripts_path = os.path.join(self.svn_checkout_path, 'WebKitTools', 'Scripts')
+ os.makedirs(scripts_path)
+ create_patch_path = os.path.join(scripts_path, 'svn-create-patch')
+ write_into_file_at_path(create_patch_path, '#!/bin/sh\necho $PWD') # We could pass -n to prevent the \n, but not all echo accept -n.
+ os.chmod(create_patch_path, stat.S_IXUSR | stat.S_IRUSR)
+
+ # Change into our test directory and run the create_patch command.
+ os.chdir(test_dir_path)
+ scm = detect_scm_system(test_dir_path)
+ self.assertEqual(scm.checkout_root, self.svn_checkout_path) # Sanity check that detection worked right.
+ patch_contents = scm.create_patch()
+ # Our fake 'svn-create-patch' returns $PWD instead of a patch, check that it was executed from the root of the repo.
+ self.assertEqual("%s\n" % os.path.realpath(scm.checkout_root), patch_contents) # Add a \n because echo adds a \n.
+
+ def test_detection(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ self.assertEqual(scm.display_name(), "svn")
+ self.assertEqual(scm.supports_local_commits(), False)
+
+ def test_apply_small_binary_patch(self):
+ patch_contents = """Index: test_file.swf
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+
+Property changes on: test_file.swf
+___________________________________________________________________
+Name: svn:mime-type
+ + application/octet-stream
+
+
+Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
+"""
+ expected_contents = base64.b64decode("Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==")
+ self._setup_webkittools_scripts_symlink(self.scm)
+ patch_file = self._create_patch(patch_contents)
+ self.checkout.apply_patch(patch_file)
+ actual_contents = read_from_path("test_file.swf", encoding=None)
+ self.assertEqual(actual_contents, expected_contents)
+
+ def test_apply_svn_patch(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ patch = self._create_patch(_svn_diff("-r5:4"))
+ self._setup_webkittools_scripts_symlink(scm)
+ Checkout(scm).apply_patch(patch)
+
+ def test_apply_svn_patch_force(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ patch = self._create_patch(_svn_diff("-r3:5"))
+ self._setup_webkittools_scripts_symlink(scm)
+ self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
+
+ def test_commit_logs(self):
+ # Commits have dates and usernames in them, so we can't just direct compare.
+ self.assertTrue(re.search('fourth commit', self.scm.last_svn_commit_log()))
+ self.assertTrue(re.search('second commit', self.scm.svn_commit_log(3)))
+
+ def _shared_test_commit_with_message(self, username=None):
+ write_into_file_at_path('test_file', 'more test content')
+ commit_text = self.scm.commit_with_message("another test commit", username)
+ self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
+
+ self.scm.dryrun = True
+ write_into_file_at_path('test_file', 'still more test content')
+ commit_text = self.scm.commit_with_message("yet another test commit", username)
+ self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '0')
+
+ def test_commit_text_parsing(self):
+ self._shared_test_commit_with_message()
+
+ def test_commit_with_username(self):
+ self._shared_test_commit_with_message("dbates@webkit.org")
+
+ def test_commit_without_authorization(self):
+ self.scm.has_authorization_for_realm = lambda: False
+ self.assertRaises(AuthenticationError, self._shared_test_commit_with_message)
+
+ def test_has_authorization_for_realm(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ fake_home_dir = tempfile.mkdtemp(suffix="fake_home_dir")
+ svn_config_dir_path = os.path.join(fake_home_dir, ".subversion")
+ os.mkdir(svn_config_dir_path)
+ fake_webkit_auth_file = os.path.join(svn_config_dir_path, "fake_webkit_auth_file")
+ write_into_file_at_path(fake_webkit_auth_file, SVN.svn_server_realm)
+ self.assertTrue(scm.has_authorization_for_realm(home_directory=fake_home_dir))
+ os.remove(fake_webkit_auth_file)
+ os.rmdir(svn_config_dir_path)
+ os.rmdir(fake_home_dir)
+
+ def test_not_have_authorization_for_realm(self):
+ scm = detect_scm_system(self.svn_checkout_path)
+ fake_home_dir = tempfile.mkdtemp(suffix="fake_home_dir")
+ svn_config_dir_path = os.path.join(fake_home_dir, ".subversion")
+ os.mkdir(svn_config_dir_path)
+ self.assertFalse(scm.has_authorization_for_realm(home_directory=fake_home_dir))
+ os.rmdir(svn_config_dir_path)
+ os.rmdir(fake_home_dir)
+
+ def test_reverse_diff(self):
+ self._shared_test_reverse_diff()
+
+ def test_diff_for_revision(self):
+ self._shared_test_diff_for_revision()
+
+ def test_svn_apply_git_patch(self):
+ self._shared_test_svn_apply_git_patch()
+
+ def test_changed_files(self):
+ self._shared_test_changed_files()
+
+ def test_changed_files_for_revision(self):
+ self._shared_test_changed_files_for_revision()
+
+ def test_added_files(self):
+ self._shared_test_added_files()
+
+ def test_contents_at_revision(self):
+ self._shared_test_contents_at_revision()
+
+ def test_committer_email_for_revision(self):
+ self._shared_test_committer_email_for_revision()
+
+ def test_add_recursively(self):
+ self._shared_test_add_recursively()
+
+ def test_delete(self):
+ os.chdir(self.svn_checkout_path)
+ self.scm.delete("test_file")
+ self.assertTrue("test_file" in self.scm.deleted_files())
+
+ def test_propset_propget(self):
+ filepath = os.path.join(self.svn_checkout_path, "test_file")
+ expected_mime_type = "x-application/foo-bar"
+ self.scm.propset("svn:mime-type", expected_mime_type, filepath)
+ self.assertEqual(expected_mime_type, self.scm.propget("svn:mime-type", filepath))
+
+ def test_show_head(self):
+ write_into_file_at_path("test_file", u"Hello!", "utf-8")
+ SVNTestRepository._svn_commit("fourth commit")
+ self.assertEqual("Hello!", self.scm.show_head('test_file'))
+
+ def test_show_head_binary(self):
+ data = "\244"
+ write_into_file_at_path("binary_file", data, encoding=None)
+ self.scm.add("binary_file")
+ self.scm.commit_with_message("a test commit")
+ self.assertEqual(data, self.scm.show_head('binary_file'))
+
+ def do_test_diff_for_file(self):
+ write_into_file_at_path('test_file', 'some content')
+ self.scm.commit_with_message("a test commit")
+ diff = self.scm.diff_for_file('test_file')
+ self.assertEqual(diff, "")
+
+ write_into_file_at_path("test_file", "changed content")
+ diff = self.scm.diff_for_file('test_file')
+ self.assertTrue("-some content" in diff)
+ self.assertTrue("+changed content" in diff)
+
+ def clean_bogus_dir(self):
+ self.bogus_dir = self.scm._bogus_dir_name()
+ if os.path.exists(self.bogus_dir):
+ shutil.rmtree(self.bogus_dir)
+
+ def test_diff_for_file_with_existing_bogus_dir(self):
+ self.clean_bogus_dir()
+ os.mkdir(self.bogus_dir)
+ self.do_test_diff_for_file()
+ self.assertTrue(os.path.exists(self.bogus_dir))
+ shutil.rmtree(self.bogus_dir)
+
+ def test_diff_for_file_with_missing_bogus_dir(self):
+ self.clean_bogus_dir()
+ self.do_test_diff_for_file()
+ self.assertFalse(os.path.exists(self.bogus_dir))
+
+
+class GitTest(SCMTest):
+
+ def setUp(self):
+ """Sets up fresh git repository with one commit. Then setups a second git
+ repo that tracks the first one."""
+ self.original_dir = os.getcwd()
+
+ self.untracking_checkout_path = tempfile.mkdtemp(suffix="git_test_checkout2")
+ run_command(['git', 'init', self.untracking_checkout_path])
+
+ os.chdir(self.untracking_checkout_path)
+ write_into_file_at_path('foo_file', 'foo')
+ run_command(['git', 'add', 'foo_file'])
+ run_command(['git', 'commit', '-am', 'dummy commit'])
+ self.untracking_scm = detect_scm_system(self.untracking_checkout_path)
+
+ self.tracking_git_checkout_path = tempfile.mkdtemp(suffix="git_test_checkout")
+ run_command(['git', 'clone', '--quiet', self.untracking_checkout_path, self.tracking_git_checkout_path])
+ os.chdir(self.tracking_git_checkout_path)
+ self.tracking_scm = detect_scm_system(self.tracking_git_checkout_path)
+
+ def tearDown(self):
+ # Change back to a valid directory so that later calls to os.getcwd() do not fail.
+ os.chdir(self.original_dir)
+ run_command(['rm', '-rf', self.tracking_git_checkout_path])
+ run_command(['rm', '-rf', self.untracking_checkout_path])
+
+ def test_remote_branch_ref(self):
+ self.assertEqual(self.tracking_scm.remote_branch_ref(), 'refs/remotes/origin/master')
+
+ os.chdir(self.untracking_checkout_path)
+ self.assertRaises(ScriptError, self.untracking_scm.remote_branch_ref)
+
+
+class GitSVNTest(SCMTest):
+
+ def _setup_git_checkout(self):
+ self.git_checkout_path = tempfile.mkdtemp(suffix="git_test_checkout")
+ # --quiet doesn't make git svn silent, so we use run_silent to redirect output
+ run_silent(['git', 'svn', 'clone', '-T', 'trunk', self.svn_repo_url, self.git_checkout_path])
+ os.chdir(self.git_checkout_path)
+
+ def _tear_down_git_checkout(self):
+ # Change back to a valid directory so that later calls to os.getcwd() do not fail.
+ os.chdir(self.original_dir)
+ run_command(['rm', '-rf', self.git_checkout_path])
+
+ def setUp(self):
+ self.original_dir = os.getcwd()
+
+ SVNTestRepository.setup(self)
+ self._setup_git_checkout()
+ self.scm = detect_scm_system(self.git_checkout_path)
+ # For historical reasons, we test some checkout code here too.
+ self.checkout = Checkout(self.scm)
+
+ def tearDown(self):
+ SVNTestRepository.tear_down(self)
+ self._tear_down_git_checkout()
+
+ def test_detection(self):
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertEqual(scm.display_name(), "git")
+ self.assertEqual(scm.supports_local_commits(), True)
+
+ def test_read_git_config(self):
+ key = 'test.git-config'
+ value = 'git-config value'
+ run_command(['git', 'config', key, value])
+ self.assertEqual(self.scm.read_git_config(key), value)
+
+ def test_local_commits(self):
+ test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(test_file, 'foo')
+ run_command(['git', 'commit', '-a', '-m', 'local commit'])
+
+ self.assertEqual(len(self.scm.local_commits()), 1)
+
+ def test_discard_local_commits(self):
+ test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(test_file, 'foo')
+ run_command(['git', 'commit', '-a', '-m', 'local commit'])
+
+ self.assertEqual(len(self.scm.local_commits()), 1)
+ self.scm.discard_local_commits()
+ self.assertEqual(len(self.scm.local_commits()), 0)
+
+ def test_delete_branch(self):
+ new_branch = 'foo'
+
+ run_command(['git', 'checkout', '-b', new_branch])
+ self.assertEqual(run_command(['git', 'symbolic-ref', 'HEAD']).strip(), 'refs/heads/' + new_branch)
+
+ run_command(['git', 'checkout', '-b', 'bar'])
+ self.scm.delete_branch(new_branch)
+
+ self.assertFalse(re.search(r'foo', run_command(['git', 'branch'])))
+
+ def test_remote_merge_base(self):
+ # Diff to merge-base should include working-copy changes,
+ # which the diff to svn_branch.. doesn't.
+ test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(test_file, 'foo')
+
+ diff_to_common_base = _git_diff(self.scm.remote_branch_ref() + '..')
+ diff_to_merge_base = _git_diff(self.scm.remote_merge_base())
+
+ self.assertFalse(re.search(r'foo', diff_to_common_base))
+ self.assertTrue(re.search(r'foo', diff_to_merge_base))
+
+ def test_rebase_in_progress(self):
+ svn_test_file = os.path.join(self.svn_checkout_path, 'test_file')
+ write_into_file_at_path(svn_test_file, "svn_checkout")
+ run_command(['svn', 'commit', '--message', 'commit to conflict with git commit'], cwd=self.svn_checkout_path)
+
+ git_test_file = os.path.join(self.git_checkout_path, 'test_file')
+ write_into_file_at_path(git_test_file, "git_checkout")
+ run_command(['git', 'commit', '-a', '-m', 'commit to be thrown away by rebase abort'])
+
+ # --quiet doesn't make git svn silent, so use run_silent to redirect output
+ self.assertRaises(ScriptError, run_silent, ['git', 'svn', '--quiet', 'rebase']) # Will fail due to a conflict leaving us mid-rebase.
+
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertTrue(scm.rebase_in_progress())
+
+ # Make sure our cleanup works.
+ scm.clean_working_directory()
+ self.assertFalse(scm.rebase_in_progress())
+
+ # Make sure cleanup doesn't throw when no rebase is in progress.
+ scm.clean_working_directory()
+
+ def test_commitish_parsing(self):
+ scm = detect_scm_system(self.git_checkout_path)
+
+ # Multiple revisions are cherry-picked.
+ self.assertEqual(len(scm.commit_ids_from_commitish_arguments(['HEAD~2'])), 1)
+ self.assertEqual(len(scm.commit_ids_from_commitish_arguments(['HEAD', 'HEAD~2'])), 2)
+
+ # ... is an invalid range specifier
+ self.assertRaises(ScriptError, scm.commit_ids_from_commitish_arguments, ['trunk...HEAD'])
+
+ def test_commitish_order(self):
+ scm = detect_scm_system(self.git_checkout_path)
+
+ commit_range = 'HEAD~3..HEAD'
+
+ actual_commits = scm.commit_ids_from_commitish_arguments([commit_range])
+ expected_commits = []
+ expected_commits += reversed(run_command(['git', 'rev-list', commit_range]).splitlines())
+
+ self.assertEqual(actual_commits, expected_commits)
+
+ def test_apply_git_patch(self):
+ scm = detect_scm_system(self.git_checkout_path)
+ # We carefullly pick a diff which does not have a directory addition
+ # as currently svn-apply will error out when trying to remove directories
+ # in Git: https://bugs.webkit.org/show_bug.cgi?id=34871
+ patch = self._create_patch(_git_diff('HEAD..HEAD^'))
+ self._setup_webkittools_scripts_symlink(scm)
+ Checkout(scm).apply_patch(patch)
+
+ def test_apply_git_patch_force(self):
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = self._create_patch(_git_diff('HEAD~2..HEAD'))
+ self._setup_webkittools_scripts_symlink(scm)
+ self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
+
+ def test_commit_text_parsing(self):
+ write_into_file_at_path('test_file', 'more test content')
+ commit_text = self.scm.commit_with_message("another test commit")
+ self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '6')
+
+ self.scm.dryrun = True
+ write_into_file_at_path('test_file', 'still more test content')
+ commit_text = self.scm.commit_with_message("yet another test commit")
+ self.assertEqual(self.scm.svn_revision_from_commit_text(commit_text), '0')
+
+ def test_commit_with_message_working_copy_only(self):
+ write_into_file_at_path('test_file_commit1', 'more test content')
+ run_command(['git', 'add', 'test_file_commit1'])
+ scm = detect_scm_system(self.git_checkout_path)
+ commit_text = scm.commit_with_message("yet another test commit")
+
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+
+ def _one_local_commit(self):
+ write_into_file_at_path('test_file_commit1', 'more test content')
+ run_command(['git', 'add', 'test_file_commit1'])
+ self.scm.commit_locally_with_message("another test commit")
+
+ def _one_local_commit_plus_working_copy_changes(self):
+ self._one_local_commit()
+ write_into_file_at_path('test_file_commit2', 'still more test content')
+ run_command(['git', 'add', 'test_file_commit2'])
+
+ def _two_local_commits(self):
+ self._one_local_commit()
+ write_into_file_at_path('test_file_commit2', 'still more test content')
+ run_command(['git', 'add', 'test_file_commit2'])
+ self.scm.commit_locally_with_message("yet another test commit")
+
+ def _three_local_commits(self):
+ write_into_file_at_path('test_file_commit0', 'more test content')
+ run_command(['git', 'add', 'test_file_commit0'])
+ self.scm.commit_locally_with_message("another test commit")
+ self._two_local_commits()
+
+ def test_commit_with_message(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "yet another test commit")
+ commit_text = scm.commit_with_message("yet another test commit", force_squash=True)
+
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit2', svn_log))
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+
+ def test_commit_with_message_git_commit(self):
+ self._two_local_commits()
+
+ scm = detect_scm_system(self.git_checkout_path)
+ commit_text = scm.commit_with_message("another test commit", git_commit="HEAD^")
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+ self.assertFalse(re.search(r'test_file_commit2', svn_log))
+
+ def test_commit_with_message_git_commit_range(self):
+ self._three_local_commits()
+
+ scm = detect_scm_system(self.git_checkout_path)
+ commit_text = scm.commit_with_message("another test commit", git_commit="HEAD~2..HEAD")
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertFalse(re.search(r'test_file_commit0', svn_log))
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+ self.assertTrue(re.search(r'test_file_commit2', svn_log))
+
+ def test_changed_files_working_copy_only(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ commit_text = scm.commit_with_message("another test commit", git_commit="HEAD..")
+ self.assertFalse(re.search(r'test_file_commit1', svn_log))
+ self.assertTrue(re.search(r'test_file_commit2', svn_log))
+
+ def test_commit_with_message_only_local_commit(self):
+ self._one_local_commit()
+ scm = detect_scm_system(self.git_checkout_path)
+ commit_text = scm.commit_with_message("another test commit")
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+
+ def test_commit_with_message_multiple_local_commits_and_working_copy(self):
+ self._two_local_commits()
+ write_into_file_at_path('test_file_commit1', 'working copy change')
+ scm = detect_scm_system(self.git_checkout_path)
+
+ self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "another test commit")
+ commit_text = scm.commit_with_message("another test commit", force_squash=True)
+
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit2', svn_log))
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+
+ def test_commit_with_message_git_commit_and_working_copy(self):
+ self._two_local_commits()
+ write_into_file_at_path('test_file_commit1', 'working copy change')
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", git_commit="HEAD^")
+
+ def test_commit_with_message_multiple_local_commits_always_squash(self):
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ scm._assert_can_squash = lambda working_directory_is_clean: True
+ commit_text = scm.commit_with_message("yet another test commit")
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit2', svn_log))
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+
+ def test_commit_with_message_multiple_local_commits(self):
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "yet another test commit")
+ commit_text = scm.commit_with_message("yet another test commit", force_squash=True)
+
+ self.assertEqual(scm.svn_revision_from_commit_text(commit_text), '6')
+
+ svn_log = run_command(['git', 'svn', 'log', '--limit=1', '--verbose'])
+ self.assertTrue(re.search(r'test_file_commit2', svn_log))
+ self.assertTrue(re.search(r'test_file_commit1', svn_log))
+
+ def test_commit_with_message_not_synced(self):
+ run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertRaises(AmbiguousCommitError, scm.commit_with_message, "another test commit")
+ self.assertRaises(ScriptError, scm.commit_with_message, "another test commit", force_squash=True)
+
+ def test_remote_branch_ref(self):
+ self.assertEqual(self.scm.remote_branch_ref(), 'refs/remotes/trunk')
+
+ def test_reverse_diff(self):
+ self._shared_test_reverse_diff()
+
+ def test_diff_for_revision(self):
+ self._shared_test_diff_for_revision()
+
+ def test_svn_apply_git_patch(self):
+ self._shared_test_svn_apply_git_patch()
+
+ def test_create_patch_local_plus_working_copy(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = scm.create_patch()
+ self.assertTrue(re.search(r'test_file_commit1', patch))
+ self.assertTrue(re.search(r'test_file_commit2', patch))
+
+ def test_create_patch(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = scm.create_patch()
+ self.assertTrue(re.search(r'test_file_commit2', patch))
+ self.assertTrue(re.search(r'test_file_commit1', patch))
+
+ def test_create_patch_git_commit(self):
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = scm.create_patch(git_commit="HEAD^")
+ self.assertTrue(re.search(r'test_file_commit1', patch))
+ self.assertFalse(re.search(r'test_file_commit2', patch))
+
+ def test_create_patch_git_commit_range(self):
+ self._three_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = scm.create_patch(git_commit="HEAD~2..HEAD")
+ self.assertFalse(re.search(r'test_file_commit0', patch))
+ self.assertTrue(re.search(r'test_file_commit2', patch))
+ self.assertTrue(re.search(r'test_file_commit1', patch))
+
+ def test_create_patch_working_copy_only(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = scm.create_patch(git_commit="HEAD..")
+ self.assertFalse(re.search(r'test_file_commit1', patch))
+ self.assertTrue(re.search(r'test_file_commit2', patch))
+
+ def test_create_patch_multiple_local_commits(self):
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ patch = scm.create_patch()
+ self.assertTrue(re.search(r'test_file_commit2', patch))
+ self.assertTrue(re.search(r'test_file_commit1', patch))
+
+ def test_create_patch_not_synced(self):
+ run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertRaises(ScriptError, scm.create_patch)
+
+ def test_create_binary_patch(self):
+ # Create a git binary patch and check the contents.
+ scm = detect_scm_system(self.git_checkout_path)
+ test_file_name = 'binary_file'
+ test_file_path = os.path.join(self.git_checkout_path, test_file_name)
+ file_contents = ''.join(map(chr, range(256)))
+ write_into_file_at_path(test_file_path, file_contents, encoding=None)
+ run_command(['git', 'add', test_file_name])
+ patch = scm.create_patch()
+ self.assertTrue(re.search(r'\nliteral 0\n', patch))
+ self.assertTrue(re.search(r'\nliteral 256\n', patch))
+
+ # Check if we can apply the created patch.
+ run_command(['git', 'rm', '-f', test_file_name])
+ self._setup_webkittools_scripts_symlink(scm)
+ self.checkout.apply_patch(self._create_patch(patch))
+ self.assertEqual(file_contents, read_from_path(test_file_path, encoding=None))
+
+ # Check if we can create a patch from a local commit.
+ write_into_file_at_path(test_file_path, file_contents, encoding=None)
+ run_command(['git', 'add', test_file_name])
+ run_command(['git', 'commit', '-m', 'binary diff'])
+ patch_from_local_commit = scm.create_patch('HEAD')
+ self.assertTrue(re.search(r'\nliteral 0\n', patch_from_local_commit))
+ self.assertTrue(re.search(r'\nliteral 256\n', patch_from_local_commit))
+
+ def test_changed_files_local_plus_working_copy(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ files = scm.changed_files()
+ self.assertTrue('test_file_commit1' in files)
+ self.assertTrue('test_file_commit2' in files)
+
+ def test_changed_files_git_commit(self):
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ files = scm.changed_files(git_commit="HEAD^")
+ self.assertTrue('test_file_commit1' in files)
+ self.assertFalse('test_file_commit2' in files)
+
+ def test_changed_files_git_commit_range(self):
+ self._three_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ files = scm.changed_files(git_commit="HEAD~2..HEAD")
+ self.assertTrue('test_file_commit0' not in files)
+ self.assertTrue('test_file_commit1' in files)
+ self.assertTrue('test_file_commit2' in files)
+
+ def test_changed_files_working_copy_only(self):
+ self._one_local_commit_plus_working_copy_changes()
+ scm = detect_scm_system(self.git_checkout_path)
+ files = scm.changed_files(git_commit="HEAD..")
+ self.assertFalse('test_file_commit1' in files)
+ self.assertTrue('test_file_commit2' in files)
+
+ def test_changed_files_multiple_local_commits(self):
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ files = scm.changed_files()
+ self.assertTrue('test_file_commit2' in files)
+ self.assertTrue('test_file_commit1' in files)
+
+ def test_changed_files_not_synced(self):
+ run_command(['git', 'checkout', '-b', 'my-branch', 'trunk~3'])
+ self._two_local_commits()
+ scm = detect_scm_system(self.git_checkout_path)
+ self.assertRaises(ScriptError, scm.changed_files)
+
+ def test_changed_files(self):
+ self._shared_test_changed_files()
+
+ def test_changed_files_for_revision(self):
+ self._shared_test_changed_files_for_revision()
+
+ def test_contents_at_revision(self):
+ self._shared_test_contents_at_revision()
+
+ def test_added_files(self):
+ self._shared_test_added_files()
+
+ def test_committer_email_for_revision(self):
+ self._shared_test_committer_email_for_revision()
+
+ def test_add_recursively(self):
+ self._shared_test_add_recursively()
+
+ def test_delete(self):
+ self._two_local_commits()
+ self.scm.delete('test_file_commit1')
+ self.assertTrue("test_file_commit1" in self.scm.deleted_files())
+
+ def test_to_object_name(self):
+ relpath = 'test_file_commit1'
+ fullpath = os.path.join(self.git_checkout_path, relpath)
+ self._two_local_commits()
+ self.assertEqual(relpath, self.scm.to_object_name(fullpath))
+
+ def test_show_head(self):
+ self._two_local_commits()
+ self.assertEqual("more test content", self.scm.show_head('test_file_commit1'))
+
+ def test_show_head_binary(self):
+ self._two_local_commits()
+ data = "\244"
+ write_into_file_at_path("binary_file", data, encoding=None)
+ self.scm.add("binary_file")
+ self.scm.commit_locally_with_message("a test commit")
+ self.assertEqual(data, self.scm.show_head('binary_file'))
+
+ def test_diff_for_file(self):
+ self._two_local_commits()
+ write_into_file_at_path('test_file_commit1', "Updated", encoding=None)
+
+ diff = self.scm.diff_for_file('test_file_commit1')
+ cached_diff = self.scm.diff_for_file('test_file_commit1')
+ self.assertTrue("+Updated" in diff)
+ self.assertTrue("-more test content" in diff)
+
+ self.scm.add('test_file_commit1')
+
+ cached_diff = self.scm.diff_for_file('test_file_commit1')
+ self.assertTrue("+Updated" in cached_diff)
+ self.assertTrue("-more test content" in cached_diff)
+
+if __name__ == '__main__':
+ unittest.main()