blob: a9d13882d1edca53094678526e16c98aff59ab71 [file] [log] [blame]
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
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"
205 ATTRS = ("_KillAshChromeIfNeeded", "_DisableRootfsVerification")
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700206
Alex Klein1699fab2022-09-08 08:46:06 -0600207 def __init__(self):
208 partial_mock.PartialMock.__init__(self)
209 self.remote_device_mock = remote_access_unittest.RemoteDeviceMock()
210 # Target starts off as having rootfs verification enabled.
211 self.rsh_mock = remote_access_unittest.RemoteShMock()
212 self.rsh_mock.SetDefaultCmdResult(0)
213 self.MockMountCmd(1)
214 self.rsh_mock.AddCmdResult(
215 deploy_chrome.LSOF_COMMAND_CHROME % (deploy_chrome._CHROME_DIR,), 1
216 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700217
Alex Klein1699fab2022-09-08 08:46:06 -0600218 def MockMountCmd(self, returnvalue):
219 self.rsh_mock.AddCmdResult(deploy_chrome.MOUNT_RW_COMMAND, returnvalue)
David James88e6f032013-03-02 08:13:20 -0800220
Alex Klein1699fab2022-09-08 08:46:06 -0600221 def _DisableRootfsVerification(self, inst):
222 with mock.patch.object(time, "sleep"):
223 self.backup["_DisableRootfsVerification"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700224
Alex Klein1699fab2022-09-08 08:46:06 -0600225 def PreStart(self):
226 self.remote_device_mock.start()
227 self.rsh_mock.start()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700228
Alex Klein1699fab2022-09-08 08:46:06 -0600229 def PreStop(self):
230 self.rsh_mock.stop()
231 self.remote_device_mock.stop()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 def _KillAshChromeIfNeeded(self, _inst):
234 # Fully stub out for now.
235 pass
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700236
237
Ryan Cuief91e702013-02-04 12:06:36 -0800238class DeployTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600239 """Setup a deploy object with a GS-path for use in tests."""
Steve Funge984a532013-11-25 17:09:25 -0800240
Alex Klein1699fab2022-09-08 08:46:06 -0600241 def _GetDeployChrome(self, args):
242 options = _ParseCommandLine(args)
243 return deploy_chrome.DeployChrome(
244 options, self.tempdir, os.path.join(self.tempdir, "staging")
245 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700246
Alex Klein1699fab2022-09-08 08:46:06 -0600247 def setUp(self):
248 self.deploy_mock = self.StartPatcher(DeployChromeMock())
249 self.deploy = self._GetDeployChrome(
250 list(_REGULAR_TO)
251 + [
252 "--board",
253 _TARGET_BOARD,
254 "--gs-path",
255 _GS_PATH,
256 "--force",
257 "--mount",
258 ]
259 )
260 self.remote_reboot_mock = self.PatchObject(
261 remote_access.RemoteAccess, "RemoteReboot", return_value=True
262 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700263
Avery Musbach3edff0e2020-03-27 13:35:53 -0700264
265class TestCheckIfBoardMatches(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600266 """Testing checking whether the DUT board matches the target board."""
Avery Musbach3edff0e2020-03-27 13:35:53 -0700267
Alex Klein1699fab2022-09-08 08:46:06 -0600268 def testMatchedBoard(self):
269 """Test the case where the DUT board matches the target board."""
270 self.PatchObject(remote_access.ChromiumOSDevice, "board", _TARGET_BOARD)
271 self.assertTrue(self.deploy.options.force)
272 self.deploy._CheckBoard()
273 self.deploy.options.force = False
274 self.deploy._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700275
Alex Klein1699fab2022-09-08 08:46:06 -0600276 def testMismatchedBoard(self):
277 """Test the case where the DUT board does not match the target board."""
278 self.PatchObject(remote_access.ChromiumOSDevice, "board", "cedar")
279 self.assertTrue(self.deploy.options.force)
280 self.deploy._CheckBoard()
281 self.deploy.options.force = False
282 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=True)
283 self.deploy._CheckBoard()
284 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=False)
285 self.assertRaises(deploy_chrome.DeployFailure, self.deploy._CheckBoard)
Avery Musbach3edff0e2020-03-27 13:35:53 -0700286
287
David James88e6f032013-03-02 08:13:20 -0800288class TestDisableRootfsVerification(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600289 """Testing disabling of rootfs verification and RO mode."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 def testDisableRootfsVerificationSuccess(self):
292 """Test the working case, disabling rootfs verification."""
293 self.deploy_mock.MockMountCmd(0)
294 self.deploy._DisableRootfsVerification()
295 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700296
Alex Klein1699fab2022-09-08 08:46:06 -0600297 def testDisableRootfsVerificationFailure(self):
298 """Test failure to disable rootfs verification."""
299 # pylint: disable=unused-argument
300 def RaiseRunCommandError(timeout_sec=None):
301 raise cros_build_lib.RunCommandError("Mock RunCommandError")
302
303 self.remote_reboot_mock.side_effect = RaiseRunCommandError
304 self.assertRaises(
305 cros_build_lib.RunCommandError,
306 self.deploy._DisableRootfsVerification,
307 )
308 self.remote_reboot_mock.side_effect = None
309 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800310
311
312class TestMount(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600313 """Testing mount success and failure."""
David James88e6f032013-03-02 08:13:20 -0800314
Alex Klein1699fab2022-09-08 08:46:06 -0600315 def testSuccess(self):
316 """Test case where we are able to mount as writable."""
317 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
318 self.deploy_mock.MockMountCmd(0)
319 self.deploy._MountRootfsAsWritable()
320 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800321
Alex Klein1699fab2022-09-08 08:46:06 -0600322 def testMountError(self):
323 """Test that mount failure doesn't raise an exception by default."""
324 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
325 self.PatchObject(
326 remote_access.ChromiumOSDevice,
327 "IsDirWritable",
328 return_value=False,
329 autospec=True,
330 )
331 self.deploy._MountRootfsAsWritable()
332 self.assertTrue(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700333
Alex Klein1699fab2022-09-08 08:46:06 -0600334 def testMountRwFailure(self):
335 """Test that mount failure raises an exception if check=True."""
336 self.assertRaises(
337 cros_build_lib.RunCommandError,
338 self.deploy._MountRootfsAsWritable,
339 check=True,
340 )
341 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Robert Flack1dc7ea82014-11-26 13:50:24 -0500342
Alex Klein1699fab2022-09-08 08:46:06 -0600343 def testMountTempDir(self):
344 """Test that mount succeeds if target dir is writable."""
345 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
346 self.PatchObject(
347 remote_access.ChromiumOSDevice,
348 "IsDirWritable",
349 return_value=True,
350 autospec=True,
351 )
352 self.deploy._MountRootfsAsWritable()
353 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700354
355
Anushruth8d797672019-10-17 12:22:31 -0700356class TestMountTarget(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600357 """Testing mount and umount command handling."""
Anushruth8d797672019-10-17 12:22:31 -0700358
Alex Klein1699fab2022-09-08 08:46:06 -0600359 def testMountTargetUmountFailure(self):
360 """Test error being thrown if umount fails.
Anushruth8d797672019-10-17 12:22:31 -0700361
Alex Klein1699fab2022-09-08 08:46:06 -0600362 Test that 'lsof' is run on mount-dir and 'mount -rbind' command is not run
363 if 'umount' cmd fails.
364 """
365 mount_dir = self.deploy.options.mount_dir
366 target_dir = self.deploy.options.target_dir
367 self.deploy_mock.rsh_mock.AddCmdResult(
368 deploy_chrome._UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": mount_dir},
369 returncode=errno.EBUSY,
370 stderr="Target is Busy",
371 )
372 self.deploy_mock.rsh_mock.AddCmdResult(
373 deploy_chrome.LSOF_COMMAND % (mount_dir,),
374 returncode=0,
375 stdout="process " + mount_dir,
376 )
377 # Check for RunCommandError being thrown.
378 self.assertRaises(
379 cros_build_lib.RunCommandError, self.deploy._MountTarget
380 )
381 # Check for the 'mount -rbind' command not run.
382 self.deploy_mock.rsh_mock.assertCommandContains(
383 (deploy_chrome._BIND_TO_FINAL_DIR_CMD % (target_dir, mount_dir)),
384 expected=False,
385 )
386 # Check for lsof command being called.
387 self.deploy_mock.rsh_mock.assertCommandContains(
388 (deploy_chrome.LSOF_COMMAND % (mount_dir,))
389 )
Anushruth8d797672019-10-17 12:22:31 -0700390
391
Ryan Cuief91e702013-02-04 12:06:36 -0800392class TestUiJobStarted(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600393 """Test detection of a running 'ui' job."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700394
Alex Klein1699fab2022-09-08 08:46:06 -0600395 def MockStatusUiCmd(self, **kwargs):
396 self.deploy_mock.rsh_mock.AddCmdResult("status ui", **kwargs)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700397
Alex Klein1699fab2022-09-08 08:46:06 -0600398 def testUiJobStartedFalse(self):
399 """Correct results with a stopped job."""
400 self.MockStatusUiCmd(stdout="ui stop/waiting")
401 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuif2d1a582013-02-19 14:08:13 -0800402
Alex Klein1699fab2022-09-08 08:46:06 -0600403 def testNoUiJob(self):
404 """Correct results when the job doesn't exist."""
405 self.MockStatusUiCmd(stderr="start: Unknown job: ui", returncode=1)
406 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700407
Alex Klein1699fab2022-09-08 08:46:06 -0600408 def testCheckRootfsWriteableTrue(self):
409 """Correct results with a running job."""
410 self.MockStatusUiCmd(stdout="ui start/running, process 297")
411 self.assertTrue(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700412
413
Ryan Cuief91e702013-02-04 12:06:36 -0800414class StagingTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600415 """Test user-mode and ebuild-mode staging functionality."""
Ryan Cuief91e702013-02-04 12:06:36 -0800416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 def setUp(self):
418 self.staging_dir = os.path.join(self.tempdir, "staging")
419 osutils.SafeMakedirs(self.staging_dir)
420 self.staging_tarball_path = os.path.join(
421 self.tempdir, deploy_chrome._CHROME_DIR_STAGING_TARBALL_ZSTD
422 )
423 self.build_dir = os.path.join(self.tempdir, "build_dir")
424 self.common_flags = [
425 "--board",
426 _TARGET_BOARD,
427 "--build-dir",
428 self.build_dir,
429 "--staging-only",
430 "--cache-dir",
431 str(self.tempdir),
432 ]
433 self.sdk_mock = self.StartPatcher(
434 cros_chrome_sdk_unittest.SDKFetcherMock()
435 )
436 self.PatchObject(
437 osutils,
438 "SourceEnvironment",
439 autospec=True,
440 return_value={"STRIP": "x86_64-cros-linux-gnu-strip"},
441 )
Ryan Cuief91e702013-02-04 12:06:36 -0800442
Alex Klein1699fab2022-09-08 08:46:06 -0600443 def testSingleFileDeployFailure(self):
444 """Default staging enforces that mandatory files are copied"""
445 options = _ParseCommandLine(self.common_flags)
446 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
447 self.assertRaises(
448 chrome_util.MissingPathError,
449 deploy_chrome._PrepareStagingDir,
450 options,
451 self.tempdir,
452 self.staging_dir,
453 chrome_util._COPY_PATHS_CHROME,
454 )
Ryan Cuief91e702013-02-04 12:06:36 -0800455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 def testSloppyDeployFailure(self):
457 """Sloppy staging enforces that at least one file is copied."""
458 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
459 self.assertRaises(
460 chrome_util.MissingPathError,
461 deploy_chrome._PrepareStagingDir,
462 options,
463 self.tempdir,
464 self.staging_dir,
465 chrome_util._COPY_PATHS_CHROME,
466 )
David Jamesa6e08892013-03-01 13:34:11 -0800467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 def testSloppyDeploySuccess(self):
469 """Sloppy staging - stage one file."""
470 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
471 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
472 deploy_chrome._PrepareStagingDir(
473 options,
474 self.tempdir,
475 self.staging_dir,
476 chrome_util._COPY_PATHS_CHROME,
477 )
David Jamesa6e08892013-03-01 13:34:11 -0800478
Alex Klein1699fab2022-09-08 08:46:06 -0600479 @cros_test_lib.pytestmark_network_test
480 def testUploadStagingDir(self):
481 """Upload staging directory."""
482 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
483 staging_upload = "gs://some-path"
484 options = _ParseCommandLine(
485 self.common_flags + ["--staging-upload", staging_upload]
486 )
487 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
488 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
489 self.assertEqual(
490 mockGsCopy.call_args_list,
491 [
492 mock.call(self.staging_tarball_path, staging_upload, acl=""),
493 ],
494 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000495
Alex Klein1699fab2022-09-08 08:46:06 -0600496 @cros_test_lib.pytestmark_network_test
497 def testUploadStagingPublicReadACL(self):
498 """Upload staging directory with public-read ACL."""
499 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
500 staging_upload = "gs://some-path"
501 options = _ParseCommandLine(
502 self.common_flags
503 + ["--staging-upload", staging_upload, "--public-read"]
504 )
505 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
506 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
507 self.assertEqual(
508 mockGsCopy.call_args_list,
509 [
510 mock.call(
511 self.staging_tarball_path, staging_upload, acl="public-read"
512 ),
513 ],
514 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000515
Steve Funge984a532013-11-25 17:09:25 -0800516
517class DeployTestBuildDir(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600518 """Set up a deploy object with a build-dir for use in deployment type tests"""
Steve Funge984a532013-11-25 17:09:25 -0800519
Alex Klein1699fab2022-09-08 08:46:06 -0600520 def _GetDeployChrome(self, args):
521 options = _ParseCommandLine(args)
522 return deploy_chrome.DeployChrome(
523 options, self.tempdir, os.path.join(self.tempdir, "staging")
524 )
Steve Funge984a532013-11-25 17:09:25 -0800525
Alex Klein1699fab2022-09-08 08:46:06 -0600526 def setUp(self):
527 self.staging_dir = os.path.join(self.tempdir, "staging")
528 self.build_dir = os.path.join(self.tempdir, "build_dir")
529 self.deploy_mock = self.StartPatcher(DeployChromeMock())
530 self.deploy = self._GetDeployChrome(
531 list(_REGULAR_TO)
532 + [
533 "--board",
534 _TARGET_BOARD,
535 "--build-dir",
536 self.build_dir,
537 "--staging-only",
538 "--cache-dir",
539 str(self.tempdir),
540 "--sloppy",
541 ]
542 )
Steve Funge984a532013-11-25 17:09:25 -0800543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 def getCopyPath(self, source_path):
545 """Return a chrome_util.Path or None if not present."""
546 paths = [p for p in self.deploy.copy_paths if p.src == source_path]
547 return paths[0] if paths else None
548
Steve Funge984a532013-11-25 17:09:25 -0800549
Daniel Erat1ae46382014-08-14 10:23:39 -0700550class TestDeploymentType(DeployTestBuildDir):
Alex Klein1699fab2022-09-08 08:46:06 -0600551 """Test detection of deployment type using build dir."""
Steve Funge984a532013-11-25 17:09:25 -0800552
Alex Klein1699fab2022-09-08 08:46:06 -0600553 def testAppShellDetection(self):
554 """Check for an app_shell deployment"""
555 osutils.Touch(
556 os.path.join(self.deploy.options.build_dir, "app_shell"),
557 makedirs=True,
558 )
559 self.deploy._CheckDeployType()
560 self.assertTrue(self.getCopyPath("app_shell"))
561 self.assertFalse(self.getCopyPath("chrome"))
Steve Funge984a532013-11-25 17:09:25 -0800562
Alex Klein1699fab2022-09-08 08:46:06 -0600563 def testChromeAndAppShellDetection(self):
564 """Check for a chrome deployment when app_shell also exists."""
565 osutils.Touch(
566 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
567 )
568 osutils.Touch(
569 os.path.join(self.deploy.options.build_dir, "app_shell"),
570 makedirs=True,
571 )
572 self.deploy._CheckDeployType()
573 self.assertTrue(self.getCopyPath("chrome"))
574 self.assertFalse(self.getCopyPath("app_shell"))
Steve Fung63d705d2014-03-16 03:14:03 -0700575
Alex Klein1699fab2022-09-08 08:46:06 -0600576 def testChromeDetection(self):
577 """Check for a regular chrome deployment"""
578 osutils.Touch(
579 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
580 )
581 self.deploy._CheckDeployType()
582 self.assertTrue(self.getCopyPath("chrome"))
583 self.assertFalse(self.getCopyPath("app_shell"))
Ben Pastenee484b342020-06-30 18:29:27 -0700584
585
586class TestDeployTestBinaries(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600587 """Tests _DeployTestBinaries()."""
Ben Pastenee484b342020-06-30 18:29:27 -0700588
Alex Klein1699fab2022-09-08 08:46:06 -0600589 def setUp(self):
590 options = _ParseCommandLine(
591 list(_REGULAR_TO)
592 + [
593 "--board",
594 _TARGET_BOARD,
595 "--force",
596 "--mount",
597 "--build-dir",
598 os.path.join(self.tempdir, "build_dir"),
599 "--nostrip",
600 ]
601 )
602 self.deploy = deploy_chrome.DeployChrome(
603 options, self.tempdir, os.path.join(self.tempdir, "staging")
604 )
Ben Pastenee484b342020-06-30 18:29:27 -0700605
Alex Klein1699fab2022-09-08 08:46:06 -0600606 def _SimulateBinaries(self):
607 # Ensure the staging dir contains the right binaries to copy over.
608 test_binaries = [
609 "run_a_tests",
610 "run_b_tests",
611 "run_c_tests",
612 ]
613 # Simulate having the binaries both on the device and in our local build
614 # dir.
615 self.rc.AddCmdResult(
616 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD),
617 stdout="\n".join(test_binaries),
618 )
619 for binary in test_binaries:
620 osutils.Touch(
621 os.path.join(self.deploy.options.build_dir, binary),
622 makedirs=True,
623 mode=0o700,
624 )
625 return test_binaries
Ben Pastenee484b342020-06-30 18:29:27 -0700626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 def _AssertBinariesInStagingDir(self, test_binaries):
628 # Ensure the binaries were placed in the staging dir used to copy them over.
629 staging_dir = os.path.join(
630 self.tempdir, os.path.basename(deploy_chrome._CHROME_TEST_BIN_DIR)
631 )
632 for binary in test_binaries:
633 self.assertIn(binary, os.listdir(staging_dir))
Erik Chen75a2f492020-08-06 19:15:11 -0700634
Alex Klein1699fab2022-09-08 08:46:06 -0600635 def testFindError(self):
636 """Ensure an error is thrown if we can't inspect the device."""
637 self.rc.AddCmdResult(
638 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD), 1
639 )
640 self.assertRaises(
641 deploy_chrome.DeployFailure, self.deploy._DeployTestBinaries
642 )
Brian Sheedy86f12342020-10-29 15:30:02 -0700643
Alex Klein1699fab2022-09-08 08:46:06 -0600644 def testSuccess(self):
645 """Ensure that the happy path succeeds as expected."""
646 test_binaries = self._SimulateBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700647 self.deploy._DeployTestBinaries()
Alex Klein1699fab2022-09-08 08:46:06 -0600648 self._AssertBinariesInStagingDir(test_binaries)
649
650 def testRetrySuccess(self):
651 """Ensure that a transient exception still results in success."""
652 # Raises a RunCommandError on its first invocation, but passes on subsequent
653 # calls.
654 def SideEffect(*args, **kwargs):
655 # pylint: disable=unused-argument
656 if not SideEffect.called:
657 SideEffect.called = True
658 raise cros_build_lib.RunCommandError("fail")
659
660 SideEffect.called = False
661
662 test_binaries = self._SimulateBinaries()
663 with mock.patch.object(
664 remote_access.ChromiumOSDevice,
665 "CopyToDevice",
666 side_effect=SideEffect,
667 ) as copy_mock:
668 self.deploy._DeployTestBinaries()
669 self.assertEqual(copy_mock.call_count, 2)
670 self._AssertBinariesInStagingDir(test_binaries)
671
672 def testRetryFailure(self):
673 """Ensure that consistent exceptions result in failure."""
674 self._SimulateBinaries()
675 with self.assertRaises(cros_build_lib.RunCommandError):
676 with mock.patch.object(
677 remote_access.ChromiumOSDevice,
678 "CopyToDevice",
679 side_effect=cros_build_lib.RunCommandError("fail"),
680 ):
681 self.deploy._DeployTestBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700682
Erik Chen75a2f492020-08-06 19:15:11 -0700683
684class LacrosPerformTest(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600685 """Line coverage for Perform() method with --lacros option."""
Erik Chen75a2f492020-08-06 19:15:11 -0700686
Alex Klein1699fab2022-09-08 08:46:06 -0600687 def setUp(self):
688 self.deploy = None
689 self._ran_start_command = False
690 self.StartPatcher(parallel_unittest.ParallelMock())
Yuke Liao24fc60c2020-12-26 22:16:49 -0800691
Alex Klein1699fab2022-09-08 08:46:06 -0600692 def start_ui_side_effect(*args, **kwargs):
693 # pylint: disable=unused-argument
694 self._ran_start_command = True
Yuke Liao24fc60c2020-12-26 22:16:49 -0800695
Alex Klein1699fab2022-09-08 08:46:06 -0600696 self.rc.AddCmdResult(
697 partial_mock.In("start ui"), side_effect=start_ui_side_effect
698 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800699
Alex Klein1699fab2022-09-08 08:46:06 -0600700 def prepareDeploy(self, options=None):
701 if not options:
702 options = _ParseCommandLine(
703 [
704 "--lacros",
705 "--nostrip",
706 "--build-dir",
707 "/path/to/nowhere",
708 "--device",
709 "monkey",
710 ]
711 )
712 self.deploy = deploy_chrome.DeployChrome(
713 options, self.tempdir, os.path.join(self.tempdir, "staging")
714 )
Erik Chen75a2f492020-08-06 19:15:11 -0700715
Alex Klein1699fab2022-09-08 08:46:06 -0600716 # These methods being mocked are all side effects expected for a --lacros
717 # deploy.
718 self.deploy._EnsureTargetDir = mock.Mock()
719 self.deploy._GetDeviceInfo = mock.Mock()
720 self.deploy._CheckConnection = mock.Mock()
721 self.deploy._MountRootfsAsWritable = mock.Mock()
722 self.deploy._PrepareStagingDir = mock.Mock()
723 self.deploy._CheckDeviceFreeSpace = mock.Mock()
724 self.deploy._KillAshChromeIfNeeded = mock.Mock()
Erik Chen75a2f492020-08-06 19:15:11 -0700725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 def testConfNotModified(self):
727 """When the conf file is not modified we don't restart chrome ."""
728 self.prepareDeploy()
729 self.deploy.Perform()
730 self.deploy._KillAshChromeIfNeeded.assert_not_called()
731 self.assertFalse(self._ran_start_command)
Erik Chen75a2f492020-08-06 19:15:11 -0700732
Alex Klein1699fab2022-09-08 08:46:06 -0600733 def testConfModified(self):
734 """When the conf file is modified we restart chrome."""
735 self.prepareDeploy()
Erik Chen75a2f492020-08-06 19:15:11 -0700736
Alex Klein1699fab2022-09-08 08:46:06 -0600737 # We intentionally add '\n' to MODIFIED_CONF_FILE to simulate echo adding a
738 # newline when invoked in the shell.
739 self.rc.AddCmdResult(
740 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
741 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
742 )
Erik Chen75a2f492020-08-06 19:15:11 -0700743
Alex Klein1699fab2022-09-08 08:46:06 -0600744 self.deploy.Perform()
745 self.deploy._KillAshChromeIfNeeded.assert_called()
746 self.assertTrue(self._ran_start_command)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800747
Alex Klein1699fab2022-09-08 08:46:06 -0600748 def testSkipModifyingConf(self):
749 """SKip modifying the config file when the argument is specified."""
750 self.prepareDeploy(
751 _ParseCommandLine(
752 [
753 "--lacros",
754 "--nostrip",
755 "--build-dir",
756 "/path/to/nowhere",
757 "--device",
758 "monkey",
759 "--skip-modifying-config-file",
760 ]
761 )
762 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800763
Alex Klein1699fab2022-09-08 08:46:06 -0600764 self.rc.AddCmdResult(
765 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
766 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
767 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800768
Alex Klein1699fab2022-09-08 08:46:06 -0600769 self.deploy.Perform()
770 self.deploy._KillAshChromeIfNeeded.assert_not_called()
771 self.assertFalse(self._ran_start_command)