blob: ce699de143c757fe8d969b2110901849be992ab1 [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
Brian Norris74d1af92023-06-14 11:28:12 -070010from pathlib import Path
Greg Edelston325e4412023-05-04 16:22:10 -060011from typing import List
Mike Frysinger166fea02021-02-12 05:30:33 -050012from unittest import mock
Chris Sosa471532a2011-02-01 15:10:06 -080013
Greg Edelston325e4412023-05-04 16:22:10 -060014import pytest
15
Mike Frysinger40ffb532021-02-12 07:36:08 -050016from chromite.lib import binpkg
Brian Norris74d1af92023-06-14 11:28:12 -070017from chromite.lib import cros_build_lib
Brian Harringc92788f2012-09-21 18:07:15 -070018from chromite.lib import cros_test_lib
David James2c7ccb42012-11-04 15:34:28 -080019from chromite.lib import gs
Zdenek Behanc0e18762012-09-22 04:06:17 +020020from chromite.lib import osutils
Mike Frysinger36870f92015-04-12 02:59:55 -040021from chromite.lib import parallel_unittest
Brian Norris74d1af92023-06-14 11:28:12 -070022from chromite.lib import path_util
Prathmesh Prabhu421eef22014-10-16 17:13:19 -070023from chromite.lib import portage_util
Mike Frysinger40ffb532021-02-12 07:36:08 -050024from chromite.scripts import upload_prebuilts as prebuilt
David James8c846492011-01-25 17:07:29 -080025
Mike Frysinger68182472014-11-05 22:38:39 -050026
Mike Frysinger27e21b72018-07-12 14:20:21 -040027# pylint: disable=protected-access
28
29
Alex Klein1699fab2022-09-08 08:46:06 -060030PUBLIC_PACKAGES = [
31 {"CPV": "gtk+/public1", "SHA1": "1", "MTIME": "1"},
32 {"CPV": "gtk+/public2", "SHA1": "2", "PATH": "gtk+/foo.tgz", "MTIME": "2"},
33]
34PRIVATE_PACKAGES = [{"CPV": "private", "SHA1": "3", "MTIME": "3"}]
David James8c846492011-01-25 17:07:29 -080035
36
37def SimplePackageIndex(header=True, packages=True):
Alex Klein1699fab2022-09-08 08:46:06 -060038 pkgindex = binpkg.PackageIndex()
39 if header:
40 pkgindex.header["URI"] = "gs://example"
41 if packages:
42 pkgindex.packages = copy.deepcopy(PUBLIC_PACKAGES + PRIVATE_PACKAGES)
43 return pkgindex
David James8c846492011-01-25 17:07:29 -080044
45
Mike Frysinger68182472014-11-05 22:38:39 -050046class TestPrebuilt(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060047 """Tests for Prebuilt logic."""
David James8c846492011-01-25 17:07:29 -080048
Alex Klein1699fab2022-09-08 08:46:06 -060049 def setUp(self):
50 self._base_local_path = "/b/cbuild/build/chroot/build/x86-dogfood/"
51 self._gs_bucket_path = "gs://chromeos-prebuilt/host/version"
52 self._local_path = os.path.join(self._base_local_path, "public1.tbz2")
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -080053
Alex Klein1699fab2022-09-08 08:46:06 -060054 def testGenerateUploadDict(self):
55 self.PatchObject(prebuilt.os.path, "exists", return_true=True)
56 pkgs = [{"CPV": "public1"}]
57 result = prebuilt.GenerateUploadDict(
58 self._base_local_path, self._gs_bucket_path, pkgs
59 )
60 expected = {
61 self._local_path: self._gs_bucket_path + "/public1.tbz2",
62 }
63 self.assertEqual(result, expected)
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -080064
Alex Klein1699fab2022-09-08 08:46:06 -060065 def testGenerateUploadDictWithDebug(self):
66 self.PatchObject(prebuilt.os.path, "exists", return_true=True)
67 pkgs = [{"CPV": "public1", "DEBUG_SYMBOLS": "yes"}]
68 result = prebuilt.GenerateUploadDict(
69 self._base_local_path, self._gs_bucket_path, pkgs
70 )
71 expected = {
72 self._local_path: self._gs_bucket_path + "/public1.tbz2",
Trent Aptedc20bb6d2023-05-10 15:00:03 +100073 self._local_path.replace(".tbz2", ".debug.tbz2"): (
74 self._gs_bucket_path + "/public1.debug.tbz2"
75 ),
Alex Klein1699fab2022-09-08 08:46:06 -060076 }
77 self.assertEqual(result, expected)
David James8c846492011-01-25 17:07:29 -080078
Alex Klein1699fab2022-09-08 08:46:06 -060079 def testDeterminePrebuiltConfHost(self):
80 """Test that the host prebuilt path comes back properly."""
81 expected_path = os.path.join(prebuilt._PREBUILT_MAKE_CONF["amd64"])
82 self.assertEqual(
83 prebuilt.DeterminePrebuiltConfFile("fake_path", "amd64"),
84 expected_path,
85 )
David James8c846492011-01-25 17:07:29 -080086
David James8c846492011-01-25 17:07:29 -080087
David James2c7ccb42012-11-04 15:34:28 -080088class TestPkgIndex(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060089 """Helper for tests that update the Packages index file."""
David James2c7ccb42012-11-04 15:34:28 -080090
Alex Klein1699fab2022-09-08 08:46:06 -060091 def setUp(self):
92 self.db = {}
93 self.pkgindex = SimplePackageIndex()
94 self.empty = SimplePackageIndex(packages=False)
David James2c7ccb42012-11-04 15:34:28 -080095
Alex Klein1699fab2022-09-08 08:46:06 -060096 def assertURIs(self, uris):
97 """Verify that the duplicate DB has the specified URLs."""
98 expected = [v.uri for _, v in sorted(self.db.items())]
99 self.assertEqual(expected, uris)
David James2c7ccb42012-11-04 15:34:28 -0800100
101
102class TestPackagesFileFiltering(TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600103 """Tests for Packages filtering behavior."""
David James8c846492011-01-25 17:07:29 -0800104
Alex Klein1699fab2022-09-08 08:46:06 -0600105 def testFilterPkgIndex(self):
106 """Test filtering out of private packages."""
107 self.pkgindex.RemoveFilteredPackages(
108 lambda pkg: pkg in PRIVATE_PACKAGES
109 )
110 self.assertEqual(self.pkgindex.packages, PUBLIC_PACKAGES)
111 self.assertEqual(self.pkgindex.modified, True)
David James8c846492011-01-25 17:07:29 -0800112
113
David James2c7ccb42012-11-04 15:34:28 -0800114class TestPopulateDuplicateDB(TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600115 """Tests for the _PopulateDuplicateDB function."""
David James8c846492011-01-25 17:07:29 -0800116
Alex Klein1699fab2022-09-08 08:46:06 -0600117 def testEmptyIndex(self):
118 """Test population of the duplicate DB with an empty index."""
119 self.empty._PopulateDuplicateDB(self.db, 0)
120 self.assertEqual(self.db, {})
David James8c846492011-01-25 17:07:29 -0800121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 def testNormalIndex(self):
123 """Test population of the duplicate DB with a full index."""
124 self.pkgindex._PopulateDuplicateDB(self.db, 0)
125 self.assertURIs(
126 [
127 "gs://example/gtk+/public1.tbz2",
128 "gs://example/gtk+/foo.tgz",
129 "gs://example/private.tbz2",
130 ]
131 )
David James8c846492011-01-25 17:07:29 -0800132
Alex Klein1699fab2022-09-08 08:46:06 -0600133 def testMissingSHA1(self):
134 """Test population of the duplicate DB with a missing SHA1."""
135 del self.pkgindex.packages[0]["SHA1"]
136 self.pkgindex._PopulateDuplicateDB(self.db, 0)
137 self.assertURIs(
138 ["gs://example/gtk+/foo.tgz", "gs://example/private.tbz2"]
139 )
David James8c846492011-01-25 17:07:29 -0800140
Alex Klein1699fab2022-09-08 08:46:06 -0600141 def testFailedPopulate(self):
142 """Test failure conditions for the populate method."""
143 headerless = SimplePackageIndex(header=False)
144 self.assertRaises(KeyError, headerless._PopulateDuplicateDB, self.db, 0)
145 del self.pkgindex.packages[0]["CPV"]
146 self.assertRaises(
147 KeyError, self.pkgindex._PopulateDuplicateDB, self.db, 0
148 )
David James8c846492011-01-25 17:07:29 -0800149
150
Mike Frysinger68182472014-11-05 22:38:39 -0500151class TestResolveDuplicateUploads(cros_test_lib.MockTestCase, TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600152 """Tests for the ResolveDuplicateUploads function."""
David James8c846492011-01-25 17:07:29 -0800153
Alex Klein1699fab2022-09-08 08:46:06 -0600154 def setUp(self):
155 self.PatchObject(binpkg.time, "time", return_value=binpkg.TWO_WEEKS)
156 self.db = {}
157 self.dup = SimplePackageIndex()
158 self.expected_pkgindex = SimplePackageIndex()
David James2c7ccb42012-11-04 15:34:28 -0800159
Alex Klein1699fab2022-09-08 08:46:06 -0600160 def assertNoDuplicates(self, candidates):
161 """Verify no duplicates are found with the specified candidates."""
162 uploads = self.pkgindex.ResolveDuplicateUploads(candidates)
163 self.assertEqual(uploads, self.pkgindex.packages)
164 self.assertEqual(
165 len(self.pkgindex.packages), len(self.expected_pkgindex.packages)
166 )
167 for pkg1, pkg2 in zip(
168 self.pkgindex.packages, self.expected_pkgindex.packages
169 ):
170 self.assertNotEqual(pkg1["MTIME"], pkg2["MTIME"])
171 del pkg1["MTIME"]
172 del pkg2["MTIME"]
173 self.assertEqual(self.pkgindex.modified, False)
174 self.assertEqual(
175 self.pkgindex.packages, self.expected_pkgindex.packages
176 )
David James2c7ccb42012-11-04 15:34:28 -0800177
Alex Klein1699fab2022-09-08 08:46:06 -0600178 def assertAllDuplicates(self, candidates):
179 """Verify every package is a duplicate in the specified list."""
180 for pkg in self.expected_pkgindex.packages:
181 pkg.setdefault("PATH", pkg["CPV"] + ".tbz2")
182 self.pkgindex.ResolveDuplicateUploads(candidates)
183 self.assertEqual(
184 self.pkgindex.packages, self.expected_pkgindex.packages
185 )
David James615e5b52011-06-03 11:10:15 -0700186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 def testEmptyList(self):
188 """If no candidates are supplied, no duplicates should be found."""
189 self.assertNoDuplicates([])
David James8c846492011-01-25 17:07:29 -0800190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 def testEmptyIndex(self):
192 """If no packages are supplied, no duplicates should be found."""
193 self.assertNoDuplicates([self.empty])
David James8c846492011-01-25 17:07:29 -0800194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 def testDifferentURI(self):
196 """If the URI differs, no duplicates should be found."""
197 self.dup.header["URI"] = "gs://example2"
198 self.assertNoDuplicates([self.dup])
David James2c7ccb42012-11-04 15:34:28 -0800199
Alex Klein1699fab2022-09-08 08:46:06 -0600200 def testUpdateModificationTime(self):
201 """When duplicates are found, we should use the latest mtime."""
202 for pkg in self.expected_pkgindex.packages:
203 pkg["MTIME"] = "10"
204 for pkg in self.dup.packages:
205 pkg["MTIME"] = "4"
206 self.assertAllDuplicates([self.expected_pkgindex, self.dup])
David James2c7ccb42012-11-04 15:34:28 -0800207
Alex Klein1699fab2022-09-08 08:46:06 -0600208 def testCanonicalUrl(self):
Trent Apted3abd3d22023-05-17 11:40:51 +1000209 """If the URL is in a different format, should still find duplicates."""
Alex Klein1699fab2022-09-08 08:46:06 -0600210 self.dup.header["URI"] = gs.PUBLIC_BASE_HTTPS_URL + "example"
211 self.assertAllDuplicates([self.dup])
David James8c846492011-01-25 17:07:29 -0800212
Alex Klein1699fab2022-09-08 08:46:06 -0600213 def testMissingSHA1(self):
214 """We should not find duplicates if there is no SHA1."""
215 del self.pkgindex.packages[0]["SHA1"]
216 del self.expected_pkgindex.packages[0]["SHA1"]
217 for pkg in self.expected_pkgindex.packages[1:]:
218 pkg.setdefault("PATH", pkg["CPV"] + ".tbz2")
219 self.pkgindex.ResolveDuplicateUploads([self.dup])
220 self.assertNotEqual(
221 self.pkgindex.packages[0]["MTIME"],
222 self.expected_pkgindex.packages[0]["MTIME"],
223 )
224 del self.pkgindex.packages[0]["MTIME"]
225 del self.expected_pkgindex.packages[0]["MTIME"]
226 self.assertEqual(
227 self.pkgindex.packages, self.expected_pkgindex.packages
228 )
David James8c846492011-01-25 17:07:29 -0800229
Alex Klein1699fab2022-09-08 08:46:06 -0600230 def testSymbolsAvailable(self):
Trent Apted3abd3d22023-05-17 11:40:51 +1000231 """If symbols are available remotely: re-use them, set DEBUG_SYMBOLS."""
Alex Klein1699fab2022-09-08 08:46:06 -0600232 self.dup.packages[0]["DEBUG_SYMBOLS"] = "yes"
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800233
Alex Klein1699fab2022-09-08 08:46:06 -0600234 uploads = self.pkgindex.ResolveDuplicateUploads([self.dup])
235 self.assertEqual(uploads, [])
236 self.assertEqual(self.pkgindex.packages[0].get("DEBUG_SYMBOLS"), "yes")
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800237
Alex Klein1699fab2022-09-08 08:46:06 -0600238 def testSymbolsAvailableLocallyOnly(self):
239 """If the symbols are only available locally, reupload them."""
240 self.pkgindex.packages[0]["DEBUG_SYMBOLS"] = "yes"
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 uploads = self.pkgindex.ResolveDuplicateUploads([self.dup])
243 self.assertEqual(uploads, [self.pkgindex.packages[0]])
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800244
David James8c846492011-01-25 17:07:29 -0800245
Mike Frysinger68182472014-11-05 22:38:39 -0500246class TestWritePackageIndex(cros_test_lib.MockTestCase, TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600247 """Tests for the WriteToNamedTemporaryFile function."""
David James8c846492011-01-25 17:07:29 -0800248
Alex Klein1699fab2022-09-08 08:46:06 -0600249 def testSimple(self):
250 """Test simple call of WriteToNamedTemporaryFile()"""
251 self.PatchObject(self.pkgindex, "Write")
252 f = self.pkgindex.WriteToNamedTemporaryFile()
253 self.assertEqual(f.read(), "")
David James8c846492011-01-25 17:07:29 -0800254
255
Mike Frysinger36870f92015-04-12 02:59:55 -0400256class TestUploadPrebuilt(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600257 """Tests for the _UploadPrebuilt function."""
David James05bcb2b2011-02-09 09:25:47 -0800258
Alex Klein1699fab2022-09-08 08:46:06 -0600259 def setUp(self):
260 class MockTemporaryFile(object):
261 """Mock out the temporary file logic."""
David James05bcb2b2011-02-09 09:25:47 -0800262
Alex Klein1699fab2022-09-08 08:46:06 -0600263 def __init__(self, name):
264 self.name = name
265
266 self.pkgindex = SimplePackageIndex()
267 self.PatchObject(
268 binpkg, "GrabLocalPackageIndex", return_value=self.pkgindex
269 )
270 self.PatchObject(
271 self.pkgindex,
272 "ResolveDuplicateUploads",
273 return_value=PRIVATE_PACKAGES,
274 )
275 self.PatchObject(
276 self.pkgindex,
277 "WriteToNamedTemporaryFile",
278 return_value=MockTemporaryFile("fake"),
279 )
280 self.remote_up_mock = self.PatchObject(prebuilt, "RemoteUpload")
281 self.gs_up_mock = self.PatchObject(prebuilt, "_GsUpload")
282
283 def testSuccessfulGsUpload(self):
284 uploads = {
285 os.path.join(self.tempdir, "private.tbz2"): "gs://foo/private.tbz2"
286 }
287 dev_extras = os.path.join(self.tempdir, "dev-only-extras.tar.xz")
288 osutils.Touch(dev_extras)
289 self.PatchObject(prebuilt, "GenerateUploadDict", return_value=uploads)
290 uploads = uploads.copy()
291 uploads["fake"] = "gs://foo/suffix/Packages"
292 uploads[dev_extras] = "gs://foo/suffix/dev-only-extras.tar.xz"
293 acl = "public-read"
294 uri = self.pkgindex.header["URI"]
295 uploader = prebuilt.PrebuiltUploader(
296 "gs://foo",
297 acl,
298 uri,
299 [],
300 "/",
301 [],
302 False,
303 "foo",
304 False,
305 "x86-foo",
306 [],
307 "",
Bob Haarmanc0082602022-09-20 16:12:43 -0700308 report={},
Alex Klein1699fab2022-09-08 08:46:06 -0600309 )
310 uploader._UploadPrebuilt(self.tempdir, "suffix")
311 self.remote_up_mock.assert_called_once_with(mock.ANY, acl, uploads)
312 self.assertTrue(self.gs_up_mock.called)
David James05bcb2b2011-02-09 09:25:47 -0800313
David James05bcb2b2011-02-09 09:25:47 -0800314
Greg Edelston64620d12023-02-23 15:29:49 -0700315class TestUpdateRemoteSdkLatestFile(cros_test_lib.MockTestCase):
316 """Tests for PrebuiltUploader._UpdateRemoteSdkLatestFile."""
317
318 def setUp(self):
319 self._write_file_patch = self.PatchObject(osutils, "WriteFile")
320 self.PatchObject(prebuilt.PrebuiltUploader, "_Upload")
321 self.PatchObject(
322 gs.GSContext,
323 "LoadKeyValueStore",
324 return_value={
325 "LATEST_SDK": "1000",
326 "LATEST_SDK_UPREV_TARGET": "2000",
327 },
328 )
329 self._uploader = prebuilt.PrebuiltUploader(
330 "gs://foo",
331 "public-read",
332 SimplePackageIndex().header["URI"],
333 [],
334 "/",
335 [],
336 False,
337 "foo",
338 False,
339 "x86-foo",
340 [],
341 "",
342 report={},
343 )
344
345 def testNoChanges(self):
346 self._uploader._UpdateRemoteSdkLatestFile()
347 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
348 "1000", "2000"
349 )
350 self._write_file_patch.assert_called_with(mock.ANY, expected)
351
352 def testChangeLatestSdk(self):
353 self._uploader._UpdateRemoteSdkLatestFile(latest_sdk="3000")
354 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
355 "3000", "2000"
356 )
357 self._write_file_patch.assert_called_with(mock.ANY, expected)
358
359 def testChangeLatestUprevTarget(self):
360 self._uploader._UpdateRemoteSdkLatestFile(
361 latest_sdk_uprev_target="4000"
362 )
363 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
364 "1000", "4000"
365 )
366 self._write_file_patch.assert_called_with(mock.ANY, expected)
367
368 def testChangeBoth(self):
369 self._uploader._UpdateRemoteSdkLatestFile(
370 latest_sdk="3000", latest_sdk_uprev_target="4000"
371 )
372 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
373 "3000", "4000"
374 )
375 self._write_file_patch.assert_called_with(mock.ANY, expected)
376
377
Mike Frysinger36870f92015-04-12 02:59:55 -0400378class TestSyncPrebuilts(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600379 """Tests for the SyncHostPrebuilts function."""
David James05bcb2b2011-02-09 09:25:47 -0800380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 def setUp(self):
Bob Haarmanc0082602022-09-20 16:12:43 -0700382 clnum = [1]
383
384 def mock_rev(_filename, _data, report, *_args, **_kwargs):
385 report.setdefault("created_cls", []).append(
386 f"https://crrev.com/unittest/{clnum[0]}"
387 )
388 clnum[0] += 1
389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 self.rev_mock = self.PatchObject(
Greg Edelston86aea7b2023-02-15 16:50:25 -0700391 binpkg,
392 "UpdateAndSubmitKeyValueFile",
Bob Haarmanc0082602022-09-20 16:12:43 -0700393 side_effect=mock_rev,
Alex Klein1699fab2022-09-08 08:46:06 -0600394 )
395 self.update_binhost_mock = self.PatchObject(
396 prebuilt, "UpdateBinhostConfFile", return_value=None
397 )
398 self.build_path = "/trunk"
399 self.upload_location = "gs://upload/"
400 self.version = "1"
401 self.binhost = "http://prebuilt/"
402 self.key = "PORTAGE_BINHOST"
403 self.upload_mock = self.PatchObject(
404 prebuilt.PrebuiltUploader, "_UploadPrebuilt", return_value=True
405 )
Brian Norris74d1af92023-06-14 11:28:12 -0700406 self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
David James05bcb2b2011-02-09 09:25:47 -0800407
Brian Norris74d1af92023-06-14 11:28:12 -0700408 def _testSyncHostPrebuilts(self, chroot, out_dir):
Alex Klein1699fab2022-09-08 08:46:06 -0600409 board = "x86-foo"
410 target = prebuilt.BuildTarget(board, "aura")
411 slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
Bob Haarmanc0082602022-09-20 16:12:43 -0700412 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600413 if chroot is None:
Brian Norris74d1af92023-06-14 11:28:12 -0700414 package_path = path_util.FromChrootPath(
415 os.path.join(os.path.sep, prebuilt._HOST_PACKAGES_PATH),
416 source_path=self.build_path,
Alex Klein1699fab2022-09-08 08:46:06 -0600417 )
418 else:
Brian Norris74d1af92023-06-14 11:28:12 -0700419 package_path = path_util.FromChrootPath(
420 os.path.join(os.path.sep, prebuilt._HOST_PACKAGES_PATH),
421 chroot_path=chroot,
422 out_path=out_dir,
423 )
Alex Klein1699fab2022-09-08 08:46:06 -0600424 url_suffix = prebuilt._REL_HOST_PATH % {
425 "version": self.version,
426 "host_arch": prebuilt._HOST_ARCH,
427 "target": target,
428 }
429 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
430 url_value = "%s/%s/" % (
431 self.binhost.rstrip("/"),
432 packages_url_suffix.rstrip("/"),
433 )
434 urls = [url_value.replace("foo", "bar"), url_value]
435 binhost = " ".join(urls)
436 uploader = prebuilt.PrebuiltUploader(
437 self.upload_location,
438 "public-read",
439 self.binhost,
440 [],
441 self.build_path,
442 [],
443 False,
444 "foo",
445 False,
446 target,
447 slave_targets,
448 self.version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700449 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600450 chroot=chroot,
Brian Norris74d1af92023-06-14 11:28:12 -0700451 out_dir=out_dir,
Alex Klein1699fab2022-09-08 08:46:06 -0600452 )
453 uploader.SyncHostPrebuilts(self.key, True, True)
Bob Haarmanc0082602022-09-20 16:12:43 -0700454 self.assertEqual(
455 report,
456 {
457 "created_cls": ["https://crrev.com/unittest/1"],
458 },
459 )
Alex Klein1699fab2022-09-08 08:46:06 -0600460 self.upload_mock.assert_called_once_with(
461 package_path, packages_url_suffix
462 )
463 self.rev_mock.assert_called_once_with(
Bob Haarmanc0082602022-09-20 16:12:43 -0700464 mock.ANY, {self.key: binhost}, report, dryrun=False
Alex Klein1699fab2022-09-08 08:46:06 -0600465 )
466 self.update_binhost_mock.assert_called_once_with(
467 mock.ANY, self.key, binhost
468 )
David James05bcb2b2011-02-09 09:25:47 -0800469
Alex Klein1699fab2022-09-08 08:46:06 -0600470 def testSyncHostPrebuilts(self):
Brian Norris74d1af92023-06-14 11:28:12 -0700471 self._testSyncHostPrebuilts(chroot=None, out_dir=None)
Bob Haarmand1225ea2022-01-19 22:01:42 +0000472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 def testSyncHostPrebuiltsWithChroot(self):
Brian Norris74d1af92023-06-14 11:28:12 -0700474 self._testSyncHostPrebuilts(Path("/test/chroot"), Path("/test/out"))
Bob Haarmand1225ea2022-01-19 22:01:42 +0000475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 def testSyncBoardPrebuilts(self):
477 board = "x86-foo"
478 target = prebuilt.BuildTarget(board, "aura")
479 slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
Brian Norris74d1af92023-06-14 11:28:12 -0700480 board_path = path_util.FromChrootPath(
481 os.path.join(os.path.sep, prebuilt._BOARD_PATH % {"board": board}),
482 source_path=self.build_path,
Alex Klein1699fab2022-09-08 08:46:06 -0600483 )
484 package_path = os.path.join(board_path, "packages")
485 url_suffix = prebuilt._REL_BOARD_PATH % {
486 "version": self.version,
487 "target": target,
488 }
489 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
490 url_value = "%s/%s/" % (
491 self.binhost.rstrip("/"),
492 packages_url_suffix.rstrip("/"),
493 )
494 bar_binhost = url_value.replace("foo", "bar")
495 determine_mock = self.PatchObject(
496 prebuilt, "DeterminePrebuiltConfFile", side_effect=("bar", "foo")
497 )
498 self.PatchObject(prebuilt.PrebuiltUploader, "_UploadSdkTarball")
Bob Haarmanc0082602022-09-20 16:12:43 -0700499 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600500 with parallel_unittest.ParallelMock():
501 multiprocessing.Process.exitcode = 0
502 uploader = prebuilt.PrebuiltUploader(
503 self.upload_location,
504 "public-read",
505 self.binhost,
506 [],
507 self.build_path,
508 [],
509 False,
510 "foo",
511 False,
512 target,
513 slave_targets,
514 self.version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700515 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600516 )
517 uploader.SyncBoardPrebuilts(
Greg Edelston8da51ce2023-03-22 10:40:59 -0600518 self.key, True, True, True, None, None, None, None, None, True
Alex Klein1699fab2022-09-08 08:46:06 -0600519 )
520 determine_mock.assert_has_calls(
521 [
522 mock.call(self.build_path, slave_targets[0]),
523 mock.call(self.build_path, target),
524 ]
525 )
526 self.upload_mock.assert_called_once_with(
527 package_path, packages_url_suffix
528 )
529 self.rev_mock.assert_has_calls(
530 [
Bob Haarmanc0082602022-09-20 16:12:43 -0700531 mock.call(
532 "bar",
533 {self.key: bar_binhost},
534 {
535 "created_cls": [
536 "https://crrev.com/unittest/1",
537 "https://crrev.com/unittest/2",
538 ],
539 },
540 dryrun=False,
541 ),
542 mock.call(
543 "foo",
544 {self.key: url_value},
545 {
546 "created_cls": [
547 "https://crrev.com/unittest/1",
548 "https://crrev.com/unittest/2",
549 ],
550 },
551 dryrun=False,
552 ),
Alex Klein1699fab2022-09-08 08:46:06 -0600553 ]
554 )
555 self.update_binhost_mock.assert_has_calls(
556 [
557 mock.call(mock.ANY, self.key, bar_binhost),
558 mock.call(mock.ANY, self.key, url_value),
559 ]
560 )
David James05bcb2b2011-02-09 09:25:47 -0800561
562
Mike Frysinger36870f92015-04-12 02:59:55 -0400563class TestMain(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600564 """Tests for the main() function."""
David Jamesc0f158a2011-02-22 16:07:29 -0800565
Alex Klein1699fab2022-09-08 08:46:06 -0600566 def testMain(self):
567 """Test that the main function works."""
Bob Haarman35460c22022-11-08 00:11:59 +0000568 # Use a real object as returned from ParseOptions as a spec for
569 # the mock options object, so that we don't have any properties
570 # that the real object doesn't have.
571 options_spec, _ = prebuilt.ParseOptions(
572 [
573 "--dry-run",
574 "--build-path",
575 "/trunk",
576 "-u",
577 "gs://upload",
578 ]
579 )
580 options = mock.MagicMock(spec=options_spec)
Alex Klein1699fab2022-09-08 08:46:06 -0600581 old_binhost = "http://prebuilt/1"
582 options.previous_binhost_url = [old_binhost]
583 options.board = "x86-foo"
584 options.profile = None
585 target = prebuilt.BuildTarget(options.board, options.profile)
586 options.build_path = "/trunk"
587 options.chroot = None
Brian Norris74d1af92023-06-14 11:28:12 -0700588 options.out_dir = None
Alex Klein1699fab2022-09-08 08:46:06 -0600589 options.dryrun = False
590 options.private = True
591 options.packages = []
592 options.sync_host = True
593 options.git_sync = True
Greg Edelston8da51ce2023-03-22 10:40:59 -0600594 options.sync_remote_latest_sdk_file = True
Alex Klein1699fab2022-09-08 08:46:06 -0600595 options.upload_board_tarball = True
596 options.prepackaged_tarball = None
597 options.toolchains_overlay_tarballs = []
598 options.toolchains_overlay_upload_path = ""
599 options.toolchain_tarballs = []
600 options.toolchain_upload_path = ""
601 options.upload = "gs://upload/"
602 options.binhost_base_url = options.upload
603 options.prepend_version = True
604 options.set_version = None
605 options.skip_upload = False
606 options.filters = True
607 options.key = "PORTAGE_BINHOST"
608 options.binhost_conf_dir = None
609 options.sync_binhost_conf = True
610 options.slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
611 self.PatchObject(
612 prebuilt, "ParseOptions", return_value=tuple([options, target])
613 )
614 self.PatchObject(binpkg, "GrabRemotePackageIndex", return_value=True)
615 init_mock = self.PatchObject(
616 prebuilt.PrebuiltUploader, "__init__", return_value=None
617 )
618 expected_gs_acl_path = os.path.join(
619 "/fake_path", prebuilt._GOOGLESTORAGE_GSUTIL_FILE
620 )
621 self.PatchObject(
622 portage_util, "FindOverlayFile", return_value=expected_gs_acl_path
623 )
624 host_mock = self.PatchObject(
625 prebuilt.PrebuiltUploader, "SyncHostPrebuilts", return_value=None
626 )
627 board_mock = self.PatchObject(
628 prebuilt.PrebuiltUploader, "SyncBoardPrebuilts", return_value=None
629 )
Mike Frysinger36870f92015-04-12 02:59:55 -0400630
Alex Klein1699fab2022-09-08 08:46:06 -0600631 prebuilt.main([])
Mike Frysinger36870f92015-04-12 02:59:55 -0400632
Alex Klein1699fab2022-09-08 08:46:06 -0600633 init_mock.assert_called_once_with(
634 options.upload,
635 expected_gs_acl_path,
636 options.upload,
637 mock.ANY,
638 options.build_path,
639 options.packages,
640 False,
641 None,
642 False,
643 target,
644 options.slave_targets,
645 mock.ANY,
Bob Haarmanc0082602022-09-20 16:12:43 -0700646 {},
Brian Norris74d1af92023-06-14 11:28:12 -0700647 chroot=None,
648 out_dir=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600649 )
650 board_mock.assert_called_once_with(
651 options.key,
652 options.git_sync,
653 options.sync_binhost_conf,
654 options.upload_board_tarball,
655 None,
656 [],
657 "",
658 [],
659 "",
Greg Edelston8da51ce2023-03-22 10:40:59 -0600660 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600661 )
662 host_mock.assert_called_once_with(
663 options.key, options.git_sync, options.sync_binhost_conf
664 )
David Jamesc0f158a2011-02-22 16:07:29 -0800665
Mike Frysinger9e979b92012-11-29 02:55:09 -0500666
Mike Frysinger68182472014-11-05 22:38:39 -0500667class TestSdk(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600668 """Test logic related to uploading SDK binaries"""
Mike Frysinger9e979b92012-11-29 02:55:09 -0500669
Alex Klein1699fab2022-09-08 08:46:06 -0600670 def setUp(self):
671 self.PatchObject(
672 prebuilt,
673 "_GsUpload",
674 side_effect=Exception("should not get called"),
675 )
676 self.PatchObject(
677 prebuilt,
678 "UpdateBinhostConfFile",
679 side_effect=Exception("should not get called"),
680 )
Greg Edelston64620d12023-02-23 15:29:49 -0700681 self.PatchObject(
682 gs.GSContext,
683 "LoadKeyValueStore",
684 return_value={
685 "LATEST_SDK": "1000",
686 "LATEST_SDK_UPREV_TARGET": "2000",
687 },
688 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700689 self.write_file_mock = self.PatchObject(osutils, "WriteFile")
Alex Klein1699fab2022-09-08 08:46:06 -0600690 self.upload_mock = self.PatchObject(
691 prebuilt.PrebuiltUploader, "_Upload"
692 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500693
Alex Klein1699fab2022-09-08 08:46:06 -0600694 self.acl = "magic-acl"
Mike Frysinger9e979b92012-11-29 02:55:09 -0500695
Alex Klein1699fab2022-09-08 08:46:06 -0600696 # All these args pretty much get ignored. Whee.
697 self.uploader = prebuilt.PrebuiltUploader(
698 "gs://foo",
699 self.acl,
700 "prebuilt",
701 [],
702 "/",
703 [],
704 False,
705 "foo",
706 False,
707 "x86-foo",
708 [],
709 "chroot-1234",
Bob Haarmanc0082602022-09-20 16:12:43 -0700710 report={},
Alex Klein1699fab2022-09-08 08:46:06 -0600711 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500712
Alex Klein1699fab2022-09-08 08:46:06 -0600713 def testSdkUpload(
714 self,
715 to_tarballs=(),
716 to_upload_path=None,
717 tc_tarballs=(),
718 tc_upload_path=None,
719 ):
720 """Make sure we can upload just an SDK tarball"""
721 tar = "sdk.tar.xz"
722 ver = "1234"
723 vtar = "cros-sdk-%s.tar.xz" % ver
Mike Frysinger9e979b92012-11-29 02:55:09 -0500724
Greg Edelstonf3916f92023-02-22 15:49:38 -0700725 upload_calls = [
Alex Klein1699fab2022-09-08 08:46:06 -0600726 mock.call(
727 "%s.Manifest" % tar, "gs://chromiumos-sdk/%s.Manifest" % vtar
728 ),
729 mock.call(tar, "gs://chromiumos-sdk/%s" % vtar),
730 ]
731 for to in to_tarballs:
732 to = to.split(":")
Greg Edelstonf3916f92023-02-22 15:49:38 -0700733 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600734 mock.call(
735 to[1],
736 ("gs://chromiumos-sdk/" + to_upload_path)
737 % {"toolchains": to[0]},
738 )
739 )
740 for tc in tc_tarballs:
741 tc = tc.split(":")
Greg Edelstonf3916f92023-02-22 15:49:38 -0700742 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600743 mock.call(
744 tc[1],
745 ("gs://chromiumos-sdk/" + tc_upload_path)
746 % {"target": tc[0]},
747 )
748 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700749 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600750 mock.call(mock.ANY, "gs://chromiumos-sdk/cros-sdk-latest.conf")
751 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500752
Alex Klein1699fab2022-09-08 08:46:06 -0600753 self.uploader._UploadSdkTarball(
754 "amd64-host",
755 "",
756 tar,
757 to_tarballs,
758 to_upload_path,
759 tc_tarballs,
760 tc_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600761 True,
Alex Klein1699fab2022-09-08 08:46:06 -0600762 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700763 self.upload_mock.assert_has_calls(upload_calls)
764
765 expected_latest_file_contents = f"""\
766# The most recent SDK that is tested and ready for use.
767LATEST_SDK="{ver}"
768
769# The most recently built version. New uprev attempts should target this.
770# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700771LATEST_SDK_UPREV_TARGET=\"2000\""""
Greg Edelstonf3916f92023-02-22 15:49:38 -0700772 self.write_file_mock.assert_any_call(
773 mock.ANY, expected_latest_file_contents
774 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500775
Alex Klein1699fab2022-09-08 08:46:06 -0600776 def testBoardOverlayTarballUpload(self):
777 """Make sure processing of board-specific overlay tarballs works."""
778 to_tarballs = (
779 (
Trent Aptedc4f366a2023-05-16 15:32:48 +1000780 "i686-pc-linux-gnu:/some/path/built-sdk-overlay-toolchains-"
781 "i686-pc-linux-gnu.tar.xz"
Alex Klein1699fab2022-09-08 08:46:06 -0600782 ),
783 (
Trent Aptedc4f366a2023-05-16 15:32:48 +1000784 "armv7a-cros-linux-gnueabi-arm-none-eabi:/some/path/built-sdk-"
785 "overlay-toolchains-armv7a-cros-linux-gnueabi-arm-none-eabi"
Alex Klein1699fab2022-09-08 08:46:06 -0600786 ),
787 )
Trent Aptedc4f366a2023-05-16 15:32:48 +1000788 to_upload_path = (
789 "1994/04/cros-sdk-overlay-toolchains-%(toolchains)s-1994.04.02."
790 "tar.xz"
791 )
Alex Klein1699fab2022-09-08 08:46:06 -0600792 self.testSdkUpload(
793 to_tarballs=to_tarballs, to_upload_path=to_upload_path
794 )
Gilad Arnoldad333182015-05-27 15:50:41 -0700795
Alex Klein1699fab2022-09-08 08:46:06 -0600796 def testToolchainTarballUpload(self):
797 """Make sure processing of toolchain tarballs works."""
798 tc_tarballs = (
799 "i686:/some/i686.tar.xz",
800 "arm-none:/some/arm.tar.xz",
801 )
802 tc_upload_path = "1994/04/%(target)s-1994.04.02.tar.xz"
803 self.testSdkUpload(
804 tc_tarballs=tc_tarballs, tc_upload_path=tc_upload_path
805 )
Greg Edelston325e4412023-05-04 16:22:10 -0600806
807
808@pytest.mark.parametrize(
809 "extra_args,expected_sync",
810 [
811 ([], True),
812 (["--sync-remote-latest-sdk-file"], True),
813 (["--no-sync-remote-latest-sdk-file"], False),
814 (
815 [
816 "--no-sync-remote-latest-sdk-file",
817 "--sync-remote-latest-sdk-file",
818 ],
819 True,
820 ),
821 (
822 [
823 "--sync-remote-latest-sdk-file",
824 "--no-sync-remote-latest-sdk-file",
825 ],
826 False,
827 ),
828 ],
829)
830def test_parse_options_sync_remote_latest_file(
831 extra_args: List[str],
832 expected_sync: bool,
833):
834 """Test --sync-remote-latest-file and --no-sync-remote-latest-file.
835
836 Desired behavior:
837 * If either of those args is given, take the last one.
838 * If neither is given, default to True.
839
840 Args:
841 extra_args: Command-line args to pass into parse_options() besides the
842 bare minimum.
843 expected_sync: Whether options.sync_remote_latest_file should be True.
844 """
845 # Bare minimum args to run upload_prebuilts
846 args = ["--build-path", "/foo", "-u", "gs://foo/bar"]
847 args.extend(extra_args)
848 options, _ = prebuilt.ParseOptions(args)
849 assert options.sync_remote_latest_sdk_file == expected_sync