blob: 6ec7a234f6950bdb5a058c5265decb4316719f50 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Steve Funge984a532013-11-25 17:09:25 -08005"""Unit tests for the deploy_chrome script."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07006
Anushruth8d797672019-10-17 12:22:31 -07007import errno
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07008import os
David James88e6f032013-03-02 08:13:20 -08009import time
Mike Frysinger166fea02021-02-12 05:30:33 -050010from unittest import mock
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070011
David Pursellcfd58872015-03-19 09:15:48 -070012from chromite.cli.cros import cros_chrome_sdk_unittest
Ryan Cuief91e702013-02-04 12:06:36 -080013from chromite.lib import chrome_util
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070014from chromite.lib import cros_build_lib
15from chromite.lib import cros_test_lib
Jae Hoon Kimdf842912022-05-19 06:40:42 +000016from chromite.lib import gs
Ryan Cui686ec052013-02-12 16:39:41 -080017from chromite.lib import osutils
Erik Chen75a2f492020-08-06 19:15:11 -070018from chromite.lib import parallel_unittest
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070019from chromite.lib import partial_mock
Robert Flack1dc7ea82014-11-26 13:50:24 -050020from chromite.lib import remote_access
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070021from chromite.lib import remote_access_unittest
22from chromite.scripts import deploy_chrome
23
Ryan Cuief91e702013-02-04 12:06:36 -080024
Mike Frysinger27e21b72018-07-12 14:20:21 -040025# pylint: disable=protected-access
26
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070027
Alex Klein1699fab2022-09-08 08:46:06 -060028_REGULAR_TO = ("--device", "monkey")
29_TARGET_BOARD = "eve"
30_GS_PATH = "gs://foon"
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070031
32
33def _ParseCommandLine(argv):
Alex Klein1699fab2022-09-08 08:46:06 -060034 return deploy_chrome._ParseCommandLine(["--log-level", "debug"] + argv)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070035
36
37class InterfaceTest(cros_test_lib.OutputTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060038 """Tests the commandline interface of the script."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070039
Alex Klein1699fab2022-09-08 08:46:06 -060040 def testGsLocalPathUnSpecified(self):
41 """Test no chrome path specified."""
42 with self.OutputCapturer():
43 self.assertRaises2(
44 SystemExit,
45 _ParseCommandLine,
46 list(_REGULAR_TO) + ["--board", _TARGET_BOARD],
47 check_attrs={"code": 2},
48 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070049
Alex Klein1699fab2022-09-08 08:46:06 -060050 def testBuildDirSpecified(self):
51 """Test case of build dir specified."""
52 argv = list(_REGULAR_TO) + [
53 "--board",
54 _TARGET_BOARD,
55 "--build-dir",
56 "/path/to/chrome",
57 ]
58 _ParseCommandLine(argv)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090059
Alex Klein1699fab2022-09-08 08:46:06 -060060 def testBuildDirSpecifiedWithoutBoard(self):
61 """Test case of build dir specified without --board."""
62 argv = list(_REGULAR_TO) + [
63 "--build-dir",
64 "/path/to/chrome/out_" + _TARGET_BOARD + "/Release",
65 ]
66 options = _ParseCommandLine(argv)
67 self.assertEqual(options.board, _TARGET_BOARD)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090068
Alex Klein1699fab2022-09-08 08:46:06 -060069 def testBuildDirSpecifiedWithoutBoardError(self):
70 """Test case of irregular build dir specified without --board."""
71 argv = list(_REGULAR_TO) + ["--build-dir", "/path/to/chrome/foo/bar"]
72 self.assertParseError(argv)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090073
Alex Klein1699fab2022-09-08 08:46:06 -060074 def testGsPathSpecified(self):
75 """Test case of GS path specified."""
76 argv = list(_REGULAR_TO) + [
77 "--board",
78 _TARGET_BOARD,
79 "--gs-path",
80 _GS_PATH,
81 ]
82 _ParseCommandLine(argv)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070083
Alex Klein1699fab2022-09-08 08:46:06 -060084 def testLocalPathSpecified(self):
85 """Test case of local path specified."""
86 argv = list(_REGULAR_TO) + [
87 "--board",
88 _TARGET_BOARD,
89 "--local-pkg-path",
90 "/path/to/chrome",
91 ]
92 _ParseCommandLine(argv)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070093
Alex Klein1699fab2022-09-08 08:46:06 -060094 def testNoBoard(self):
95 """Test no board specified."""
96 argv = list(_REGULAR_TO) + ["--gs-path", _GS_PATH]
97 self.assertParseError(argv)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090098
Alex Klein1699fab2022-09-08 08:46:06 -060099 def testNoTarget(self):
100 """Test no target specified."""
101 argv = ["--board", _TARGET_BOARD, "--gs-path", _GS_PATH]
102 self.assertParseError(argv)
Ryan Cuief91e702013-02-04 12:06:36 -0800103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 def testLacros(self):
105 """Test basic lacros invocation."""
106 argv = [
107 "--lacros",
108 "--build-dir",
109 "/path/to/nowhere",
110 "--device",
111 "monkey",
112 "--board",
113 "atlas",
114 ]
115 options = _ParseCommandLine(argv)
116 self.assertTrue(options.lacros)
117 self.assertEqual(options.target_dir, deploy_chrome.LACROS_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 def testLacrosNoStrip(self):
120 """Test lacros invocation with nostrip."""
121 argv = [
122 "--lacros",
123 "--nostrip",
124 "--build-dir",
125 "/path/to/nowhere",
126 "--device",
127 "monkey",
128 ]
129 options = _ParseCommandLine(argv)
130 self.assertTrue(options.lacros)
131 self.assertFalse(options.dostrip)
132 self.assertEqual(options.target_dir, deploy_chrome.LACROS_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700133
Alex Klein1699fab2022-09-08 08:46:06 -0600134 def assertParseError(self, argv):
135 with self.OutputCapturer():
136 self.assertRaises2(
137 SystemExit, _ParseCommandLine, argv, check_attrs={"code": 2}
138 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 def testMountOptionSetsTargetDir(self):
141 argv = list(_REGULAR_TO) + [
142 "--board",
143 _TARGET_BOARD,
144 "--gs-path",
145 _GS_PATH,
146 "--mount",
147 ]
148 options = _ParseCommandLine(argv)
149 self.assertIsNot(options.target_dir, None)
Thiago Goncales12793312013-05-23 11:26:17 -0700150
Alex Klein1699fab2022-09-08 08:46:06 -0600151 def testMountOptionSetsMountDir(self):
152 argv = list(_REGULAR_TO) + [
153 "--board",
154 _TARGET_BOARD,
155 "--gs-path",
156 _GS_PATH,
157 "--mount",
158 ]
159 options = _ParseCommandLine(argv)
160 self.assertIsNot(options.mount_dir, None)
Thiago Goncales12793312013-05-23 11:26:17 -0700161
Alex Klein1699fab2022-09-08 08:46:06 -0600162 def testMountOptionDoesNotOverrideTargetDir(self):
163 argv = list(_REGULAR_TO) + [
164 "--board",
165 _TARGET_BOARD,
166 "--gs-path",
167 _GS_PATH,
168 "--mount",
169 "--target-dir",
170 "/foo/bar/cow",
171 ]
172 options = _ParseCommandLine(argv)
173 self.assertEqual(options.target_dir, "/foo/bar/cow")
Thiago Goncales12793312013-05-23 11:26:17 -0700174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 def testMountOptionDoesNotOverrideMountDir(self):
176 argv = list(_REGULAR_TO) + [
177 "--board",
178 _TARGET_BOARD,
179 "--gs-path",
180 _GS_PATH,
181 "--mount",
182 "--mount-dir",
183 "/foo/bar/cow",
184 ]
185 options = _ParseCommandLine(argv)
186 self.assertEqual(options.mount_dir, "/foo/bar/cow")
Thiago Goncales12793312013-05-23 11:26:17 -0700187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 def testSshIdentityOptionSetsOption(self):
189 argv = list(_REGULAR_TO) + [
190 "--board",
191 _TARGET_BOARD,
192 "--private-key",
193 "/foo/bar/key",
194 "--build-dir",
195 "/path/to/nowhere",
196 ]
197 options = _ParseCommandLine(argv)
198 self.assertEqual(options.private_key, "/foo/bar/key")
199
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700200
201class DeployChromeMock(partial_mock.PartialMock):
Alex Klein1699fab2022-09-08 08:46:06 -0600202 """Deploy Chrome Mock Class."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 TARGET = "chromite.scripts.deploy_chrome.DeployChrome"
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000205 ATTRS = (
206 "_ChromeFileInUse",
207 "_DisableRootfsVerification",
208 "_ShouldUseCompressedAsh",
209 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700210
Alex Klein1699fab2022-09-08 08:46:06 -0600211 def __init__(self):
212 partial_mock.PartialMock.__init__(self)
213 self.remote_device_mock = remote_access_unittest.RemoteDeviceMock()
214 # Target starts off as having rootfs verification enabled.
215 self.rsh_mock = remote_access_unittest.RemoteShMock()
216 self.rsh_mock.SetDefaultCmdResult(0)
217 self.MockMountCmd(1)
218 self.rsh_mock.AddCmdResult(
219 deploy_chrome.LSOF_COMMAND_CHROME % (deploy_chrome._CHROME_DIR,), 1
220 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700221
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000222 self.rsh_mock.AddCmdResult(
223 "status ui", stdout="ui start/running, process 123"
224 )
225
Alex Klein1699fab2022-09-08 08:46:06 -0600226 def MockMountCmd(self, returnvalue):
227 self.rsh_mock.AddCmdResult(deploy_chrome.MOUNT_RW_COMMAND, returnvalue)
David James88e6f032013-03-02 08:13:20 -0800228
Alex Klein1699fab2022-09-08 08:46:06 -0600229 def _DisableRootfsVerification(self, inst):
230 with mock.patch.object(time, "sleep"):
231 self.backup["_DisableRootfsVerification"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 def PreStart(self):
234 self.remote_device_mock.start()
235 self.rsh_mock.start()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700236
Alex Klein1699fab2022-09-08 08:46:06 -0600237 def PreStop(self):
238 self.rsh_mock.stop()
239 self.remote_device_mock.stop()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700240
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000241 def _ChromeFileInUse(self, _inst):
242 # Fully stub out for now. Can be replaced if further testing is added.
243 return False
244
245 def _ShouldUseCompressedAsh(self, inst):
246 with mock.patch.object(
247 remote_access.RemoteDevice, "IfFileExists"
248 ) as exists_mock:
249 exists_mock.return_value = False
250 self.backup["_ShouldUseCompressedAsh"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700251
252
Ryan Cuief91e702013-02-04 12:06:36 -0800253class DeployTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600254 """Setup a deploy object with a GS-path for use in tests."""
Steve Funge984a532013-11-25 17:09:25 -0800255
Alex Klein1699fab2022-09-08 08:46:06 -0600256 def _GetDeployChrome(self, args):
257 options = _ParseCommandLine(args)
258 return deploy_chrome.DeployChrome(
259 options, self.tempdir, os.path.join(self.tempdir, "staging")
260 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700261
Alex Klein1699fab2022-09-08 08:46:06 -0600262 def setUp(self):
263 self.deploy_mock = self.StartPatcher(DeployChromeMock())
264 self.deploy = self._GetDeployChrome(
265 list(_REGULAR_TO)
266 + [
267 "--board",
268 _TARGET_BOARD,
269 "--gs-path",
270 _GS_PATH,
271 "--force",
272 "--mount",
273 ]
274 )
275 self.remote_reboot_mock = self.PatchObject(
276 remote_access.RemoteAccess, "RemoteReboot", return_value=True
277 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700278
George Burgess IV4c876ac2022-12-07 10:33:49 -0700279 def tearDown(self):
280 self.deploy.Cleanup()
281
Avery Musbach3edff0e2020-03-27 13:35:53 -0700282
283class TestCheckIfBoardMatches(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600284 """Testing checking whether the DUT board matches the target board."""
Avery Musbach3edff0e2020-03-27 13:35:53 -0700285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 def testMatchedBoard(self):
287 """Test the case where the DUT board matches the target board."""
288 self.PatchObject(remote_access.ChromiumOSDevice, "board", _TARGET_BOARD)
289 self.assertTrue(self.deploy.options.force)
290 self.deploy._CheckBoard()
291 self.deploy.options.force = False
292 self.deploy._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700293
Alex Klein1699fab2022-09-08 08:46:06 -0600294 def testMismatchedBoard(self):
295 """Test the case where the DUT board does not match the target board."""
296 self.PatchObject(remote_access.ChromiumOSDevice, "board", "cedar")
297 self.assertTrue(self.deploy.options.force)
298 self.deploy._CheckBoard()
299 self.deploy.options.force = False
300 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=True)
301 self.deploy._CheckBoard()
302 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=False)
303 self.assertRaises(deploy_chrome.DeployFailure, self.deploy._CheckBoard)
Avery Musbach3edff0e2020-03-27 13:35:53 -0700304
305
David James88e6f032013-03-02 08:13:20 -0800306class TestDisableRootfsVerification(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600307 """Testing disabling of rootfs verification and RO mode."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700308
Alex Klein1699fab2022-09-08 08:46:06 -0600309 def testDisableRootfsVerificationSuccess(self):
310 """Test the working case, disabling rootfs verification."""
311 self.deploy_mock.MockMountCmd(0)
312 self.deploy._DisableRootfsVerification()
313 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700314
Alex Klein1699fab2022-09-08 08:46:06 -0600315 def testDisableRootfsVerificationFailure(self):
316 """Test failure to disable rootfs verification."""
Mike Frysinger16474792023-03-01 01:18:00 -0500317
Alex Klein1699fab2022-09-08 08:46:06 -0600318 # pylint: disable=unused-argument
319 def RaiseRunCommandError(timeout_sec=None):
320 raise cros_build_lib.RunCommandError("Mock RunCommandError")
321
322 self.remote_reboot_mock.side_effect = RaiseRunCommandError
323 self.assertRaises(
324 cros_build_lib.RunCommandError,
325 self.deploy._DisableRootfsVerification,
326 )
327 self.remote_reboot_mock.side_effect = None
328 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800329
330
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000331class TestDeployCompressedAsh(DeployTest):
332 """Testing deployments with --ash-compressed passed."""
333
334 def _GetDeployChrome(self, args):
335 args.append("--compressed-ash")
336 return super(TestDeployCompressedAsh, self)._GetDeployChrome(args)
337
338 def testUnmountSuccess(self):
339 """Test case for a successful 'umount' call."""
340 self.deploy._KillAshChromeIfNeeded()
341
342 def testUnmountFailure(self):
343 """Test case for a failed 'umount' call."""
344 self.deploy_mock.rsh_mock.AddCmdResult(
345 ["umount", deploy_chrome.RAW_ASH_PATH],
346 returncode=32,
347 stderr="umount failure",
348 )
349 self.assertRaises(
350 deploy_chrome.DeployFailure, self.deploy._KillAshChromeIfNeeded
351 )
352
353
David James88e6f032013-03-02 08:13:20 -0800354class TestMount(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600355 """Testing mount success and failure."""
David James88e6f032013-03-02 08:13:20 -0800356
Alex Klein1699fab2022-09-08 08:46:06 -0600357 def testSuccess(self):
358 """Test case where we are able to mount as writable."""
359 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
360 self.deploy_mock.MockMountCmd(0)
361 self.deploy._MountRootfsAsWritable()
362 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800363
Alex Klein1699fab2022-09-08 08:46:06 -0600364 def testMountError(self):
365 """Test that mount failure doesn't raise an exception by default."""
366 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
367 self.PatchObject(
368 remote_access.ChromiumOSDevice,
369 "IsDirWritable",
370 return_value=False,
371 autospec=True,
372 )
373 self.deploy._MountRootfsAsWritable()
374 self.assertTrue(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 def testMountRwFailure(self):
377 """Test that mount failure raises an exception if check=True."""
378 self.assertRaises(
379 cros_build_lib.RunCommandError,
380 self.deploy._MountRootfsAsWritable,
381 check=True,
382 )
383 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Robert Flack1dc7ea82014-11-26 13:50:24 -0500384
Alex Klein1699fab2022-09-08 08:46:06 -0600385 def testMountTempDir(self):
386 """Test that mount succeeds if target dir is writable."""
387 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
388 self.PatchObject(
389 remote_access.ChromiumOSDevice,
390 "IsDirWritable",
391 return_value=True,
392 autospec=True,
393 )
394 self.deploy._MountRootfsAsWritable()
395 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700396
397
Anushruth8d797672019-10-17 12:22:31 -0700398class TestMountTarget(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600399 """Testing mount and umount command handling."""
Anushruth8d797672019-10-17 12:22:31 -0700400
Alex Klein1699fab2022-09-08 08:46:06 -0600401 def testMountTargetUmountFailure(self):
402 """Test error being thrown if umount fails.
Anushruth8d797672019-10-17 12:22:31 -0700403
Alex Klein1699fab2022-09-08 08:46:06 -0600404 Test that 'lsof' is run on mount-dir and 'mount -rbind' command is not run
405 if 'umount' cmd fails.
406 """
407 mount_dir = self.deploy.options.mount_dir
408 target_dir = self.deploy.options.target_dir
409 self.deploy_mock.rsh_mock.AddCmdResult(
410 deploy_chrome._UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": mount_dir},
411 returncode=errno.EBUSY,
412 stderr="Target is Busy",
413 )
414 self.deploy_mock.rsh_mock.AddCmdResult(
415 deploy_chrome.LSOF_COMMAND % (mount_dir,),
416 returncode=0,
417 stdout="process " + mount_dir,
418 )
419 # Check for RunCommandError being thrown.
420 self.assertRaises(
421 cros_build_lib.RunCommandError, self.deploy._MountTarget
422 )
423 # Check for the 'mount -rbind' command not run.
424 self.deploy_mock.rsh_mock.assertCommandContains(
425 (deploy_chrome._BIND_TO_FINAL_DIR_CMD % (target_dir, mount_dir)),
426 expected=False,
427 )
428 # Check for lsof command being called.
429 self.deploy_mock.rsh_mock.assertCommandContains(
430 (deploy_chrome.LSOF_COMMAND % (mount_dir,))
431 )
Anushruth8d797672019-10-17 12:22:31 -0700432
433
Ryan Cuief91e702013-02-04 12:06:36 -0800434class TestUiJobStarted(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600435 """Test detection of a running 'ui' job."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 def MockStatusUiCmd(self, **kwargs):
438 self.deploy_mock.rsh_mock.AddCmdResult("status ui", **kwargs)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700439
Alex Klein1699fab2022-09-08 08:46:06 -0600440 def testUiJobStartedFalse(self):
441 """Correct results with a stopped job."""
442 self.MockStatusUiCmd(stdout="ui stop/waiting")
443 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuif2d1a582013-02-19 14:08:13 -0800444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 def testNoUiJob(self):
446 """Correct results when the job doesn't exist."""
447 self.MockStatusUiCmd(stderr="start: Unknown job: ui", returncode=1)
448 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700449
Alex Klein1699fab2022-09-08 08:46:06 -0600450 def testCheckRootfsWriteableTrue(self):
451 """Correct results with a running job."""
452 self.MockStatusUiCmd(stdout="ui start/running, process 297")
453 self.assertTrue(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700454
455
Ryan Cuief91e702013-02-04 12:06:36 -0800456class StagingTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600457 """Test user-mode and ebuild-mode staging functionality."""
Ryan Cuief91e702013-02-04 12:06:36 -0800458
Alex Klein1699fab2022-09-08 08:46:06 -0600459 def setUp(self):
460 self.staging_dir = os.path.join(self.tempdir, "staging")
461 osutils.SafeMakedirs(self.staging_dir)
462 self.staging_tarball_path = os.path.join(
463 self.tempdir, deploy_chrome._CHROME_DIR_STAGING_TARBALL_ZSTD
464 )
465 self.build_dir = os.path.join(self.tempdir, "build_dir")
466 self.common_flags = [
467 "--board",
468 _TARGET_BOARD,
469 "--build-dir",
470 self.build_dir,
471 "--staging-only",
472 "--cache-dir",
473 str(self.tempdir),
474 ]
475 self.sdk_mock = self.StartPatcher(
476 cros_chrome_sdk_unittest.SDKFetcherMock()
477 )
478 self.PatchObject(
479 osutils,
480 "SourceEnvironment",
481 autospec=True,
482 return_value={"STRIP": "x86_64-cros-linux-gnu-strip"},
483 )
Ryan Cuief91e702013-02-04 12:06:36 -0800484
Alex Klein1699fab2022-09-08 08:46:06 -0600485 def testSingleFileDeployFailure(self):
486 """Default staging enforces that mandatory files are copied"""
487 options = _ParseCommandLine(self.common_flags)
488 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
489 self.assertRaises(
490 chrome_util.MissingPathError,
491 deploy_chrome._PrepareStagingDir,
492 options,
493 self.tempdir,
494 self.staging_dir,
495 chrome_util._COPY_PATHS_CHROME,
496 )
Ryan Cuief91e702013-02-04 12:06:36 -0800497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 def testSloppyDeployFailure(self):
499 """Sloppy staging enforces that at least one file is copied."""
500 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
501 self.assertRaises(
502 chrome_util.MissingPathError,
503 deploy_chrome._PrepareStagingDir,
504 options,
505 self.tempdir,
506 self.staging_dir,
507 chrome_util._COPY_PATHS_CHROME,
508 )
David Jamesa6e08892013-03-01 13:34:11 -0800509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 def testSloppyDeploySuccess(self):
511 """Sloppy staging - stage one file."""
512 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
513 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
514 deploy_chrome._PrepareStagingDir(
515 options,
516 self.tempdir,
517 self.staging_dir,
518 chrome_util._COPY_PATHS_CHROME,
519 )
David Jamesa6e08892013-03-01 13:34:11 -0800520
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000521 def testSloppyDeploySuccessLacros(self):
522 """Ensure the squashfs mechanism with --compressed-ash doesn't throw."""
523 options = _ParseCommandLine(
524 self.common_flags + ["--sloppy", "--compressed-ash"]
525 )
526 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
527 deploy_chrome._PrepareStagingDir(
528 options,
529 self.tempdir,
530 self.staging_dir,
531 chrome_util._COPY_PATHS_CHROME,
532 )
533
Alex Klein1699fab2022-09-08 08:46:06 -0600534 @cros_test_lib.pytestmark_network_test
535 def testUploadStagingDir(self):
536 """Upload staging directory."""
537 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
538 staging_upload = "gs://some-path"
539 options = _ParseCommandLine(
540 self.common_flags + ["--staging-upload", staging_upload]
541 )
542 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
543 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
544 self.assertEqual(
545 mockGsCopy.call_args_list,
546 [
547 mock.call(self.staging_tarball_path, staging_upload, acl=""),
548 ],
549 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000550
Alex Klein1699fab2022-09-08 08:46:06 -0600551 @cros_test_lib.pytestmark_network_test
552 def testUploadStagingPublicReadACL(self):
553 """Upload staging directory with public-read ACL."""
554 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
555 staging_upload = "gs://some-path"
556 options = _ParseCommandLine(
557 self.common_flags
558 + ["--staging-upload", staging_upload, "--public-read"]
559 )
560 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
561 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
562 self.assertEqual(
563 mockGsCopy.call_args_list,
564 [
565 mock.call(
566 self.staging_tarball_path, staging_upload, acl="public-read"
567 ),
568 ],
569 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000570
Steve Funge984a532013-11-25 17:09:25 -0800571
572class DeployTestBuildDir(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600573 """Set up a deploy object with a build-dir for use in deployment type tests"""
Steve Funge984a532013-11-25 17:09:25 -0800574
Alex Klein1699fab2022-09-08 08:46:06 -0600575 def _GetDeployChrome(self, args):
576 options = _ParseCommandLine(args)
577 return deploy_chrome.DeployChrome(
578 options, self.tempdir, os.path.join(self.tempdir, "staging")
579 )
Steve Funge984a532013-11-25 17:09:25 -0800580
Alex Klein1699fab2022-09-08 08:46:06 -0600581 def setUp(self):
582 self.staging_dir = os.path.join(self.tempdir, "staging")
583 self.build_dir = os.path.join(self.tempdir, "build_dir")
584 self.deploy_mock = self.StartPatcher(DeployChromeMock())
585 self.deploy = self._GetDeployChrome(
586 list(_REGULAR_TO)
587 + [
588 "--board",
589 _TARGET_BOARD,
590 "--build-dir",
591 self.build_dir,
592 "--staging-only",
593 "--cache-dir",
594 str(self.tempdir),
595 "--sloppy",
596 ]
597 )
Steve Funge984a532013-11-25 17:09:25 -0800598
Alex Klein1699fab2022-09-08 08:46:06 -0600599 def getCopyPath(self, source_path):
600 """Return a chrome_util.Path or None if not present."""
601 paths = [p for p in self.deploy.copy_paths if p.src == source_path]
602 return paths[0] if paths else None
603
Steve Funge984a532013-11-25 17:09:25 -0800604
Daniel Erat1ae46382014-08-14 10:23:39 -0700605class TestDeploymentType(DeployTestBuildDir):
Alex Klein1699fab2022-09-08 08:46:06 -0600606 """Test detection of deployment type using build dir."""
Steve Funge984a532013-11-25 17:09:25 -0800607
Alex Klein1699fab2022-09-08 08:46:06 -0600608 def testAppShellDetection(self):
609 """Check for an app_shell deployment"""
610 osutils.Touch(
611 os.path.join(self.deploy.options.build_dir, "app_shell"),
612 makedirs=True,
613 )
614 self.deploy._CheckDeployType()
615 self.assertTrue(self.getCopyPath("app_shell"))
616 self.assertFalse(self.getCopyPath("chrome"))
Steve Funge984a532013-11-25 17:09:25 -0800617
Alex Klein1699fab2022-09-08 08:46:06 -0600618 def testChromeAndAppShellDetection(self):
619 """Check for a chrome deployment when app_shell also exists."""
620 osutils.Touch(
621 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
622 )
623 osutils.Touch(
624 os.path.join(self.deploy.options.build_dir, "app_shell"),
625 makedirs=True,
626 )
627 self.deploy._CheckDeployType()
628 self.assertTrue(self.getCopyPath("chrome"))
629 self.assertFalse(self.getCopyPath("app_shell"))
Steve Fung63d705d2014-03-16 03:14:03 -0700630
Alex Klein1699fab2022-09-08 08:46:06 -0600631 def testChromeDetection(self):
632 """Check for a regular chrome deployment"""
633 osutils.Touch(
634 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
635 )
636 self.deploy._CheckDeployType()
637 self.assertTrue(self.getCopyPath("chrome"))
638 self.assertFalse(self.getCopyPath("app_shell"))
Ben Pastenee484b342020-06-30 18:29:27 -0700639
640
641class TestDeployTestBinaries(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600642 """Tests _DeployTestBinaries()."""
Ben Pastenee484b342020-06-30 18:29:27 -0700643
Alex Klein1699fab2022-09-08 08:46:06 -0600644 def setUp(self):
645 options = _ParseCommandLine(
646 list(_REGULAR_TO)
647 + [
648 "--board",
649 _TARGET_BOARD,
650 "--force",
651 "--mount",
652 "--build-dir",
653 os.path.join(self.tempdir, "build_dir"),
654 "--nostrip",
655 ]
656 )
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000657 self.remote_exists_mock = self.PatchObject(
658 remote_access.RemoteDevice, "IfFileExists", return_value=False
659 )
Alex Klein1699fab2022-09-08 08:46:06 -0600660 self.deploy = deploy_chrome.DeployChrome(
661 options, self.tempdir, os.path.join(self.tempdir, "staging")
662 )
Ben Pastenee484b342020-06-30 18:29:27 -0700663
Alex Klein1699fab2022-09-08 08:46:06 -0600664 def _SimulateBinaries(self):
665 # Ensure the staging dir contains the right binaries to copy over.
666 test_binaries = [
667 "run_a_tests",
668 "run_b_tests",
669 "run_c_tests",
670 ]
671 # Simulate having the binaries both on the device and in our local build
672 # dir.
673 self.rc.AddCmdResult(
674 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD),
675 stdout="\n".join(test_binaries),
676 )
677 for binary in test_binaries:
678 osutils.Touch(
679 os.path.join(self.deploy.options.build_dir, binary),
680 makedirs=True,
681 mode=0o700,
682 )
683 return test_binaries
Ben Pastenee484b342020-06-30 18:29:27 -0700684
Alex Klein1699fab2022-09-08 08:46:06 -0600685 def _AssertBinariesInStagingDir(self, test_binaries):
686 # Ensure the binaries were placed in the staging dir used to copy them over.
687 staging_dir = os.path.join(
688 self.tempdir, os.path.basename(deploy_chrome._CHROME_TEST_BIN_DIR)
689 )
690 for binary in test_binaries:
691 self.assertIn(binary, os.listdir(staging_dir))
Erik Chen75a2f492020-08-06 19:15:11 -0700692
Alex Klein1699fab2022-09-08 08:46:06 -0600693 def testFindError(self):
694 """Ensure an error is thrown if we can't inspect the device."""
695 self.rc.AddCmdResult(
696 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD), 1
697 )
698 self.assertRaises(
699 deploy_chrome.DeployFailure, self.deploy._DeployTestBinaries
700 )
Brian Sheedy86f12342020-10-29 15:30:02 -0700701
Alex Klein1699fab2022-09-08 08:46:06 -0600702 def testSuccess(self):
703 """Ensure that the happy path succeeds as expected."""
704 test_binaries = self._SimulateBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700705 self.deploy._DeployTestBinaries()
Alex Klein1699fab2022-09-08 08:46:06 -0600706 self._AssertBinariesInStagingDir(test_binaries)
707
708 def testRetrySuccess(self):
709 """Ensure that a transient exception still results in success."""
Mike Frysinger16474792023-03-01 01:18:00 -0500710
Alex Klein1699fab2022-09-08 08:46:06 -0600711 # Raises a RunCommandError on its first invocation, but passes on subsequent
712 # calls.
713 def SideEffect(*args, **kwargs):
714 # pylint: disable=unused-argument
715 if not SideEffect.called:
716 SideEffect.called = True
717 raise cros_build_lib.RunCommandError("fail")
718
719 SideEffect.called = False
720
721 test_binaries = self._SimulateBinaries()
722 with mock.patch.object(
723 remote_access.ChromiumOSDevice,
724 "CopyToDevice",
725 side_effect=SideEffect,
726 ) as copy_mock:
727 self.deploy._DeployTestBinaries()
728 self.assertEqual(copy_mock.call_count, 2)
729 self._AssertBinariesInStagingDir(test_binaries)
730
731 def testRetryFailure(self):
732 """Ensure that consistent exceptions result in failure."""
733 self._SimulateBinaries()
734 with self.assertRaises(cros_build_lib.RunCommandError):
735 with mock.patch.object(
736 remote_access.ChromiumOSDevice,
737 "CopyToDevice",
738 side_effect=cros_build_lib.RunCommandError("fail"),
739 ):
740 self.deploy._DeployTestBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700741
Erik Chen75a2f492020-08-06 19:15:11 -0700742
743class LacrosPerformTest(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600744 """Line coverage for Perform() method with --lacros option."""
Erik Chen75a2f492020-08-06 19:15:11 -0700745
Alex Klein1699fab2022-09-08 08:46:06 -0600746 def setUp(self):
747 self.deploy = None
748 self._ran_start_command = False
749 self.StartPatcher(parallel_unittest.ParallelMock())
Yuke Liao24fc60c2020-12-26 22:16:49 -0800750
Alex Klein1699fab2022-09-08 08:46:06 -0600751 def start_ui_side_effect(*args, **kwargs):
752 # pylint: disable=unused-argument
753 self._ran_start_command = True
Yuke Liao24fc60c2020-12-26 22:16:49 -0800754
Alex Klein1699fab2022-09-08 08:46:06 -0600755 self.rc.AddCmdResult(
756 partial_mock.In("start ui"), side_effect=start_ui_side_effect
757 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800758
Alex Klein1699fab2022-09-08 08:46:06 -0600759 def prepareDeploy(self, options=None):
760 if not options:
761 options = _ParseCommandLine(
762 [
763 "--lacros",
764 "--nostrip",
765 "--build-dir",
766 "/path/to/nowhere",
767 "--device",
768 "monkey",
769 ]
770 )
771 self.deploy = deploy_chrome.DeployChrome(
772 options, self.tempdir, os.path.join(self.tempdir, "staging")
773 )
Erik Chen75a2f492020-08-06 19:15:11 -0700774
Alex Klein1699fab2022-09-08 08:46:06 -0600775 # These methods being mocked are all side effects expected for a --lacros
776 # deploy.
777 self.deploy._EnsureTargetDir = mock.Mock()
778 self.deploy._GetDeviceInfo = mock.Mock()
779 self.deploy._CheckConnection = mock.Mock()
780 self.deploy._MountRootfsAsWritable = mock.Mock()
781 self.deploy._PrepareStagingDir = mock.Mock()
782 self.deploy._CheckDeviceFreeSpace = mock.Mock()
783 self.deploy._KillAshChromeIfNeeded = mock.Mock()
Erik Chen75a2f492020-08-06 19:15:11 -0700784
Alex Klein1699fab2022-09-08 08:46:06 -0600785 def testConfNotModified(self):
786 """When the conf file is not modified we don't restart chrome ."""
787 self.prepareDeploy()
788 self.deploy.Perform()
789 self.deploy._KillAshChromeIfNeeded.assert_not_called()
790 self.assertFalse(self._ran_start_command)
Erik Chen75a2f492020-08-06 19:15:11 -0700791
Alex Klein1699fab2022-09-08 08:46:06 -0600792 def testConfModified(self):
793 """When the conf file is modified we restart chrome."""
794 self.prepareDeploy()
Erik Chen75a2f492020-08-06 19:15:11 -0700795
Alex Klein1699fab2022-09-08 08:46:06 -0600796 # We intentionally add '\n' to MODIFIED_CONF_FILE to simulate echo adding a
797 # newline when invoked in the shell.
798 self.rc.AddCmdResult(
799 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
800 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
801 )
Erik Chen75a2f492020-08-06 19:15:11 -0700802
Alex Klein1699fab2022-09-08 08:46:06 -0600803 self.deploy.Perform()
804 self.deploy._KillAshChromeIfNeeded.assert_called()
805 self.assertTrue(self._ran_start_command)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800806
Alex Klein1699fab2022-09-08 08:46:06 -0600807 def testSkipModifyingConf(self):
808 """SKip modifying the config file when the argument is specified."""
809 self.prepareDeploy(
810 _ParseCommandLine(
811 [
812 "--lacros",
813 "--nostrip",
814 "--build-dir",
815 "/path/to/nowhere",
816 "--device",
817 "monkey",
818 "--skip-modifying-config-file",
819 ]
820 )
821 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800822
Alex Klein1699fab2022-09-08 08:46:06 -0600823 self.rc.AddCmdResult(
824 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
825 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
826 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800827
Alex Klein1699fab2022-09-08 08:46:06 -0600828 self.deploy.Perform()
829 self.deploy._KillAshChromeIfNeeded.assert_not_called()
830 self.assertFalse(self._ran_start_command)