blob: 26a2e75d65da549aa6216a08a1787b6a3de36050 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2015 The ChromiumOS Authors
David Pursellf1d16a62015-03-25 13:31:04 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Unit tests for the flash module."""
6
Chris McDonald14ac61d2021-07-21 11:49:56 -06007import logging
David Pursellf1d16a62015-03-25 13:31:04 -07008import os
Mike Frysinger166fea02021-02-12 05:30:33 -05009from unittest import mock
David Pursellf1d16a62015-03-25 13:31:04 -070010
11from chromite.cli import flash
David Pursellf1d16a62015-03-25 13:31:04 -070012from chromite.lib import commandline
13from chromite.lib import cros_build_lib
14from chromite.lib import cros_test_lib
15from chromite.lib import dev_server_wrapper
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070016from chromite.lib import osutils
David Pursellf1d16a62015-03-25 13:31:04 -070017from chromite.lib import partial_mock
Amin Hassanic0f06fa2019-01-28 15:24:47 -080018
David Pursellf1d16a62015-03-25 13:31:04 -070019
David Pursellf1d16a62015-03-25 13:31:04 -070020class USBImagerMock(partial_mock.PartialCmdMock):
Alex Klein1699fab2022-09-08 08:46:06 -060021 """Mock out USBImager."""
David Pursellf1d16a62015-03-25 13:31:04 -070022
Alex Klein1699fab2022-09-08 08:46:06 -060023 TARGET = "chromite.cli.flash.USBImager"
24 ATTRS = (
25 "CopyImageToDevice",
26 "ChooseRemovableDevice",
27 "ListAllRemovableDevices",
28 "GetRemovableDeviceDescription",
29 )
30 VALID_IMAGE = True
David Pursellf1d16a62015-03-25 13:31:04 -070031
Alex Klein1699fab2022-09-08 08:46:06 -060032 def __init__(self):
33 partial_mock.PartialCmdMock.__init__(self)
David Pursellf1d16a62015-03-25 13:31:04 -070034
Alex Klein1699fab2022-09-08 08:46:06 -060035 def CopyImageToDevice(self, _inst, *_args, **_kwargs):
36 """Mock out CopyImageToDevice."""
David Pursellf1d16a62015-03-25 13:31:04 -070037
Alex Klein1699fab2022-09-08 08:46:06 -060038 def ChooseRemovableDevice(self, _inst, *_args, **_kwargs):
39 """Mock out ChooseRemovableDevice."""
David Pursellf1d16a62015-03-25 13:31:04 -070040
Alex Klein1699fab2022-09-08 08:46:06 -060041 def ListAllRemovableDevices(self, _inst, *_args, **_kwargs):
42 """Mock out ListAllRemovableDevices."""
43 return ["foo", "taco", "milk"]
44
45 def GetRemovableDeviceDescription(self, _inst, *_args, **_kwargs):
46 """Mock out GetRemovableDeviceDescription."""
David Pursellf1d16a62015-03-25 13:31:04 -070047
David Pursellf1d16a62015-03-25 13:31:04 -070048
49class USBImagerTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060050 """Test the flow of flash.Flash() with USBImager."""
David Pursellf1d16a62015-03-25 13:31:04 -070051
Alex Klein1699fab2022-09-08 08:46:06 -060052 IMAGE = "/path/to/image"
David Pursellf1d16a62015-03-25 13:31:04 -070053
Alex Klein1699fab2022-09-08 08:46:06 -060054 def Device(self, path):
55 """Create a USB device for passing to flash.Flash()."""
56 return commandline.Device(
57 scheme=commandline.DEVICE_SCHEME_USB, path=path
58 )
David Pursellf1d16a62015-03-25 13:31:04 -070059
Alex Klein1699fab2022-09-08 08:46:06 -060060 def setUp(self):
61 """Patches objects."""
62 self.usb_mock = USBImagerMock()
63 self.imager_mock = self.StartPatcher(self.usb_mock)
64 self.PatchObject(
65 dev_server_wrapper,
66 "GetImagePathWithXbuddy",
67 return_value=(
68 "taco-paladin/R36/chromiumos_test_image.bin",
69 "remote/taco-paladin/R36/test",
70 ),
71 )
72 self.PatchObject(os.path, "exists", return_value=True)
73 self.PatchObject(os.path, "getsize", return_value=200)
74 self.isgpt_mock = self.PatchObject(
75 flash, "_IsFilePathGPTDiskImage", return_value=True
76 )
77 self.PatchObject(osutils, "GetDeviceSize", return_value=200)
David Pursellf1d16a62015-03-25 13:31:04 -070078
Alex Klein1699fab2022-09-08 08:46:06 -060079 def testLocalImagePathCopy(self):
80 """Tests that imaging methods are called correctly."""
81 with mock.patch("os.path.isfile", return_value=True):
82 flash.Flash(self.Device("/dev/foo"), self.IMAGE)
83 self.assertTrue(
84 self.imager_mock.patched["CopyImageToDevice"].called
85 )
David Pursellf1d16a62015-03-25 13:31:04 -070086
Alex Klein1699fab2022-09-08 08:46:06 -060087 def testLocalBadImagePath(self):
88 """Tests that using an image not having the magic bytes has prompt."""
89 self.isgpt_mock.return_value = False
90 with mock.patch("os.path.isfile", return_value=True):
91 with mock.patch.object(
92 cros_build_lib, "BooleanPrompt"
93 ) as mock_prompt:
94 mock_prompt.return_value = False
95 flash.Flash(self.Device("/dev/foo"), self.IMAGE)
96 self.assertTrue(mock_prompt.called)
David Pursellf1d16a62015-03-25 13:31:04 -070097
Alex Klein1699fab2022-09-08 08:46:06 -060098 def testNonLocalImagePath(self):
99 """Tests that we try to get the image path using xbuddy."""
100 with mock.patch.object(
101 dev_server_wrapper,
102 "GetImagePathWithXbuddy",
103 return_value=("translated/xbuddy/path", "resolved/xbuddy/path"),
104 ) as mock_xbuddy:
105 with mock.patch("os.path.isfile", return_value=False):
106 with mock.patch("os.path.isdir", return_value=False):
107 flash.Flash(self.Device("/dev/foo"), self.IMAGE)
108 self.assertTrue(mock_xbuddy.called)
David Pursellf1d16a62015-03-25 13:31:04 -0700109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 def testConfirmNonRemovableDevice(self):
111 """Tests that we ask user to confirm if the device is not removable."""
112 with mock.patch.object(cros_build_lib, "BooleanPrompt") as mock_prompt:
113 flash.Flash(self.Device("/dev/stub"), self.IMAGE)
114 self.assertTrue(mock_prompt.called)
David Pursellf1d16a62015-03-25 13:31:04 -0700115
Alex Klein1699fab2022-09-08 08:46:06 -0600116 def testSkipPromptNonRemovableDevice(self):
117 """Tests that we skip the prompt for non-removable with --yes."""
118 with mock.patch.object(cros_build_lib, "BooleanPrompt") as mock_prompt:
119 flash.Flash(self.Device("/dev/stub"), self.IMAGE, yes=True)
120 self.assertFalse(mock_prompt.called)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 def testChooseRemovableDevice(self):
123 """Tests that we ask user to choose a device if none is given."""
124 flash.Flash(self.Device(""), self.IMAGE)
125 self.assertTrue(
126 self.imager_mock.patched["ChooseRemovableDevice"].called
127 )
128
129 def testInsufficientRemovableDeviceStorage(self):
130 self.PatchObject(osutils, "GetDeviceSize", return_value=100)
131 with self.assertRaises(flash.FlashError):
132 flash.Flash(self.Device(""), self.IMAGE)
Jae Hoon Kim7f7bc232022-05-04 23:01:16 +0000133
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700134
Benjamin Gordon121a2aa2018-05-04 16:24:45 -0600135class UsbImagerOperationTest(cros_test_lib.RunCommandTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600136 """Tests for flash.UsbImagerOperation."""
Ralph Nathan9b997232015-05-15 13:13:12 -0700137
Alex Klein1699fab2022-09-08 08:46:06 -0600138 # pylint: disable=protected-access
Ralph Nathan9b997232015-05-15 13:13:12 -0700139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 def setUp(self):
141 self.PatchObject(
142 flash.UsbImagerOperation, "__init__", return_value=None
143 )
Ralph Nathan9b997232015-05-15 13:13:12 -0700144
Alex Klein1699fab2022-09-08 08:46:06 -0600145 def testUsbImagerOperationCalled(self):
146 """Test that flash.UsbImagerOperation is called when log level <= NOTICE."""
147 expected_cmd = [
148 "dd",
149 "if=foo",
150 "of=bar",
151 "bs=4M",
152 "iflag=fullblock",
153 "oflag=direct",
154 "conv=fdatasync",
155 ]
156 usb_imager = flash.USBImager("stub_device", "board", "foo", "latest")
157 run_mock = self.PatchObject(flash.UsbImagerOperation, "Run")
158 self.PatchObject(
159 logging.Logger, "getEffectiveLevel", return_value=logging.NOTICE
160 )
161 usb_imager.CopyImageToDevice("foo", "bar")
Ralph Nathan9b997232015-05-15 13:13:12 -0700162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 # Check that flash.UsbImagerOperation.Run() is called correctly.
164 run_mock.assert_called_with(
165 cros_build_lib.sudo_run,
166 expected_cmd,
167 debug_level=logging.NOTICE,
168 encoding="utf-8",
169 update_period=0.5,
170 )
Ralph Nathan9b997232015-05-15 13:13:12 -0700171
Alex Klein1699fab2022-09-08 08:46:06 -0600172 def testSudoRunCommandCalled(self):
173 """Test that sudo_run is called when log level > NOTICE."""
174 expected_cmd = [
175 "dd",
176 "if=foo",
177 "of=bar",
178 "bs=4M",
179 "iflag=fullblock",
180 "oflag=direct",
181 "conv=fdatasync",
182 ]
183 usb_imager = flash.USBImager("stub_device", "board", "foo", "latest")
184 run_mock = self.PatchObject(cros_build_lib, "sudo_run")
185 self.PatchObject(
186 logging.Logger, "getEffectiveLevel", return_value=logging.WARNING
187 )
188 usb_imager.CopyImageToDevice("foo", "bar")
Ralph Nathan9b997232015-05-15 13:13:12 -0700189
Alex Klein1699fab2022-09-08 08:46:06 -0600190 # Check that sudo_run() is called correctly.
191 run_mock.assert_any_call(
192 expected_cmd, debug_level=logging.NOTICE, print_cmd=False
193 )
Ralph Nathan9b997232015-05-15 13:13:12 -0700194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 def testPingDD(self):
196 """Test that UsbImagerOperation._PingDD() sends the correct signal."""
197 expected_cmd = ["kill", "-USR1", "5"]
198 run_mock = self.PatchObject(cros_build_lib, "sudo_run")
199 op = flash.UsbImagerOperation("foo")
200 op._PingDD(5)
Ralph Nathan9b997232015-05-15 13:13:12 -0700201
Alex Klein1699fab2022-09-08 08:46:06 -0600202 # Check that sudo_run was called correctly.
203 run_mock.assert_called_with(expected_cmd, print_cmd=False)
Ralph Nathan9b997232015-05-15 13:13:12 -0700204
Alex Klein1699fab2022-09-08 08:46:06 -0600205 def testGetDDPidFound(self):
206 """Check that the expected pid is returned for _GetDDPid()."""
207 expected_pid = 5
208 op = flash.UsbImagerOperation("foo")
209 self.PatchObject(osutils, "IsChildProcess", return_value=True)
210 self.rc.AddCmdResult(
211 partial_mock.Ignore(), stdout=f"{expected_pid}\n10\n"
212 )
Ralph Nathan9b997232015-05-15 13:13:12 -0700213
Alex Klein1699fab2022-09-08 08:46:06 -0600214 pid = op._GetDDPid()
Ralph Nathan9b997232015-05-15 13:13:12 -0700215
Alex Klein1699fab2022-09-08 08:46:06 -0600216 # Check that the correct pid was returned.
217 self.assertEqual(pid, expected_pid)
Ralph Nathan9b997232015-05-15 13:13:12 -0700218
Alex Klein1699fab2022-09-08 08:46:06 -0600219 def testGetDDPidNotFound(self):
220 """Check that -1 is returned for _GetDDPid() if the pids aren't valid."""
221 expected_pid = -1
222 op = flash.UsbImagerOperation("foo")
223 self.PatchObject(osutils, "IsChildProcess", return_value=False)
224 self.rc.AddCmdResult(partial_mock.Ignore(), stdout="5\n10\n")
Ralph Nathan9b997232015-05-15 13:13:12 -0700225
Alex Klein1699fab2022-09-08 08:46:06 -0600226 pid = op._GetDDPid()
227
228 # Check that the correct pid was returned.
229 self.assertEqual(pid, expected_pid)
Ralph Nathan9b997232015-05-15 13:13:12 -0700230
231
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700232class FlashUtilTest(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600233 """Tests the helpers from cli.flash."""
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700234
Alex Klein1699fab2022-09-08 08:46:06 -0600235 def testChooseImage(self):
236 """Tests that we can detect a GPT image."""
237 # pylint: disable=protected-access
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700238
Alex Klein1699fab2022-09-08 08:46:06 -0600239 with self.PatchObject(
240 flash, "_IsFilePathGPTDiskImage", return_value=True
241 ):
242 # No images defined. Choosing the image should raise an error.
243 with self.assertRaises(ValueError):
244 flash._ChooseImageFromDirectory(self.tempdir)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 file_a = os.path.join(self.tempdir, "a")
247 osutils.Touch(file_a)
248 # Only one image available, it should be selected automatically.
249 self.assertEqual(
250 file_a, flash._ChooseImageFromDirectory(self.tempdir)
251 )
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 osutils.Touch(os.path.join(self.tempdir, "b"))
254 file_c = os.path.join(self.tempdir, "c")
255 osutils.Touch(file_c)
256 osutils.Touch(os.path.join(self.tempdir, "d"))
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700257
Alex Klein1699fab2022-09-08 08:46:06 -0600258 # Multiple images available, we should ask the user to select the right
259 # image.
260 with self.PatchObject(cros_build_lib, "GetChoice", return_value=2):
261 self.assertEqual(
262 file_c, flash._ChooseImageFromDirectory(self.tempdir)
263 )
Mike Frysinger32759e42016-12-21 18:40:16 -0500264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 def testIsFilePathGPTDiskImage(self):
266 """Tests the GPT image probing."""
267 # pylint: disable=protected-access
Mike Frysinger32759e42016-12-21 18:40:16 -0500268
Alex Klein1699fab2022-09-08 08:46:06 -0600269 INVALID_PMBR = b" " * 0x200
270 INVALID_GPT = b" " * 0x200
271 VALID_PMBR = (b" " * 0x1FE) + b"\x55\xaa"
272 VALID_GPT = b"EFI PART" + (b" " * 0x1F8)
273 TESTCASES = (
274 (False, False, INVALID_PMBR + INVALID_GPT),
275 (False, False, VALID_PMBR + INVALID_GPT),
276 (False, True, INVALID_PMBR + VALID_GPT),
277 (True, True, VALID_PMBR + VALID_GPT),
278 )
Mike Frysinger32759e42016-12-21 18:40:16 -0500279
Alex Klein1699fab2022-09-08 08:46:06 -0600280 img = os.path.join(self.tempdir, "img.bin")
281 for exp_pmbr_t, exp_pmbr_f, data in TESTCASES:
282 osutils.WriteFile(img, data, mode="wb")
283 self.assertEqual(
284 flash._IsFilePathGPTDiskImage(img, require_pmbr=True),
285 exp_pmbr_t,
286 )
287 self.assertEqual(
288 flash._IsFilePathGPTDiskImage(img, require_pmbr=False),
289 exp_pmbr_f,
290 )