blob: a92f6f13258a033f196f4de13e54059ae01878ec [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
7from __future__ import print_function
8
9import mock
10import os
11
12from chromite.cli import flash
13from chromite.lib import brick_lib
14from chromite.lib import commandline
15from chromite.lib import cros_build_lib
Ralph Nathan9b997232015-05-15 13:13:12 -070016from chromite.lib import cros_build_lib_unittest
17from 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
Gilad Arnold8ecd42a2015-04-27 11:47:05 -070023from chromite.lib import workspace_lib
David Pursellf1d16a62015-03-25 13:31:04 -070024
25
26class RemoteDeviceUpdaterMock(partial_mock.PartialCmdMock):
27 """Mock out RemoteDeviceUpdater."""
28 TARGET = 'chromite.cli.flash.RemoteDeviceUpdater'
29 ATTRS = ('UpdateStateful', 'UpdateRootfs', 'SetupRootfsUpdate', 'Verify')
30
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
43 def Verify(self, _inst, *_args, **_kwargs):
44 """Mock out SetupRootfsUpdate."""
45
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')
Gilad Arnold8ecd42a2015-04-27 11:47:05 -070066 self.PatchObject(workspace_lib, 'WorkspacePath', return_value=None)
David Pursellf1d16a62015-03-25 13:31:04 -070067
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):
92 self.assertRaises(flash.FlashError, flash.Flash, self.DEVICE, self.IMAGE)
93
94 def testProjectSdk(self):
95 """Tests that Project SDK flashing invoked as expected."""
David Pursellf1d16a62015-03-25 13:31:04 -070096 with mock.patch('os.path.exists', return_value=True):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -070097 with mock.patch('chromite.lib.project_sdk.FindVersion',
98 return_value='1.2.3'):
99 flash.Flash(self.DEVICE, self.IMAGE, project_sdk_image=True)
100 dev_server_wrapper.GetImagePathWithXbuddy.assert_called_with(
101 'project_sdk', mock.ANY, version='1.2.3', static_dir=mock.ANY,
102 lookup_only=True)
103 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
104 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -0700105
106
107class USBImagerMock(partial_mock.PartialCmdMock):
108 """Mock out USBImager."""
109 TARGET = 'chromite.cli.flash.USBImager'
110 ATTRS = ('CopyImageToDevice', 'InstallImageToDevice',
111 'ChooseRemovableDevice', 'ListAllRemovableDevices',
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700112 'GetRemovableDeviceDescription')
David Pursellf1d16a62015-03-25 13:31:04 -0700113 VALID_IMAGE = True
114
115 def __init__(self):
116 partial_mock.PartialCmdMock.__init__(self)
117
118 def CopyImageToDevice(self, _inst, *_args, **_kwargs):
119 """Mock out CopyImageToDevice."""
120
121 def InstallImageToDevice(self, _inst, *_args, **_kwargs):
122 """Mock out InstallImageToDevice."""
123
124 def ChooseRemovableDevice(self, _inst, *_args, **_kwargs):
125 """Mock out ChooseRemovableDevice."""
126
127 def ListAllRemovableDevices(self, _inst, *_args, **_kwargs):
128 """Mock out ListAllRemovableDevices."""
129 return ['foo', 'taco', 'milk']
130
131 def GetRemovableDeviceDescription(self, _inst, *_args, **_kwargs):
132 """Mock out GetRemovableDeviceDescription."""
133
David Pursellf1d16a62015-03-25 13:31:04 -0700134
135class USBImagerTest(cros_test_lib.MockTempDirTestCase):
136 """Test the flow of flash.Flash() with USBImager."""
137 IMAGE = '/path/to/image'
138
139 def Device(self, path):
140 """Create a USB device for passing to flash.Flash()."""
141 return commandline.Device(scheme=commandline.DEVICE_SCHEME_USB,
142 path=path)
143
144 def setUp(self):
145 """Patches objects."""
146 self.usb_mock = USBImagerMock()
147 self.imager_mock = self.StartPatcher(self.usb_mock)
148 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
149 return_value='xbuddy/local/latest')
150 self.PatchObject(dev_server_wrapper, 'DevServerWrapper')
151 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700152 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
153 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -0700154 self.PatchObject(os.path, 'exists', return_value=True)
155 self.PatchObject(brick_lib, 'FindBrickInPath', return_value=None)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700156 self.isgpt_mock = self.PatchObject(flash, '_IsFilePathGPTDiskImage',
157 return_value=True)
Gilad Arnold8ecd42a2015-04-27 11:47:05 -0700158 self.PatchObject(workspace_lib, 'WorkspacePath', return_value=None)
David Pursellf1d16a62015-03-25 13:31:04 -0700159
160 def testLocalImagePathCopy(self):
161 """Tests that imaging methods are called correctly."""
162 with mock.patch('os.path.isfile', return_value=True):
163 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
164 self.assertTrue(self.imager_mock.patched['CopyImageToDevice'].called)
165
166 def testLocalImagePathInstall(self):
167 """Tests that imaging methods are called correctly."""
168 with mock.patch('os.path.isfile', return_value=True):
169 flash.Flash(self.Device('/dev/foo'), self.IMAGE, board='taco',
170 install=True)
171 self.assertTrue(self.imager_mock.patched['InstallImageToDevice'].called)
172
173 def testLocalBadImagePath(self):
174 """Tests that using an image not having the magic bytes has prompt."""
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700175 self.isgpt_mock.return_value = False
David Pursellf1d16a62015-03-25 13:31:04 -0700176 with mock.patch('os.path.isfile', return_value=True):
177 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
178 mock_prompt.return_value = False
179 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
180 self.assertTrue(mock_prompt.called)
181
182 def testNonLocalImagePath(self):
183 """Tests that we try to get the image path using xbuddy."""
184 with mock.patch.object(
185 dev_server_wrapper,
186 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700187 return_value=('translated/xbuddy/path',
188 'resolved/xbuddy/path')) as mock_xbuddy:
David Pursellf1d16a62015-03-25 13:31:04 -0700189 with mock.patch('os.path.isfile', return_value=False):
190 with mock.patch('os.path.isdir', return_value=False):
191 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
192 self.assertTrue(mock_xbuddy.called)
193
194 def testConfirmNonRemovableDevice(self):
195 """Tests that we ask user to confirm if the device is not removable."""
196 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
197 flash.Flash(self.Device('/dev/dummy'), self.IMAGE)
198 self.assertTrue(mock_prompt.called)
199
200 def testSkipPromptNonRemovableDevice(self):
201 """Tests that we skip the prompt for non-removable with --yes."""
202 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
203 flash.Flash(self.Device('/dev/dummy'), self.IMAGE, yes=True)
204 self.assertFalse(mock_prompt.called)
205
206 def testChooseRemovableDevice(self):
207 """Tests that we ask user to choose a device if none is given."""
208 flash.Flash(self.Device(''), self.IMAGE)
209 self.assertTrue(self.imager_mock.patched['ChooseRemovableDevice'].called)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700210
211
Ralph Nathan9b997232015-05-15 13:13:12 -0700212class UsbImagerOperationTest(cros_build_lib_unittest.RunCommandTestCase):
213 """Tests for flash.UsbImagerOperation."""
214 # pylint: disable=protected-access
215
216 def setUp(self):
217 self.PatchObject(flash.UsbImagerOperation, '__init__', return_value=None)
218
219 def testUsbImagerOperationCalled(self):
220 """Test that flash.UsbImagerOperation 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(flash.UsbImagerOperation, 'Run')
225 self.PatchObject(logging.Logger, 'getEffectiveLevel',
226 return_value=logging.NOTICE)
227 usb_imager.CopyImageToDevice('foo', 'bar')
228
229 # Check that flash.UsbImagerOperation.Run() is called correctly.
230 run_mock.assert_called_with(cros_build_lib.SudoRunCommand, expected_cmd,
231 debug_level=logging.NOTICE, update_period=0.5)
232
233 def testSudoRunCommandCalled(self):
234 """Test that SudoRunCommand is called when log level > NOTICE."""
235 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
236 'oflag=sync']
237 usb_imager = flash.USBImager('dummy_device', 'board', 'foo')
238 run_mock = self.PatchObject(cros_build_lib, 'SudoRunCommand')
239 self.PatchObject(logging.Logger, 'getEffectiveLevel',
240 return_value=logging.WARNING)
241 usb_imager.CopyImageToDevice('foo', 'bar')
242
243 # Check that SudoRunCommand() is called correctly.
244 run_mock.assert_any_call(expected_cmd, debug_level=logging.NOTICE,
245 print_cmd=False)
246
247 def testPingDD(self):
248 """Test that UsbImagerOperation._PingDD() sends the correct signal."""
249 expected_cmd = ['kill', '-USR1', '5']
250 run_mock = self.PatchObject(cros_build_lib, 'SudoRunCommand')
251 op = flash.UsbImagerOperation('foo')
252 op._PingDD(5)
253
254 # Check that SudoRunCommand was called correctly.
255 run_mock.assert_called_with(expected_cmd, print_cmd=False)
256
257 def testGetDDPidFound(self):
258 """Check that the expected pid is returned for _GetDDPid()."""
259 expected_pid = 5
260 op = flash.UsbImagerOperation('foo')
261 self.PatchObject(osutils, 'IsChildProcess', return_value=True)
262 self.rc.AddCmdResult(partial_mock.Ignore(),
263 output='%d\n10\n' % expected_pid)
264
265 pid = op._GetDDPid()
266
267 # Check that the correct pid was returned.
268 self.assertEqual(pid, expected_pid)
269
270 def testGetDDPidNotFound(self):
271 """Check that -1 is returned for _GetDDPid() if the pids aren't valid."""
272 expected_pid = -1
273 op = flash.UsbImagerOperation('foo')
274 self.PatchObject(osutils, 'IsChildProcess', return_value=False)
275 self.rc.AddCmdResult(partial_mock.Ignore(), output='5\n10\n')
276
277 pid = op._GetDDPid()
278
279 # Check that the correct pid was returned.
280 self.assertEqual(pid, expected_pid)
281
282
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700283class FlashUtilTest(cros_test_lib.MockTempDirTestCase):
284 """Tests the helpers from cli.flash."""
285
286 def testChooseImage(self):
287 """Tests that we can detect a GPT image."""
288 # pylint: disable=protected-access
289
290 with self.PatchObject(flash, '_IsFilePathGPTDiskImage', return_value=True):
291 # No images defined. Choosing the image should raise an error.
292 with self.assertRaises(ValueError):
293 flash._ChooseImageFromDirectory(self.tempdir)
294
295 file_a = os.path.join(self.tempdir, 'a')
296 osutils.Touch(file_a)
297 # Only one image available, it should be selected automatically.
298 self.assertEqual(file_a, flash._ChooseImageFromDirectory(self.tempdir))
299
300 osutils.Touch(os.path.join(self.tempdir, 'b'))
301 file_c = os.path.join(self.tempdir, 'c')
302 osutils.Touch(file_c)
303 osutils.Touch(os.path.join(self.tempdir, 'd'))
304
305 # Multiple images available, we should ask the user to select the right
306 # image.
307 with self.PatchObject(cros_build_lib, 'GetChoice', return_value=2):
308 self.assertEqual(file_c, flash._ChooseImageFromDirectory(self.tempdir))