blob: c5606b4c94ba62ae22cec21f361815d810de001c [file] [log] [blame]
Amin Hassani92f6c4a2021-02-20 17:36:09 -08001# Copyright 2021 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 device_imager module."""
6
Amin Hassanid4b3ff82021-02-20 23:05:14 -08007import os
Amin Hassani92f6c4a2021-02-20 17:36:09 -08008import tempfile
Amin Hassani55970562021-02-22 20:49:13 -08009import time
Mike Frysinger166fea02021-02-12 05:30:33 -050010from unittest import mock
Amin Hassani92f6c4a2021-02-20 17:36:09 -080011
Amin Hassani92f6c4a2021-02-20 17:36:09 -080012from chromite.cli import device_imager
Amin Hassanid4b3ff82021-02-20 23:05:14 -080013from chromite.lib import constants
14from chromite.lib import cros_build_lib
Amin Hassani92f6c4a2021-02-20 17:36:09 -080015from chromite.lib import cros_test_lib
Amin Hassani0fe49ae2021-02-21 23:41:58 -080016from chromite.lib import gs
Amin Hassanid4b3ff82021-02-20 23:05:14 -080017from chromite.lib import image_lib
18from chromite.lib import image_lib_unittest
Amin Hassani92f6c4a2021-02-20 17:36:09 -080019from chromite.lib import partial_mock
20from chromite.lib import remote_access
21from chromite.lib import remote_access_unittest
Amin Hassani74403082021-02-22 11:40:09 -080022from chromite.lib import stateful_updater
23from chromite.lib.paygen import paygen_stateful_payload_lib
Amin Hassani92f6c4a2021-02-20 17:36:09 -080024from chromite.lib.xbuddy import xbuddy
25
26
Amin Hassani92f6c4a2021-02-20 17:36:09 -080027# pylint: disable=protected-access
28
Amin Hassanid4b3ff82021-02-20 23:05:14 -080029
30def GetFdPath(fd):
31 """Returns the fd path for the current process."""
32 return f'/proc/self/fd/{fd}'
33
34
Amin Hassani92f6c4a2021-02-20 17:36:09 -080035class DeviceImagerTest(cros_test_lib.MockTestCase):
36 """Tests DeviceImager class methods."""
37
38 def setUp(self):
39 """Sets up the class by creating proper mocks."""
40 self.rsh_mock = self.StartPatcher(remote_access_unittest.RemoteShMock())
41 self.rsh_mock.AddCmdResult(partial_mock.In('${PATH}'), stdout='')
42 self.path_env = 'PATH=%s:' % remote_access.DEV_BIN_PATHS
43
Amin Hassanib1993eb2021-04-28 12:00:11 -070044 def test_LocateImageLocalFile(self):
Amin Hassani92f6c4a2021-02-20 17:36:09 -080045 """Tests getting the path to local image."""
46 with tempfile.NamedTemporaryFile() as fp:
47 di = device_imager.DeviceImager(None, fp.name)
Amin Hassanib1993eb2021-04-28 12:00:11 -070048 di._LocateImage()
49 self.assertEqual(di._image, fp.name)
50 self.assertEqual(di._image_type, device_imager.ImageType.FULL)
Amin Hassani92f6c4a2021-02-20 17:36:09 -080051
Amin Hassanib1993eb2021-04-28 12:00:11 -070052 def test_LocateImageDir(self):
Amin Hassani92f6c4a2021-02-20 17:36:09 -080053 """Tests failing on a given directory as a path."""
54 di = device_imager.DeviceImager(None, '/tmp')
55 with self.assertRaises(ValueError):
Amin Hassanib1993eb2021-04-28 12:00:11 -070056 di._LocateImage()
Amin Hassani92f6c4a2021-02-20 17:36:09 -080057
58 @mock.patch.object(xbuddy.XBuddy, 'Translate', return_value=('eve/R90', None))
Amin Hassani70c372a2021-03-31 20:18:51 -070059 @mock.patch.object(remote_access.ChromiumOSDevice, 'board',
60 return_value='foo', new_callable=mock.PropertyMock)
61 # pylint: disable=unused-argument
Amin Hassanib1993eb2021-04-28 12:00:11 -070062 def test_LocateImageXBuddyRemote(self, _, board_mock):
Amin Hassani92f6c4a2021-02-20 17:36:09 -080063 """Tests getting remote xBuddy image path."""
Amin Hassani70c372a2021-03-31 20:18:51 -070064 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
65 di = device_imager.DeviceImager(device, 'xbuddy://remote/eve/latest')
Amin Hassanib1993eb2021-04-28 12:00:11 -070066 di._LocateImage()
67 self.assertEqual(di._image, 'gs://chromeos-image-archive/eve/R90')
68 self.assertEqual(di._image_type, device_imager.ImageType.REMOTE_DIRECTORY)
Amin Hassani92f6c4a2021-02-20 17:36:09 -080069
70 @mock.patch.object(xbuddy.XBuddy, 'Translate',
71 return_value=('eve/R90', 'path/to/file'))
Amin Hassani70c372a2021-03-31 20:18:51 -070072 @mock.patch.object(remote_access.ChromiumOSDevice, 'board',
73 return_value='foo', new_callable=mock.PropertyMock)
74 # pylint: disable=unused-argument
Amin Hassanib1993eb2021-04-28 12:00:11 -070075 def test_LocateImageXBuddyLocal(self, _, board_mock):
Amin Hassani92f6c4a2021-02-20 17:36:09 -080076 """Tests getting local xBuddy image path."""
Amin Hassani70c372a2021-03-31 20:18:51 -070077 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
78 di = device_imager.DeviceImager(device, 'xbuddy://local/eve/latest')
Amin Hassanib1993eb2021-04-28 12:00:11 -070079 di._LocateImage()
80 self.assertEqual(di._image, 'path/to/file')
81 self.assertEqual(di._image_type, device_imager.ImageType.FULL)
Amin Hassani92f6c4a2021-02-20 17:36:09 -080082
83 def test_SplitDevPath(self):
84 """Tests splitting a device path into prefix and partition number."""
85
86 di = device_imager.DeviceImager(None, None)
87 self.assertEqual(di._SplitDevPath('/dev/foop3'), ('/dev/foop', 3))
88
89 with self.assertRaises(device_imager.Error):
90 di._SplitDevPath('/foo')
91
92 with self.assertRaises(device_imager.Error):
93 di._SplitDevPath('/foo/p3p')
94
95 def test_GetKernelState(self):
96 """Tests getting the current active and inactive kernel states."""
97 di = device_imager.DeviceImager(None, None)
98 self.assertEqual(di._GetKernelState(3), (device_imager.DeviceImager.A,
99 device_imager.DeviceImager.B))
100 self.assertEqual(di._GetKernelState(5), (device_imager.DeviceImager.B,
101 device_imager.DeviceImager.A))
102
103 with self.assertRaises(device_imager.Error):
104 di._GetKernelState(1)
105
106 @mock.patch.object(remote_access.ChromiumOSDevice, 'root_dev',
107 return_value='/dev/foop3', new_callable=mock.PropertyMock)
108 def test_VerifyBootExpectations(self, _):
109 """Tests verifying the boot expectations after reboot."""
110
111 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
112 di = device_imager.DeviceImager(device, None)
113 di._inactive_state = device_imager.DeviceImager.A
114 di._VerifyBootExpectations()
115
116 @mock.patch.object(remote_access.ChromiumOSDevice, 'root_dev',
117 return_value='/dev/foop3', new_callable=mock.PropertyMock)
118 def test_VerifyBootExpectationsFails(self, _):
119 """Tests failure of boot expectations."""
120
121 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
122 di = device_imager.DeviceImager(device, None)
123 di._inactive_state = device_imager.DeviceImager.B
124 with self.assertRaises(device_imager.Error):
125 di._VerifyBootExpectations()
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800126
127
128class TestReaderBase(cros_test_lib.MockTestCase):
129 """Test ReaderBase class"""
130
131 def testNamedPipe(self):
132 """Tests initializing the class with named pipe."""
133 with device_imager.ReaderBase(use_named_pipes=True) as r:
134 self.assertIsInstance(r.Target(), str)
135 self.assertEqual(r._Source(), r.Target())
136 self.assertExists(r.Target())
137
138 r._CloseSource() # Should not have any effect.
139 self.assertExists(r._Source())
140
141 # Closing target should delete the named pipe.
142 r.CloseTarget()
143 self.assertNotExists(r.Target())
144
145 def testFdPipe(self):
146 """Tests initializing the class with normal file descriptor pipes."""
147 with device_imager.ReaderBase() as r:
148 self.assertIsInstance(r.Target(), int)
149 self.assertIsInstance(r._Source(), int)
150 self.assertNotEqual(r._Source(), r.Target())
151 self.assertExists(GetFdPath(r.Target()))
152 self.assertExists(GetFdPath(r._Source()))
153
Amin Hassanifa11c692021-04-07 09:17:31 -0700154 # Per crbug.com/1196702 it seems like some other process gets the file
155 # descriptor right after we close it and by the time we check its
156 # existence, it is still there and this can flake. So it might be better
157 # to make sure this is checked properly through real paths and not
158 # symlinks.
159 path = GetFdPath(r._Source())
160 old_path = os.path.realpath(path)
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800161 r._CloseSource()
Amin Hassanifa11c692021-04-07 09:17:31 -0700162 with self.assertRaises(OSError):
163 new_path = os.path.realpath(path)
164 self.assertNotEqual(old_path, new_path)
165 raise OSError('Fake the context manager.')
166
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800167 self.assertExists(GetFdPath(r.Target()))
168
Amin Hassanifa11c692021-04-07 09:17:31 -0700169 path = GetFdPath(r.Target())
170 old_path = os.path.realpath(path)
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800171 r.CloseTarget()
Amin Hassanifa11c692021-04-07 09:17:31 -0700172 with self.assertRaises(OSError):
173 new_path = os.path.realpath(path)
174 self.assertNotEqual(old_path, new_path)
175 raise OSError('Fake the context manager.')
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800176
177 def testFdPipeCommunicate(self):
178 """Tests that file descriptors pipe can actually communicate."""
179 with device_imager.ReaderBase() as r:
180 with os.fdopen(r._Source(), 'w') as fp:
181 fp.write('helloworld')
182
183 with os.fdopen(r.Target(), 'r') as fp:
184 self.assertEqual(fp.read(), 'helloworld')
185
186
187class PartialFileReaderTest(cros_test_lib.RunCommandTestCase):
188 """Tests PartialFileReader class."""
189
190 def testRun(self):
191 """Tests the main run() function."""
192 with device_imager.PartialFileReader(
193 '/foo', 512 * 2, 512, cros_build_lib.COMP_GZIP) as pfr:
194 pass
195
196 self.assertCommandCalled(
197 'dd status=none if=/foo ibs=512 skip=2 count=1 | /usr/bin/pigz',
198 stdout=pfr._Source(), shell=True)
199
200 # Make sure the source has been close.
201 self.assertNotExists(GetFdPath(pfr._Source()))
202
203
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800204class GsFileCopierTest(cros_test_lib.TestCase):
205 """Tests GsFileCopier class."""
206
207 @mock.patch.object(gs.GSContext, 'Copy')
208 def testRun(self, copy_mock):
209 """Tests the run() function."""
210 image = 'gs://path/to/image'
211 with device_imager.GsFileCopier(image) as gfc:
212 self.assertTrue(gfc._use_named_pipes)
213
214 copy_mock.assert_called_with(image, gfc._Source())
215
216
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800217class PartitionUpdaterBaseTest(cros_test_lib.TestCase):
218 """Tests PartitionUpdaterBase class"""
219
220 def testRunNotImplemented(self):
221 """Tests running the main Run() function is not implemented."""
222 # We just want to make sure the _Run() function is not implemented here.
223 pub = device_imager.PartitionUpdaterBase(None, None, None, None, None)
224 with self.assertRaises(NotImplementedError):
225 pub.Run()
226
227 def testRevertNotImplemented(self):
228 """Tests running the Revert() function is not implemented."""
229 pub = device_imager.PartitionUpdaterBase(None, None, None, None, None)
230 with self.assertRaises(NotImplementedError):
231 pub.Revert()
232
233 @mock.patch.object(device_imager.PartitionUpdaterBase, '_Run')
234 def testIsFinished(self, _):
235 """Tests IsFinished() function."""
236 pub = device_imager.PartitionUpdaterBase(None, None, None, None, None)
237 self.assertFalse(pub.IsFinished())
238 pub.Run()
239 self.assertTrue(pub.IsFinished())
240
241
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800242class RawPartitionUpdaterTest(cros_test_lib.MockTempDirTestCase):
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800243 """Tests RawPartitionUpdater class."""
244
245 def setUp(self):
246 """Sets up the class by creating proper mocks."""
247 self.rsh_mock = self.StartPatcher(remote_access_unittest.RemoteShMock())
248 self.rsh_mock.AddCmdResult(partial_mock.In('${PATH}'), stdout='')
249 self.path_env = 'PATH=%s:' % remote_access.DEV_BIN_PATHS
250
251 @mock.patch.object(device_imager.RawPartitionUpdater, '_GetPartitionName',
252 return_value=constants.PART_KERN_A)
253 @mock.patch.object(image_lib, 'GetImageDiskPartitionInfo',
254 return_value=image_lib_unittest.LOOP_PARTITION_INFO)
255 @mock.patch.object(device_imager.PartialFileReader, 'CloseTarget')
256 @mock.patch.object(device_imager.PartialFileReader, 'run')
257 def test_RunFullImage(self, run_mock, close_mock, _, name_mock):
258 """Test main Run() function for full image.
259
260 This function should parts of the source image and write it into the device
261 using proper compression programs.
262 """
263 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
264 self.rsh_mock.AddCmdResult([partial_mock.In('which'), 'gzip'],
265 returncode=0)
266 self.rsh_mock.AddCmdResult(
267 self.path_env +
268 ' gzip --decompress --stdout | dd bs=1M oflag=direct of=/dev/mmcblk0p2')
269
270 device_imager.RawPartitionUpdater(
271 device, 'foo-image', device_imager.ImageType.FULL,
272 '/dev/mmcblk0p2', cros_build_lib.COMP_GZIP).Run()
273 run_mock.assert_called()
274 close_mock.assert_called()
275 name_mock.assert_called()
Amin Hassanid684e982021-02-26 11:10:58 -0800276
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800277 def test_RunRemoteImage(self):
278 """Test main Run() function for remote images."""
279 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
280 self.rsh_mock.AddCmdResult([partial_mock.In('which'), 'gzip'],
281 returncode=0)
282 self.rsh_mock.AddCmdResult(
283 self.path_env +
284 ' gzip --decompress --stdout | dd bs=1M oflag=direct of=/dev/mmcblk0p2')
285
286 path = os.path.join(self.tempdir,
287 constants.QUICK_PROVISION_PAYLOAD_KERNEL)
288 with open(path, 'w') as image:
289 image.write('helloworld')
290
291 device_imager.KernelUpdater(
292 device, self.tempdir, device_imager.ImageType.REMOTE_DIRECTORY,
293 '/dev/mmcblk0p2', cros_build_lib.COMP_GZIP).Run()
294
Amin Hassanid684e982021-02-26 11:10:58 -0800295
296class KernelUpdaterTest(cros_test_lib.MockTempDirTestCase):
297 """Tests KernelUpdater class."""
298
299 def test_GetPartitionName(self):
300 """Tests the name of the partitions."""
301 ku = device_imager.KernelUpdater(None, None, None, None, None)
302 self.assertEqual(constants.PART_KERN_B, ku._GetPartitionName())
Amin Hassani75c5f942021-02-20 23:56:53 -0800303
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800304 def test_GetRemotePartitionName(self):
305 """Tests the name of the partitions."""
306 ku = device_imager.KernelUpdater(None, None, None, None, None)
307 self.assertEqual(constants.QUICK_PROVISION_PAYLOAD_KERNEL,
308 ku._GetRemotePartitionName())
309
Amin Hassani75c5f942021-02-20 23:56:53 -0800310
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000311class MiniOSUpdaterTest(cros_test_lib.MockTempDirTestCase):
312 """Tests MiniOSUpdater class."""
313
314 def test_GetPartitionName(self):
315 """Tests the name of the partitions."""
316 u = device_imager.MiniOSUpdater(*([None] * 5))
317 self.assertEqual(constants.PART_MINIOS_A, u._GetPartitionName())
318
319 def test_GetRemotePartitionName(self):
320 """Tests the name of the partitions."""
321 # TODO(b/190631159, b/196056723): Allow fetching once miniOS payloads exist.
322 u = device_imager.MiniOSUpdater(*([None] * 5))
323 with self.assertRaises(NotImplementedError):
324 u._GetRemotePartitionName()
325
326 @mock.patch.object(device_imager.MiniOSUpdater, '_CopyPartitionFromImage')
327 @mock.patch.object(device_imager.MiniOSUpdater, '_MiniOSPartitionExists',
328 return_value=True)
329 @mock.patch.object(device_imager.MiniOSUpdater, '_RunPostInstall')
330 def test_Run(self, postinstall_mock, minios_mock, copy_mock):
331 """Test main Run() function."""
332 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
333 device_imager.MiniOSUpdater(
334 device, 'foo-image', device_imager.ImageType.FULL,
335 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP).Run()
336
337 copy_mock.assert_called_with(constants.PART_MINIOS_A)
338 minios_mock.assert_called_with()
339 postinstall_mock.assert_called_with()
340
341 @mock.patch.object(device_imager.MiniOSUpdater, '_CopyPartitionFromImage')
342 @mock.patch.object(device_imager.MiniOSUpdater, '_MiniOSPartitionExists',
343 return_value=False)
344 @mock.patch.object(device_imager.MiniOSUpdater, '_RunPostInstall')
345 def test_RunMissingMiniOS(self, postinstall_mock, minios_mock, copy_mock):
346 """Test main Run() function."""
347 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
348 device_imager.MiniOSUpdater(
349 device, 'foo-image', device_imager.ImageType.FULL,
350 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP).Run()
351
352 copy_mock.assert_not_called()
353 minios_mock.assert_called_with()
354 postinstall_mock.assert_called_with()
355
356 @mock.patch.object(device_imager.MiniOSUpdater, '_FlipMiniOSPriority')
357 def test_RunPostInstall(self, flip_mock):
358 """Test _RunPostInstall() function."""
359 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
360 device_imager.MiniOSUpdater(
361 device, 'foo-image', device_imager.ImageType.FULL,
362 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP)._RunPostInstall()
363
364 flip_mock.assert_called_with()
365
366 @mock.patch.object(device_imager.MiniOSUpdater, '_FlipMiniOSPriority')
367 def test_Revert(self, flip_mock):
368 """Test Revert() function."""
369 u = device_imager.MiniOSUpdater(
370 None, 'foo-image', device_imager.ImageType.FULL,
371 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP)
372
373 # Before PostInstall runs.
374 u.Revert()
375 flip_mock.assert_not_called()
376
377 u._ran_postinst = True
378 u.Revert()
379
380 flip_mock.assert_called_with()
381
382 @mock.patch.object(device_imager.MiniOSUpdater, '_GetMiniOSPriority',
383 return_value='A')
384 @mock.patch.object(device_imager.MiniOSUpdater, '_SetMiniOSPriority')
385 def test_FlipMiniOSPriority(self, set_mock, get_mock):
386 """Test _FlipMiniOSPriority() function."""
387 device_imager.MiniOSUpdater(
388 None, 'foo-image', device_imager.ImageType.FULL,
389 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP)._FlipMiniOSPriority()
390
391 get_mock.assert_called_with()
392 set_mock.assert_called_with('B')
393
394
Amin Hassani75c5f942021-02-20 23:56:53 -0800395class RootfsUpdaterTest(cros_test_lib.MockTestCase):
396 """Tests RootfsUpdater class."""
397
398 def setUp(self):
399 """Sets up the class by creating proper mocks."""
400 self.rsh_mock = self.StartPatcher(remote_access_unittest.RemoteShMock())
401 self.rsh_mock.AddCmdResult(partial_mock.In('${PATH}'), stdout='')
402 self.path_env = 'PATH=%s:' % remote_access.DEV_BIN_PATHS
403
404 def test_GetPartitionName(self):
405 """Tests the name of the partitions."""
406 ru = device_imager.RootfsUpdater(None, None, None, None, None, None)
407 self.assertEqual(constants.PART_ROOT_A, ru._GetPartitionName())
408
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800409 def test_GetRemotePartitionName(self):
410 """Tests the name of the partitions."""
411 ru = device_imager.RootfsUpdater(None, None, None, None, None, None)
412 self.assertEqual(constants.QUICK_PROVISION_PAYLOAD_ROOTFS,
413 ru._GetRemotePartitionName())
414
Amin Hassani55970562021-02-22 20:49:13 -0800415 @mock.patch.object(device_imager.ProgressWatcher, 'run')
Amin Hassani75c5f942021-02-20 23:56:53 -0800416 @mock.patch.object(device_imager.RootfsUpdater, '_RunPostInst')
417 @mock.patch.object(device_imager.RootfsUpdater, '_CopyPartitionFromImage')
Amin Hassani55970562021-02-22 20:49:13 -0800418 def test_Run(self, copy_mock, postinst_mock, pw_mock):
Amin Hassani75c5f942021-02-20 23:56:53 -0800419 """Test main Run() function.
420
421 This function should parts of the source image and write it into the device
422 using proper compression programs.
423 """
424 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
425 device_imager.RootfsUpdater(
426 '/dev/mmcblk0p5', device, 'foo-image', device_imager.ImageType.FULL,
427 '/dev/mmcblk0p3', cros_build_lib.COMP_GZIP).Run()
428
429 copy_mock.assert_called_with(constants.PART_ROOT_A)
430 postinst_mock.assert_called_with()
Amin Hassani55970562021-02-22 20:49:13 -0800431 pw_mock.assert_called()
Amin Hassani75c5f942021-02-20 23:56:53 -0800432
433 def test_RunPostInstOnTarget(self):
434 """Test _RunPostInst() function."""
435 target = '/dev/mmcblk0p3'
436 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
437 device._work_dir = '/tmp/work_dir'
438 temp_dir = os.path.join(device.work_dir, 'dir')
439 self.rsh_mock.AddCmdResult(
440 [self.path_env, 'mktemp', '-d', '-p', device.work_dir],
441 stdout=temp_dir)
442 self.rsh_mock.AddCmdResult(
443 [self.path_env, 'mount', '-o', 'ro', target, temp_dir])
444 self.rsh_mock.AddCmdResult(
445 [self.path_env, os.path.join(temp_dir, 'postinst'), target])
446 self.rsh_mock.AddCmdResult([self.path_env, 'umount', temp_dir])
447
448 device_imager.RootfsUpdater(
449 '/dev/mmcblk0p5', device, 'foo-image', device_imager.ImageType.FULL,
450 target, cros_build_lib.COMP_GZIP)._RunPostInst()
451
452 def test_RunPostInstOnCurrentRoot(self):
453 """Test _RunPostInst() on current root (used for reverting an update)."""
454 root_dev = '/dev/mmcblk0p5'
455 self.rsh_mock.AddCmdResult([self.path_env, '/postinst', root_dev])
456
457 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
458 device_imager.RootfsUpdater(
459 root_dev, device, 'foo-image', device_imager.ImageType.FULL,
460 '/dev/mmcblk0p3', cros_build_lib.COMP_GZIP)._RunPostInst(
461 on_target=False)
462
463 @mock.patch.object(device_imager.RootfsUpdater, '_RunPostInst')
464 def testRevert(self, postinst_mock):
465 """Tests Revert() function."""
466 ru = device_imager.RootfsUpdater(None, None, None, None, None, None)
467
468 ru.Revert()
469 postinst_mock.assert_not_called()
470
471 ru._ran_postinst = True
472 ru.Revert()
473 postinst_mock.assert_called_with(on_target=False)
Amin Hassani74403082021-02-22 11:40:09 -0800474
475
476class StatefulPayloadGeneratorTest(cros_test_lib.TestCase):
477 """Tests stateful payload generator."""
478 @mock.patch.object(paygen_stateful_payload_lib, 'GenerateStatefulPayload')
479 def testRun(self, paygen_mock):
480 """Tests run() function."""
481 image = '/foo/image'
482 with device_imager.StatefulPayloadGenerator(image) as spg:
483 pass
484
485 paygen_mock.assert_called_with(image, spg._Source())
486
487
488class StatefulUpdaterTest(cros_test_lib.TestCase):
489 """Tests StatefulUpdater."""
490 @mock.patch.object(paygen_stateful_payload_lib, 'GenerateStatefulPayload')
491 @mock.patch.object(stateful_updater.StatefulUpdater, 'Update')
492 def test_RunFullImage(self, update_mock, paygen_mock):
493 """Test main Run() function for full image."""
494 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
495 device_imager.StatefulUpdater(False, device, 'foo-image',
496 device_imager.ImageType.FULL, None, None).Run()
497 update_mock.assert_called_with(mock.ANY,
498 is_payload_on_device=False,
499 update_type=None)
500 paygen_mock.assert_called()
501
502 @mock.patch.object(gs.GSContext, 'Copy')
503 @mock.patch.object(stateful_updater.StatefulUpdater, 'Update')
504 def test_RunRemoteImage(self, update_mock, copy_mock):
505 """Test main Run() function for remote images."""
506 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
507 device_imager.StatefulUpdater(False, device, 'gs://foo-image',
508 device_imager.ImageType.REMOTE_DIRECTORY, None,
509 None).Run()
510 copy_mock.assert_called_with('gs://foo-image/stateful.tgz',mock.ANY)
511 update_mock.assert_called_with(mock.ANY, is_payload_on_device=False,
512 update_type=None)
513
514 @mock.patch.object(stateful_updater.StatefulUpdater, 'Reset')
515 def testRevert(self, reset_mock):
516 """Tests Revert() function."""
517 su = device_imager.StatefulUpdater(False, None, None, None, None, None)
518
519 su.Revert()
520 reset_mock.assert_called()
Amin Hassani55970562021-02-22 20:49:13 -0800521
522
523class ProgressWatcherTest(cros_test_lib.MockTestCase):
524 """Tests ProgressWatcher class"""
525
526 def setUp(self):
527 """Sets up the class by creating proper mocks."""
528 self.rsh_mock = self.StartPatcher(remote_access_unittest.RemoteShMock())
529 self.rsh_mock.AddCmdResult(partial_mock.In('${PATH}'), stdout='')
530 self.path_env = 'PATH=%s:' % remote_access.DEV_BIN_PATHS
531
532 @mock.patch.object(time, 'sleep')
533 @mock.patch.object(device_imager.ProgressWatcher, '_ShouldExit',
534 side_effect=[False, False, True])
535 # pylint: disable=unused-argument
536 def testRun(self, exit_mock, _):
537 """Tests the run() function."""
538 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
539 target_root = '/foo/root'
540 self.rsh_mock.AddCmdResult(
541 [self.path_env, 'blockdev', '--getsize64', target_root], stdout='100')
542 self.rsh_mock.AddCmdResult(
543 self.path_env + f' lsof 2>/dev/null | grep {target_root}',
544 stdout='xz 999')
545 self.rsh_mock.AddCmdResult([self.path_env, 'cat', '/proc/999/fdinfo/1'],
546 stdout='pos: 10\nflags: foo')
547 pw = device_imager.ProgressWatcher(device, target_root)
548 pw.run()