blob: 6f2ea5ffe767ccf63409b52fdf665f97964c055e [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Steve Funge984a532013-11-25 17:09:25 -08005"""Unit tests for the deploy_chrome script."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07006
Anushruth8d797672019-10-17 12:22:31 -07007import errno
Ryan Cuiafd6c5c2012-07-30 17:48:22 -07008import os
David James88e6f032013-03-02 08:13:20 -08009import time
Mike Frysinger166fea02021-02-12 05:30:33 -050010from unittest import mock
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070011
David Pursellcfd58872015-03-19 09:15:48 -070012from chromite.cli.cros import cros_chrome_sdk_unittest
Ryan Cuief91e702013-02-04 12:06:36 -080013from chromite.lib import chrome_util
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070014from chromite.lib import cros_build_lib
15from chromite.lib import cros_test_lib
Jae Hoon Kimdf842912022-05-19 06:40:42 +000016from chromite.lib import gs
Ryan Cui686ec052013-02-12 16:39:41 -080017from chromite.lib import osutils
Erik Chen75a2f492020-08-06 19:15:11 -070018from chromite.lib import parallel_unittest
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070019from chromite.lib import partial_mock
Robert Flack1dc7ea82014-11-26 13:50:24 -050020from chromite.lib import remote_access
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070021from chromite.lib import remote_access_unittest
22from chromite.scripts import deploy_chrome
23
Ryan Cuief91e702013-02-04 12:06:36 -080024
Mike Frysinger27e21b72018-07-12 14:20:21 -040025# pylint: disable=protected-access
26
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070027
Alex Klein1699fab2022-09-08 08:46:06 -060028_REGULAR_TO = ("--device", "monkey")
29_TARGET_BOARD = "eve"
30_GS_PATH = "gs://foon"
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070031
32
33def _ParseCommandLine(argv):
Alex Klein1699fab2022-09-08 08:46:06 -060034 return deploy_chrome._ParseCommandLine(["--log-level", "debug"] + argv)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070035
36
37class InterfaceTest(cros_test_lib.OutputTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060038 """Tests the commandline interface of the script."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070039
Alex Klein1699fab2022-09-08 08:46:06 -060040 def testGsLocalPathUnSpecified(self):
41 """Test no chrome path specified."""
42 with self.OutputCapturer():
43 self.assertRaises2(
44 SystemExit,
45 _ParseCommandLine,
46 list(_REGULAR_TO) + ["--board", _TARGET_BOARD],
47 check_attrs={"code": 2},
48 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070049
Alex Klein1699fab2022-09-08 08:46:06 -060050 def testBuildDirSpecified(self):
51 """Test case of build dir specified."""
52 argv = list(_REGULAR_TO) + [
53 "--board",
54 _TARGET_BOARD,
55 "--build-dir",
56 "/path/to/chrome",
57 ]
58 _ParseCommandLine(argv)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090059
Alex Klein1699fab2022-09-08 08:46:06 -060060 def testBuildDirSpecifiedWithoutBoard(self):
61 """Test case of build dir specified without --board."""
62 argv = list(_REGULAR_TO) + [
63 "--build-dir",
64 "/path/to/chrome/out_" + _TARGET_BOARD + "/Release",
65 ]
66 options = _ParseCommandLine(argv)
67 self.assertEqual(options.board, _TARGET_BOARD)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090068
Alex Klein1699fab2022-09-08 08:46:06 -060069 def testBuildDirSpecifiedWithoutBoardError(self):
70 """Test case of irregular build dir specified without --board."""
71 argv = list(_REGULAR_TO) + ["--build-dir", "/path/to/chrome/foo/bar"]
72 self.assertParseError(argv)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090073
Alex Klein1699fab2022-09-08 08:46:06 -060074 def testGsPathSpecified(self):
75 """Test case of GS path specified."""
76 argv = list(_REGULAR_TO) + [
77 "--board",
78 _TARGET_BOARD,
79 "--gs-path",
80 _GS_PATH,
81 ]
82 _ParseCommandLine(argv)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070083
Alex Klein1699fab2022-09-08 08:46:06 -060084 def testLocalPathSpecified(self):
85 """Test case of local path specified."""
86 argv = list(_REGULAR_TO) + [
87 "--board",
88 _TARGET_BOARD,
89 "--local-pkg-path",
90 "/path/to/chrome",
91 ]
92 _ParseCommandLine(argv)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -070093
Alex Klein1699fab2022-09-08 08:46:06 -060094 def testNoBoard(self):
95 """Test no board specified."""
96 argv = list(_REGULAR_TO) + ["--gs-path", _GS_PATH]
97 self.assertParseError(argv)
Ryo Hashimoto77f8eca2021-04-16 16:43:37 +090098
Alex Klein1699fab2022-09-08 08:46:06 -060099 def testNoTarget(self):
100 """Test no target specified."""
101 argv = ["--board", _TARGET_BOARD, "--gs-path", _GS_PATH]
102 self.assertParseError(argv)
Ryan Cuief91e702013-02-04 12:06:36 -0800103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 def testLacros(self):
105 """Test basic lacros invocation."""
106 argv = [
107 "--lacros",
108 "--build-dir",
109 "/path/to/nowhere",
110 "--device",
111 "monkey",
112 "--board",
113 "atlas",
114 ]
115 options = _ParseCommandLine(argv)
116 self.assertTrue(options.lacros)
117 self.assertEqual(options.target_dir, deploy_chrome.LACROS_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 def testLacrosNoStrip(self):
120 """Test lacros invocation with nostrip."""
121 argv = [
122 "--lacros",
123 "--nostrip",
124 "--build-dir",
125 "/path/to/nowhere",
126 "--device",
127 "monkey",
128 ]
129 options = _ParseCommandLine(argv)
130 self.assertTrue(options.lacros)
131 self.assertFalse(options.dostrip)
132 self.assertEqual(options.target_dir, deploy_chrome.LACROS_DIR)
Erik Chen75a2f492020-08-06 19:15:11 -0700133
Alex Klein1699fab2022-09-08 08:46:06 -0600134 def assertParseError(self, argv):
135 with self.OutputCapturer():
136 self.assertRaises2(
137 SystemExit, _ParseCommandLine, argv, check_attrs={"code": 2}
138 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 def testMountOptionSetsTargetDir(self):
141 argv = list(_REGULAR_TO) + [
142 "--board",
143 _TARGET_BOARD,
144 "--gs-path",
145 _GS_PATH,
146 "--mount",
147 ]
148 options = _ParseCommandLine(argv)
149 self.assertIsNot(options.target_dir, None)
Thiago Goncales12793312013-05-23 11:26:17 -0700150
Alex Klein1699fab2022-09-08 08:46:06 -0600151 def testMountOptionSetsMountDir(self):
152 argv = list(_REGULAR_TO) + [
153 "--board",
154 _TARGET_BOARD,
155 "--gs-path",
156 _GS_PATH,
157 "--mount",
158 ]
159 options = _ParseCommandLine(argv)
160 self.assertIsNot(options.mount_dir, None)
Thiago Goncales12793312013-05-23 11:26:17 -0700161
Alex Klein1699fab2022-09-08 08:46:06 -0600162 def testMountOptionDoesNotOverrideTargetDir(self):
163 argv = list(_REGULAR_TO) + [
164 "--board",
165 _TARGET_BOARD,
166 "--gs-path",
167 _GS_PATH,
168 "--mount",
169 "--target-dir",
170 "/foo/bar/cow",
171 ]
172 options = _ParseCommandLine(argv)
173 self.assertEqual(options.target_dir, "/foo/bar/cow")
Thiago Goncales12793312013-05-23 11:26:17 -0700174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 def testMountOptionDoesNotOverrideMountDir(self):
176 argv = list(_REGULAR_TO) + [
177 "--board",
178 _TARGET_BOARD,
179 "--gs-path",
180 _GS_PATH,
181 "--mount",
182 "--mount-dir",
183 "/foo/bar/cow",
184 ]
185 options = _ParseCommandLine(argv)
186 self.assertEqual(options.mount_dir, "/foo/bar/cow")
Thiago Goncales12793312013-05-23 11:26:17 -0700187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 def testSshIdentityOptionSetsOption(self):
189 argv = list(_REGULAR_TO) + [
190 "--board",
191 _TARGET_BOARD,
192 "--private-key",
193 "/foo/bar/key",
194 "--build-dir",
195 "/path/to/nowhere",
196 ]
197 options = _ParseCommandLine(argv)
198 self.assertEqual(options.private_key, "/foo/bar/key")
199
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700200
201class DeployChromeMock(partial_mock.PartialMock):
Alex Klein1699fab2022-09-08 08:46:06 -0600202 """Deploy Chrome Mock Class."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 TARGET = "chromite.scripts.deploy_chrome.DeployChrome"
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000205 ATTRS = (
206 "_ChromeFileInUse",
207 "_DisableRootfsVerification",
208 "_ShouldUseCompressedAsh",
209 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700210
Alex Klein1699fab2022-09-08 08:46:06 -0600211 def __init__(self):
212 partial_mock.PartialMock.__init__(self)
213 self.remote_device_mock = remote_access_unittest.RemoteDeviceMock()
214 # Target starts off as having rootfs verification enabled.
215 self.rsh_mock = remote_access_unittest.RemoteShMock()
216 self.rsh_mock.SetDefaultCmdResult(0)
217 self.MockMountCmd(1)
218 self.rsh_mock.AddCmdResult(
219 deploy_chrome.LSOF_COMMAND_CHROME % (deploy_chrome._CHROME_DIR,), 1
220 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700221
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000222 self.rsh_mock.AddCmdResult(
223 "status ui", stdout="ui start/running, process 123"
224 )
225
Alex Klein1699fab2022-09-08 08:46:06 -0600226 def MockMountCmd(self, returnvalue):
227 self.rsh_mock.AddCmdResult(deploy_chrome.MOUNT_RW_COMMAND, returnvalue)
David James88e6f032013-03-02 08:13:20 -0800228
Alex Klein1699fab2022-09-08 08:46:06 -0600229 def _DisableRootfsVerification(self, inst):
230 with mock.patch.object(time, "sleep"):
231 self.backup["_DisableRootfsVerification"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 def PreStart(self):
234 self.remote_device_mock.start()
235 self.rsh_mock.start()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700236
Alex Klein1699fab2022-09-08 08:46:06 -0600237 def PreStop(self):
238 self.rsh_mock.stop()
239 self.remote_device_mock.stop()
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700240
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000241 def _ChromeFileInUse(self, _inst):
242 # Fully stub out for now. Can be replaced if further testing is added.
243 return False
244
245 def _ShouldUseCompressedAsh(self, inst):
246 with mock.patch.object(
247 remote_access.RemoteDevice, "IfFileExists"
248 ) as exists_mock:
249 exists_mock.return_value = False
250 self.backup["_ShouldUseCompressedAsh"](inst)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700251
252
Ryan Cuief91e702013-02-04 12:06:36 -0800253class DeployTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600254 """Setup a deploy object with a GS-path for use in tests."""
Steve Funge984a532013-11-25 17:09:25 -0800255
Alex Klein1699fab2022-09-08 08:46:06 -0600256 def _GetDeployChrome(self, args):
257 options = _ParseCommandLine(args)
258 return deploy_chrome.DeployChrome(
259 options, self.tempdir, os.path.join(self.tempdir, "staging")
260 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700261
Alex Klein1699fab2022-09-08 08:46:06 -0600262 def setUp(self):
263 self.deploy_mock = self.StartPatcher(DeployChromeMock())
264 self.deploy = self._GetDeployChrome(
265 list(_REGULAR_TO)
266 + [
267 "--board",
268 _TARGET_BOARD,
269 "--gs-path",
270 _GS_PATH,
271 "--force",
272 "--mount",
273 ]
274 )
275 self.remote_reboot_mock = self.PatchObject(
276 remote_access.RemoteAccess, "RemoteReboot", return_value=True
277 )
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700278
George Burgess IV4c876ac2022-12-07 10:33:49 -0700279 def tearDown(self):
280 self.deploy.Cleanup()
281
Avery Musbach3edff0e2020-03-27 13:35:53 -0700282
283class TestCheckIfBoardMatches(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600284 """Testing checking whether the DUT board matches the target board."""
Avery Musbach3edff0e2020-03-27 13:35:53 -0700285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 def testMatchedBoard(self):
287 """Test the case where the DUT board matches the target board."""
288 self.PatchObject(remote_access.ChromiumOSDevice, "board", _TARGET_BOARD)
289 self.assertTrue(self.deploy.options.force)
290 self.deploy._CheckBoard()
291 self.deploy.options.force = False
292 self.deploy._CheckBoard()
Avery Musbach3edff0e2020-03-27 13:35:53 -0700293
Alex Klein1699fab2022-09-08 08:46:06 -0600294 def testMismatchedBoard(self):
295 """Test the case where the DUT board does not match the target board."""
296 self.PatchObject(remote_access.ChromiumOSDevice, "board", "cedar")
297 self.assertTrue(self.deploy.options.force)
298 self.deploy._CheckBoard()
299 self.deploy.options.force = False
300 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=True)
301 self.deploy._CheckBoard()
302 self.PatchObject(cros_build_lib, "BooleanPrompt", return_value=False)
303 self.assertRaises(deploy_chrome.DeployFailure, self.deploy._CheckBoard)
Avery Musbach3edff0e2020-03-27 13:35:53 -0700304
305
David James88e6f032013-03-02 08:13:20 -0800306class TestDisableRootfsVerification(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600307 """Testing disabling of rootfs verification and RO mode."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700308
Alex Klein1699fab2022-09-08 08:46:06 -0600309 def testDisableRootfsVerificationSuccess(self):
310 """Test the working case, disabling rootfs verification."""
311 self.deploy_mock.MockMountCmd(0)
312 self.deploy._DisableRootfsVerification()
313 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700314
Alex Klein1699fab2022-09-08 08:46:06 -0600315 def testDisableRootfsVerificationFailure(self):
316 """Test failure to disable rootfs verification."""
317 # pylint: disable=unused-argument
318 def RaiseRunCommandError(timeout_sec=None):
319 raise cros_build_lib.RunCommandError("Mock RunCommandError")
320
321 self.remote_reboot_mock.side_effect = RaiseRunCommandError
322 self.assertRaises(
323 cros_build_lib.RunCommandError,
324 self.deploy._DisableRootfsVerification,
325 )
326 self.remote_reboot_mock.side_effect = None
327 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800328
329
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000330class TestDeployCompressedAsh(DeployTest):
331 """Testing deployments with --ash-compressed passed."""
332
333 def _GetDeployChrome(self, args):
334 args.append("--compressed-ash")
335 return super(TestDeployCompressedAsh, self)._GetDeployChrome(args)
336
337 def testUnmountSuccess(self):
338 """Test case for a successful 'umount' call."""
339 self.deploy._KillAshChromeIfNeeded()
340
341 def testUnmountFailure(self):
342 """Test case for a failed 'umount' call."""
343 self.deploy_mock.rsh_mock.AddCmdResult(
344 ["umount", deploy_chrome.RAW_ASH_PATH],
345 returncode=32,
346 stderr="umount failure",
347 )
348 self.assertRaises(
349 deploy_chrome.DeployFailure, self.deploy._KillAshChromeIfNeeded
350 )
351
352
David James88e6f032013-03-02 08:13:20 -0800353class TestMount(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600354 """Testing mount success and failure."""
David James88e6f032013-03-02 08:13:20 -0800355
Alex Klein1699fab2022-09-08 08:46:06 -0600356 def testSuccess(self):
357 """Test case where we are able to mount as writable."""
358 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
359 self.deploy_mock.MockMountCmd(0)
360 self.deploy._MountRootfsAsWritable()
361 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
David James88e6f032013-03-02 08:13:20 -0800362
Alex Klein1699fab2022-09-08 08:46:06 -0600363 def testMountError(self):
364 """Test that mount failure doesn't raise an exception by default."""
365 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
366 self.PatchObject(
367 remote_access.ChromiumOSDevice,
368 "IsDirWritable",
369 return_value=False,
370 autospec=True,
371 )
372 self.deploy._MountRootfsAsWritable()
373 self.assertTrue(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700374
Alex Klein1699fab2022-09-08 08:46:06 -0600375 def testMountRwFailure(self):
376 """Test that mount failure raises an exception if check=True."""
377 self.assertRaises(
378 cros_build_lib.RunCommandError,
379 self.deploy._MountRootfsAsWritable,
380 check=True,
381 )
382 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Robert Flack1dc7ea82014-11-26 13:50:24 -0500383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 def testMountTempDir(self):
385 """Test that mount succeeds if target dir is writable."""
386 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
387 self.PatchObject(
388 remote_access.ChromiumOSDevice,
389 "IsDirWritable",
390 return_value=True,
391 autospec=True,
392 )
393 self.deploy._MountRootfsAsWritable()
394 self.assertFalse(self.deploy._root_dir_is_still_readonly.is_set())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700395
396
Anushruth8d797672019-10-17 12:22:31 -0700397class TestMountTarget(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600398 """Testing mount and umount command handling."""
Anushruth8d797672019-10-17 12:22:31 -0700399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 def testMountTargetUmountFailure(self):
401 """Test error being thrown if umount fails.
Anushruth8d797672019-10-17 12:22:31 -0700402
Alex Klein1699fab2022-09-08 08:46:06 -0600403 Test that 'lsof' is run on mount-dir and 'mount -rbind' command is not run
404 if 'umount' cmd fails.
405 """
406 mount_dir = self.deploy.options.mount_dir
407 target_dir = self.deploy.options.target_dir
408 self.deploy_mock.rsh_mock.AddCmdResult(
409 deploy_chrome._UMOUNT_DIR_IF_MOUNTPOINT_CMD % {"dir": mount_dir},
410 returncode=errno.EBUSY,
411 stderr="Target is Busy",
412 )
413 self.deploy_mock.rsh_mock.AddCmdResult(
414 deploy_chrome.LSOF_COMMAND % (mount_dir,),
415 returncode=0,
416 stdout="process " + mount_dir,
417 )
418 # Check for RunCommandError being thrown.
419 self.assertRaises(
420 cros_build_lib.RunCommandError, self.deploy._MountTarget
421 )
422 # Check for the 'mount -rbind' command not run.
423 self.deploy_mock.rsh_mock.assertCommandContains(
424 (deploy_chrome._BIND_TO_FINAL_DIR_CMD % (target_dir, mount_dir)),
425 expected=False,
426 )
427 # Check for lsof command being called.
428 self.deploy_mock.rsh_mock.assertCommandContains(
429 (deploy_chrome.LSOF_COMMAND % (mount_dir,))
430 )
Anushruth8d797672019-10-17 12:22:31 -0700431
432
Ryan Cuief91e702013-02-04 12:06:36 -0800433class TestUiJobStarted(DeployTest):
Alex Klein1699fab2022-09-08 08:46:06 -0600434 """Test detection of a running 'ui' job."""
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 def MockStatusUiCmd(self, **kwargs):
437 self.deploy_mock.rsh_mock.AddCmdResult("status ui", **kwargs)
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700438
Alex Klein1699fab2022-09-08 08:46:06 -0600439 def testUiJobStartedFalse(self):
440 """Correct results with a stopped job."""
441 self.MockStatusUiCmd(stdout="ui stop/waiting")
442 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuif2d1a582013-02-19 14:08:13 -0800443
Alex Klein1699fab2022-09-08 08:46:06 -0600444 def testNoUiJob(self):
445 """Correct results when the job doesn't exist."""
446 self.MockStatusUiCmd(stderr="start: Unknown job: ui", returncode=1)
447 self.assertFalse(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 def testCheckRootfsWriteableTrue(self):
450 """Correct results with a running job."""
451 self.MockStatusUiCmd(stdout="ui start/running, process 297")
452 self.assertTrue(self.deploy._CheckUiJobStarted())
Ryan Cuiafd6c5c2012-07-30 17:48:22 -0700453
454
Ryan Cuief91e702013-02-04 12:06:36 -0800455class StagingTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600456 """Test user-mode and ebuild-mode staging functionality."""
Ryan Cuief91e702013-02-04 12:06:36 -0800457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 def setUp(self):
459 self.staging_dir = os.path.join(self.tempdir, "staging")
460 osutils.SafeMakedirs(self.staging_dir)
461 self.staging_tarball_path = os.path.join(
462 self.tempdir, deploy_chrome._CHROME_DIR_STAGING_TARBALL_ZSTD
463 )
464 self.build_dir = os.path.join(self.tempdir, "build_dir")
465 self.common_flags = [
466 "--board",
467 _TARGET_BOARD,
468 "--build-dir",
469 self.build_dir,
470 "--staging-only",
471 "--cache-dir",
472 str(self.tempdir),
473 ]
474 self.sdk_mock = self.StartPatcher(
475 cros_chrome_sdk_unittest.SDKFetcherMock()
476 )
477 self.PatchObject(
478 osutils,
479 "SourceEnvironment",
480 autospec=True,
481 return_value={"STRIP": "x86_64-cros-linux-gnu-strip"},
482 )
Ryan Cuief91e702013-02-04 12:06:36 -0800483
Alex Klein1699fab2022-09-08 08:46:06 -0600484 def testSingleFileDeployFailure(self):
485 """Default staging enforces that mandatory files are copied"""
486 options = _ParseCommandLine(self.common_flags)
487 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
488 self.assertRaises(
489 chrome_util.MissingPathError,
490 deploy_chrome._PrepareStagingDir,
491 options,
492 self.tempdir,
493 self.staging_dir,
494 chrome_util._COPY_PATHS_CHROME,
495 )
Ryan Cuief91e702013-02-04 12:06:36 -0800496
Alex Klein1699fab2022-09-08 08:46:06 -0600497 def testSloppyDeployFailure(self):
498 """Sloppy staging enforces that at least one file is copied."""
499 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
500 self.assertRaises(
501 chrome_util.MissingPathError,
502 deploy_chrome._PrepareStagingDir,
503 options,
504 self.tempdir,
505 self.staging_dir,
506 chrome_util._COPY_PATHS_CHROME,
507 )
David Jamesa6e08892013-03-01 13:34:11 -0800508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 def testSloppyDeploySuccess(self):
510 """Sloppy staging - stage one file."""
511 options = _ParseCommandLine(self.common_flags + ["--sloppy"])
512 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
513 deploy_chrome._PrepareStagingDir(
514 options,
515 self.tempdir,
516 self.staging_dir,
517 chrome_util._COPY_PATHS_CHROME,
518 )
David Jamesa6e08892013-03-01 13:34:11 -0800519
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000520 def testSloppyDeploySuccessLacros(self):
521 """Ensure the squashfs mechanism with --compressed-ash doesn't throw."""
522 options = _ParseCommandLine(
523 self.common_flags + ["--sloppy", "--compressed-ash"]
524 )
525 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
526 deploy_chrome._PrepareStagingDir(
527 options,
528 self.tempdir,
529 self.staging_dir,
530 chrome_util._COPY_PATHS_CHROME,
531 )
532
Alex Klein1699fab2022-09-08 08:46:06 -0600533 @cros_test_lib.pytestmark_network_test
534 def testUploadStagingDir(self):
535 """Upload staging directory."""
536 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
537 staging_upload = "gs://some-path"
538 options = _ParseCommandLine(
539 self.common_flags + ["--staging-upload", staging_upload]
540 )
541 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
542 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
543 self.assertEqual(
544 mockGsCopy.call_args_list,
545 [
546 mock.call(self.staging_tarball_path, staging_upload, acl=""),
547 ],
548 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 @cros_test_lib.pytestmark_network_test
551 def testUploadStagingPublicReadACL(self):
552 """Upload staging directory with public-read ACL."""
553 mockGsCopy = self.PatchObject(gs.GSContext, "Copy")
554 staging_upload = "gs://some-path"
555 options = _ParseCommandLine(
556 self.common_flags
557 + ["--staging-upload", staging_upload, "--public-read"]
558 )
559 osutils.Touch(os.path.join(self.build_dir, "chrome"), makedirs=True)
560 deploy_chrome._UploadStagingDir(options, self.tempdir, self.staging_dir)
561 self.assertEqual(
562 mockGsCopy.call_args_list,
563 [
564 mock.call(
565 self.staging_tarball_path, staging_upload, acl="public-read"
566 ),
567 ],
568 )
Jae Hoon Kimdf842912022-05-19 06:40:42 +0000569
Steve Funge984a532013-11-25 17:09:25 -0800570
571class DeployTestBuildDir(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600572 """Set up a deploy object with a build-dir for use in deployment type tests"""
Steve Funge984a532013-11-25 17:09:25 -0800573
Alex Klein1699fab2022-09-08 08:46:06 -0600574 def _GetDeployChrome(self, args):
575 options = _ParseCommandLine(args)
576 return deploy_chrome.DeployChrome(
577 options, self.tempdir, os.path.join(self.tempdir, "staging")
578 )
Steve Funge984a532013-11-25 17:09:25 -0800579
Alex Klein1699fab2022-09-08 08:46:06 -0600580 def setUp(self):
581 self.staging_dir = os.path.join(self.tempdir, "staging")
582 self.build_dir = os.path.join(self.tempdir, "build_dir")
583 self.deploy_mock = self.StartPatcher(DeployChromeMock())
584 self.deploy = self._GetDeployChrome(
585 list(_REGULAR_TO)
586 + [
587 "--board",
588 _TARGET_BOARD,
589 "--build-dir",
590 self.build_dir,
591 "--staging-only",
592 "--cache-dir",
593 str(self.tempdir),
594 "--sloppy",
595 ]
596 )
Steve Funge984a532013-11-25 17:09:25 -0800597
Alex Klein1699fab2022-09-08 08:46:06 -0600598 def getCopyPath(self, source_path):
599 """Return a chrome_util.Path or None if not present."""
600 paths = [p for p in self.deploy.copy_paths if p.src == source_path]
601 return paths[0] if paths else None
602
Steve Funge984a532013-11-25 17:09:25 -0800603
Daniel Erat1ae46382014-08-14 10:23:39 -0700604class TestDeploymentType(DeployTestBuildDir):
Alex Klein1699fab2022-09-08 08:46:06 -0600605 """Test detection of deployment type using build dir."""
Steve Funge984a532013-11-25 17:09:25 -0800606
Alex Klein1699fab2022-09-08 08:46:06 -0600607 def testAppShellDetection(self):
608 """Check for an app_shell deployment"""
609 osutils.Touch(
610 os.path.join(self.deploy.options.build_dir, "app_shell"),
611 makedirs=True,
612 )
613 self.deploy._CheckDeployType()
614 self.assertTrue(self.getCopyPath("app_shell"))
615 self.assertFalse(self.getCopyPath("chrome"))
Steve Funge984a532013-11-25 17:09:25 -0800616
Alex Klein1699fab2022-09-08 08:46:06 -0600617 def testChromeAndAppShellDetection(self):
618 """Check for a chrome deployment when app_shell also exists."""
619 osutils.Touch(
620 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
621 )
622 osutils.Touch(
623 os.path.join(self.deploy.options.build_dir, "app_shell"),
624 makedirs=True,
625 )
626 self.deploy._CheckDeployType()
627 self.assertTrue(self.getCopyPath("chrome"))
628 self.assertFalse(self.getCopyPath("app_shell"))
Steve Fung63d705d2014-03-16 03:14:03 -0700629
Alex Klein1699fab2022-09-08 08:46:06 -0600630 def testChromeDetection(self):
631 """Check for a regular chrome deployment"""
632 osutils.Touch(
633 os.path.join(self.deploy.options.build_dir, "chrome"), makedirs=True
634 )
635 self.deploy._CheckDeployType()
636 self.assertTrue(self.getCopyPath("chrome"))
637 self.assertFalse(self.getCopyPath("app_shell"))
Ben Pastenee484b342020-06-30 18:29:27 -0700638
639
640class TestDeployTestBinaries(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600641 """Tests _DeployTestBinaries()."""
Ben Pastenee484b342020-06-30 18:29:27 -0700642
Alex Klein1699fab2022-09-08 08:46:06 -0600643 def setUp(self):
644 options = _ParseCommandLine(
645 list(_REGULAR_TO)
646 + [
647 "--board",
648 _TARGET_BOARD,
649 "--force",
650 "--mount",
651 "--build-dir",
652 os.path.join(self.tempdir, "build_dir"),
653 "--nostrip",
654 ]
655 )
Daniil Lunev0c4f65c2022-09-19 11:01:34 +1000656 self.remote_exists_mock = self.PatchObject(
657 remote_access.RemoteDevice, "IfFileExists", return_value=False
658 )
Alex Klein1699fab2022-09-08 08:46:06 -0600659 self.deploy = deploy_chrome.DeployChrome(
660 options, self.tempdir, os.path.join(self.tempdir, "staging")
661 )
Ben Pastenee484b342020-06-30 18:29:27 -0700662
Alex Klein1699fab2022-09-08 08:46:06 -0600663 def _SimulateBinaries(self):
664 # Ensure the staging dir contains the right binaries to copy over.
665 test_binaries = [
666 "run_a_tests",
667 "run_b_tests",
668 "run_c_tests",
669 ]
670 # Simulate having the binaries both on the device and in our local build
671 # dir.
672 self.rc.AddCmdResult(
673 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD),
674 stdout="\n".join(test_binaries),
675 )
676 for binary in test_binaries:
677 osutils.Touch(
678 os.path.join(self.deploy.options.build_dir, binary),
679 makedirs=True,
680 mode=0o700,
681 )
682 return test_binaries
Ben Pastenee484b342020-06-30 18:29:27 -0700683
Alex Klein1699fab2022-09-08 08:46:06 -0600684 def _AssertBinariesInStagingDir(self, test_binaries):
685 # Ensure the binaries were placed in the staging dir used to copy them over.
686 staging_dir = os.path.join(
687 self.tempdir, os.path.basename(deploy_chrome._CHROME_TEST_BIN_DIR)
688 )
689 for binary in test_binaries:
690 self.assertIn(binary, os.listdir(staging_dir))
Erik Chen75a2f492020-08-06 19:15:11 -0700691
Alex Klein1699fab2022-09-08 08:46:06 -0600692 def testFindError(self):
693 """Ensure an error is thrown if we can't inspect the device."""
694 self.rc.AddCmdResult(
695 partial_mock.In(deploy_chrome._FIND_TEST_BIN_CMD), 1
696 )
697 self.assertRaises(
698 deploy_chrome.DeployFailure, self.deploy._DeployTestBinaries
699 )
Brian Sheedy86f12342020-10-29 15:30:02 -0700700
Alex Klein1699fab2022-09-08 08:46:06 -0600701 def testSuccess(self):
702 """Ensure that the happy path succeeds as expected."""
703 test_binaries = self._SimulateBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700704 self.deploy._DeployTestBinaries()
Alex Klein1699fab2022-09-08 08:46:06 -0600705 self._AssertBinariesInStagingDir(test_binaries)
706
707 def testRetrySuccess(self):
708 """Ensure that a transient exception still results in success."""
709 # Raises a RunCommandError on its first invocation, but passes on subsequent
710 # calls.
711 def SideEffect(*args, **kwargs):
712 # pylint: disable=unused-argument
713 if not SideEffect.called:
714 SideEffect.called = True
715 raise cros_build_lib.RunCommandError("fail")
716
717 SideEffect.called = False
718
719 test_binaries = self._SimulateBinaries()
720 with mock.patch.object(
721 remote_access.ChromiumOSDevice,
722 "CopyToDevice",
723 side_effect=SideEffect,
724 ) as copy_mock:
725 self.deploy._DeployTestBinaries()
726 self.assertEqual(copy_mock.call_count, 2)
727 self._AssertBinariesInStagingDir(test_binaries)
728
729 def testRetryFailure(self):
730 """Ensure that consistent exceptions result in failure."""
731 self._SimulateBinaries()
732 with self.assertRaises(cros_build_lib.RunCommandError):
733 with mock.patch.object(
734 remote_access.ChromiumOSDevice,
735 "CopyToDevice",
736 side_effect=cros_build_lib.RunCommandError("fail"),
737 ):
738 self.deploy._DeployTestBinaries()
Brian Sheedy86f12342020-10-29 15:30:02 -0700739
Erik Chen75a2f492020-08-06 19:15:11 -0700740
741class LacrosPerformTest(cros_test_lib.RunCommandTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600742 """Line coverage for Perform() method with --lacros option."""
Erik Chen75a2f492020-08-06 19:15:11 -0700743
Alex Klein1699fab2022-09-08 08:46:06 -0600744 def setUp(self):
745 self.deploy = None
746 self._ran_start_command = False
747 self.StartPatcher(parallel_unittest.ParallelMock())
Yuke Liao24fc60c2020-12-26 22:16:49 -0800748
Alex Klein1699fab2022-09-08 08:46:06 -0600749 def start_ui_side_effect(*args, **kwargs):
750 # pylint: disable=unused-argument
751 self._ran_start_command = True
Yuke Liao24fc60c2020-12-26 22:16:49 -0800752
Alex Klein1699fab2022-09-08 08:46:06 -0600753 self.rc.AddCmdResult(
754 partial_mock.In("start ui"), side_effect=start_ui_side_effect
755 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800756
Alex Klein1699fab2022-09-08 08:46:06 -0600757 def prepareDeploy(self, options=None):
758 if not options:
759 options = _ParseCommandLine(
760 [
761 "--lacros",
762 "--nostrip",
763 "--build-dir",
764 "/path/to/nowhere",
765 "--device",
766 "monkey",
767 ]
768 )
769 self.deploy = deploy_chrome.DeployChrome(
770 options, self.tempdir, os.path.join(self.tempdir, "staging")
771 )
Erik Chen75a2f492020-08-06 19:15:11 -0700772
Alex Klein1699fab2022-09-08 08:46:06 -0600773 # These methods being mocked are all side effects expected for a --lacros
774 # deploy.
775 self.deploy._EnsureTargetDir = mock.Mock()
776 self.deploy._GetDeviceInfo = mock.Mock()
777 self.deploy._CheckConnection = mock.Mock()
778 self.deploy._MountRootfsAsWritable = mock.Mock()
779 self.deploy._PrepareStagingDir = mock.Mock()
780 self.deploy._CheckDeviceFreeSpace = mock.Mock()
781 self.deploy._KillAshChromeIfNeeded = mock.Mock()
Erik Chen75a2f492020-08-06 19:15:11 -0700782
Alex Klein1699fab2022-09-08 08:46:06 -0600783 def testConfNotModified(self):
784 """When the conf file is not modified we don't restart chrome ."""
785 self.prepareDeploy()
786 self.deploy.Perform()
787 self.deploy._KillAshChromeIfNeeded.assert_not_called()
788 self.assertFalse(self._ran_start_command)
Erik Chen75a2f492020-08-06 19:15:11 -0700789
Alex Klein1699fab2022-09-08 08:46:06 -0600790 def testConfModified(self):
791 """When the conf file is modified we restart chrome."""
792 self.prepareDeploy()
Erik Chen75a2f492020-08-06 19:15:11 -0700793
Alex Klein1699fab2022-09-08 08:46:06 -0600794 # We intentionally add '\n' to MODIFIED_CONF_FILE to simulate echo adding a
795 # newline when invoked in the shell.
796 self.rc.AddCmdResult(
797 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
798 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
799 )
Erik Chen75a2f492020-08-06 19:15:11 -0700800
Alex Klein1699fab2022-09-08 08:46:06 -0600801 self.deploy.Perform()
802 self.deploy._KillAshChromeIfNeeded.assert_called()
803 self.assertTrue(self._ran_start_command)
Yuke Liao24fc60c2020-12-26 22:16:49 -0800804
Alex Klein1699fab2022-09-08 08:46:06 -0600805 def testSkipModifyingConf(self):
806 """SKip modifying the config file when the argument is specified."""
807 self.prepareDeploy(
808 _ParseCommandLine(
809 [
810 "--lacros",
811 "--nostrip",
812 "--build-dir",
813 "/path/to/nowhere",
814 "--device",
815 "monkey",
816 "--skip-modifying-config-file",
817 ]
818 )
819 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800820
Alex Klein1699fab2022-09-08 08:46:06 -0600821 self.rc.AddCmdResult(
822 partial_mock.In(deploy_chrome.ENABLE_LACROS_VIA_CONF_COMMAND),
823 stdout=deploy_chrome.MODIFIED_CONF_FILE + "\n",
824 )
Yuke Liao24fc60c2020-12-26 22:16:49 -0800825
Alex Klein1699fab2022-09-08 08:46:06 -0600826 self.deploy.Perform()
827 self.deploy._KillAshChromeIfNeeded.assert_not_called()
828 self.assertFalse(self._ran_start_command)