| # Copyright 2019 The Chromium OS Authors. All rights reserved. |
| # 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 builtins |
| import collections |
| import datetime |
| import glob |
| import io |
| import json |
| import os |
| 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)) |
| |
| 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 orderfile success |
| orderfile_name = ('chromeos-chrome-orderfile-field-77-3809.38-1562580965' |
| '-benchmark-77.0.3849.0-r1.orderfile.xz') |
| result = toolchain_util._ParseMergedProfileName(orderfile_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 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))) |
| |
| def testGetOrderfileName(self): |
| """Test method _GetOrderfileName and related methods.""" |
| profile_name = ('chromeos-chrome-amd64-atom-77-3809.38-1562580965-' |
| 'benchmark-77.0.3849.0-r1-redacted.afdo.xz') |
| self.PatchObject( |
| toolchain_util, |
| '_GetArtifactVersionInChromium', |
| return_value=profile_name) |
| result = toolchain_util._GetOrderfileName('/path/to/chrome_root') |
| cwp_name = 'field-77-3809.38-1562580965' |
| benchmark_name = 'benchmark-77.0.3849.0-r1' |
| self.assertEqual( |
| result, 'chromeos-chrome-orderfile-%s-%s' % (cwp_name, benchmark_name)) |
| |
| 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('Only kernel afdo is 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.board = 'chell' |
| self.chroot = chroot_lib.Chroot(path=self.tempdir, chrome_root=self.tempdir) |
| self.sysroot = '/build/%s' % self.board |
| self.chrome_package = 'chromeos-chrome' |
| self.kernel_package = 'chromeos-kernel-3_18' |
| self.chrome_PV = 'chromeos-base/chromeos-chrome-78.0.3893.0_rc-r1' |
| self.chrome_ebuild = os.path.realpath( |
| os.path.join( |
| os.path.dirname(__file__), '..', '..', |
| 'src', 'third_party', 'chromiumos-overlay', |
| os.path.dirname(self.chrome_PV), 'chromeos-chrome', |
| '%s.ebuild' % os.path.basename(self.chrome_PV))) |
| 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) |
| self.gs_context = self.PatchObject(self.obj, '_gs_context') |
| self.gsc_list = self.PatchObject(self.gs_context, 'List', return_value=[]) |
| self.data = b'data' |
| 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() |
| |
| def test_GetOrderfileName(self): |
| """Test that GetOrderfileName finds the right answer.""" |
| vers = self.PatchObject( |
| self.obj, |
| '_GetArtifactVersionInGob', |
| return_value=('chromeos-chrome-amd64-atom-78-1111.0-' |
| '157000000-benchmark-78.0.3893.0-r1-redacted.afdo.xz')) |
| self.assertEqual( |
| 'chromeos-chrome-orderfile-field-78-1111.0-' |
| '157000000-benchmark-78.0.3893.0-r1.orderfile', |
| self.obj._GetOrderfileName()) |
| vers.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-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 |
| # Orderfiles |
| ('chromeos-chrome-orderfile-field-78-3877.0-1567418235-' |
| 'benchmark-78.0.3893.0-r1.orderfile.xz', 1.2), # Latest |
| ('chromeos-chrome-orderfile-field-78-3877.0-1567418235-' |
| 'benchmark-78.0.3850.0-r1.orderfile.xz', 2.2), |
| ] |
| |
| 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 testFindLatestAFDOArtifactPassWithBenchmarkAFDO(self): |
| """Test _FindLatestAFDOArtifact returns latest benchmark AFDO.""" |
| latest_afdo = self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._RankValidBenchmarkProfiles) |
| self.assertEqual( |
| latest_afdo, |
| os.path.join(self.gs_url, |
| 'chromeos-chrome-amd64-78.0.3896.0_rc-r1.afdo.bz2')) |
| |
| def testFindLatestAFDOArtifactPassWithOrderfile(self): |
| """Test _FindLatestAFDOArtifact return latest orderfile.""" |
| latest_orderfile = self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._RankValidOrderfiles) |
| self.assertEqual( |
| latest_orderfile, |
| os.path.join( |
| self.gs_url, 'chromeos-chrome-orderfile-field-78-3877.0-1567418235-' |
| 'benchmark-78.0.3893.0-r1.orderfile.xz')) |
| |
| 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_orderfile = self.obj._FindLatestAFDOArtifact( |
| [self.gs_url], self.obj._RankValidOrderfiles) |
| self.assertEqual( |
| latest_orderfile, |
| os.path.join( |
| self.gs_url, 'chromeos-chrome-orderfile-field-78-3877.0-1567418235-' |
| 'benchmark-78.0.3893.0-r1.orderfile.xz')) |
| |
| 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(RuntimeError) as context: |
| self.obj._FindLatestAFDOArtifact([self.gs_url], |
| self.obj._RankValidOrderfiles) |
| self.assertEqual('No files for branch 80 found in %s' % self.gs_url, |
| str(context.exception)) |
| |
| def testFindLatestAFDOArtifactsFindMaxFromInvalidFiles(self): |
| """Test function fails when searching max from list of invalid files.""" |
| mock_gs_list = [ |
| self.MockListResult( |
| url=os.path.join(self.gs_url, 'Invalid-name-but-end-in-78.afdo'), |
| 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._RankValidBenchmarkProfiles) |
| 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 = { |
| 'kernel_version': self.kernel_version.replace('_', '.'), |
| } |
| self.gsc_exists = None |
| self.patch_ebuild = mock.MagicMock() |
| self.orderfile_name = ( |
| 'chromeos-chrome-orderfile-field-78-3877.0-1567418235-' |
| 'benchmark-78.0.3893.0-r1.orderfile') |
| self.afdo_name = 'chromeos-chrome-amd64-78.0.3893.0-r1.afdo' |
| self.PatchObject( |
| toolchain_util._CommonPrepareBundle, |
| '_GetOrderfileName', |
| return_value=self.orderfile_name) |
| self.PatchObject( |
| toolchain_util._CommonPrepareBundle, |
| '_FindLatestOrderfileArtifact', |
| return_value=self.orderfile_name + toolchain_util.XZ_COMPRESSION_SUFFIX) |
| |
| def SetUpPrepare(self, artifact_type, input_artifacts, mock_patch=True): |
| """Set up to test _Prepare${artifactType}.""" |
| self.artifact_type = artifact_type |
| self.input_artifacts = input_artifacts |
| 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.PatchObject(self.obj, '_GetOrderfileName', return_value='orderfile') |
| self.gsc_exists = self.PatchObject( |
| self.gs_context, 'Exists', return_value=True) |
| if mock_patch: |
| self.patch_ebuild = self.PatchObject( |
| toolchain_util._CommonPrepareBundle, '_PatchEbuild') |
| |
| def testPrepareUnverifiedChromeLlvmOrderfileExists(self): |
| """Test that PrepareUnverfiedChromeLlvmOrderfile works when POINTLESS.""" |
| self.SetUpPrepare( |
| 'UnverifiedChromeLlvmOrderfile', |
| {'UnverifiedChromeLlvmOrderfile': ['gs://publish/location']}) |
| self.assertEqual(toolchain_util.PrepareForBuildReturn.POINTLESS, |
| self.obj.Prepare()) |
| self.gs_context.Exists.assert_called_once_with( |
| 'gs://publish/location/orderfile.xz') |
| |
| def testPrepareUnverifiedChromeLlvmOrderfileMissing(self): |
| """Test that PrepareUnverfiedChromeLlvmOrderfile works when NEEDED.""" |
| self.SetUpPrepare( |
| 'UnverifiedChromeLlvmOrderfile', |
| {'UnverifiedChromeLlvmOrderfile': ['gs://publish/location']}) |
| self.gsc_exists.return_value = False |
| self.assertEqual(toolchain_util.PrepareForBuildReturn.NEEDED, |
| self.obj.Prepare()) |
| self.gs_context.Exists.assert_called_once_with( |
| 'gs://publish/location/orderfile.xz') |
| |
| def testPrepareVerifiedChromeLlvmOrderfileExists(self): |
| """Test that PrepareVerfiedChromeLlvmOrderfile works when POINTLESS.""" |
| self.SetUpPrepare( |
| 'VerifiedChromeLlvmOrderfile', { |
| 'UnverifiedChromeLlvmOrderfile': |
| ['gs://path/to/unvetted', 'gs://other/path/to/unvetted'] |
| }) |
| self.assertEqual(toolchain_util.PrepareForBuildReturn.POINTLESS, |
| self.obj.Prepare()) |
| self.gs_context.Exists.assert_called_once_with('gs://path/to/vetted/%s.xz' % |
| self.orderfile_name) |
| # The ebuild is still updated. |
| self.patch_ebuild.assert_called_once() |
| |
| def testPrepareVerifiedChromeLlvmOrderfileMissing(self): |
| """Test that PrepareVerfiedChromeLlvmOrderfile works when NEEDED.""" |
| self.SetUpPrepare( |
| 'VerifiedChromeLlvmOrderfile', { |
| 'UnverifiedChromeLlvmOrderfile': |
| ['gs://path/to/unvetted', 'gs://other/path/to/unvetted'] |
| }) |
| self.gsc_exists.return_value = False |
| self.assertEqual(toolchain_util.PrepareForBuildReturn.NEEDED, |
| self.obj.Prepare()) |
| self.gs_context.Exists.assert_called_once_with('gs://path/to/vetted/%s.xz' % |
| self.orderfile_name) |
| self.patch_ebuild.assert_called_once() |
| |
| def testPrepareUnverifiedChromeBenchmarkAfdoFile(self): |
| self.SetUpPrepare( |
| 'UnverifiedChromeBenchmarkAfdoFile', { |
| 'UnverifiedChromeBenchmarkPerfFile': ['gs://path/to/perfdata'], |
| 'UnverifiedChromeBenchmarkAfdoFile': ['gs://path/to/unvetted'], |
| 'ChromeDebugBinary': ['gs://image-archive/path'], |
| }) |
| # Published artifact is missing, debug binary is present, perf.data is |
| # missing. |
| self.gsc_exists.side_effect = (False, True, False) |
| self.assertEqual(toolchain_util.PrepareForBuildReturn.NEEDED, |
| self.obj.Prepare()) |
| expected = [ |
| mock.call('gs://path/to/unvetted/' |
| 'chromeos-chrome-amd64-78.0.3893.0_rc-r1.afdo.bz2'), |
| mock.call('gs://image-archive/path/' |
| 'chromeos-chrome-amd64-78.0.3893.0_rc-r1.debug.bz2'), |
| mock.call('gs://path/to/perfdata/' |
| 'chromeos-chrome-amd64-78.0.3893.0.perf.data.bz2'), |
| ] |
| self.assertEqual(expected, self.gs_context.Exists.call_args_list) |
| # There is no need to patch the ebuild. |
| self.patch_ebuild.assert_not_called() |
| |
| 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_loc = 'gs://path/to/cwp/kernel/5.4' |
| self.SetUpPrepare( |
| 'VerifiedKernelCwpAfdoFile', { |
| 'UnverifiedKernelCwpAfdoFile': [cwp_loc], |
| 'VerifiedKernelCwpAfdoFile': [cwp_loc], |
| }, mock_patch=False) |
| cwp_old_ver = 'R99-14469.8-1644229953' |
| cwp_new_ver = 'R100-14496.0-1644834841' |
| kernel_cwp = os.path.join(cwp_loc, cwp_new_ver) |
| ebuild_info = toolchain_util._EbuildInfo( |
| path='/path/to/kernel-9999.ebuild', CPV=mock.MagicMock()) |
| |
| self.PatchObject(os, 'rename') |
| self.PatchObject(cros_build_lib, 'run') |
| self.PatchObject(toolchain_util, '_GetProfileAge', return_value=0) |
| self.PatchObject(self.obj, '_GetEbuildInfo', return_value=ebuild_info) |
| 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( |
| kernel_cwp_loc='', kernel_cwp_ver=cwp_old_ver) |
| # We are going to check how the mock_object was called. |
| mock_open = self.PatchObject( |
| builtins, 'open', mock.mock_open(read_data=(ebuild_old_str))) |
| |
| self.obj.Prepare() |
| |
| # Check the expected patched lines. |
| for ebuild_line in ebuild_list_of_str: |
| resolved_line = ebuild_line.format(kernel_cwp_loc=cwp_loc, |
| kernel_cwp_ver=cwp_new_ver) |
| self.assertIn(mock.call().write(resolved_line), mock_open.mock_calls) |
| |
| def testPrepareVerifiedKernelCwpAfdoFileOldEbuild(self): |
| """Test PrepareVerifiedKernelCwpAfdoFile and patch old ebuild.""" |
| ebuild_data = ('# some comment\n', |
| 'AFDO_LOCATION="{kernel_cwp_loc}"\n', |
| 'AFDO_PROFILE_VERSION="{kernel_cwp_ver}"') |
| self.callPrepareVerifiedKernelCwpAfdoFile(ebuild_data) |
| |
| def testPrepareVerifiedKernelCwpAfdoFileNewEbuild(self): |
| """Test PrepareVerifiedKernelCwpAfdoFile and patch new ebuild.""" |
| ebuild_data = ('# some comment\n', |
| 'export AFDO_LOCATION="{kernel_cwp_loc}"\n', |
| 'export AFDO_PROFILE_VERSION="{kernel_cwp_ver}"') |
| self.callPrepareVerifiedKernelCwpAfdoFile(ebuild_data) |
| |
| |
| 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 = { |
| 'kernel_version': self.kernel_version.replace('_', '.'), |
| } |
| self.orderfile_name = ( |
| 'chromeos-chrome-orderfile-field-78-3877.0-1567418235-' |
| 'benchmark-78.0.3893.0-r1.orderfile') |
| self.afdo_name = 'chromeos-chrome-amd64-78.0.3893.0_rc-r1.afdo' |
| self.perf_name = 'chromeos-chrome-amd64-78.0.3893.0.perf.data' |
| self.debug_binary_name = 'chromeos-chrome-amd64-78.0.3893.0_rc-r1.debug' |
| self.merged_afdo_name = ( |
| 'chromeos-chrome-amd64-78.0.3893.0_rc-r1-merged.afdo') |
| self.kernel_name = 'R89-13638.0-1607337135' |
| |
| self.gen_order = self.PatchObject( |
| toolchain_util.GenerateChromeOrderfile, 'Bundle', new=_Bundle) |
| self.PatchObject( |
| toolchain_util, '_GetOrderfileName', return_value=self.orderfile_name) |
| self.copy2 = self.PatchObject(shutil, 'copy2') |
| |
| class mock_datetime(object): |
| """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 testBundleUnverifiedChromeLlvmOrderfile(self): |
| """Test that BundleUnverfiedChromeLlvmOrderfile works.""" |
| self.SetUpBundle('UnverifiedChromeLlvmOrderfile') |
| artifact = os.path.join(self.outdir, 'artifact') |
| self.assertEqual([artifact], self.obj.Bundle()) |
| self.copy2.assert_called_once_with(mock.ANY, artifact) |
| |
| def testBundleVerifiedChromeLlvmOrderfileExists(self): |
| """Test that BundleVerfiedChromeLlvmOrderfile works.""" |
| self.SetUpBundle('VerifiedChromeLlvmOrderfile') |
| self.PatchObject( |
| toolchain_util._CommonPrepareBundle, |
| '_GetArtifactVersionInEbuild', |
| return_value=self.orderfile_name) |
| artifact = os.path.join(self.outdir, '%s.xz' % self.orderfile_name) |
| self.assertEqual([artifact], self.obj.Bundle()) |
| self.copy2.assert_called_once_with( |
| os.path.join(self.chroot.path, 'build', self.board, 'opt/google/chrome', |
| '%s.xz' % self.orderfile_name), artifact) |
| |
| def testBundleVerifiedChromeLlvmOrderfileRaises(self): |
| """Test that BundleVerfiedChromeLlvmOrderfile raises exception.""" |
| self.SetUpBundle('VerifiedChromeLlvmOrderfile') |
| # Chrome ebuild file is missing UNVETTED_ORDERFILE. |
| self.PatchObject( |
| builtins, 'open', mock.mock_open(read_data='')) |
| with self.assertRaisesRegex( |
| toolchain_util.BundleArtifactsHandlerError, |
| f'Could not find UNVETTED_ORDERFILE version in {constants.CHROME_PN}'): |
| self.obj.Bundle() |
| |
| 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()) |
| |
| @mock.patch.object(builtins, 'open') |
| def testBundleUnverifiedChromeBenchmarkAfdoFile(self, mock_open): |
| self.SetUpBundle('UnverifiedChromeBenchmarkAfdoFile') |
| self.PatchObject( |
| self.obj, |
| '_GetEbuildInfo', |
| return_value=toolchain_util._EbuildInfo( |
| path=self.chrome_ebuild, CPV=self.chrome_pkg)) |
| run_command = self.PatchObject(cros_build_lib, 'run') |
| sym_link_command = self.PatchObject(osutils, 'SafeSymlink') |
| # Return ~1MB profile size. |
| self.PatchObject(os.path, 'getsize', return_value=100000) |
| mock_file_obj = io.StringIO() |
| mock_open.return_value = mock_file_obj |
| |
| 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 |
| mock_calls = [ |
| mock.call([ |
| 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', |
| ], |
| enter_chroot=True, |
| print_cmd=True), |
| mock.call(['bzip2', '-c', afdo_path_inside], |
| stdout=mock_file_obj, |
| enter_chroot=True, |
| print_cmd=True) |
| ] |
| run_command.assert_has_calls(mock_calls) |
| |
| @mock.patch.object(builtins, 'open') |
| def testBundleUnverifiedChromeBenchmarkAfdoFileRaisesError(self, mock_open): |
| self.SetUpBundle('UnverifiedChromeBenchmarkAfdoFile') |
| self.PatchObject( |
| self.obj, |
| '_GetEbuildInfo', |
| return_value=toolchain_util._EbuildInfo( |
| path=self.chrome_ebuild, CPV=self.chrome_pkg)) |
| self.PatchObject(cros_build_lib, 'run') |
| self.PatchObject(osutils, 'SafeSymlink') |
| # Return invalid size of the profile. |
| self.PatchObject(os.path, 'getsize', return_value=100) |
| mock_file_obj = io.StringIO() |
| mock_open.return_value = mock_file_obj |
| |
| 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() |
| |
| @mock.patch.object(builtins, 'open') |
| def testBundleChromeAFDOProfileForAndroidLinuxPass(self, mock_open): |
| self.SetUpBundle('ChromeAFDOProfileForAndroidLinux') |
| self.PatchObject(os.path, 'exists', return_value=True) |
| run_command = self.PatchObject(cros_build_lib, 'run') |
| merge_function = self.PatchObject( |
| self.obj, |
| '_CreateAndUploadMergedAFDOProfile', |
| return_value=self.merged_afdo_name) |
| mock_file_obj = io.StringIO() |
| mock_open.return_value = mock_file_obj |
| |
| ret = self.obj.Bundle() |
| merged_path = os.path.join( |
| self.outdir, |
| self.merged_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_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))) |
| run_command.assert_called_with( |
| ['bzip2', '-c', merged_path_inside], |
| stdout=mock_file_obj, |
| enter_chroot=True, |
| print_cmd=True, |
| ) |
| |
| def callBundleVerifiedKernelCwpAfdoFile(self, ebuild_data_list): |
| self.SetUpBundle('VerifiedKernelCwpAfdoFile') |
| ebuild_info = toolchain_util._EbuildInfo( |
| path='/path/to/kernel-9999.ebuild', 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.PatchObject( |
| builtins, 'open', mock.mock_open(read_data=(ebuild_old_str))) |
| |
| 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 = os.path.join( |
| self.chroot.path, self.sysroot[1:], '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 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, |
| '/build/%s%s' % (self.board, artifact_path) |
| ] |
| for d in (self.chroot.full_path(x) for x in in_chroot_dirs): |
| for l in input_files: |
| p = os.path.join(d, l) |
| osutils.SafeMakedirs(os.path.dirname(p)) |
| osutils.Touch(p) |
| |
| 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.tempdir, |
| self.cwp_name), toolchain_util.RELEASE_CWP_MERGE_WEIGHT), |
| (os.path.join(self.tempdir, self.benchmark_name), |
| toolchain_util.RELEASE_BENCHMARK_MERGE_WEIGHT), |
| ] |
| self.merge_output = os.path.join(self.tempdir, self.merged_name) |
| |
| self.gs_copy = self.PatchObject(self.gs_context, 'Copy') |
| self.decompress = self.PatchObject(cros_build_lib, 'UncompressFile') |
| self.run_command = self.PatchObject(cros_build_lib, 'run') |
| |
| 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.run_command.assert_called_once_with( |
| merge_command, enter_chroot=True, print_cmd=True) |
| |
| def runProcessAFDOProfileOnce(self, |
| expected_commands, |
| input_path=None, |
| output_path=None, |
| *args, |
| **kwargs): |
| if not input_path: |
| input_path = os.path.join(self.tempdir, 'input.afdo') |
| if not output_path: |
| output_path = os.path.join(self.tempdir, 'output.afdo') |
| # Return ~1MB profile size. |
| self.PatchObject(os.path, 'getsize', return_value=100000) |
| self.obj._ProcessAFDOProfile(input_path, output_path, *args, **kwargs) |
| |
| self.run_command.assert_has_calls(expected_commands) |
| |
| def testProcessAFDOProfileForAndroidLinuxProfile(self): |
| """Test call on _processAFDOProfile() for Android/Linux profiles.""" |
| input_path = os.path.join(self.tempdir, '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 = os.path.join(self.tempdir, 'android.prof.output.afdo') |
| expected_commands = [ |
| mock.call( |
| [ |
| 'llvm-profdata', |
| 'merge', |
| '-sample', |
| '-text', |
| input_path_inchroot, |
| '-output', |
| input_to_text, |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ), |
| mock.call( |
| [ |
| 'remove_indirect_calls', |
| '--input=' + input_to_text, |
| '--output=' + removed_temp, |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ), |
| mock.call( |
| [ |
| 'remove_cold_functions', |
| '--input=' + removed_temp, |
| '--output=' + reduced_temp, |
| '--number=' + str(reduce_functions), |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ), |
| mock.call( |
| [ |
| 'llvm-profdata', |
| 'merge', |
| '-sample', |
| reduced_temp, |
| '-output', |
| self.chroot.chroot_path(output_path), |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ) |
| ] |
| |
| self.runProcessAFDOProfileOnce( |
| expected_commands, |
| input_path=input_path, |
| output_path=output_path, |
| remove=True, |
| reduce_functions=reduce_functions) |
| |
| def testProcessAFDOProfileRaisesError(self): |
| input_path = os.path.join(self.tempdir, 'input.afdo') |
| output_path = os.path.join(self.tempdir, '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) |
| |
| @mock.patch.object(builtins, 'open') |
| def testProcessAFDOProfileForChromeOSReleaseProfile(self, mock_open): |
| """Test call on _processAFDOProfile() for CrOS release profiles.""" |
| input_path = os.path.join(self.tempdir, 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' |
| redacted_temp_full = input_path + '.redacted.temp' |
| removed_temp = input_path_inchroot + '.removed.temp' |
| reduced_temp = input_path_inchroot + '.reduced.tmp' |
| reduce_functions = 20000 |
| output_path = os.path.join(self.tempdir, self.redacted_name) |
| mock_file_obj = io.StringIO() |
| mock_open.return_value = mock_file_obj |
| |
| expected_commands = [ |
| mock.call( |
| [ |
| 'llvm-profdata', |
| 'merge', |
| '-sample', |
| '-text', |
| input_path_inchroot, |
| '-output', |
| input_to_text, |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ), |
| mock.call( |
| ['redact_textual_afdo_profile'], |
| input=mock_file_obj, |
| stdout=redacted_temp_full, |
| print_cmd=True, |
| enter_chroot=True, |
| ), |
| mock.call( |
| [ |
| 'remove_indirect_calls', |
| '--input=' + redacted_temp, |
| '--output=' + removed_temp, |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ), |
| mock.call( |
| [ |
| 'remove_cold_functions', |
| '--input=' + removed_temp, |
| '--output=' + reduced_temp, |
| '--number=' + str(reduce_functions), |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ), |
| mock.call( |
| [ |
| 'llvm-profdata', |
| 'merge', |
| '-sample', |
| reduced_temp, |
| '-output', |
| self.chroot.chroot_path(output_path), |
| '-compbinary', |
| ], |
| enter_chroot=True, |
| print_cmd=True, |
| ) |
| ] |
| self.runProcessAFDOProfileOnce( |
| expected_commands, |
| input_path=input_path, |
| output_path=output_path, |
| redact=True, |
| remove=True, |
| reduce_functions=reduce_functions, |
| compbinary=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.tempdir, self.merged_name) |
| |
| self.assertEqual(ret, os.path.join(self.tempdir, self.redacted_name)) |
| self.gs_copy.assert_has_calls([ |
| mock.call(self.cwp_url, os.path.join(self.tempdir, self.cwp_full)), |
| mock.call(self.benchmark_url, |
| os.path.join(self.tempdir, self.benchmark_full)) |
| ]) |
| |
| # Check decompress files. |
| decompress_calls = [ |
| mock.call( |
| os.path.join(self.tempdir, self.cwp_full), |
| os.path.join(self.tempdir, self.cwp_name)), |
| mock.call( |
| os.path.join(self.tempdir, self.benchmark_full), |
| os.path.join(self.tempdir, 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.tempdir, self.merged_name), |
| ) |
| |
| # Check calls to redact. |
| process_call.assert_called_once_with( |
| self.merge_output, |
| os.path.join(self.tempdir, self.redacted_name), |
| redact=True, |
| remove=True, |
| reduce_functions=20000, |
| compbinary=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): |
| suffix = '-merged' if merged_suffix else '' |
| result = 'chromeos-chrome-amd64-%d.%d.%d.%d_rc-r%d%s' % ( |
| major, minor, build, patch, 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, **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', |
| 'run_command', |
| '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), |
| self._benchmark_afdo_profile_name( |
| major=10, build=13, merged_suffix=True), |
| 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, merged_suffix=True), |
| self._benchmark_afdo_profile_name(major=11, build=15), |
| ] |
| |
| 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)) |
| return results |
| |
| self.gs_context.List = MockList |
| run_command = self.PatchObject(cros_build_lib, 'run') |
| 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 |
| |
| merged_name = self.obj._CreateAndUploadMergedAFDOProfile(**kwargs) |
| return merged_name, Mocks( |
| gs_context=self.gs_context, |
| find_artifact=MockList, |
| run_command=run_command, |
| 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 = mocks.run_command.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 = mocks.run_command.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, |
| compbinary=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)) |
| |
| mocks.run_command.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 = mocks.run_command.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) |
| merged_name, mocks = self.runCreateAndUploadMergedAFDOProfileOnce( |
| unmerged_name=max_name) |
| 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 = mocks.run_command.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() |
| mocks.run_command.assert_not_called() |
| mocks.uncompress_file.assert_not_called() |
| mocks.compress_file.assert_not_called() |
| |
| |
| 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') 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} |
| |
| 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]) |
| 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)) |
| |
| |
| class GenerateChromeOrderfileTest(cros_test_lib.MockTempDirTestCase): |
| """Test GenerateChromeOrderfile class.""" |
| |
| # pylint: disable=protected-access |
| def setUp(self): |
| self.board = 'board' |
| self.out_dir = os.path.join(self.tempdir, 'outdir') |
| osutils.SafeMakedirs(self.out_dir) |
| self.chroot_dir = os.path.join(self.tempdir, 'chroot') |
| self.working_dir = os.path.join(self.chroot_dir, 'tmp') |
| osutils.SafeMakedirs(self.working_dir) |
| self.working_dir_inchroot = '/tmp' |
| self.chroot_args = [] |
| self.orderfile_name = 'chromeos-chrome-orderfile-1.0' |
| self.PatchObject( |
| toolchain_util, '_GetOrderfileName', return_value=self.orderfile_name) |
| self.test_obj = toolchain_util.GenerateChromeOrderfile( |
| self.board, self.out_dir, '/path/to/chrome_root', self.chroot_dir, |
| self.chroot_args) |
| |
| def testCheckArgumentsFail(self): |
| """Test arguments checking fails without files existing.""" |
| with self.assertRaises( |
| toolchain_util.GenerateChromeOrderfileError) as context: |
| self.test_obj._CheckArguments() |
| |
| self.assertIn('Chrome binary does not exist at', str(context.exception)) |
| |
| def testGenerateChromeNM(self): |
| """Test generating chrome NM is handled correctly.""" |
| chrome_binary = self.test_obj.CHROME_BINARY_PATH.replace( |
| '${BOARD}', self.board) |
| cmd = ['llvm-nm', '-n', chrome_binary] |
| output = os.path.join(self.working_dir, self.orderfile_name + '.nm') |
| self.test_obj.tempdir = self.tempdir |
| self.PatchObject(cros_build_lib, 'run') |
| self.test_obj._GenerateChromeNM() |
| cros_build_lib.run.assert_called_with( |
| cmd, stdout=output, enter_chroot=True, chroot_args=self.chroot_args) |
| |
| def testPostProcessOrderfile(self): |
| """Test post-processing orderfile is handled correctly.""" |
| chrome_nm = os.path.join(self.working_dir_inchroot, |
| self.orderfile_name + '.nm') |
| input_orderfile = self.test_obj.INPUT_ORDERFILE_PATH.replace( |
| '${BOARD}', self.board) |
| output = os.path.join(self.working_dir_inchroot, |
| self.orderfile_name + '.orderfile') |
| self.PatchObject(cros_build_lib, 'run') |
| self.test_obj._PostProcessOrderfile(chrome_nm) |
| cmd = [ |
| self.test_obj.PROCESS_SCRIPT, '--chrome', chrome_nm, '--input', |
| input_orderfile, '--output', output |
| ] |
| cros_build_lib.run.assert_called_with( |
| cmd, |
| enter_chroot=True, |
| chroot_args=self.chroot_args, |
| check=True, |
| capture_output=True) |
| |
| def testSuccessRun(self): |
| """Test the main function is running successfully.""" |
| # Patch the two functions that generate artifacts from inputs that are |
| # non-existent without actually building Chrome |
| chrome_nm = os.path.join(self.working_dir, self.orderfile_name + '.nm') |
| with open(chrome_nm, 'w') as f: |
| print('Write something in the nm file', file=f) |
| self.PatchObject( |
| toolchain_util.GenerateChromeOrderfile, |
| '_GenerateChromeNM', |
| return_value=chrome_nm) |
| chrome_orderfile = os.path.join(self.working_dir, |
| self.orderfile_name + '.orderfile') |
| with open(chrome_orderfile, 'w') as f: |
| print('Write something in the orderfile', file=f) |
| self.PatchObject( |
| toolchain_util.GenerateChromeOrderfile, |
| '_PostProcessOrderfile', |
| return_value=chrome_orderfile) |
| |
| self.PatchObject(toolchain_util.GenerateChromeOrderfile, '_CheckArguments') |
| |
| self.test_obj.Bundle() |
| |
| # Make sure the tarballs are inside the output directory |
| output_files = os.listdir(self.out_dir) |
| self.assertIn( |
| self.orderfile_name + '.nm' + toolchain_util.XZ_COMPRESSION_SUFFIX, |
| output_files) |
| self.assertIn( |
| self.orderfile_name + '.orderfile' + |
| toolchain_util.XZ_COMPRESSION_SUFFIX, output_files) |