blob: 409508ec3ac1d0bcadb57ce654fade45090ea805 [file] [log] [blame]
Don Garrettc4114cc2016-11-01 20:04:06 -07001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -07005"""Unit tests for chromite.scripts.cbuildbot_launch."""
Don Garrettc4114cc2016-11-01 20:04:06 -07006
Don Garrett7ade05a2017-02-17 13:31:47 -08007import os
Mike Nicholsd0acc7f2021-05-21 17:18:24 +00008import time
Mike Frysinger166fea02021-02-12 05:30:33 -05009from unittest import mock
Don Garrett86881cb2017-02-15 15:41:55 -080010
Mike Frysingerc263d092019-09-18 15:11:47 -040011from chromite.cbuildbot import commands
Don Garrett86881cb2017-02-15 15:41:55 -080012from chromite.cbuildbot import repository
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060013from chromite.lib import build_summary
Don Garrett861e9182017-05-15 15:30:23 -070014from chromite.lib import constants
Benjamin Gordon74645232018-05-04 17:40:42 -060015from chromite.lib import cros_sdk_lib
Don Garrett7ade05a2017-02-17 13:31:47 -080016from chromite.lib import cros_test_lib
Don Garrett86881cb2017-02-15 15:41:55 -080017from chromite.lib import osutils
Mike Frysingerf0146252019-09-02 13:31:05 -040018from chromite.lib import timeout_util
Don Garrett0c54ed72017-03-03 11:18:57 -080019from chromite.scripts import cbuildbot_launch
Don Garrett86881cb2017-02-15 15:41:55 -080020
Mike Frysinger3c831a72020-02-19 02:47:04 -050021
Don Garrett86881cb2017-02-15 15:41:55 -080022EXPECTED_MANIFEST_URL = 'https://chrome-internal-review.googlesource.com/chromeos/manifest-internal' # pylint: disable=line-too-long
Don Garrettc4114cc2016-11-01 20:04:06 -070023
24
Don Garrettacbb2392017-05-11 18:27:41 -070025# It's reasonable for unittests to look at internals.
26# pylint: disable=protected-access
27
28
Don Garrett8d314792017-05-18 13:11:42 -070029class FakeException(Exception):
30 """Test exception to raise during tests."""
31
32
Don Garrett0c54ed72017-03-03 11:18:57 -080033class CbuildbotLaunchTest(cros_test_lib.MockTestCase):
34 """Tests for cbuildbot_launch script."""
Don Garrettc4114cc2016-11-01 20:04:06 -070035
Don Garrett86881cb2017-02-15 15:41:55 -080036 def testPreParseArguments(self):
Don Garrettc4114cc2016-11-01 20:04:06 -070037 """Test that we can correctly extract branch values from cbuildbot args."""
Don Garrett597ddff2017-02-17 18:29:37 -080038 CASES = (
39 (['--buildroot', '/buildroot', 'daisy-incremental'],
40 (None, '/buildroot', None)),
41
42 (['--buildbot', '--buildroot', '/buildroot',
43 '--git-cache-dir', '/git-cache',
44 '-b', 'release-R57-9202.B',
45 'daisy-incremental'],
46 ('release-R57-9202.B', '/buildroot', '/git-cache')),
47
48 (['--debug', '--buildbot', '--notests',
49 '--buildroot', '/buildroot',
50 '--git-cache-dir', '/git-cache',
51 '--branch', 'release-R57-9202.B',
52 'daisy-incremental'],
53 ('release-R57-9202.B', '/buildroot', '/git-cache')),
Don Garrettc4114cc2016-11-01 20:04:06 -070054 )
55
Don Garrett597ddff2017-02-17 18:29:37 -080056 for cmd_args, expected in CASES:
57 expected_branch, expected_buildroot, expected_cache_dir = expected
58
Don Garrett0c54ed72017-03-03 11:18:57 -080059 options = cbuildbot_launch.PreParseArguments(cmd_args)
Don Garrett597ddff2017-02-17 18:29:37 -080060
61 self.assertEqual(options.branch, expected_branch)
62 self.assertEqual(options.buildroot, expected_buildroot)
63 self.assertEqual(options.git_cache_dir, expected_cache_dir)
Don Garrett86881cb2017-02-15 15:41:55 -080064
Don Garrettf324bc32017-05-23 14:00:53 -070065 def testInitialCheckout(self):
Don Garrett86881cb2017-02-15 15:41:55 -080066 """Test InitialCheckout with minimum settings."""
Don Garrettf324bc32017-05-23 14:00:53 -070067 mock_repo = mock.MagicMock()
68 mock_repo.branch = 'branch'
Mike Nichols9fb48832021-01-29 14:47:15 -070069 argv = ['-r', '/root', 'config']
70 options = cbuildbot_launch.PreParseArguments(argv)
Don Garrett86881cb2017-02-15 15:41:55 -080071
Mike Nichols9fb48832021-01-29 14:47:15 -070072 cbuildbot_launch.InitialCheckout(mock_repo, options)
Don Garrett86881cb2017-02-15 15:41:55 -080073
74 self.assertEqual(mock_repo.mock_calls, [
Mike Nichols53f0ab82021-08-26 02:19:37 +000075 mock.call.PreLoad('/preload/chromeos'),
Mike Nicholsfe1a5e62021-09-03 13:11:17 -040076 mock.call.Sync(jobs=32, detach=True, downgrade_repo=False),
Don Garrett8d314792017-05-18 13:11:42 -070077 ])
78
Don Garrettf15d65b2017-04-12 12:39:55 -070079 def testConfigureGlobalEnvironment(self):
Don Garrett60967922017-04-12 18:51:44 -070080 """Ensure that we can setup our global runtime environment correctly."""
Don Garrett86fec482017-05-17 18:13:33 -070081
82 os.environ.pop('LANG', None)
83 os.environ['LC_MONETARY'] = 'bad'
84
Don Garrettf15d65b2017-04-12 12:39:55 -070085 cbuildbot_launch.ConfigureGlobalEnvironment()
86
Don Garrett86fec482017-05-17 18:13:33 -070087 # Verify umask is updated.
Don Garrettf15d65b2017-04-12 12:39:55 -070088 self.assertEqual(os.umask(0), 0o22)
89
Don Garrett86fec482017-05-17 18:13:33 -070090 # Verify ENVs are cleaned up.
91 self.assertEqual(os.environ['LANG'], 'en_US.UTF-8')
92 self.assertNotIn('LC_MONETARY', os.environ)
93
Don Garrettf15d65b2017-04-12 12:39:55 -070094
Benjamin Gordon121a2aa2018-05-04 16:24:45 -060095class RunTests(cros_test_lib.RunCommandTestCase):
Don Garrett0c54ed72017-03-03 11:18:57 -080096 """Tests for cbuildbot_launch script."""
Don Garrett597ddff2017-02-17 18:29:37 -080097
98 ARGS_BASE = ['--buildroot', '/buildroot']
Don Garrett5cd946b2017-07-20 13:42:20 -070099 EXPECTED_ARGS_BASE = ['--buildroot', '/cbuildbot_buildroot']
Don Garrett597ddff2017-02-17 18:29:37 -0800100 ARGS_GIT_CACHE = ['--git-cache-dir', '/git-cache']
101 ARGS_CONFIG = ['config']
Don Garrettbf90cdf2017-05-19 15:54:02 -0700102 CMD = ['/cbuildbot_buildroot/chromite/bin/cbuildbot']
Don Garrett597ddff2017-02-17 18:29:37 -0800103
Don Garrett6e5c6b92018-04-06 17:58:49 -0700104 def verifyCbuildbot(self, args, expected_cmd, version):
Don Garrett86881cb2017-02-15 15:41:55 -0800105 """Ensure we invoke cbuildbot correctly."""
Don Garrett597ddff2017-02-17 18:29:37 -0800106 self.PatchObject(
Mike Frysingerc263d092019-09-18 15:11:47 -0400107 commands, 'GetTargetChromiteApiVersion', autospec=True,
Don Garrett597ddff2017-02-17 18:29:37 -0800108 return_value=version)
109
Don Garrett6e5c6b92018-04-06 17:58:49 -0700110 cbuildbot_launch.Cbuildbot('/cbuildbot_buildroot', '/depot_tools', args)
Don Garrett597ddff2017-02-17 18:29:37 -0800111
112 self.assertCommandCalled(
Don Garretta50bf492017-09-28 18:33:02 -0700113 expected_cmd, extra_env={'PATH': mock.ANY},
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500114 cwd='/cbuildbot_buildroot', check=False)
Don Garrett597ddff2017-02-17 18:29:37 -0800115
Don Garrett6e5c6b92018-04-06 17:58:49 -0700116 def testCbuildbotSimple(self):
Don Garrett597ddff2017-02-17 18:29:37 -0800117 """Ensure we invoke cbuildbot correctly."""
Don Garrett6e5c6b92018-04-06 17:58:49 -0700118 self.verifyCbuildbot(
Don Garrett597ddff2017-02-17 18:29:37 -0800119 self.ARGS_BASE + self.ARGS_CONFIG,
Don Garrettbf90cdf2017-05-19 15:54:02 -0700120 self.CMD + self.ARGS_CONFIG + self.EXPECTED_ARGS_BASE,
Don Garrett597ddff2017-02-17 18:29:37 -0800121 (0, 4))
122
Don Garrett6e5c6b92018-04-06 17:58:49 -0700123 def testCbuildbotNotFiltered(self):
Don Garrett597ddff2017-02-17 18:29:37 -0800124 """Ensure we invoke cbuildbot correctly."""
Don Garrett6e5c6b92018-04-06 17:58:49 -0700125 self.verifyCbuildbot(
Don Garrett597ddff2017-02-17 18:29:37 -0800126 self.ARGS_BASE + self.ARGS_CONFIG + self.ARGS_GIT_CACHE,
Don Garrettbf90cdf2017-05-19 15:54:02 -0700127 (self.CMD + self.ARGS_CONFIG + self.EXPECTED_ARGS_BASE +
128 self.ARGS_GIT_CACHE),
Don Garrett597ddff2017-02-17 18:29:37 -0800129 (0, 4))
130
Don Garrett6e5c6b92018-04-06 17:58:49 -0700131 def testCbuildbotFiltered(self):
Don Garrett597ddff2017-02-17 18:29:37 -0800132 """Ensure we invoke cbuildbot correctly."""
Don Garrett6e5c6b92018-04-06 17:58:49 -0700133 self.verifyCbuildbot(
Don Garrett597ddff2017-02-17 18:29:37 -0800134 self.ARGS_BASE + self.ARGS_CONFIG + self.ARGS_GIT_CACHE,
Don Garrettbf90cdf2017-05-19 15:54:02 -0700135 self.CMD + self.ARGS_CONFIG + self.EXPECTED_ARGS_BASE,
Don Garrett597ddff2017-02-17 18:29:37 -0800136 (0, 2))
Don Garrettc4114cc2016-11-01 20:04:06 -0700137
Don Garrett86881cb2017-02-15 15:41:55 -0800138 def testMainMin(self):
139 """Test a minimal set of command line options."""
Don Garrett597ddff2017-02-17 18:29:37 -0800140 self.PatchObject(osutils, 'SafeMakedirs', autospec=True)
Mike Frysingerc263d092019-09-18 15:11:47 -0400141 self.PatchObject(commands, 'GetTargetChromiteApiVersion',
Don Garrett861e9182017-05-15 15:30:23 -0700142 autospec=True, return_value=(constants.REEXEC_API_MAJOR,
143 constants.REEXEC_API_MINOR))
Don Garrettf324bc32017-05-23 14:00:53 -0700144 mock_repo = mock.MagicMock()
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000145 mock_repo.branch = 'main'
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000146 mock_repo.directory = '/root/repository'
Don Garrettbf90cdf2017-05-19 15:54:02 -0700147
Don Garrettf324bc32017-05-23 14:00:53 -0700148 mock_repo_create = self.PatchObject(repository, 'RepoRepository',
149 autospec=True, return_value=mock_repo)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000150 mock_clean = self.PatchObject(cbuildbot_launch, 'CleanBuildRoot',
151 autospec=True)
Don Garrett0c54ed72017-03-03 11:18:57 -0800152 mock_checkout = self.PatchObject(cbuildbot_launch, 'InitialCheckout',
Don Garrett86881cb2017-02-15 15:41:55 -0800153 autospec=True)
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700154 mock_cleanup_chroot = self.PatchObject(cbuildbot_launch, 'CleanupChroot',
155 autospec=True)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600156 mock_set_last_build_state = self.PatchObject(
157 cbuildbot_launch, 'SetLastBuildState', autospec=True)
158
159 expected_build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600160 build_number=0, master_build_id=0, status=mock.ANY,
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000161 buildroot_layout=2, branch='main')
Don Garrett7ade05a2017-02-17 13:31:47 -0800162
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000163 argv = ['-r', '/root', 'config']
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600164 options = cbuildbot_launch.PreParseArguments(argv)
165 cbuildbot_launch._main(options, argv)
Don Garrettc4114cc2016-11-01 20:04:06 -0700166
Don Garrettf324bc32017-05-23 14:00:53 -0700167 # Did we create the repo instance correctly?
168 self.assertEqual(mock_repo_create.mock_calls,
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000169 [mock.call(EXPECTED_MANIFEST_URL, '/root/repository',
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000170 git_cache_dir=None, branch='main')])
Don Garrettf324bc32017-05-23 14:00:53 -0700171
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000172 # Ensure we clean, as expected.
173 self.assertEqual(mock_clean.mock_calls, [
174 mock.call('/root', mock_repo, '/root/repository/.cache',
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400175 expected_build_state, False)])
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000176
Don Garrett86881cb2017-02-15 15:41:55 -0800177 # Ensure we checkout, as expected.
178 self.assertEqual(mock_checkout.mock_calls,
Mike Nichols9fb48832021-01-29 14:47:15 -0700179 [mock.call(mock_repo, options)])
Don Garrettc4114cc2016-11-01 20:04:06 -0700180
Don Garrett86881cb2017-02-15 15:41:55 -0800181 # Ensure we invoke cbuildbot, as expected.
182 self.assertCommandCalled(
Don Garrett5cd946b2017-07-20 13:42:20 -0700183 [
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000184 '/root/repository/chromite/bin/cbuildbot',
Don Garrett5cd946b2017-07-20 13:42:20 -0700185 'config',
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000186 '-r', '/root/repository',
Don Garrettb497f552018-07-09 16:01:13 -0700187 '--workspace', '/root/workspace',
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000188 '--cache-dir', '/root/repository/.cache',
Don Garrett61ce1ee2019-02-26 16:20:25 -0800189 # The duplication is a bug, but not harmful.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000190 '--cache-dir', '/root/repository/.cache',
Don Garrett5cd946b2017-07-20 13:42:20 -0700191 ],
Don Garretta50bf492017-09-28 18:33:02 -0700192 extra_env={'PATH': mock.ANY},
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000193 cwd='/root/repository',
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500194 check=False)
Don Garrettc4114cc2016-11-01 20:04:06 -0700195
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600196 # Ensure we saved the final state, as expected.
197 self.assertEqual(expected_build_state.status,
198 constants.BUILDER_STATUS_PASSED)
199 self.assertEqual(mock_set_last_build_state.mock_calls, [
200 mock.call('/root', expected_build_state)])
201
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700202 # Ensure we clean the chroot, as expected.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000203 mock_cleanup_chroot.assert_called_once_with('/root/repository')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700204
Don Garrett86881cb2017-02-15 15:41:55 -0800205 def testMainMax(self):
Don Garrett597ddff2017-02-17 18:29:37 -0800206 """Test a larger set of command line options."""
207 self.PatchObject(osutils, 'SafeMakedirs', autospec=True)
Mike Frysingerc263d092019-09-18 15:11:47 -0400208 self.PatchObject(commands, 'GetTargetChromiteApiVersion',
Don Garrett861e9182017-05-15 15:30:23 -0700209 autospec=True, return_value=(constants.REEXEC_API_MAJOR,
210 constants.REEXEC_API_MINOR))
Don Garrettf324bc32017-05-23 14:00:53 -0700211 mock_repo = mock.MagicMock()
212 mock_repo.branch = 'branch'
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000213 mock_repo.directory = '/root/repository'
Don Garrettbf90cdf2017-05-19 15:54:02 -0700214
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600215 mock_summary = build_summary.BuildSummary(
216 build_number=313,
217 master_build_id=123123123,
218 status=constants.BUILDER_STATUS_FAILED,
219 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
220 branch='branch')
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600221
222 mock_get_last_build_state = self.PatchObject(
223 cbuildbot_launch, 'GetLastBuildState', autospec=True,
224 return_value=mock_summary)
Don Garrettf324bc32017-05-23 14:00:53 -0700225 mock_repo_create = self.PatchObject(repository, 'RepoRepository',
226 autospec=True, return_value=mock_repo)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000227 mock_clean = self.PatchObject(cbuildbot_launch, 'CleanBuildRoot',
228 autospec=True)
Don Garrett0c54ed72017-03-03 11:18:57 -0800229 mock_checkout = self.PatchObject(cbuildbot_launch, 'InitialCheckout',
Don Garrett86881cb2017-02-15 15:41:55 -0800230 autospec=True)
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700231 mock_cleanup_chroot = self.PatchObject(cbuildbot_launch, 'CleanupChroot',
232 autospec=True)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600233 mock_set_last_build_state = self.PatchObject(
234 cbuildbot_launch, 'SetLastBuildState', autospec=True)
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600235 argv = ['--buildroot', '/root',
236 '--branch', 'branch',
237 '--git-cache-dir', '/git-cache',
Don Garrett61ce1ee2019-02-26 16:20:25 -0800238 '--cache-dir', '/cache',
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600239 '--remote-trybot',
240 '--master-build-id', '123456789',
241 '--buildnumber', '314',
242 'config']
243 options = cbuildbot_launch.PreParseArguments(argv)
244 cbuildbot_launch._main(options, argv)
Don Garrettc4114cc2016-11-01 20:04:06 -0700245
Don Garrettf324bc32017-05-23 14:00:53 -0700246 # Did we create the repo instance correctly?
247 self.assertEqual(mock_repo_create.mock_calls,
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000248 [mock.call(EXPECTED_MANIFEST_URL, '/root/repository',
Don Garrett33872502018-08-03 22:30:40 +0000249 git_cache_dir='/git-cache', branch='branch')])
Don Garrettf324bc32017-05-23 14:00:53 -0700250
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600251 # Ensure we look up the previous status.
252 self.assertEqual(mock_get_last_build_state.mock_calls, [
253 mock.call('/root')])
254
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000255 # Ensure we clean, as expected.
256 self.assertEqual(mock_clean.mock_calls, [
257 mock.call('/root',
258 mock_repo,
259 '/cache',
260 build_summary.BuildSummary(
261 build_number=314,
262 master_build_id=123456789,
263 status=mock.ANY,
264 branch='branch',
265 buildroot_layout=2
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400266 ), False)])
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000267
Don Garrett86881cb2017-02-15 15:41:55 -0800268 # Ensure we checkout, as expected.
269 self.assertEqual(mock_checkout.mock_calls,
Mike Nichols9fb48832021-01-29 14:47:15 -0700270 [mock.call(mock_repo, options)])
Don Garrett86881cb2017-02-15 15:41:55 -0800271
272 # Ensure we invoke cbuildbot, as expected.
273 self.assertCommandCalled(
Don Garrett5cd946b2017-07-20 13:42:20 -0700274 [
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000275 '/root/repository/chromite/bin/cbuildbot',
Don Garrett5cd946b2017-07-20 13:42:20 -0700276 'config',
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000277 '--buildroot', '/root/repository',
Don Garrett5cd946b2017-07-20 13:42:20 -0700278 '--branch', 'branch',
279 '--git-cache-dir', '/git-cache',
Don Garrett61ce1ee2019-02-26 16:20:25 -0800280 '--cache-dir', '/cache',
Don Garrett5cd946b2017-07-20 13:42:20 -0700281 '--remote-trybot',
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600282 '--master-build-id', '123456789',
283 '--buildnumber', '314',
284 '--previous-build-state',
Mike Frysingerd0960812020-06-09 01:53:32 -0400285 'eyJicmFuY2giOiJicmFuY2giLCJidWlsZF9udW1iZXIiOjMxMywiYnVpbGRyb290X'
286 '2xheW91dCI6MiwibWFzdGVyX2J1aWxkX2lkIjoxMjMxMjMxMjMsInN0YXR1cyI6Im'
287 'ZhaWwifQ==',
Don Garrettb497f552018-07-09 16:01:13 -0700288 '--workspace', '/root/workspace',
Don Garrett61ce1ee2019-02-26 16:20:25 -0800289 '--cache-dir', '/cache',
Don Garrett5cd946b2017-07-20 13:42:20 -0700290 ],
Don Garretta50bf492017-09-28 18:33:02 -0700291 extra_env={'PATH': mock.ANY},
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000292 cwd='/root/repository',
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500293 check=False)
Don Garrett7ade05a2017-02-17 13:31:47 -0800294
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600295 # Ensure we write the final build state, as expected.
296 final_state = build_summary.BuildSummary(
297 build_number=314,
298 master_build_id=123456789,
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600299 status=constants.BUILDER_STATUS_PASSED,
300 buildroot_layout=2,
301 branch='branch')
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600302 self.assertEqual(mock_set_last_build_state.mock_calls, [
303 mock.call('/root', final_state)])
304
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700305 # Ensure we clean the chroot, as expected.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000306 mock_cleanup_chroot.assert_called_once_with('/root/repository')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700307
Don Garrett7ade05a2017-02-17 13:31:47 -0800308
Don Garrettbf90cdf2017-05-19 15:54:02 -0700309class CleanBuildRootTest(cros_test_lib.MockTempDirTestCase):
310 """Tests for CleanBuildRoot method."""
Don Garrett7ade05a2017-02-17 13:31:47 -0800311
312 def setUp(self):
313 """Create standard buildroot contents for cleanup."""
Don Garrettbf90cdf2017-05-19 15:54:02 -0700314 self.root = os.path.join(self.tempdir)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600315 self.previous_build_state = os.path.join(
316 self.root, '.cbuildbot_build_state.json')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700317 self.buildroot = os.path.join(self.root, 'buildroot')
318 self.repo = os.path.join(self.buildroot, '.repo/repo')
Don Garrett36650112018-06-28 15:54:34 -0700319 self.chroot = os.path.join(self.buildroot, 'chroot')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700320 self.general = os.path.join(self.buildroot, 'general/general')
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700321 self.cache = os.path.join(self.buildroot, '.cache')
322 self.distfiles = os.path.join(self.cache, 'distfiles')
Don Garrett7ade05a2017-02-17 13:31:47 -0800323
Don Garrett4166d182018-12-17 12:52:02 -0800324 self.mock_repo = mock.Mock(repository.RepoRepository)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700325 self.mock_repo.directory = self.buildroot
Don Garrettf324bc32017-05-23 14:00:53 -0700326
Benjamin Gordon8642bcc2018-05-01 13:49:56 -0600327 def populateBuildroot(self, previous_build_state=None):
Don Garrett7ade05a2017-02-17 13:31:47 -0800328 """Create standard buildroot contents for cleanup."""
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600329 if previous_build_state:
330 osutils.SafeMakedirs(self.root)
331 osutils.WriteFile(self.previous_build_state, previous_build_state)
332
Don Garrett7ade05a2017-02-17 13:31:47 -0800333 # Create files.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700334 for f in (self.repo, self.chroot, self.general, self.distfiles):
Don Garrette17e1d92017-04-12 15:28:19 -0700335 osutils.Touch(f, makedirs=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800336
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000337 def testNoBuildroot(self):
338 """Test CleanBuildRoot with no history."""
339 self.mock_repo.branch = 'main'
340
341 build_state = build_summary.BuildSummary(
342 status=constants.BUILDER_STATUS_INFLIGHT,
343 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
344 branch='main')
345 cbuildbot_launch.CleanBuildRoot(
346 self.root, self.mock_repo, self.cache, build_state)
347
348 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
349 self.assertEqual(new_summary.buildroot_layout, 2)
350 self.assertEqual(new_summary.branch, 'main')
351 self.assertIsNotNone(new_summary.distfiles_ts)
352 self.assertEqual(new_summary, build_state)
353
354 self.assertExists(self.previous_build_state)
355
356 def testBuildrootNoState(self):
357 """Test CleanBuildRoot with no state information."""
358 self.populateBuildroot()
359 self.mock_repo.branch = 'main'
360
361 build_state = build_summary.BuildSummary(
362 status=constants.BUILDER_STATUS_INFLIGHT,
363 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
364 branch='main')
365 cbuildbot_launch.CleanBuildRoot(
366 self.root, self.mock_repo, self.cache, build_state)
367
368 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
369 self.assertEqual(new_summary.buildroot_layout, 2)
370 self.assertEqual(new_summary.branch, 'main')
371 self.assertIsNotNone(new_summary.distfiles_ts)
372 self.assertEqual(new_summary, build_state)
373
374 self.assertNotExists(self.repo)
375 self.assertNotExists(self.chroot)
376 self.assertNotExists(self.general)
377 self.assertNotExists(self.distfiles)
378 self.assertExists(self.previous_build_state)
379
380 def testBuildrootFormatMismatch(self):
381 """Test CleanBuildRoot with buildroot layout mismatch."""
382 old_build_state = build_summary.BuildSummary(
383 status=constants.BUILDER_STATUS_PASSED,
384 buildroot_layout=1,
385 branch='main')
386 self.populateBuildroot(previous_build_state=old_build_state.to_json())
387 self.mock_repo.branch = 'main'
388
389 build_state = build_summary.BuildSummary(
390 status=constants.BUILDER_STATUS_INFLIGHT,
391 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
392 branch='main')
393 cbuildbot_launch.CleanBuildRoot(
394 self.root, self.mock_repo, self.cache, build_state)
395
396 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
397 self.assertEqual(new_summary.buildroot_layout, 2)
398 self.assertEqual(new_summary.branch, 'main')
399 self.assertIsNotNone(new_summary.distfiles_ts)
400 self.assertEqual(new_summary, build_state)
401
402 self.assertNotExists(self.repo)
403 self.assertNotExists(self.chroot)
404 self.assertNotExists(self.general)
405 self.assertNotExists(self.distfiles)
406 self.assertExists(self.previous_build_state)
407
408 def testBuildrootBranchChange(self):
409 """Test CleanBuildRoot with a change in branches."""
410 old_build_state = build_summary.BuildSummary(
411 status=constants.BUILDER_STATUS_PASSED,
412 buildroot_layout=2,
413 branch='branchA')
414 self.populateBuildroot(previous_build_state=old_build_state.to_json())
415 self.mock_repo.branch = 'branchB'
416 m = self.PatchObject(cros_sdk_lib, 'CleanupChrootMount')
417
418 build_state = build_summary.BuildSummary(
419 status=constants.BUILDER_STATUS_INFLIGHT,
420 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
421 branch='branchB')
422 cbuildbot_launch.CleanBuildRoot(
423 self.root, self.mock_repo, self.cache, build_state)
424
425 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
426 self.assertEqual(new_summary.buildroot_layout, 2)
427 self.assertEqual(new_summary.branch, 'branchB')
428 self.assertIsNotNone(new_summary.distfiles_ts)
429 self.assertEqual(new_summary, build_state)
430
431 # self.assertExists(self.repo)
432 self.assertExists(self.general)
433 self.assertNotExists(self.distfiles)
434 self.assertExists(self.previous_build_state)
435 m.assert_called_with(self.chroot, delete=True)
436
437 def testBuildrootBranchMatch(self):
438 """Test CleanBuildRoot with no change in branch."""
439 old_build_state = build_summary.BuildSummary(
440 status=constants.BUILDER_STATUS_PASSED,
441 buildroot_layout=2,
442 branch='branchA')
443 self.populateBuildroot(previous_build_state=old_build_state.to_json())
444 self.mock_repo.branch = 'branchA'
445
446 build_state = build_summary.BuildSummary(
447 status=constants.BUILDER_STATUS_INFLIGHT,
448 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
449 branch='branchA')
450 cbuildbot_launch.CleanBuildRoot(
451 self.root, self.mock_repo, self.cache, build_state)
452
453 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
454 self.assertEqual(new_summary.buildroot_layout, 2)
455 self.assertEqual(new_summary.branch, 'branchA')
456 self.assertIsNotNone(new_summary.distfiles_ts)
457 self.assertEqual(new_summary, build_state)
458
459 self.assertExists(self.repo)
460 self.assertExists(self.chroot)
461 self.assertExists(self.general)
462 self.assertExists(self.distfiles)
463 self.assertExists(self.previous_build_state)
464
465 def testBuildrootGitLocksPrevPass(self):
466 """Verify not CleanStaleLocks, if previous build was in passed."""
467 old_build_state = build_summary.BuildSummary(
468 status=constants.BUILDER_STATUS_PASSED,
469 buildroot_layout=2,
470 branch='branchA')
471 self.populateBuildroot(previous_build_state=old_build_state.to_json())
472 self.mock_repo.branch = 'branchA'
473
474 build_state = build_summary.BuildSummary(
475 status=constants.BUILDER_STATUS_INFLIGHT,
476 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
477 branch='branchA')
478 cbuildbot_launch.CleanBuildRoot(
479 self.root, self.mock_repo, self.cache, build_state)
480
481 self.assertEqual(
482 self.mock_repo.mock_calls, [
483 mock.call.PreLoad(),
484 mock.call.BuildRootGitCleanup(prune_all=True),
485 ])
486
487 def testBuildrootGitLocksPrevFail(self):
488 """Verify not CleanStaleLocks, if previous build was in failed."""
489 old_build_state = build_summary.BuildSummary(
490 status=constants.BUILDER_STATUS_FAILED,
491 buildroot_layout=2,
492 branch='branchA')
493 self.populateBuildroot(previous_build_state=old_build_state.to_json())
494 self.mock_repo.branch = 'branchA'
495
496 build_state = build_summary.BuildSummary(
497 status=constants.BUILDER_STATUS_INFLIGHT,
498 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
499 branch='branchA')
500 cbuildbot_launch.CleanBuildRoot(
501 self.root, self.mock_repo, self.cache, build_state)
502
503 self.assertEqual(
504 self.mock_repo.mock_calls, [
505 mock.call.PreLoad(),
506 mock.call.BuildRootGitCleanup(prune_all=True),
507 ])
508
509 def testBuildrootGitLocksPrevInFlight(self):
510 """Verify CleanStaleLocks, if previous build was in flight."""
511 old_build_state = build_summary.BuildSummary(
512 status=constants.BUILDER_STATUS_INFLIGHT,
513 buildroot_layout=2,
514 branch='branchA')
515 self.populateBuildroot(previous_build_state=old_build_state.to_json())
516 self.mock_repo.branch = 'branchA'
517
518 build_state = build_summary.BuildSummary(
519 status=constants.BUILDER_STATUS_INFLIGHT,
520 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
521 branch='branchA')
522 cbuildbot_launch.CleanBuildRoot(
523 self.root, self.mock_repo, self.cache, build_state)
524
525
526 self.assertEqual(
527 self.mock_repo.method_calls, [
528 mock.call.PreLoad(),
529 mock.call.CleanStaleLocks(),
530 mock.call.BuildRootGitCleanup(prune_all=True),
531 ])
532
533 def testBuildrootDistfilesRecentCache(self):
534 """Test CleanBuildRoot does not delete distfiles when cache is recent."""
535 seed_distfiles_ts = time.time() - 60
536 old_build_state = build_summary.BuildSummary(
537 status=constants.BUILDER_STATUS_PASSED,
538 buildroot_layout=2,
539 branch='branchA',
540 distfiles_ts=seed_distfiles_ts)
541 self.populateBuildroot(previous_build_state=old_build_state.to_json())
542 self.mock_repo.branch = 'branchA'
543
544 build_state = build_summary.BuildSummary(
545 status=constants.BUILDER_STATUS_INFLIGHT,
546 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
547 branch='branchA')
548 cbuildbot_launch.CleanBuildRoot(
549 self.root, self.mock_repo, self.cache, build_state)
550
551 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
552 self.assertEqual(new_summary.buildroot_layout, 2)
553 self.assertEqual(new_summary.branch, 'branchA')
554 # Same cache creation timestamp is rewritten to state.
555 self.assertEqual(new_summary.distfiles_ts, seed_distfiles_ts)
556 self.assertEqual(new_summary, build_state)
557
558 self.assertExists(self.repo)
559 self.assertExists(self.chroot)
560 self.assertExists(self.general)
561 self.assertExists(self.distfiles)
562 self.assertExists(self.previous_build_state)
563
564 def testBuildrootDistfilesCacheExpired(self):
565 """Test CleanBuildRoot when the distfiles cache is too old."""
566 old_build_state = build_summary.BuildSummary(
567 status=constants.BUILDER_STATUS_PASSED,
568 buildroot_layout=2,
569 branch='branchA',
570 distfiles_ts=100.0)
571 self.populateBuildroot(previous_build_state=old_build_state.to_json())
572 self.mock_repo.branch = 'branchA'
573
574 build_state = build_summary.BuildSummary(
575 status=constants.BUILDER_STATUS_INFLIGHT,
576 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
577 branch='branchA')
578 cbuildbot_launch.CleanBuildRoot(
579 self.root, self.mock_repo, self.cache, build_state)
580
581 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
582 self.assertEqual(new_summary.buildroot_layout, 2)
583 self.assertEqual(new_summary.branch, 'branchA')
584 self.assertIsNotNone(new_summary.distfiles_ts)
585 self.assertEqual(new_summary, build_state)
586
587 self.assertExists(self.repo)
588 self.assertExists(self.chroot)
589 self.assertExists(self.general)
590 self.assertNotExists(self.distfiles)
591 self.assertExists(self.previous_build_state)
592
593 def testRootOwnedCache(self):
594 """Test CleanBuildRoot with no history."""
595 seed_distfiles_ts = time.time() - 60
596 old_build_state = build_summary.BuildSummary(
597 status=constants.BUILDER_STATUS_PASSED,
598 buildroot_layout=2,
599 branch='branchA',
600 distfiles_ts=seed_distfiles_ts)
601 self.populateBuildroot(previous_build_state=old_build_state.to_json())
602 self.mock_repo.branch = 'branchA'
603
604 osutils.Chown(self.cache, 'root', 'root')
605
606 build_state = build_summary.BuildSummary(
607 status=constants.BUILDER_STATUS_INFLIGHT,
608 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
609 branch='branchA')
610 cbuildbot_launch.CleanBuildRoot(
611 self.root, self.mock_repo, self.cache, build_state)
612
613 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
614 self.assertEqual(new_summary.buildroot_layout, 2)
615 self.assertEqual(new_summary.branch, 'branchA')
616 # Same cache creation timestamp is rewritten to state.
617 self.assertEqual(new_summary.distfiles_ts, seed_distfiles_ts)
618 self.assertEqual(new_summary, build_state)
619
620 self.assertExists(self.repo)
621 self.assertExists(self.chroot)
622 self.assertExists(self.general)
623 self.assertNotExists(self.distfiles)
624 self.assertExists(self.previous_build_state)
625
626 def testBuildrootRepoCleanFailure(self):
627 """Test CleanBuildRoot with repo checkout failure."""
628 old_build_state = build_summary.BuildSummary(
629 status=constants.BUILDER_STATUS_PASSED,
630 buildroot_layout=1,
631 branch='branchA')
632 self.populateBuildroot(previous_build_state=old_build_state.to_json())
633 self.mock_repo.branch = 'branchA'
634 self.mock_repo.BuildRootGitCleanup.side_effect = Exception
635
636 build_state = build_summary.BuildSummary(
637 status=constants.BUILDER_STATUS_INFLIGHT,
638 buildroot_layout=cbuildbot_launch.BUILDROOT_BUILDROOT_LAYOUT,
639 branch='branchA')
640 cbuildbot_launch.CleanBuildRoot(
641 self.root, self.mock_repo, self.cache, build_state)
642
643 new_summary = cbuildbot_launch.GetLastBuildState(self.root)
644 self.assertEqual(new_summary.buildroot_layout, 2)
645 self.assertEqual(new_summary.branch, 'branchA')
646 self.assertIsNotNone(new_summary.distfiles_ts)
647 self.assertEqual(new_summary, build_state)
648
649 self.assertNotExists(self.repo)
650 self.assertNotExists(self.chroot)
651 self.assertNotExists(self.general)
652 self.assertNotExists(self.distfiles)
653 self.assertExists(self.previous_build_state)
654
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600655 def testGetCurrentBuildStateNoArgs(self):
656 """Tests GetCurrentBuildState without arguments."""
657 options = cbuildbot_launch.PreParseArguments([
658 '--buildroot', self.root, 'config'
659 ])
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000660 state = cbuildbot_launch.GetCurrentBuildState(options, 'main')
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600661
662 expected_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600663 status=constants.BUILDER_STATUS_INFLIGHT,
664 buildroot_layout=2,
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000665 branch='main')
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600666 self.assertEqual(state, expected_state)
667
668 def testGetCurrentBuildStateHasArgs(self):
669 """Tests GetCurrentBuildState with arguments."""
670 options = cbuildbot_launch.PreParseArguments([
671 '--buildroot', self.root,
672 '--buildnumber', '20',
673 '--master-build-id', '50',
674 'config'
675 ])
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600676 state = cbuildbot_launch.GetCurrentBuildState(options, 'branchA')
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600677
678 expected_state = build_summary.BuildSummary(
679 build_number=20,
680 master_build_id=50,
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600681 status=constants.BUILDER_STATUS_INFLIGHT,
682 buildroot_layout=2,
683 branch='branchA')
684 self.assertEqual(state, expected_state)
685
686 def testGetCurrentBuildStateLayout(self):
687 """Test that GetCurrentBuildState uses the current buildroot layout."""
688 # Change to a future version.
689 self.PatchObject(cbuildbot_launch, 'BUILDROOT_BUILDROOT_LAYOUT', 22)
690
691 options = cbuildbot_launch.PreParseArguments([
692 '--buildroot', self.root, 'config'
693 ])
694 state = cbuildbot_launch.GetCurrentBuildState(options, 'branchA')
695
696 expected_state = build_summary.BuildSummary(
697 status=constants.BUILDER_STATUS_INFLIGHT,
698 buildroot_layout=22,
699 branch='branchA')
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600700 self.assertEqual(state, expected_state)
701
702 def testGetLastBuildStateNoFile(self):
703 """Tests GetLastBuildState if the file is missing."""
704 osutils.SafeMakedirs(self.root)
705 state = cbuildbot_launch.GetLastBuildState(self.root)
706 self.assertEqual(state, build_summary.BuildSummary())
707
708 def testGetLastBuildStateBadFile(self):
709 """Tests GetLastBuildState if the file contains invalid JSON."""
710 osutils.SafeMakedirs(self.root)
711 osutils.WriteFile(self.previous_build_state, '}}')
712 state = cbuildbot_launch.GetLastBuildState(self.root)
713 self.assertEqual(state, build_summary.BuildSummary())
714
715 def testGetLastBuildStateMissingBuildStatus(self):
716 """Tests GetLastBuildState if the file doesn't have a valid status."""
717 osutils.SafeMakedirs(self.root)
718 osutils.WriteFile(self.previous_build_state, '{"build_number": "3"}')
719 state = cbuildbot_launch.GetLastBuildState(self.root)
720 self.assertEqual(state, build_summary.BuildSummary())
721
722 def testGetLastBuildStateGoodFile(self):
723 """Tests GetLastBuildState on a good file."""
724 osutils.SafeMakedirs(self.root)
725 osutils.WriteFile(
726 self.previous_build_state,
727 '{"build_number": 1, "master_build_id": 3, "status": "pass"}')
728 state = cbuildbot_launch.GetLastBuildState(self.root)
729 self.assertEqual(
730 state,
731 build_summary.BuildSummary(
732 build_number=1, master_build_id=3, status='pass'))
733
734 def testSetLastBuildState(self):
735 """Verifies that SetLastBuildState writes to the expected file."""
736 osutils.SafeMakedirs(self.root)
737 old_state = build_summary.BuildSummary(
738 build_number=314,
739 master_build_id=2178,
740 status=constants.BUILDER_STATUS_PASSED)
741 cbuildbot_launch.SetLastBuildState(self.root, old_state)
742
743 saved_state = osutils.ReadFile(self.previous_build_state)
744 new_state = build_summary.BuildSummary()
745 new_state.from_json(saved_state)
746
747 self.assertEqual(old_state, new_state)
Mike Frysingerf0146252019-09-02 13:31:05 -0400748
749 def testCleanupChrootNoChroot(self):
750 """Check CleanupChroot without a chroot."""
751 self.StartPatcher(cros_test_lib.RunCommandMock())
752 with mock.patch.object(cros_sdk_lib, 'CleanupChrootMount'):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400753 cbuildbot_launch.CleanupChroot(self.buildroot)
Mike Frysingerf0146252019-09-02 13:31:05 -0400754
755 def testCleanupChrootNormal(self):
756 """Check normal CleanupChroot."""
757 osutils.SafeMakedirs(self.chroot)
758 osutils.Touch(self.chroot + '.img')
759 self.StartPatcher(cros_test_lib.RunCommandMock())
760 with mock.patch.object(cros_sdk_lib, 'CleanupChrootMount'):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400761 cbuildbot_launch.CleanupChroot(self.buildroot)
Mike Frysingerf0146252019-09-02 13:31:05 -0400762
763 def testCleanupChrootTimeout(self):
764 """Check timeouts in CleanupChroot."""
765 osutils.SafeMakedirs(self.chroot)
766 osutils.Touch(self.chroot + '.img')
767 rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock())
768 rc_mock.SetDefaultCmdResult()
769 with mock.patch.object(cros_sdk_lib, 'CleanupChrootMount',
770 side_effect=timeout_util.TimeoutError):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400771 cbuildbot_launch.CleanupChroot(self.buildroot)