blob: 503cc99d09505e50130b10e9c87a253bade7626c [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
David James8c846492011-01-25 17:07:29 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Mike Frysingerc6824f62014-02-03 11:09:44 -05005"""Unittests for upload_prebuilts.py."""
6
David James8c846492011-01-25 17:07:29 -08007import copy
David James8fa34ea2011-04-15 13:00:20 -07008import multiprocessing
Mike Frysinger40ffb532021-02-12 07:36:08 -05009import os
Greg Edelston325e4412023-05-04 16:22:10 -060010from typing import List
Mike Frysinger166fea02021-02-12 05:30:33 -050011from unittest import mock
Chris Sosa471532a2011-02-01 15:10:06 -080012
Greg Edelston325e4412023-05-04 16:22:10 -060013import pytest
14
Mike Frysinger40ffb532021-02-12 07:36:08 -050015from chromite.lib import binpkg
Brian Harringc92788f2012-09-21 18:07:15 -070016from chromite.lib import cros_test_lib
David James2c7ccb42012-11-04 15:34:28 -080017from chromite.lib import gs
Zdenek Behanc0e18762012-09-22 04:06:17 +020018from chromite.lib import osutils
Mike Frysinger36870f92015-04-12 02:59:55 -040019from chromite.lib import parallel_unittest
Prathmesh Prabhu421eef22014-10-16 17:13:19 -070020from chromite.lib import portage_util
Mike Frysinger40ffb532021-02-12 07:36:08 -050021from chromite.scripts import upload_prebuilts as prebuilt
David James8c846492011-01-25 17:07:29 -080022
Mike Frysinger68182472014-11-05 22:38:39 -050023
Mike Frysinger27e21b72018-07-12 14:20:21 -040024# pylint: disable=protected-access
25
26
Alex Klein1699fab2022-09-08 08:46:06 -060027PUBLIC_PACKAGES = [
28 {"CPV": "gtk+/public1", "SHA1": "1", "MTIME": "1"},
29 {"CPV": "gtk+/public2", "SHA1": "2", "PATH": "gtk+/foo.tgz", "MTIME": "2"},
30]
31PRIVATE_PACKAGES = [{"CPV": "private", "SHA1": "3", "MTIME": "3"}]
David James8c846492011-01-25 17:07:29 -080032
33
34def SimplePackageIndex(header=True, packages=True):
Alex Klein1699fab2022-09-08 08:46:06 -060035 pkgindex = binpkg.PackageIndex()
36 if header:
37 pkgindex.header["URI"] = "gs://example"
38 if packages:
39 pkgindex.packages = copy.deepcopy(PUBLIC_PACKAGES + PRIVATE_PACKAGES)
40 return pkgindex
David James8c846492011-01-25 17:07:29 -080041
42
Mike Frysinger68182472014-11-05 22:38:39 -050043class TestPrebuilt(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060044 """Tests for Prebuilt logic."""
David James8c846492011-01-25 17:07:29 -080045
Alex Klein1699fab2022-09-08 08:46:06 -060046 def setUp(self):
47 self._base_local_path = "/b/cbuild/build/chroot/build/x86-dogfood/"
48 self._gs_bucket_path = "gs://chromeos-prebuilt/host/version"
49 self._local_path = os.path.join(self._base_local_path, "public1.tbz2")
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -080050
Alex Klein1699fab2022-09-08 08:46:06 -060051 def testGenerateUploadDict(self):
52 self.PatchObject(prebuilt.os.path, "exists", return_true=True)
53 pkgs = [{"CPV": "public1"}]
54 result = prebuilt.GenerateUploadDict(
55 self._base_local_path, self._gs_bucket_path, pkgs
56 )
57 expected = {
58 self._local_path: self._gs_bucket_path + "/public1.tbz2",
59 }
60 self.assertEqual(result, expected)
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -080061
Alex Klein1699fab2022-09-08 08:46:06 -060062 def testGenerateUploadDictWithDebug(self):
63 self.PatchObject(prebuilt.os.path, "exists", return_true=True)
64 pkgs = [{"CPV": "public1", "DEBUG_SYMBOLS": "yes"}]
65 result = prebuilt.GenerateUploadDict(
66 self._base_local_path, self._gs_bucket_path, pkgs
67 )
68 expected = {
69 self._local_path: self._gs_bucket_path + "/public1.tbz2",
70 self._local_path.replace(
71 ".tbz2", ".debug.tbz2"
72 ): self._gs_bucket_path
73 + "/public1.debug.tbz2",
74 }
75 self.assertEqual(result, expected)
David James8c846492011-01-25 17:07:29 -080076
Alex Klein1699fab2022-09-08 08:46:06 -060077 def testDeterminePrebuiltConfHost(self):
78 """Test that the host prebuilt path comes back properly."""
79 expected_path = os.path.join(prebuilt._PREBUILT_MAKE_CONF["amd64"])
80 self.assertEqual(
81 prebuilt.DeterminePrebuiltConfFile("fake_path", "amd64"),
82 expected_path,
83 )
David James8c846492011-01-25 17:07:29 -080084
David James8c846492011-01-25 17:07:29 -080085
David James2c7ccb42012-11-04 15:34:28 -080086class TestPkgIndex(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060087 """Helper for tests that update the Packages index file."""
David James2c7ccb42012-11-04 15:34:28 -080088
Alex Klein1699fab2022-09-08 08:46:06 -060089 def setUp(self):
90 self.db = {}
91 self.pkgindex = SimplePackageIndex()
92 self.empty = SimplePackageIndex(packages=False)
David James2c7ccb42012-11-04 15:34:28 -080093
Alex Klein1699fab2022-09-08 08:46:06 -060094 def assertURIs(self, uris):
95 """Verify that the duplicate DB has the specified URLs."""
96 expected = [v.uri for _, v in sorted(self.db.items())]
97 self.assertEqual(expected, uris)
David James2c7ccb42012-11-04 15:34:28 -080098
99
100class TestPackagesFileFiltering(TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600101 """Tests for Packages filtering behavior."""
David James8c846492011-01-25 17:07:29 -0800102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 def testFilterPkgIndex(self):
104 """Test filtering out of private packages."""
105 self.pkgindex.RemoveFilteredPackages(
106 lambda pkg: pkg in PRIVATE_PACKAGES
107 )
108 self.assertEqual(self.pkgindex.packages, PUBLIC_PACKAGES)
109 self.assertEqual(self.pkgindex.modified, True)
David James8c846492011-01-25 17:07:29 -0800110
111
David James2c7ccb42012-11-04 15:34:28 -0800112class TestPopulateDuplicateDB(TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600113 """Tests for the _PopulateDuplicateDB function."""
David James8c846492011-01-25 17:07:29 -0800114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 def testEmptyIndex(self):
116 """Test population of the duplicate DB with an empty index."""
117 self.empty._PopulateDuplicateDB(self.db, 0)
118 self.assertEqual(self.db, {})
David James8c846492011-01-25 17:07:29 -0800119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 def testNormalIndex(self):
121 """Test population of the duplicate DB with a full index."""
122 self.pkgindex._PopulateDuplicateDB(self.db, 0)
123 self.assertURIs(
124 [
125 "gs://example/gtk+/public1.tbz2",
126 "gs://example/gtk+/foo.tgz",
127 "gs://example/private.tbz2",
128 ]
129 )
David James8c846492011-01-25 17:07:29 -0800130
Alex Klein1699fab2022-09-08 08:46:06 -0600131 def testMissingSHA1(self):
132 """Test population of the duplicate DB with a missing SHA1."""
133 del self.pkgindex.packages[0]["SHA1"]
134 self.pkgindex._PopulateDuplicateDB(self.db, 0)
135 self.assertURIs(
136 ["gs://example/gtk+/foo.tgz", "gs://example/private.tbz2"]
137 )
David James8c846492011-01-25 17:07:29 -0800138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 def testFailedPopulate(self):
140 """Test failure conditions for the populate method."""
141 headerless = SimplePackageIndex(header=False)
142 self.assertRaises(KeyError, headerless._PopulateDuplicateDB, self.db, 0)
143 del self.pkgindex.packages[0]["CPV"]
144 self.assertRaises(
145 KeyError, self.pkgindex._PopulateDuplicateDB, self.db, 0
146 )
David James8c846492011-01-25 17:07:29 -0800147
148
Mike Frysinger68182472014-11-05 22:38:39 -0500149class TestResolveDuplicateUploads(cros_test_lib.MockTestCase, TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600150 """Tests for the ResolveDuplicateUploads function."""
David James8c846492011-01-25 17:07:29 -0800151
Alex Klein1699fab2022-09-08 08:46:06 -0600152 def setUp(self):
153 self.PatchObject(binpkg.time, "time", return_value=binpkg.TWO_WEEKS)
154 self.db = {}
155 self.dup = SimplePackageIndex()
156 self.expected_pkgindex = SimplePackageIndex()
David James2c7ccb42012-11-04 15:34:28 -0800157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 def assertNoDuplicates(self, candidates):
159 """Verify no duplicates are found with the specified candidates."""
160 uploads = self.pkgindex.ResolveDuplicateUploads(candidates)
161 self.assertEqual(uploads, self.pkgindex.packages)
162 self.assertEqual(
163 len(self.pkgindex.packages), len(self.expected_pkgindex.packages)
164 )
165 for pkg1, pkg2 in zip(
166 self.pkgindex.packages, self.expected_pkgindex.packages
167 ):
168 self.assertNotEqual(pkg1["MTIME"], pkg2["MTIME"])
169 del pkg1["MTIME"]
170 del pkg2["MTIME"]
171 self.assertEqual(self.pkgindex.modified, False)
172 self.assertEqual(
173 self.pkgindex.packages, self.expected_pkgindex.packages
174 )
David James2c7ccb42012-11-04 15:34:28 -0800175
Alex Klein1699fab2022-09-08 08:46:06 -0600176 def assertAllDuplicates(self, candidates):
177 """Verify every package is a duplicate in the specified list."""
178 for pkg in self.expected_pkgindex.packages:
179 pkg.setdefault("PATH", pkg["CPV"] + ".tbz2")
180 self.pkgindex.ResolveDuplicateUploads(candidates)
181 self.assertEqual(
182 self.pkgindex.packages, self.expected_pkgindex.packages
183 )
David James615e5b52011-06-03 11:10:15 -0700184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 def testEmptyList(self):
186 """If no candidates are supplied, no duplicates should be found."""
187 self.assertNoDuplicates([])
David James8c846492011-01-25 17:07:29 -0800188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 def testEmptyIndex(self):
190 """If no packages are supplied, no duplicates should be found."""
191 self.assertNoDuplicates([self.empty])
David James8c846492011-01-25 17:07:29 -0800192
Alex Klein1699fab2022-09-08 08:46:06 -0600193 def testDifferentURI(self):
194 """If the URI differs, no duplicates should be found."""
195 self.dup.header["URI"] = "gs://example2"
196 self.assertNoDuplicates([self.dup])
David James2c7ccb42012-11-04 15:34:28 -0800197
Alex Klein1699fab2022-09-08 08:46:06 -0600198 def testUpdateModificationTime(self):
199 """When duplicates are found, we should use the latest mtime."""
200 for pkg in self.expected_pkgindex.packages:
201 pkg["MTIME"] = "10"
202 for pkg in self.dup.packages:
203 pkg["MTIME"] = "4"
204 self.assertAllDuplicates([self.expected_pkgindex, self.dup])
David James2c7ccb42012-11-04 15:34:28 -0800205
Alex Klein1699fab2022-09-08 08:46:06 -0600206 def testCanonicalUrl(self):
207 """If the URL is in a different format, we should still find duplicates."""
208 self.dup.header["URI"] = gs.PUBLIC_BASE_HTTPS_URL + "example"
209 self.assertAllDuplicates([self.dup])
David James8c846492011-01-25 17:07:29 -0800210
Alex Klein1699fab2022-09-08 08:46:06 -0600211 def testMissingSHA1(self):
212 """We should not find duplicates if there is no SHA1."""
213 del self.pkgindex.packages[0]["SHA1"]
214 del self.expected_pkgindex.packages[0]["SHA1"]
215 for pkg in self.expected_pkgindex.packages[1:]:
216 pkg.setdefault("PATH", pkg["CPV"] + ".tbz2")
217 self.pkgindex.ResolveDuplicateUploads([self.dup])
218 self.assertNotEqual(
219 self.pkgindex.packages[0]["MTIME"],
220 self.expected_pkgindex.packages[0]["MTIME"],
221 )
222 del self.pkgindex.packages[0]["MTIME"]
223 del self.expected_pkgindex.packages[0]["MTIME"]
224 self.assertEqual(
225 self.pkgindex.packages, self.expected_pkgindex.packages
226 )
David James8c846492011-01-25 17:07:29 -0800227
Alex Klein1699fab2022-09-08 08:46:06 -0600228 def testSymbolsAvailable(self):
229 """If symbols are available remotely, re-use them and set DEBUG_SYMBOLS."""
230 self.dup.packages[0]["DEBUG_SYMBOLS"] = "yes"
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 uploads = self.pkgindex.ResolveDuplicateUploads([self.dup])
233 self.assertEqual(uploads, [])
234 self.assertEqual(self.pkgindex.packages[0].get("DEBUG_SYMBOLS"), "yes")
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800235
Alex Klein1699fab2022-09-08 08:46:06 -0600236 def testSymbolsAvailableLocallyOnly(self):
237 """If the symbols are only available locally, reupload them."""
238 self.pkgindex.packages[0]["DEBUG_SYMBOLS"] = "yes"
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 uploads = self.pkgindex.ResolveDuplicateUploads([self.dup])
241 self.assertEqual(uploads, [self.pkgindex.packages[0]])
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800242
David James8c846492011-01-25 17:07:29 -0800243
Mike Frysinger68182472014-11-05 22:38:39 -0500244class TestWritePackageIndex(cros_test_lib.MockTestCase, TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600245 """Tests for the WriteToNamedTemporaryFile function."""
David James8c846492011-01-25 17:07:29 -0800246
Alex Klein1699fab2022-09-08 08:46:06 -0600247 def testSimple(self):
248 """Test simple call of WriteToNamedTemporaryFile()"""
249 self.PatchObject(self.pkgindex, "Write")
250 f = self.pkgindex.WriteToNamedTemporaryFile()
251 self.assertEqual(f.read(), "")
David James8c846492011-01-25 17:07:29 -0800252
253
Mike Frysinger36870f92015-04-12 02:59:55 -0400254class TestUploadPrebuilt(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600255 """Tests for the _UploadPrebuilt function."""
David James05bcb2b2011-02-09 09:25:47 -0800256
Alex Klein1699fab2022-09-08 08:46:06 -0600257 def setUp(self):
258 class MockTemporaryFile(object):
259 """Mock out the temporary file logic."""
David James05bcb2b2011-02-09 09:25:47 -0800260
Alex Klein1699fab2022-09-08 08:46:06 -0600261 def __init__(self, name):
262 self.name = name
263
264 self.pkgindex = SimplePackageIndex()
265 self.PatchObject(
266 binpkg, "GrabLocalPackageIndex", return_value=self.pkgindex
267 )
268 self.PatchObject(
269 self.pkgindex,
270 "ResolveDuplicateUploads",
271 return_value=PRIVATE_PACKAGES,
272 )
273 self.PatchObject(
274 self.pkgindex,
275 "WriteToNamedTemporaryFile",
276 return_value=MockTemporaryFile("fake"),
277 )
278 self.remote_up_mock = self.PatchObject(prebuilt, "RemoteUpload")
279 self.gs_up_mock = self.PatchObject(prebuilt, "_GsUpload")
280
281 def testSuccessfulGsUpload(self):
282 uploads = {
283 os.path.join(self.tempdir, "private.tbz2"): "gs://foo/private.tbz2"
284 }
285 dev_extras = os.path.join(self.tempdir, "dev-only-extras.tar.xz")
286 osutils.Touch(dev_extras)
287 self.PatchObject(prebuilt, "GenerateUploadDict", return_value=uploads)
288 uploads = uploads.copy()
289 uploads["fake"] = "gs://foo/suffix/Packages"
290 uploads[dev_extras] = "gs://foo/suffix/dev-only-extras.tar.xz"
291 acl = "public-read"
292 uri = self.pkgindex.header["URI"]
293 uploader = prebuilt.PrebuiltUploader(
294 "gs://foo",
295 acl,
296 uri,
297 [],
298 "/",
299 [],
300 False,
301 "foo",
302 False,
303 "x86-foo",
304 [],
305 "",
Bob Haarmanc0082602022-09-20 16:12:43 -0700306 report={},
Alex Klein1699fab2022-09-08 08:46:06 -0600307 )
308 uploader._UploadPrebuilt(self.tempdir, "suffix")
309 self.remote_up_mock.assert_called_once_with(mock.ANY, acl, uploads)
310 self.assertTrue(self.gs_up_mock.called)
David James05bcb2b2011-02-09 09:25:47 -0800311
David James05bcb2b2011-02-09 09:25:47 -0800312
Greg Edelston64620d12023-02-23 15:29:49 -0700313class TestUpdateRemoteSdkLatestFile(cros_test_lib.MockTestCase):
314 """Tests for PrebuiltUploader._UpdateRemoteSdkLatestFile."""
315
316 def setUp(self):
317 self._write_file_patch = self.PatchObject(osutils, "WriteFile")
318 self.PatchObject(prebuilt.PrebuiltUploader, "_Upload")
319 self.PatchObject(
320 gs.GSContext,
321 "LoadKeyValueStore",
322 return_value={
323 "LATEST_SDK": "1000",
324 "LATEST_SDK_UPREV_TARGET": "2000",
325 },
326 )
327 self._uploader = prebuilt.PrebuiltUploader(
328 "gs://foo",
329 "public-read",
330 SimplePackageIndex().header["URI"],
331 [],
332 "/",
333 [],
334 False,
335 "foo",
336 False,
337 "x86-foo",
338 [],
339 "",
340 report={},
341 )
342
343 def testNoChanges(self):
344 self._uploader._UpdateRemoteSdkLatestFile()
345 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
346 "1000", "2000"
347 )
348 self._write_file_patch.assert_called_with(mock.ANY, expected)
349
350 def testChangeLatestSdk(self):
351 self._uploader._UpdateRemoteSdkLatestFile(latest_sdk="3000")
352 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
353 "3000", "2000"
354 )
355 self._write_file_patch.assert_called_with(mock.ANY, expected)
356
357 def testChangeLatestUprevTarget(self):
358 self._uploader._UpdateRemoteSdkLatestFile(
359 latest_sdk_uprev_target="4000"
360 )
361 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
362 "1000", "4000"
363 )
364 self._write_file_patch.assert_called_with(mock.ANY, expected)
365
366 def testChangeBoth(self):
367 self._uploader._UpdateRemoteSdkLatestFile(
368 latest_sdk="3000", latest_sdk_uprev_target="4000"
369 )
370 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
371 "3000", "4000"
372 )
373 self._write_file_patch.assert_called_with(mock.ANY, expected)
374
375
Mike Frysinger36870f92015-04-12 02:59:55 -0400376class TestSyncPrebuilts(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600377 """Tests for the SyncHostPrebuilts function."""
David James05bcb2b2011-02-09 09:25:47 -0800378
Alex Klein1699fab2022-09-08 08:46:06 -0600379 def setUp(self):
Bob Haarmanc0082602022-09-20 16:12:43 -0700380 clnum = [1]
381
382 def mock_rev(_filename, _data, report, *_args, **_kwargs):
383 report.setdefault("created_cls", []).append(
384 f"https://crrev.com/unittest/{clnum[0]}"
385 )
386 clnum[0] += 1
387
Alex Klein1699fab2022-09-08 08:46:06 -0600388 self.rev_mock = self.PatchObject(
Greg Edelston86aea7b2023-02-15 16:50:25 -0700389 binpkg,
390 "UpdateAndSubmitKeyValueFile",
Bob Haarmanc0082602022-09-20 16:12:43 -0700391 side_effect=mock_rev,
Alex Klein1699fab2022-09-08 08:46:06 -0600392 )
393 self.update_binhost_mock = self.PatchObject(
394 prebuilt, "UpdateBinhostConfFile", return_value=None
395 )
396 self.build_path = "/trunk"
397 self.upload_location = "gs://upload/"
398 self.version = "1"
399 self.binhost = "http://prebuilt/"
400 self.key = "PORTAGE_BINHOST"
401 self.upload_mock = self.PatchObject(
402 prebuilt.PrebuiltUploader, "_UploadPrebuilt", return_value=True
403 )
David James05bcb2b2011-02-09 09:25:47 -0800404
Alex Klein1699fab2022-09-08 08:46:06 -0600405 def _testSyncHostPrebuilts(self, chroot):
406 board = "x86-foo"
407 target = prebuilt.BuildTarget(board, "aura")
408 slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
Bob Haarmanc0082602022-09-20 16:12:43 -0700409 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600410 if chroot is None:
411 package_path = os.path.join(
412 self.build_path, "chroot", prebuilt._HOST_PACKAGES_PATH
413 )
414 else:
415 package_path = os.path.join(chroot, prebuilt._HOST_PACKAGES_PATH)
416 url_suffix = prebuilt._REL_HOST_PATH % {
417 "version": self.version,
418 "host_arch": prebuilt._HOST_ARCH,
419 "target": target,
420 }
421 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
422 url_value = "%s/%s/" % (
423 self.binhost.rstrip("/"),
424 packages_url_suffix.rstrip("/"),
425 )
426 urls = [url_value.replace("foo", "bar"), url_value]
427 binhost = " ".join(urls)
428 uploader = prebuilt.PrebuiltUploader(
429 self.upload_location,
430 "public-read",
431 self.binhost,
432 [],
433 self.build_path,
434 [],
435 False,
436 "foo",
437 False,
438 target,
439 slave_targets,
440 self.version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700441 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600442 chroot=chroot,
443 )
444 uploader.SyncHostPrebuilts(self.key, True, True)
Bob Haarmanc0082602022-09-20 16:12:43 -0700445 self.assertEqual(
446 report,
447 {
448 "created_cls": ["https://crrev.com/unittest/1"],
449 },
450 )
Alex Klein1699fab2022-09-08 08:46:06 -0600451 self.upload_mock.assert_called_once_with(
452 package_path, packages_url_suffix
453 )
454 self.rev_mock.assert_called_once_with(
Bob Haarmanc0082602022-09-20 16:12:43 -0700455 mock.ANY, {self.key: binhost}, report, dryrun=False
Alex Klein1699fab2022-09-08 08:46:06 -0600456 )
457 self.update_binhost_mock.assert_called_once_with(
458 mock.ANY, self.key, binhost
459 )
David James05bcb2b2011-02-09 09:25:47 -0800460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 def testSyncHostPrebuilts(self):
462 self._testSyncHostPrebuilts(chroot=None)
Bob Haarmand1225ea2022-01-19 22:01:42 +0000463
Alex Klein1699fab2022-09-08 08:46:06 -0600464 def testSyncHostPrebuiltsWithChroot(self):
465 self._testSyncHostPrebuilts("/test/chroot")
Bob Haarmand1225ea2022-01-19 22:01:42 +0000466
Alex Klein1699fab2022-09-08 08:46:06 -0600467 def testSyncBoardPrebuilts(self):
468 board = "x86-foo"
469 target = prebuilt.BuildTarget(board, "aura")
470 slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
471 board_path = os.path.join(
472 self.build_path, prebuilt._BOARD_PATH % {"board": board}
473 )
474 package_path = os.path.join(board_path, "packages")
475 url_suffix = prebuilt._REL_BOARD_PATH % {
476 "version": self.version,
477 "target": target,
478 }
479 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
480 url_value = "%s/%s/" % (
481 self.binhost.rstrip("/"),
482 packages_url_suffix.rstrip("/"),
483 )
484 bar_binhost = url_value.replace("foo", "bar")
485 determine_mock = self.PatchObject(
486 prebuilt, "DeterminePrebuiltConfFile", side_effect=("bar", "foo")
487 )
488 self.PatchObject(prebuilt.PrebuiltUploader, "_UploadSdkTarball")
Bob Haarmanc0082602022-09-20 16:12:43 -0700489 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600490 with parallel_unittest.ParallelMock():
491 multiprocessing.Process.exitcode = 0
492 uploader = prebuilt.PrebuiltUploader(
493 self.upload_location,
494 "public-read",
495 self.binhost,
496 [],
497 self.build_path,
498 [],
499 False,
500 "foo",
501 False,
502 target,
503 slave_targets,
504 self.version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700505 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600506 )
507 uploader.SyncBoardPrebuilts(
Greg Edelston8da51ce2023-03-22 10:40:59 -0600508 self.key, True, True, True, None, None, None, None, None, True
Alex Klein1699fab2022-09-08 08:46:06 -0600509 )
510 determine_mock.assert_has_calls(
511 [
512 mock.call(self.build_path, slave_targets[0]),
513 mock.call(self.build_path, target),
514 ]
515 )
516 self.upload_mock.assert_called_once_with(
517 package_path, packages_url_suffix
518 )
519 self.rev_mock.assert_has_calls(
520 [
Bob Haarmanc0082602022-09-20 16:12:43 -0700521 mock.call(
522 "bar",
523 {self.key: bar_binhost},
524 {
525 "created_cls": [
526 "https://crrev.com/unittest/1",
527 "https://crrev.com/unittest/2",
528 ],
529 },
530 dryrun=False,
531 ),
532 mock.call(
533 "foo",
534 {self.key: url_value},
535 {
536 "created_cls": [
537 "https://crrev.com/unittest/1",
538 "https://crrev.com/unittest/2",
539 ],
540 },
541 dryrun=False,
542 ),
Alex Klein1699fab2022-09-08 08:46:06 -0600543 ]
544 )
545 self.update_binhost_mock.assert_has_calls(
546 [
547 mock.call(mock.ANY, self.key, bar_binhost),
548 mock.call(mock.ANY, self.key, url_value),
549 ]
550 )
David James05bcb2b2011-02-09 09:25:47 -0800551
552
Mike Frysinger36870f92015-04-12 02:59:55 -0400553class TestMain(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600554 """Tests for the main() function."""
David Jamesc0f158a2011-02-22 16:07:29 -0800555
Alex Klein1699fab2022-09-08 08:46:06 -0600556 def testMain(self):
557 """Test that the main function works."""
Bob Haarman35460c22022-11-08 00:11:59 +0000558 # Use a real object as returned from ParseOptions as a spec for
559 # the mock options object, so that we don't have any properties
560 # that the real object doesn't have.
561 options_spec, _ = prebuilt.ParseOptions(
562 [
563 "--dry-run",
564 "--build-path",
565 "/trunk",
566 "-u",
567 "gs://upload",
568 ]
569 )
570 options = mock.MagicMock(spec=options_spec)
Alex Klein1699fab2022-09-08 08:46:06 -0600571 old_binhost = "http://prebuilt/1"
572 options.previous_binhost_url = [old_binhost]
573 options.board = "x86-foo"
574 options.profile = None
575 target = prebuilt.BuildTarget(options.board, options.profile)
576 options.build_path = "/trunk"
577 options.chroot = None
578 options.dryrun = False
579 options.private = True
580 options.packages = []
581 options.sync_host = True
582 options.git_sync = True
Greg Edelston8da51ce2023-03-22 10:40:59 -0600583 options.sync_remote_latest_sdk_file = True
Alex Klein1699fab2022-09-08 08:46:06 -0600584 options.upload_board_tarball = True
585 options.prepackaged_tarball = None
586 options.toolchains_overlay_tarballs = []
587 options.toolchains_overlay_upload_path = ""
588 options.toolchain_tarballs = []
589 options.toolchain_upload_path = ""
590 options.upload = "gs://upload/"
591 options.binhost_base_url = options.upload
592 options.prepend_version = True
593 options.set_version = None
594 options.skip_upload = False
595 options.filters = True
596 options.key = "PORTAGE_BINHOST"
597 options.binhost_conf_dir = None
598 options.sync_binhost_conf = True
599 options.slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
600 self.PatchObject(
601 prebuilt, "ParseOptions", return_value=tuple([options, target])
602 )
603 self.PatchObject(binpkg, "GrabRemotePackageIndex", return_value=True)
604 init_mock = self.PatchObject(
605 prebuilt.PrebuiltUploader, "__init__", return_value=None
606 )
607 expected_gs_acl_path = os.path.join(
608 "/fake_path", prebuilt._GOOGLESTORAGE_GSUTIL_FILE
609 )
610 self.PatchObject(
611 portage_util, "FindOverlayFile", return_value=expected_gs_acl_path
612 )
613 host_mock = self.PatchObject(
614 prebuilt.PrebuiltUploader, "SyncHostPrebuilts", return_value=None
615 )
616 board_mock = self.PatchObject(
617 prebuilt.PrebuiltUploader, "SyncBoardPrebuilts", return_value=None
618 )
Mike Frysinger36870f92015-04-12 02:59:55 -0400619
Alex Klein1699fab2022-09-08 08:46:06 -0600620 prebuilt.main([])
Mike Frysinger36870f92015-04-12 02:59:55 -0400621
Alex Klein1699fab2022-09-08 08:46:06 -0600622 init_mock.assert_called_once_with(
623 options.upload,
624 expected_gs_acl_path,
625 options.upload,
626 mock.ANY,
627 options.build_path,
628 options.packages,
629 False,
630 None,
631 False,
632 target,
633 options.slave_targets,
634 mock.ANY,
Bob Haarmanc0082602022-09-20 16:12:43 -0700635 {},
Alex Klein1699fab2022-09-08 08:46:06 -0600636 None,
637 )
638 board_mock.assert_called_once_with(
639 options.key,
640 options.git_sync,
641 options.sync_binhost_conf,
642 options.upload_board_tarball,
643 None,
644 [],
645 "",
646 [],
647 "",
Greg Edelston8da51ce2023-03-22 10:40:59 -0600648 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600649 )
650 host_mock.assert_called_once_with(
651 options.key, options.git_sync, options.sync_binhost_conf
652 )
David Jamesc0f158a2011-02-22 16:07:29 -0800653
Mike Frysinger9e979b92012-11-29 02:55:09 -0500654
Mike Frysinger68182472014-11-05 22:38:39 -0500655class TestSdk(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600656 """Test logic related to uploading SDK binaries"""
Mike Frysinger9e979b92012-11-29 02:55:09 -0500657
Alex Klein1699fab2022-09-08 08:46:06 -0600658 def setUp(self):
659 self.PatchObject(
660 prebuilt,
661 "_GsUpload",
662 side_effect=Exception("should not get called"),
663 )
664 self.PatchObject(
665 prebuilt,
666 "UpdateBinhostConfFile",
667 side_effect=Exception("should not get called"),
668 )
Greg Edelston64620d12023-02-23 15:29:49 -0700669 self.PatchObject(
670 gs.GSContext,
671 "LoadKeyValueStore",
672 return_value={
673 "LATEST_SDK": "1000",
674 "LATEST_SDK_UPREV_TARGET": "2000",
675 },
676 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700677 self.write_file_mock = self.PatchObject(osutils, "WriteFile")
Alex Klein1699fab2022-09-08 08:46:06 -0600678 self.upload_mock = self.PatchObject(
679 prebuilt.PrebuiltUploader, "_Upload"
680 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500681
Alex Klein1699fab2022-09-08 08:46:06 -0600682 self.acl = "magic-acl"
Mike Frysinger9e979b92012-11-29 02:55:09 -0500683
Alex Klein1699fab2022-09-08 08:46:06 -0600684 # All these args pretty much get ignored. Whee.
685 self.uploader = prebuilt.PrebuiltUploader(
686 "gs://foo",
687 self.acl,
688 "prebuilt",
689 [],
690 "/",
691 [],
692 False,
693 "foo",
694 False,
695 "x86-foo",
696 [],
697 "chroot-1234",
Bob Haarmanc0082602022-09-20 16:12:43 -0700698 report={},
Alex Klein1699fab2022-09-08 08:46:06 -0600699 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500700
Alex Klein1699fab2022-09-08 08:46:06 -0600701 def testSdkUpload(
702 self,
703 to_tarballs=(),
704 to_upload_path=None,
705 tc_tarballs=(),
706 tc_upload_path=None,
707 ):
708 """Make sure we can upload just an SDK tarball"""
709 tar = "sdk.tar.xz"
710 ver = "1234"
711 vtar = "cros-sdk-%s.tar.xz" % ver
Mike Frysinger9e979b92012-11-29 02:55:09 -0500712
Greg Edelstonf3916f92023-02-22 15:49:38 -0700713 upload_calls = [
Alex Klein1699fab2022-09-08 08:46:06 -0600714 mock.call(
715 "%s.Manifest" % tar, "gs://chromiumos-sdk/%s.Manifest" % vtar
716 ),
717 mock.call(tar, "gs://chromiumos-sdk/%s" % vtar),
718 ]
719 for to in to_tarballs:
720 to = to.split(":")
Greg Edelstonf3916f92023-02-22 15:49:38 -0700721 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600722 mock.call(
723 to[1],
724 ("gs://chromiumos-sdk/" + to_upload_path)
725 % {"toolchains": to[0]},
726 )
727 )
728 for tc in tc_tarballs:
729 tc = tc.split(":")
Greg Edelstonf3916f92023-02-22 15:49:38 -0700730 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600731 mock.call(
732 tc[1],
733 ("gs://chromiumos-sdk/" + tc_upload_path)
734 % {"target": tc[0]},
735 )
736 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700737 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600738 mock.call(mock.ANY, "gs://chromiumos-sdk/cros-sdk-latest.conf")
739 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500740
Alex Klein1699fab2022-09-08 08:46:06 -0600741 self.uploader._UploadSdkTarball(
742 "amd64-host",
743 "",
744 tar,
745 to_tarballs,
746 to_upload_path,
747 tc_tarballs,
748 tc_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600749 True,
Alex Klein1699fab2022-09-08 08:46:06 -0600750 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700751 self.upload_mock.assert_has_calls(upload_calls)
752
753 expected_latest_file_contents = f"""\
754# The most recent SDK that is tested and ready for use.
755LATEST_SDK="{ver}"
756
757# The most recently built version. New uprev attempts should target this.
758# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700759LATEST_SDK_UPREV_TARGET=\"2000\""""
Greg Edelstonf3916f92023-02-22 15:49:38 -0700760 self.write_file_mock.assert_any_call(
761 mock.ANY, expected_latest_file_contents
762 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500763
Alex Klein1699fab2022-09-08 08:46:06 -0600764 def testBoardOverlayTarballUpload(self):
765 """Make sure processing of board-specific overlay tarballs works."""
766 to_tarballs = (
767 (
768 "i686-pc-linux-gnu:"
769 "/some/path/built-sdk-overlay-toolchains-i686-pc-linux-gnu.tar.xz"
770 ),
771 (
772 "armv7a-cros-linux-gnueabi-arm-none-eabi:"
773 "/some/path/built-sdk-overlay-toolchains-armv7a-cros-linux-gnueabi-"
774 "arm-none-eabi"
775 ),
776 )
777 to_upload_path = "1994/04/cros-sdk-overlay-toolchains-%(toolchains)s-1994.04.02.tar.xz"
778 self.testSdkUpload(
779 to_tarballs=to_tarballs, to_upload_path=to_upload_path
780 )
Gilad Arnoldad333182015-05-27 15:50:41 -0700781
Alex Klein1699fab2022-09-08 08:46:06 -0600782 def testToolchainTarballUpload(self):
783 """Make sure processing of toolchain tarballs works."""
784 tc_tarballs = (
785 "i686:/some/i686.tar.xz",
786 "arm-none:/some/arm.tar.xz",
787 )
788 tc_upload_path = "1994/04/%(target)s-1994.04.02.tar.xz"
789 self.testSdkUpload(
790 tc_tarballs=tc_tarballs, tc_upload_path=tc_upload_path
791 )
Greg Edelston325e4412023-05-04 16:22:10 -0600792
793
794@pytest.mark.parametrize(
795 "extra_args,expected_sync",
796 [
797 ([], True),
798 (["--sync-remote-latest-sdk-file"], True),
799 (["--no-sync-remote-latest-sdk-file"], False),
800 (
801 [
802 "--no-sync-remote-latest-sdk-file",
803 "--sync-remote-latest-sdk-file",
804 ],
805 True,
806 ),
807 (
808 [
809 "--sync-remote-latest-sdk-file",
810 "--no-sync-remote-latest-sdk-file",
811 ],
812 False,
813 ),
814 ],
815)
816def test_parse_options_sync_remote_latest_file(
817 extra_args: List[str],
818 expected_sync: bool,
819):
820 """Test --sync-remote-latest-file and --no-sync-remote-latest-file.
821
822 Desired behavior:
823 * If either of those args is given, take the last one.
824 * If neither is given, default to True.
825
826 Args:
827 extra_args: Command-line args to pass into parse_options() besides the
828 bare minimum.
829 expected_sync: Whether options.sync_remote_latest_file should be True.
830 """
831 # Bare minimum args to run upload_prebuilts
832 args = ["--build-path", "/foo", "-u", "gs://foo/bar"]
833 args.extend(extra_args)
834 options, _ = prebuilt.ParseOptions(args)
835 assert options.sync_remote_latest_sdk_file == expected_sync