blob: 21dcba47a02afe3a0b0bdc15bb7a7a68b49549c2 [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(
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700193 '/foo', 512 * 2, 512, cros_build_lib.COMP_GZIP) as pfr:
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800194 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(
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700267 self.path_env +
268 ' gzip --decompress --stdout | ' +
269 'dd bs=1M oflag=direct of=/dev/mmcblk0p2')
Amin Hassanid4b3ff82021-02-20 23:05:14 -0800270
271 device_imager.RawPartitionUpdater(
272 device, 'foo-image', device_imager.ImageType.FULL,
273 '/dev/mmcblk0p2', cros_build_lib.COMP_GZIP).Run()
274 run_mock.assert_called()
275 close_mock.assert_called()
276 name_mock.assert_called()
Amin Hassanid684e982021-02-26 11:10:58 -0800277
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800278 def test_RunRemoteImage(self):
279 """Test main Run() function for remote images."""
280 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
281 self.rsh_mock.AddCmdResult([partial_mock.In('which'), 'gzip'],
282 returncode=0)
283 self.rsh_mock.AddCmdResult(
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700284 self.path_env +
285 ' gzip --decompress --stdout | ' +
286 'dd bs=1M oflag=direct of=/dev/mmcblk0p2')
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800287
288 path = os.path.join(self.tempdir,
289 constants.QUICK_PROVISION_PAYLOAD_KERNEL)
290 with open(path, 'w') as image:
291 image.write('helloworld')
292
293 device_imager.KernelUpdater(
294 device, self.tempdir, device_imager.ImageType.REMOTE_DIRECTORY,
295 '/dev/mmcblk0p2', cros_build_lib.COMP_GZIP).Run()
296
Amin Hassanid684e982021-02-26 11:10:58 -0800297
298class KernelUpdaterTest(cros_test_lib.MockTempDirTestCase):
299 """Tests KernelUpdater class."""
300
301 def test_GetPartitionName(self):
302 """Tests the name of the partitions."""
303 ku = device_imager.KernelUpdater(None, None, None, None, None)
304 self.assertEqual(constants.PART_KERN_B, ku._GetPartitionName())
Amin Hassani75c5f942021-02-20 23:56:53 -0800305
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800306 def test_GetRemotePartitionName(self):
307 """Tests the name of the partitions."""
308 ku = device_imager.KernelUpdater(None, None, None, None, None)
309 self.assertEqual(constants.QUICK_PROVISION_PAYLOAD_KERNEL,
310 ku._GetRemotePartitionName())
311
Amin Hassani75c5f942021-02-20 23:56:53 -0800312
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000313class MiniOSUpdaterTest(cros_test_lib.MockTempDirTestCase):
314 """Tests MiniOSUpdater class."""
315
316 def test_GetPartitionName(self):
317 """Tests the name of the partitions."""
318 u = device_imager.MiniOSUpdater(*([None] * 5))
319 self.assertEqual(constants.PART_MINIOS_A, u._GetPartitionName())
320
321 def test_GetRemotePartitionName(self):
322 """Tests the name of the partitions."""
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000323 u = device_imager.MiniOSUpdater(*([None] * 5))
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700324 self.assertEqual(constants.QUICK_PROVISION_PAYLOAD_MINIOS,
325 u._GetRemotePartitionName())
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000326
327 @mock.patch.object(device_imager.MiniOSUpdater, '_CopyPartitionFromImage')
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700328 @mock.patch.object(device_imager.MiniOSUpdater,
329 '_MiniOSPartitionsExistInImage', return_value=True)
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000330 @mock.patch.object(device_imager.MiniOSUpdater, '_RunPostInstall')
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700331 def test_Run(self, postinstall_mock, partitions_exist_mock, copy_mock):
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000332 """Test main Run() function."""
333 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
334 device_imager.MiniOSUpdater(
335 device, 'foo-image', device_imager.ImageType.FULL,
336 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP).Run()
337
338 copy_mock.assert_called_with(constants.PART_MINIOS_A)
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700339 partitions_exist_mock.assert_called_with()
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000340 postinstall_mock.assert_called_with()
341
342 @mock.patch.object(device_imager.MiniOSUpdater, '_CopyPartitionFromImage')
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700343 @mock.patch.object(device_imager.MiniOSUpdater,
344 '_MiniOSPartitionsExistInImage', return_value=False)
345 def test_RunMissingMiniOS(self, partitions_exist_mock, copy_mock):
346 """Test main Run() function with missing miniOS partitions on image."""
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000347 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()
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700353 partitions_exist_mock.assert_called_with()
354
355 @mock.patch.object(device_imager.MiniOSUpdater, '_RunPostInstall')
356 @mock.patch.object(device_imager.MiniOSUpdater, '_RedirectPartition')
357 @mock.patch.object(device_imager.MiniOSUpdater, '_MiniOSPartitionsExist')
358 @mock.patch.object(gs.GSContext, 'Exists', return_value=False)
359 def test_RunMissingMiniOSRemotePayload(self, gs_context_mock,
360 partitions_exist_mock, redirect_mock,
361 post_install_mock):
362 """Test main Run() function with missing miniOS remote payloads."""
363 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
364 device_imager.MiniOSUpdater(
365 device, 'foo-image', device_imager.ImageType.REMOTE_DIRECTORY,
366 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP).Run()
367
368 post_install_mock.assert_not_called()
369 redirect_mock.assert_not_called()
370 partitions_exist_mock.assert_not_called()
371 gs_context_mock.assert_called()
372
373 @mock.patch.object(device_imager.MiniOSUpdater, '_RunPostInstall')
374 @mock.patch.object(device_imager.MiniOSUpdater, '_RedirectPartition')
375 @mock.patch.object(device_imager.MiniOSUpdater, '_MiniOSPartitionsExist',
376 return_value=False)
377 @mock.patch.object(gs.GSContext, 'Exists', return_value=True)
378 def test_RunMissingMiniOSPartitions(self, gs_context_mock,
379 partitions_exist_mock, redirect_mock,
380 post_install_mock):
381 """Test main Run() function with missing miniOS remote payloads."""
382 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
383 device_imager.MiniOSUpdater(
384 device, 'foo-image', device_imager.ImageType.REMOTE_DIRECTORY,
385 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP).Run()
386
387 post_install_mock.assert_not_called()
388 redirect_mock.assert_not_called()
389 partitions_exist_mock.assert_called()
390 gs_context_mock.assert_called()
391
392 @mock.patch.object(device_imager.MiniOSUpdater, '_RunPostInstall')
393 @mock.patch.object(device_imager.MiniOSUpdater, '_RedirectPartition')
394 @mock.patch.object(device_imager.MiniOSUpdater, '_MiniOSPartitionsExist',
395 return_value=True)
396 @mock.patch.object(gs.GSContext, 'Exists', return_value=True)
397 def test_RunMiniOSRemotePayload(self, gs_context_mock, partitions_exist_mock,
398 redirect_mock, post_install_mock):
399 """Test main Run() function with missing miniOS remote payloads."""
400 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
401 device_imager.MiniOSUpdater(
402 device, 'foo-image', device_imager.ImageType.REMOTE_DIRECTORY,
403 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP).Run()
404
405 post_install_mock.assert_called()
406 redirect_mock.assert_called()
407 partitions_exist_mock.assert_called()
408 gs_context_mock.assert_called()
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000409
410 @mock.patch.object(device_imager.MiniOSUpdater, '_FlipMiniOSPriority')
411 def test_RunPostInstall(self, flip_mock):
412 """Test _RunPostInstall() function."""
413 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
414 device_imager.MiniOSUpdater(
415 device, 'foo-image', device_imager.ImageType.FULL,
416 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP)._RunPostInstall()
417
418 flip_mock.assert_called_with()
419
420 @mock.patch.object(device_imager.MiniOSUpdater, '_FlipMiniOSPriority')
421 def test_Revert(self, flip_mock):
422 """Test Revert() function."""
423 u = device_imager.MiniOSUpdater(
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700424 None, 'foo-image', device_imager.ImageType.FULL,
425 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP)
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000426
427 # Before PostInstall runs.
428 u.Revert()
429 flip_mock.assert_not_called()
430
431 u._ran_postinst = True
432 u.Revert()
433
434 flip_mock.assert_called_with()
435
436 @mock.patch.object(device_imager.MiniOSUpdater, '_GetMiniOSPriority',
437 return_value='A')
438 @mock.patch.object(device_imager.MiniOSUpdater, '_SetMiniOSPriority')
439 def test_FlipMiniOSPriority(self, set_mock, get_mock):
440 """Test _FlipMiniOSPriority() function."""
441 device_imager.MiniOSUpdater(
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700442 None, 'foo-image', device_imager.ImageType.FULL,
443 '/dev/mmcblk0p10', cros_build_lib.COMP_GZIP)._FlipMiniOSPriority()
Jae Hoon Kimcc723e02021-08-16 21:03:21 +0000444
445 get_mock.assert_called_with()
446 set_mock.assert_called_with('B')
447
448
Amin Hassani75c5f942021-02-20 23:56:53 -0800449class RootfsUpdaterTest(cros_test_lib.MockTestCase):
450 """Tests RootfsUpdater class."""
451
452 def setUp(self):
453 """Sets up the class by creating proper mocks."""
454 self.rsh_mock = self.StartPatcher(remote_access_unittest.RemoteShMock())
455 self.rsh_mock.AddCmdResult(partial_mock.In('${PATH}'), stdout='')
456 self.path_env = 'PATH=%s:' % remote_access.DEV_BIN_PATHS
457
458 def test_GetPartitionName(self):
459 """Tests the name of the partitions."""
460 ru = device_imager.RootfsUpdater(None, None, None, None, None, None)
461 self.assertEqual(constants.PART_ROOT_A, ru._GetPartitionName())
462
Amin Hassani0fe49ae2021-02-21 23:41:58 -0800463 def test_GetRemotePartitionName(self):
464 """Tests the name of the partitions."""
465 ru = device_imager.RootfsUpdater(None, None, None, None, None, None)
466 self.assertEqual(constants.QUICK_PROVISION_PAYLOAD_ROOTFS,
467 ru._GetRemotePartitionName())
468
Amin Hassani55970562021-02-22 20:49:13 -0800469 @mock.patch.object(device_imager.ProgressWatcher, 'run')
Amin Hassani75c5f942021-02-20 23:56:53 -0800470 @mock.patch.object(device_imager.RootfsUpdater, '_RunPostInst')
471 @mock.patch.object(device_imager.RootfsUpdater, '_CopyPartitionFromImage')
Amin Hassani55970562021-02-22 20:49:13 -0800472 def test_Run(self, copy_mock, postinst_mock, pw_mock):
Amin Hassani75c5f942021-02-20 23:56:53 -0800473 """Test main Run() function.
474
475 This function should parts of the source image and write it into the device
476 using proper compression programs.
477 """
478 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
479 device_imager.RootfsUpdater(
480 '/dev/mmcblk0p5', device, 'foo-image', device_imager.ImageType.FULL,
481 '/dev/mmcblk0p3', cros_build_lib.COMP_GZIP).Run()
482
483 copy_mock.assert_called_with(constants.PART_ROOT_A)
484 postinst_mock.assert_called_with()
Amin Hassani55970562021-02-22 20:49:13 -0800485 pw_mock.assert_called()
Amin Hassani75c5f942021-02-20 23:56:53 -0800486
487 def test_RunPostInstOnTarget(self):
488 """Test _RunPostInst() function."""
489 target = '/dev/mmcblk0p3'
490 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
491 device._work_dir = '/tmp/work_dir'
492 temp_dir = os.path.join(device.work_dir, 'dir')
493 self.rsh_mock.AddCmdResult(
494 [self.path_env, 'mktemp', '-d', '-p', device.work_dir],
495 stdout=temp_dir)
496 self.rsh_mock.AddCmdResult(
497 [self.path_env, 'mount', '-o', 'ro', target, temp_dir])
498 self.rsh_mock.AddCmdResult(
499 [self.path_env, os.path.join(temp_dir, 'postinst'), target])
500 self.rsh_mock.AddCmdResult([self.path_env, 'umount', temp_dir])
501
502 device_imager.RootfsUpdater(
503 '/dev/mmcblk0p5', device, 'foo-image', device_imager.ImageType.FULL,
504 target, cros_build_lib.COMP_GZIP)._RunPostInst()
505
506 def test_RunPostInstOnCurrentRoot(self):
507 """Test _RunPostInst() on current root (used for reverting an update)."""
508 root_dev = '/dev/mmcblk0p5'
509 self.rsh_mock.AddCmdResult([self.path_env, '/postinst', root_dev])
510
511 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
512 device_imager.RootfsUpdater(
513 root_dev, device, 'foo-image', device_imager.ImageType.FULL,
514 '/dev/mmcblk0p3', cros_build_lib.COMP_GZIP)._RunPostInst(
515 on_target=False)
516
517 @mock.patch.object(device_imager.RootfsUpdater, '_RunPostInst')
518 def testRevert(self, postinst_mock):
519 """Tests Revert() function."""
520 ru = device_imager.RootfsUpdater(None, None, None, None, None, None)
521
522 ru.Revert()
523 postinst_mock.assert_not_called()
524
525 ru._ran_postinst = True
526 ru.Revert()
527 postinst_mock.assert_called_with(on_target=False)
Amin Hassani74403082021-02-22 11:40:09 -0800528
529
530class StatefulPayloadGeneratorTest(cros_test_lib.TestCase):
531 """Tests stateful payload generator."""
532 @mock.patch.object(paygen_stateful_payload_lib, 'GenerateStatefulPayload')
533 def testRun(self, paygen_mock):
534 """Tests run() function."""
535 image = '/foo/image'
536 with device_imager.StatefulPayloadGenerator(image) as spg:
537 pass
538
539 paygen_mock.assert_called_with(image, spg._Source())
540
541
542class StatefulUpdaterTest(cros_test_lib.TestCase):
543 """Tests StatefulUpdater."""
544 @mock.patch.object(paygen_stateful_payload_lib, 'GenerateStatefulPayload')
545 @mock.patch.object(stateful_updater.StatefulUpdater, 'Update')
546 def test_RunFullImage(self, update_mock, paygen_mock):
547 """Test main Run() function for full image."""
548 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
549 device_imager.StatefulUpdater(False, device, 'foo-image',
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700550 device_imager.ImageType.FULL, None,
551 None).Run()
Amin Hassani74403082021-02-22 11:40:09 -0800552 update_mock.assert_called_with(mock.ANY,
553 is_payload_on_device=False,
554 update_type=None)
555 paygen_mock.assert_called()
556
557 @mock.patch.object(gs.GSContext, 'Copy')
558 @mock.patch.object(stateful_updater.StatefulUpdater, 'Update')
559 def test_RunRemoteImage(self, update_mock, copy_mock):
560 """Test main Run() function for remote images."""
561 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
562 device_imager.StatefulUpdater(False, device, 'gs://foo-image',
Jae Hoon Kimb88b7962021-10-18 11:08:38 -0700563 device_imager.ImageType.REMOTE_DIRECTORY,
564 None, None).Run()
565 copy_mock.assert_called_with('gs://foo-image/stateful.tgz', mock.ANY)
Amin Hassani74403082021-02-22 11:40:09 -0800566 update_mock.assert_called_with(mock.ANY, is_payload_on_device=False,
567 update_type=None)
568
569 @mock.patch.object(stateful_updater.StatefulUpdater, 'Reset')
570 def testRevert(self, reset_mock):
571 """Tests Revert() function."""
572 su = device_imager.StatefulUpdater(False, None, None, None, None, None)
573
574 su.Revert()
575 reset_mock.assert_called()
Amin Hassani55970562021-02-22 20:49:13 -0800576
577
578class ProgressWatcherTest(cros_test_lib.MockTestCase):
579 """Tests ProgressWatcher class"""
580
581 def setUp(self):
582 """Sets up the class by creating proper mocks."""
583 self.rsh_mock = self.StartPatcher(remote_access_unittest.RemoteShMock())
584 self.rsh_mock.AddCmdResult(partial_mock.In('${PATH}'), stdout='')
585 self.path_env = 'PATH=%s:' % remote_access.DEV_BIN_PATHS
586
587 @mock.patch.object(time, 'sleep')
588 @mock.patch.object(device_imager.ProgressWatcher, '_ShouldExit',
589 side_effect=[False, False, True])
590 # pylint: disable=unused-argument
591 def testRun(self, exit_mock, _):
592 """Tests the run() function."""
593 with remote_access.ChromiumOSDeviceHandler(remote_access.TEST_IP) as device:
594 target_root = '/foo/root'
595 self.rsh_mock.AddCmdResult(
596 [self.path_env, 'blockdev', '--getsize64', target_root], stdout='100')
597 self.rsh_mock.AddCmdResult(
598 self.path_env + f' lsof 2>/dev/null | grep {target_root}',
599 stdout='xz 999')
600 self.rsh_mock.AddCmdResult([self.path_env, 'cat', '/proc/999/fdinfo/1'],
601 stdout='pos: 10\nflags: foo')
602 pw = device_imager.ProgressWatcher(device, target_root)
603 pw.run()