blob: ceb41dd4a6a963890c20a2dc216a1ed00d74292e [file] [log] [blame]
David Pursell9476bf42015-03-30 13:34:27 -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 deploy module."""
6
7from __future__ import print_function
8
9import json
Ralph Nathane01ccf12015-04-16 10:40:32 -070010import multiprocessing
David Pursell9476bf42015-03-30 13:34:27 -070011import os
12
Ralph Nathane01ccf12015-04-16 10:40:32 -070013from chromite.cli import command
David Pursell9476bf42015-03-30 13:34:27 -070014from chromite.cli import deploy
15from chromite.lib import cros_build_lib
16from chromite.lib import cros_test_lib
Gilad Arnold0e1b1da2015-06-10 06:41:05 -070017from chromite.lib import portage_util
Ralph Nathane01ccf12015-04-16 10:40:32 -070018from chromite.lib import remote_access
David Pursell9476bf42015-03-30 13:34:27 -070019try:
20 import portage
21except ImportError:
22 if cros_build_lib.IsInsideChroot():
23 raise
24
25
26# pylint: disable=protected-access
27
28
Ralph Nathane01ccf12015-04-16 10:40:32 -070029class ChromiumOSDeviceFake(object):
30 """Fake for device."""
31
32 def __init__(self):
33 self.board = 'board'
34 self.hostname = None
35 self.username = None
36 self.port = None
37 self.lsb_release = None
38
Mike Frysinger74ccd572015-05-21 21:18:20 -040039 def IsDirWritable(self, _):
Ralph Nathane01ccf12015-04-16 10:40:32 -070040 return True
41
42
David Pursell9476bf42015-03-30 13:34:27 -070043class ChromiumOSDeviceHandlerFake(object):
44 """Fake for chromite.lib.remote_access.ChomiumOSDeviceHandler."""
45
46 class RemoteAccessFake(object):
47 """Fake for chromite.lib.remote_access.RemoteAccess."""
48
49 def __init__(self):
50 self.remote_sh_output = None
51
52 def RemoteSh(self, *_args, **_kwargs):
53 return cros_build_lib.CommandResult(output=self.remote_sh_output)
54
Ralph Nathane01ccf12015-04-16 10:40:32 -070055 def __init__(self, *_args, **_kwargs):
David Pursell67a82762015-04-30 17:26:59 -070056 self._agent = self.RemoteAccessFake()
Mike Frysinger539db512015-05-21 18:14:01 -040057 self.device = ChromiumOSDeviceFake()
David Pursell67a82762015-04-30 17:26:59 -070058
Ralph Nathane01ccf12015-04-16 10:40:32 -070059 # TODO(dpursell): Mock remote access object in cros_test_lib (brbug.com/986).
David Pursell67a82762015-04-30 17:26:59 -070060 def GetAgent(self):
61 return self._agent
David Pursell9476bf42015-03-30 13:34:27 -070062
Ralph Nathane01ccf12015-04-16 10:40:32 -070063 def __exit__(self, _type, _value, _traceback):
64 pass
65
66 def __enter__(self):
67 return ChromiumOSDeviceFake()
68
69
70class BrilloDeployOperationFake(deploy.BrilloDeployOperation):
71 """Fake for deploy.BrilloDeployOperation."""
72 def __init__(self, pkg_count, emerge, queue):
73 super(BrilloDeployOperationFake, self).__init__(pkg_count, emerge)
74 self._queue = queue
75
Ralph Nathandc14ed92015-04-22 11:17:40 -070076 def ParseOutput(self, output=None):
77 super(BrilloDeployOperationFake, self).ParseOutput(output)
Ralph Nathane01ccf12015-04-16 10:40:32 -070078 self._queue.put('advance')
79
David Pursell9476bf42015-03-30 13:34:27 -070080
81class DbApiFake(object):
82 """Fake for Portage dbapi."""
83
84 def __init__(self, pkgs):
85 self.pkg_db = {}
86 for cpv, slot, rdeps_raw, build_time in pkgs:
87 self.pkg_db[cpv] = {
88 'SLOT': slot, 'RDEPEND': rdeps_raw, 'BUILD_TIME': build_time}
89
90 def cpv_all(self):
91 return self.pkg_db.keys()
92
93 def aux_get(self, cpv, keys):
94 pkg_info = self.pkg_db[cpv]
95 return [pkg_info[key] for key in keys]
96
97
Ralph Nathane01ccf12015-04-16 10:40:32 -070098class PackageScannerFake(object):
99 """Fake for PackageScanner."""
100
101 def __init__(self, packages):
102 self.pkgs = packages
103 self.listed = []
104 self.num_updates = None
105
106 def Run(self, _device, _root, _packages, _update, _deep, _deep_rev):
107 return self.pkgs, self.listed, self.num_updates
108
109
David Pursell9476bf42015-03-30 13:34:27 -0700110class PortageTreeFake(object):
111 """Fake for Portage tree."""
112
113 def __init__(self, dbapi):
114 self.dbapi = dbapi
115
116
Ralph Nathane01ccf12015-04-16 10:40:32 -0700117class TestInstallPackageScanner(cros_test_lib.MockOutputTestCase):
David Pursell9476bf42015-03-30 13:34:27 -0700118 """Test the update package scanner."""
119 _BOARD = 'foo_board'
120 _BUILD_ROOT = '/build/%s' % _BOARD
121 _VARTREE = [
122 ('foo/app1-1.2.3-r4', '0', 'foo/app2 !foo/app3', '1413309336'),
123 ('foo/app2-4.5.6-r7', '0', '', '1413309336'),
124 ('foo/app4-2.0.0-r1', '0', 'foo/app1 foo/app5', '1413309336'),
125 ('foo/app5-3.0.7-r3', '0', '', '1413309336'),
126 ]
127
128 def setUp(self):
129 """Patch imported modules."""
130 self.PatchObject(cros_build_lib, 'GetChoice', return_value=0)
131 self.device = ChromiumOSDeviceHandlerFake()
132 self.scanner = deploy._InstallPackageScanner(self._BUILD_ROOT)
133
134 def SetupVartree(self, vartree_pkgs):
David Pursell67a82762015-04-30 17:26:59 -0700135 self.device.GetAgent().remote_sh_output = json.dumps(vartree_pkgs)
David Pursell9476bf42015-03-30 13:34:27 -0700136
137 def SetupBintree(self, bintree_pkgs):
138 bintree = PortageTreeFake(DbApiFake(bintree_pkgs))
139 build_root = os.path.join(self._BUILD_ROOT, '')
140 portage_db = {build_root: {'bintree': bintree}}
141 self.PatchObject(portage, 'create_trees', return_value=portage_db)
142
143 def ValidatePkgs(self, actual, expected, constraints=None):
144 # Containing exactly the same packages.
145 self.assertEquals(sorted(expected), sorted(actual))
146 # Packages appear in the right order.
147 if constraints is not None:
148 for needs, needed in constraints:
149 self.assertGreater(actual.index(needs), actual.index(needed))
150
151 def testRunUpdatedVersion(self):
152 self.SetupVartree(self._VARTREE)
153 app1 = 'foo/app1-1.2.5-r4'
154 self.SetupBintree([
155 (app1, '0', 'foo/app2 !foo/app3', '1413309336'),
156 ('foo/app2-4.5.6-r7', '0', '', '1413309336'),
157 ])
158 installs, listed, num_updates = self.scanner.Run(
159 self.device, '/', ['app1'], True, True, True)
160 self.ValidatePkgs(installs, [app1])
161 self.ValidatePkgs(listed, [app1])
162 self.assertEquals(num_updates, 1)
163
164 def testRunUpdatedBuildTime(self):
165 self.SetupVartree(self._VARTREE)
166 app1 = 'foo/app1-1.2.3-r4'
167 self.SetupBintree([
168 (app1, '0', 'foo/app2 !foo/app3', '1413309350'),
169 ('foo/app2-4.5.6-r7', '0', '', '1413309336'),
170 ])
171 installs, listed, num_updates = self.scanner.Run(
172 self.device, '/', ['app1'], True, True, True)
173 self.ValidatePkgs(installs, [app1])
174 self.ValidatePkgs(listed, [app1])
175 self.assertEquals(num_updates, 1)
176
177 def testRunExistingDepUpdated(self):
178 self.SetupVartree(self._VARTREE)
179 app1 = 'foo/app1-1.2.5-r2'
180 app2 = 'foo/app2-4.5.8-r3'
181 self.SetupBintree([
182 (app1, '0', 'foo/app2 !foo/app3', '1413309350'),
183 (app2, '0', '', '1413309350'),
184 ])
185 installs, listed, num_updates = self.scanner.Run(
186 self.device, '/', ['app1'], True, True, True)
187 self.ValidatePkgs(installs, [app1, app2], constraints=[(app1, app2)])
188 self.ValidatePkgs(listed, [app1])
189 self.assertEquals(num_updates, 2)
190
191 def testRunMissingDepUpdated(self):
192 self.SetupVartree(self._VARTREE)
193 app1 = 'foo/app1-1.2.5-r2'
194 app6 = 'foo/app6-1.0.0-r1'
195 self.SetupBintree([
196 (app1, '0', 'foo/app2 !foo/app3 foo/app6', '1413309350'),
197 ('foo/app2-4.5.6-r7', '0', '', '1413309336'),
198 (app6, '0', '', '1413309350'),
199 ])
200 installs, listed, num_updates = self.scanner.Run(
201 self.device, '/', ['app1'], True, True, True)
202 self.ValidatePkgs(installs, [app1, app6], constraints=[(app1, app6)])
203 self.ValidatePkgs(listed, [app1])
204 self.assertEquals(num_updates, 1)
205
206 def testRunExistingRevDepUpdated(self):
207 self.SetupVartree(self._VARTREE)
208 app1 = 'foo/app1-1.2.5-r2'
209 app4 = 'foo/app4-2.0.1-r3'
210 self.SetupBintree([
211 (app1, '0', 'foo/app2 !foo/app3', '1413309350'),
212 (app4, '0', 'foo/app1 foo/app5', '1413309350'),
213 ('foo/app5-3.0.7-r3', '0', '', '1413309336'),
214 ])
215 installs, listed, num_updates = self.scanner.Run(
216 self.device, '/', ['app1'], True, True, True)
217 self.ValidatePkgs(installs, [app1, app4], constraints=[(app4, app1)])
218 self.ValidatePkgs(listed, [app1])
219 self.assertEquals(num_updates, 2)
220
221 def testRunMissingRevDepNotUpdated(self):
222 self.SetupVartree(self._VARTREE)
223 app1 = 'foo/app1-1.2.5-r2'
224 app6 = 'foo/app6-1.0.0-r1'
225 self.SetupBintree([
226 (app1, '0', 'foo/app2 !foo/app3', '1413309350'),
227 (app6, '0', 'foo/app1', '1413309350'),
228 ])
229 installs, listed, num_updates = self.scanner.Run(
230 self.device, '/', ['app1'], True, True, True)
231 self.ValidatePkgs(installs, [app1])
232 self.ValidatePkgs(listed, [app1])
233 self.assertEquals(num_updates, 1)
234
235 def testRunTransitiveDepsUpdated(self):
236 self.SetupVartree(self._VARTREE)
237 app1 = 'foo/app1-1.2.5-r2'
238 app2 = 'foo/app2-4.5.8-r3'
239 app4 = 'foo/app4-2.0.0-r1'
240 app5 = 'foo/app5-3.0.8-r2'
241 self.SetupBintree([
242 (app1, '0', 'foo/app2 !foo/app3', '1413309350'),
243 (app2, '0', '', '1413309350'),
244 (app4, '0', 'foo/app1 foo/app5', '1413309350'),
245 (app5, '0', '', '1413309350'),
246 ])
247 installs, listed, num_updates = self.scanner.Run(
248 self.device, '/', ['app1'], True, True, True)
249 self.ValidatePkgs(installs, [app1, app2, app4, app5],
250 constraints=[(app1, app2), (app4, app1), (app4, app5)])
251 self.ValidatePkgs(listed, [app1])
252 self.assertEquals(num_updates, 4)
253
254 def testRunDisjunctiveDepsExistingUpdated(self):
255 self.SetupVartree(self._VARTREE)
256 app1 = 'foo/app1-1.2.5-r2'
257 self.SetupBintree([
258 (app1, '0', '|| ( foo/app6 foo/app2 ) !foo/app3', '1413309350'),
259 ('foo/app2-4.5.6-r7', '0', '', '1413309336'),
260 ])
261 installs, listed, num_updates = self.scanner.Run(
262 self.device, '/', ['app1'], True, True, True)
263 self.ValidatePkgs(installs, [app1])
264 self.ValidatePkgs(listed, [app1])
265 self.assertEquals(num_updates, 1)
266
267 def testRunDisjunctiveDepsDefaultUpdated(self):
268 self.SetupVartree(self._VARTREE)
269 app1 = 'foo/app1-1.2.5-r2'
270 app7 = 'foo/app7-1.0.0-r1'
271 self.SetupBintree([
272 (app1, '0', '|| ( foo/app6 foo/app7 ) !foo/app3', '1413309350'),
273 (app7, '0', '', '1413309350'),
274 ])
275 installs, listed, num_updates = self.scanner.Run(
276 self.device, '/', ['app1'], True, True, True)
277 self.ValidatePkgs(installs, [app1, app7], constraints=[(app1, app7)])
278 self.ValidatePkgs(listed, [app1])
279 self.assertEquals(num_updates, 1)
Ralph Nathane01ccf12015-04-16 10:40:32 -0700280
281
282class TestDeploy(cros_test_lib.ProgressBarTestCase):
283 """Test deploy.Deploy."""
284
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700285 @staticmethod
286 def FakeGetPackagesByCPV(cpvs, _strip, _sysroot):
287 return ['/path/to/%s.tbz2' % cpv.pv for cpv in cpvs]
288
Ralph Nathane01ccf12015-04-16 10:40:32 -0700289 def setUp(self):
290 self.PatchObject(remote_access, 'ChromiumOSDeviceHandler',
291 side_effect=ChromiumOSDeviceHandlerFake)
292 self.PatchObject(cros_build_lib, 'GetBoard', return_value=None)
293 self.PatchObject(cros_build_lib, 'GetSysroot', return_value='sysroot')
294 self.package_scanner = self.PatchObject(deploy, '_InstallPackageScanner')
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700295 self.get_packages_paths = self.PatchObject(
296 deploy, '_GetPackagesByCPV', side_effect=self.FakeGetPackagesByCPV)
Ralph Nathane01ccf12015-04-16 10:40:32 -0700297 self.emerge = self.PatchObject(deploy, '_Emerge', return_value=None)
298 self.unmerge = self.PatchObject(deploy, '_Unmerge', return_value=None)
299
300 def testDeployEmerge(self):
301 """Test that deploy._Emerge is called for each package."""
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700302
303 _BINPKG = '/path/to/bar-1.2.5.tbz2'
304 def FakeIsFile(fname):
305 return fname == _BINPKG
306
307 packages = ['some/foo-1.2.3', _BINPKG, 'some/foobar-2.0']
Ralph Nathane01ccf12015-04-16 10:40:32 -0700308 self.package_scanner.return_value = PackageScannerFake(packages)
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700309 self.PatchObject(os.path, 'isfile', side_effect=FakeIsFile)
Ralph Nathane01ccf12015-04-16 10:40:32 -0700310
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700311 deploy.Deploy(None, ['package'], force=True, clean_binpkg=False)
Ralph Nathane01ccf12015-04-16 10:40:32 -0700312
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700313 # Check that package names were correctly resolved into binary packages.
314 self.get_packages_paths.assert_called_once_with(
315 [portage_util.SplitCPV(p) for p in packages if p != _BINPKG],
316 True, 'sysroot')
Ralph Nathane01ccf12015-04-16 10:40:32 -0700317 # Check that deploy._Emerge is called the right number of times.
318 self.assertEqual(self.emerge.call_count, len(packages))
319 self.assertEqual(self.unmerge.call_count, 0)
320
321 def testDeployUnmerge(self):
322 """Test that deploy._Unmerge is called for each package."""
323 packages = ['foo', 'bar', 'foobar']
324 self.package_scanner.return_value = PackageScannerFake(packages)
325
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700326 deploy.Deploy(None, ['package'], force=True, clean_binpkg=False,
Ralph Nathane01ccf12015-04-16 10:40:32 -0700327 emerge=False)
328
329 # Check that deploy._Unmerge is called the right number of times.
330 self.assertEqual(self.emerge.call_count, 0)
331 self.assertEqual(self.unmerge.call_count, len(packages))
332
333 def testDeployMergeWithProgressBar(self):
334 """Test that BrilloDeployOperation.Run() is called for merge."""
335 packages = ['foo', 'bar', 'foobar']
336 self.package_scanner.return_value = PackageScannerFake(packages)
337
338 run = self.PatchObject(deploy.BrilloDeployOperation, 'Run',
339 return_value=None)
340
341 self.PatchObject(command, 'UseProgressBar', return_value=True)
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700342 deploy.Deploy(None, ['package'], force=True, clean_binpkg=False)
Ralph Nathane01ccf12015-04-16 10:40:32 -0700343
344 # Check that BrilloDeployOperation.Run was called.
345 self.assertTrue(run.called)
346
347 def testDeployUnmergeWithProgressBar(self):
348 """Test that BrilloDeployOperation.Run() is called for unmerge."""
349 packages = ['foo', 'bar', 'foobar']
350 self.package_scanner.return_value = PackageScannerFake(packages)
351
352 run = self.PatchObject(deploy.BrilloDeployOperation, 'Run',
353 return_value=None)
354
355 self.PatchObject(command, 'UseProgressBar', return_value=True)
Gilad Arnold0e1b1da2015-06-10 06:41:05 -0700356 deploy.Deploy(None, ['package'], force=True, clean_binpkg=False,
Ralph Nathane01ccf12015-04-16 10:40:32 -0700357 emerge=False)
358
359 # Check that BrilloDeployOperation.Run was called.
360 self.assertTrue(run.called)
361
362 def testBrilloDeployMergeOperation(self):
363 """Test that BrilloDeployOperation works for merge."""
364 def func(queue):
Ralph Nathan90475a12015-05-20 13:19:01 -0700365 for event in op.MERGE_EVENTS:
Ralph Nathane01ccf12015-04-16 10:40:32 -0700366 queue.get()
367 print(event)
368
369 queue = multiprocessing.Queue()
370 # Emerge one package.
371 op = BrilloDeployOperationFake(1, True, queue)
372
373 with self.OutputCapturer():
374 op.Run(func, queue)
375
376 # Check that the progress bar prints correctly.
Ralph Nathan90475a12015-05-20 13:19:01 -0700377 self.AssertProgressBarAllEvents(len(op.MERGE_EVENTS))
Ralph Nathane01ccf12015-04-16 10:40:32 -0700378
379 def testBrilloDeployUnmergeOperation(self):
380 """Test that BrilloDeployOperation works for unmerge."""
381 def func(queue):
Ralph Nathan90475a12015-05-20 13:19:01 -0700382 for event in op.UNMERGE_EVENTS:
Ralph Nathane01ccf12015-04-16 10:40:32 -0700383 queue.get()
384 print(event)
385
386 queue = multiprocessing.Queue()
387 # Unmerge one package.
388 op = BrilloDeployOperationFake(1, False, queue)
389
390 with self.OutputCapturer():
391 op.Run(func, queue)
392
393 # Check that the progress bar prints correctly.
Ralph Nathan90475a12015-05-20 13:19:01 -0700394 self.AssertProgressBarAllEvents(len(op.UNMERGE_EVENTS))