Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / lib / git_unittest.py
1 #!/usr/bin/python
2 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Unit tests for chromite.lib.git and helpers for testing that module."""
7
8 from __future__ import print_function
9
10 import functools
11 import os
12 import sys
13
14 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
15     os.path.abspath(__file__)))))
16
17 from chromite.lib import cros_build_lib
18 from chromite.lib import cros_build_lib_unittest
19 from chromite.lib import cros_test_lib
20 from chromite.lib import git
21 from chromite.lib import partial_mock
22 from chromite.lib import patch_unittest
23
24 import mock
25
26
27 class ManifestMock(partial_mock.PartialMock):
28   """Partial mock for git.Manifest."""
29   TARGET = 'chromite.lib.git.Manifest'
30   ATTRS = ('_RunParser',)
31
32   def _RunParser(self, *_args):
33     pass
34
35
36 class ManifestCheckoutMock(partial_mock.PartialMock):
37   """Partial mock for git.ManifestCheckout."""
38   TARGET = 'chromite.lib.git.ManifestCheckout'
39   ATTRS = ('_GetManifestsBranch',)
40
41   def _GetManifestsBranch(self, _root):
42     return 'default'
43
44
45 class NormalizeRefTest(cros_test_lib.TestCase):
46   """Test the Normalize*Ref functions."""
47
48   def _TestNormalize(self, functor, tests):
49     """Helper function for testing Normalize*Ref functions.
50
51     Args:
52       functor: Normalize*Ref functor that only needs the input
53         ref argument.
54       tests: Dict of test inputs to expected test outputs.
55     """
56     for test_input, test_output in tests.iteritems():
57       result = functor(test_input)
58       msg = ('Expected %s to translate %r to %r, but got %r.' %
59              (functor.__name__, test_input, test_output, result))
60       self.assertEquals(test_output, result, msg)
61
62   def testNormalizeRef(self):
63     """Test git.NormalizeRef function."""
64     tests = {
65         # These should all get 'refs/heads/' prefix.
66         'foo': 'refs/heads/foo',
67         'foo-bar-123': 'refs/heads/foo-bar-123',
68
69         # If input starts with 'refs/' it should be left alone.
70         'refs/foo/bar': 'refs/foo/bar',
71         'refs/heads/foo': 'refs/heads/foo',
72
73         # Plain 'refs' is nothing special.
74         'refs': 'refs/heads/refs',
75
76         None: None,
77     }
78     self._TestNormalize(git.NormalizeRef, tests)
79
80   def testNormalizeRemoteRef(self):
81     """Test git.NormalizeRemoteRef function."""
82     remote = 'TheRemote'
83     tests = {
84         # These should all get 'refs/remotes/TheRemote' prefix.
85         'foo': 'refs/remotes/%s/foo' % remote,
86         'foo-bar-123': 'refs/remotes/%s/foo-bar-123' % remote,
87
88         # These should be translated from local to remote ref.
89         'refs/heads/foo': 'refs/remotes/%s/foo' % remote,
90         'refs/heads/foo-bar-123': 'refs/remotes/%s/foo-bar-123' % remote,
91
92         # These should be moved from one remote to another.
93         'refs/remotes/OtherRemote/foo': 'refs/remotes/%s/foo' % remote,
94
95         # These should be left alone.
96         'refs/remotes/%s/foo' % remote: 'refs/remotes/%s/foo' % remote,
97         'refs/foo/bar': 'refs/foo/bar',
98
99         # Plain 'refs' is nothing special.
100         'refs': 'refs/remotes/%s/refs' % remote,
101
102         None: None,
103     }
104
105     # Add remote arg to git.NormalizeRemoteRef.
106     functor = functools.partial(git.NormalizeRemoteRef, remote)
107     functor.__name__ = git.NormalizeRemoteRef.__name__
108
109     self._TestNormalize(functor, tests)
110
111
112 class ProjectCheckoutTest(cros_test_lib.TestCase):
113   """Tests for git.ProjectCheckout"""
114
115   def setUp(self):
116     self.fake_unversioned_patchable = git.ProjectCheckout(
117         dict(name='chromite',
118              path='src/chromite',
119              revision='remotes/for/master'))
120     self.fake_unversioned_unpatchable = git.ProjectCheckout(
121         dict(name='chromite',
122              path='src/platform/somethingsomething/chromite',
123              # Pinned to a SHA1.
124              revision='1deadbeeaf1deadbeeaf1deadbeeaf1deadbeeaf'))
125     self.fake_versioned_patchable = git.ProjectCheckout(
126         dict(name='chromite',
127              path='src/chromite',
128              revision='1deadbeeaf1deadbeeaf1deadbeeaf1deadbeeaf',
129              upstream='remotes/for/master'))
130     self.fake_versioned_unpatchable = git.ProjectCheckout(
131         dict(name='chromite',
132              path='src/chromite',
133              revision='1deadbeeaf1deadbeeaf1deadbeeaf1deadbeeaf',
134              upstream='1deadbeeaf1deadbeeaf1deadbeeaf1deadbeeaf'))
135
136   def testIsPatchable(self):
137     self.assertTrue(self.fake_unversioned_patchable.IsPatchable())
138     self.assertFalse(self.fake_unversioned_unpatchable.IsPatchable())
139     self.assertTrue(self.fake_versioned_patchable.IsPatchable())
140     self.assertFalse(self.fake_versioned_unpatchable.IsPatchable())
141
142
143 class GitPushTest(cros_test_lib.MockTestCase):
144   """Tests for git.GitPush function."""
145
146   # Non fast-forward push error message.
147   NON_FF_PUSH_ERROR = ('To https://localhost/repo.git\n'
148       '! [remote rejected] master -> master (non-fast-forward)\n'
149       'error: failed to push some refs to \'https://localhost/repo.git\'\n')
150
151   # List of possible GoB transient errors.
152   TRANSIENT_ERRORS = (
153       # Hook error when creating a new branch from SHA1 ref.
154       ('remote: Processing changes: (-)To https://localhost/repo.git\n'
155        '! [remote rejected] 6c78ca083c3a9d64068c945fd9998eb1e0a3e739 -> '
156        'stabilize-4636.B (error in hook)\n'
157        'error: failed to push some refs to \'https://localhost/repo.git\'\n'),
158
159       # 'failed to lock' error when creating a new branch from SHA1 ref.
160       ('remote: Processing changes: done\nTo https://localhost/repo.git\n'
161        '! [remote rejected] 4ea09c129b5fedb261bae2431ce2511e35ac3923 -> '
162        'stabilize-daisy-4319.96.B (failed to lock)\n'
163        'error: failed to push some refs to \'https://localhost/repo.git\'\n'),
164
165       # Hook error when pushing branch.
166       ('remote: Processing changes: (\)To https://localhost/repo.git\n'
167        '! [remote rejected] temp_auto_checkin_branch -> '
168        'master (error in hook)\n'
169        'error: failed to push some refs to \'https://localhost/repo.git\'\n'),
170
171       # Another kind of error when pushing a branch.
172       'fatal: remote error: Internal Server Error',
173
174       # crbug.com/298189
175       ('error: gnutls_handshake() failed: A TLS packet with unexpected length '
176        'was received. while accessing '
177        'http://localhost/repo.git/info/refs?service=git-upload-pack\n'
178        'fatal: HTTP request failed'),
179
180       # crbug.com/298189
181       ('fatal: unable to access \'https://localhost/repo.git\': GnuTLS recv '
182        'error (-9): A TLS packet with unexpected length was received.'),
183   )
184
185   def setUp(self):
186     self.StartPatcher(mock.patch('time.sleep'))
187
188   @staticmethod
189   def _RunGitPush():
190     """Runs git.GitPush with some default arguments."""
191     git.GitPush('some_repo_path', 'local-ref',
192                 git.RemoteRef('some-remote', 'remote-ref'),
193                 dryrun=True, retry=True)
194
195   def testPushSuccess(self):
196     """Test handling of successful git push."""
197     with cros_build_lib_unittest.RunCommandMock() as rc_mock:
198       rc_mock.AddCmdResult(partial_mock.In('push'), returncode=0)
199       self._RunGitPush()
200
201   def testNonFFPush(self):
202     """Non fast-forward push error propagates to the caller."""
203     with cros_build_lib_unittest.RunCommandMock() as rc_mock:
204       rc_mock.AddCmdResult(partial_mock.In('push'), returncode=128,
205                            error=self.NON_FF_PUSH_ERROR)
206       self.assertRaises(cros_build_lib.RunCommandError, self._RunGitPush)
207
208   def testPersistentTransientError(self):
209     """GitPush fails if transient error occurs multiple times."""
210     for error in self.TRANSIENT_ERRORS:
211       with cros_build_lib_unittest.RunCommandMock() as rc_mock:
212         rc_mock.AddCmdResult(partial_mock.In('push'), returncode=128,
213                              error=error)
214         self.assertRaises(cros_build_lib.RunCommandError, self._RunGitPush)
215
216   def testOneTimeTransientError(self):
217     """GitPush retries transient errors."""
218     for error in self.TRANSIENT_ERRORS:
219       with cros_build_lib_unittest.RunCommandMock() as rc_mock:
220         results = [
221             rc_mock.CmdResult(128, '', error),
222             rc_mock.CmdResult(0, 'success', ''),
223         ]
224         side_effect = lambda *_args, **_kwargs: results.pop(0)
225         rc_mock.AddCmdResult(partial_mock.In('push'), side_effect=side_effect)
226         self._RunGitPush()
227
228
229 class GitBranchDetectionTest(patch_unittest.GitRepoPatchTestCase):
230   """Tests that git library functions related to branch detection work."""
231   def testDoesCommitExistInRepoWithAmbiguousBranchName(self):
232     git1 = self._MakeRepo('git1', self.source)
233     git.CreateBranch(git1, 'peach', track=True)
234     self.CommitFile(git1, 'peach', 'Keep me.')
235     self.assertTrue(git.DoesCommitExistInRepo(git1, 'peach'))
236
237
238 if __name__ == '__main__':
239   cros_test_lib.main()