blob: ae5756c03b55c5225dd386958dbf49f2611fb1bc [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
David Pursellf1d16a62015-03-25 13:31:04 -07002# Copyright 2015 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Unit tests for the flash module."""
7
8from __future__ import print_function
9
10import mock
11import os
12
13from chromite.cli import flash
xixuane851dfb2016-05-02 18:02:37 -070014from chromite.lib import auto_updater
David Pursellf1d16a62015-03-25 13:31:04 -070015from chromite.lib import commandline
16from chromite.lib import cros_build_lib
Ralph Nathan9b997232015-05-15 13:13:12 -070017from chromite.lib import cros_logging as logging
David Pursellf1d16a62015-03-25 13:31:04 -070018from chromite.lib import cros_test_lib
19from chromite.lib import dev_server_wrapper
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070020from chromite.lib import osutils
David Pursellf1d16a62015-03-25 13:31:04 -070021from chromite.lib import partial_mock
22from chromite.lib import remote_access
23
24
25class RemoteDeviceUpdaterMock(partial_mock.PartialCmdMock):
26 """Mock out RemoteDeviceUpdater."""
xixuan9a157272016-05-31 11:35:13 -070027 TARGET = 'chromite.lib.auto_updater.ChromiumOSFlashUpdater'
xixuane851dfb2016-05-02 18:02:37 -070028 ATTRS = ('UpdateStateful', 'UpdateRootfs', 'SetupRootfsUpdate',
29 'RebootAndVerify')
David Pursellf1d16a62015-03-25 13:31:04 -070030
31 def __init__(self):
32 partial_mock.PartialCmdMock.__init__(self)
33
34 def UpdateStateful(self, _inst, *_args, **_kwargs):
35 """Mock out UpdateStateful."""
36
37 def UpdateRootfs(self, _inst, *_args, **_kwargs):
38 """Mock out UpdateRootfs."""
39
40 def SetupRootfsUpdate(self, _inst, *_args, **_kwargs):
41 """Mock out SetupRootfsUpdate."""
42
xixuane851dfb2016-05-02 18:02:37 -070043 def RebootAndVerify(self, _inst, *_args, **_kwargs):
44 """Mock out RebootAndVerify."""
David Pursellf1d16a62015-03-25 13:31:04 -070045
46
David Pursellf1d16a62015-03-25 13:31:04 -070047class RemoteDeviceUpdaterTest(cros_test_lib.MockTempDirTestCase):
48 """Test the flow of flash.Flash() with RemoteDeviceUpdater."""
49
50 IMAGE = '/path/to/image'
51 DEVICE = commandline.Device(scheme=commandline.DEVICE_SCHEME_SSH,
52 hostname='1.1.1.1')
53
54 def setUp(self):
55 """Patches objects."""
56 self.updater_mock = self.StartPatcher(RemoteDeviceUpdaterMock())
57 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
58 return_value='xbuddy/local/latest')
59 self.PatchObject(dev_server_wrapper, 'DevServerWrapper')
60 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -070061 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
62 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -070063 self.PatchObject(dev_server_wrapper, 'GetUpdatePayloads')
64 self.PatchObject(remote_access, 'CHECK_INTERVAL', new=0)
65 self.PatchObject(remote_access, 'ChromiumOSDevice')
66
67 def testUpdateAll(self):
68 """Tests that update methods are called correctly."""
David Pursellf1d16a62015-03-25 13:31:04 -070069 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070070 flash.Flash(self.DEVICE, self.IMAGE)
71 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
72 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -070073
74 def testUpdateStateful(self):
75 """Tests that update methods are called correctly."""
David Pursellf1d16a62015-03-25 13:31:04 -070076 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070077 flash.Flash(self.DEVICE, self.IMAGE, rootfs_update=False)
78 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
79 self.assertFalse(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -070080
81 def testUpdateRootfs(self):
82 """Tests that update methods are called correctly."""
David Pursellf1d16a62015-03-25 13:31:04 -070083 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070084 flash.Flash(self.DEVICE, self.IMAGE, stateful_update=False)
85 self.assertFalse(self.updater_mock.patched['UpdateStateful'].called)
86 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -070087
88 def testMissingPayloads(self):
89 """Tests we raise FlashError when payloads are missing."""
90 with mock.patch('os.path.exists', return_value=False):
xixuane851dfb2016-05-02 18:02:37 -070091 self.assertRaises(auto_updater.ChromiumOSUpdateError, flash.Flash,
92 self.DEVICE, self.IMAGE)
David Pursellf1d16a62015-03-25 13:31:04 -070093
David Pursellf1d16a62015-03-25 13:31:04 -070094
95class USBImagerMock(partial_mock.PartialCmdMock):
96 """Mock out USBImager."""
97 TARGET = 'chromite.cli.flash.USBImager'
98 ATTRS = ('CopyImageToDevice', 'InstallImageToDevice',
99 'ChooseRemovableDevice', 'ListAllRemovableDevices',
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700100 'GetRemovableDeviceDescription')
David Pursellf1d16a62015-03-25 13:31:04 -0700101 VALID_IMAGE = True
102
103 def __init__(self):
104 partial_mock.PartialCmdMock.__init__(self)
105
106 def CopyImageToDevice(self, _inst, *_args, **_kwargs):
107 """Mock out CopyImageToDevice."""
108
109 def InstallImageToDevice(self, _inst, *_args, **_kwargs):
110 """Mock out InstallImageToDevice."""
111
112 def ChooseRemovableDevice(self, _inst, *_args, **_kwargs):
113 """Mock out ChooseRemovableDevice."""
114
115 def ListAllRemovableDevices(self, _inst, *_args, **_kwargs):
116 """Mock out ListAllRemovableDevices."""
117 return ['foo', 'taco', 'milk']
118
119 def GetRemovableDeviceDescription(self, _inst, *_args, **_kwargs):
120 """Mock out GetRemovableDeviceDescription."""
121
David Pursellf1d16a62015-03-25 13:31:04 -0700122
123class USBImagerTest(cros_test_lib.MockTempDirTestCase):
124 """Test the flow of flash.Flash() with USBImager."""
125 IMAGE = '/path/to/image'
126
127 def Device(self, path):
128 """Create a USB device for passing to flash.Flash()."""
129 return commandline.Device(scheme=commandline.DEVICE_SCHEME_USB,
130 path=path)
131
132 def setUp(self):
133 """Patches objects."""
134 self.usb_mock = USBImagerMock()
135 self.imager_mock = self.StartPatcher(self.usb_mock)
136 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
137 return_value='xbuddy/local/latest')
138 self.PatchObject(dev_server_wrapper, 'DevServerWrapper')
139 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700140 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
141 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -0700142 self.PatchObject(os.path, 'exists', return_value=True)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700143 self.isgpt_mock = self.PatchObject(flash, '_IsFilePathGPTDiskImage',
144 return_value=True)
David Pursellf1d16a62015-03-25 13:31:04 -0700145
146 def testLocalImagePathCopy(self):
147 """Tests that imaging methods are called correctly."""
148 with mock.patch('os.path.isfile', return_value=True):
149 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
150 self.assertTrue(self.imager_mock.patched['CopyImageToDevice'].called)
151
152 def testLocalImagePathInstall(self):
153 """Tests that imaging methods are called correctly."""
154 with mock.patch('os.path.isfile', return_value=True):
155 flash.Flash(self.Device('/dev/foo'), self.IMAGE, board='taco',
156 install=True)
157 self.assertTrue(self.imager_mock.patched['InstallImageToDevice'].called)
158
159 def testLocalBadImagePath(self):
160 """Tests that using an image not having the magic bytes has prompt."""
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700161 self.isgpt_mock.return_value = False
David Pursellf1d16a62015-03-25 13:31:04 -0700162 with mock.patch('os.path.isfile', return_value=True):
163 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
164 mock_prompt.return_value = False
165 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
166 self.assertTrue(mock_prompt.called)
167
168 def testNonLocalImagePath(self):
169 """Tests that we try to get the image path using xbuddy."""
170 with mock.patch.object(
171 dev_server_wrapper,
172 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700173 return_value=('translated/xbuddy/path',
174 'resolved/xbuddy/path')) as mock_xbuddy:
David Pursellf1d16a62015-03-25 13:31:04 -0700175 with mock.patch('os.path.isfile', return_value=False):
176 with mock.patch('os.path.isdir', return_value=False):
177 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
178 self.assertTrue(mock_xbuddy.called)
179
180 def testConfirmNonRemovableDevice(self):
181 """Tests that we ask user to confirm if the device is not removable."""
182 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
183 flash.Flash(self.Device('/dev/dummy'), self.IMAGE)
184 self.assertTrue(mock_prompt.called)
185
186 def testSkipPromptNonRemovableDevice(self):
187 """Tests that we skip the prompt for non-removable with --yes."""
188 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
189 flash.Flash(self.Device('/dev/dummy'), self.IMAGE, yes=True)
190 self.assertFalse(mock_prompt.called)
191
192 def testChooseRemovableDevice(self):
193 """Tests that we ask user to choose a device if none is given."""
194 flash.Flash(self.Device(''), self.IMAGE)
195 self.assertTrue(self.imager_mock.patched['ChooseRemovableDevice'].called)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700196
197
Benjamin Gordon121a2aa2018-05-04 16:24:45 -0600198class UsbImagerOperationTest(cros_test_lib.RunCommandTestCase):
Ralph Nathan9b997232015-05-15 13:13:12 -0700199 """Tests for flash.UsbImagerOperation."""
200 # pylint: disable=protected-access
201
202 def setUp(self):
203 self.PatchObject(flash.UsbImagerOperation, '__init__', return_value=None)
204
205 def testUsbImagerOperationCalled(self):
206 """Test that flash.UsbImagerOperation is called when log level <= NOTICE."""
207 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
208 'oflag=sync']
209 usb_imager = flash.USBImager('dummy_device', 'board', 'foo')
210 run_mock = self.PatchObject(flash.UsbImagerOperation, 'Run')
211 self.PatchObject(logging.Logger, 'getEffectiveLevel',
212 return_value=logging.NOTICE)
213 usb_imager.CopyImageToDevice('foo', 'bar')
214
215 # Check that flash.UsbImagerOperation.Run() is called correctly.
216 run_mock.assert_called_with(cros_build_lib.SudoRunCommand, expected_cmd,
217 debug_level=logging.NOTICE, update_period=0.5)
218
219 def testSudoRunCommandCalled(self):
220 """Test that SudoRunCommand is called when log level > NOTICE."""
221 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
222 'oflag=sync']
223 usb_imager = flash.USBImager('dummy_device', 'board', 'foo')
224 run_mock = self.PatchObject(cros_build_lib, 'SudoRunCommand')
225 self.PatchObject(logging.Logger, 'getEffectiveLevel',
226 return_value=logging.WARNING)
227 usb_imager.CopyImageToDevice('foo', 'bar')
228
229 # Check that SudoRunCommand() is called correctly.
230 run_mock.assert_any_call(expected_cmd, debug_level=logging.NOTICE,
231 print_cmd=False)
232
233 def testPingDD(self):
234 """Test that UsbImagerOperation._PingDD() sends the correct signal."""
235 expected_cmd = ['kill', '-USR1', '5']
236 run_mock = self.PatchObject(cros_build_lib, 'SudoRunCommand')
237 op = flash.UsbImagerOperation('foo')
238 op._PingDD(5)
239
240 # Check that SudoRunCommand was called correctly.
241 run_mock.assert_called_with(expected_cmd, print_cmd=False)
242
243 def testGetDDPidFound(self):
244 """Check that the expected pid is returned for _GetDDPid()."""
245 expected_pid = 5
246 op = flash.UsbImagerOperation('foo')
247 self.PatchObject(osutils, 'IsChildProcess', return_value=True)
248 self.rc.AddCmdResult(partial_mock.Ignore(),
249 output='%d\n10\n' % expected_pid)
250
251 pid = op._GetDDPid()
252
253 # Check that the correct pid was returned.
254 self.assertEqual(pid, expected_pid)
255
256 def testGetDDPidNotFound(self):
257 """Check that -1 is returned for _GetDDPid() if the pids aren't valid."""
258 expected_pid = -1
259 op = flash.UsbImagerOperation('foo')
260 self.PatchObject(osutils, 'IsChildProcess', return_value=False)
261 self.rc.AddCmdResult(partial_mock.Ignore(), output='5\n10\n')
262
263 pid = op._GetDDPid()
264
265 # Check that the correct pid was returned.
266 self.assertEqual(pid, expected_pid)
267
268
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700269class FlashUtilTest(cros_test_lib.MockTempDirTestCase):
270 """Tests the helpers from cli.flash."""
271
272 def testChooseImage(self):
273 """Tests that we can detect a GPT image."""
274 # pylint: disable=protected-access
275
276 with self.PatchObject(flash, '_IsFilePathGPTDiskImage', return_value=True):
277 # No images defined. Choosing the image should raise an error.
278 with self.assertRaises(ValueError):
279 flash._ChooseImageFromDirectory(self.tempdir)
280
281 file_a = os.path.join(self.tempdir, 'a')
282 osutils.Touch(file_a)
283 # Only one image available, it should be selected automatically.
284 self.assertEqual(file_a, flash._ChooseImageFromDirectory(self.tempdir))
285
286 osutils.Touch(os.path.join(self.tempdir, 'b'))
287 file_c = os.path.join(self.tempdir, 'c')
288 osutils.Touch(file_c)
289 osutils.Touch(os.path.join(self.tempdir, 'd'))
290
291 # Multiple images available, we should ask the user to select the right
292 # image.
293 with self.PatchObject(cros_build_lib, 'GetChoice', return_value=2):
294 self.assertEqual(file_c, flash._ChooseImageFromDirectory(self.tempdir))
Mike Frysinger32759e42016-12-21 18:40:16 -0500295
296 def testIsFilePathGPTDiskImage(self):
297 """Tests the GPT image probing."""
298 # pylint: disable=protected-access
299
300 INVALID_PMBR = ' ' * 0x200
301 INVALID_GPT = ' ' * 0x200
302 VALID_PMBR = (' ' * 0x1fe) + '\x55\xaa'
303 VALID_GPT = 'EFI PART' + (' ' * 0x1f8)
304 TESTCASES = (
305 (False, False, INVALID_PMBR + INVALID_GPT),
306 (False, False, VALID_PMBR + INVALID_GPT),
307 (False, True, INVALID_PMBR + VALID_GPT),
308 (True, True, VALID_PMBR + VALID_GPT),
309 )
310
311 img = os.path.join(self.tempdir, 'img.bin')
312 for exp_pmbr_t, exp_pmbr_f, data in TESTCASES:
313 osutils.WriteFile(img, data)
314 self.assertEqual(
315 flash._IsFilePathGPTDiskImage(img, require_pmbr=True), exp_pmbr_t)
316 self.assertEqual(
317 flash._IsFilePathGPTDiskImage(img, require_pmbr=False), exp_pmbr_f)