| # -*- coding: utf-8 -*- |
| # Copyright (c) 2012 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. |
| |
| """Unittests for the artifact stages.""" |
| |
| from __future__ import print_function |
| |
| import json |
| import os |
| import sys |
| |
| import mock |
| |
| from chromite.cbuildbot import cbuildbot_unittest |
| from chromite.cbuildbot import commands |
| from chromite.lib import constants |
| from chromite.lib import failures_lib |
| from chromite.cbuildbot import prebuilts |
| from chromite.lib import results_lib |
| from chromite.cbuildbot.stages import artifact_stages |
| from chromite.cbuildbot.stages import build_stages_unittest |
| from chromite.cbuildbot.stages import generic_stages_unittest |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_test_lib |
| from chromite.lib import osutils |
| from chromite.lib import parallel |
| from chromite.lib import parallel_unittest |
| from chromite.lib import partial_mock |
| from chromite.lib import path_util |
| from chromite.lib.buildstore import FakeBuildStore |
| |
| from chromite.cbuildbot.stages.generic_stages_unittest import patch |
| from chromite.cbuildbot.stages.generic_stages_unittest import patches |
| |
| # pylint: disable=too-many-ancestors |
| |
| |
| class ArchiveStageTest(generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Exercise ArchiveStage functionality.""" |
| |
| # pylint: disable=protected-access |
| |
| RELEASE_TAG = '' |
| VERSION = '3333.1.0' |
| |
| def _PatchDependencies(self): |
| """Patch dependencies of ArchiveStage.PerformStage().""" |
| to_patch = [(parallel, 'RunParallelSteps'), (commands, 'PushImages'), |
| (commands, 'UploadArchivedFile')] |
| self.AutoPatch(to_patch) |
| |
| def setUp(self): |
| self._PatchDependencies() |
| |
| self._Prepare() |
| self.buildstore = FakeBuildStore() |
| |
| # Our API here is not great when it comes to kwargs passing. |
| def _Prepare(self, bot_id=None, **kwargs): # pylint: disable=arguments-differ |
| extra_config = {'upload_symbols': True, 'push_image': True} |
| super(ArchiveStageTest, self)._Prepare( |
| bot_id, extra_config=extra_config, **kwargs) |
| |
| def ConstructStage(self): |
| self._run.GetArchive().SetupArchivePath() |
| return artifact_stages.ArchiveStage(self._run, self.buildstore, |
| self._current_board) |
| |
| def testArchive(self): |
| """Simple did-it-run test.""" |
| # TODO(davidjames): Test the individual archive steps as well. |
| self.RunStage() |
| |
| # TODO(build): This test is not actually testing anything real. It confirms |
| # that PushImages is not called, but the mock for RunParallelSteps already |
| # prevents PushImages from being called, regardless of whether this is a |
| # trybot flow. |
| def testNoPushImagesForRemoteTrybot(self): |
| """Test that remote trybot overrides work to disable push images.""" |
| self._Prepare(cmd_args=[ |
| '--remote-trybot', '-r', self.build_root, '--buildnumber=1234', |
| 'eve-release' |
| ]) |
| self.RunStage() |
| # pylint: disable=no-member |
| self.assertEqual(commands.PushImages.call_count, 0) |
| |
| def ConstructStageForArchiveStep(self): |
| """Stage construction for archive steps.""" |
| stage = self.ConstructStage() |
| self.PatchObject(stage._upload_queue, 'put', autospec=True) |
| self.PatchObject(path_util, 'ToChrootPath', return_value='', autospec=True) |
| return stage |
| |
| |
| class UploadPrebuiltsStageTest( |
| generic_stages_unittest.RunCommandAbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Tests for the UploadPrebuilts stage.""" |
| |
| cmd = 'upload_prebuilts' |
| RELEASE_TAG = '' |
| |
| # Our API here is not great when it comes to kwargs passing. |
| def _Prepare(self, bot_id=None, **kwargs): # pylint: disable=arguments-differ |
| super(UploadPrebuiltsStageTest, self)._Prepare(bot_id, **kwargs) |
| self.cmd = os.path.join(self.build_root, constants.CHROMITE_BIN_SUBDIR, |
| 'upload_prebuilts') |
| self._run.options.prebuilts = True |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| return artifact_stages.UploadPrebuiltsStage(self._run, self.buildstore, |
| self._run.config.boards[-1]) |
| |
| def _VerifyBoardMap(self, |
| bot_id, |
| count, |
| board_map, |
| public_args=None, |
| private_args=None): |
| """Verify that the prebuilts are uploaded for the specified bot. |
| |
| Args: |
| bot_id: Bot to upload prebuilts for. |
| count: Number of assert checks that should be performed. |
| board_map: Map from slave boards to whether the bot is public. |
| public_args: List of extra arguments for public boards. |
| private_args: List of extra arguments for private boards. |
| """ |
| self._Prepare(bot_id) |
| self.RunStage() |
| public_prefix = [self.cmd] + (public_args or []) |
| private_prefix = [self.cmd] + (private_args or []) |
| for board, public in board_map.items(): |
| if public or public_args: |
| public_cmd = public_prefix + ['--slave-board', board] |
| self.assertCommandContains(public_cmd, expected=public) |
| count -= 1 |
| private_cmd = private_prefix + ['--slave-board', board, '--private'] |
| self.assertCommandContains(private_cmd, expected=not public) |
| count -= 1 |
| if board_map: |
| self.assertCommandContains( |
| [self.cmd, '--set-version', |
| self._run.GetVersion()],) |
| count -= 1 |
| self.assertEqual( |
| count, 0, |
| 'Number of asserts performed does not match (%d remaining)' % count) |
| |
| def testFullPrebuiltsUpload(self): |
| """Test uploading of full builder prebuilts.""" |
| self._VerifyBoardMap('amd64-generic-full', 0, {}) |
| self.assertCommandContains([self.cmd, '--git-sync']) |
| |
| def testIncorrectCount(self): |
| """Test that _VerifyBoardMap asserts when the count is wrong.""" |
| self.assertRaises(AssertionError, self._VerifyBoardMap, |
| 'amd64-generic-full', 1, {}) |
| |
| |
| class UploadDevInstallerPrebuiltsStageTest( |
| generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Tests for the UploadDevInstallerPrebuilts stage.""" |
| |
| RELEASE_TAG = 'RT' |
| |
| def setUp(self): |
| self.upload_mock = self.PatchObject(prebuilts, |
| 'UploadDevInstallerPrebuilts') |
| |
| self._Prepare() |
| |
| # Our API here is not great when it comes to kwargs passing. |
| def _Prepare(self, bot_id=None, **kwargs): # pylint: disable=arguments-differ |
| super(UploadDevInstallerPrebuiltsStageTest, self)._Prepare(bot_id, **kwargs) |
| |
| self._run.options.chrome_rev = None |
| self._run.options.prebuilts = True |
| self._run.config['dev_installer_prebuilts'] = True |
| self._run.config['binhost_bucket'] = 'gs://testbucket' |
| self._run.config['binhost_key'] = 'dontcare' |
| self._run.config['binhost_base_url'] = 'https://dontcare/here' |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| return artifact_stages.DevInstallerPrebuiltsStage( |
| self._run, self.buildstore, self._current_board) |
| |
| def testDevInstallerUpload(self): |
| """Basic sanity test testing uploads of dev installer prebuilts.""" |
| self.RunStage() |
| |
| self.upload_mock.assert_called_with( |
| binhost_bucket=self._run.config.binhost_bucket, |
| binhost_key=self._run.config.binhost_key, |
| binhost_base_url=self._run.config.binhost_base_url, |
| buildroot=self.build_root, |
| board=self._current_board, |
| extra_args=mock.ANY) |
| |
| |
| class CPEExportStageTest(generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Test CPEExportStage""" |
| |
| def setUp(self): |
| self.CreateMockOverlay('amd64-generic') |
| |
| self.StartPatcher(generic_stages_unittest.ArchivingStageMixinMock()) |
| self.StartPatcher(parallel_unittest.ParallelMock()) |
| |
| self.rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock()) |
| self.rc_mock.SetDefaultCmdResult(output='') |
| |
| self.stage = None |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| """Create a CPEExportStage instance for testing""" |
| self._run.GetArchive().SetupArchivePath() |
| return artifact_stages.CPEExportStage(self._run, self.buildstore, |
| self._current_board) |
| |
| def assertBoardAttrEqual(self, attr, expected_value): |
| """Assert the value of a board run |attr| against |expected_value|.""" |
| value = self.stage.board_runattrs.GetParallel(attr) |
| self.assertEqual(expected_value, value) |
| |
| def _TestPerformStage(self): |
| """Run PerformStage for the stage.""" |
| self._Prepare() |
| self._run.attrs.release_tag = self.VERSION |
| |
| self.stage = self.ConstructStage() |
| self.stage.PerformStage() |
| |
| def testCPEExport(self): |
| """Test that CPEExport stage runs without syntax errors.""" |
| self._TestPerformStage() |
| |
| |
| class DebugSymbolsStageTest(generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Test DebugSymbolsStage""" |
| |
| # pylint: disable=protected-access |
| |
| def setUp(self): |
| self.CreateMockOverlay('amd64-generic') |
| |
| self.StartPatcher(generic_stages_unittest.ArchivingStageMixinMock()) |
| |
| self.gen_mock = self.PatchObject(commands, 'GenerateBreakpadSymbols') |
| self.gen_android_mock = self.PatchObject(commands, |
| 'GenerateAndroidBreakpadSymbols') |
| self.upload_mock = self.PatchObject(commands, 'UploadSymbols') |
| self.tar_mock = self.PatchObject(commands, 'GenerateDebugTarball') |
| |
| self.rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock()) |
| self.rc_mock.SetDefaultCmdResult(output='') |
| |
| self.stage = None |
| |
| # Our API here is not great when it comes to kwargs passing. |
| # pylint: disable=arguments-differ |
| def _Prepare(self, extra_config=None, **kwargs): |
| """Prepare this stage for testing.""" |
| if extra_config is None: |
| extra_config = { |
| 'archive_build_debug': True, |
| 'vm_tests': True, |
| 'upload_symbols': True, |
| } |
| super(DebugSymbolsStageTest, self)._Prepare( |
| extra_config=extra_config, **kwargs) |
| self._run.attrs.release_tag = self.VERSION |
| self.buildstore = FakeBuildStore() |
| # pylint: enable=arguments-differ |
| |
| def ConstructStage(self): |
| """Create a DebugSymbolsStage instance for testing""" |
| self._run.GetArchive().SetupArchivePath() |
| return artifact_stages.DebugSymbolsStage(self._run, self.buildstore, |
| self._current_board) |
| |
| def assertBoardAttrEqual(self, attr, expected_value): |
| """Assert the value of a board run |attr| against |expected_value|.""" |
| value = self.stage.board_runattrs.GetParallel(attr) |
| self.assertEqual(expected_value, value) |
| |
| def _TestPerformStage(self, |
| extra_config=None, |
| create_android_symbols_archive=False): |
| """Run PerformStage for the stage with the given extra config.""" |
| self._Prepare(extra_config=extra_config) |
| |
| self.tar_mock.side_effect = '/my/tar/ball' |
| self.stage = self.ConstructStage() |
| |
| if create_android_symbols_archive: |
| symbols_file = os.path.join(self.stage.archive_path, |
| constants.ANDROID_SYMBOLS_FILE) |
| osutils.Touch(symbols_file) |
| |
| try: |
| self.stage.PerformStage() |
| except Exception: |
| return self.stage._HandleStageException(sys.exc_info()) |
| |
| def testPerformStageWithSymbols(self): |
| """Smoke test for an PerformStage when debugging is enabled""" |
| self._TestPerformStage() |
| |
| self.assertEqual(self.gen_mock.call_count, 1) |
| self.assertEqual(self.gen_android_mock.call_count, 0) |
| self.assertEqual(self.upload_mock.call_count, 1) |
| self.assertEqual(self.tar_mock.call_count, 2) |
| |
| self.assertBoardAttrEqual('breakpad_symbols_generated', True) |
| self.assertBoardAttrEqual('debug_tarball_generated', True) |
| |
| def testPerformStageWithAndroidSymbols(self): |
| """Smoke test for an PerformStage when Android symbols are available""" |
| self._TestPerformStage(create_android_symbols_archive=True) |
| |
| self.assertEqual(self.gen_mock.call_count, 1) |
| self.assertEqual(self.gen_android_mock.call_count, 1) |
| self.assertEqual(self.upload_mock.call_count, 1) |
| self.assertEqual(self.tar_mock.call_count, 2) |
| |
| self.assertBoardAttrEqual('breakpad_symbols_generated', True) |
| self.assertBoardAttrEqual('debug_tarball_generated', True) |
| |
| def testPerformStageNoSymbols(self): |
| """Smoke test for an PerformStage when debugging is disabled""" |
| extra_config = { |
| 'archive_build_debug': False, |
| 'vm_tests': False, |
| 'upload_symbols': False, |
| } |
| result = self._TestPerformStage(extra_config) |
| self.assertIsNone(result) |
| |
| self.assertEqual(self.gen_mock.call_count, 1) |
| self.assertEqual(self.gen_android_mock.call_count, 0) |
| self.assertEqual(self.upload_mock.call_count, 0) |
| self.assertEqual(self.tar_mock.call_count, 2) |
| |
| self.assertBoardAttrEqual('breakpad_symbols_generated', True) |
| self.assertBoardAttrEqual('debug_tarball_generated', True) |
| |
| def testGenerateCrashStillNotifies(self): |
| """Crashes in symbol generation should still notify external events.""" |
| |
| class TestError(Exception): |
| """Unique test exception""" |
| |
| self.gen_mock.side_effect = TestError('mew') |
| result = self._TestPerformStage() |
| self.assertIsInstance(result[0], failures_lib.InfrastructureFailure) |
| |
| self.assertEqual(self.gen_mock.call_count, 1) |
| self.assertEqual(self.gen_android_mock.call_count, 0) |
| self.assertEqual(self.upload_mock.call_count, 0) |
| self.assertEqual(self.tar_mock.call_count, 0) |
| |
| self.assertBoardAttrEqual('breakpad_symbols_generated', False) |
| self.assertBoardAttrEqual('debug_tarball_generated', False) |
| |
| def testUploadCrashStillNotifies(self): |
| """Crashes in symbol upload should still notify external events.""" |
| self.upload_mock.side_effect = failures_lib.BuildScriptFailure( |
| cros_build_lib.RunCommandError('mew'), 'mew') |
| result = self._TestPerformStage() |
| self.assertIs(result[0], results_lib.Results.FORGIVEN) |
| |
| self.assertBoardAttrEqual('breakpad_symbols_generated', True) |
| self.assertBoardAttrEqual('debug_tarball_generated', True) |
| |
| def testUploadCrashUploadsList(self): |
| """A crash in symbol upload should still post the failed list file.""" |
| self.upload_mock.side_effect = failures_lib.BuildScriptFailure( |
| cros_build_lib.RunCommandError('mew'), 'mew') |
| self._Prepare() |
| stage = self.ConstructStage() |
| |
| with mock.patch.object(os.path, 'exists') as mock_exists, \ |
| mock.patch.object(artifact_stages.DebugSymbolsStage, |
| 'UploadArtifact') as mock_upload: |
| mock_exists.return_value = True |
| self.assertRaises(artifact_stages.DebugSymbolsUploadException, |
| stage.UploadSymbols, stage._build_root, |
| stage._current_board) |
| self.assertEqual(mock_exists.call_count, 1) |
| self.assertEqual(mock_upload.call_count, 1) |
| |
| |
| class UploadTestArtifactsStageMock( |
| generic_stages_unittest.ArchivingStageMixinMock): |
| """Partial mock for BuildImageStage.""" |
| |
| TARGET = 'chromite.cbuildbot.stages.artifact_stages.UploadTestArtifactsStage' |
| ATTRS = ( |
| generic_stages_unittest.ArchivingStageMixinMock.ATTRS + |
| ('BuildAutotestTarballs', 'BuildTastTarball', |
| 'BuildGuestImagesTarball')) |
| |
| def BuildAutotestTarballs(self, *args, **kwargs): |
| with patches( |
| patch(commands, 'BuildTarball'), |
| patch(commands, 'FindFilesWithPattern', return_value=['foo.txt'])): |
| self.backup['BuildAutotestTarballs'](*args, **kwargs) |
| |
| def BuildTastTarball(self, *args, **kwargs): |
| with patch(commands, 'BuildTarball'): |
| self.backup['BuildTastTarball'](*args, **kwargs) |
| |
| def BuildGuestImagesTarball(self, *args, **kwargs): |
| self.backup['BuildGuestImagesTarball'](*args, **kwargs) |
| |
| class UploadTestArtifactsStageTest(build_stages_unittest.AllConfigsTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Tests UploadTestArtifactsStage.""" |
| |
| def setUp(self): |
| self._release_tag = None |
| |
| osutils.SafeMakedirs(os.path.join(self.build_root, 'chroot', 'tmp')) |
| self.StartPatcher(UploadTestArtifactsStageMock()) |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| return artifact_stages.UploadTestArtifactsStage(self._run, self.buildstore, |
| self._current_board) |
| |
| def RunTestsWithBotId(self, bot_id, options_tests=True): |
| """Test with the config for the specified bot_id.""" |
| self._Prepare(bot_id) |
| self._run.options.tests = options_tests |
| self._run.attrs.release_tag = '0.0.1' |
| |
| # Simulate images being ready. |
| board_runattrs = self._run.GetBoardRunAttrs(self._current_board) |
| board_runattrs.SetParallel('images_generated', True) |
| |
| generate_quick_provision_payloads_mock = self.PatchObject( |
| commands, 'GenerateQuickProvisionPayloads') |
| generate_update_payloads_mock = self.PatchObject(commands, |
| 'GeneratePayloads') |
| |
| with parallel_unittest.ParallelMock(): |
| with self.RunStageWithConfig(): |
| if (self._run.config.upload_hw_test_artifacts and |
| self._run.config.images): |
| self.assertNotEqual(generate_update_payloads_mock.call_count, 0) |
| self.assertNotEqual(generate_quick_provision_payloads_mock.call_count, |
| 0) |
| else: |
| self.assertEqual(generate_update_payloads_mock.call_count, 0) |
| self.assertEqual(generate_quick_provision_payloads_mock.call_count, 0) |
| |
| def testAllConfigs(self): |
| """Test all major configurations""" |
| self.RunAllConfigs(self.RunTestsWithBotId) |
| |
| |
| # TODO: Delete ArchivingMock once ArchivingStage is deprecated. |
| class ArchivingMock(partial_mock.PartialMock): |
| """Partial mock for ArchivingStage.""" |
| |
| TARGET = 'chromite.cbuildbot.stages.artifact_stages.ArchivingStage' |
| ATTRS = ('UploadArtifact',) |
| |
| def UploadArtifact(self, *args, **kwargs): |
| with patch(commands, 'ArchiveFile', return_value='foo.txt'): |
| with patch(commands, 'UploadArchivedFile'): |
| self.backup['UploadArtifact'](*args, **kwargs) |
| |
| |
| # TODO: Delete ArchivingStageTest once ArchivingStage is deprecated. |
| class ArchivingStageTest(generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Excerise ArchivingStage functionality.""" |
| RELEASE_TAG = '' |
| |
| def setUp(self): |
| self.StartPatcher(ArchivingMock()) |
| |
| self._Prepare() |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| self._run.GetArchive().SetupArchivePath() |
| archive_stage = artifact_stages.ArchiveStage(self._run, self.buildstore, |
| self._current_board) |
| return artifact_stages.ArchivingStage(self._run, self.buildstore, |
| self._current_board, archive_stage) |
| |
| |
| class GenerateSysrootStageTest(generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Exercise GenerateSysrootStage functionality.""" |
| |
| RELEASE_TAG = '' |
| |
| # pylint: disable=protected-access |
| |
| def setUp(self): |
| self._Prepare() |
| self.rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock()) |
| self.rc_mock.SetDefaultCmdResult() |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| self._run.GetArchive().SetupArchivePath() |
| return artifact_stages.GenerateSysrootStage(self._run, self.buildstore, |
| self._current_board) |
| |
| def testGenerateSysroot(self): |
| """Test that the sysroot generation was called correctly.""" |
| stage = self.ConstructStage() |
| self.PatchObject(path_util, 'ToChrootPath', return_value='', autospec=True) |
| self.PatchObject(stage._upload_queue, 'put', autospec=True) |
| stage._GenerateSysroot() |
| sysroot_tarball = 'sysroot_%s.tar.xz' % ('virtual_target-os') |
| stage._upload_queue.put.assert_called_with([sysroot_tarball]) |
| |
| |
| class CollectPGOProfilesStageTest(generic_stages_unittest.AbstractStageTestCase, |
| cbuildbot_unittest.SimpleBuilderTestCase): |
| """Exercise CollectPGOProfilesStage functionality.""" |
| |
| RELEASE_TAG = '' |
| |
| _VALID_CLANG_VERSION_SHA = '4e8231b5cf0f5f62c7a51a857e29f5be5cb55734' |
| _VALID_CLANG_VERSION_STRING = ( |
| 'Chromium OS 10.0_pre377782_p20200113-r1 clang version 10.0.0 ' |
| '(/var/cache/chromeos-cache/distfiles/host/egit-src/llvm-project ' |
| '4e8231b5cf0f5f62c7a51a857e29f5be5cb55734)\n' |
| 'Target: x86_64-pc-linux-gnu\n' |
| 'Thread model: posix\n' |
| 'InstalledDir: /usr/bin\n' |
| ) |
| |
| # pylint: disable=protected-access |
| |
| def setUp(self): |
| self._Prepare() |
| self.rc_mock = self.StartPatcher(cros_test_lib.RunCommandMock()) |
| self.rc_mock.SetDefaultCmdResult() |
| self.buildstore = FakeBuildStore() |
| |
| def ConstructStage(self): |
| self._run.GetArchive().SetupArchivePath() |
| return artifact_stages.CollectPGOProfilesStage(self._run, self.buildstore, |
| self._current_board) |
| |
| def testParseLLVMHeadSHA(self): |
| stage = self.ConstructStage() |
| actual_sha = stage._ParseLLVMHeadSHA(self._VALID_CLANG_VERSION_STRING) |
| self.assertEqual(actual_sha, self._VALID_CLANG_VERSION_SHA) |
| |
| def _MetadataMultiDispatch(self, equery_uses_fn, clang_version_fn): |
| def result(command, enter_chroot, stdout): |
| self.assertTrue(enter_chroot) |
| self.assertTrue(stdout) |
| |
| if command == ['equery', '-C', '-N', 'uses', 'sys-devel/llvm']: |
| output = equery_uses_fn() |
| elif command == ['clang', '--version']: |
| output = clang_version_fn() |
| else: |
| raise ValueError('Unexpected command: %s' % command) |
| |
| return cros_build_lib.CommandResult(stdout=output) |
| |
| return result |
| |
| def testCollectLLVMMetadataRaisesOnAnInvalidVersionString(self): |
| stage = self.ConstructStage() |
| |
| def equery_uses(): |
| return ' - + llvm_pgo_generate :' |
| |
| def clang_version(): |
| valid_version_lines = self._VALID_CLANG_VERSION_STRING.splitlines() |
| valid_version_lines[0] = 'clang version 8.0.1\n' |
| return ''.join(valid_version_lines) |
| |
| with patch(cros_build_lib, 'run') as run_command: |
| run_command.side_effect = self._MetadataMultiDispatch( |
| equery_uses_fn=equery_uses, |
| clang_version_fn=clang_version) |
| |
| with self.assertRaises(ValueError) as raised: |
| stage._CollectLLVMMetadata() |
| |
| self.assertIn('version string', str(raised.exception)) |
| |
| def testCollectLLVMMetadataRaisesIfClangIsntPGOGenerated(self): |
| stage = self.ConstructStage() |
| |
| def clang_version(): |
| return self._VALID_CLANG_VERSION_STRING |
| |
| with patch(cros_build_lib, 'run') as run_command: |
| for uses in ['', ' - - llvm_pgo_generate :']: |
| def equery_uses(): |
| # We're using a loop var on purpose; this function should die by the |
| # end of the current iteration. |
| # pylint: disable=cell-var-from-loop |
| return uses |
| |
| run_command.side_effect = self._MetadataMultiDispatch( |
| equery_uses_fn=equery_uses, |
| clang_version_fn=clang_version) |
| |
| with self.assertRaises(ValueError) as raised: |
| stage._CollectLLVMMetadata() |
| |
| self.assertIn('pgo_generate flag', str(raised.exception)) |
| |
| def testCollectLLVMMetadataFunctionsInASimpleCase(self): |
| def clang_version(): |
| return self._VALID_CLANG_VERSION_STRING |
| |
| def equery_uses(): |
| return ' - + llvm_pgo_generate :' |
| |
| stage = self.ConstructStage() |
| |
| run_command = self.PatchObject(cros_build_lib, 'run') |
| run_command.side_effect = self._MetadataMultiDispatch(equery_uses, |
| clang_version) |
| write_file = self.PatchObject(osutils, 'WriteFile') |
| upload_queue_put = self.PatchObject(stage._upload_queue, 'put') |
| stage._CollectLLVMMetadata() |
| |
| write_file.assert_called_once() |
| upload_queue_put.assert_called_once() |
| |
| (written_file, metadata_json), kwargs = write_file.call_args |
| self.assertEqual(kwargs, {}) |
| |
| expected_metadata = { |
| 'head_sha': self._VALID_CLANG_VERSION_SHA, |
| } |
| |
| given_metadata = json.loads(metadata_json) |
| self.assertEqual(given_metadata, expected_metadata) |
| |
| upload_queue_put.assert_called_with([written_file]) |
| |
| def testCollectPGOProfiles(self): |
| """Test that the sysroot generation was called correctly.""" |
| stage = self.ConstructStage() |
| |
| # No profiles directory |
| with self.assertRaises(Exception) as msg: |
| stage._CollectPGOProfiles() |
| self.assertEqual('No profile directories found.', str(msg.exception)) |
| |
| # Create profiles directory |
| cov_path = 'build/%s/build/coverage_data' % stage._current_board |
| out_cov_path = os.path.abspath( |
| os.path.join(self.build_root, 'chroot', cov_path)) |
| os.makedirs(os.path.join(out_cov_path, 'raw_profiles')) |
| |
| # No profraw files |
| with self.assertRaises(Exception) as msg: |
| stage._CollectPGOProfiles() |
| self.assertEqual('No profraw files found in profiles directory.', |
| str(msg.exception)) |
| |
| # Create profraw file |
| profraw = os.path.join(out_cov_path, 'raw_profiles', 'a.profraw') |
| with open(profraw, 'a') as f: |
| f.write('123') |
| |
| # Check uploading tarball |
| self.PatchObject(stage._upload_queue, 'put', autospec=True) |
| stage._CollectPGOProfiles() |
| llvm_profdata = path_util.ToChrootPath( |
| os.path.join(stage.archive_path, 'llvm.profdata')) |
| profraw_list = path_util.ToChrootPath( |
| os.path.join(stage.archive_path, 'profraw_list')) |
| self.assertEqual(['llvm-profdata', 'merge', |
| '-output', llvm_profdata, |
| '-f', profraw_list], |
| stage._merge_cmd) |
| tarball = stage.PROFDATA_TAR |
| stage._upload_queue.put.assert_called_with([tarball]) |