blob: e694f2038f5bbe21cfb2a2bcbdebe58df659c23c [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
David Pursellf1d16a62015-03-25 13:31:04 -070010import os
Mike Frysinger3f087aa2020-03-20 06:03:16 -040011import sys
David Pursellf1d16a62015-03-25 13:31:04 -070012
Mike Frysinger6db648e2018-07-24 19:57:58 -040013import mock
14
David Pursellf1d16a62015-03-25 13:31:04 -070015from chromite.cli import flash
Sanika Kulkarnia8c4e3a2019-09-20 16:47:25 -070016from chromite.lib import auto_updater_transfer
David Pursellf1d16a62015-03-25 13:31:04 -070017from chromite.lib import commandline
18from chromite.lib import cros_build_lib
Ralph Nathan9b997232015-05-15 13:13:12 -070019from chromite.lib import cros_logging as logging
David Pursellf1d16a62015-03-25 13:31:04 -070020from chromite.lib import cros_test_lib
21from chromite.lib import dev_server_wrapper
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070022from chromite.lib import osutils
David Pursellf1d16a62015-03-25 13:31:04 -070023from chromite.lib import partial_mock
24from chromite.lib import remote_access
Mike Frysingerdda695b2019-11-23 20:58:59 -050025from chromite.lib import remote_access_unittest
David Pursellf1d16a62015-03-25 13:31:04 -070026
Amin Hassanic0f06fa2019-01-28 15:24:47 -080027from chromite.lib.paygen import paygen_payload_lib
28from chromite.lib.paygen import paygen_stateful_payload_lib
29
David Pursellf1d16a62015-03-25 13:31:04 -070030
Mike Frysinger3f087aa2020-03-20 06:03:16 -040031assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
32
33
David Pursellf1d16a62015-03-25 13:31:04 -070034class RemoteDeviceUpdaterMock(partial_mock.PartialCmdMock):
35 """Mock out RemoteDeviceUpdater."""
Amin Hassani9800d432019-07-24 14:23:39 -070036 TARGET = 'chromite.lib.auto_updater.ChromiumOSUpdater'
xixuane851dfb2016-05-02 18:02:37 -070037 ATTRS = ('UpdateStateful', 'UpdateRootfs', 'SetupRootfsUpdate',
Amin Hassani3e87ce12020-10-22 10:39:36 -070038 'RebootAndVerify')
David Pursellf1d16a62015-03-25 13:31:04 -070039
40 def __init__(self):
41 partial_mock.PartialCmdMock.__init__(self)
42
43 def UpdateStateful(self, _inst, *_args, **_kwargs):
44 """Mock out UpdateStateful."""
45
46 def UpdateRootfs(self, _inst, *_args, **_kwargs):
47 """Mock out UpdateRootfs."""
48
49 def SetupRootfsUpdate(self, _inst, *_args, **_kwargs):
50 """Mock out SetupRootfsUpdate."""
51
xixuane851dfb2016-05-02 18:02:37 -070052 def RebootAndVerify(self, _inst, *_args, **_kwargs):
53 """Mock out RebootAndVerify."""
David Pursellf1d16a62015-03-25 13:31:04 -070054
Sanika Kulkarnie3b177b2019-11-26 14:42:48 -080055
Mike Frysingerdda695b2019-11-23 20:58:59 -050056class RemoteAccessMock(remote_access_unittest.RemoteShMock):
57 """Mock out RemoteAccess."""
58
59 ATTRS = ('RemoteSh', 'Rsync', 'Scp')
60
61 def Rsync(self, *_args, **_kwargs):
62 return cros_build_lib.CommandResult(returncode=0)
63
64 def Scp(self, *_args, **_kwargs):
65 return cros_build_lib.CommandResult(returncode=0)
66
67
David Pursellf1d16a62015-03-25 13:31:04 -070068class RemoteDeviceUpdaterTest(cros_test_lib.MockTempDirTestCase):
69 """Test the flow of flash.Flash() with RemoteDeviceUpdater."""
70
71 IMAGE = '/path/to/image'
72 DEVICE = commandline.Device(scheme=commandline.DEVICE_SCHEME_SSH,
Mike Frysingerb5a297f2019-11-23 21:17:41 -050073 hostname=remote_access.TEST_IP)
David Pursellf1d16a62015-03-25 13:31:04 -070074
75 def setUp(self):
76 """Patches objects."""
77 self.updater_mock = self.StartPatcher(RemoteDeviceUpdaterMock())
78 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
79 return_value='xbuddy/local/latest')
David Pursellf1d16a62015-03-25 13:31:04 -070080 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -070081 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
82 'remote/taco-paladin/R36/test'))
Amin Hassanic0f06fa2019-01-28 15:24:47 -080083 self.PatchObject(paygen_payload_lib, 'GenerateUpdatePayload')
84 self.PatchObject(paygen_stateful_payload_lib, 'GenerateStatefulPayload')
David Pursellf1d16a62015-03-25 13:31:04 -070085 self.PatchObject(remote_access, 'CHECK_INTERVAL', new=0)
Mike Frysingerdda695b2019-11-23 20:58:59 -050086 self.PatchObject(remote_access.ChromiumOSDevice, 'Pingable',
87 return_value=True)
88 m = self.StartPatcher(RemoteAccessMock())
89 m.AddCmdResult(['cat', '/etc/lsb-release'],
90 stdout='CHROMEOS_RELEASE_BOARD=board')
91 m.SetDefaultCmdResult()
David Pursellf1d16a62015-03-25 13:31:04 -070092
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -080093 def _ExistsMock(self, path, ret=True):
94 """Mock function for os.path.exists.
95
96 os.path.exists is used a lot; we only want to mock it for devserver/static,
97 and actually check if the file exists in all other cases (using os.access).
98
99 Args:
100 path: path to check.
101 ret: return value of mock.
102
103 Returns:
104 ret for paths under devserver/static, and the expected value of
105 os.path.exists otherwise.
106 """
Achuith Bhandarkareda9b222020-05-02 10:36:16 +0000107 if path.startswith(dev_server_wrapper.DEFAULT_STATIC_DIR):
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800108 return ret
109 return os.access(path, os.F_OK)
110
David Pursellf1d16a62015-03-25 13:31:04 -0700111 def testUpdateAll(self):
112 """Tests that update methods are called correctly."""
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800113 with mock.patch('os.path.exists', side_effect=self._ExistsMock):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -0700114 flash.Flash(self.DEVICE, self.IMAGE)
115 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
116 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -0700117
118 def testUpdateStateful(self):
119 """Tests that update methods are called correctly."""
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800120 with mock.patch('os.path.exists', side_effect=self._ExistsMock):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -0700121 flash.Flash(self.DEVICE, self.IMAGE, rootfs_update=False)
122 self.assertTrue(self.updater_mock.patched['UpdateStateful'].called)
123 self.assertFalse(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -0700124
125 def testUpdateRootfs(self):
126 """Tests that update methods are called correctly."""
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800127 with mock.patch('os.path.exists', side_effect=self._ExistsMock):
Bertrand SIMONNETb34a98b2015-04-22 14:30:04 -0700128 flash.Flash(self.DEVICE, self.IMAGE, stateful_update=False)
129 self.assertFalse(self.updater_mock.patched['UpdateStateful'].called)
130 self.assertTrue(self.updater_mock.patched['UpdateRootfs'].called)
David Pursellf1d16a62015-03-25 13:31:04 -0700131
132 def testMissingPayloads(self):
133 """Tests we raise FlashError when payloads are missing."""
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800134 with mock.patch('os.path.exists',
135 side_effect=lambda p: self._ExistsMock(p, ret=False)):
Sanika Kulkarnia8c4e3a2019-09-20 16:47:25 -0700136 self.assertRaises(auto_updater_transfer.ChromiumOSTransferError,
137 flash.Flash, self.DEVICE, self.IMAGE)
David Pursellf1d16a62015-03-25 13:31:04 -0700138
Achuith Bhandarkar4d4275f2019-10-01 17:07:23 +0200139 def testFullPayload(self):
140 """Tests that we download full_payload and stateful using xBuddy."""
141 with mock.patch.object(
142 dev_server_wrapper,
143 'GetImagePathWithXbuddy',
144 return_value=('translated/xbuddy/path',
145 'resolved/xbuddy/path')) as mock_xbuddy:
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800146 with mock.patch('os.path.exists', side_effect=self._ExistsMock):
Achuith Bhandarkar4d4275f2019-10-01 17:07:23 +0200147 flash.Flash(self.DEVICE, self.IMAGE)
148
149 # Call to download full_payload and stateful. No other calls.
150 mock_xbuddy.assert_has_calls(
Achuith Bhandarkareda9b222020-05-02 10:36:16 +0000151 [mock.call('/path/to/image/full_payload', 'board', None, silent=True),
152 mock.call('/path/to/image/stateful', 'board', None, silent=True)])
Achuith Bhandarkar4d4275f2019-10-01 17:07:23 +0200153 self.assertEqual(mock_xbuddy.call_count, 2)
154
155 def testTestImage(self):
156 """Tests that we download the test image when the full payload fails."""
157 with mock.patch.object(
158 dev_server_wrapper,
159 'GetImagePathWithXbuddy',
160 side_effect=(dev_server_wrapper.ImagePathError,
161 ('translated/xbuddy/path',
162 'resolved/xbuddy/path'))) as mock_xbuddy:
Achuith Bhandarkar1f54a212019-12-09 12:08:35 -0800163 with mock.patch('os.path.exists', side_effect=self._ExistsMock):
Achuith Bhandarkar4d4275f2019-10-01 17:07:23 +0200164 flash.Flash(self.DEVICE, self.IMAGE)
165
166 # Call to download full_payload and image. No other calls.
167 mock_xbuddy.assert_has_calls(
Achuith Bhandarkareda9b222020-05-02 10:36:16 +0000168 [mock.call('/path/to/image/full_payload', 'board', None, silent=True),
169 mock.call('/path/to/image', 'board', None)])
Achuith Bhandarkar4d4275f2019-10-01 17:07:23 +0200170 self.assertEqual(mock_xbuddy.call_count, 2)
171
David Pursellf1d16a62015-03-25 13:31:04 -0700172
173class USBImagerMock(partial_mock.PartialCmdMock):
174 """Mock out USBImager."""
175 TARGET = 'chromite.cli.flash.USBImager'
176 ATTRS = ('CopyImageToDevice', 'InstallImageToDevice',
177 'ChooseRemovableDevice', 'ListAllRemovableDevices',
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700178 'GetRemovableDeviceDescription')
David Pursellf1d16a62015-03-25 13:31:04 -0700179 VALID_IMAGE = True
180
181 def __init__(self):
182 partial_mock.PartialCmdMock.__init__(self)
183
184 def CopyImageToDevice(self, _inst, *_args, **_kwargs):
185 """Mock out CopyImageToDevice."""
186
187 def InstallImageToDevice(self, _inst, *_args, **_kwargs):
188 """Mock out InstallImageToDevice."""
189
190 def ChooseRemovableDevice(self, _inst, *_args, **_kwargs):
191 """Mock out ChooseRemovableDevice."""
192
193 def ListAllRemovableDevices(self, _inst, *_args, **_kwargs):
194 """Mock out ListAllRemovableDevices."""
195 return ['foo', 'taco', 'milk']
196
197 def GetRemovableDeviceDescription(self, _inst, *_args, **_kwargs):
198 """Mock out GetRemovableDeviceDescription."""
199
David Pursellf1d16a62015-03-25 13:31:04 -0700200
201class USBImagerTest(cros_test_lib.MockTempDirTestCase):
202 """Test the flow of flash.Flash() with USBImager."""
203 IMAGE = '/path/to/image'
204
205 def Device(self, path):
206 """Create a USB device for passing to flash.Flash()."""
207 return commandline.Device(scheme=commandline.DEVICE_SCHEME_USB,
208 path=path)
209
210 def setUp(self):
211 """Patches objects."""
212 self.usb_mock = USBImagerMock()
213 self.imager_mock = self.StartPatcher(self.usb_mock)
214 self.PatchObject(dev_server_wrapper, 'GenerateXbuddyRequest',
215 return_value='xbuddy/local/latest')
David Pursellf1d16a62015-03-25 13:31:04 -0700216 self.PatchObject(dev_server_wrapper, 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700217 return_value=('taco-paladin/R36/chromiumos_test_image.bin',
218 'remote/taco-paladin/R36/test'))
David Pursellf1d16a62015-03-25 13:31:04 -0700219 self.PatchObject(os.path, 'exists', return_value=True)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700220 self.isgpt_mock = self.PatchObject(flash, '_IsFilePathGPTDiskImage',
221 return_value=True)
David Pursellf1d16a62015-03-25 13:31:04 -0700222
223 def testLocalImagePathCopy(self):
224 """Tests that imaging methods are called correctly."""
225 with mock.patch('os.path.isfile', return_value=True):
226 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
227 self.assertTrue(self.imager_mock.patched['CopyImageToDevice'].called)
228
229 def testLocalImagePathInstall(self):
230 """Tests that imaging methods are called correctly."""
231 with mock.patch('os.path.isfile', return_value=True):
232 flash.Flash(self.Device('/dev/foo'), self.IMAGE, board='taco',
233 install=True)
234 self.assertTrue(self.imager_mock.patched['InstallImageToDevice'].called)
235
236 def testLocalBadImagePath(self):
237 """Tests that using an image not having the magic bytes has prompt."""
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700238 self.isgpt_mock.return_value = False
David Pursellf1d16a62015-03-25 13:31:04 -0700239 with mock.patch('os.path.isfile', return_value=True):
240 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
241 mock_prompt.return_value = False
242 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
243 self.assertTrue(mock_prompt.called)
244
245 def testNonLocalImagePath(self):
246 """Tests that we try to get the image path using xbuddy."""
247 with mock.patch.object(
248 dev_server_wrapper,
249 'GetImagePathWithXbuddy',
Gilad Arnolde62ec902015-04-24 14:41:02 -0700250 return_value=('translated/xbuddy/path',
251 'resolved/xbuddy/path')) as mock_xbuddy:
David Pursellf1d16a62015-03-25 13:31:04 -0700252 with mock.patch('os.path.isfile', return_value=False):
253 with mock.patch('os.path.isdir', return_value=False):
254 flash.Flash(self.Device('/dev/foo'), self.IMAGE)
255 self.assertTrue(mock_xbuddy.called)
256
257 def testConfirmNonRemovableDevice(self):
258 """Tests that we ask user to confirm if the device is not removable."""
259 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
260 flash.Flash(self.Device('/dev/dummy'), self.IMAGE)
261 self.assertTrue(mock_prompt.called)
262
263 def testSkipPromptNonRemovableDevice(self):
264 """Tests that we skip the prompt for non-removable with --yes."""
265 with mock.patch.object(cros_build_lib, 'BooleanPrompt') as mock_prompt:
266 flash.Flash(self.Device('/dev/dummy'), self.IMAGE, yes=True)
267 self.assertFalse(mock_prompt.called)
268
269 def testChooseRemovableDevice(self):
270 """Tests that we ask user to choose a device if none is given."""
271 flash.Flash(self.Device(''), self.IMAGE)
272 self.assertTrue(self.imager_mock.patched['ChooseRemovableDevice'].called)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700273
274
Benjamin Gordon121a2aa2018-05-04 16:24:45 -0600275class UsbImagerOperationTest(cros_test_lib.RunCommandTestCase):
Ralph Nathan9b997232015-05-15 13:13:12 -0700276 """Tests for flash.UsbImagerOperation."""
277 # pylint: disable=protected-access
278
279 def setUp(self):
280 self.PatchObject(flash.UsbImagerOperation, '__init__', return_value=None)
281
282 def testUsbImagerOperationCalled(self):
283 """Test that flash.UsbImagerOperation is called when log level <= NOTICE."""
284 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
Frank Huang8e626432019-06-24 19:51:08 +0800285 'oflag=direct', 'conv=fdatasync']
Achuith Bhandarkaree1336f2020-04-18 11:44:09 +0000286 usb_imager = flash.USBImager('dummy_device', 'board', 'foo', 'latest')
Ralph Nathan9b997232015-05-15 13:13:12 -0700287 run_mock = self.PatchObject(flash.UsbImagerOperation, 'Run')
288 self.PatchObject(logging.Logger, 'getEffectiveLevel',
289 return_value=logging.NOTICE)
290 usb_imager.CopyImageToDevice('foo', 'bar')
291
292 # Check that flash.UsbImagerOperation.Run() is called correctly.
Mike Frysinger45602c72019-09-22 02:15:11 -0400293 run_mock.assert_called_with(cros_build_lib.sudo_run, expected_cmd,
Mike Frysinger3d5de8f2019-10-23 00:48:39 -0400294 debug_level=logging.NOTICE, encoding='utf-8',
295 update_period=0.5)
Ralph Nathan9b997232015-05-15 13:13:12 -0700296
297 def testSudoRunCommandCalled(self):
Mike Frysinger45602c72019-09-22 02:15:11 -0400298 """Test that sudo_run is called when log level > NOTICE."""
Ralph Nathan9b997232015-05-15 13:13:12 -0700299 expected_cmd = ['dd', 'if=foo', 'of=bar', 'bs=4M', 'iflag=fullblock',
Frank Huang8e626432019-06-24 19:51:08 +0800300 'oflag=direct', 'conv=fdatasync']
Achuith Bhandarkaree1336f2020-04-18 11:44:09 +0000301 usb_imager = flash.USBImager('dummy_device', 'board', 'foo', 'latest')
Mike Frysinger45602c72019-09-22 02:15:11 -0400302 run_mock = self.PatchObject(cros_build_lib, 'sudo_run')
Ralph Nathan9b997232015-05-15 13:13:12 -0700303 self.PatchObject(logging.Logger, 'getEffectiveLevel',
304 return_value=logging.WARNING)
305 usb_imager.CopyImageToDevice('foo', 'bar')
306
Mike Frysinger45602c72019-09-22 02:15:11 -0400307 # Check that sudo_run() is called correctly.
Ralph Nathan9b997232015-05-15 13:13:12 -0700308 run_mock.assert_any_call(expected_cmd, debug_level=logging.NOTICE,
309 print_cmd=False)
310
311 def testPingDD(self):
312 """Test that UsbImagerOperation._PingDD() sends the correct signal."""
313 expected_cmd = ['kill', '-USR1', '5']
Mike Frysinger45602c72019-09-22 02:15:11 -0400314 run_mock = self.PatchObject(cros_build_lib, 'sudo_run')
Ralph Nathan9b997232015-05-15 13:13:12 -0700315 op = flash.UsbImagerOperation('foo')
316 op._PingDD(5)
317
Mike Frysinger45602c72019-09-22 02:15:11 -0400318 # Check that sudo_run was called correctly.
Ralph Nathan9b997232015-05-15 13:13:12 -0700319 run_mock.assert_called_with(expected_cmd, print_cmd=False)
320
321 def testGetDDPidFound(self):
322 """Check that the expected pid is returned for _GetDDPid()."""
323 expected_pid = 5
324 op = flash.UsbImagerOperation('foo')
325 self.PatchObject(osutils, 'IsChildProcess', return_value=True)
326 self.rc.AddCmdResult(partial_mock.Ignore(),
327 output='%d\n10\n' % expected_pid)
328
329 pid = op._GetDDPid()
330
331 # Check that the correct pid was returned.
332 self.assertEqual(pid, expected_pid)
333
334 def testGetDDPidNotFound(self):
335 """Check that -1 is returned for _GetDDPid() if the pids aren't valid."""
336 expected_pid = -1
337 op = flash.UsbImagerOperation('foo')
338 self.PatchObject(osutils, 'IsChildProcess', return_value=False)
339 self.rc.AddCmdResult(partial_mock.Ignore(), output='5\n10\n')
340
341 pid = op._GetDDPid()
342
343 # Check that the correct pid was returned.
344 self.assertEqual(pid, expected_pid)
345
346
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700347class FlashUtilTest(cros_test_lib.MockTempDirTestCase):
348 """Tests the helpers from cli.flash."""
349
350 def testChooseImage(self):
351 """Tests that we can detect a GPT image."""
352 # pylint: disable=protected-access
353
354 with self.PatchObject(flash, '_IsFilePathGPTDiskImage', return_value=True):
355 # No images defined. Choosing the image should raise an error.
356 with self.assertRaises(ValueError):
357 flash._ChooseImageFromDirectory(self.tempdir)
358
359 file_a = os.path.join(self.tempdir, 'a')
360 osutils.Touch(file_a)
361 # Only one image available, it should be selected automatically.
362 self.assertEqual(file_a, flash._ChooseImageFromDirectory(self.tempdir))
363
364 osutils.Touch(os.path.join(self.tempdir, 'b'))
365 file_c = os.path.join(self.tempdir, 'c')
366 osutils.Touch(file_c)
367 osutils.Touch(os.path.join(self.tempdir, 'd'))
368
369 # Multiple images available, we should ask the user to select the right
370 # image.
371 with self.PatchObject(cros_build_lib, 'GetChoice', return_value=2):
372 self.assertEqual(file_c, flash._ChooseImageFromDirectory(self.tempdir))
Mike Frysinger32759e42016-12-21 18:40:16 -0500373
374 def testIsFilePathGPTDiskImage(self):
375 """Tests the GPT image probing."""
376 # pylint: disable=protected-access
377
Mike Frysinger3d5de8f2019-10-23 00:48:39 -0400378 INVALID_PMBR = b' ' * 0x200
379 INVALID_GPT = b' ' * 0x200
380 VALID_PMBR = (b' ' * 0x1fe) + b'\x55\xaa'
381 VALID_GPT = b'EFI PART' + (b' ' * 0x1f8)
Mike Frysinger32759e42016-12-21 18:40:16 -0500382 TESTCASES = (
383 (False, False, INVALID_PMBR + INVALID_GPT),
384 (False, False, VALID_PMBR + INVALID_GPT),
385 (False, True, INVALID_PMBR + VALID_GPT),
386 (True, True, VALID_PMBR + VALID_GPT),
387 )
388
389 img = os.path.join(self.tempdir, 'img.bin')
390 for exp_pmbr_t, exp_pmbr_f, data in TESTCASES:
Mike Frysinger3d5de8f2019-10-23 00:48:39 -0400391 osutils.WriteFile(img, data, mode='wb')
Mike Frysinger32759e42016-12-21 18:40:16 -0500392 self.assertEqual(
393 flash._IsFilePathGPTDiskImage(img, require_pmbr=True), exp_pmbr_t)
394 self.assertEqual(
395 flash._IsFilePathGPTDiskImage(img, require_pmbr=False), exp_pmbr_f)