blob: ad9ffa104920584801e329ef1d631cf14414af47 [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
Joel Hockey764728e2023-03-14 17:10:04 -0700200 def testUnlockPassword(self):
201 argv = list(_REGULAR_TO) + [
202 "--board",
203 _TARGET_BOARD,
204 "--unlock-password",
205 "letmein",
206 "--build-dir",
207 "/path/to/nowhere",
208 ]
209 options = _ParseCommandLine(argv)
210 self.assertEqual(options.unlock_password, "letmein")
211
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700212
213class DeployChromeMock(partial_mock.PartialMock):
Alex Klein1699fab2022-09-08 08:46:06 -0600214 """Deploy Chrome Mock Class."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700215
Alex Klein1699fab2022-09-08 08:46:06 -0600216 TARGET = "chromite.scripts.deploy_chrome.DeployChrome"
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000217 ATTRS = (
218 "_ChromeFileInUse",
219 "_DisableRootfsVerification",
220 "_ShouldUseCompressedAsh",
221 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700222
Alex Klein1699fab2022-09-08 08:46:06 -0600223 def __init__(self):
224 partial_mock.PartialMock.__init__(self)
225 self.remote_device_mock = remote_access_unittest.RemoteDeviceMock()
226 # Target starts off as having rootfs verification enabled.
227 self.rsh_mock = remote_access_unittest.RemoteShMock()
228 self.rsh_mock.SetDefaultCmdResult(0)
229 self.MockMountCmd(1)
230 self.rsh_mock.AddCmdResult(
231 deploy_chrome.LSOF_COMMAND_CHROME % (deploy_chrome._CHROME_DIR,), 1
232 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700233
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000234 self.rsh_mock.AddCmdResult(
235 "status ui", stdout="ui start/running, process 123"
236 )
237
Alex Klein1699fab2022-09-08 08:46:06 -0600238 def MockMountCmd(self, returnvalue):
239 self.rsh_mock.AddCmdResult(deploy_chrome.MOUNT_RW_COMMAND, returnvalue)
David James88e6f032013-03-02 08:13:20 -0800240
Alex Klein1699fab2022-09-08 08:46:06 -0600241 def _DisableRootfsVerification(self, inst):
242 with mock.patch.object(time, "sleep"):
243 self.backup["_DisableRootfsVerification"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700244
Alex Klein1699fab2022-09-08 08:46:06 -0600245 def PreStart(self):
246 self.remote_device_mock.start()
247 self.rsh_mock.start()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 def PreStop(self):
250 self.rsh_mock.stop()
251 self.remote_device_mock.stop()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700252
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000253 def _ChromeFileInUse(self, _inst):
254 # Fully stub out for now. Can be replaced if further testing is added.
255 return False
256
257 def _ShouldUseCompressedAsh(self, inst):
258 with mock.patch.object(
259 remote_access.RemoteDevice, "IfFileExists"
260 ) as exists_mock:
261 exists_mock.return_value = False
262 self.backup["_ShouldUseCompressedAsh"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700263
264
Ryan Cuief91e702013-02-04 12:06:36 -0800265class DeployTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600266 """Setup a deploy object with a GS-path for use in tests."""
Steve Funge984a532013-11-25 17:09:25 -0800267
Alex Klein1699fab2022-09-08 08:46:06 -0600268 def _GetDeployChrome(self, args):
269 options = _ParseCommandLine(args)
270 return deploy_chrome.DeployChrome(
271 options, self.tempdir, os.path.join(self.tempdir, "staging")
272 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 def setUp(self):
275 self.deploy_mock = self.StartPatcher(DeployChromeMock())
276 self.deploy = self._GetDeployChrome(
277 list(_REGULAR_TO)
278 + [
279 "--board",
280 _TARGET_BOARD,
281 "--gs-path",
282 _GS_PATH,
283 "--force",
284 "--mount",
285 ]
286 )
287 self.remote_reboot_mock = self.PatchObject(
288 remote_access.RemoteAccess, "RemoteReboot", return_value=True
289 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700290
George Burgess IV4c876ac2022-12-07 10:33:49 -0700291 def tearDown(self):
292 self.deploy.Cleanup()
293
Avery Musbach3edff0e2020-03-27 13:35:53 -0700294
295class TestCheckIfBoardMatches(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600296 """Testing checking whether the DUT board matches the target board."""
Avery Musbach3edff0e2020-03-27 13:35:53 -0700297
Alex Klein1699fab2022-09-08 08:46:06 -0600298 def testMatchedBoard(self):
299 """Test the case where the DUT board matches the target board."""
300 self.PatchObject(remote_access.ChromiumOSDevice, "board", _TARGET_BOARD)
301 self.assertTrue(self.deploy.options.force)
302 self.deploy._CheckBoard()
303 self.deploy.options.force = False
304 self.deploy._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 def testMismatchedBoard(self):
307 """Test the case where the DUT board does not match the target board."""
308 self.PatchObject(remote_access.ChromiumOSDevice, "board", "cedar")
309 self.assertTrue(self.deploy.options.force)
310 self.deploy._CheckBoard()
311 self.deploy.options.force = False
312 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=True)
313 self.deploy._CheckBoard()
314 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=False)
315 self.assertRaises(deploy_chrome.DeployFailure, self.deploy._CheckBoard)
Avery Musbach3edff0e2020-03-27 13:35:53 -0700316
317
David James88e6f032013-03-02 08:13:20 -0800318class TestDisableRootfsVerification(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600319 """Testing disabling of rootfs verification and RO mode."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700320
Alex Klein1699fab2022-09-08 08:46:06 -0600321 def testDisableRootfsVerificationSuccess(self):
322 """Test the working case, disabling rootfs verification."""
323 self.deploy_mock.MockMountCmd(0)
324 self.deploy._DisableRootfsVerification()
325 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 def testDisableRootfsVerificationFailure(self):
328 """Test failure to disable rootfs verification."""
Mike Frysinger16474792023-03-01 01:18:00 -0500329
Alex Klein1699fab2022-09-08 08:46:06 -0600330 # pylint: disable=unused-argument
331 def RaiseRunCommandError(timeout_sec=None):
332 raise cros_build_lib.RunCommandError("Mock RunCommandError")
333
334 self.remote_reboot_mock.side_effect = RaiseRunCommandError
335 self.assertRaises(
336 cros_build_lib.RunCommandError,
337 self.deploy._DisableRootfsVerification,
338 )
339 self.remote_reboot_mock.side_effect = None
340 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800341
342
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000343class TestDeployCompressedAsh(DeployTest):
344 """Testing deployments with --ash-compressed passed."""
345
346 def _GetDeployChrome(self, args):
347 args.append("--compressed-ash")
348 return super(TestDeployCompressedAsh, self)._GetDeployChrome(args)
349
350 def testUnmountSuccess(self):
351 """Test case for a successful 'umount' call."""
352 self.deploy._KillAshChromeIfNeeded()
353
354 def testUnmountFailure(self):
355 """Test case for a failed 'umount' call."""
356 self.deploy_mock.rsh_mock.AddCmdResult(
357 ["umount", deploy_chrome.RAW_ASH_PATH],
358 returncode=32,
359 stderr="umount failure",
360 )
361 self.assertRaises(
362 deploy_chrome.DeployFailure, self.deploy._KillAshChromeIfNeeded
363 )
364
365
David James88e6f032013-03-02 08:13:20 -0800366class TestMount(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600367 """Testing mount success and failure."""
David James88e6f032013-03-02 08:13:20 -0800368
Alex Klein1699fab2022-09-08 08:46:06 -0600369 def testSuccess(self):
370 """Test case where we are able to mount as writable."""
371 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
372 self.deploy_mock.MockMountCmd(0)
373 self.deploy._MountRootfsAsWritable()
374 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 def testMountError(self):
377 """Test that mount failure doesn't raise an exception by default."""
378 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
379 self.PatchObject(
380 remote_access.ChromiumOSDevice,
381 "IsDirWritable",
382 return_value=False,
383 autospec=True,
384 )
385 self.deploy._MountRootfsAsWritable()
386 self.assertTrue(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700387
Alex Klein1699fab2022-09-08 08:46:06 -0600388 def testMountRwFailure(self):
389 """Test that mount failure raises an exception if check=True."""
390 self.assertRaises(
391 cros_build_lib.RunCommandError,
392 self.deploy._MountRootfsAsWritable,
393 check=True,
394 )
395 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Robert Flack1dc7ea82014-11-26 13:50:24 -0500396
Alex Klein1699fab2022-09-08 08:46:06 -0600397 def testMountTempDir(self):
398 """Test that mount succeeds if target dir is writable."""
399 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
400 self.PatchObject(
401 remote_access.ChromiumOSDevice,
402 "IsDirWritable",
403 return_value=True,
404 autospec=True,
405 )
406 self.deploy._MountRootfsAsWritable()
407 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700408
409
Anushruth8d797672019-10-17 12:22:31 -0700410class TestMountTarget(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600411 """Testing mount and umount command handling."""
Anushruth8d797672019-10-17 12:22:31 -0700412
Alex Klein1699fab2022-09-08 08:46:06 -0600413 def testMountTargetUmountFailure(self):
414 """Test error being thrown if umount fails.
Anushruth8d797672019-10-17 12:22:31 -0700415
Alex Klein1699fab2022-09-08 08:46:06 -0600416 Test that 'lsof' is run on mount-dir and 'mount -rbind' command is not run
417 if 'umount' cmd fails.
418 """
419 mount_dir = self.deploy.options.mount_dir
420 target_dir = self.deploy.options.target_dir
421 self.deploy_mock.rsh_mock.AddCmdResult(
422 deploy_chrome._UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": mount_dir},
423 returncode=errno.EBUSY,
424 stderr="Target is Busy",
425 )
426 self.deploy_mock.rsh_mock.AddCmdResult(
427 deploy_chrome.LSOF_COMMAND % (mount_dir,),
428 returncode=0,
429 stdout="process " + mount_dir,
430 )
431 # Check for RunCommandError being thrown.
432 self.assertRaises(
433 cros_build_lib.RunCommandError, self.deploy._MountTarget
434 )
435 # Check for the 'mount -rbind' command not run.
436 self.deploy_mock.rsh_mock.assertCommandContains(
437 (deploy_chrome._BIND_TO_FINAL_DIR_CMD % (target_dir, mount_dir)),
438 expected=False,
439 )
440 # Check for lsof command being called.
441 self.deploy_mock.rsh_mock.assertCommandContains(
442 (deploy_chrome.LSOF_COMMAND % (mount_dir,))
443 )
Anushruth8d797672019-10-17 12:22:31 -0700444
445
Ryan Cuief91e702013-02-04 12:06:36 -0800446class TestUiJobStarted(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600447 """Test detection of a running 'ui' job."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 def MockStatusUiCmd(self, **kwargs):
450 self.deploy_mock.rsh_mock.AddCmdResult("status ui", **kwargs)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700451
Alex Klein1699fab2022-09-08 08:46:06 -0600452 def testUiJobStartedFalse(self):
453 """Correct results with a stopped job."""
454 self.MockStatusUiCmd(stdout="ui stop/waiting")
455 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuif2d1a582013-02-19 14:08:13 -0800456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 def testNoUiJob(self):
458 """Correct results when the job doesn't exist."""
459 self.MockStatusUiCmd(stderr="start: Unknown job: ui", returncode=1)
460 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700461
Alex Klein1699fab2022-09-08 08:46:06 -0600462 def testCheckRootfsWriteableTrue(self):
463 """Correct results with a running job."""
464 self.MockStatusUiCmd(stdout="ui start/running, process 297")
465 self.assertTrue(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700466
467
Joel Hockey764728e2023-03-14 17:10:04 -0700468class TestUnlockPassword(DeployTest):
469 """Test that unlock password is sent."""
470
471 def _GetDeployChrome(self, args):
472 args.append("--unlock-password=letmein")
473 return super(TestUnlockPassword, self)._GetDeployChrome(args)
474
475 def testUnlock(self):
476 """Test that unlock password is sent."""
477 self.deploy._stopped_ui = True
478
479 # Update LAST_LOGIN_COMMAND to return a different value.
480 def SideEffect(*args, **kwargs):
481 # pylint: disable=unused-argument
482 self.deploy_mock.rsh_mock.AddCmdResult(
483 deploy_chrome.LAST_LOGIN_COMMAND, stdout="2.0"
484 )
485
486 # LAST_LOGIN_COMMAND returns 1.0 the first time it is called, then 2.0.
487 self.deploy_mock.rsh_mock.AddCmdResult(
488 deploy_chrome.LAST_LOGIN_COMMAND,
489 stdout="1.0",
490 side_effect=SideEffect,
491 )
492 with mock.patch.object(remote_access.ChromiumOSDevice, "CopyToDevice"):
493 with mock.patch.object(time, "sleep"):
494 self.deploy._Deploy()
495 # Ensure unlock command was called.
496 self.deploy_mock.rsh_mock.assertCommandContains(
497 deploy_chrome.UNLOCK_PASSWORD_COMMAND % "letmein"
498 )
499
500
Ryan Cuief91e702013-02-04 12:06:36 -0800501class StagingTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600502 """Test user-mode and ebuild-mode staging functionality."""
Ryan Cuief91e702013-02-04 12:06:36 -0800503
Alex Klein1699fab2022-09-08 08:46:06 -0600504 def setUp(self):
505 self.staging_dir = os.path.join(self.tempdir, "staging")
506 osutils.SafeMakedirs(self.staging_dir)
507 self.staging_tarball_path = os.path.join(
508 self.tempdir, deploy_chrome._CHROME_DIR_STAGING_TARBALL_ZSTD
509 )
510 self.build_dir = os.path.join(self.tempdir, "build_dir")
511 self.common_flags = [
512 "--board",
513 _TARGET_BOARD,
514 "--build-dir",
515 self.build_dir,
516 "--staging-only",
517 "--cache-dir",
518 str(self.tempdir),
519 ]
520 self.sdk_mock = self.StartPatcher(
521 cros_chrome_sdk_unittest.SDKFetcherMock()
522 )
523 self.PatchObject(
524 osutils,
525 "SourceEnvironment",
526 autospec=True,
527 return_value={"STRIP": "x86_64-cros-linux-gnu-strip"},
528 )
Ryan Cuief91e702013-02-04 12:06:36 -0800529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 def testSingleFileDeployFailure(self):
531 """Default staging enforces that mandatory files are copied"""
532 options = _ParseCommandLine(self.common_flags)
533 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
534 self.assertRaises(
535 chrome_util.MissingPathError,
536 deploy_chrome._PrepareStagingDir,
537 options,
538 self.tempdir,
539 self.staging_dir,
540 chrome_util._COPY_PATHS_CHROME,
541 )
Ryan Cuief91e702013-02-04 12:06:36 -0800542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 def testSloppyDeployFailure(self):
544 """Sloppy staging enforces that at least one file is copied."""
545 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
546 self.assertRaises(
547 chrome_util.MissingPathError,
548 deploy_chrome._PrepareStagingDir,
549 options,
550 self.tempdir,
551 self.staging_dir,
552 chrome_util._COPY_PATHS_CHROME,
553 )
David Jamesa6e08892013-03-01 13:34:11 -0800554
Alex Klein1699fab2022-09-08 08:46:06 -0600555 def testSloppyDeploySuccess(self):
556 """Sloppy staging - stage one file."""
557 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
558 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
559 deploy_chrome._PrepareStagingDir(
560 options,
561 self.tempdir,
562 self.staging_dir,
563 chrome_util._COPY_PATHS_CHROME,
564 )
David Jamesa6e08892013-03-01 13:34:11 -0800565
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000566 def testSloppyDeploySuccessLacros(self):
567 """Ensure the squashfs mechanism with --compressed-ash doesn't throw."""
568 options = _ParseCommandLine(
569 self.common_flags + ["--sloppy", "--compressed-ash"]
570 )
571 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
572 deploy_chrome._PrepareStagingDir(
573 options,
574 self.tempdir,
575 self.staging_dir,
576 chrome_util._COPY_PATHS_CHROME,
577 )
578
Alex Klein1699fab2022-09-08 08:46:06 -0600579 @cros_test_lib.pytestmark_network_test
580 def testUploadStagingDir(self):
581 """Upload staging directory."""
582 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
583 staging_upload = "gs://some-path"
584 options = _ParseCommandLine(
585 self.common_flags + ["--staging-upload", staging_upload]
586 )
587 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
588 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
589 self.assertEqual(
590 mockGsCopy.call_args_list,
591 [
592 mock.call(self.staging_tarball_path, staging_upload, acl=""),
593 ],
594 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000595
Alex Klein1699fab2022-09-08 08:46:06 -0600596 @cros_test_lib.pytestmark_network_test
597 def testUploadStagingPublicReadACL(self):
598 """Upload staging directory with public-read ACL."""
599 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
600 staging_upload = "gs://some-path"
601 options = _ParseCommandLine(
602 self.common_flags
603 + ["--staging-upload", staging_upload, "--public-read"]
604 )
605 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
606 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
607 self.assertEqual(
608 mockGsCopy.call_args_list,
609 [
610 mock.call(
611 self.staging_tarball_path, staging_upload, acl="public-read"
612 ),
613 ],
614 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000615
Steve Funge984a532013-11-25 17:09:25 -0800616
617class DeployTestBuildDir(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600618 """Set up a deploy object with a build-dir for use in deployment type tests"""
Steve Funge984a532013-11-25 17:09:25 -0800619
Alex Klein1699fab2022-09-08 08:46:06 -0600620 def _GetDeployChrome(self, args):
621 options = _ParseCommandLine(args)
622 return deploy_chrome.DeployChrome(
623 options, self.tempdir, os.path.join(self.tempdir, "staging")
624 )
Steve Funge984a532013-11-25 17:09:25 -0800625
Alex Klein1699fab2022-09-08 08:46:06 -0600626 def setUp(self):
627 self.staging_dir = os.path.join(self.tempdir, "staging")
628 self.build_dir = os.path.join(self.tempdir, "build_dir")
629 self.deploy_mock = self.StartPatcher(DeployChromeMock())
630 self.deploy = self._GetDeployChrome(
631 list(_REGULAR_TO)
632 + [
633 "--board",
634 _TARGET_BOARD,
635 "--build-dir",
636 self.build_dir,
637 "--staging-only",
638 "--cache-dir",
639 str(self.tempdir),
640 "--sloppy",
641 ]
642 )
Steve Funge984a532013-11-25 17:09:25 -0800643
Alex Klein1699fab2022-09-08 08:46:06 -0600644 def getCopyPath(self, source_path):
645 """Return a chrome_util.Path or None if not present."""
646 paths = [p for p in self.deploy.copy_paths if p.src == source_path]
647 return paths[0] if paths else None
648
Steve Funge984a532013-11-25 17:09:25 -0800649
Daniel Erat1ae46382014-08-14 10:23:39 -0700650class TestDeploymentType(DeployTestBuildDir):
Alex Klein1699fab2022-09-08 08:46:06 -0600651 """Test detection of deployment type using build dir."""
Steve Funge984a532013-11-25 17:09:25 -0800652
Alex Klein1699fab2022-09-08 08:46:06 -0600653 def testAppShellDetection(self):
654 """Check for an app_shell deployment"""
655 osutils.Touch(
656 os.path.join(self.deploy.options.build_dir, "app_shell"),
657 makedirs=True,
658 )
659 self.deploy._CheckDeployType()
660 self.assertTrue(self.getCopyPath("app_shell"))
661 self.assertFalse(self.getCopyPath("chrome"))
Steve Funge984a532013-11-25 17:09:25 -0800662
Alex Klein1699fab2022-09-08 08:46:06 -0600663 def testChromeAndAppShellDetection(self):
664 """Check for a chrome deployment when app_shell also exists."""
665 osutils.Touch(
666 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
667 )
668 osutils.Touch(
669 os.path.join(self.deploy.options.build_dir, "app_shell"),
670 makedirs=True,
671 )
672 self.deploy._CheckDeployType()
673 self.assertTrue(self.getCopyPath("chrome"))
674 self.assertFalse(self.getCopyPath("app_shell"))
Steve Fung63d705d2014-03-16 03:14:03 -0700675
Alex Klein1699fab2022-09-08 08:46:06 -0600676 def testChromeDetection(self):
677 """Check for a regular chrome deployment"""
678 osutils.Touch(
679 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
680 )
681 self.deploy._CheckDeployType()
682 self.assertTrue(self.getCopyPath("chrome"))
683 self.assertFalse(self.getCopyPath("app_shell"))
Ben Pastenee484b342020-06-30 18:29:27 -0700684
685
686class TestDeployTestBinaries(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600687 """Tests _DeployTestBinaries()."""
Ben Pastenee484b342020-06-30 18:29:27 -0700688
Alex Klein1699fab2022-09-08 08:46:06 -0600689 def setUp(self):
690 options = _ParseCommandLine(
691 list(_REGULAR_TO)
692 + [
693 "--board",
694 _TARGET_BOARD,
695 "--force",
696 "--mount",
697 "--build-dir",
698 os.path.join(self.tempdir, "build_dir"),
699 "--nostrip",
700 ]
701 )
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000702 self.remote_exists_mock = self.PatchObject(
703 remote_access.RemoteDevice, "IfFileExists", return_value=False
704 )
Alex Klein1699fab2022-09-08 08:46:06 -0600705 self.deploy = deploy_chrome.DeployChrome(
706 options, self.tempdir, os.path.join(self.tempdir, "staging")
707 )
Ben Pastenee484b342020-06-30 18:29:27 -0700708
Alex Klein1699fab2022-09-08 08:46:06 -0600709 def _SimulateBinaries(self):
710 # Ensure the staging dir contains the right binaries to copy over.
711 test_binaries = [
712 "run_a_tests",
713 "run_b_tests",
714 "run_c_tests",
715 ]
716 # Simulate having the binaries both on the device and in our local build
717 # dir.
718 self.rc.AddCmdResult(
719 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD),
720 stdout="\n".join(test_binaries),
721 )
722 for binary in test_binaries:
723 osutils.Touch(
724 os.path.join(self.deploy.options.build_dir, binary),
725 makedirs=True,
726 mode=0o700,
727 )
728 return test_binaries
Ben Pastenee484b342020-06-30 18:29:27 -0700729
Alex Klein1699fab2022-09-08 08:46:06 -0600730 def _AssertBinariesInStagingDir(self, test_binaries):
731 # Ensure the binaries were placed in the staging dir used to copy them over.
732 staging_dir = os.path.join(
733 self.tempdir, os.path.basename(deploy_chrome._CHROME_TEST_BIN_DIR)
734 )
735 for binary in test_binaries:
736 self.assertIn(binary, os.listdir(staging_dir))
Erik Chen75a2f492020-08-06 19:15:11 -0700737
Alex Klein1699fab2022-09-08 08:46:06 -0600738 def testFindError(self):
739 """Ensure an error is thrown if we can't inspect the device."""
740 self.rc.AddCmdResult(
741 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD), 1
742 )
743 self.assertRaises(
744 deploy_chrome.DeployFailure, self.deploy._DeployTestBinaries
745 )
Brian Sheedy86f12342020-10-29 15:30:02 -0700746
Alex Klein1699fab2022-09-08 08:46:06 -0600747 def testSuccess(self):
748 """Ensure that the happy path succeeds as expected."""
749 test_binaries = self._SimulateBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700750 self.deploy._DeployTestBinaries()
Alex Klein1699fab2022-09-08 08:46:06 -0600751 self._AssertBinariesInStagingDir(test_binaries)
752
753 def testRetrySuccess(self):
754 """Ensure that a transient exception still results in success."""
Mike Frysinger16474792023-03-01 01:18:00 -0500755
Alex Klein1699fab2022-09-08 08:46:06 -0600756 # Raises a RunCommandError on its first invocation, but passes on subsequent
757 # calls.
758 def SideEffect(*args, **kwargs):
759 # pylint: disable=unused-argument
760 if not SideEffect.called:
761 SideEffect.called = True
762 raise cros_build_lib.RunCommandError("fail")
763
764 SideEffect.called = False
765
766 test_binaries = self._SimulateBinaries()
767 with mock.patch.object(
768 remote_access.ChromiumOSDevice,
769 "CopyToDevice",
770 side_effect=SideEffect,
771 ) as copy_mock:
772 self.deploy._DeployTestBinaries()
773 self.assertEqual(copy_mock.call_count, 2)
774 self._AssertBinariesInStagingDir(test_binaries)
775
776 def testRetryFailure(self):
777 """Ensure that consistent exceptions result in failure."""
778 self._SimulateBinaries()
779 with self.assertRaises(cros_build_lib.RunCommandError):
780 with mock.patch.object(
781 remote_access.ChromiumOSDevice,
782 "CopyToDevice",
783 side_effect=cros_build_lib.RunCommandError("fail"),
784 ):
785 self.deploy._DeployTestBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700786
Erik Chen75a2f492020-08-06 19:15:11 -0700787
788class LacrosPerformTest(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600789 """Line coverage for Perform() method with --lacros option."""
Erik Chen75a2f492020-08-06 19:15:11 -0700790
Alex Klein1699fab2022-09-08 08:46:06 -0600791 def setUp(self):
792 self.deploy = None
793 self._ran_start_command = False
794 self.StartPatcher(parallel_unittest.ParallelMock())
Yuke Liao24fc60c2020-12-26 22:16:49 -0800795
Alex Klein1699fab2022-09-08 08:46:06 -0600796 def start_ui_side_effect(*args, **kwargs):
797 # pylint: disable=unused-argument
798 self._ran_start_command = True
Yuke Liao24fc60c2020-12-26 22:16:49 -0800799
Alex Klein1699fab2022-09-08 08:46:06 -0600800 self.rc.AddCmdResult(
801 partial_mock.In("start ui"), side_effect=start_ui_side_effect
802 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800803
Alex Klein1699fab2022-09-08 08:46:06 -0600804 def prepareDeploy(self, options=None):
805 if not options:
806 options = _ParseCommandLine(
807 [
808 "--lacros",
809 "--nostrip",
810 "--build-dir",
811 "/path/to/nowhere",
812 "--device",
813 "monkey",
814 ]
815 )
816 self.deploy = deploy_chrome.DeployChrome(
817 options, self.tempdir, os.path.join(self.tempdir, "staging")
818 )
Erik Chen75a2f492020-08-06 19:15:11 -0700819
Alex Klein1699fab2022-09-08 08:46:06 -0600820 # These methods being mocked are all side effects expected for a --lacros
821 # deploy.
822 self.deploy._EnsureTargetDir = mock.Mock()
823 self.deploy._GetDeviceInfo = mock.Mock()
824 self.deploy._CheckConnection = mock.Mock()
825 self.deploy._MountRootfsAsWritable = mock.Mock()
826 self.deploy._PrepareStagingDir = mock.Mock()
827 self.deploy._CheckDeviceFreeSpace = mock.Mock()
828 self.deploy._KillAshChromeIfNeeded = mock.Mock()
Erik Chen75a2f492020-08-06 19:15:11 -0700829
Alex Klein1699fab2022-09-08 08:46:06 -0600830 def testConfNotModified(self):
831 """When the conf file is not modified we don't restart chrome ."""
832 self.prepareDeploy()
833 self.deploy.Perform()
834 self.deploy._KillAshChromeIfNeeded.assert_not_called()
835 self.assertFalse(self._ran_start_command)
Erik Chen75a2f492020-08-06 19:15:11 -0700836
Alex Klein1699fab2022-09-08 08:46:06 -0600837 def testConfModified(self):
838 """When the conf file is modified we restart chrome."""
839 self.prepareDeploy()
Erik Chen75a2f492020-08-06 19:15:11 -0700840
Alex Klein1699fab2022-09-08 08:46:06 -0600841 # We intentionally add '\n' to MODIFIED_CONF_FILE to simulate echo adding a
842 # newline when invoked in the shell.
843 self.rc.AddCmdResult(
844 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
845 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
846 )
Erik Chen75a2f492020-08-06 19:15:11 -0700847
Alex Klein1699fab2022-09-08 08:46:06 -0600848 self.deploy.Perform()
849 self.deploy._KillAshChromeIfNeeded.assert_called()
850 self.assertTrue(self._ran_start_command)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800851
Alex Klein1699fab2022-09-08 08:46:06 -0600852 def testSkipModifyingConf(self):
853 """SKip modifying the config file when the argument is specified."""
854 self.prepareDeploy(
855 _ParseCommandLine(
856 [
857 "--lacros",
858 "--nostrip",
859 "--build-dir",
860 "/path/to/nowhere",
861 "--device",
862 "monkey",
863 "--skip-modifying-config-file",
864 ]
865 )
866 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800867
Alex Klein1699fab2022-09-08 08:46:06 -0600868 self.rc.AddCmdResult(
869 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
870 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
871 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800872
Alex Klein1699fab2022-09-08 08:46:06 -0600873 self.deploy.Perform()
874 self.deploy._KillAshChromeIfNeeded.assert_not_called()
875 self.assertFalse(self._ran_start_command)