blob: 669a62318fccf60b2eb9e2013a2e326e0eef0b0c [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
David James8c846492011-01-25 17:07:29 -080010import tempfile
Mike Frysinger166fea02021-02-12 05:30:33 -050011from unittest import mock
Chris Sosa471532a2011-02-01 15:10:06 -080012
Mike Frysinger40ffb532021-02-12 07:36:08 -050013from chromite.lib import binpkg
Brian Harringc92788f2012-09-21 18:07:15 -070014from chromite.lib import cros_test_lib
David James2c7ccb42012-11-04 15:34:28 -080015from chromite.lib import gs
Zdenek Behanc0e18762012-09-22 04:06:17 +020016from chromite.lib import osutils
Mike Frysinger36870f92015-04-12 02:59:55 -040017from chromite.lib import parallel_unittest
Prathmesh Prabhu421eef22014-10-16 17:13:19 -070018from chromite.lib import portage_util
Mike Frysinger40ffb532021-02-12 07:36:08 -050019from chromite.scripts import upload_prebuilts as prebuilt
David James8c846492011-01-25 17:07:29 -080020
Mike Frysinger68182472014-11-05 22:38:39 -050021
Mike Frysinger27e21b72018-07-12 14:20:21 -040022# pylint: disable=protected-access
23
24
Alex Klein1699fab2022-09-08 08:46:06 -060025PUBLIC_PACKAGES = [
26 {"CPV": "gtk+/public1", "SHA1": "1", "MTIME": "1"},
27 {"CPV": "gtk+/public2", "SHA1": "2", "PATH": "gtk+/foo.tgz", "MTIME": "2"},
28]
29PRIVATE_PACKAGES = [{"CPV": "private", "SHA1": "3", "MTIME": "3"}]
David James8c846492011-01-25 17:07:29 -080030
31
32def SimplePackageIndex(header=True, packages=True):
Alex Klein1699fab2022-09-08 08:46:06 -060033 pkgindex = binpkg.PackageIndex()
34 if header:
35 pkgindex.header["URI"] = "gs://example"
36 if packages:
37 pkgindex.packages = copy.deepcopy(PUBLIC_PACKAGES + PRIVATE_PACKAGES)
38 return pkgindex
David James8c846492011-01-25 17:07:29 -080039
40
Brian Harringc92788f2012-09-21 18:07:15 -070041class TestUpdateFile(cros_test_lib.TempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -060042 """Tests for the UpdateLocalFile function."""
David James8c846492011-01-25 17:07:29 -080043
Alex Klein1699fab2022-09-08 08:46:06 -060044 def setUp(self):
45 self.contents_str = [
46 "# comment that should be skipped",
47 'PKGDIR="/var/lib/portage/pkgs"',
48 'PORTAGE_BINHOST="http://no.thanks.com"',
49 "portage portage-20100310.tar.bz2",
50 'COMPILE_FLAGS="some_value=some_other"',
51 ]
52 self.version_file = os.path.join(self.tempdir, "version")
53 osutils.WriteFile(self.version_file, "\n".join(self.contents_str))
David James8c846492011-01-25 17:07:29 -080054
Alex Klein1699fab2022-09-08 08:46:06 -060055 def _read_version_file(self, version_file=None):
56 """Read the contents of self.version_file and return as a list."""
57 if not version_file:
58 version_file = self.version_file
David James8c846492011-01-25 17:07:29 -080059
Mike Frysinger31fdddd2023-02-24 15:50:55 -050060 with open(version_file, encoding="utf-8") as version_fh:
Alex Klein1699fab2022-09-08 08:46:06 -060061 return [line.strip() for line in version_fh.readlines()]
Sergey Frolov89d28242022-06-06 17:49:20 -060062
Alex Klein1699fab2022-09-08 08:46:06 -060063 def _verify_key_pair(self, key, val):
64 file_contents = self._read_version_file()
David James8c846492011-01-25 17:07:29 -080065
Alex Klein1699fab2022-09-08 08:46:06 -060066 # Make sure 'key' entry is only found once.
67 entry_found = False
Sergey Frolov09280f12022-06-06 17:47:32 -060068
Alex Klein1699fab2022-09-08 08:46:06 -060069 # Ensure value is wrapped on quotes.
70 if '"' not in val:
71 val = '"%s"' % val
Sergey Frolov09280f12022-06-06 17:47:32 -060072
Alex Klein1699fab2022-09-08 08:46:06 -060073 # Inspect file contents.
74 for entry in file_contents:
75 if "=" not in entry:
76 continue
77 file_key, file_val = entry.split("=", maxsplit=1)
78 if file_key == key:
79 if val == file_val:
80 if entry_found:
81 self.fail(f"Variable {file_key} appears twice")
82 else:
83 entry_found = True
Sergey Frolov09280f12022-06-06 17:47:32 -060084
Alex Klein1699fab2022-09-08 08:46:06 -060085 if not entry_found:
86 self.fail('Could not find "%s=%s" in version file' % (key, val))
Sergey Frolov09280f12022-06-06 17:47:32 -060087
Alex Klein1699fab2022-09-08 08:46:06 -060088 def testAddVariableThatDoesNotExist(self):
89 """Add in a new variable that was no present in the file."""
90 key = "PORTAGE_BINHOST"
91 value = "1234567"
92 prebuilt.UpdateLocalFile(self.version_file, value)
93 print(self.version_file)
94 self._read_version_file()
95 self._verify_key_pair(key, value)
96 print(self.version_file)
David James8c846492011-01-25 17:07:29 -080097
Alex Klein1699fab2022-09-08 08:46:06 -060098 def testUpdateVariable(self):
99 """Test updating a variable that already exists."""
100 binhost_key, binhost_val = self.contents_str[2].split("=")
101 if binhost_key != "PORTAGE_BINHOST":
102 self.fail(
103 "unexpected test input: expected PORTAGE_BINHOST at line[2]"
104 )
105 self._verify_key_pair(binhost_key, binhost_val)
David James8c846492011-01-25 17:07:29 -0800106
Alex Klein1699fab2022-09-08 08:46:06 -0600107 # Confirm that unrelated variable 'PKGDIR' does not change.
108 pkgdir_key, pkgdir_val = self.contents_str[1].split("=")
109 if pkgdir_key != "PKGDIR":
110 self.fail("unexpected test input: expected PKGDIR at line[1]")
111 self._verify_key_pair(pkgdir_key, pkgdir_val)
Sergey Frolov09280f12022-06-06 17:47:32 -0600112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 binhost_new_val = "test_update"
114 prebuilt.UpdateLocalFile(self.version_file, binhost_new_val)
115 self._verify_key_pair(binhost_key, binhost_new_val)
116 self._verify_key_pair(pkgdir_key, pkgdir_val)
Sergey Frolov09280f12022-06-06 17:47:32 -0600117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 binhost_new_val = "test_update2"
119 prebuilt.UpdateLocalFile(self.version_file, binhost_new_val)
120 self._verify_key_pair(binhost_key, binhost_new_val)
121 self._verify_key_pair(pkgdir_key, pkgdir_val)
Sergey Frolov09280f12022-06-06 17:47:32 -0600122
Alex Klein1699fab2022-09-08 08:46:06 -0600123 def testUpdateNonExistentFile(self):
124 key = "PORTAGE_BINHOST"
125 value = "1234567"
126 non_existent_file = tempfile.mktemp()
127 try:
128 prebuilt.UpdateLocalFile(non_existent_file, value)
129 file_contents = self._read_version_file(non_existent_file)
130 self.assertEqual(file_contents, ['%s="%s"' % (key, value)])
131 finally:
132 if os.path.exists(non_existent_file):
133 os.remove(non_existent_file)
David James8c846492011-01-25 17:07:29 -0800134
135
Mike Frysinger68182472014-11-05 22:38:39 -0500136class TestPrebuilt(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600137 """Tests for Prebuilt logic."""
David James8c846492011-01-25 17:07:29 -0800138
Alex Klein1699fab2022-09-08 08:46:06 -0600139 def setUp(self):
140 self._base_local_path = "/b/cbuild/build/chroot/build/x86-dogfood/"
141 self._gs_bucket_path = "gs://chromeos-prebuilt/host/version"
142 self._local_path = os.path.join(self._base_local_path, "public1.tbz2")
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800143
Alex Klein1699fab2022-09-08 08:46:06 -0600144 def testGenerateUploadDict(self):
145 self.PatchObject(prebuilt.os.path, "exists", return_true=True)
146 pkgs = [{"CPV": "public1"}]
147 result = prebuilt.GenerateUploadDict(
148 self._base_local_path, self._gs_bucket_path, pkgs
149 )
150 expected = {
151 self._local_path: self._gs_bucket_path + "/public1.tbz2",
152 }
153 self.assertEqual(result, expected)
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 def testGenerateUploadDictWithDebug(self):
156 self.PatchObject(prebuilt.os.path, "exists", return_true=True)
157 pkgs = [{"CPV": "public1", "DEBUG_SYMBOLS": "yes"}]
158 result = prebuilt.GenerateUploadDict(
159 self._base_local_path, self._gs_bucket_path, pkgs
160 )
161 expected = {
162 self._local_path: self._gs_bucket_path + "/public1.tbz2",
163 self._local_path.replace(
164 ".tbz2", ".debug.tbz2"
165 ): self._gs_bucket_path
166 + "/public1.debug.tbz2",
167 }
168 self.assertEqual(result, expected)
David James8c846492011-01-25 17:07:29 -0800169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 def testDeterminePrebuiltConfHost(self):
171 """Test that the host prebuilt path comes back properly."""
172 expected_path = os.path.join(prebuilt._PREBUILT_MAKE_CONF["amd64"])
173 self.assertEqual(
174 prebuilt.DeterminePrebuiltConfFile("fake_path", "amd64"),
175 expected_path,
176 )
David James8c846492011-01-25 17:07:29 -0800177
David James8c846492011-01-25 17:07:29 -0800178
David James2c7ccb42012-11-04 15:34:28 -0800179class TestPkgIndex(cros_test_lib.TestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600180 """Helper for tests that update the Packages index file."""
David James2c7ccb42012-11-04 15:34:28 -0800181
Alex Klein1699fab2022-09-08 08:46:06 -0600182 def setUp(self):
183 self.db = {}
184 self.pkgindex = SimplePackageIndex()
185 self.empty = SimplePackageIndex(packages=False)
David James2c7ccb42012-11-04 15:34:28 -0800186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 def assertURIs(self, uris):
188 """Verify that the duplicate DB has the specified URLs."""
189 expected = [v.uri for _, v in sorted(self.db.items())]
190 self.assertEqual(expected, uris)
David James2c7ccb42012-11-04 15:34:28 -0800191
192
193class TestPackagesFileFiltering(TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600194 """Tests for Packages filtering behavior."""
David James8c846492011-01-25 17:07:29 -0800195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 def testFilterPkgIndex(self):
197 """Test filtering out of private packages."""
198 self.pkgindex.RemoveFilteredPackages(
199 lambda pkg: pkg in PRIVATE_PACKAGES
200 )
201 self.assertEqual(self.pkgindex.packages, PUBLIC_PACKAGES)
202 self.assertEqual(self.pkgindex.modified, True)
David James8c846492011-01-25 17:07:29 -0800203
204
David James2c7ccb42012-11-04 15:34:28 -0800205class TestPopulateDuplicateDB(TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600206 """Tests for the _PopulateDuplicateDB function."""
David James8c846492011-01-25 17:07:29 -0800207
Alex Klein1699fab2022-09-08 08:46:06 -0600208 def testEmptyIndex(self):
209 """Test population of the duplicate DB with an empty index."""
210 self.empty._PopulateDuplicateDB(self.db, 0)
211 self.assertEqual(self.db, {})
David James8c846492011-01-25 17:07:29 -0800212
Alex Klein1699fab2022-09-08 08:46:06 -0600213 def testNormalIndex(self):
214 """Test population of the duplicate DB with a full index."""
215 self.pkgindex._PopulateDuplicateDB(self.db, 0)
216 self.assertURIs(
217 [
218 "gs://example/gtk+/public1.tbz2",
219 "gs://example/gtk+/foo.tgz",
220 "gs://example/private.tbz2",
221 ]
222 )
David James8c846492011-01-25 17:07:29 -0800223
Alex Klein1699fab2022-09-08 08:46:06 -0600224 def testMissingSHA1(self):
225 """Test population of the duplicate DB with a missing SHA1."""
226 del self.pkgindex.packages[0]["SHA1"]
227 self.pkgindex._PopulateDuplicateDB(self.db, 0)
228 self.assertURIs(
229 ["gs://example/gtk+/foo.tgz", "gs://example/private.tbz2"]
230 )
David James8c846492011-01-25 17:07:29 -0800231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 def testFailedPopulate(self):
233 """Test failure conditions for the populate method."""
234 headerless = SimplePackageIndex(header=False)
235 self.assertRaises(KeyError, headerless._PopulateDuplicateDB, self.db, 0)
236 del self.pkgindex.packages[0]["CPV"]
237 self.assertRaises(
238 KeyError, self.pkgindex._PopulateDuplicateDB, self.db, 0
239 )
David James8c846492011-01-25 17:07:29 -0800240
241
Mike Frysinger68182472014-11-05 22:38:39 -0500242class TestResolveDuplicateUploads(cros_test_lib.MockTestCase, TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600243 """Tests for the ResolveDuplicateUploads function."""
David James8c846492011-01-25 17:07:29 -0800244
Alex Klein1699fab2022-09-08 08:46:06 -0600245 def setUp(self):
246 self.PatchObject(binpkg.time, "time", return_value=binpkg.TWO_WEEKS)
247 self.db = {}
248 self.dup = SimplePackageIndex()
249 self.expected_pkgindex = SimplePackageIndex()
David James2c7ccb42012-11-04 15:34:28 -0800250
Alex Klein1699fab2022-09-08 08:46:06 -0600251 def assertNoDuplicates(self, candidates):
252 """Verify no duplicates are found with the specified candidates."""
253 uploads = self.pkgindex.ResolveDuplicateUploads(candidates)
254 self.assertEqual(uploads, self.pkgindex.packages)
255 self.assertEqual(
256 len(self.pkgindex.packages), len(self.expected_pkgindex.packages)
257 )
258 for pkg1, pkg2 in zip(
259 self.pkgindex.packages, self.expected_pkgindex.packages
260 ):
261 self.assertNotEqual(pkg1["MTIME"], pkg2["MTIME"])
262 del pkg1["MTIME"]
263 del pkg2["MTIME"]
264 self.assertEqual(self.pkgindex.modified, False)
265 self.assertEqual(
266 self.pkgindex.packages, self.expected_pkgindex.packages
267 )
David James2c7ccb42012-11-04 15:34:28 -0800268
Alex Klein1699fab2022-09-08 08:46:06 -0600269 def assertAllDuplicates(self, candidates):
270 """Verify every package is a duplicate in the specified list."""
271 for pkg in self.expected_pkgindex.packages:
272 pkg.setdefault("PATH", pkg["CPV"] + ".tbz2")
273 self.pkgindex.ResolveDuplicateUploads(candidates)
274 self.assertEqual(
275 self.pkgindex.packages, self.expected_pkgindex.packages
276 )
David James615e5b52011-06-03 11:10:15 -0700277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 def testEmptyList(self):
279 """If no candidates are supplied, no duplicates should be found."""
280 self.assertNoDuplicates([])
David James8c846492011-01-25 17:07:29 -0800281
Alex Klein1699fab2022-09-08 08:46:06 -0600282 def testEmptyIndex(self):
283 """If no packages are supplied, no duplicates should be found."""
284 self.assertNoDuplicates([self.empty])
David James8c846492011-01-25 17:07:29 -0800285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 def testDifferentURI(self):
287 """If the URI differs, no duplicates should be found."""
288 self.dup.header["URI"] = "gs://example2"
289 self.assertNoDuplicates([self.dup])
David James2c7ccb42012-11-04 15:34:28 -0800290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 def testUpdateModificationTime(self):
292 """When duplicates are found, we should use the latest mtime."""
293 for pkg in self.expected_pkgindex.packages:
294 pkg["MTIME"] = "10"
295 for pkg in self.dup.packages:
296 pkg["MTIME"] = "4"
297 self.assertAllDuplicates([self.expected_pkgindex, self.dup])
David James2c7ccb42012-11-04 15:34:28 -0800298
Alex Klein1699fab2022-09-08 08:46:06 -0600299 def testCanonicalUrl(self):
300 """If the URL is in a different format, we should still find duplicates."""
301 self.dup.header["URI"] = gs.PUBLIC_BASE_HTTPS_URL + "example"
302 self.assertAllDuplicates([self.dup])
David James8c846492011-01-25 17:07:29 -0800303
Alex Klein1699fab2022-09-08 08:46:06 -0600304 def testMissingSHA1(self):
305 """We should not find duplicates if there is no SHA1."""
306 del self.pkgindex.packages[0]["SHA1"]
307 del self.expected_pkgindex.packages[0]["SHA1"]
308 for pkg in self.expected_pkgindex.packages[1:]:
309 pkg.setdefault("PATH", pkg["CPV"] + ".tbz2")
310 self.pkgindex.ResolveDuplicateUploads([self.dup])
311 self.assertNotEqual(
312 self.pkgindex.packages[0]["MTIME"],
313 self.expected_pkgindex.packages[0]["MTIME"],
314 )
315 del self.pkgindex.packages[0]["MTIME"]
316 del self.expected_pkgindex.packages[0]["MTIME"]
317 self.assertEqual(
318 self.pkgindex.packages, self.expected_pkgindex.packages
319 )
David James8c846492011-01-25 17:07:29 -0800320
Alex Klein1699fab2022-09-08 08:46:06 -0600321 def testSymbolsAvailable(self):
322 """If symbols are available remotely, re-use them and set DEBUG_SYMBOLS."""
323 self.dup.packages[0]["DEBUG_SYMBOLS"] = "yes"
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 uploads = self.pkgindex.ResolveDuplicateUploads([self.dup])
326 self.assertEqual(uploads, [])
327 self.assertEqual(self.pkgindex.packages[0].get("DEBUG_SYMBOLS"), "yes")
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800328
Alex Klein1699fab2022-09-08 08:46:06 -0600329 def testSymbolsAvailableLocallyOnly(self):
330 """If the symbols are only available locally, reupload them."""
331 self.pkgindex.packages[0]["DEBUG_SYMBOLS"] = "yes"
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 uploads = self.pkgindex.ResolveDuplicateUploads([self.dup])
334 self.assertEqual(uploads, [self.pkgindex.packages[0]])
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800335
David James8c846492011-01-25 17:07:29 -0800336
Mike Frysinger68182472014-11-05 22:38:39 -0500337class TestWritePackageIndex(cros_test_lib.MockTestCase, TestPkgIndex):
Alex Klein1699fab2022-09-08 08:46:06 -0600338 """Tests for the WriteToNamedTemporaryFile function."""
David James8c846492011-01-25 17:07:29 -0800339
Alex Klein1699fab2022-09-08 08:46:06 -0600340 def testSimple(self):
341 """Test simple call of WriteToNamedTemporaryFile()"""
342 self.PatchObject(self.pkgindex, "Write")
343 f = self.pkgindex.WriteToNamedTemporaryFile()
344 self.assertEqual(f.read(), "")
David James8c846492011-01-25 17:07:29 -0800345
346
Mike Frysinger36870f92015-04-12 02:59:55 -0400347class TestUploadPrebuilt(cros_test_lib.MockTempDirTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600348 """Tests for the _UploadPrebuilt function."""
David James05bcb2b2011-02-09 09:25:47 -0800349
Alex Klein1699fab2022-09-08 08:46:06 -0600350 def setUp(self):
351 class MockTemporaryFile(object):
352 """Mock out the temporary file logic."""
David James05bcb2b2011-02-09 09:25:47 -0800353
Alex Klein1699fab2022-09-08 08:46:06 -0600354 def __init__(self, name):
355 self.name = name
356
357 self.pkgindex = SimplePackageIndex()
358 self.PatchObject(
359 binpkg, "GrabLocalPackageIndex", return_value=self.pkgindex
360 )
361 self.PatchObject(
362 self.pkgindex,
363 "ResolveDuplicateUploads",
364 return_value=PRIVATE_PACKAGES,
365 )
366 self.PatchObject(
367 self.pkgindex,
368 "WriteToNamedTemporaryFile",
369 return_value=MockTemporaryFile("fake"),
370 )
371 self.remote_up_mock = self.PatchObject(prebuilt, "RemoteUpload")
372 self.gs_up_mock = self.PatchObject(prebuilt, "_GsUpload")
373
374 def testSuccessfulGsUpload(self):
375 uploads = {
376 os.path.join(self.tempdir, "private.tbz2"): "gs://foo/private.tbz2"
377 }
378 dev_extras = os.path.join(self.tempdir, "dev-only-extras.tar.xz")
379 osutils.Touch(dev_extras)
380 self.PatchObject(prebuilt, "GenerateUploadDict", return_value=uploads)
381 uploads = uploads.copy()
382 uploads["fake"] = "gs://foo/suffix/Packages"
383 uploads[dev_extras] = "gs://foo/suffix/dev-only-extras.tar.xz"
384 acl = "public-read"
385 uri = self.pkgindex.header["URI"]
386 uploader = prebuilt.PrebuiltUploader(
387 "gs://foo",
388 acl,
389 uri,
390 [],
391 "/",
392 [],
393 False,
394 "foo",
395 False,
396 "x86-foo",
397 [],
398 "",
Bob Haarmanc0082602022-09-20 16:12:43 -0700399 report={},
Alex Klein1699fab2022-09-08 08:46:06 -0600400 )
401 uploader._UploadPrebuilt(self.tempdir, "suffix")
402 self.remote_up_mock.assert_called_once_with(mock.ANY, acl, uploads)
403 self.assertTrue(self.gs_up_mock.called)
David James05bcb2b2011-02-09 09:25:47 -0800404
David James05bcb2b2011-02-09 09:25:47 -0800405
Greg Edelston64620d12023-02-23 15:29:49 -0700406class TestUpdateRemoteSdkLatestFile(cros_test_lib.MockTestCase):
407 """Tests for PrebuiltUploader._UpdateRemoteSdkLatestFile."""
408
409 def setUp(self):
410 self._write_file_patch = self.PatchObject(osutils, "WriteFile")
411 self.PatchObject(prebuilt.PrebuiltUploader, "_Upload")
412 self.PatchObject(
413 gs.GSContext,
414 "LoadKeyValueStore",
415 return_value={
416 "LATEST_SDK": "1000",
417 "LATEST_SDK_UPREV_TARGET": "2000",
418 },
419 )
420 self._uploader = prebuilt.PrebuiltUploader(
421 "gs://foo",
422 "public-read",
423 SimplePackageIndex().header["URI"],
424 [],
425 "/",
426 [],
427 False,
428 "foo",
429 False,
430 "x86-foo",
431 [],
432 "",
433 report={},
434 )
435
436 def testNoChanges(self):
437 self._uploader._UpdateRemoteSdkLatestFile()
438 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
439 "1000", "2000"
440 )
441 self._write_file_patch.assert_called_with(mock.ANY, expected)
442
443 def testChangeLatestSdk(self):
444 self._uploader._UpdateRemoteSdkLatestFile(latest_sdk="3000")
445 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
446 "3000", "2000"
447 )
448 self._write_file_patch.assert_called_with(mock.ANY, expected)
449
450 def testChangeLatestUprevTarget(self):
451 self._uploader._UpdateRemoteSdkLatestFile(
452 latest_sdk_uprev_target="4000"
453 )
454 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
455 "1000", "4000"
456 )
457 self._write_file_patch.assert_called_with(mock.ANY, expected)
458
459 def testChangeBoth(self):
460 self._uploader._UpdateRemoteSdkLatestFile(
461 latest_sdk="3000", latest_sdk_uprev_target="4000"
462 )
463 expected = prebuilt.PrebuiltUploader._CreateRemoteSdkLatestFileContents(
464 "3000", "4000"
465 )
466 self._write_file_patch.assert_called_with(mock.ANY, expected)
467
468
Mike Frysinger36870f92015-04-12 02:59:55 -0400469class TestSyncPrebuilts(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600470 """Tests for the SyncHostPrebuilts function."""
David James05bcb2b2011-02-09 09:25:47 -0800471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 def setUp(self):
Bob Haarmanc0082602022-09-20 16:12:43 -0700473 clnum = [1]
474
475 def mock_rev(_filename, _data, report, *_args, **_kwargs):
476 report.setdefault("created_cls", []).append(
477 f"https://crrev.com/unittest/{clnum[0]}"
478 )
479 clnum[0] += 1
480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 self.rev_mock = self.PatchObject(
Bob Haarmanc0082602022-09-20 16:12:43 -0700482 prebuilt,
483 "RevGitFile",
484 side_effect=mock_rev,
Alex Klein1699fab2022-09-08 08:46:06 -0600485 )
486 self.update_binhost_mock = self.PatchObject(
487 prebuilt, "UpdateBinhostConfFile", return_value=None
488 )
489 self.build_path = "/trunk"
490 self.upload_location = "gs://upload/"
491 self.version = "1"
492 self.binhost = "http://prebuilt/"
493 self.key = "PORTAGE_BINHOST"
494 self.upload_mock = self.PatchObject(
495 prebuilt.PrebuiltUploader, "_UploadPrebuilt", return_value=True
496 )
David James05bcb2b2011-02-09 09:25:47 -0800497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 def _testSyncHostPrebuilts(self, chroot):
499 board = "x86-foo"
500 target = prebuilt.BuildTarget(board, "aura")
501 slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
Bob Haarmanc0082602022-09-20 16:12:43 -0700502 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600503 if chroot is None:
504 package_path = os.path.join(
505 self.build_path, "chroot", prebuilt._HOST_PACKAGES_PATH
506 )
507 else:
508 package_path = os.path.join(chroot, prebuilt._HOST_PACKAGES_PATH)
509 url_suffix = prebuilt._REL_HOST_PATH % {
510 "version": self.version,
511 "host_arch": prebuilt._HOST_ARCH,
512 "target": target,
513 }
514 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
515 url_value = "%s/%s/" % (
516 self.binhost.rstrip("/"),
517 packages_url_suffix.rstrip("/"),
518 )
519 urls = [url_value.replace("foo", "bar"), url_value]
520 binhost = " ".join(urls)
521 uploader = prebuilt.PrebuiltUploader(
522 self.upload_location,
523 "public-read",
524 self.binhost,
525 [],
526 self.build_path,
527 [],
528 False,
529 "foo",
530 False,
531 target,
532 slave_targets,
533 self.version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700534 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600535 chroot=chroot,
536 )
537 uploader.SyncHostPrebuilts(self.key, True, True)
Bob Haarmanc0082602022-09-20 16:12:43 -0700538 self.assertEqual(
539 report,
540 {
541 "created_cls": ["https://crrev.com/unittest/1"],
542 },
543 )
Alex Klein1699fab2022-09-08 08:46:06 -0600544 self.upload_mock.assert_called_once_with(
545 package_path, packages_url_suffix
546 )
547 self.rev_mock.assert_called_once_with(
Bob Haarmanc0082602022-09-20 16:12:43 -0700548 mock.ANY, {self.key: binhost}, report, dryrun=False
Alex Klein1699fab2022-09-08 08:46:06 -0600549 )
550 self.update_binhost_mock.assert_called_once_with(
551 mock.ANY, self.key, binhost
552 )
David James05bcb2b2011-02-09 09:25:47 -0800553
Alex Klein1699fab2022-09-08 08:46:06 -0600554 def testSyncHostPrebuilts(self):
555 self._testSyncHostPrebuilts(chroot=None)
Bob Haarmand1225ea2022-01-19 22:01:42 +0000556
Alex Klein1699fab2022-09-08 08:46:06 -0600557 def testSyncHostPrebuiltsWithChroot(self):
558 self._testSyncHostPrebuilts("/test/chroot")
Bob Haarmand1225ea2022-01-19 22:01:42 +0000559
Alex Klein1699fab2022-09-08 08:46:06 -0600560 def testSyncBoardPrebuilts(self):
561 board = "x86-foo"
562 target = prebuilt.BuildTarget(board, "aura")
563 slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
564 board_path = os.path.join(
565 self.build_path, prebuilt._BOARD_PATH % {"board": board}
566 )
567 package_path = os.path.join(board_path, "packages")
568 url_suffix = prebuilt._REL_BOARD_PATH % {
569 "version": self.version,
570 "target": target,
571 }
572 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
573 url_value = "%s/%s/" % (
574 self.binhost.rstrip("/"),
575 packages_url_suffix.rstrip("/"),
576 )
577 bar_binhost = url_value.replace("foo", "bar")
578 determine_mock = self.PatchObject(
579 prebuilt, "DeterminePrebuiltConfFile", side_effect=("bar", "foo")
580 )
581 self.PatchObject(prebuilt.PrebuiltUploader, "_UploadSdkTarball")
Bob Haarmanc0082602022-09-20 16:12:43 -0700582 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600583 with parallel_unittest.ParallelMock():
584 multiprocessing.Process.exitcode = 0
585 uploader = prebuilt.PrebuiltUploader(
586 self.upload_location,
587 "public-read",
588 self.binhost,
589 [],
590 self.build_path,
591 [],
592 False,
593 "foo",
594 False,
595 target,
596 slave_targets,
597 self.version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700598 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600599 )
600 uploader.SyncBoardPrebuilts(
601 self.key, True, True, True, None, None, None, None, None
602 )
603 determine_mock.assert_has_calls(
604 [
605 mock.call(self.build_path, slave_targets[0]),
606 mock.call(self.build_path, target),
607 ]
608 )
609 self.upload_mock.assert_called_once_with(
610 package_path, packages_url_suffix
611 )
612 self.rev_mock.assert_has_calls(
613 [
Bob Haarmanc0082602022-09-20 16:12:43 -0700614 mock.call(
615 "bar",
616 {self.key: bar_binhost},
617 {
618 "created_cls": [
619 "https://crrev.com/unittest/1",
620 "https://crrev.com/unittest/2",
621 ],
622 },
623 dryrun=False,
624 ),
625 mock.call(
626 "foo",
627 {self.key: url_value},
628 {
629 "created_cls": [
630 "https://crrev.com/unittest/1",
631 "https://crrev.com/unittest/2",
632 ],
633 },
634 dryrun=False,
635 ),
Alex Klein1699fab2022-09-08 08:46:06 -0600636 ]
637 )
638 self.update_binhost_mock.assert_has_calls(
639 [
640 mock.call(mock.ANY, self.key, bar_binhost),
641 mock.call(mock.ANY, self.key, url_value),
642 ]
643 )
David James05bcb2b2011-02-09 09:25:47 -0800644
645
Mike Frysinger36870f92015-04-12 02:59:55 -0400646class TestMain(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600647 """Tests for the main() function."""
David Jamesc0f158a2011-02-22 16:07:29 -0800648
Alex Klein1699fab2022-09-08 08:46:06 -0600649 def testMain(self):
650 """Test that the main function works."""
Bob Haarman35460c22022-11-08 00:11:59 +0000651 # Use a real object as returned from ParseOptions as a spec for
652 # the mock options object, so that we don't have any properties
653 # that the real object doesn't have.
654 options_spec, _ = prebuilt.ParseOptions(
655 [
656 "--dry-run",
657 "--build-path",
658 "/trunk",
659 "-u",
660 "gs://upload",
661 ]
662 )
663 options = mock.MagicMock(spec=options_spec)
Alex Klein1699fab2022-09-08 08:46:06 -0600664 old_binhost = "http://prebuilt/1"
665 options.previous_binhost_url = [old_binhost]
666 options.board = "x86-foo"
667 options.profile = None
668 target = prebuilt.BuildTarget(options.board, options.profile)
669 options.build_path = "/trunk"
670 options.chroot = None
671 options.dryrun = False
672 options.private = True
673 options.packages = []
674 options.sync_host = True
675 options.git_sync = True
676 options.upload_board_tarball = True
677 options.prepackaged_tarball = None
678 options.toolchains_overlay_tarballs = []
679 options.toolchains_overlay_upload_path = ""
680 options.toolchain_tarballs = []
681 options.toolchain_upload_path = ""
682 options.upload = "gs://upload/"
683 options.binhost_base_url = options.upload
684 options.prepend_version = True
685 options.set_version = None
686 options.skip_upload = False
687 options.filters = True
688 options.key = "PORTAGE_BINHOST"
689 options.binhost_conf_dir = None
690 options.sync_binhost_conf = True
691 options.slave_targets = [prebuilt.BuildTarget("x86-bar", "aura")]
692 self.PatchObject(
693 prebuilt, "ParseOptions", return_value=tuple([options, target])
694 )
695 self.PatchObject(binpkg, "GrabRemotePackageIndex", return_value=True)
696 init_mock = self.PatchObject(
697 prebuilt.PrebuiltUploader, "__init__", return_value=None
698 )
699 expected_gs_acl_path = os.path.join(
700 "/fake_path", prebuilt._GOOGLESTORAGE_GSUTIL_FILE
701 )
702 self.PatchObject(
703 portage_util, "FindOverlayFile", return_value=expected_gs_acl_path
704 )
705 host_mock = self.PatchObject(
706 prebuilt.PrebuiltUploader, "SyncHostPrebuilts", return_value=None
707 )
708 board_mock = self.PatchObject(
709 prebuilt.PrebuiltUploader, "SyncBoardPrebuilts", return_value=None
710 )
Mike Frysinger36870f92015-04-12 02:59:55 -0400711
Alex Klein1699fab2022-09-08 08:46:06 -0600712 prebuilt.main([])
Mike Frysinger36870f92015-04-12 02:59:55 -0400713
Alex Klein1699fab2022-09-08 08:46:06 -0600714 init_mock.assert_called_once_with(
715 options.upload,
716 expected_gs_acl_path,
717 options.upload,
718 mock.ANY,
719 options.build_path,
720 options.packages,
721 False,
722 None,
723 False,
724 target,
725 options.slave_targets,
726 mock.ANY,
Bob Haarmanc0082602022-09-20 16:12:43 -0700727 {},
Alex Klein1699fab2022-09-08 08:46:06 -0600728 None,
729 )
730 board_mock.assert_called_once_with(
731 options.key,
732 options.git_sync,
733 options.sync_binhost_conf,
734 options.upload_board_tarball,
735 None,
736 [],
737 "",
738 [],
739 "",
740 )
741 host_mock.assert_called_once_with(
742 options.key, options.git_sync, options.sync_binhost_conf
743 )
David Jamesc0f158a2011-02-22 16:07:29 -0800744
Mike Frysinger9e979b92012-11-29 02:55:09 -0500745
Mike Frysinger68182472014-11-05 22:38:39 -0500746class TestSdk(cros_test_lib.MockTestCase):
Alex Klein1699fab2022-09-08 08:46:06 -0600747 """Test logic related to uploading SDK binaries"""
Mike Frysinger9e979b92012-11-29 02:55:09 -0500748
Alex Klein1699fab2022-09-08 08:46:06 -0600749 def setUp(self):
750 self.PatchObject(
751 prebuilt,
752 "_GsUpload",
753 side_effect=Exception("should not get called"),
754 )
755 self.PatchObject(
756 prebuilt,
757 "UpdateBinhostConfFile",
758 side_effect=Exception("should not get called"),
759 )
Greg Edelston64620d12023-02-23 15:29:49 -0700760 self.PatchObject(
761 gs.GSContext,
762 "LoadKeyValueStore",
763 return_value={
764 "LATEST_SDK": "1000",
765 "LATEST_SDK_UPREV_TARGET": "2000",
766 },
767 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700768 self.write_file_mock = self.PatchObject(osutils, "WriteFile")
Alex Klein1699fab2022-09-08 08:46:06 -0600769 self.upload_mock = self.PatchObject(
770 prebuilt.PrebuiltUploader, "_Upload"
771 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500772
Alex Klein1699fab2022-09-08 08:46:06 -0600773 self.acl = "magic-acl"
Mike Frysinger9e979b92012-11-29 02:55:09 -0500774
Alex Klein1699fab2022-09-08 08:46:06 -0600775 # All these args pretty much get ignored. Whee.
776 self.uploader = prebuilt.PrebuiltUploader(
777 "gs://foo",
778 self.acl,
779 "prebuilt",
780 [],
781 "/",
782 [],
783 False,
784 "foo",
785 False,
786 "x86-foo",
787 [],
788 "chroot-1234",
Bob Haarmanc0082602022-09-20 16:12:43 -0700789 report={},
Alex Klein1699fab2022-09-08 08:46:06 -0600790 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500791
Alex Klein1699fab2022-09-08 08:46:06 -0600792 def testSdkUpload(
793 self,
794 to_tarballs=(),
795 to_upload_path=None,
796 tc_tarballs=(),
797 tc_upload_path=None,
798 ):
799 """Make sure we can upload just an SDK tarball"""
800 tar = "sdk.tar.xz"
801 ver = "1234"
802 vtar = "cros-sdk-%s.tar.xz" % ver
Mike Frysinger9e979b92012-11-29 02:55:09 -0500803
Greg Edelstonf3916f92023-02-22 15:49:38 -0700804 upload_calls = [
Alex Klein1699fab2022-09-08 08:46:06 -0600805 mock.call(
806 "%s.Manifest" % tar, "gs://chromiumos-sdk/%s.Manifest" % vtar
807 ),
808 mock.call(tar, "gs://chromiumos-sdk/%s" % vtar),
809 ]
810 for to in to_tarballs:
811 to = to.split(":")
Greg Edelstonf3916f92023-02-22 15:49:38 -0700812 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600813 mock.call(
814 to[1],
815 ("gs://chromiumos-sdk/" + to_upload_path)
816 % {"toolchains": to[0]},
817 )
818 )
819 for tc in tc_tarballs:
820 tc = tc.split(":")
Greg Edelstonf3916f92023-02-22 15:49:38 -0700821 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600822 mock.call(
823 tc[1],
824 ("gs://chromiumos-sdk/" + tc_upload_path)
825 % {"target": tc[0]},
826 )
827 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700828 upload_calls.append(
Alex Klein1699fab2022-09-08 08:46:06 -0600829 mock.call(mock.ANY, "gs://chromiumos-sdk/cros-sdk-latest.conf")
830 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500831
Alex Klein1699fab2022-09-08 08:46:06 -0600832 self.uploader._UploadSdkTarball(
833 "amd64-host",
834 "",
835 tar,
836 to_tarballs,
837 to_upload_path,
838 tc_tarballs,
839 tc_upload_path,
840 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700841 self.upload_mock.assert_has_calls(upload_calls)
842
843 expected_latest_file_contents = f"""\
844# The most recent SDK that is tested and ready for use.
845LATEST_SDK="{ver}"
846
847# The most recently built version. New uprev attempts should target this.
848# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700849LATEST_SDK_UPREV_TARGET=\"2000\""""
Greg Edelstonf3916f92023-02-22 15:49:38 -0700850 self.write_file_mock.assert_any_call(
851 mock.ANY, expected_latest_file_contents
852 )
Mike Frysinger9e979b92012-11-29 02:55:09 -0500853
Alex Klein1699fab2022-09-08 08:46:06 -0600854 def testBoardOverlayTarballUpload(self):
855 """Make sure processing of board-specific overlay tarballs works."""
856 to_tarballs = (
857 (
858 "i686-pc-linux-gnu:"
859 "/some/path/built-sdk-overlay-toolchains-i686-pc-linux-gnu.tar.xz"
860 ),
861 (
862 "armv7a-cros-linux-gnueabi-arm-none-eabi:"
863 "/some/path/built-sdk-overlay-toolchains-armv7a-cros-linux-gnueabi-"
864 "arm-none-eabi"
865 ),
866 )
867 to_upload_path = "1994/04/cros-sdk-overlay-toolchains-%(toolchains)s-1994.04.02.tar.xz"
868 self.testSdkUpload(
869 to_tarballs=to_tarballs, to_upload_path=to_upload_path
870 )
Gilad Arnoldad333182015-05-27 15:50:41 -0700871
Alex Klein1699fab2022-09-08 08:46:06 -0600872 def testToolchainTarballUpload(self):
873 """Make sure processing of toolchain tarballs works."""
874 tc_tarballs = (
875 "i686:/some/i686.tar.xz",
876 "arm-none:/some/arm.tar.xz",
877 )
878 tc_upload_path = "1994/04/%(target)s-1994.04.02.tar.xz"
879 self.testSdkUpload(
880 tc_tarballs=tc_tarballs, tc_upload_path=tc_upload_path
881 )