| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unit tests for toolchain_util.""" |
| |
| import base64 |
| import collections |
| import datetime |
| import fnmatch |
| import glob |
| import json |
| import os |
| from pathlib import Path |
| import shutil |
| import time |
| from unittest import mock |
| |
| from chromite.lib import chroot_lib |
| from chromite.lib import constants |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_test_lib |
| from chromite.lib import gob_util |
| from chromite.lib import gs |
| from chromite.lib import osutils |
| from chromite.lib import partial_mock |
| from chromite.lib import portage_util |
| from chromite.lib import toolchain_util |
| from chromite.lib.parser import package_info |
| |
| |
| # pylint: disable=protected-access |
| |
| _input_artifact = collections.namedtuple( |
| "_input_artifact", ["name", "gs_locations"] |
| ) |
| |
| |
| class ProfilesNameHelperTest(cros_test_lib.MockTempDirTestCase): |
| """Test the helper functions related to naming.""" |
| |
| # pylint: disable=protected-access |
| def testParseBenchmarkProfileName(self): |
| """Test top-level function _ParseBenchmarkProfileName.""" |
| # Test parse failure |
| profile_name_to_fail = "this_is_an_invalid_name" |
| with self.assertRaises( |
| toolchain_util.ProfilesNameHelperError |
| ) as context: |
| toolchain_util._ParseBenchmarkProfileName(profile_name_to_fail) |
| self.assertIn( |
| "Unparseable benchmark profile name:", str(context.exception) |
| ) |
| |
| # Test parse success |
| profile_name = "chromeos-chrome-amd64-77.0.3849.0_rc-r1.afdo" |
| result = toolchain_util._ParseBenchmarkProfileName(profile_name) |
| self.assertEqual( |
| result, |
| toolchain_util.BenchmarkProfileVersion( |
| major=77, |
| minor=0, |
| build=3849, |
| patch=0, |
| revision=1, |
| is_merged=False, |
| ), |
| ) |
| |
| # Test Arm parsing. |
| profile_name = "chromeos-chrome-arm-77.0.3849.0_rc-r1.afdo" |
| result = toolchain_util._ParseBenchmarkProfileName(profile_name) |
| self.assertEqual( |
| result, |
| toolchain_util.BenchmarkProfileVersion( |
| major=77, |
| minor=0, |
| build=3849, |
| patch=0, |
| revision=1, |
| is_merged=False, |
| ), |
| ) |
| |
| def testParseCWPProfileName(self): |
| """Test top-level function _ParseCWPProfileName.""" |
| # Test parse failure |
| profile_name_to_fail = "this_is_an_invalid_name" |
| with self.assertRaises( |
| toolchain_util.ProfilesNameHelperError |
| ) as context: |
| toolchain_util._ParseCWPProfileName(profile_name_to_fail) |
| self.assertIn("Unparseable CWP profile name:", str(context.exception)) |
| |
| # Test parse success |
| profile_name = "R77-3809.38-1562580965.afdo.xz" |
| result = toolchain_util._ParseCWPProfileName(profile_name) |
| self.assertEqual( |
| result, |
| toolchain_util.CWPProfileVersion( |
| major=77, build=3809, patch=38, clock=1562580965 |
| ), |
| ) |
| |
| def testParseMergedProfileName(self): |
| """Test top-level function _ParseMergedProfileName.""" |
| # Test parse failure |
| profile_name_to_fail = "this_is_an_invalid_name" |
| with self.assertRaises( |
| toolchain_util.ProfilesNameHelperError |
| ) as context: |
| toolchain_util._ParseMergedProfileName(profile_name_to_fail) |
| self.assertIn("Unparseable merged AFDO name:", str(context.exception)) |
| |
| # Test parse release AFDO success |
| afdo_name = ( |
| "chromeos-chrome-amd64-atom-77-3809.38-1562580965" |
| "-benchmark-77.0.3849.0-r1-redacted.afdo.xz" |
| ) |
| result = toolchain_util._ParseMergedProfileName(afdo_name) |
| self.assertEqual( |
| result, |
| ( |
| toolchain_util.BenchmarkProfileVersion( |
| major=77, |
| minor=0, |
| build=3849, |
| patch=0, |
| revision=1, |
| is_merged=False, |
| ), |
| toolchain_util.CWPProfileVersion( |
| major=77, build=3809, patch=38, clock=1562580965 |
| ), |
| ), |
| ) |
| # Test parse release Arm AFDO success |
| afdo_name = ( |
| "chromeos-chrome-arm-none-77-3809.38-1562580965" |
| "-benchmark-77.0.3849.0-r1-redacted.afdo.xz" |
| ) |
| result = toolchain_util._ParseMergedProfileName(afdo_name) |
| self.assertEqual( |
| result, |
| ( |
| toolchain_util.BenchmarkProfileVersion( |
| major=77, |
| minor=0, |
| build=3849, |
| patch=0, |
| revision=1, |
| is_merged=False, |
| ), |
| toolchain_util.CWPProfileVersion( |
| major=77, build=3809, patch=38, clock=1562580965 |
| ), |
| ), |
| ) |
| |
| def testCompressAFDOFiles(self): |
| """Test _CompressAFDOFiles().""" |
| input_dir = "/path/to/inputs" |
| output_dir = "/another/path/to/outputs" |
| targets = ["input1", "/path/to/inputs/input2"] |
| suffix = ".xz" |
| self.PatchObject(cros_build_lib, "CompressFile") |
| # Should raise exception because the input doesn't exist |
| with self.assertRaises(RuntimeError) as context: |
| toolchain_util._CompressAFDOFiles( |
| targets, input_dir, output_dir, suffix |
| ) |
| self.assertEqual( |
| str(context.exception), |
| "file %s to compress does not exist" |
| % os.path.join(input_dir, targets[0]), |
| ) |
| # Should pass |
| self.PatchObject(os.path, "exists", return_value=True) |
| # Return ~1MB profile size. |
| self.PatchObject(os.path, "getsize", return_value=100000) |
| toolchain_util._CompressAFDOFiles( |
| targets, input_dir, output_dir, suffix |
| ) |
| compressed_names = [os.path.basename(x) for x in targets] |
| inputs = [os.path.join(input_dir, n) for n in compressed_names] |
| outputs = [ |
| os.path.join(output_dir, n + suffix) for n in compressed_names |
| ] |
| calls = [mock.call(n, o) for n, o in zip(inputs, outputs)] |
| cros_build_lib.CompressFile.assert_has_calls(calls) |
| |
| def testGetProfileAge(self): |
| """Test top-level function _GetProfileAge().""" |
| # Test unsupported artifact_type |
| current_day_profile = "R0-0.0-%d" % int(time.time()) |
| with self.assertRaises(ValueError) as context: |
| toolchain_util._GetProfileAge( |
| current_day_profile, "unsupported_type" |
| ) |
| self.assertEqual( |
| "'unsupported_type' is currently not supported to check profile " |
| "age.", |
| str(context.exception), |
| ) |
| |
| # Test using profile of the current day. |
| ret = toolchain_util._GetProfileAge(current_day_profile, "kernel_afdo") |
| self.assertEqual(0, ret) |
| |
| # Test using profile from the last day. |
| last_day_profile = "R0-0.0-%d" % int(time.time() - 86400) |
| ret = toolchain_util._GetProfileAge(last_day_profile, "kernel_afdo") |
| self.assertEqual(1, ret) |
| |
| |
| class PrepareBundleTest(cros_test_lib.RunCommandTempDirTestCase): |
| """Setup code common to Prepare/Bundle class methods.""" |
| |
| def setUp(self): |
| self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False) |
| |
| self.board = "chell" |
| self.chroot = chroot_lib.Chroot( |
| path=self.tempdir / "chroot", |
| out_path=self.tempdir / "out", |
| ) |
| osutils.SafeMakedirs(self.chroot.path) |
| osutils.SafeMakedirs(self.chroot.tmp) |
| self.sysroot = f"/build/{self.board}" |
| self.sysroot_full_path = self.chroot.full_path("build", self.board) |
| self.chrome_package = "chromeos-chrome" |
| self.kernel_package = "chromeos-kernel-3_18" |
| self.profile_info = {"arch": "amd64"} |
| self.chrome_PV = "chromeos-base/chromeos-chrome-78.0.3893.0_rc-r1" |
| self.chrome_ebuild = os.path.join( |
| self.tempdir, |
| "src", |
| "third_party", |
| "chromiumos-overlay", |
| os.path.dirname(self.chrome_PV), |
| "chromeos-chrome", |
| f"{os.path.basename(self.chrome_PV)}.ebuild", |
| ) |
| self.chrome_pkg = package_info.parse(self.chrome_PV) |
| self.glob = self.PatchObject( |
| glob, "glob", return_value=[self.chrome_ebuild] |
| ) |
| self.rc.AddCmdResult(partial_mock.In("rm"), returncode=0) |
| self.obj = toolchain_util._CommonPrepareBundle( |
| "None", |
| chroot=self.chroot, |
| sysroot_path=self.sysroot, |
| profile_info=self.profile_info, |
| ) |
| self.gs_context = self.PatchObject(self.obj, "_gs_context") |
| self.gsc_list = self.PatchObject( |
| self.gs_context, "List", return_value=[] |
| ) |
| self.data = ( |
| b"chromeos-chrome-amd64-atom-77-3809.38-1562580965" |
| b"-benchmark-77.0.3849.0-r1-redacted.afdo.xz" |
| ) |
| self.arch = "atom" |
| self.fetch = self.PatchObject( |
| gob_util, "FetchUrl", return_value=base64.encodebytes(self.data) |
| ) |
| |
| |
| class CommonPrepareBundleTest(PrepareBundleTest): |
| """Test common Prepare/Bundle class methods.""" |
| |
| def testGetEbuildInfo(self): |
| """Verify that EbuildInfo is correctly returned.""" |
| self.glob.return_value = ["chromeos-chrome-96.0.4657.0_rc-r2.ebuild"] |
| ret = self.obj._GetEbuildInfo("chromeos-chrome") |
| self.assertEqual(ret.CPV.vr, "96.0.4657.0_rc-r2") |
| self.assertEqual(ret.CPV.version, "96.0.4657.0_rc") |
| self.assertEqual(ret.CPV.revision, 2) |
| self.assertTrue(ret.CPV.with_version("96.0.4657.0_rc")) |
| self.assertEqual(ret.CPV.category, "chromeos-base") |
| self.assertEqual(ret.CPV.package, "chromeos-chrome") |
| self.glob.assert_called_once() |
| |
| def testGetEbuildInfoWithoutRevision(self): |
| """Verify that EbuildInfo is correctly returned.""" |
| self.glob.return_value = ["chromeos-chrome-96.0.4657.0_rc.ebuild"] |
| ret = self.obj._GetEbuildInfo("chromeos-chrome") |
| self.assertEqual(ret.CPV.vr, "96.0.4657.0_rc") |
| self.assertEqual(ret.CPV.version, "96.0.4657.0_rc") |
| self.assertEqual(ret.CPV.revision, 0) |
| |
| def testGetEbuildInfoWithMultipleChromes(self): |
| self.glob.return_value = [ |
| "chromeos-chrome-78.0.3893.0.ebuild", |
| "chromeos-chrome-78.0.3893.0_rc-r1.ebuild", |
| "chromeos-chrome-78.0.3893.100_rc-r1.ebuild", |
| "chromeos-chrome-78.0.3893.10_rc-r1.ebuild", |
| ] |
| ret = self.obj._GetEbuildInfo("chromeos-chrome") |
| self.assertEqual(ret.CPV.vr, "78.0.3893.100_rc-r1") |
| self.assertEqual(ret.CPV.version, "78.0.3893.100_rc") |
| self.assertEqual(ret.CPV.revision, 1) |
| |
| def test_GetArtifactVersionInGob(self): |
| """Test that we look in the right place in GoB.""" |
| self.assertRaises( |
| ValueError, self.obj._GetArtifactVersionInGob, "badarch" |
| ) |
| |
| self.assertEqual( |
| self.data.decode("utf-8"), |
| self.obj._GetArtifactVersionInGob(self.arch), |
| ) |
| self.fetch.assert_called_once_with( |
| constants.EXTERNAL_GOB_HOST, |
| "chromium/src/+/refs/tags/%s/chromeos/profiles/%s.afdo.newest.txt" |
| "?format=text" % (self.chrome_pkg.version.split("_")[0], self.arch), |
| ) |
| |
| self.fetch.reset_mock() |
| self.fetch.return_value = "" |
| self.assertRaises( |
| RuntimeError, self.obj._GetArtifactVersionInGob, self.arch |
| ) |
| self.fetch.assert_called_once() |
| |
| |
| class PrepBundLatestAFDOArtifactTest(PrepareBundleTest): |
| """Test related function to compare freshness of AFDO artifacts.""" |
| |
| def setUp(self): |
| self.board = "board" |
| self.gs_url = "gs://path/to/any_gs_url" |
| self.current_branch = "78" |
| self.current_arch = "atom" |
| self.MockListResult = collections.namedtuple( |
| "MockListResult", ("url", "creation_time") |
| ) |
| files_in_gs_bucket = [ |
| # Benchmark profiles |
| ("chromeos-chrome-arm-78.0.3892.0_rc-r1.afdo.bz2", 1.0), |
| ("chromeos-chrome-amd64-78.0.3893.0_rc-r1.afdo.bz2", 2.0), |
| ("chromeos-chrome-amd64-78.0.3896.0_rc-r1.afdo.bz2", 1.0), # Latest |
| ("chromeos-chrome-amd64-78.0.3897.0_rc-r1-merged.afdo.bz2", 3.0), |
| # CWP profiles |
| ("R78-3869.38-1562580965.afdo.xz", 2.1), |
| ("R78-3866.0-1570000000.afdo.xz", 1.1), # Latest |
| ("R77-3811.0-1580000000.afdo.xz", 3.1), |
| # Kernel profiles |
| ("R76-3869.38-1562580965.gcov.xz", 1.3), |
| ("R76-3866.0-1570000000.gcov.xz", 2.3), # Latest |
| ] |
| |
| self.gs_list = [ |
| self.MockListResult( |
| url=os.path.join(self.gs_url, x), creation_time=y |
| ) |
| for x, y in files_in_gs_bucket |
| ] |
| self.gsc_list.return_value = self.gs_list |
| |
| def testValidBenchmarkProfileVersion(self): |
| """Test that it returns None for unparsable profile name.""" |
| prof = "unparsable-file.data" |
| ver_none = self.obj._ValidBenchmarkProfileVersion(prof) |
| self.assertIsNone(ver_none) |
| |
| def testFindLatestAFDOArtifactPassWithBenchmarkAfdo(self): |
| """Test _FindLatestAFDOArtifact returns latest benchmark AFDO.""" |
| latest_afdo = self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._ValidBenchmarkProfileVersion |
| ) |
| self.assertEqual( |
| latest_afdo, |
| os.path.join( |
| self.gs_url, "chromeos-chrome-amd64-78.0.3896.0_rc-r1.afdo.bz2" |
| ), |
| ) |
| |
| def testFindLatestAFDOArtifactPassWithBenchmarkAfdoArm(self): |
| """Test _FindLatestAFDOArtifact returns latest benchmark Arm AFDO.""" |
| self.obj.arch = "arm" |
| latest_afdo = self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._ValidBenchmarkProfileVersion |
| ) |
| self.assertEqual( |
| latest_afdo, |
| os.path.join( |
| self.gs_url, "chromeos-chrome-arm-78.0.3892.0_rc-r1.afdo.bz2" |
| ), |
| ) |
| |
| def testFindLatestAfdoArtifactOnPriorBranch(self): |
| """Test that we find a file from prior branch when we have none.""" |
| self.obj._ebuild_info["chromeos-chrome"] = toolchain_util._EbuildInfo( |
| path="path", |
| CPV=package_info.parse( |
| "chromeos-base/chromeos-chrome-79.0.3900.0_rc-r1" |
| ), |
| ) |
| latest_afdo = self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._ValidBenchmarkProfileVersion |
| ) |
| self.assertEqual( |
| latest_afdo, |
| os.path.join( |
| self.gs_url, |
| "chromeos-chrome-amd64-78.0.3896.0_rc-r1.afdo.bz2", |
| ), |
| ) |
| |
| def testFindLatestAFDOArtifactFailToFindAnyFiles(self): |
| """Test function fails when no files on current branch.""" |
| self.obj._ebuild_info["chromeos-chrome"] = toolchain_util._EbuildInfo( |
| path="path", |
| CPV=package_info.parse( |
| "chromeos-base/chromeos-chrome-80.0.3950.0_rc-r1" |
| ), |
| ) |
| self.gsc_list.side_effect = gs.GSNoSuchKey("No files") |
| with self.assertRaises( |
| toolchain_util.NoProfilesInGsBucketError |
| ) as context: |
| self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._ValidBenchmarkProfileVersion |
| ) |
| self.assertEqual( |
| "No files for branch 80 found in %s" % self.gs_url, |
| str(context.exception), |
| ) |
| |
| def testFindLatestAFDOArtifactsFindMaxFromInvalidFiles(self): |
| """Test function fails when finds only invalid files.""" |
| mock_gs_list = [ |
| self.MockListResult( |
| # Invalid chrome version (not full). |
| url=os.path.join( |
| self.gs_url, "chromeos-chrome-amd64-78.afdo.bz2" |
| ), |
| creation_time=1.0, |
| ) |
| ] |
| self.gsc_list.return_value = mock_gs_list |
| with self.assertRaises(RuntimeError) as context: |
| self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._ValidBenchmarkProfileVersion |
| ) |
| self.assertIn( |
| "No valid latest artifact was found", str(context.exception) |
| ) |
| |
| |
| class PrepareForBuildHandlerTest(PrepareBundleTest): |
| """Test PrepareForBuildHandler specific methods.""" |
| |
| def setUp(self): |
| self.artifact_type = "Unspecified" |
| self.input_artifacts = {} |
| self.kernel_version = "5_4" |
| self.profile_info = { |
| "arch": "amd64", |
| "kernel_version": self.kernel_version.replace("_", "."), |
| } |
| self.gsc_exists = None |
| self.gsc_ls = None |
| self.patch_ebuild = mock.MagicMock() |
| # Save datetime for use in mocks. |
| self.dt = datetime.datetime |
| self.now = datetime.datetime.fromtimestamp( |
| 1658747184, |
| tz=datetime.timezone.utc, |
| ) |
| self.day_old_ts = int( |
| datetime.datetime.timestamp(self.now - datetime.timedelta(days=1)) |
| ) |
| self.week_old_ts = int( |
| datetime.datetime.timestamp(self.now - datetime.timedelta(weeks=1)) |
| ) |
| self.month_old_ts = int( |
| datetime.datetime.timestamp(self.now - datetime.timedelta(days=30)) |
| ) |
| self.verified_afdo_name = ( |
| f"chromeos-chrome-amd64-atom-78-3876.0-{self.week_old_ts}-" |
| "benchmark-78.0.3839.0-r1-redacted.afdo" |
| ) |
| self.cwp_gs_location = "gs://path/to/gs_bucket/cwp" |
| self.benchmark_gs_location = "gs://path/to/gs_bucket/benchmark" |
| self.PatchObject( |
| toolchain_util, "CWP_AFDO_GS_URL", new=self.cwp_gs_location |
| ) |
| self.PatchObject( |
| toolchain_util, |
| "BENCHMARK_AFDO_GS_URL", |
| new=self.benchmark_gs_location, |
| ) |
| |
| class mock_datetime: |
| """Class for mocking datetime.datetime.""" |
| |
| @staticmethod |
| def fromtimestamp(ts, tz=None): |
| return self.dt.fromtimestamp(ts, tz) |
| |
| @staticmethod |
| def now(tz=None): |
| del tz |
| return self.now |
| |
| self.PatchObject(toolchain_util.datetime, "datetime", new=mock_datetime) |
| |
| def SetUpPrepare( |
| self, |
| artifact_type, |
| input_artifacts, |
| mock_patch=True, |
| profile_info_extra=None, |
| ): |
| """Set up to test _Prepare${artifactType}.""" |
| self.artifact_type = artifact_type |
| self.input_artifacts = input_artifacts |
| if profile_info_extra: |
| self.profile_info.update(profile_info_extra) |
| self.obj = toolchain_util.PrepareForBuildHandler( |
| self.artifact_type, |
| self.chroot, |
| self.sysroot, |
| self.board, |
| self.input_artifacts, |
| self.profile_info, |
| ) |
| self.obj._gs_context = self.gs_context |
| self.gsc_exists = self.PatchObject( |
| self.gs_context, "Exists", return_value=True |
| ) |
| self.gsc_ls = self.PatchObject( |
| self.gs_context, "LS", return_value=["gs://path"] |
| ) |
| if mock_patch: |
| self.patch_ebuild = self.PatchObject( |
| toolchain_util._CommonPrepareBundle, "_PatchEbuild" |
| ) |
| |
| def setupUnverifiedChromeBenchmarkAfdoFileInputProperties( |
| self, profile_info_extra=None |
| ): |
| self.SetUpPrepare( |
| "UnverifiedChromeBenchmarkAfdoFile", |
| { |
| "UnverifiedChromeBenchmarkPerfFile": ["gs://path/to/perfdata"], |
| "UnverifiedChromeBenchmarkAfdoFile": ["gs://path/to/unvetted"], |
| "ChromeDebugBinary": ["gs://image-archive/path"], |
| }, |
| profile_info_extra=profile_info_extra, |
| ) |
| |
| def testPrepareUnverifiedChromeBenchmarkAfdoFileExists(self): |
| """Normal flow, build is needed, all artifacts are present.""" |
| self.setupUnverifiedChromeBenchmarkAfdoFileInputProperties() |
| # Published artifact is missing, debug binary is present, perf.data is |
| # present. |
| self.gsc_exists.return_value = False |
| self.gsc_ls.side_effect = ( |
| ["gs://image-archive/path/to/debug"], |
| ["gs://image-archive/path/to/perf"], |
| ) |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| expected_exists = [ |
| mock.call( |
| "gs://path/to/unvetted/" |
| "chromeos-chrome-amd64-78.0.3893.0_rc-r1.afdo.bz2" |
| ), |
| ] |
| expected_ls = [ |
| mock.call( |
| "gs://image-archive/path/chromeos-chrome-amd64-*.debug.bz2" |
| ), |
| mock.call( |
| "gs://path/to/perfdata/" |
| "chromeos-chrome-amd64-78.0.3893.0.perf.data.bz2" |
| ), |
| ] |
| self.assertEqual(expected_exists, self.gs_context.Exists.call_args_list) |
| self.assertEqual(expected_ls, self.gs_context.LS.call_args_list) |
| # There is no need to patch the ebuild. |
| self.patch_ebuild.assert_not_called() |
| |
| def testPrepareUnverifiedChromeBenchmarkArmAfdoFile(self): |
| """Normal flow with Arm, build is needed, all artifacts are present.""" |
| profile_info_extra = {"chrome_cwp_profile": "arm", "arch": "arm"} |
| self.setupUnverifiedChromeBenchmarkAfdoFileInputProperties( |
| profile_info_extra |
| ) |
| # Published artifact is missing, debug binary is present, perf.data is |
| # present. |
| self.gsc_exists.return_value = False |
| self.gsc_ls.side_effect = ( |
| ["gs://image-archive/path/to/debug"], |
| ["gs://image-archive/path/to/perf"], |
| ) |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| # Expect arm profiles. |
| expected_exists = [ |
| mock.call( |
| "gs://path/to/unvetted/" |
| "chromeos-chrome-arm-78.0.3893.0_rc-r1.afdo.bz2" |
| ), |
| ] |
| expected_ls = [ |
| mock.call( |
| "gs://image-archive/path/chromeos-chrome-arm-*.debug.bz2" |
| ), |
| mock.call( |
| "gs://path/to/perfdata/" |
| "chromeos-chrome-arm-78.0.3893.0.perf.data.bz2" |
| ), |
| ] |
| self.assertEqual(expected_exists, self.gs_context.Exists.call_args_list) |
| self.assertEqual(expected_ls, self.gs_context.LS.call_args_list) |
| # There is no need to patch the ebuild. |
| self.patch_ebuild.assert_not_called() |
| |
| def testPrepareUnverifiedChromeBenchmarkAfdoFileMissingDebug(self): |
| """Test raised exception when chrome.debug file is missing.""" |
| self.setupUnverifiedChromeBenchmarkAfdoFileInputProperties() |
| # Published artifact is missing, debug binary is missing. |
| self.gsc_exists.return_value = False |
| self.gsc_ls.return_value = [] |
| with self.assertRaisesRegex( |
| toolchain_util.PrepareForBuildHandlerError, |
| r"Could not find an artifact matching the pattern " |
| r'"chromeos-chrome-amd64-\*.debug.bz2" in ' |
| r"\['gs://image-archive/path'\].", |
| ): |
| self.obj.Prepare() |
| |
| def testPrepareUnverifiedChromeBenchmarkAfdoFileMissingPerf(self): |
| """Test raised exception when perf.data file is missing.""" |
| self.setupUnverifiedChromeBenchmarkAfdoFileInputProperties() |
| # Published artifact is missing, debug binary is present, |
| # perf.data is missing. |
| self.gsc_exists.return_value = False |
| self.gsc_ls.side_effect = (["gs://image-archive/path/to/debug"], []) |
| with self.assertRaisesRegex( |
| toolchain_util.PrepareForBuildHandlerError, |
| r'Could not find "chromeos-chrome-amd64-78.0.3893.0.perf.data.bz2" ' |
| r"in \['gs://path/to/perfdata'\].", |
| ): |
| self.obj.Prepare() |
| |
| def testPrepareUnverifiedChromeBenchmarkAfdoFileMultArtifacts(self): |
| """Test raised exception on multiple artifacts of one type.""" |
| self.setupUnverifiedChromeBenchmarkAfdoFileInputProperties() |
| # Published artifact is missing, multiple debug binary artifacts. |
| self.gsc_exists.return_value = False |
| self.gsc_ls.return_value = [ |
| "gs://image-archive/path/to/debug1", |
| "gs://image-archive/path/to/debug2", |
| ] |
| with self.assertRaisesRegex( |
| toolchain_util.PrepareForBuildHandlerError, |
| r"Found \['gs://image-archive/path/to/debug1', " |
| r"'gs://image-archive/path/to/debug2'\] artifacts at " |
| r"gs://image-archive/path. Expected ONE file.", |
| ): |
| self.obj.Prepare() |
| |
| def testCleanupArtifactDirectory(self): |
| mock_rmdir = self.PatchObject(osutils, "RmDir") |
| mock_isdir = self.PatchObject(os.path, "exists") |
| for test_dir in [ |
| "/tmp/fatal_clang_warnings", |
| "/tmp/clang_crash_diagnostics", |
| ]: |
| mock_rmdir.reset_mock() |
| # When the dirs don't exist, we shouldn't try to remove them |
| mock_isdir.return_value = False |
| self.obj._CleanupArtifactDirectory(test_dir) |
| mock_rmdir.assert_not_called() |
| |
| # When the dirs exist, we should remove all of them |
| mock_isdir.return_value = True |
| self.obj._CleanupArtifactDirectory(test_dir) |
| mock_rmdir.assert_has_calls( |
| [ |
| mock.call(self.chroot.full_path(test_dir), sudo=True), |
| mock.call( |
| self.chroot.full_path( |
| os.path.join(self.sysroot, test_dir[1:]) |
| ), |
| sudo=True, |
| ), |
| ] |
| ) |
| |
| # A non-absolute path will trigger assertion |
| with self.assertRaises(Exception) as context: |
| self.obj._CleanupArtifactDirectory("non/absolute/path") |
| self.assertIn("needs to be an absolute path", str(context.exception)) |
| |
| def callPrepareVerifiedKernelCwpAfdoFile( |
| self, |
| ebuild_list_of_str, |
| cwp_old_loc=None, |
| cwp_new_loc=None, |
| cwp_old_ver=None, |
| cwp_new_ver=None, |
| ): |
| """Helper function to set up and verify Prepare() call. |
| |
| Args: |
| ebuild_list_of_str: list[str] of ebuild contents. |
| Must contain {changing_cwp_loc} which resolves to |
| "cwp_old_loc" before Prepare() and "cwp_new_loc" after. |
| Must contain {changing_cwp_ver} which resolves to |
| "cwp_old_ver" before Prepare() and "cwp_new_ver" after. |
| cwp_old_loc: AFDO_LOCATION value before Prepare(). |
| cwp_new_loc: AFDO_LOCATION value after Prepare(). |
| cwp_old_ver: AFDO version before Prepare(). |
| cwp_new_ver: AFDO version after Prepare(). |
| """ |
| if not cwp_old_loc: |
| cwp_old_loc = "" |
| if not cwp_new_loc: |
| cwp_new_loc = "gs://path/to/cwp/kernel/5.4" |
| if not cwp_old_ver: |
| cwp_old_ver = "R99-14469.8-1644229953" |
| if not cwp_new_ver: |
| cwp_new_ver = "R100-14496.0-1644834841" |
| |
| self.SetUpPrepare( |
| "VerifiedKernelCwpAfdoFile", |
| { |
| "UnverifiedKernelCwpAfdoFile": [cwp_new_loc], |
| "VerifiedKernelCwpAfdoFile": [cwp_new_loc], |
| }, |
| mock_patch=False, |
| ) |
| kernel_package = "sys-kernel/chromeos-kernel-5_15-5.15.12-r1234" |
| ebuild_spec = "{c}/{p}-{v}-r{r}.ebuild" |
| kernel_cpv = package_info.parse(kernel_package) |
| ebuild_info_path = os.path.join( |
| self.sysroot_full_path, format(kernel_cpv, ebuild_spec) |
| ) |
| ebuild_info = toolchain_util._EbuildInfo( |
| path=ebuild_info_path, CPV=kernel_cpv |
| ) |
| self.PatchObject(toolchain_util, "_GetProfileAge", return_value=0) |
| self.PatchObject(self.obj, "_GetEbuildInfo", return_value=ebuild_info) |
| kernel_cwp = os.path.join(cwp_new_loc, cwp_new_ver) |
| self.PatchObject( |
| self.obj, "_FindLatestAFDOArtifact", return_value=kernel_cwp |
| ) |
| # The artifact is missing, build is needed. |
| self.gsc_exists.return_value = False |
| ebuild_old_str = "".join(ebuild_list_of_str).format( |
| changing_cwp_loc=cwp_old_loc, |
| changing_cwp_ver=cwp_old_ver, |
| ) |
| self.WriteTempFile( |
| ebuild_info_path, |
| ebuild_old_str, |
| makedirs=True, |
| ) |
| |
| self.obj.Prepare() |
| |
| # Check contents in the uprevved package. |
| uprev_path = os.path.join( |
| self.sysroot_full_path, |
| format(kernel_cpv.revision_bump(), ebuild_spec), |
| ) |
| new_contents = self.ReadTempFile(uprev_path) |
| self.assertEqual( |
| "".join(ebuild_list_of_str).format( |
| changing_cwp_loc=cwp_new_loc, |
| changing_cwp_ver=cwp_new_ver, |
| ), |
| new_contents, |
| ) |
| |
| def testPrepareVerifiedKernelCwpAfdoFileOldEbuild(self): |
| """Test PrepareVerifiedKernelCwpAfdoFile and patch old ebuild.""" |
| ebuild_data = ( |
| "# some comment\n", |
| 'AFDO_LOCATION="{changing_cwp_loc}"\n', |
| 'AFDO_PROFILE_VERSION="{changing_cwp_ver}"', |
| ) |
| self.callPrepareVerifiedKernelCwpAfdoFile(ebuild_data) |
| |
| def testPrepareVerifiedKernelCwpAfdoFileNewEbuild(self): |
| """Test PrepareVerifiedKernelCwpAfdoFile and patch new ebuild.""" |
| ebuild_data = ( |
| "# some comment\n", |
| 'export AFDO_LOCATION="{changing_cwp_loc}"\n', |
| 'export AFDO_PROFILE_VERSION="{changing_cwp_ver}"', |
| ) |
| self.callPrepareVerifiedKernelCwpAfdoFile(ebuild_data) |
| |
| def testPrepareVerifiedKernelCwpAfdoFileArm(self): |
| """Test PrepareVerifiedKernelCwpAfdoFile with the Arm profile.""" |
| cwp_old_ver = "R99-14469.8-1644229953" |
| cwp_new_ver = "R100-14496.0-1644834841" |
| # changing_cwp_ver is going to be resolved to cwp_old_ver |
| # before Prepare() and cwp_new_ver after. |
| fixed_version = cwp_old_ver |
| ebuild_data = ( |
| "# some comment\n", |
| 'AFDO_LOCATION="{changing_cwp_loc}"\n', |
| f'AFDO_PROFILE_VERSION="{fixed_version}"\n', |
| 'ARM_AFDO_PROFILE_VERSION="{changing_cwp_ver}"', |
| ) |
| # Overwrite profile_info with arm profile info. |
| self.profile_info = { |
| "kernel_version": "5.14", |
| "arch": "arm", |
| } |
| self.callPrepareVerifiedKernelCwpAfdoFile( |
| ebuild_data, cwp_old_ver=cwp_old_ver, cwp_new_ver=cwp_new_ver |
| ) |
| |
| def testPrepareVerifiedKernelCwpAfdoFileArmAndAmd64(self): |
| """Test PrepareVerifiedKernelCwpAfdoFile with the Amd64 profile.""" |
| cwp_old_ver = "R99-14469.8-1644229953" |
| cwp_new_ver = "R100-14496.0-1644834841" |
| # changing_cwp_ver is going to be resolved to cwp_old_ver |
| # before Prepare() and cwp_new_ver after. |
| fixed_version = cwp_old_ver |
| # Ebuild contains both Arm and Amd64 version but we change only Amd64. |
| ebuild_data = ( |
| "# some comment\n", |
| 'AFDO_LOCATION="{changing_cwp_loc}"\n', |
| 'AFDO_PROFILE_VERSION="{changing_cwp_ver}"\n', |
| f'ARM_AFDO_PROFILE_VERSION="{fixed_version}"', |
| ) |
| self.callPrepareVerifiedKernelCwpAfdoFile( |
| ebuild_data, cwp_old_ver=cwp_old_ver, cwp_new_ver=cwp_new_ver |
| ) |
| |
| def mockFindLatestAFDOArtifact(self, gs_urls, _, arch=None): |
| """Return artifacts from bench and cwp gs buckets.""" |
| if not arch: |
| arch = "amd64" |
| atom_cwp_location = os.path.join(self.cwp_gs_location, "atom") |
| arm_cwp_location = os.path.join(self.cwp_gs_location, "arm") |
| if toolchain_util.BENCHMARK_AFDO_GS_URL in gs_urls: |
| if arch == "amd64": |
| return os.path.join( |
| toolchain_util.BENCHMARK_AFDO_GS_URL, |
| "chromeos-chrome-amd64-78.0.3839.0_rc-r1.afdo.bz2", |
| ) |
| if arch == "arm": |
| return os.path.join( |
| toolchain_util.BENCHMARK_AFDO_GS_URL, |
| "chromeos-chrome-arm-78.0.3840.0_rc-r1.afdo.bz2", |
| ) |
| raise toolchain_util.NoProfilesInGsBucketError("no profiles") |
| if atom_cwp_location in gs_urls: |
| # Profile is 1-week old compared to self.now. |
| return os.path.join( |
| atom_cwp_location, |
| f"R78-3876.0-{self.week_old_ts}.afdo.xz", |
| ) |
| if arm_cwp_location in gs_urls: |
| # Profile is 1-week old compared to self.now. |
| return os.path.join( |
| arm_cwp_location, |
| f"R78-3879.0-{self.day_old_ts}.afdo.xz", |
| ) |
| raise toolchain_util.NoProfilesInGsBucketError("no profiles") |
| |
| def setupPrepareVerifiedReleaseAfdoFileMocks(self): |
| self.PatchObject( |
| self.obj, |
| "_FindLatestAFDOArtifact", |
| side_effect=self.mockFindLatestAFDOArtifact, |
| ) |
| self.PatchObject(self.obj.chroot, "tempdir", return_value=self.tempdir) |
| self.PatchObject(self.obj, "_MergeAFDOProfiles") |
| self.PatchObject(self.obj, "_ProcessAFDOProfile") |
| self.PatchObject(os, "rename") |
| |
| def testPrepareVerifiedReleaseAfdoFileExists(self): |
| """Test that _PrepareVerifiedReleaseAfdoFile works when POINTLESS.""" |
| profile_info_extra = {"chrome_cwp_profile": "atom"} |
| self.SetUpPrepare( |
| "VerifiedReleaseAfdoFile", |
| { |
| "UnverifiedChromeBenchmarkAfdoFile": [ |
| self.benchmark_gs_location |
| ], |
| "UnverifiedChromeCwpAfdoFile": [ |
| os.path.join(self.cwp_gs_location, "atom") |
| ], |
| "VerifiedReleaseAfdoFile": ["gs://path/to/vetted"], |
| }, |
| profile_info_extra=profile_info_extra, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.POINTLESS, self.obj.Prepare() |
| ) |
| self.gs_context.Exists.assert_called_once_with( |
| f"gs://path/to/vetted/{self.verified_afdo_name}.xz" |
| ) |
| # The ebuild is still updated. |
| self.patch_ebuild.assert_called_once() |
| |
| def setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| self, |
| profile_info_extra, |
| input_artifacts=None, |
| ): |
| profile = ( |
| profile_info_extra["chrome_cwp_profile"] |
| if profile_info_extra |
| else "atom" |
| ) |
| if not input_artifacts: |
| input_artifacts = { |
| "UnverifiedChromeBenchmarkAfdoFile": [ |
| toolchain_util.BENCHMARK_AFDO_GS_URL |
| ], |
| "UnverifiedChromeCwpAfdoFile": [ |
| os.path.join(toolchain_util.CWP_AFDO_GS_URL, profile) |
| ], |
| } |
| |
| self.SetUpPrepare( |
| "VerifiedReleaseAfdoFile", |
| input_artifacts, |
| profile_info_extra=profile_info_extra, |
| ) |
| |
| def testPrepareVerifiedReleaseAfdoFile(self): |
| """Normal flow, build is needed, all artifacts are present.""" |
| pi_extra = {"chrome_cwp_profile": "atom"} |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=pi_extra, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| # Published artifact is missing, debug binary is present, perf.data is |
| # present. |
| self.gsc_exists.return_value = False |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| expected_exists = [ |
| mock.call( |
| os.path.join( |
| toolchain_util.RELEASE_PROFILE_VETTED_URL, |
| ( |
| "chromeos-chrome-amd64-atom-78-3876.0-" |
| f"{self.week_old_ts}-benchmark-78.0.3839.0-r1" |
| "-redacted.afdo.xz" |
| ), |
| ) |
| ), |
| ] |
| self.assertEqual(expected_exists, self.gs_context.Exists.call_args_list) |
| self.patch_ebuild.assert_called_once_with( |
| self.obj._GetEbuildInfo(toolchain_util.constants.CHROME_PN), |
| { |
| "UNVETTED_AFDO_FILE": os.path.join( |
| os.path.sep, |
| "tmp", |
| ( |
| "chromeos-chrome-amd64-atom-78-3876.0-" |
| f"{self.week_old_ts}-benchmark-78.0.3839.0-r1" |
| "-redacted.afdo" |
| ), |
| ) |
| }, |
| uprev=True, |
| ) |
| |
| def testPrepareVerifiedReleaseAfdoFileArmProfile(self): |
| """Test fresh arm profiles.""" |
| pi_extra = {"chrome_cwp_profile": "arm", "arch": "arm"} |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=pi_extra, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| self.gsc_exists.return_value = False |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| # The merged -arm- profile uses the arm's version 78-3877.0-. |
| self.patch_ebuild.assert_called_once_with( |
| self.obj._GetEbuildInfo(toolchain_util.constants.CHROME_PN), |
| { |
| "UNVETTED_AFDO_FILE": os.path.join( |
| os.path.sep, |
| "tmp", |
| ( |
| f"chromeos-chrome-arm-none-78-3879.0-{self.day_old_ts}-" |
| "benchmark-78.0.3840.0-r1-redacted.afdo" |
| ), |
| ) |
| }, |
| uprev=True, |
| ) |
| |
| def testPrepareVerifiedReleaseAfdoFileMissingInput(self): |
| """Test that _PrepareVerifiedReleaseAfdoFile raises assert.""" |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=None |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| with self.assertRaisesRegex( |
| toolchain_util.PrepareForBuildHandlerError, |
| ( |
| r"Profile name is not set. " |
| r"Is 'chrome_cwp_profile' missing in profile_info?" |
| ), |
| ): |
| self.obj.Prepare() |
| |
| def testPrepareVerifiedReleaseAfdoFileArm32Profile(self): |
| """Test fresh arm profiles for arm32.""" |
| pi_extra = {"chrome_cwp_profile": "arm32", "arch": "arm"} |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=pi_extra, |
| input_artifacts={ |
| "UnverifiedChromeBenchmarkAfdoFile": [ |
| toolchain_util.BENCHMARK_AFDO_GS_URL |
| ], |
| "UnverifiedChromeCwpAfdoFile": [ |
| os.path.join(toolchain_util.CWP_AFDO_GS_URL, "arm") |
| ], |
| }, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| self.gsc_exists.return_value = False |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| # The merged profile must have -arm-arm32-. |
| self.patch_ebuild.assert_called_once_with( |
| self.obj._GetEbuildInfo(toolchain_util.constants.CHROME_PN), |
| { |
| "UNVETTED_AFDO_FILE": os.path.join( |
| os.path.sep, |
| "tmp", |
| ( |
| "chromeos-chrome-arm-arm32-78-3879.0-" |
| f"{self.day_old_ts}-benchmark-78.0.3840.0-r1-redacted" |
| ".afdo" |
| ), |
| ) |
| }, |
| uprev=True, |
| ) |
| |
| def testPrepareVerifiedReleaseAfdoFileExpProfileFromArm(self): |
| """Test experimental profiles on arm.""" |
| pi_extra = {"chrome_cwp_profile": "exp", "arch": "arm"} |
| # cwp location is atom. |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=pi_extra, |
| input_artifacts={ |
| "UnverifiedChromeBenchmarkAfdoFile": [ |
| toolchain_util.BENCHMARK_AFDO_GS_URL |
| ], |
| "UnverifiedChromeCwpAfdoFile": [ |
| os.path.join(toolchain_util.CWP_AFDO_GS_URL, "atom") |
| ], |
| }, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| self.gsc_exists.return_value = False |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| # The merged profile must have -arm-exp- and cwp version from atom |
| # which is 78-3876. |
| self.patch_ebuild.assert_called_once_with( |
| self.obj._GetEbuildInfo(toolchain_util.constants.CHROME_PN), |
| { |
| "UNVETTED_AFDO_FILE": os.path.join( |
| os.path.sep, |
| "tmp", |
| ( |
| "chromeos-chrome-arm-exp-78-3876.0-" |
| f"{self.week_old_ts}-benchmark-78.0.3840.0-r1-redacted" |
| ".afdo" |
| ), |
| ) |
| }, |
| uprev=True, |
| ) |
| |
| def testPrepareVerifiedReleaseAfdoFileExpProfileFromAmd(self): |
| """Test experimental profiles on arm.""" |
| pi_extra = {"chrome_cwp_profile": "exp-amd64", "arch": "arm"} |
| # cwp location is atom. |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=pi_extra, |
| input_artifacts={ |
| "UnverifiedChromeBenchmarkAfdoFile": [ |
| toolchain_util.BENCHMARK_AFDO_GS_URL |
| ], |
| "UnverifiedChromeCwpAfdoFile": [ |
| os.path.join(toolchain_util.CWP_AFDO_GS_URL, "atom") |
| ], |
| }, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| self.gsc_exists.return_value = False |
| self.assertEqual( |
| toolchain_util.PrepareForBuildReturn.NEEDED, self.obj.Prepare() |
| ) |
| # The merged profile has arm arch but uses bench version |
| # from amd. |
| self.patch_ebuild.assert_called_once_with( |
| self.obj._GetEbuildInfo(toolchain_util.constants.CHROME_PN), |
| { |
| "UNVETTED_AFDO_FILE": os.path.join( |
| os.path.sep, |
| "tmp", |
| ( |
| "chromeos-chrome-arm-exp-78-3876.0-" |
| f"{self.week_old_ts}-benchmark-78.0.3839.0-r1-redacted" |
| ".afdo" |
| ), |
| ) |
| }, |
| uprev=True, |
| ) |
| |
| def testPrepareVerifiedReleaseAfdoFileExpInvalidProfile(self): |
| """Test experimental profiles on arm.""" |
| pi_extra = {"chrome_cwp_profile": "exp-invalid", "arch": "arm"} |
| # cwp location is atom. |
| self.setupPrepareVerifiedReleaseAfdoFileInputProperties( |
| profile_info_extra=pi_extra, |
| input_artifacts={ |
| "UnverifiedChromeBenchmarkAfdoFile": [ |
| toolchain_util.BENCHMARK_AFDO_GS_URL |
| ], |
| "UnverifiedChromeCwpAfdoFile": [ |
| os.path.join(toolchain_util.CWP_AFDO_GS_URL, "atom") |
| ], |
| }, |
| ) |
| self.setupPrepareVerifiedReleaseAfdoFileMocks() |
| self.gsc_exists.return_value = False |
| with self.assertRaises(toolchain_util.NoProfilesInGsBucketError): |
| self.obj.Prepare() |
| |
| |
| class BundleArtifactHandlerTest(PrepareBundleTest): |
| """Test BundleArtifactHandler specific methods.""" |
| |
| def setUp(self): |
| def _Bundle(_self): |
| osutils.WriteFile( |
| os.path.join(_self.output_dir, "artifact"), "data\n" |
| ) |
| |
| self.artifact_type = "Unspecified" |
| self.outdir = None |
| self.afdo_tmp_path = None |
| self.kernel_version = "4_4" |
| self.profile_info = { |
| "arch": "amd64", |
| "kernel_version": self.kernel_version.replace("_", "."), |
| } |
| cwp_version = "78-3877.0-1567418235" |
| benchmark_version = "78.0.3893.0" |
| self.afdo_name = f"chromeos-chrome-amd64-{benchmark_version}_rc-r1.afdo" |
| self.perf_name = f"chromeos-chrome-amd64-{benchmark_version}.perf.data" |
| self.release_afdo_name = ( |
| f"chromeos-chrome-amd64-atom-{cwp_version}-" |
| f"benchmark-{benchmark_version}-r1-redacted.afdo.xz" |
| ) |
| self.debug_binary_name = ( |
| f"chromeos-chrome-amd64-{benchmark_version}_rc-r1.debug" |
| ) |
| self.merged_android_afdo_name = ( |
| f"chromeos-chrome-amd64-{benchmark_version}_rc-r1-merged.afdo" |
| ) |
| self.kernel_name = "R89-13638.0-1607337135" |
| |
| self.copy2 = self.PatchObject(shutil, "copy2") |
| self.fetch = self.PatchObject( |
| gob_util, |
| "FetchUrl", |
| return_value=base64.encodebytes(self.release_afdo_name.encode()), |
| ) |
| |
| class mock_datetime: |
| """Class for mocking datetime.datetime.""" |
| |
| @staticmethod |
| def strftime(_when, _fmt): |
| return "DATE" |
| |
| @staticmethod |
| def now(): |
| return -1 |
| |
| self.PatchObject(datetime, "datetime", new=mock_datetime) |
| |
| def SetUpBundle(self, artifact_type): |
| """Set up to test _Bundle${artifactType}.""" |
| self.artifact_type = artifact_type |
| self.outdir = os.path.join(self.tempdir, "tmp", "output_dir") |
| osutils.SafeMakedirs(self.outdir) |
| self.afdo_tmp_path = "/tmp/benchmark-afdo-generate" |
| osutils.SafeMakedirs(self.chroot.full_path(self.afdo_tmp_path)) |
| self.obj = toolchain_util.BundleArtifactHandler( |
| self.artifact_type, |
| self.chroot, |
| self.sysroot, |
| self.board, |
| self.outdir, |
| self.profile_info, |
| ) |
| self.obj._gs_context = self.gs_context |
| |
| def testBundleArtifactHandlerWithoutArchRaises(self): |
| """Test that BundleArtifactHandler w/o arch in profile_info raises.""" |
| self.profile_info = { |
| "chrome_cwp_profile": "atom", |
| # "arch" is missing. |
| } |
| with self.assertRaisesRegex( |
| ValueError, |
| "No 'arch' specified in ArtifactProfileInfo", |
| ): |
| self.SetUpBundle("UnverifiedChromeBenchmarkPerfFile") |
| |
| def mockChrome(self) -> Path: |
| """Generate names and mock the files in fs.""" |
| self.PatchObject( |
| toolchain_util, "CHROME_BINARY_PATH", new="path/out_{board}/chrome" |
| ) |
| chrome_binary = Path( |
| toolchain_util.CHROME_BINARY_PATH.format(board=self.board) |
| ) |
| self.WriteTempFile( |
| self.chroot.full_path(self.sysroot, chrome_binary), |
| "", |
| makedirs=True, |
| ) |
| return chrome_binary |
| |
| def testCheckArgumentsFail(self): |
| """Test arguments checking fails without files existing.""" |
| self.SetUpBundle("UnverifiedChromeBenchmarkAfdoFile") |
| chrome_binary = self.mockChrome() |
| invalid_dir = "/does/not/exist/" |
| |
| invalid_chrome = Path(invalid_dir, "chrome") |
| with self.assertRaisesRegex( |
| toolchain_util.BundleArtifactsHandlerError, |
| f"'{self.chroot.full_path(invalid_chrome)}' chrome binary does not " |
| "exist", |
| ): |
| self.obj._CheckArguments(invalid_chrome) |
| |
| self.obj.output_dir = invalid_dir |
| with self.assertRaisesRegex( |
| toolchain_util.BundleArtifactsHandlerError, |
| f"Non-existent directory '{invalid_dir}' specified for --out-dir", |
| ): |
| self.obj._CheckArguments(chrome_binary) |
| |
| def testBundleChromeClangWarningsFile(self): |
| """Test that BundleChromeClangWarningsFile works.""" |
| self.SetUpBundle("ChromeClangWarningsFile") |
| artifact = os.path.join( |
| self.outdir, "%s.DATE.clang_tidy_warnings.tar.xz" % self.board |
| ) |
| self.assertEqual([artifact], self.obj.Bundle()) |
| self.copy2.assert_called_once_with(mock.ANY, artifact) |
| |
| def testBundleUnverifiedLlvmPgoFile(self, llvm_path="llvm-project"): |
| self.SetUpBundle("UnverifiedLlvmPgoFile") |
| llvm_version = "10.0_pre377782_p20200113-r14" |
| llvm_clang_sha = "a21beccea2020f950845cbb68db663d0737e174c" |
| llvm_pkg = package_info.parse("sys-devel/llvm-%s" % llvm_version) |
| self.PatchObject( |
| self.obj, |
| "_GetProfileNames", |
| return_value=[ |
| self.chroot.full_path( |
| self.sysroot, |
| "build", |
| "coverage_data", |
| "sys-libs", |
| "libcxxabi", |
| "raw_profiles", |
| "libcxxabi-10.0_pre3_1673101222_0.profraw", |
| ) |
| ], |
| ) |
| self.PatchObject( |
| portage_util, "FindPackageNameMatches", return_value=[llvm_pkg] |
| ) |
| self.rc.AddCmdResult( |
| partial_mock.In("clang"), |
| returncode=0, |
| stdout=( |
| f"Chromium OS {llvm_version} clang version 10.0.0 " |
| f"(/path/to/{llvm_path} {llvm_clang_sha})" |
| ), |
| ) |
| base = f"{llvm_pkg.pvr}-{llvm_clang_sha}" |
| artifacts = [ |
| os.path.join(self.outdir, x) |
| for x in ( |
| f"{base}.llvm_metadata.json", |
| "llvm_metadata.json", |
| f"{base}.llvm.profdata.tar.xz", |
| ) |
| ] |
| self.assertEqual(artifacts, self.obj.Bundle()) |
| |
| def testBundleUnverifiedLlvmPgoFileWorkaround(self): |
| self.testBundleUnverifiedLlvmPgoFile("clang") |
| |
| def testBundleUnverifiedChromeBenchmarkPerfFile(self): |
| self.SetUpBundle("UnverifiedChromeBenchmarkPerfFile") |
| self.assertEqual([], self.obj.Bundle()) |
| |
| def testBundleChromeDebugBinary(self): |
| self.SetUpBundle("ChromeDebugBinary") |
| bin_path = toolchain_util._CHROME_DEBUG_BIN % { |
| "root": self.chroot.path, |
| "sysroot": self.sysroot, |
| } |
| osutils.WriteFile(bin_path, "", makedirs=True) |
| output = os.path.join( |
| self.outdir, |
| self.debug_binary_name + toolchain_util.BZ2_COMPRESSION_SUFFIX, |
| ) |
| self.assertEqual([output], self.obj.Bundle()) |
| |
| def testBundleUnverifiedChromeBenchmarkAfdoFile(self): |
| self.SetUpBundle("UnverifiedChromeBenchmarkAfdoFile") |
| self.PatchObject( |
| self.obj, |
| "_GetEbuildInfo", |
| return_value=toolchain_util._EbuildInfo( |
| path=self.chrome_ebuild, CPV=self.chrome_pkg |
| ), |
| ) |
| sym_link_command = self.PatchObject(osutils, "SafeSymlink") |
| # Return ~1MB profile size. |
| self.PatchObject(os.path, "getsize", return_value=100000) |
| |
| chrome_debug_file = os.path.join( |
| self.afdo_tmp_path, self.debug_binary_name |
| ) |
| osutils.WriteFile( |
| self.chroot.full_path(chrome_debug_file), "", makedirs=True |
| ) |
| ret = self.obj.Bundle() |
| afdo_path = os.path.join( |
| self.outdir, self.afdo_name + toolchain_util.BZ2_COMPRESSION_SUFFIX |
| ) |
| self.assertEqual([afdo_path], ret) |
| # Make sure the sym link to debug Chrome is created |
| sym_link_command.assert_called_with( |
| self.debug_binary_name, |
| self.chroot.full_path( |
| os.path.join(self.afdo_tmp_path, "chrome.unstripped") |
| ), |
| ) |
| afdo_path_inside = os.path.join(self.afdo_tmp_path, self.afdo_name) |
| # Make sure commands are executed correctly |
| self.rc.assertCommandContains( |
| [ |
| toolchain_util._AFDO_GENERATE_LLVM_PROF, |
| "--binary=" |
| + os.path.join(self.afdo_tmp_path, "chrome.unstripped"), |
| "--profile=" + os.path.join(self.afdo_tmp_path, self.perf_name), |
| "--out=" + afdo_path_inside, |
| "--sample_threshold_frac=0", |
| ] |
| ) |
| self.rc.assertCommandContains(["bzip2", "-c", afdo_path_inside]) |
| |
| def testBundleUnverifiedChromeBenchmarkAfdoFileLinksMismatchedChrome(self): |
| """Checks that Bundle() is OK with mismatched debuginfo files. |
| |
| Regression test for b/292382163. It's correct, though rare, for us to |
| download an older version of chrome.debug symbols. |
| """ |
| self.SetUpBundle("UnverifiedChromeBenchmarkAfdoFile") |
| self.PatchObject( |
| self.obj, |
| "_GetEbuildInfo", |
| return_value=toolchain_util._EbuildInfo( |
| path=self.chrome_ebuild, CPV=self.chrome_pkg |
| ), |
| ) |
| sym_link_command = self.PatchObject(osutils, "SafeSymlink") |
| # Return ~1MB profile size. |
| self.PatchObject(os.path, "getsize", return_value=100000) |
| mismatched_debug_binary = self.debug_binary_name.replace("-r1", "-r0") |
| # Use a regular assert here since this firing == a bug in the test. |
| assert mismatched_debug_binary != self.debug_binary_name |
| |
| chrome_debug_file = os.path.join( |
| self.afdo_tmp_path, mismatched_debug_binary |
| ) |
| osutils.WriteFile( |
| self.chroot.full_path(chrome_debug_file), "", makedirs=True |
| ) |
| self.obj.Bundle() |
| sym_link_command.assert_called_with( |
| mismatched_debug_binary, |
| self.chroot.full_path( |
| os.path.join(self.afdo_tmp_path, "chrome.unstripped") |
| ), |
| ) |
| |
| def testBundleUnverifiedChromeBenchmarkAfdoFileRaisesError(self): |
| self.SetUpBundle("UnverifiedChromeBenchmarkAfdoFile") |
| self.PatchObject( |
| self.obj, |
| "_GetEbuildInfo", |
| return_value=toolchain_util._EbuildInfo( |
| path=self.chrome_ebuild, CPV=self.chrome_pkg |
| ), |
| ) |
| self.PatchObject(osutils, "SafeSymlink") |
| # Return invalid size of the profile. |
| self.PatchObject(os.path, "getsize", return_value=100) |
| |
| with self.assertRaises(toolchain_util.BundleArtifactsHandlerError): |
| self.obj.Bundle() |
| |
| def testBundleChromeAFDOProfileForAndroidLinuxFailWhenNoBenchmark(self): |
| self.SetUpBundle("ChromeAFDOProfileForAndroidLinux") |
| merge_function = self.PatchObject( |
| self.obj, "_CreateAndUploadMergedAFDOProfile" |
| ) |
| with self.assertRaises(AssertionError) as context: |
| self.obj.Bundle() |
| self.assertIn("No new AFDO profile created", str(context.exception)) |
| merge_function.assert_not_called() |
| |
| def testBundleChromeAFDOProfileForAndroidLinuxPass(self): |
| self.SetUpBundle("ChromeAFDOProfileForAndroidLinux") |
| self.PatchObject(os.path, "exists", return_value=True) |
| merge_function = self.PatchObject( |
| self.obj, |
| "_CreateAndUploadMergedAFDOProfile", |
| return_value=self.merged_android_afdo_name, |
| ) |
| |
| ret = self.obj.Bundle() |
| merged_path = os.path.join( |
| self.outdir, |
| self.merged_android_afdo_name |
| + toolchain_util.BZ2_COMPRESSION_SUFFIX, |
| ) |
| self.assertEqual([merged_path], ret) |
| # Make sure merged function is called |
| afdo_path_inside = os.path.join(self.afdo_tmp_path, self.afdo_name) |
| merged_path_inside = os.path.join( |
| self.afdo_tmp_path, self.merged_android_afdo_name |
| ) |
| merge_function.assert_called_with( |
| self.chroot.full_path(afdo_path_inside), |
| self.chroot.full_path(os.path.join(self.afdo_tmp_path)), |
| ) |
| self.rc.assertCommandContains(["bzip2", "-c", merged_path_inside]) |
| |
| def callBundleVerifiedKernelCwpAfdoFile(self, ebuild_data_list): |
| self.SetUpBundle("VerifiedKernelCwpAfdoFile") |
| ebuild_info_path = self.chroot.full_path( |
| "path", "to", "kernel-9999.ebuild" |
| ) |
| ebuild_info = toolchain_util._EbuildInfo( |
| path=ebuild_info_path, CPV=mock.MagicMock() |
| ) |
| self.PatchObject(self.obj, "_GetEbuildInfo", return_value=ebuild_info) |
| ebuild_old_str = "".join(ebuild_data_list) |
| # We are going to check how the mock_object was called. |
| self.WriteTempFile( |
| ebuild_info_path, |
| ebuild_old_str, |
| makedirs=True, |
| ) |
| |
| ret = self.obj.Bundle() |
| |
| profile_name = self.kernel_name + ( |
| toolchain_util.KERNEL_AFDO_COMPRESSION_SUFFIX |
| ) |
| verified_profile = os.path.join(self.outdir, profile_name) |
| self.assertEqual([verified_profile], ret) |
| profile_path = self.chroot.full_path( |
| self.sysroot, |
| "usr", |
| "lib", |
| "debug", |
| "boot", |
| f"chromeos-kernel-{self.kernel_version}-{profile_name}", |
| ) |
| self.copy2.assert_called_once_with(profile_path, verified_profile) |
| |
| def testBundleVerifiedKernelCwpAfdoFileOld(self): |
| """Test BundleVerifiedKernelCwpAfdoFile with the old ebuild.""" |
| ebuild_data_list = ( |
| "# some comment\n", |
| 'AFDO_LOCATION=""\n', |
| f'AFDO_PROFILE_VERSION="{self.kernel_name}"', |
| ) |
| self.callBundleVerifiedKernelCwpAfdoFile(ebuild_data_list) |
| |
| def testBundleVerifiedKernelCwpAfdoFileNew(self): |
| """Test BundleVerifiedKernelCwpAfdoFile with the new ebuild.""" |
| ebuild_data_list = ( |
| "# some comment\n", |
| 'export AFDO_LOCATION=""\n', |
| f'export AFDO_PROFILE_VERSION="{self.kernel_name}"', |
| ) |
| self.callBundleVerifiedKernelCwpAfdoFile(ebuild_data_list) |
| |
| def testBundleVerifiedKernelCwpAfdoFileArm(self): |
| """Test BundleVerifiedKernelCwpAfdoFile with the old ebuild.""" |
| unchanged_profile = "R100-14496.0-1644834841" |
| ebuild_data_list = ( |
| "# some comment\n", |
| 'AFDO_LOCATION=""\n', |
| f'AFDO_PROFILE_VERSION="{unchanged_profile}"', |
| f'ARM_AFDO_PROFILE_VERSION="{self.kernel_name}"', |
| ) |
| # Overwrite profile_info with arm profile info. |
| self.profile_info = { |
| "kernel_version": "5.15", |
| "arch": "arm", |
| } |
| # Expected kernel ebuild version. |
| self.kernel_version = "5_15" |
| self.callBundleVerifiedKernelCwpAfdoFile(ebuild_data_list) |
| |
| def testBundleVerifiedKernelCwpAfdoFileRaises(self): |
| """Test that BundleVerifiedKernelCwpAfdoFile raises exception.""" |
| # AFDO_PROFILE_VERSION is missing in the ebuild. |
| ebuild_data_list = ("# some comment\n", 'AFDO_LOCATION=""') |
| with self.assertRaisesRegex( |
| toolchain_util.BundleArtifactsHandlerError, |
| "Could not find AFDO_PROFILE_VERSION in " |
| f"chromeos-kernel-{self.kernel_version}", |
| ): |
| self.callBundleVerifiedKernelCwpAfdoFile(ebuild_data_list) |
| |
| def runToolchainBundleTest( |
| self, artifact_path, tarball_name, input_files, expected_output_files |
| ): |
| """Asserts that the given artifact_path is tarred up properly. |
| |
| If no output files are expected, we assert that no tarballs are created. |
| |
| Args: |
| artifact_path: the path to touch |input_files| in. |
| tarball_name: the expected name of the tarball we will produce. |
| input_files: a list of files to |touch| relative to |artifact_path|. |
| expected_output_files: a list of files that should be present in the |
| tarball. |
| |
| Returns: |
| Nothing. |
| """ |
| with mock.patch.object( |
| cros_build_lib, "CreateTarball" |
| ) as create_tarball_mock: |
| in_chroot_dirs = [ |
| artifact_path, |
| f"/build/{self.board}{artifact_path}", |
| ] |
| for d in (self.chroot.full_path(x) for x in in_chroot_dirs): |
| for l in input_files: |
| self.WriteTempFile(os.path.join(d, l), "", makedirs=True) |
| |
| tarball = self.obj.Bundle() |
| |
| if len(expected_output_files) > 0: |
| tarball_path = os.path.join(self.outdir, tarball_name) |
| self.assertEqual(tarball, [tarball_path]) |
| |
| create_tarball_mock.assert_called_once() |
| output, _tempdir = create_tarball_mock.call_args[0] |
| self.assertEqual(output, tarball_path) |
| inputs = create_tarball_mock.call_args[1]["inputs"] |
| self.assertCountEqual(expected_output_files, inputs) |
| else: |
| # Bundlers do not create tarballs when no artifacts are found. |
| self.assertEqual(tarball, []) |
| |
| def testBundleToolchainWarningLogs(self): |
| self.SetUpBundle("ToolchainWarningLogs") |
| artifact_path = "/tmp/fatal_clang_warnings" |
| tarball_name = "%s.DATE.fatal_clang_warnings.tar.xz" % self.board |
| |
| # Test behaviour when no artifacts are found. |
| self.runToolchainBundleTest(artifact_path, tarball_name, [], []) |
| |
| # Test behaviour when artifacts are found. |
| self.runToolchainBundleTest( |
| artifact_path, |
| tarball_name, |
| input_files=("log1.json", "log2.json", "log3.notjson", "log4"), |
| expected_output_files=( |
| "log1.json", |
| "log10.json", |
| "log2.json", |
| "log20.json", |
| ), |
| ) |
| |
| def testBundleClangCrashDiagnoses(self): |
| self.SetUpBundle("ClangCrashDiagnoses") |
| artifact_path = "/tmp/clang_crash_diagnostics" |
| tarball_name = "%s.DATE.clang_crash_diagnoses.tar.xz" % self.board |
| |
| # Test behaviour when no artifacts are found. |
| self.runToolchainBundleTest(artifact_path, tarball_name, [], []) |
| |
| # Test behaviour when artifacts are found. |
| self.runToolchainBundleTest( |
| artifact_path, |
| tarball_name, |
| input_files=("1.cpp", "1.sh", "2.cc", "2.sh", "foo/bar.sh"), |
| expected_output_files=( |
| "1.cpp", |
| "1.sh", |
| "10.cpp", |
| "10.sh", |
| "2.cc", |
| "2.sh", |
| "20.cc", |
| "20.sh", |
| "foo/bar.sh", |
| "foo/bar0.sh", |
| ), |
| ) |
| |
| def testBundleCompilerRusageLogs(self): |
| self.SetUpBundle("CompilerRusageLogs") |
| artifact_path = "/tmp/compiler_rusage" |
| tarball_name = "%s.DATE.compiler_rusage_logs.tar.xz" % self.board |
| |
| # Test behaviour when no artifacts are found. |
| self.runToolchainBundleTest(artifact_path, tarball_name, [], []) |
| |
| # Test behaviour when artifacts are found. |
| self.runToolchainBundleTest( |
| artifact_path, |
| tarball_name, |
| input_files=( |
| "good1.json", |
| "good2.json", |
| "good3.json", |
| "bad1.notjson", |
| "bad2", |
| "json", |
| ), |
| expected_output_files=( |
| "good1.json", |
| "good2.json", |
| "good3.json", |
| "good10.json", |
| "good20.json", |
| "good30.json", |
| ), |
| ) |
| |
| |
| class ReleaseChromeAFDOProfileTest(PrepareBundleTest): |
| """Test functions related to create a release CrOS profile. |
| |
| Since these functions are similar to _UploadReleaseChromeAFDO() and |
| related functions. These tests are also similar to |
| UploadReleaseChromeAFDOTest, except the setup are within recipe |
| environment. |
| """ |
| |
| def setUp(self): |
| self.cwp_name = "R77-3809.38-1562580965.afdo" |
| self.cwp_full = self.cwp_name + toolchain_util.XZ_COMPRESSION_SUFFIX |
| self.arch = "atom" |
| self.benchmark_name = "chromeos-chrome-amd64-77.0.3849.0_rc-r1.afdo" |
| self.benchmark_full = ( |
| self.benchmark_name + toolchain_util.BZ2_COMPRESSION_SUFFIX |
| ) |
| cwp_string = "%s-77-3809.38-1562580965" % self.arch |
| benchmark_string = "benchmark-77.0.3849.0-r1" |
| self.merged_name = "chromeos-chrome-amd64-%s-%s" % ( |
| cwp_string, |
| benchmark_string, |
| ) |
| self.redacted_name = self.merged_name + "-redacted.afdo" |
| self.cwp_url = os.path.join( |
| toolchain_util.CWP_AFDO_GS_URL, self.arch, self.cwp_full |
| ) |
| self.benchmark_url = os.path.join( |
| toolchain_util.BENCHMARK_AFDO_GS_URL, self.benchmark_full |
| ) |
| self.merge_inputs = [ |
| ( |
| os.path.join(self.chroot.tmp, self.cwp_name), |
| toolchain_util.RELEASE_CWP_MERGE_WEIGHT, |
| ), |
| ( |
| os.path.join(self.chroot.tmp, self.benchmark_name), |
| toolchain_util.RELEASE_BENCHMARK_MERGE_WEIGHT, |
| ), |
| ] |
| self.merge_output = os.path.join(self.chroot.tmp, self.merged_name) |
| |
| self.gs_copy = self.PatchObject(self.gs_context, "Copy") |
| self.decompress = self.PatchObject(cros_build_lib, "UncompressFile") |
| |
| def testMergeAFDOProfiles(self): |
| self.obj._MergeAFDOProfiles(self.merge_inputs, self.merge_output) |
| merge_command = [ |
| "llvm-profdata", |
| "merge", |
| "-sample", |
| "-output=" + self.chroot.chroot_path(self.merge_output), |
| ] + [ |
| "-weighted-input=%d,%s" % (weight, self.chroot.chroot_path(name)) |
| for name, weight in self.merge_inputs |
| ] |
| self.rc.assertCommandContains(merge_command) |
| |
| def runProcessAFDOProfileOnce( |
| self, |
| expected_commands, |
| input_path=None, |
| output_path=None, |
| *args, |
| **kwargs, |
| ): |
| if not input_path: |
| input_path = self.chroot.full_path("input.afdo") |
| if not output_path: |
| output_path = self.chroot.full_path("output.afdo") |
| # Return ~1MB profile size. |
| self.PatchObject(os.path, "getsize", return_value=100000) |
| self.obj._ProcessAFDOProfile(input_path, output_path, *args, **kwargs) |
| |
| for expected_command in expected_commands: |
| self.rc.assertCommandContains(expected_command) |
| |
| def testProcessAFDOProfileForAndroidLinuxProfile(self): |
| """Test call on _processAFDOProfile() for Android/Linux profiles.""" |
| input_path = self.chroot.full_path("android.prof.afdo") |
| input_path_inchroot = self.chroot.chroot_path(input_path) |
| input_to_text = input_path_inchroot + ".text.temp" |
| removed_temp = input_path_inchroot + ".removed.temp" |
| reduced_temp = input_path_inchroot + ".reduced.tmp" |
| reduce_functions = 70000 |
| output_path = self.chroot.full_path("android.prof.output.afdo") |
| expected_commands = [ |
| [ |
| "llvm-profdata", |
| "merge", |
| "-sample", |
| "-text", |
| input_path_inchroot, |
| "-output", |
| input_to_text, |
| ], |
| [ |
| "remove_indirect_calls", |
| "--input=" + input_to_text, |
| "--output=" + removed_temp, |
| ], |
| [ |
| "remove_cold_functions", |
| "--input=" + removed_temp, |
| "--output=" + reduced_temp, |
| "--number=" + str(reduce_functions), |
| ], |
| [ |
| "llvm-profdata", |
| "merge", |
| "-sample", |
| reduced_temp, |
| "-output", |
| self.chroot.chroot_path(output_path), |
| ], |
| ] |
| |
| self.runProcessAFDOProfileOnce( |
| expected_commands, |
| input_path=input_path, |
| output_path=output_path, |
| remove=True, |
| reduce_functions=reduce_functions, |
| ) |
| |
| def testProcessAFDOProfileRaisesError(self): |
| input_path = self.chroot.full_path("input.afdo") |
| output_path = self.chroot.full_path("output.afdo") |
| # Return invalid size of the profile. |
| self.PatchObject(os.path, "getsize", return_value=100) |
| with self.assertRaises(toolchain_util.BundleArtifactsHandlerError): |
| self.obj._ProcessAFDOProfile(input_path, output_path) |
| |
| def testProcessAFDOProfileForChromeOSReleaseProfile(self): |
| """Test call on _processAFDOProfile() for CrOS release profiles.""" |
| input_path = self.chroot.full_path(self.merged_name) |
| input_path_inchroot = self.chroot.chroot_path(input_path) |
| input_to_text = input_path_inchroot + ".text.temp" |
| redacted_temp = input_path_inchroot + ".redacted.temp" |
| removed_temp = input_path_inchroot + ".removed.temp" |
| reduced_temp = input_path_inchroot + ".reduced.tmp" |
| reduce_functions = 70000 |
| output_path = self.chroot.full_path(self.redacted_name) |
| osutils.WriteFile( |
| self.chroot.full_path(input_to_text), "", makedirs=True |
| ) |
| |
| expected_commands = [ |
| [ |
| "llvm-profdata", |
| "merge", |
| "-sample", |
| "-text", |
| input_path_inchroot, |
| "-output", |
| input_to_text, |
| ], |
| ["redact_textual_afdo_profile"], |
| [ |
| "remove_indirect_calls", |
| "--input=" + redacted_temp, |
| "--output=" + removed_temp, |
| ], |
| [ |
| "remove_cold_functions", |
| "--input=" + removed_temp, |
| "--output=" + reduced_temp, |
| "--number=" + str(reduce_functions), |
| ], |
| [ |
| "llvm-profdata", |
| "merge", |
| "-sample", |
| reduced_temp, |
| "-output", |
| self.chroot.chroot_path(output_path), |
| "--extbinary", |
| ], |
| ] |
| self.runProcessAFDOProfileOnce( |
| expected_commands, |
| input_path=input_path, |
| output_path=output_path, |
| redact=True, |
| remove=True, |
| reduce_functions=reduce_functions, |
| extbinary=True, |
| ) |
| |
| def testCreateReleaseChromeAFDO(self): |
| merged_call = self.PatchObject(self.obj, "_MergeAFDOProfiles") |
| process_call = self.PatchObject(self.obj, "_ProcessAFDOProfile") |
| ret = self.obj._CreateReleaseChromeAFDO( |
| self.cwp_url, self.benchmark_url, self.chroot.tmp, self.merged_name |
| ) |
| |
| self.assertEqual(ret, os.path.join(self.chroot.tmp, self.redacted_name)) |
| self.gs_copy.assert_has_calls( |
| [ |
| mock.call( |
| self.cwp_url, os.path.join(self.chroot.tmp, self.cwp_full) |
| ), |
| mock.call( |
| self.benchmark_url, |
| os.path.join(self.chroot.tmp, self.benchmark_full), |
| ), |
| ] |
| ) |
| |
| # Check decompress files. |
| decompress_calls = [ |
| mock.call( |
| os.path.join(self.chroot.tmp, self.cwp_full), |
| os.path.join(self.chroot.tmp, self.cwp_name), |
| ), |
| mock.call( |
| os.path.join(self.chroot.tmp, self.benchmark_full), |
| os.path.join(self.chroot.tmp, self.benchmark_name), |
| ), |
| ] |
| self.decompress.assert_has_calls(decompress_calls) |
| |
| # Check call to merge. |
| merged_call.assert_called_once_with( |
| self.merge_inputs, |
| os.path.join(self.chroot.tmp, self.merged_name), |
| ) |
| |
| # Check calls to redact. |
| process_call.assert_called_once_with( |
| self.merge_output, |
| os.path.join(self.chroot.tmp, self.redacted_name), |
| redact=True, |
| remove=True, |
| reduce_functions=20000, |
| extbinary=True, |
| ) |
| |
| |
| class CreateAndUploadMergedAFDOProfileTest(PrepBundLatestAFDOArtifactTest): |
| """Test CreateAndUploadMergedAFDOProfile and related functions. |
| |
| These tests are mostly coming from cbuildbot/afdo_unittest.py, and are |
| written to adapt to recipe functions. When legacy builders are removed, |
| those tests can be safely preserved by this one. |
| """ |
| |
| @staticmethod |
| def _benchmark_afdo_profile_name( |
| major=0, |
| minor=0, |
| build=0, |
| patch=0, |
| rev=1, |
| merged_suffix=False, |
| compression_suffix=True, |
| arch="amd64", |
| ): |
| suffix = "-merged" if merged_suffix else "" |
| result = ( |
| f"chromeos-chrome-{arch}-{major}.{minor}.{build}.{patch}" |
| f"_rc-r{rev}{suffix}" |
| ) |
| result += toolchain_util.AFDO_SUFFIX |
| if compression_suffix: |
| result += toolchain_util.BZ2_COMPRESSION_SUFFIX |
| return result |
| |
| def setUp(self): |
| self.benchmark_url = "gs://path/to/unvetted" |
| self.obj.input_artifacts = { |
| "UnverifiedChromeBenchmarkAfdoFile": [self.benchmark_url], |
| } |
| self.obj.chroot = self.chroot |
| self.output_dir = os.path.join(self.chroot.path, "tmp", "output_dir") |
| osutils.SafeMakedirs(self.output_dir) |
| self.output_dir_inchroot = self.chroot.chroot_path(self.output_dir) |
| self.now = datetime.datetime.now() |
| |
| def runCreateAndUploadMergedAFDOProfileOnce(self, arch=None, **kwargs): |
| if "unmerged_name" not in kwargs: |
| # Match everything. |
| kwargs["unmerged_name"] = self._benchmark_afdo_profile_name( |
| major=9999, compression_suffix=False |
| ) |
| |
| if "output_dir" not in kwargs: |
| kwargs["output_dir"] = self.output_dir |
| |
| Mocks = collections.namedtuple( |
| "Mocks", |
| [ |
| "gs_context", |
| "find_artifact", |
| "uncompress_file", |
| "compress_file", |
| "process_afdo_profile", |
| ], |
| ) |
| |
| def MockList(*_args, **_kwargs): |
| files = [ |
| self._benchmark_afdo_profile_name(major=10, build=9), |
| self._benchmark_afdo_profile_name(major=10, build=10), |
| self._benchmark_afdo_profile_name( |
| major=10, build=10, merged_suffix=True |
| ), |
| self._benchmark_afdo_profile_name(major=10, build=11), |
| self._benchmark_afdo_profile_name(major=10, build=12), |
| self._benchmark_afdo_profile_name(major=10, build=13), |
| # Profiles with 'arm' have to be filtered out unless Bundle uses |
| # other than 'amd64' arch. |
| self._benchmark_afdo_profile_name( |
| major=10, build=13, arch="arm" |
| ), |
| self._benchmark_afdo_profile_name( |
| major=10, build=13, merged_suffix=True |
| ), |
| self._benchmark_afdo_profile_name( |
| major=10, build=13, patch=1, arch="arm" |
| ), |
| self._benchmark_afdo_profile_name(major=10, build=13, patch=1), |
| self._benchmark_afdo_profile_name(major=10, build=13, patch=2), |
| self._benchmark_afdo_profile_name( |
| major=10, build=13, patch=2, merged_suffix=True |
| ), |
| self._benchmark_afdo_profile_name(major=11, build=14), |
| self._benchmark_afdo_profile_name( |
| major=11, build=14, arch="arm" |
| ), |
| self._benchmark_afdo_profile_name( |
| major=11, build=14, merged_suffix=True |
| ), |
| self._benchmark_afdo_profile_name(major=11, build=15), |
| self._benchmark_afdo_profile_name( |
| major=11, build=15, arch="arm" |
| ), |
| ] |
| |
| results = [] |
| for i, name in enumerate(files): |
| url = os.path.join(self.benchmark_url, name) |
| now = self.now - datetime.timedelta(days=len(files) - i) |
| results.append(self.MockListResult(url=url, creation_time=now)) |
| # Add a random file with the newest timestamp. |
| # The file has to be ignored by the pattern matcher. |
| results.append( |
| self.MockListResult( |
| url="file-to-be-ignored", creation_time=self.now |
| ) |
| ) |
| # Make MockList() a little bit smarter. |
| # Give the list of files glob matching "path" from |
| # gs_context.List(path). fnmatch() is what glob() is eventually |
| # calling. |
| return [ |
| res for res in results if fnmatch.fnmatch(res.url, _args[0]) |
| ] |
| |
| self.gs_context.List = MockList |
| uncompress_file = self.PatchObject(cros_build_lib, "UncompressFile") |
| compress_file = self.PatchObject(cros_build_lib, "CompressFile") |
| process_afdo_profile = self.PatchObject(self.obj, "_ProcessAFDOProfile") |
| unmerged_profile = os.path.join( |
| self.output_dir, kwargs.pop("unmerged_name") |
| ) |
| osutils.Touch(unmerged_profile) |
| kwargs["unmerged_profile"] = unmerged_profile |
| |
| # Change arch based on the test argument. |
| if arch: |
| self.obj.arch = arch |
| merged_name = self.obj._CreateAndUploadMergedAFDOProfile(**kwargs) |
| return merged_name, Mocks( |
| gs_context=self.gs_context, |
| find_artifact=MockList, |
| uncompress_file=uncompress_file, |
| compress_file=compress_file, |
| process_afdo_profile=process_afdo_profile, |
| ) |
| |
| def testCreateAndUploadMergedAFDOProfileErrorWhenProfileInBucket(self): |
| unmerged_name = self._benchmark_afdo_profile_name(major=10, build=13) |
| merged_name = None |
| with self.assertRaises(AssertionError): |
| merged_name, _ = self.runCreateAndUploadMergedAFDOProfileOnce( |
| unmerged_name=unmerged_name |
| ) |
| self.assertIsNone(merged_name) |
| |
| def testCreateAndUploadMergedAFDOProfileMergesBranchProfiles(self): |
| unmerged_name = self._benchmark_afdo_profile_name( |
| major=10, build=13, patch=99, compression_suffix=False |
| ) |
| |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| unmerged_name=unmerged_name |
| ) |
| self.assertIsNotNone(merged_name) |
| |
| def _afdo_name(major, build, patch=0, merged_suffix=False): |
| return self._benchmark_afdo_profile_name( |
| major=major, |
| build=build, |
| patch=patch, |
| merged_suffix=merged_suffix, |
| compression_suffix=False, |
| ) |
| |
| expected_unordered_args = [ |
| "-output=" |
| + os.path.join( |
| self.output_dir_inchroot, |
| "raw-" |
| + _afdo_name(major=10, build=13, patch=99, merged_suffix=True), |
| ) |
| ] + [ |
| "-weighted-input=1," + os.path.join(self.output_dir_inchroot, s) |
| for s in [ |
| _afdo_name(major=10, build=12), |
| _afdo_name(major=10, build=13), |
| _afdo_name(major=10, build=13, patch=1), |
| _afdo_name(major=10, build=13, patch=2), |
| _afdo_name(major=10, build=13, patch=99), |
| ] |
| ] |
| |
| # Note that these should all be in-chroot names. |
| expected_ordered_args = ["llvm-profdata", "merge", "-sample"] |
| |
| args = cros_build_lib.run.call_args[0][0] |
| ordered_args = args[: len(expected_ordered_args)] |
| self.assertEqual(ordered_args, expected_ordered_args) |
| |
| unordered_args = args[len(expected_ordered_args) :] |
| |
| self.assertCountEqual(unordered_args, expected_unordered_args) |
| self.assertEqual(mocks.gs_context.Copy.call_count, 4) |
| |
| def testCreateAndUploadMergedAFDOProfileRemovesIndirectCallTargets(self): |
| unmerged_name = self._benchmark_afdo_profile_name( |
| major=10, build=13, patch=99, compression_suffix=False |
| ) |
| |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| recent_to_merge=2, unmerged_name=unmerged_name |
| ) |
| self.assertIsNotNone(merged_name) |
| |
| def _afdo_name(major, build, patch=0, merged_suffix=False): |
| return self._benchmark_afdo_profile_name( |
| major=major, |
| build=build, |
| patch=patch, |
| merged_suffix=merged_suffix, |
| compression_suffix=False, |
| ) |
| |
| merge_output_name = "raw-" + _afdo_name( |
| major=10, build=13, patch=99, merged_suffix=True |
| ) |
| self.assertNotEqual(merged_name, merge_output_name) |
| |
| expected_unordered_args = [ |
| "-output=" |
| + os.path.join(self.output_dir_inchroot, merge_output_name), |
| "-weighted-input=1," |
| + os.path.join( |
| self.output_dir_inchroot, |
| _afdo_name(major=10, build=13, patch=2), |
| ), |
| "-weighted-input=1," |
| + os.path.join( |
| self.output_dir_inchroot, |
| _afdo_name(major=10, build=13, patch=99), |
| ), |
| ] |
| |
| # Note that these should all be in-chroot names. |
| expected_ordered_args = ["llvm-profdata", "merge", "-sample"] |
| args = cros_build_lib.run.call_args[0][0] |
| ordered_args = args[: len(expected_ordered_args)] |
| self.assertEqual(ordered_args, expected_ordered_args) |
| |
| unordered_args = args[len(expected_ordered_args) :] |
| self.assertCountEqual(unordered_args, expected_unordered_args) |
| |
| mocks.process_afdo_profile.assert_called_once_with( |
| os.path.join(self.output_dir, merge_output_name), |
| os.path.join(self.output_dir, merged_name), |
| redact=False, |
| remove=True, |
| reduce_functions=70000, |
| extbinary=False, |
| ) |
| |
| def testCreateAndUploadMergedAFDOProfileRedactsProfileOnArm(self): |
| prof = self._benchmark_afdo_profile_name( |
| major=9999, compression_suffix=False, arch="arm" |
| ) |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| unmerged_name=prof, arch="arm" |
| ) |
| self.assertIsNotNone(merged_name) |
| mocks.process_afdo_profile.assert_called_once_with( |
| os.path.join(self.output_dir, "raw-" + merged_name), |
| os.path.join(self.output_dir, merged_name), |
| redact=True, |
| remove=True, |
| reduce_functions=70000, |
| extbinary=False, |
| ) |
| |
| def testCreateAndUploadMergedAFDOProfileWorksInTheHappyCase(self): |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce() |
| self.assertIsNotNone(merged_name) |
| |
| # Note that we always return the *basename* |
| self.assertEqual( |
| merged_name, |
| self._benchmark_afdo_profile_name( |
| major=9999, merged_suffix=True, compression_suffix=False |
| ), |
| ) |
| |
| cros_build_lib.run.assert_called_once() |
| |
| # Note that these should all be in-chroot names. |
| expected_ordered_args = ["llvm-profdata", "merge", "-sample"] |
| |
| def _afdo_name(major, build=0, patch=0, merged_suffix=False): |
| return self._benchmark_afdo_profile_name( |
| major=major, |
| build=build, |
| patch=patch, |
| merged_suffix=merged_suffix, |
| compression_suffix=False, |
| ) |
| |
| input_afdo_names = [ |
| _afdo_name(major=10, build=13, patch=1), |
| _afdo_name(major=10, build=13, patch=2), |
| _afdo_name(major=11, build=14), |
| _afdo_name(major=11, build=15), |
| _afdo_name(major=9999), |
| ] |
| |
| output_afdo_name = _afdo_name(major=9999, merged_suffix=True) |
| expected_unordered_args = [ |
| "-output=" |
| + os.path.join(self.output_dir_inchroot, "raw-" + output_afdo_name) |
| ] + [ |
| "-weighted-input=1," + os.path.join(self.output_dir_inchroot, n) |
| for n in input_afdo_names |
| ] |
| |
| args = cros_build_lib.run.call_args[0][0] |
| ordered_args = args[: len(expected_ordered_args)] |
| self.assertEqual(ordered_args, expected_ordered_args) |
| |
| unordered_args = args[len(expected_ordered_args) :] |
| self.assertCountEqual(unordered_args, expected_unordered_args) |
| self.assertEqual(mocks.gs_context.Copy.call_count, 4) |
| self.assertEqual(mocks.uncompress_file.call_count, 4) |
| |
| def call_for(name): |
| basis = os.path.join(self.output_dir, name) |
| return mock.call( |
| basis + toolchain_util.BZ2_COMPRESSION_SUFFIX, basis |
| ) |
| |
| # The last profile is not compressed, so no need to uncompress it |
| mocks.uncompress_file.assert_has_calls( |
| any_order=True, calls=[call_for(n) for n in input_afdo_names[:-1]] |
| ) |
| |
| def testCreateAndUploadMergedAFDOProfileWorksForArm(self): |
| prof = self._benchmark_afdo_profile_name( |
| major=9999, compression_suffix=False, arch="arm" |
| ) |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| unmerged_name=prof, arch="arm" |
| ) |
| self.assertIsNotNone(merged_name) |
| |
| # Note that we always return the *basename* |
| self.assertEqual( |
| merged_name, |
| self._benchmark_afdo_profile_name( |
| major=9999, |
| merged_suffix=True, |
| compression_suffix=False, |
| arch="arm", |
| ), |
| ) |
| |
| cros_build_lib.run.assert_called_once() |
| |
| # Note that these should all be in-chroot names. |
| expected_ordered_args = ["llvm-profdata", "merge", "-sample"] |
| |
| def _afdo_name(major, build=0, patch=0, merged_suffix=False): |
| return self._benchmark_afdo_profile_name( |
| major=major, |
| build=build, |
| patch=patch, |
| merged_suffix=merged_suffix, |
| compression_suffix=False, |
| arch="arm", |
| ) |
| |
| input_afdo_names = [ |
| _afdo_name(major=10, build=13), |
| _afdo_name(major=10, build=13, patch=1), |
| _afdo_name(major=11, build=14), |
| _afdo_name(major=11, build=15), |
| _afdo_name(major=9999), |
| ] |
| |
| output_afdo_name = _afdo_name(major=9999, merged_suffix=True) |
| expected_unordered_args = [ |
| "-output=" |
| + os.path.join(self.output_dir_inchroot, "raw-" + output_afdo_name) |
| ] + [ |
| "-weighted-input=1," + os.path.join(self.output_dir_inchroot, n) |
| for n in input_afdo_names |
| ] |
| |
| args = cros_build_lib.run.call_args[0][0] |
| ordered_args = args[: len(expected_ordered_args)] |
| self.assertEqual(ordered_args, expected_ordered_args) |
| |
| unordered_args = args[len(expected_ordered_args) :] |
| self.assertCountEqual(unordered_args, expected_unordered_args) |
| self.assertEqual(mocks.gs_context.Copy.call_count, 4) |
| self.assertEqual(mocks.uncompress_file.call_count, 4) |
| |
| def call_for(name): |
| basis = os.path.join(self.output_dir, name) |
| return mock.call( |
| basis + toolchain_util.BZ2_COMPRESSION_SUFFIX, basis |
| ) |
| |
| # The last profile is not compressed, so no need to uncompress it |
| mocks.uncompress_file.assert_has_calls( |
| any_order=True, calls=[call_for(n) for n in input_afdo_names[:-1]] |
| ) |
| |
| def testMergeIsOKIfWeFindFewerProfilesThanWeWant(self): |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| recent_to_merge=1000, max_age_days=1000 |
| ) |
| self.assertIsNotNone(merged_name) |
| self.assertEqual(mocks.gs_context.Copy.call_count, 9) |
| |
| def testNoFilesAfterUnmergedNameAreIncluded(self): |
| max_name = self._benchmark_afdo_profile_name( |
| major=10, build=11, patch=2, compression_suffix=False |
| ) |
| # Profiles in this test can be older than 14 days. |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| unmerged_name=max_name, max_age_days=21 |
| ) |
| self.assertIsNotNone(merged_name) |
| |
| self.assertEqual( |
| self._benchmark_afdo_profile_name( |
| major=10, |
| build=11, |
| patch=2, |
| merged_suffix=True, |
| compression_suffix=False, |
| ), |
| merged_name, |
| ) |
| |
| def _afdo_name(major, build, patch=0, merged_suffix=False): |
| return self._benchmark_afdo_profile_name( |
| major=major, |
| build=build, |
| patch=patch, |
| merged_suffix=merged_suffix, |
| compression_suffix=False, |
| ) |
| |
| # Note that these should all be in-chroot names. |
| expected_ordered_args = ["llvm-profdata", "merge", "-sample"] |
| expected_unordered_args = [ |
| "-output=" |
| + os.path.join( |
| self.output_dir_inchroot, |
| "raw-" |
| + _afdo_name(major=10, build=11, patch=2, merged_suffix=True), |
| ), |
| ] + [ |
| "-weighted-input=1," + os.path.join(self.output_dir_inchroot, s) |
| for s in [ |
| _afdo_name(major=10, build=9), |
| _afdo_name(major=10, build=10), |
| _afdo_name(major=10, build=11), |
| _afdo_name(major=10, build=11, patch=2), |
| ] |
| ] |
| |
| args = cros_build_lib.run.call_args[0][0] |
| ordered_args = args[: len(expected_ordered_args)] |
| self.assertEqual(ordered_args, expected_ordered_args) |
| |
| unordered_args = args[len(expected_ordered_args) :] |
| self.assertCountEqual(unordered_args, expected_unordered_args) |
| |
| self.assertEqual(mocks.gs_context.Copy.call_count, 3) |
| self.assertEqual(mocks.uncompress_file.call_count, 3) |
| |
| def testMergeDoesntHappenIfNoProfilesAreMerged(self): |
| runs = [ |
| self.runCreateAndUploadMergedAFDOProfileOnce(recent_to_merge=1), |
| self.runCreateAndUploadMergedAFDOProfileOnce(max_age_days=0), |
| ] |
| |
| for merged_name, mocks in runs: |
| self.assertIsNone(merged_name) |
| self.gs_context.Copy.assert_not_called() |
| cros_build_lib.run.assert_not_called() |
| mocks.uncompress_file.assert_not_called() |
| mocks.compress_file.assert_not_called() |
| |
| def testCreateAndUploadMergedAFDOProfileNoProfiles(self): |
| unmerged_name = self._benchmark_afdo_profile_name(major=10, build=13) |
| merged_name = None |
| self.gs_context.List = mock.MagicMock() |
| self.gs_context.List.side_effect = gs.GSNoSuchKey("No objects") |
| |
| merged_name = self.obj._CreateAndUploadMergedAFDOProfile( |
| unmerged_name, self.output_dir |
| ) |
| |
| self.assertIsNone(merged_name) |
| |
| |
| class GetUpdatedFilesTest(cros_test_lib.MockTempDirTestCase): |
| """Test functions in class GetUpdatedFilesForCommit.""" |
| |
| def setUp(self): |
| # Prepare a JSON file containing metadata |
| toolchain_util.TOOLCHAIN_UTILS_PATH = self.tempdir |
| osutils.SafeMakedirs(os.path.join(self.tempdir, "afdo_metadata")) |
| self.json_file = os.path.join( |
| self.tempdir, "afdo_metadata/kernel_afdo_4_14.json" |
| ) |
| self.kernel = "4.14" |
| self.kernel_name = self.kernel.replace(".", "_") |
| self.kernel_key_name = f"chromeos-kernel-{self.kernel_name}" |
| self.afdo_sorted_by_freshness = [ |
| "R78-3865.0-1560000000.afdo", |
| "R78-3869.38-1562580965.afdo", |
| "R78-3866.0-1570000000.afdo", |
| ] |
| self.afdo_versions = { |
| self.kernel_key_name: { |
| "name": self.afdo_sorted_by_freshness[1], |
| }, |
| } |
| |
| with open(self.json_file, "w", encoding="utf-8") as f: |
| json.dump(self.afdo_versions, f) |
| self.artifact_path = os.path.join( |
| "/any/path/to/", |
| self.afdo_sorted_by_freshness[2] |
| + toolchain_util.KERNEL_AFDO_COMPRESSION_SUFFIX, |
| ) |
| self.profile_info = { |
| "kernel_version": self.kernel, |
| "arch": "amd64", |
| } |
| |
| def testUpdateKernelMetadataFailureWithInvalidKernel(self): |
| with self.assertRaises(AssertionError) as context: |
| toolchain_util.GetUpdatedFilesHandler._UpdateKernelMetadata( |
| "3.8", None |
| ) |
| self.assertIn("does not exist", str(context.exception)) |
| |
| def testUpdateKernelMetadataFailureWithOlderProfile(self): |
| with self.assertRaises(AssertionError) as context: |
| toolchain_util.GetUpdatedFilesHandler._UpdateKernelMetadata( |
| self.kernel, self.afdo_sorted_by_freshness[0] |
| ) |
| self.assertIn("is not newer than", str(context.exception)) |
| |
| def testUpdateKernelMetadataPass(self): |
| toolchain_util.GetUpdatedFilesHandler._UpdateKernelMetadata( |
| self.kernel, self.afdo_sorted_by_freshness[2] |
| ) |
| # Check changes in JSON file |
| new_afdo_versions = json.loads(osutils.ReadFile(self.json_file)) |
| self.assertEqual(len(self.afdo_versions), len(new_afdo_versions)) |
| self.assertEqual( |
| new_afdo_versions[self.kernel_key_name]["name"], |
| self.afdo_sorted_by_freshness[2], |
| ) |
| # TODO(b/236161656): Fix. |
| # pylint: disable-next=consider-using-dict-items |
| for k in self.afdo_versions: |
| # Make sure other fields are not changed |
| if k != self.kernel_key_name: |
| self.assertEqual(self.afdo_versions[k], new_afdo_versions[k]) |
| |
| def testUpdateKernelProfileMetadata(self): |
| ret_files, ret_commit = toolchain_util.GetUpdatedFiles( |
| "VerifiedKernelCwpAfdoFile", self.artifact_path, self.profile_info |
| ) |
| file_to_update = os.path.join( |
| self.tempdir, |
| "afdo_metadata", |
| f"kernel_afdo_{self.kernel_name}.json", |
| ) |
| self.assertEqual(ret_files, [file_to_update]) |
| self.assertIn("Publish new kernel profiles", ret_commit) |
| self.assertIn( |
| f"Update 4.14 to {self.afdo_sorted_by_freshness[2]}", ret_commit |
| ) |
| |
| def testUpdateFailWithOtherTypes(self): |
| with self.assertRaises( |
| toolchain_util.GetUpdatedFilesForCommitError |
| ) as context: |
| toolchain_util.GetUpdatedFiles("OtherType", "", "") |
| self.assertIn( |
| "has no handler in GetUpdatedFiles", str(context.exception) |
| ) |