Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / chromite / cbuildbot / stages / branch_stages_unittest.py
1 #!/usr/bin/python
2 # Copyright (c) 2012 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 """Unittests for the branch stages."""
7
8 from __future__ import print_function
9
10 import os
11 import sys
12
13 sys.path.insert(0, os.path.abspath('%s/../../..' % os.path.dirname(__file__)))
14 from chromite.cbuildbot import constants
15 from chromite.cbuildbot import manifest_version
16 from chromite.cbuildbot import manifest_version_unittest
17 from chromite.cbuildbot.stages import branch_stages
18 from chromite.cbuildbot.stages import generic_stages_unittest
19 from chromite.lib import cros_build_lib
20 from chromite.lib import cros_build_lib_unittest
21 from chromite.lib import cros_test_lib
22 from chromite.lib import git
23 from chromite.lib import git_unittest
24 from chromite.lib import osutils
25 from chromite.lib import parallel_unittest
26 from chromite.lib import partial_mock
27
28
29 MANIFEST_CONTENTS = """\
30 <?xml version="1.0" encoding="UTF-8"?>
31 <manifest>
32   <remote fetch="https://chromium.googlesource.com"
33           name="cros"
34           review="chromium-review.googlesource.com"/>
35
36   <default remote="cros" revision="refs/heads/master" sync-j="8"/>
37
38   <project groups="minilayout,buildtools"
39            name="chromiumos/chromite"
40            path="chromite"
41            revision="refs/heads/special-branch"/>
42
43   <project name="chromiumos/special"
44            path="src/special-new"
45            revision="new-special-branch"/>
46
47   <project name="chromiumos/special"
48            path="src/special-old"
49            revision="old-special-branch"/>
50
51   <project name="faraway/external"
52            path="external"
53            revision="refs/heads/master"/>
54
55   <project name="faraway/unpinned"
56            path="unpinned"
57            revision="refs/heads/master"
58            pin="False" />
59
60 </manifest>"""
61
62 CHROMITE_REVISION = "fb46d34d7cd4b9c167b74f494f2a99b68df50b18"
63 SPECIAL_REVISION1 = "7bc42f093d644eeaf1c77fab60883881843c3c65"
64 SPECIAL_REVISION2 = "6270eb3b4f78d9bffec77df50f374f5aae72b370"
65
66 VERSIONED_MANIFEST_CONTENTS = """\
67 <?xml version="1.0" encoding="UTF-8"?>
68 <manifest revision="fe72f0912776fa4596505e236e39286fb217961b">
69   <remote fetch="https://chrome-internal.googlesource.com" name="chrome"/>
70   <remote fetch="https://chromium.googlesource.com/" name="chromium"/>
71   <remote fetch="https://chromium.googlesource.com" name="cros" \
72 review="chromium-review.googlesource.com"/>
73   <remote fetch="https://chrome-internal.googlesource.com" name="cros-internal" \
74 review="https://chrome-internal-review.googlesource.com"/>
75   <remote fetch="https://special.googlesource.com/" name="special" \
76 review="https://special.googlesource.com/"/>
77
78   <default remote="cros" revision="refs/heads/master" sync-j="8"/>
79
80   <project name="chromeos/manifest-internal" path="manifest-internal" \
81 remote="cros-internal" revision="fe72f0912776fa4596505e236e39286fb217961b" \
82 upstream="refs/heads/master"/>
83   <project groups="minilayout,buildtools" name="chromiumos/chromite" \
84 path="chromite" revision="%(chromite_revision)s" \
85 upstream="refs/heads/master"/>
86   <project name="chromiumos/manifest" path="manifest" \
87 revision="f24b69176b16bf9153f53883c0cc752df8e07d8b" \
88 upstream="refs/heads/master"/>
89   <project groups="minilayout" name="chromiumos/overlays/chromiumos-overlay" \
90 path="src/third_party/chromiumos-overlay" \
91 revision="3ac713c65b5d18585e606a0ee740385c8ec83e44" \
92 upstream="refs/heads/master"/>
93   <project name="chromiumos/special" path="src/special-new" \
94 revision="%(special_revision1)s" \
95 upstream="new-special-branch"/>
96   <project name="chromiumos/special" path="src/special-old" \
97 revision="%(special_revision2)s" \
98 upstream="old-special-branch"/>
99 </manifest>""" % dict(chromite_revision=CHROMITE_REVISION,
100                       special_revision1=SPECIAL_REVISION1,
101                       special_revision2=SPECIAL_REVISION2)
102
103
104 # pylint: disable=W0212,R0901
105 class BranchUtilStageTest(generic_stages_unittest.AbstractStageTest,
106                           cros_test_lib.LoggingTestCase):
107   """Tests for branch creation/deletion."""
108
109   BOT_ID = constants.BRANCH_UTIL_CONFIG
110   DEFAULT_VERSION = '111.0.0'
111   RELEASE_BRANCH_NAME = 'release-test-branch'
112
113   def _CreateVersionFile(self, version=None):
114     if version is None:
115       version = self.DEFAULT_VERSION
116     version_file = os.path.join(self.build_root, constants.VERSION_FILE)
117     manifest_version_unittest.VersionInfoTest.WriteFakeVersionFile(
118         version_file, version=version)
119
120   def setUp(self):
121     """Setup patchers for specified bot id."""
122     # Mock out methods as needed.
123     self.StartPatcher(parallel_unittest.ParallelMock())
124     self.StartPatcher(git_unittest.ManifestCheckoutMock())
125     self._CreateVersionFile()
126     self.rc_mock = self.StartPatcher(cros_build_lib_unittest.RunCommandMock())
127     self.rc_mock.SetDefaultCmdResult()
128
129     # We have a versioned manifest (generated by ManifestVersionSyncStage) and
130     # the regular, user-maintained manifests.
131     manifests = {
132         '.repo/manifest.xml': VERSIONED_MANIFEST_CONTENTS,
133         'manifest/default.xml': MANIFEST_CONTENTS,
134         'manifest-internal/official.xml': MANIFEST_CONTENTS,
135     }
136     for m_path, m_content in manifests.iteritems():
137       full_path = os.path.join(self.build_root, m_path)
138       osutils.SafeMakedirs(os.path.dirname(full_path))
139       osutils.WriteFile(full_path, m_content)
140
141     self.norm_name = git.NormalizeRef(self.RELEASE_BRANCH_NAME)
142
143   def _Prepare(self, bot_id=None, **kwargs):
144     if 'cmd_args' not in kwargs:
145       # Fill in cmd_args so we do not use the default, which specifies
146       # --branch.  That is incompatible with some branch-util flows.
147       kwargs['cmd_args'] = ['-r', self.build_root]
148     super(BranchUtilStageTest, self)._Prepare(bot_id, **kwargs)
149
150   def ConstructStage(self):
151     return branch_stages.BranchUtilStage(self._run)
152
153   def _VerifyPush(self, new_branch, rename_from=None, delete=False):
154     """Verify that |new_branch| has been created.
155
156     Args:
157       new_branch: The new remote branch to create (or delete).
158       rename_from: If set, |rename_from| is being renamed to |new_branch|.
159       delete: If set, |new_branch| is being deleted.
160     """
161     # Pushes all operate on remote branch refs.
162     new_branch = git.NormalizeRef(new_branch)
163
164     # Calculate source and destination revisions.
165     suffixes = ['', '-new-special-branch', '-old-special-branch']
166     if delete:
167       src_revs = [''] * len(suffixes)
168     elif rename_from is not None:
169       rename_from = git.NormalizeRef(rename_from)
170       rename_from_tracking = git.NormalizeRemoteRef('cros', rename_from)
171       src_revs = [
172           '%s%s' % (rename_from_tracking, suffix) for suffix in suffixes
173       ]
174     else:
175       src_revs = [CHROMITE_REVISION, SPECIAL_REVISION1, SPECIAL_REVISION2]
176     dest_revs = ['%s%s' % (new_branch, suffix) for suffix in suffixes]
177
178     # Verify pushes happened correctly.
179     for src_rev, dest_rev in zip(src_revs, dest_revs):
180       cmd = ['push', '%s:%s' % (src_rev, dest_rev)]
181       self.rc_mock.assertCommandContains(cmd)
182       if rename_from is not None:
183         cmd = ['push', ':%s' % (rename_from,)]
184         self.rc_mock.assertCommandContains(cmd)
185
186   def testRelease(self):
187     """Run-through of branch creation."""
188     self._Prepare(extra_cmd_args=['--branch-name', self.RELEASE_BRANCH_NAME,
189                                   '--version', self.DEFAULT_VERSION])
190     # Simulate branch not existing.
191     self.rc_mock.AddCmdResult(
192         partial_mock.ListRegex('git show-ref .*%s' % self.RELEASE_BRANCH_NAME),
193         returncode=1)
194     # SHA1 of HEAD for pinned branches.
195     self.rc_mock.AddCmdResult(
196         partial_mock.ListRegex('git rev-parse HEAD'),
197         output='12345')
198
199     before = manifest_version.VersionInfo.from_repo(self.build_root)
200     self.RunStage()
201     after = manifest_version.VersionInfo.from_repo(self.build_root)
202     # Verify Chrome version was bumped.
203     self.assertEquals(int(after.chrome_branch) - int(before.chrome_branch), 1)
204     self.assertEquals(int(after.build_number) - int(before.build_number), 1)
205
206     # Verify that manifests were branched properly. Notice that external
207     # is pinned to a SHA1, not an actual branch.
208     branch_names = {
209       'chromite': self.norm_name,
210       'external': '12345',
211       'src/special-new': self.norm_name + '-new-special-branch',
212       'src/special-old': self.norm_name + '-old-special-branch',
213       'unpinned': 'refs/heads/master',
214     }
215     for m in ['manifest/default.xml', 'manifest-internal/official.xml']:
216       manifest = git.Manifest(os.path.join(self.build_root, m))
217       for project_data in manifest.checkouts_by_path.itervalues():
218         branch_name = branch_names[project_data['path']]
219         msg = (
220             'Branch name for %s should be %r, but got %r' %
221                 (project_data['path'], branch_name, project_data['revision'])
222         )
223         self.assertEquals(project_data['revision'], branch_name, msg)
224
225     self._VerifyPush(self.norm_name)
226
227   def testNonRelease(self):
228     """Non-release branch creation."""
229     self._Prepare(extra_cmd_args=['--branch-name', 'refs/heads/test-branch',
230                                   '--version', self.DEFAULT_VERSION])
231     # Simulate branch not existing.
232     self.rc_mock.AddCmdResult(
233         partial_mock.ListRegex('git show-ref .*test-branch'),
234         returncode=1)
235
236     before = manifest_version.VersionInfo.from_repo(self.build_root)
237     # Disable the new branch increment so that
238     # IncrementVersionOnDiskForSourceBranch detects we need to bump the version.
239     self.PatchObject(branch_stages.BranchUtilStage,
240                      '_IncrementVersionOnDiskForNewBranch', autospec=True)
241     self.RunStage()
242     after = manifest_version.VersionInfo.from_repo(self.build_root)
243     # Verify only branch number is bumped.
244     self.assertEquals(after.chrome_branch, before.chrome_branch)
245     self.assertEquals(int(after.build_number) - int(before.build_number), 1)
246     self._VerifyPush(self._run.options.branch_name)
247
248   def testDeletion(self):
249     """Branch deletion."""
250     self._Prepare(extra_cmd_args=['--branch-name', self.RELEASE_BRANCH_NAME,
251                                   '--delete-branch'])
252     self.rc_mock.AddCmdResult(
253         partial_mock.ListRegex('git show-ref .*release-test-branch.*'),
254         output='SomeSHA1Value'
255     )
256     self.RunStage()
257     self._VerifyPush(self.norm_name, delete=True)
258
259   def testRename(self):
260     """Branch rename."""
261     self._Prepare(extra_cmd_args=['--branch-name', self.RELEASE_BRANCH_NAME,
262                                   '--rename-to', 'refs/heads/release-rename'])
263     # Simulate source branch existing and destination branch not existing.
264     self.rc_mock.AddCmdResult(
265         partial_mock.ListRegex('git show-ref .*%s' % self.RELEASE_BRANCH_NAME),
266         output='SomeSHA1Value')
267     self.rc_mock.AddCmdResult(
268         partial_mock.ListRegex('git show-ref .*release-rename'),
269         returncode=1)
270     self.RunStage()
271     self._VerifyPush(self._run.options.rename_to, rename_from=self.norm_name)
272
273   def testDryRun(self):
274     """Verify all pushes are done with --dryrun when --debug is set."""
275     def VerifyDryRun(cmd, *_args, **_kwargs):
276       self.assertTrue('--dry-run' in cmd)
277
278     # Simulate branch not existing.
279     self.rc_mock.AddCmdResult(
280         partial_mock.ListRegex('git show-ref .*%s' % self.RELEASE_BRANCH_NAME),
281         returncode=1)
282
283     self._Prepare(extra_cmd_args=['--branch-name', self.RELEASE_BRANCH_NAME,
284                                   '--debug',
285                                   '--version', self.DEFAULT_VERSION])
286     self.rc_mock.AddCmdResult(partial_mock.In('push'),
287                               side_effect=VerifyDryRun)
288     self.RunStage()
289     self.rc_mock.assertCommandContains(['push', '--dry-run'])
290
291   def _DetermineIncrForVersion(self, version):
292     version_info = manifest_version.VersionInfo(version)
293     stage_cls = branch_stages.BranchUtilStage
294     return stage_cls.DetermineBranchIncrParams(version_info)
295
296   def testDetermineIncrBranch(self):
297     """Verify branch increment detection."""
298     incr_type, _ = self._DetermineIncrForVersion(self.DEFAULT_VERSION)
299     self.assertEquals(incr_type, 'branch')
300
301   def testDetermineIncrPatch(self):
302     """Verify patch increment detection."""
303     incr_type, _ = self._DetermineIncrForVersion('111.1.0')
304     self.assertEquals(incr_type, 'patch')
305
306   def testDetermineBranchIncrError(self):
307     """Detect unbranchable version."""
308     self.assertRaises(branch_stages.BranchError, self._DetermineIncrForVersion,
309                       '111.1.1')
310
311   def _SimulateIncrementFailure(self):
312     """Simulates a git push failure during source branch increment."""
313     self._Prepare(extra_cmd_args=['--branch-name', self.RELEASE_BRANCH_NAME,
314                                   '--version', self.DEFAULT_VERSION])
315     overlay_dir = os.path.join(
316         self.build_root, constants.CHROMIUMOS_OVERLAY_DIR)
317     self.rc_mock.AddCmdResult(partial_mock.In('push'), returncode=128)
318     stage = self.ConstructStage()
319     args = (overlay_dir, 'gerrit', 'refs/heads/master')
320     stage._IncrementVersionOnDiskForSourceBranch(*args)
321
322   def testSourceIncrementWarning(self):
323     """Test the warning case for incrementing failure."""
324     # Since all git commands are mocked out, the _FetchAndCheckoutTo function
325     # does nothing, and leaves the chromeos_version.sh file in the bumped state,
326     # so it looks like TOT version was indeed bumped by another bot.
327     with cros_test_lib.LoggingCapturer() as logger:
328       self._SimulateIncrementFailure()
329       self.AssertLogsContain(logger, 'bumped by another')
330
331   def testSourceIncrementFailure(self):
332     """Test the failure case for incrementing failure."""
333     def FetchAndCheckoutTo(*_args, **_kwargs):
334       self._CreateVersionFile()
335
336     # Simulate a git checkout of TOT.
337     self.PatchObject(branch_stages.BranchUtilStage, '_FetchAndCheckoutTo',
338                      side_effect=FetchAndCheckoutTo, autospec=True)
339     self.assertRaises(cros_build_lib.RunCommandError,
340                       self._SimulateIncrementFailure)
341
342
343 if __name__ == '__main__':
344   cros_test_lib.main()