blob: 86edea38a2bf0631093018d1dab7b57016c30882 [file] [log] [blame]
David Pursellf1d16a62015-03-25 13:31:04 -07001# Copyright 2015 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
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):
21 """Mock out USBImager."""
22 TARGET = 'chromite.cli.flash.USBImager'
Amin Hassani04314b12020-12-15 15:59:54 -080023 ATTRS = ('CopyImageToDevice', 'ChooseRemovableDevice',
24 'ListAllRemovableDevices', 'GetRemovableDeviceDescription')
David Pursellf1d16a62015-03-25 13:31:04 -070025 VALID_IMAGE = True
26
27 def __init__(self):
28 partial_mock.PartialCmdMock.__init__(self)
29
30 def CopyImageToDevice(self, _inst, *_args, **_kwargs):
31 """Mock out CopyImageToDevice."""
32
David Pursellf1d16a62015-03-25 13:31:04 -070033 def ChooseRemovableDevice(self, _inst, *_args, **_kwargs):
34 """Mock out ChooseRemovableDevice."""
35
36 def ListAllRemovableDevices(self, _inst, *_args, **_kwargs):
37 """Mock out ListAllRemovableDevices."""
38 return ['foo', 'taco', 'milk']
39
40 def GetRemovableDeviceDescription(self, _inst, *_args, **_kwargs):
41 """Mock out GetRemovableDeviceDescription."""
42
David Pursellf1d16a62015-03-25 13:31:04 -070043
44class USBImagerTest(cros_test_lib.MockTempDirTestCase):
45 """Test the flow of flash.Flash() with USBImager."""
46 IMAGE = '/path/to/image'
47
48 def Device(self, path):
49 """Create a USB device for passing to flash.Flash()."""
50 return commandline.Device(scheme=commandline.DEVICE_SCHEME_USB,
51 path=path)
52
53 def setUp(self):
54 """Patches objects."""
55 self.usb_mock = USBImagerMock()
56 self.imager_mock = self.StartPatcher(self.usb_mock)
David Pursellf1d16a62015-03-25 13:31:04 -070057 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -070058 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
59 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -070060 self.PatchObject(os.path, 'exists', return_value=True)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070061 self.isgpt_mock = self.PatchObject(flash, '_IsFilePathGPTDiskImage',
62 return_value=True)
David Pursellf1d16a62015-03-25 13:31:04 -070063
64 def testLocalImagePathCopy(self):
65 """Tests that imaging methods are called correctly."""
66 with mock.patch('os.path.isfile', return_value=True):
67 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
68 self.assertTrue(self.imager_mock.patched['CopyImageToDevice'].called)
69
David Pursellf1d16a62015-03-25 13:31:04 -070070 def testLocalBadImagePath(self):
71 """Tests that using an image not having the magic bytes has prompt."""
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070072 self.isgpt_mock.return_value = False
David Pursellf1d16a62015-03-25 13:31:04 -070073 with mock.patch('os.path.isfile', return_value=True):
74 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
75 mock_prompt.return_value = False
76 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
77 self.assertTrue(mock_prompt.called)
78
79 def testNonLocalImagePath(self):
80 """Tests that we try to get the image path using xbuddy."""
81 with mock.patch.object(
82 dev_server_wrapper,
83 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -070084 return_value=('translated/xbuddy/path',
85 'resolved/xbuddy/path')) as mock_xbuddy:
David Pursellf1d16a62015-03-25 13:31:04 -070086 with mock.patch('os.path.isfile', return_value=False):
87 with mock.patch('os.path.isdir', return_value=False):
88 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
89 self.assertTrue(mock_xbuddy.called)
90
91 def testConfirmNonRemovableDevice(self):
92 """Tests that we ask user to confirm if the device is not removable."""
93 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
Mike Frysingera5c6e792022-03-15 23:42:12 -040094 flash.Flash(self.Device('/dev/stub'), self.IMAGE)
David Pursellf1d16a62015-03-25 13:31:04 -070095 self.assertTrue(mock_prompt.called)
96
97 def testSkipPromptNonRemovableDevice(self):
98 """Tests that we skip the prompt for non-removable with --yes."""
99 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
Mike Frysingera5c6e792022-03-15 23:42:12 -0400100 flash.Flash(self.Device('/dev/stub'), self.IMAGE, yes=True)
David Pursellf1d16a62015-03-25 13:31:04 -0700101 self.assertFalse(mock_prompt.called)
102
103 def testChooseRemovableDevice(self):
104 """Tests that we ask user to choose a device if none is given."""
105 flash.Flash(self.Device(''), self.IMAGE)
106 self.assertTrue(self.imager_mock.patched['ChooseRemovableDevice'].called)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700107
108
Benjamin Gordon121a2aa2018-05-04 16:24:45 -0600109class UsbImagerOperationTest(cros_test_lib.RunCommandTestCase):
Ralph Nathan9b997232015-05-15 13:13:12 -0700110 """Tests for flash.UsbImagerOperation."""
111 # pylint: disable=protected-access
112
113 def setUp(self):
114 self.PatchObject(flash.UsbImagerOperation, '__init__', return_value=None)
115
116 def testUsbImagerOperationCalled(self):
117 """Test that flash.UsbImagerOperation is called when log level <= NOTICE."""
118 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
Frank Huang8e626432019-06-24 19:51:08 +0800119 'oflag=direct', 'conv=fdatasync']
Mike Frysingera5c6e792022-03-15 23:42:12 -0400120 usb_imager = flash.USBImager('stub_device', 'board', 'foo', 'latest')
Ralph Nathan9b997232015-05-15 13:13:12 -0700121 run_mock = self.PatchObject(flash.UsbImagerOperation, 'Run')
122 self.PatchObject(logging.Logger, 'getEffectiveLevel',
123 return_value=logging.NOTICE)
124 usb_imager.CopyImageToDevice('foo', 'bar')
125
126 # Check that flash.UsbImagerOperation.Run() is called correctly.
Mike Frysinger45602c72019-09-22 02:15:11 -0400127 run_mock.assert_called_with(cros_build_lib.sudo_run, expected_cmd,
Mike Frysinger3d5de8f2019-10-23 00:48:39 -0400128 debug_level=logging.NOTICE, encoding='utf-8',
129 update_period=0.5)
Ralph Nathan9b997232015-05-15 13:13:12 -0700130
131 def testSudoRunCommandCalled(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400132 """Test that sudo_run is called when log level > NOTICE."""
Ralph Nathan9b997232015-05-15 13:13:12 -0700133 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
Frank Huang8e626432019-06-24 19:51:08 +0800134 'oflag=direct', 'conv=fdatasync']
Mike Frysingera5c6e792022-03-15 23:42:12 -0400135 usb_imager = flash.USBImager('stub_device', 'board', 'foo', 'latest')
Mike Frysinger45602c72019-09-22 02:15:11 -0400136 run_mock = self.PatchObject(cros_build_lib, 'sudo_run')
Ralph Nathan9b997232015-05-15 13:13:12 -0700137 self.PatchObject(logging.Logger, 'getEffectiveLevel',
138 return_value=logging.WARNING)
139 usb_imager.CopyImageToDevice('foo', 'bar')
140
Mike Frysinger45602c72019-09-22 02:15:11 -0400141 # Check that sudo_run() is called correctly.
Ralph Nathan9b997232015-05-15 13:13:12 -0700142 run_mock.assert_any_call(expected_cmd, debug_level=logging.NOTICE,
143 print_cmd=False)
144
145 def testPingDD(self):
146 """Test that UsbImagerOperation._PingDD() sends the correct signal."""
147 expected_cmd = ['kill', '-USR1', '5']
Mike Frysinger45602c72019-09-22 02:15:11 -0400148 run_mock = self.PatchObject(cros_build_lib, 'sudo_run')
Ralph Nathan9b997232015-05-15 13:13:12 -0700149 op = flash.UsbImagerOperation('foo')
150 op._PingDD(5)
151
Mike Frysinger45602c72019-09-22 02:15:11 -0400152 # Check that sudo_run was called correctly.
Ralph Nathan9b997232015-05-15 13:13:12 -0700153 run_mock.assert_called_with(expected_cmd, print_cmd=False)
154
155 def testGetDDPidFound(self):
156 """Check that the expected pid is returned for _GetDDPid()."""
157 expected_pid = 5
158 op = flash.UsbImagerOperation('foo')
Mike Nicholsa1414162021-04-22 20:07:22 +0000159 self.PatchObject(osutils, 'IsChildProcess', return_value=True)
Ralph Nathan9b997232015-05-15 13:13:12 -0700160 self.rc.AddCmdResult(partial_mock.Ignore(),
161 output='%d\n10\n' % expected_pid)
162
163 pid = op._GetDDPid()
164
165 # Check that the correct pid was returned.
166 self.assertEqual(pid, expected_pid)
167
168 def testGetDDPidNotFound(self):
169 """Check that -1 is returned for _GetDDPid() if the pids aren't valid."""
170 expected_pid = -1
171 op = flash.UsbImagerOperation('foo')
Mike Nicholsa1414162021-04-22 20:07:22 +0000172 self.PatchObject(osutils, 'IsChildProcess', return_value=False)
Ralph Nathan9b997232015-05-15 13:13:12 -0700173 self.rc.AddCmdResult(partial_mock.Ignore(), output='5\n10\n')
174
175 pid = op._GetDDPid()
176
177 # Check that the correct pid was returned.
178 self.assertEqual(pid, expected_pid)
179
180
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700181class FlashUtilTest(cros_test_lib.MockTempDirTestCase):
182 """Tests the helpers from cli.flash."""
183
184 def testChooseImage(self):
185 """Tests that we can detect a GPT image."""
186 # pylint: disable=protected-access
187
188 with self.PatchObject(flash, '_IsFilePathGPTDiskImage', return_value=True):
189 # No images defined. Choosing the image should raise an error.
190 with self.assertRaises(ValueError):
191 flash._ChooseImageFromDirectory(self.tempdir)
192
193 file_a = os.path.join(self.tempdir, 'a')
194 osutils.Touch(file_a)
195 # Only one image available, it should be selected automatically.
196 self.assertEqual(file_a, flash._ChooseImageFromDirectory(self.tempdir))
197
198 osutils.Touch(os.path.join(self.tempdir, 'b'))
199 file_c = os.path.join(self.tempdir, 'c')
200 osutils.Touch(file_c)
201 osutils.Touch(os.path.join(self.tempdir, 'd'))
202
203 # Multiple images available, we should ask the user to select the right
204 # image.
205 with self.PatchObject(cros_build_lib, 'GetChoice', return_value=2):
206 self.assertEqual(file_c, flash._ChooseImageFromDirectory(self.tempdir))
Mike Frysinger32759e42016-12-21 18:40:16 -0500207
208 def testIsFilePathGPTDiskImage(self):
209 """Tests the GPT image probing."""
210 # pylint: disable=protected-access
211
Mike Frysinger3d5de8f2019-10-23 00:48:39 -0400212 INVALID_PMBR = b' ' * 0x200
213 INVALID_GPT = b' ' * 0x200
214 VALID_PMBR = (b' ' * 0x1fe) + b'\x55\xaa'
215 VALID_GPT = b'EFI PART' + (b' ' * 0x1f8)
Mike Frysinger32759e42016-12-21 18:40:16 -0500216 TESTCASES = (
217 (False, False, INVALID_PMBR + INVALID_GPT),
218 (False, False, VALID_PMBR + INVALID_GPT),
219 (False, True, INVALID_PMBR + VALID_GPT),
220 (True, True, VALID_PMBR + VALID_GPT),
221 )
222
223 img = os.path.join(self.tempdir, 'img.bin')
224 for exp_pmbr_t, exp_pmbr_f, data in TESTCASES:
Mike Frysinger3d5de8f2019-10-23 00:48:39 -0400225 osutils.WriteFile(img, data, mode='wb')
Mike Frysinger32759e42016-12-21 18:40:16 -0500226 self.assertEqual(
227 flash._IsFilePathGPTDiskImage(img, require_pmbr=True), exp_pmbr_t)
228 self.assertEqual(
229 flash._IsFilePathGPTDiskImage(img, require_pmbr=False), exp_pmbr_f)