blob: 206bdf714320a64c53f2ad493bdf6d95b7d51b1b [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
Eriko Kurimotoe01cbdf2023-06-01 14:07:28 +0900134 def testLacrosWithLacrosOnly(self):
135 """Test lacros invocation with skip restarting ui."""
136 argv = [
137 "--lacros",
138 "--build-dir",
139 "/path/to/nowhere",
140 "--device",
141 "monkey",
142 "--board",
143 "atlas",
144 "--skip-restart-ui",
145 ]
146 options = _ParseCommandLine(argv)
147 self.assertTrue(options.lacros)
148 self.assertEqual(options.target_dir, deploy_chrome.LACROS_DIR)
149 self.assertTrue(options.skip_restart_ui)
150
Alex Klein1699fab2022-09-08 08:46:06 -0600151 def assertParseError(self, argv):
152 with self.OutputCapturer():
153 self.assertRaises2(
154 SystemExit, _ParseCommandLine, argv, check_attrs={"code": 2}
155 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700156
Alex Klein1699fab2022-09-08 08:46:06 -0600157 def testMountOptionSetsTargetDir(self):
158 argv = list(_REGULAR_TO) + [
159 "--board",
160 _TARGET_BOARD,
161 "--gs-path",
162 _GS_PATH,
163 "--mount",
164 ]
165 options = _ParseCommandLine(argv)
166 self.assertIsNot(options.target_dir, None)
Thiago Goncales12793312013-05-23 11:26:17 -0700167
Alex Klein1699fab2022-09-08 08:46:06 -0600168 def testMountOptionSetsMountDir(self):
169 argv = list(_REGULAR_TO) + [
170 "--board",
171 _TARGET_BOARD,
172 "--gs-path",
173 _GS_PATH,
174 "--mount",
175 ]
176 options = _ParseCommandLine(argv)
177 self.assertIsNot(options.mount_dir, None)
Thiago Goncales12793312013-05-23 11:26:17 -0700178
Alex Klein1699fab2022-09-08 08:46:06 -0600179 def testMountOptionDoesNotOverrideTargetDir(self):
180 argv = list(_REGULAR_TO) + [
181 "--board",
182 _TARGET_BOARD,
183 "--gs-path",
184 _GS_PATH,
185 "--mount",
186 "--target-dir",
187 "/foo/bar/cow",
188 ]
189 options = _ParseCommandLine(argv)
190 self.assertEqual(options.target_dir, "/foo/bar/cow")
Thiago Goncales12793312013-05-23 11:26:17 -0700191
Alex Klein1699fab2022-09-08 08:46:06 -0600192 def testMountOptionDoesNotOverrideMountDir(self):
193 argv = list(_REGULAR_TO) + [
194 "--board",
195 _TARGET_BOARD,
196 "--gs-path",
197 _GS_PATH,
198 "--mount",
199 "--mount-dir",
200 "/foo/bar/cow",
201 ]
202 options = _ParseCommandLine(argv)
203 self.assertEqual(options.mount_dir, "/foo/bar/cow")
Thiago Goncales12793312013-05-23 11:26:17 -0700204
Alex Klein1699fab2022-09-08 08:46:06 -0600205 def testSshIdentityOptionSetsOption(self):
206 argv = list(_REGULAR_TO) + [
207 "--board",
208 _TARGET_BOARD,
209 "--private-key",
210 "/foo/bar/key",
211 "--build-dir",
212 "/path/to/nowhere",
213 ]
214 options = _ParseCommandLine(argv)
215 self.assertEqual(options.private_key, "/foo/bar/key")
216
Joel Hockey764728e2023-03-14 17:10:04 -0700217 def testUnlockPassword(self):
218 argv = list(_REGULAR_TO) + [
219 "--board",
220 _TARGET_BOARD,
221 "--unlock-password",
222 "letmein",
223 "--build-dir",
224 "/path/to/nowhere",
225 ]
226 options = _ParseCommandLine(argv)
227 self.assertEqual(options.unlock_password, "letmein")
228
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700229
230class DeployChromeMock(partial_mock.PartialMock):
Alex Klein1699fab2022-09-08 08:46:06 -0600231 """Deploy Chrome Mock Class."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 TARGET = "chromite.scripts.deploy_chrome.DeployChrome"
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000234 ATTRS = (
235 "_ChromeFileInUse",
236 "_DisableRootfsVerification",
237 "_ShouldUseCompressedAsh",
238 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 def __init__(self):
241 partial_mock.PartialMock.__init__(self)
242 self.remote_device_mock = remote_access_unittest.RemoteDeviceMock()
243 # Target starts off as having rootfs verification enabled.
244 self.rsh_mock = remote_access_unittest.RemoteShMock()
245 self.rsh_mock.SetDefaultCmdResult(0)
246 self.MockMountCmd(1)
247 self.rsh_mock.AddCmdResult(
248 deploy_chrome.LSOF_COMMAND_CHROME % (deploy_chrome._CHROME_DIR,), 1
249 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700250
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000251 self.rsh_mock.AddCmdResult(
252 "status ui", stdout="ui start/running, process 123"
253 )
254
Alex Klein1699fab2022-09-08 08:46:06 -0600255 def MockMountCmd(self, returnvalue):
256 self.rsh_mock.AddCmdResult(deploy_chrome.MOUNT_RW_COMMAND, returnvalue)
David James88e6f032013-03-02 08:13:20 -0800257
Alex Klein1699fab2022-09-08 08:46:06 -0600258 def _DisableRootfsVerification(self, inst):
259 with mock.patch.object(time, "sleep"):
260 self.backup["_DisableRootfsVerification"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700261
Alex Klein1699fab2022-09-08 08:46:06 -0600262 def PreStart(self):
263 self.remote_device_mock.start()
264 self.rsh_mock.start()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700265
Alex Klein1699fab2022-09-08 08:46:06 -0600266 def PreStop(self):
267 self.rsh_mock.stop()
268 self.remote_device_mock.stop()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700269
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000270 def _ChromeFileInUse(self, _inst):
271 # Fully stub out for now. Can be replaced if further testing is added.
272 return False
273
274 def _ShouldUseCompressedAsh(self, inst):
275 with mock.patch.object(
276 remote_access.RemoteDevice, "IfFileExists"
277 ) as exists_mock:
278 exists_mock.return_value = False
279 self.backup["_ShouldUseCompressedAsh"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700280
281
Ryan Cuief91e702013-02-04 12:06:36 -0800282class DeployTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600283 """Setup a deploy object with a GS-path for use in tests."""
Steve Funge984a532013-11-25 17:09:25 -0800284
Alex Klein1699fab2022-09-08 08:46:06 -0600285 def _GetDeployChrome(self, args):
286 options = _ParseCommandLine(args)
287 return deploy_chrome.DeployChrome(
288 options, self.tempdir, os.path.join(self.tempdir, "staging")
289 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 def setUp(self):
292 self.deploy_mock = self.StartPatcher(DeployChromeMock())
293 self.deploy = self._GetDeployChrome(
294 list(_REGULAR_TO)
295 + [
296 "--board",
297 _TARGET_BOARD,
298 "--gs-path",
299 _GS_PATH,
300 "--force",
301 "--mount",
302 ]
303 )
304 self.remote_reboot_mock = self.PatchObject(
305 remote_access.RemoteAccess, "RemoteReboot", return_value=True
306 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700307
George Burgess IV4c876ac2022-12-07 10:33:49 -0700308 def tearDown(self):
309 self.deploy.Cleanup()
310
Avery Musbach3edff0e2020-03-27 13:35:53 -0700311
312class TestCheckIfBoardMatches(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600313 """Testing checking whether the DUT board matches the target board."""
Avery Musbach3edff0e2020-03-27 13:35:53 -0700314
Alex Klein1699fab2022-09-08 08:46:06 -0600315 def testMatchedBoard(self):
316 """Test the case where the DUT board matches the target board."""
317 self.PatchObject(remote_access.ChromiumOSDevice, "board", _TARGET_BOARD)
318 self.assertTrue(self.deploy.options.force)
319 self.deploy._CheckBoard()
320 self.deploy.options.force = False
321 self.deploy._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700322
Alex Klein1699fab2022-09-08 08:46:06 -0600323 def testMismatchedBoard(self):
324 """Test the case where the DUT board does not match the target board."""
325 self.PatchObject(remote_access.ChromiumOSDevice, "board", "cedar")
326 self.assertTrue(self.deploy.options.force)
327 self.deploy._CheckBoard()
328 self.deploy.options.force = False
329 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=True)
330 self.deploy._CheckBoard()
331 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=False)
332 self.assertRaises(deploy_chrome.DeployFailure, self.deploy._CheckBoard)
Avery Musbach3edff0e2020-03-27 13:35:53 -0700333
334
David James88e6f032013-03-02 08:13:20 -0800335class TestDisableRootfsVerification(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600336 """Testing disabling of rootfs verification and RO mode."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700337
Alex Klein1699fab2022-09-08 08:46:06 -0600338 def testDisableRootfsVerificationSuccess(self):
339 """Test the working case, disabling rootfs verification."""
340 self.deploy_mock.MockMountCmd(0)
341 self.deploy._DisableRootfsVerification()
342 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 def testDisableRootfsVerificationFailure(self):
345 """Test failure to disable rootfs verification."""
Mike Frysinger16474792023-03-01 01:18:00 -0500346
Alex Klein1699fab2022-09-08 08:46:06 -0600347 # pylint: disable=unused-argument
348 def RaiseRunCommandError(timeout_sec=None):
349 raise cros_build_lib.RunCommandError("Mock RunCommandError")
350
351 self.remote_reboot_mock.side_effect = RaiseRunCommandError
352 self.assertRaises(
353 cros_build_lib.RunCommandError,
354 self.deploy._DisableRootfsVerification,
355 )
356 self.remote_reboot_mock.side_effect = None
357 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800358
359
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000360class TestDeployCompressedAsh(DeployTest):
361 """Testing deployments with --ash-compressed passed."""
362
363 def _GetDeployChrome(self, args):
364 args.append("--compressed-ash")
365 return super(TestDeployCompressedAsh, self)._GetDeployChrome(args)
366
367 def testUnmountSuccess(self):
368 """Test case for a successful 'umount' call."""
369 self.deploy._KillAshChromeIfNeeded()
370
371 def testUnmountFailure(self):
372 """Test case for a failed 'umount' call."""
373 self.deploy_mock.rsh_mock.AddCmdResult(
374 ["umount", deploy_chrome.RAW_ASH_PATH],
375 returncode=32,
376 stderr="umount failure",
377 )
378 self.assertRaises(
379 deploy_chrome.DeployFailure, self.deploy._KillAshChromeIfNeeded
380 )
381
382
David James88e6f032013-03-02 08:13:20 -0800383class TestMount(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600384 """Testing mount success and failure."""
David James88e6f032013-03-02 08:13:20 -0800385
Alex Klein1699fab2022-09-08 08:46:06 -0600386 def testSuccess(self):
387 """Test case where we are able to mount as writable."""
388 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
389 self.deploy_mock.MockMountCmd(0)
390 self.deploy._MountRootfsAsWritable()
391 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800392
Alex Klein1699fab2022-09-08 08:46:06 -0600393 def testMountError(self):
394 """Test that mount failure doesn't raise an exception by default."""
395 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
396 self.PatchObject(
397 remote_access.ChromiumOSDevice,
398 "IsDirWritable",
399 return_value=False,
400 autospec=True,
401 )
402 self.deploy._MountRootfsAsWritable()
403 self.assertTrue(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700404
Alex Klein1699fab2022-09-08 08:46:06 -0600405 def testMountRwFailure(self):
406 """Test that mount failure raises an exception if check=True."""
407 self.assertRaises(
408 cros_build_lib.RunCommandError,
409 self.deploy._MountRootfsAsWritable,
410 check=True,
411 )
412 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Robert Flack1dc7ea82014-11-26 13:50:24 -0500413
Alex Klein1699fab2022-09-08 08:46:06 -0600414 def testMountTempDir(self):
415 """Test that mount succeeds if target dir is writable."""
416 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
417 self.PatchObject(
418 remote_access.ChromiumOSDevice,
419 "IsDirWritable",
420 return_value=True,
421 autospec=True,
422 )
423 self.deploy._MountRootfsAsWritable()
424 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700425
426
Anushruth8d797672019-10-17 12:22:31 -0700427class TestMountTarget(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600428 """Testing mount and umount command handling."""
Anushruth8d797672019-10-17 12:22:31 -0700429
Alex Klein1699fab2022-09-08 08:46:06 -0600430 def testMountTargetUmountFailure(self):
431 """Test error being thrown if umount fails.
Anushruth8d797672019-10-17 12:22:31 -0700432
Alex Klein68b270c2023-04-14 14:42:50 -0600433 Test that 'lsof' is run on mount-dir and 'mount -rbind' command is not
434 run if 'umount' cmd fails.
Alex Klein1699fab2022-09-08 08:46:06 -0600435 """
436 mount_dir = self.deploy.options.mount_dir
437 target_dir = self.deploy.options.target_dir
438 self.deploy_mock.rsh_mock.AddCmdResult(
439 deploy_chrome._UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": mount_dir},
440 returncode=errno.EBUSY,
441 stderr="Target is Busy",
442 )
443 self.deploy_mock.rsh_mock.AddCmdResult(
444 deploy_chrome.LSOF_COMMAND % (mount_dir,),
445 returncode=0,
446 stdout="process " + mount_dir,
447 )
448 # Check for RunCommandError being thrown.
449 self.assertRaises(
450 cros_build_lib.RunCommandError, self.deploy._MountTarget
451 )
452 # Check for the 'mount -rbind' command not run.
453 self.deploy_mock.rsh_mock.assertCommandContains(
454 (deploy_chrome._BIND_TO_FINAL_DIR_CMD % (target_dir, mount_dir)),
455 expected=False,
456 )
457 # Check for lsof command being called.
458 self.deploy_mock.rsh_mock.assertCommandContains(
459 (deploy_chrome.LSOF_COMMAND % (mount_dir,))
460 )
Anushruth8d797672019-10-17 12:22:31 -0700461
462
Ryan Cuief91e702013-02-04 12:06:36 -0800463class TestUiJobStarted(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600464 """Test detection of a running 'ui' job."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700465
Alex Klein1699fab2022-09-08 08:46:06 -0600466 def MockStatusUiCmd(self, **kwargs):
467 self.deploy_mock.rsh_mock.AddCmdResult("status ui", **kwargs)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700468
Alex Klein1699fab2022-09-08 08:46:06 -0600469 def testUiJobStartedFalse(self):
470 """Correct results with a stopped job."""
471 self.MockStatusUiCmd(stdout="ui stop/waiting")
472 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuif2d1a582013-02-19 14:08:13 -0800473
Alex Klein1699fab2022-09-08 08:46:06 -0600474 def testNoUiJob(self):
475 """Correct results when the job doesn't exist."""
476 self.MockStatusUiCmd(stderr="start: Unknown job: ui", returncode=1)
477 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700478
Alex Klein1699fab2022-09-08 08:46:06 -0600479 def testCheckRootfsWriteableTrue(self):
480 """Correct results with a running job."""
481 self.MockStatusUiCmd(stdout="ui start/running, process 297")
482 self.assertTrue(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700483
484
Joel Hockey764728e2023-03-14 17:10:04 -0700485class TestUnlockPassword(DeployTest):
486 """Test that unlock password is sent."""
487
488 def _GetDeployChrome(self, args):
489 args.append("--unlock-password=letmein")
490 return super(TestUnlockPassword, self)._GetDeployChrome(args)
491
492 def testUnlock(self):
493 """Test that unlock password is sent."""
494 self.deploy._stopped_ui = True
495
496 # Update LAST_LOGIN_COMMAND to return a different value.
497 def SideEffect(*args, **kwargs):
498 # pylint: disable=unused-argument
499 self.deploy_mock.rsh_mock.AddCmdResult(
500 deploy_chrome.LAST_LOGIN_COMMAND, stdout="2.0"
501 )
502
503 # LAST_LOGIN_COMMAND returns 1.0 the first time it is called, then 2.0.
504 self.deploy_mock.rsh_mock.AddCmdResult(
505 deploy_chrome.LAST_LOGIN_COMMAND,
506 stdout="1.0",
507 side_effect=SideEffect,
508 )
509 with mock.patch.object(remote_access.ChromiumOSDevice, "CopyToDevice"):
510 with mock.patch.object(time, "sleep"):
511 self.deploy._Deploy()
512 # Ensure unlock command was called.
513 self.deploy_mock.rsh_mock.assertCommandContains(
514 deploy_chrome.UNLOCK_PASSWORD_COMMAND % "letmein"
515 )
516
517
Ryan Cuief91e702013-02-04 12:06:36 -0800518class StagingTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600519 """Test user-mode and ebuild-mode staging functionality."""
Ryan Cuief91e702013-02-04 12:06:36 -0800520
Alex Klein1699fab2022-09-08 08:46:06 -0600521 def setUp(self):
522 self.staging_dir = os.path.join(self.tempdir, "staging")
523 osutils.SafeMakedirs(self.staging_dir)
524 self.staging_tarball_path = os.path.join(
525 self.tempdir, deploy_chrome._CHROME_DIR_STAGING_TARBALL_ZSTD
526 )
527 self.build_dir = os.path.join(self.tempdir, "build_dir")
528 self.common_flags = [
529 "--board",
530 _TARGET_BOARD,
531 "--build-dir",
532 self.build_dir,
533 "--staging-only",
534 "--cache-dir",
535 str(self.tempdir),
536 ]
537 self.sdk_mock = self.StartPatcher(
538 cros_chrome_sdk_unittest.SDKFetcherMock()
539 )
540 self.PatchObject(
541 osutils,
542 "SourceEnvironment",
543 autospec=True,
544 return_value={"STRIP": "x86_64-cros-linux-gnu-strip"},
545 )
Ryan Cuief91e702013-02-04 12:06:36 -0800546
Alex Klein1699fab2022-09-08 08:46:06 -0600547 def testSingleFileDeployFailure(self):
548 """Default staging enforces that mandatory files are copied"""
549 options = _ParseCommandLine(self.common_flags)
550 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
551 self.assertRaises(
552 chrome_util.MissingPathError,
553 deploy_chrome._PrepareStagingDir,
554 options,
555 self.tempdir,
556 self.staging_dir,
557 chrome_util._COPY_PATHS_CHROME,
558 )
Ryan Cuief91e702013-02-04 12:06:36 -0800559
Alex Klein1699fab2022-09-08 08:46:06 -0600560 def testSloppyDeployFailure(self):
561 """Sloppy staging enforces that at least one file is copied."""
562 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
563 self.assertRaises(
564 chrome_util.MissingPathError,
565 deploy_chrome._PrepareStagingDir,
566 options,
567 self.tempdir,
568 self.staging_dir,
569 chrome_util._COPY_PATHS_CHROME,
570 )
David Jamesa6e08892013-03-01 13:34:11 -0800571
Alex Klein1699fab2022-09-08 08:46:06 -0600572 def testSloppyDeploySuccess(self):
573 """Sloppy staging - stage one file."""
574 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
575 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
576 deploy_chrome._PrepareStagingDir(
577 options,
578 self.tempdir,
579 self.staging_dir,
580 chrome_util._COPY_PATHS_CHROME,
581 )
David Jamesa6e08892013-03-01 13:34:11 -0800582
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000583 def testSloppyDeploySuccessLacros(self):
584 """Ensure the squashfs mechanism with --compressed-ash doesn't throw."""
585 options = _ParseCommandLine(
586 self.common_flags + ["--sloppy", "--compressed-ash"]
587 )
588 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
589 deploy_chrome._PrepareStagingDir(
590 options,
591 self.tempdir,
592 self.staging_dir,
593 chrome_util._COPY_PATHS_CHROME,
594 )
595
Alex Klein1699fab2022-09-08 08:46:06 -0600596 @cros_test_lib.pytestmark_network_test
597 def testUploadStagingDir(self):
598 """Upload staging directory."""
599 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
600 staging_upload = "gs://some-path"
601 options = _ParseCommandLine(
602 self.common_flags + ["--staging-upload", staging_upload]
603 )
604 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
605 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
606 self.assertEqual(
607 mockGsCopy.call_args_list,
608 [
609 mock.call(self.staging_tarball_path, staging_upload, acl=""),
610 ],
611 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000612
Alex Klein1699fab2022-09-08 08:46:06 -0600613 @cros_test_lib.pytestmark_network_test
614 def testUploadStagingPublicReadACL(self):
615 """Upload staging directory with public-read ACL."""
616 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
617 staging_upload = "gs://some-path"
618 options = _ParseCommandLine(
619 self.common_flags
620 + ["--staging-upload", staging_upload, "--public-read"]
621 )
622 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
623 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
624 self.assertEqual(
625 mockGsCopy.call_args_list,
626 [
627 mock.call(
628 self.staging_tarball_path, staging_upload, acl="public-read"
629 ),
630 ],
631 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000632
Steve Funge984a532013-11-25 17:09:25 -0800633
634class DeployTestBuildDir(cros_test_lib.MockTempDirTestCase):
Alex Klein68b270c2023-04-14 14:42:50 -0600635 """Set up a deploy object with a build-dir for use in deployment tests"""
Steve Funge984a532013-11-25 17:09:25 -0800636
Alex Klein1699fab2022-09-08 08:46:06 -0600637 def _GetDeployChrome(self, args):
638 options = _ParseCommandLine(args)
639 return deploy_chrome.DeployChrome(
640 options, self.tempdir, os.path.join(self.tempdir, "staging")
641 )
Steve Funge984a532013-11-25 17:09:25 -0800642
Alex Klein1699fab2022-09-08 08:46:06 -0600643 def setUp(self):
644 self.staging_dir = os.path.join(self.tempdir, "staging")
645 self.build_dir = os.path.join(self.tempdir, "build_dir")
646 self.deploy_mock = self.StartPatcher(DeployChromeMock())
647 self.deploy = self._GetDeployChrome(
648 list(_REGULAR_TO)
649 + [
650 "--board",
651 _TARGET_BOARD,
652 "--build-dir",
653 self.build_dir,
654 "--staging-only",
655 "--cache-dir",
656 str(self.tempdir),
657 "--sloppy",
658 ]
659 )
Steve Funge984a532013-11-25 17:09:25 -0800660
Alex Klein1699fab2022-09-08 08:46:06 -0600661 def getCopyPath(self, source_path):
662 """Return a chrome_util.Path or None if not present."""
663 paths = [p for p in self.deploy.copy_paths if p.src == source_path]
664 return paths[0] if paths else None
665
Steve Funge984a532013-11-25 17:09:25 -0800666
Daniel Erat1ae46382014-08-14 10:23:39 -0700667class TestDeploymentType(DeployTestBuildDir):
Alex Klein1699fab2022-09-08 08:46:06 -0600668 """Test detection of deployment type using build dir."""
Steve Funge984a532013-11-25 17:09:25 -0800669
Alex Klein1699fab2022-09-08 08:46:06 -0600670 def testAppShellDetection(self):
671 """Check for an app_shell deployment"""
672 osutils.Touch(
673 os.path.join(self.deploy.options.build_dir, "app_shell"),
674 makedirs=True,
675 )
676 self.deploy._CheckDeployType()
677 self.assertTrue(self.getCopyPath("app_shell"))
678 self.assertFalse(self.getCopyPath("chrome"))
Steve Funge984a532013-11-25 17:09:25 -0800679
Alex Klein1699fab2022-09-08 08:46:06 -0600680 def testChromeAndAppShellDetection(self):
681 """Check for a chrome deployment when app_shell also exists."""
682 osutils.Touch(
683 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
684 )
685 osutils.Touch(
686 os.path.join(self.deploy.options.build_dir, "app_shell"),
687 makedirs=True,
688 )
689 self.deploy._CheckDeployType()
690 self.assertTrue(self.getCopyPath("chrome"))
691 self.assertFalse(self.getCopyPath("app_shell"))
Steve Fung63d705d2014-03-16 03:14:03 -0700692
Alex Klein1699fab2022-09-08 08:46:06 -0600693 def testChromeDetection(self):
694 """Check for a regular chrome deployment"""
695 osutils.Touch(
696 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
697 )
698 self.deploy._CheckDeployType()
699 self.assertTrue(self.getCopyPath("chrome"))
700 self.assertFalse(self.getCopyPath("app_shell"))
Ben Pastenee484b342020-06-30 18:29:27 -0700701
702
703class TestDeployTestBinaries(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600704 """Tests _DeployTestBinaries()."""
Ben Pastenee484b342020-06-30 18:29:27 -0700705
Alex Klein1699fab2022-09-08 08:46:06 -0600706 def setUp(self):
707 options = _ParseCommandLine(
708 list(_REGULAR_TO)
709 + [
710 "--board",
711 _TARGET_BOARD,
712 "--force",
713 "--mount",
714 "--build-dir",
715 os.path.join(self.tempdir, "build_dir"),
716 "--nostrip",
717 ]
718 )
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000719 self.remote_exists_mock = self.PatchObject(
720 remote_access.RemoteDevice, "IfFileExists", return_value=False
721 )
Alex Klein1699fab2022-09-08 08:46:06 -0600722 self.deploy = deploy_chrome.DeployChrome(
723 options, self.tempdir, os.path.join(self.tempdir, "staging")
724 )
Ben Pastenee484b342020-06-30 18:29:27 -0700725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 def _SimulateBinaries(self):
727 # Ensure the staging dir contains the right binaries to copy over.
728 test_binaries = [
729 "run_a_tests",
730 "run_b_tests",
731 "run_c_tests",
732 ]
733 # Simulate having the binaries both on the device and in our local build
734 # dir.
735 self.rc.AddCmdResult(
736 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD),
737 stdout="\n".join(test_binaries),
738 )
739 for binary in test_binaries:
740 osutils.Touch(
741 os.path.join(self.deploy.options.build_dir, binary),
742 makedirs=True,
743 mode=0o700,
744 )
745 return test_binaries
Ben Pastenee484b342020-06-30 18:29:27 -0700746
Alex Klein1699fab2022-09-08 08:46:06 -0600747 def _AssertBinariesInStagingDir(self, test_binaries):
Alex Klein68b270c2023-04-14 14:42:50 -0600748 # Ensure the binaries were placed in the staging dir used to copy them
749 # over.
Alex Klein1699fab2022-09-08 08:46:06 -0600750 staging_dir = os.path.join(
751 self.tempdir, os.path.basename(deploy_chrome._CHROME_TEST_BIN_DIR)
752 )
753 for binary in test_binaries:
754 self.assertIn(binary, os.listdir(staging_dir))
Erik Chen75a2f492020-08-06 19:15:11 -0700755
Alex Klein1699fab2022-09-08 08:46:06 -0600756 def testFindError(self):
757 """Ensure an error is thrown if we can't inspect the device."""
758 self.rc.AddCmdResult(
759 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD), 1
760 )
761 self.assertRaises(
762 deploy_chrome.DeployFailure, self.deploy._DeployTestBinaries
763 )
Brian Sheedy86f12342020-10-29 15:30:02 -0700764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 def testSuccess(self):
766 """Ensure that the happy path succeeds as expected."""
767 test_binaries = self._SimulateBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700768 self.deploy._DeployTestBinaries()
Alex Klein1699fab2022-09-08 08:46:06 -0600769 self._AssertBinariesInStagingDir(test_binaries)
770
771 def testRetrySuccess(self):
772 """Ensure that a transient exception still results in success."""
Mike Frysinger16474792023-03-01 01:18:00 -0500773
Alex Klein68b270c2023-04-14 14:42:50 -0600774 # Raises a RunCommandError on its first invocation, but passes on
775 # subsequent calls.
Alex Klein1699fab2022-09-08 08:46:06 -0600776 def SideEffect(*args, **kwargs):
777 # pylint: disable=unused-argument
778 if not SideEffect.called:
779 SideEffect.called = True
780 raise cros_build_lib.RunCommandError("fail")
781
782 SideEffect.called = False
783
784 test_binaries = self._SimulateBinaries()
785 with mock.patch.object(
786 remote_access.ChromiumOSDevice,
787 "CopyToDevice",
788 side_effect=SideEffect,
789 ) as copy_mock:
790 self.deploy._DeployTestBinaries()
791 self.assertEqual(copy_mock.call_count, 2)
792 self._AssertBinariesInStagingDir(test_binaries)
793
794 def testRetryFailure(self):
795 """Ensure that consistent exceptions result in failure."""
796 self._SimulateBinaries()
797 with self.assertRaises(cros_build_lib.RunCommandError):
798 with mock.patch.object(
799 remote_access.ChromiumOSDevice,
800 "CopyToDevice",
801 side_effect=cros_build_lib.RunCommandError("fail"),
802 ):
803 self.deploy._DeployTestBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700804
Erik Chen75a2f492020-08-06 19:15:11 -0700805
806class LacrosPerformTest(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600807 """Line coverage for Perform() method with --lacros option."""
Erik Chen75a2f492020-08-06 19:15:11 -0700808
Alex Klein1699fab2022-09-08 08:46:06 -0600809 def setUp(self):
810 self.deploy = None
811 self._ran_start_command = False
812 self.StartPatcher(parallel_unittest.ParallelMock())
Yuke Liao24fc60c2020-12-26 22:16:49 -0800813
Alex Klein1699fab2022-09-08 08:46:06 -0600814 def start_ui_side_effect(*args, **kwargs):
815 # pylint: disable=unused-argument
816 self._ran_start_command = True
Yuke Liao24fc60c2020-12-26 22:16:49 -0800817
Alex Klein1699fab2022-09-08 08:46:06 -0600818 self.rc.AddCmdResult(
819 partial_mock.In("start ui"), side_effect=start_ui_side_effect
820 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800821
Alex Klein1699fab2022-09-08 08:46:06 -0600822 def prepareDeploy(self, options=None):
823 if not options:
824 options = _ParseCommandLine(
825 [
826 "--lacros",
827 "--nostrip",
828 "--build-dir",
829 "/path/to/nowhere",
830 "--device",
831 "monkey",
832 ]
833 )
834 self.deploy = deploy_chrome.DeployChrome(
835 options, self.tempdir, os.path.join(self.tempdir, "staging")
836 )
Erik Chen75a2f492020-08-06 19:15:11 -0700837
Alex Klein68b270c2023-04-14 14:42:50 -0600838 # These methods being mocked are all side effects expected for a
839 # --lacros deploy.
Alex Klein1699fab2022-09-08 08:46:06 -0600840 self.deploy._EnsureTargetDir = mock.Mock()
841 self.deploy._GetDeviceInfo = mock.Mock()
842 self.deploy._CheckConnection = mock.Mock()
843 self.deploy._MountRootfsAsWritable = mock.Mock()
844 self.deploy._PrepareStagingDir = mock.Mock()
845 self.deploy._CheckDeviceFreeSpace = mock.Mock()
846 self.deploy._KillAshChromeIfNeeded = mock.Mock()
Erik Chen75a2f492020-08-06 19:15:11 -0700847
Eriko Kurimotoe01cbdf2023-06-01 14:07:28 +0900848 def testLacros(self):
849 """When no flag is set, Ash should be restarted."""
Alex Klein1699fab2022-09-08 08:46:06 -0600850 self.prepareDeploy()
Erik Chen75a2f492020-08-06 19:15:11 -0700851
Alex Klein1699fab2022-09-08 08:46:06 -0600852 self.deploy.Perform()
853 self.deploy._KillAshChromeIfNeeded.assert_called()
854 self.assertTrue(self._ran_start_command)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800855
Eriko Kurimotoe01cbdf2023-06-01 14:07:28 +0900856 def testSkipRestartUi(self):
857 """When skip_restart_ui is enabled, Ash should not be restarted."""
Alex Klein1699fab2022-09-08 08:46:06 -0600858 self.prepareDeploy(
859 _ParseCommandLine(
860 [
861 "--lacros",
862 "--nostrip",
863 "--build-dir",
864 "/path/to/nowhere",
865 "--device",
866 "monkey",
Eriko Kurimotoe01cbdf2023-06-01 14:07:28 +0900867 "--skip-restart-ui",
Alex Klein1699fab2022-09-08 08:46:06 -0600868 ]
869 )
870 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800871
Alex Klein1699fab2022-09-08 08:46:06 -0600872 self.deploy.Perform()
873 self.deploy._KillAshChromeIfNeeded.assert_not_called()
874 self.assertFalse(self._ran_start_command)