blob: 4e6184f1d894ec952d970a69f77d80f0c44134da [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_build_lib_unittest
18from chromite.lib import cros_logging as logging
David Pursellf1d16a62015-03-25 13:31:04 -070019from chromite.lib import cros_test_lib
20from chromite.lib import dev_server_wrapper
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070021from chromite.lib import osutils
David Pursellf1d16a62015-03-25 13:31:04 -070022from chromite.lib import partial_mock
23from chromite.lib import remote_access
24
25
26class RemoteDeviceUpdaterMock(partial_mock.PartialCmdMock):
27 """Mock out RemoteDeviceUpdater."""
xixuan9a157272016-05-31 11:35:13 -070028 TARGET = 'chromite.lib.auto_updater.ChromiumOSFlashUpdater'
xixuane851dfb2016-05-02 18:02:37 -070029 ATTRS = ('UpdateStateful', 'UpdateRootfs', 'SetupRootfsUpdate',
30 'RebootAndVerify')
David Pursellf1d16a62015-03-25 13:31:04 -070031
32 def __init__(self):
33 partial_mock.PartialCmdMock.__init__(self)
34
35 def UpdateStateful(self, _inst, *_args, **_kwargs):
36 """Mock out UpdateStateful."""
37
38 def UpdateRootfs(self, _inst, *_args, **_kwargs):
39 """Mock out UpdateRootfs."""
40
41 def SetupRootfsUpdate(self, _inst, *_args, **_kwargs):
42 """Mock out SetupRootfsUpdate."""
43
xixuane851dfb2016-05-02 18:02:37 -070044 def RebootAndVerify(self, _inst, *_args, **_kwargs):
45 """Mock out RebootAndVerify."""
David Pursellf1d16a62015-03-25 13:31:04 -070046
47
David Pursellf1d16a62015-03-25 13:31:04 -070048class RemoteDeviceUpdaterTest(cros_test_lib.MockTempDirTestCase):
49 """Test the flow of flash.Flash() with RemoteDeviceUpdater."""
50
51 IMAGE = '/path/to/image'
52 DEVICE = commandline.Device(scheme=commandline.DEVICE_SCHEME_SSH,
53 hostname='1.1.1.1')
54
55 def setUp(self):
56 """Patches objects."""
57 self.updater_mock = self.StartPatcher(RemoteDeviceUpdaterMock())
58 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
59 return_value='xbuddy/local/latest')
60 self.PatchObject(dev_server_wrapper, 'DevServerWrapper')
61 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -070062 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
63 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -070064 self.PatchObject(dev_server_wrapper, 'GetUpdatePayloads')
65 self.PatchObject(remote_access, 'CHECK_INTERVAL', new=0)
66 self.PatchObject(remote_access, 'ChromiumOSDevice')
67
68 def testUpdateAll(self):
69 """Tests that update methods are called correctly."""
David Pursellf1d16a62015-03-25 13:31:04 -070070 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070071 flash.Flash(self.DEVICE, self.IMAGE)
72 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
73 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -070074
75 def testUpdateStateful(self):
76 """Tests that update methods are called correctly."""
David Pursellf1d16a62015-03-25 13:31:04 -070077 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070078 flash.Flash(self.DEVICE, self.IMAGE, rootfs_update=False)
79 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
80 self.assertFalse(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -070081
82 def testUpdateRootfs(self):
83 """Tests that update methods are called correctly."""
David Pursellf1d16a62015-03-25 13:31:04 -070084 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070085 flash.Flash(self.DEVICE, self.IMAGE, stateful_update=False)
86 self.assertFalse(self.updater_mock.patched['UpdateStateful'].called)
87 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -070088
89 def testMissingPayloads(self):
90 """Tests we raise FlashError when payloads are missing."""
91 with mock.patch('os.path.exists', return_value=False):
xixuane851dfb2016-05-02 18:02:37 -070092 self.assertRaises(auto_updater.ChromiumOSUpdateError, flash.Flash,
93 self.DEVICE, self.IMAGE)
David Pursellf1d16a62015-03-25 13:31:04 -070094
David Pursellf1d16a62015-03-25 13:31:04 -070095
96class USBImagerMock(partial_mock.PartialCmdMock):
97 """Mock out USBImager."""
98 TARGET = 'chromite.cli.flash.USBImager'
99 ATTRS = ('CopyImageToDevice', 'InstallImageToDevice',
100 'ChooseRemovableDevice', 'ListAllRemovableDevices',
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700101 'GetRemovableDeviceDescription')
David Pursellf1d16a62015-03-25 13:31:04 -0700102 VALID_IMAGE = True
103
104 def __init__(self):
105 partial_mock.PartialCmdMock.__init__(self)
106
107 def CopyImageToDevice(self, _inst, *_args, **_kwargs):
108 """Mock out CopyImageToDevice."""
109
110 def InstallImageToDevice(self, _inst, *_args, **_kwargs):
111 """Mock out InstallImageToDevice."""
112
113 def ChooseRemovableDevice(self, _inst, *_args, **_kwargs):
114 """Mock out ChooseRemovableDevice."""
115
116 def ListAllRemovableDevices(self, _inst, *_args, **_kwargs):
117 """Mock out ListAllRemovableDevices."""
118 return ['foo', 'taco', 'milk']
119
120 def GetRemovableDeviceDescription(self, _inst, *_args, **_kwargs):
121 """Mock out GetRemovableDeviceDescription."""
122
David Pursellf1d16a62015-03-25 13:31:04 -0700123
124class USBImagerTest(cros_test_lib.MockTempDirTestCase):
125 """Test the flow of flash.Flash() with USBImager."""
126 IMAGE = '/path/to/image'
127
128 def Device(self, path):
129 """Create a USB device for passing to flash.Flash()."""
130 return commandline.Device(scheme=commandline.DEVICE_SCHEME_USB,
131 path=path)
132
133 def setUp(self):
134 """Patches objects."""
135 self.usb_mock = USBImagerMock()
136 self.imager_mock = self.StartPatcher(self.usb_mock)
137 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
138 return_value='xbuddy/local/latest')
139 self.PatchObject(dev_server_wrapper, 'DevServerWrapper')
140 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700141 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
142 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -0700143 self.PatchObject(os.path, 'exists', return_value=True)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700144 self.isgpt_mock = self.PatchObject(flash, '_IsFilePathGPTDiskImage',
145 return_value=True)
David Pursellf1d16a62015-03-25 13:31:04 -0700146
147 def testLocalImagePathCopy(self):
148 """Tests that imaging methods are called correctly."""
149 with mock.patch('os.path.isfile', return_value=True):
150 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
151 self.assertTrue(self.imager_mock.patched['CopyImageToDevice'].called)
152
153 def testLocalImagePathInstall(self):
154 """Tests that imaging methods are called correctly."""
155 with mock.patch('os.path.isfile', return_value=True):
156 flash.Flash(self.Device('/dev/foo'), self.IMAGE, board='taco',
157 install=True)
158 self.assertTrue(self.imager_mock.patched['InstallImageToDevice'].called)
159
160 def testLocalBadImagePath(self):
161 """Tests that using an image not having the magic bytes has prompt."""
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700162 self.isgpt_mock.return_value = False
David Pursellf1d16a62015-03-25 13:31:04 -0700163 with mock.patch('os.path.isfile', return_value=True):
164 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
165 mock_prompt.return_value = False
166 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
167 self.assertTrue(mock_prompt.called)
168
169 def testNonLocalImagePath(self):
170 """Tests that we try to get the image path using xbuddy."""
171 with mock.patch.object(
172 dev_server_wrapper,
173 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700174 return_value=('translated/xbuddy/path',
175 'resolved/xbuddy/path')) as mock_xbuddy:
David Pursellf1d16a62015-03-25 13:31:04 -0700176 with mock.patch('os.path.isfile', return_value=False):
177 with mock.patch('os.path.isdir', return_value=False):
178 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
179 self.assertTrue(mock_xbuddy.called)
180
181 def testConfirmNonRemovableDevice(self):
182 """Tests that we ask user to confirm if the device is not removable."""
183 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
184 flash.Flash(self.Device('/dev/dummy'), self.IMAGE)
185 self.assertTrue(mock_prompt.called)
186
187 def testSkipPromptNonRemovableDevice(self):
188 """Tests that we skip the prompt for non-removable with --yes."""
189 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
190 flash.Flash(self.Device('/dev/dummy'), self.IMAGE, yes=True)
191 self.assertFalse(mock_prompt.called)
192
193 def testChooseRemovableDevice(self):
194 """Tests that we ask user to choose a device if none is given."""
195 flash.Flash(self.Device(''), self.IMAGE)
196 self.assertTrue(self.imager_mock.patched['ChooseRemovableDevice'].called)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700197
198
Ralph Nathan9b997232015-05-15 13:13:12 -0700199class UsbImagerOperationTest(cros_build_lib_unittest.RunCommandTestCase):
200 """Tests for flash.UsbImagerOperation."""
201 # pylint: disable=protected-access
202
203 def setUp(self):
204 self.PatchObject(flash.UsbImagerOperation, '__init__', return_value=None)
205
206 def testUsbImagerOperationCalled(self):
207 """Test that flash.UsbImagerOperation is called when log level <= NOTICE."""
208 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
209 'oflag=sync']
210 usb_imager = flash.USBImager('dummy_device', 'board', 'foo')
211 run_mock = self.PatchObject(flash.UsbImagerOperation, 'Run')
212 self.PatchObject(logging.Logger, 'getEffectiveLevel',
213 return_value=logging.NOTICE)
214 usb_imager.CopyImageToDevice('foo', 'bar')
215
216 # Check that flash.UsbImagerOperation.Run() is called correctly.
217 run_mock.assert_called_with(cros_build_lib.SudoRunCommand, expected_cmd,
218 debug_level=logging.NOTICE, update_period=0.5)
219
220 def testSudoRunCommandCalled(self):
221 """Test that SudoRunCommand is called when log level > NOTICE."""
222 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
223 'oflag=sync']
224 usb_imager = flash.USBImager('dummy_device', 'board', 'foo')
225 run_mock = self.PatchObject(cros_build_lib, 'SudoRunCommand')
226 self.PatchObject(logging.Logger, 'getEffectiveLevel',
227 return_value=logging.WARNING)
228 usb_imager.CopyImageToDevice('foo', 'bar')
229
230 # Check that SudoRunCommand() is called correctly.
231 run_mock.assert_any_call(expected_cmd, debug_level=logging.NOTICE,
232 print_cmd=False)
233
234 def testPingDD(self):
235 """Test that UsbImagerOperation._PingDD() sends the correct signal."""
236 expected_cmd = ['kill', '-USR1', '5']
237 run_mock = self.PatchObject(cros_build_lib, 'SudoRunCommand')
238 op = flash.UsbImagerOperation('foo')
239 op._PingDD(5)
240
241 # Check that SudoRunCommand was called correctly.
242 run_mock.assert_called_with(expected_cmd, print_cmd=False)
243
244 def testGetDDPidFound(self):
245 """Check that the expected pid is returned for _GetDDPid()."""
246 expected_pid = 5
247 op = flash.UsbImagerOperation('foo')
248 self.PatchObject(osutils, 'IsChildProcess', return_value=True)
249 self.rc.AddCmdResult(partial_mock.Ignore(),
250 output='%d\n10\n' % expected_pid)
251
252 pid = op._GetDDPid()
253
254 # Check that the correct pid was returned.
255 self.assertEqual(pid, expected_pid)
256
257 def testGetDDPidNotFound(self):
258 """Check that -1 is returned for _GetDDPid() if the pids aren't valid."""
259 expected_pid = -1
260 op = flash.UsbImagerOperation('foo')
261 self.PatchObject(osutils, 'IsChildProcess', return_value=False)
262 self.rc.AddCmdResult(partial_mock.Ignore(), output='5\n10\n')
263
264 pid = op._GetDDPid()
265
266 # Check that the correct pid was returned.
267 self.assertEqual(pid, expected_pid)
268
269
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700270class FlashUtilTest(cros_test_lib.MockTempDirTestCase):
271 """Tests the helpers from cli.flash."""
272
273 def testChooseImage(self):
274 """Tests that we can detect a GPT image."""
275 # pylint: disable=protected-access
276
277 with self.PatchObject(flash, '_IsFilePathGPTDiskImage', return_value=True):
278 # No images defined. Choosing the image should raise an error.
279 with self.assertRaises(ValueError):
280 flash._ChooseImageFromDirectory(self.tempdir)
281
282 file_a = os.path.join(self.tempdir, 'a')
283 osutils.Touch(file_a)
284 # Only one image available, it should be selected automatically.
285 self.assertEqual(file_a, flash._ChooseImageFromDirectory(self.tempdir))
286
287 osutils.Touch(os.path.join(self.tempdir, 'b'))
288 file_c = os.path.join(self.tempdir, 'c')
289 osutils.Touch(file_c)
290 osutils.Touch(os.path.join(self.tempdir, 'd'))
291
292 # Multiple images available, we should ask the user to select the right
293 # image.
294 with self.PatchObject(cros_build_lib, 'GetChoice', return_value=2):
295 self.assertEqual(file_c, flash._ChooseImageFromDirectory(self.tempdir))
Mike Frysinger32759e42016-12-21 18:40:16 -0500296
297 def testIsFilePathGPTDiskImage(self):
298 """Tests the GPT image probing."""
299 # pylint: disable=protected-access
300
301 INVALID_PMBR = ' ' * 0x200
302 INVALID_GPT = ' ' * 0x200
303 VALID_PMBR = (' ' * 0x1fe) + '\x55\xaa'
304 VALID_GPT = 'EFI PART' + (' ' * 0x1f8)
305 TESTCASES = (
306 (False, False, INVALID_PMBR + INVALID_GPT),
307 (False, False, VALID_PMBR + INVALID_GPT),
308 (False, True, INVALID_PMBR + VALID_GPT),
309 (True, True, VALID_PMBR + VALID_GPT),
310 )
311
312 img = os.path.join(self.tempdir, 'img.bin')
313 for exp_pmbr_t, exp_pmbr_f, data in TESTCASES:
314 osutils.WriteFile(img, data)
315 self.assertEqual(
316 flash._IsFilePathGPTDiskImage(img, require_pmbr=True), exp_pmbr_t)
317 self.assertEqual(
318 flash._IsFilePathGPTDiskImage(img, require_pmbr=False), exp_pmbr_f)