blob: f847089e763a92138895976148d7aeed5f6c9b1c [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
Avery Musbach3edff0e2020-03-27 13:35:53 -0700279
280class TestCheckIfBoardMatches(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600281 """Testing checking whether the DUT board matches the target board."""
Avery Musbach3edff0e2020-03-27 13:35:53 -0700282
Alex Klein1699fab2022-09-08 08:46:06 -0600283 def testMatchedBoard(self):
284 """Test the case where the DUT board matches the target board."""
285 self.PatchObject(remote_access.ChromiumOSDevice, "board", _TARGET_BOARD)
286 self.assertTrue(self.deploy.options.force)
287 self.deploy._CheckBoard()
288 self.deploy.options.force = False
289 self.deploy._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 def testMismatchedBoard(self):
292 """Test the case where the DUT board does not match the target board."""
293 self.PatchObject(remote_access.ChromiumOSDevice, "board", "cedar")
294 self.assertTrue(self.deploy.options.force)
295 self.deploy._CheckBoard()
296 self.deploy.options.force = False
297 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=True)
298 self.deploy._CheckBoard()
299 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=False)
300 self.assertRaises(deploy_chrome.DeployFailure, self.deploy._CheckBoard)
Avery Musbach3edff0e2020-03-27 13:35:53 -0700301
302
David James88e6f032013-03-02 08:13:20 -0800303class TestDisableRootfsVerification(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600304 """Testing disabling of rootfs verification and RO mode."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 def testDisableRootfsVerificationSuccess(self):
307 """Test the working case, disabling rootfs verification."""
308 self.deploy_mock.MockMountCmd(0)
309 self.deploy._DisableRootfsVerification()
310 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700311
Alex Klein1699fab2022-09-08 08:46:06 -0600312 def testDisableRootfsVerificationFailure(self):
313 """Test failure to disable rootfs verification."""
314 # pylint: disable=unused-argument
315 def RaiseRunCommandError(timeout_sec=None):
316 raise cros_build_lib.RunCommandError("Mock RunCommandError")
317
318 self.remote_reboot_mock.side_effect = RaiseRunCommandError
319 self.assertRaises(
320 cros_build_lib.RunCommandError,
321 self.deploy._DisableRootfsVerification,
322 )
323 self.remote_reboot_mock.side_effect = None
324 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800325
326
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000327class TestDeployCompressedAsh(DeployTest):
328 """Testing deployments with --ash-compressed passed."""
329
330 def _GetDeployChrome(self, args):
331 args.append("--compressed-ash")
332 return super(TestDeployCompressedAsh, self)._GetDeployChrome(args)
333
334 def testUnmountSuccess(self):
335 """Test case for a successful 'umount' call."""
336 self.deploy._KillAshChromeIfNeeded()
337
338 def testUnmountFailure(self):
339 """Test case for a failed 'umount' call."""
340 self.deploy_mock.rsh_mock.AddCmdResult(
341 ["umount", deploy_chrome.RAW_ASH_PATH],
342 returncode=32,
343 stderr="umount failure",
344 )
345 self.assertRaises(
346 deploy_chrome.DeployFailure, self.deploy._KillAshChromeIfNeeded
347 )
348
349
David James88e6f032013-03-02 08:13:20 -0800350class TestMount(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600351 """Testing mount success and failure."""
David James88e6f032013-03-02 08:13:20 -0800352
Alex Klein1699fab2022-09-08 08:46:06 -0600353 def testSuccess(self):
354 """Test case where we are able to mount as writable."""
355 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
356 self.deploy_mock.MockMountCmd(0)
357 self.deploy._MountRootfsAsWritable()
358 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800359
Alex Klein1699fab2022-09-08 08:46:06 -0600360 def testMountError(self):
361 """Test that mount failure doesn't raise an exception by default."""
362 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
363 self.PatchObject(
364 remote_access.ChromiumOSDevice,
365 "IsDirWritable",
366 return_value=False,
367 autospec=True,
368 )
369 self.deploy._MountRootfsAsWritable()
370 self.assertTrue(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700371
Alex Klein1699fab2022-09-08 08:46:06 -0600372 def testMountRwFailure(self):
373 """Test that mount failure raises an exception if check=True."""
374 self.assertRaises(
375 cros_build_lib.RunCommandError,
376 self.deploy._MountRootfsAsWritable,
377 check=True,
378 )
379 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Robert Flack1dc7ea82014-11-26 13:50:24 -0500380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 def testMountTempDir(self):
382 """Test that mount succeeds if target dir is writable."""
383 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
384 self.PatchObject(
385 remote_access.ChromiumOSDevice,
386 "IsDirWritable",
387 return_value=True,
388 autospec=True,
389 )
390 self.deploy._MountRootfsAsWritable()
391 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700392
393
Anushruth8d797672019-10-17 12:22:31 -0700394class TestMountTarget(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600395 """Testing mount and umount command handling."""
Anushruth8d797672019-10-17 12:22:31 -0700396
Alex Klein1699fab2022-09-08 08:46:06 -0600397 def testMountTargetUmountFailure(self):
398 """Test error being thrown if umount fails.
Anushruth8d797672019-10-17 12:22:31 -0700399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 Test that 'lsof' is run on mount-dir and 'mount -rbind' command is not run
401 if 'umount' cmd fails.
402 """
403 mount_dir = self.deploy.options.mount_dir
404 target_dir = self.deploy.options.target_dir
405 self.deploy_mock.rsh_mock.AddCmdResult(
406 deploy_chrome._UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": mount_dir},
407 returncode=errno.EBUSY,
408 stderr="Target is Busy",
409 )
410 self.deploy_mock.rsh_mock.AddCmdResult(
411 deploy_chrome.LSOF_COMMAND % (mount_dir,),
412 returncode=0,
413 stdout="process " + mount_dir,
414 )
415 # Check for RunCommandError being thrown.
416 self.assertRaises(
417 cros_build_lib.RunCommandError, self.deploy._MountTarget
418 )
419 # Check for the 'mount -rbind' command not run.
420 self.deploy_mock.rsh_mock.assertCommandContains(
421 (deploy_chrome._BIND_TO_FINAL_DIR_CMD % (target_dir, mount_dir)),
422 expected=False,
423 )
424 # Check for lsof command being called.
425 self.deploy_mock.rsh_mock.assertCommandContains(
426 (deploy_chrome.LSOF_COMMAND % (mount_dir,))
427 )
Anushruth8d797672019-10-17 12:22:31 -0700428
429
Ryan Cuief91e702013-02-04 12:06:36 -0800430class TestUiJobStarted(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600431 """Test detection of a running 'ui' job."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700432
Alex Klein1699fab2022-09-08 08:46:06 -0600433 def MockStatusUiCmd(self, **kwargs):
434 self.deploy_mock.rsh_mock.AddCmdResult("status ui", **kwargs)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 def testUiJobStartedFalse(self):
437 """Correct results with a stopped job."""
438 self.MockStatusUiCmd(stdout="ui stop/waiting")
439 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuif2d1a582013-02-19 14:08:13 -0800440
Alex Klein1699fab2022-09-08 08:46:06 -0600441 def testNoUiJob(self):
442 """Correct results when the job doesn't exist."""
443 self.MockStatusUiCmd(stderr="start: Unknown job: ui", returncode=1)
444 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700445
Alex Klein1699fab2022-09-08 08:46:06 -0600446 def testCheckRootfsWriteableTrue(self):
447 """Correct results with a running job."""
448 self.MockStatusUiCmd(stdout="ui start/running, process 297")
449 self.assertTrue(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700450
451
Ryan Cuief91e702013-02-04 12:06:36 -0800452class StagingTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600453 """Test user-mode and ebuild-mode staging functionality."""
Ryan Cuief91e702013-02-04 12:06:36 -0800454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 def setUp(self):
456 self.staging_dir = os.path.join(self.tempdir, "staging")
457 osutils.SafeMakedirs(self.staging_dir)
458 self.staging_tarball_path = os.path.join(
459 self.tempdir, deploy_chrome._CHROME_DIR_STAGING_TARBALL_ZSTD
460 )
461 self.build_dir = os.path.join(self.tempdir, "build_dir")
462 self.common_flags = [
463 "--board",
464 _TARGET_BOARD,
465 "--build-dir",
466 self.build_dir,
467 "--staging-only",
468 "--cache-dir",
469 str(self.tempdir),
470 ]
471 self.sdk_mock = self.StartPatcher(
472 cros_chrome_sdk_unittest.SDKFetcherMock()
473 )
474 self.PatchObject(
475 osutils,
476 "SourceEnvironment",
477 autospec=True,
478 return_value={"STRIP": "x86_64-cros-linux-gnu-strip"},
479 )
Ryan Cuief91e702013-02-04 12:06:36 -0800480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 def testSingleFileDeployFailure(self):
482 """Default staging enforces that mandatory files are copied"""
483 options = _ParseCommandLine(self.common_flags)
484 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
485 self.assertRaises(
486 chrome_util.MissingPathError,
487 deploy_chrome._PrepareStagingDir,
488 options,
489 self.tempdir,
490 self.staging_dir,
491 chrome_util._COPY_PATHS_CHROME,
492 )
Ryan Cuief91e702013-02-04 12:06:36 -0800493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 def testSloppyDeployFailure(self):
495 """Sloppy staging enforces that at least one file is copied."""
496 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
497 self.assertRaises(
498 chrome_util.MissingPathError,
499 deploy_chrome._PrepareStagingDir,
500 options,
501 self.tempdir,
502 self.staging_dir,
503 chrome_util._COPY_PATHS_CHROME,
504 )
David Jamesa6e08892013-03-01 13:34:11 -0800505
Alex Klein1699fab2022-09-08 08:46:06 -0600506 def testSloppyDeploySuccess(self):
507 """Sloppy staging - stage one file."""
508 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
509 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
510 deploy_chrome._PrepareStagingDir(
511 options,
512 self.tempdir,
513 self.staging_dir,
514 chrome_util._COPY_PATHS_CHROME,
515 )
David Jamesa6e08892013-03-01 13:34:11 -0800516
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000517 def testSloppyDeploySuccessLacros(self):
518 """Ensure the squashfs mechanism with --compressed-ash doesn't throw."""
519 options = _ParseCommandLine(
520 self.common_flags + ["--sloppy", "--compressed-ash"]
521 )
522 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
523 deploy_chrome._PrepareStagingDir(
524 options,
525 self.tempdir,
526 self.staging_dir,
527 chrome_util._COPY_PATHS_CHROME,
528 )
529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 @cros_test_lib.pytestmark_network_test
531 def testUploadStagingDir(self):
532 """Upload staging directory."""
533 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
534 staging_upload = "gs://some-path"
535 options = _ParseCommandLine(
536 self.common_flags + ["--staging-upload", staging_upload]
537 )
538 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
539 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
540 self.assertEqual(
541 mockGsCopy.call_args_list,
542 [
543 mock.call(self.staging_tarball_path, staging_upload, acl=""),
544 ],
545 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000546
Alex Klein1699fab2022-09-08 08:46:06 -0600547 @cros_test_lib.pytestmark_network_test
548 def testUploadStagingPublicReadACL(self):
549 """Upload staging directory with public-read ACL."""
550 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
551 staging_upload = "gs://some-path"
552 options = _ParseCommandLine(
553 self.common_flags
554 + ["--staging-upload", staging_upload, "--public-read"]
555 )
556 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
557 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
558 self.assertEqual(
559 mockGsCopy.call_args_list,
560 [
561 mock.call(
562 self.staging_tarball_path, staging_upload, acl="public-read"
563 ),
564 ],
565 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000566
Steve Funge984a532013-11-25 17:09:25 -0800567
568class DeployTestBuildDir(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600569 """Set up a deploy object with a build-dir for use in deployment type tests"""
Steve Funge984a532013-11-25 17:09:25 -0800570
Alex Klein1699fab2022-09-08 08:46:06 -0600571 def _GetDeployChrome(self, args):
572 options = _ParseCommandLine(args)
573 return deploy_chrome.DeployChrome(
574 options, self.tempdir, os.path.join(self.tempdir, "staging")
575 )
Steve Funge984a532013-11-25 17:09:25 -0800576
Alex Klein1699fab2022-09-08 08:46:06 -0600577 def setUp(self):
578 self.staging_dir = os.path.join(self.tempdir, "staging")
579 self.build_dir = os.path.join(self.tempdir, "build_dir")
580 self.deploy_mock = self.StartPatcher(DeployChromeMock())
581 self.deploy = self._GetDeployChrome(
582 list(_REGULAR_TO)
583 + [
584 "--board",
585 _TARGET_BOARD,
586 "--build-dir",
587 self.build_dir,
588 "--staging-only",
589 "--cache-dir",
590 str(self.tempdir),
591 "--sloppy",
592 ]
593 )
Steve Funge984a532013-11-25 17:09:25 -0800594
Alex Klein1699fab2022-09-08 08:46:06 -0600595 def getCopyPath(self, source_path):
596 """Return a chrome_util.Path or None if not present."""
597 paths = [p for p in self.deploy.copy_paths if p.src == source_path]
598 return paths[0] if paths else None
599
Steve Funge984a532013-11-25 17:09:25 -0800600
Daniel Erat1ae46382014-08-14 10:23:39 -0700601class TestDeploymentType(DeployTestBuildDir):
Alex Klein1699fab2022-09-08 08:46:06 -0600602 """Test detection of deployment type using build dir."""
Steve Funge984a532013-11-25 17:09:25 -0800603
Alex Klein1699fab2022-09-08 08:46:06 -0600604 def testAppShellDetection(self):
605 """Check for an app_shell deployment"""
606 osutils.Touch(
607 os.path.join(self.deploy.options.build_dir, "app_shell"),
608 makedirs=True,
609 )
610 self.deploy._CheckDeployType()
611 self.assertTrue(self.getCopyPath("app_shell"))
612 self.assertFalse(self.getCopyPath("chrome"))
Steve Funge984a532013-11-25 17:09:25 -0800613
Alex Klein1699fab2022-09-08 08:46:06 -0600614 def testChromeAndAppShellDetection(self):
615 """Check for a chrome deployment when app_shell also exists."""
616 osutils.Touch(
617 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
618 )
619 osutils.Touch(
620 os.path.join(self.deploy.options.build_dir, "app_shell"),
621 makedirs=True,
622 )
623 self.deploy._CheckDeployType()
624 self.assertTrue(self.getCopyPath("chrome"))
625 self.assertFalse(self.getCopyPath("app_shell"))
Steve Fung63d705d2014-03-16 03:14:03 -0700626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 def testChromeDetection(self):
628 """Check for a regular chrome deployment"""
629 osutils.Touch(
630 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
631 )
632 self.deploy._CheckDeployType()
633 self.assertTrue(self.getCopyPath("chrome"))
634 self.assertFalse(self.getCopyPath("app_shell"))
Ben Pastenee484b342020-06-30 18:29:27 -0700635
636
637class TestDeployTestBinaries(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600638 """Tests _DeployTestBinaries()."""
Ben Pastenee484b342020-06-30 18:29:27 -0700639
Alex Klein1699fab2022-09-08 08:46:06 -0600640 def setUp(self):
641 options = _ParseCommandLine(
642 list(_REGULAR_TO)
643 + [
644 "--board",
645 _TARGET_BOARD,
646 "--force",
647 "--mount",
648 "--build-dir",
649 os.path.join(self.tempdir, "build_dir"),
650 "--nostrip",
651 ]
652 )
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000653 self.remote_exists_mock = self.PatchObject(
654 remote_access.RemoteDevice, "IfFileExists", return_value=False
655 )
Alex Klein1699fab2022-09-08 08:46:06 -0600656 self.deploy = deploy_chrome.DeployChrome(
657 options, self.tempdir, os.path.join(self.tempdir, "staging")
658 )
Ben Pastenee484b342020-06-30 18:29:27 -0700659
Alex Klein1699fab2022-09-08 08:46:06 -0600660 def _SimulateBinaries(self):
661 # Ensure the staging dir contains the right binaries to copy over.
662 test_binaries = [
663 "run_a_tests",
664 "run_b_tests",
665 "run_c_tests",
666 ]
667 # Simulate having the binaries both on the device and in our local build
668 # dir.
669 self.rc.AddCmdResult(
670 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD),
671 stdout="\n".join(test_binaries),
672 )
673 for binary in test_binaries:
674 osutils.Touch(
675 os.path.join(self.deploy.options.build_dir, binary),
676 makedirs=True,
677 mode=0o700,
678 )
679 return test_binaries
Ben Pastenee484b342020-06-30 18:29:27 -0700680
Alex Klein1699fab2022-09-08 08:46:06 -0600681 def _AssertBinariesInStagingDir(self, test_binaries):
682 # Ensure the binaries were placed in the staging dir used to copy them over.
683 staging_dir = os.path.join(
684 self.tempdir, os.path.basename(deploy_chrome._CHROME_TEST_BIN_DIR)
685 )
686 for binary in test_binaries:
687 self.assertIn(binary, os.listdir(staging_dir))
Erik Chen75a2f492020-08-06 19:15:11 -0700688
Alex Klein1699fab2022-09-08 08:46:06 -0600689 def testFindError(self):
690 """Ensure an error is thrown if we can't inspect the device."""
691 self.rc.AddCmdResult(
692 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD), 1
693 )
694 self.assertRaises(
695 deploy_chrome.DeployFailure, self.deploy._DeployTestBinaries
696 )
Brian Sheedy86f12342020-10-29 15:30:02 -0700697
Alex Klein1699fab2022-09-08 08:46:06 -0600698 def testSuccess(self):
699 """Ensure that the happy path succeeds as expected."""
700 test_binaries = self._SimulateBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700701 self.deploy._DeployTestBinaries()
Alex Klein1699fab2022-09-08 08:46:06 -0600702 self._AssertBinariesInStagingDir(test_binaries)
703
704 def testRetrySuccess(self):
705 """Ensure that a transient exception still results in success."""
706 # Raises a RunCommandError on its first invocation, but passes on subsequent
707 # calls.
708 def SideEffect(*args, **kwargs):
709 # pylint: disable=unused-argument
710 if not SideEffect.called:
711 SideEffect.called = True
712 raise cros_build_lib.RunCommandError("fail")
713
714 SideEffect.called = False
715
716 test_binaries = self._SimulateBinaries()
717 with mock.patch.object(
718 remote_access.ChromiumOSDevice,
719 "CopyToDevice",
720 side_effect=SideEffect,
721 ) as copy_mock:
722 self.deploy._DeployTestBinaries()
723 self.assertEqual(copy_mock.call_count, 2)
724 self._AssertBinariesInStagingDir(test_binaries)
725
726 def testRetryFailure(self):
727 """Ensure that consistent exceptions result in failure."""
728 self._SimulateBinaries()
729 with self.assertRaises(cros_build_lib.RunCommandError):
730 with mock.patch.object(
731 remote_access.ChromiumOSDevice,
732 "CopyToDevice",
733 side_effect=cros_build_lib.RunCommandError("fail"),
734 ):
735 self.deploy._DeployTestBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700736
Erik Chen75a2f492020-08-06 19:15:11 -0700737
738class LacrosPerformTest(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600739 """Line coverage for Perform() method with --lacros option."""
Erik Chen75a2f492020-08-06 19:15:11 -0700740
Alex Klein1699fab2022-09-08 08:46:06 -0600741 def setUp(self):
742 self.deploy = None
743 self._ran_start_command = False
744 self.StartPatcher(parallel_unittest.ParallelMock())
Yuke Liao24fc60c2020-12-26 22:16:49 -0800745
Alex Klein1699fab2022-09-08 08:46:06 -0600746 def start_ui_side_effect(*args, **kwargs):
747 # pylint: disable=unused-argument
748 self._ran_start_command = True
Yuke Liao24fc60c2020-12-26 22:16:49 -0800749
Alex Klein1699fab2022-09-08 08:46:06 -0600750 self.rc.AddCmdResult(
751 partial_mock.In("start ui"), side_effect=start_ui_side_effect
752 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800753
Alex Klein1699fab2022-09-08 08:46:06 -0600754 def prepareDeploy(self, options=None):
755 if not options:
756 options = _ParseCommandLine(
757 [
758 "--lacros",
759 "--nostrip",
760 "--build-dir",
761 "/path/to/nowhere",
762 "--device",
763 "monkey",
764 ]
765 )
766 self.deploy = deploy_chrome.DeployChrome(
767 options, self.tempdir, os.path.join(self.tempdir, "staging")
768 )
Erik Chen75a2f492020-08-06 19:15:11 -0700769
Alex Klein1699fab2022-09-08 08:46:06 -0600770 # These methods being mocked are all side effects expected for a --lacros
771 # deploy.
772 self.deploy._EnsureTargetDir = mock.Mock()
773 self.deploy._GetDeviceInfo = mock.Mock()
774 self.deploy._CheckConnection = mock.Mock()
775 self.deploy._MountRootfsAsWritable = mock.Mock()
776 self.deploy._PrepareStagingDir = mock.Mock()
777 self.deploy._CheckDeviceFreeSpace = mock.Mock()
778 self.deploy._KillAshChromeIfNeeded = mock.Mock()
Erik Chen75a2f492020-08-06 19:15:11 -0700779
Alex Klein1699fab2022-09-08 08:46:06 -0600780 def testConfNotModified(self):
781 """When the conf file is not modified we don't restart chrome ."""
782 self.prepareDeploy()
783 self.deploy.Perform()
784 self.deploy._KillAshChromeIfNeeded.assert_not_called()
785 self.assertFalse(self._ran_start_command)
Erik Chen75a2f492020-08-06 19:15:11 -0700786
Alex Klein1699fab2022-09-08 08:46:06 -0600787 def testConfModified(self):
788 """When the conf file is modified we restart chrome."""
789 self.prepareDeploy()
Erik Chen75a2f492020-08-06 19:15:11 -0700790
Alex Klein1699fab2022-09-08 08:46:06 -0600791 # We intentionally add '\n' to MODIFIED_CONF_FILE to simulate echo adding a
792 # newline when invoked in the shell.
793 self.rc.AddCmdResult(
794 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
795 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
796 )
Erik Chen75a2f492020-08-06 19:15:11 -0700797
Alex Klein1699fab2022-09-08 08:46:06 -0600798 self.deploy.Perform()
799 self.deploy._KillAshChromeIfNeeded.assert_called()
800 self.assertTrue(self._ran_start_command)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800801
Alex Klein1699fab2022-09-08 08:46:06 -0600802 def testSkipModifyingConf(self):
803 """SKip modifying the config file when the argument is specified."""
804 self.prepareDeploy(
805 _ParseCommandLine(
806 [
807 "--lacros",
808 "--nostrip",
809 "--build-dir",
810 "/path/to/nowhere",
811 "--device",
812 "monkey",
813 "--skip-modifying-config-file",
814 ]
815 )
816 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800817
Alex Klein1699fab2022-09-08 08:46:06 -0600818 self.rc.AddCmdResult(
819 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
820 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
821 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800822
Alex Klein1699fab2022-09-08 08:46:06 -0600823 self.deploy.Perform()
824 self.deploy._KillAshChromeIfNeeded.assert_not_called()
825 self.assertFalse(self._ran_start_command)