| # -*- coding: utf-8 -*- |
| # Copyright 2015 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. |
| |
| """Tests for toolchain.""" |
| |
| from __future__ import print_function |
| |
| import mock |
| import os |
| |
| from chromite.lib import cros_build_lib |
| from chromite.lib import cros_test_lib |
| from chromite.lib import osutils |
| from chromite.lib import portage_util |
| from chromite.lib import sysroot_lib |
| from chromite.lib import toolchain |
| |
| |
| BASE_TOOLCHAIN_CONF = """# The root of all evil is money, err, this config. |
| base-target-name # This will become the base target. |
| |
| # This toolchain is bonus! |
| bonus-toolchain {"a setting": "bonus value"} # Bonus! |
| |
| """ |
| |
| ADDITIONAL_TOOLCHAIN_CONF = """# A helpful toolchain related comment. |
| extra-toolchain # Unlikely to win any performance tests. |
| |
| bonus-toolchain {"stable": true} |
| """ |
| |
| EXPECTED_TOOLCHAINS = { |
| 'bonus-toolchain': { |
| 'sdk': True, |
| 'crossdev': '', |
| 'default': False, |
| 'a setting': 'bonus value', |
| 'stable': True, |
| }, |
| 'extra-toolchain': {'sdk': True, 'crossdev': '', 'default': True}, |
| 'base-target-name': {'sdk': True, 'crossdev': '', 'default': False}, |
| } |
| |
| |
| class ToolchainTest(cros_test_lib.MockTempDirTestCase): |
| """Tests for lib.toolchain.""" |
| |
| def testArchForToolchain(self): |
| """Tests that we correctly parse crossdev's output.""" |
| rc_mock = cros_test_lib.RunCommandMock() |
| |
| noarch = """target=foo |
| category=bla |
| """ |
| rc_mock.SetDefaultCmdResult(output=noarch) |
| with rc_mock: |
| self.assertEqual(None, toolchain.GetArchForTarget('fake_target')) |
| |
| amd64arch = """arch=amd64 |
| target=foo |
| """ |
| rc_mock.SetDefaultCmdResult(output=amd64arch) |
| with rc_mock: |
| self.assertEqual('amd64', toolchain.GetArchForTarget('fake_target')) |
| |
| @mock.patch('chromite.lib.toolchain.portage_util.FindOverlays') |
| def testReadsBoardToolchains(self, find_overlays_mock): |
| """Tests that we correctly parse toolchain configs for an overlay stack.""" |
| # Create some fake overlays and put toolchain confs in a subset of them. |
| overlays = [os.path.join(self.tempdir, 'overlay%d' % i) for i in range(3)] |
| for overlay in overlays: |
| osutils.SafeMakedirs(overlay) |
| for overlay, contents in [(overlays[0], BASE_TOOLCHAIN_CONF), |
| (overlays[2], ADDITIONAL_TOOLCHAIN_CONF)]: |
| osutils.WriteFile(os.path.join(overlay, 'toolchain.conf'), contents) |
| find_overlays_mock.return_value = overlays |
| actual_targets = toolchain.GetToolchainsForBoard('board_value') |
| self.assertEqual(EXPECTED_TOOLCHAINS, actual_targets) |
| |
| |
| class ToolchainInfoTest(cros_test_lib.MockTestCase): |
| """Tests for the ToolchainInfo class.""" |
| |
| def setUp(self): |
| unused_fields = {'pv': None, 'package': None, 'version_no_rev': None, |
| 'rev': None, 'category': None, 'cp': None, 'cpv': None} |
| self.gcc_cpv = portage_util.CPV(cpf='sys-devel/gcc-1.2', version='1.2', |
| **unused_fields) |
| self.libc_cpv = portage_util.CPV(cpf='sys-libs/glibc-3.4.5', |
| version='3.4.5', **unused_fields) |
| self.go_cpv = portage_util.CPV(cpf='dev-lang/go-6.7-r8', version='6.7-r8', |
| **unused_fields) |
| |
| self.matching_toolchain = toolchain.ToolchainInfo('tc', 'tc') |
| self.not_matching_toolchain = toolchain.ToolchainInfo('tc', 'dtc') |
| |
| def testVersion(self): |
| """Test the version fetching functionality.""" |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.gcc_cpv) |
| self.assertEqual('1.2', self.matching_toolchain.gcc_version) |
| |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.libc_cpv) |
| self.assertEqual('3.4.5', self.matching_toolchain.libc_version) |
| |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.go_cpv) |
| self.assertEqual('6.7-r8', self.matching_toolchain.go_version) |
| |
| def testCpv(self): |
| """Test the CPV version functionality.""" |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.gcc_cpv) |
| self.assertEqual(self.gcc_cpv.cpf, self.matching_toolchain.gcc_cpf) |
| |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.libc_cpv) |
| self.assertEqual(self.libc_cpv.cpf, self.matching_toolchain.libc_cpf) |
| |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.go_cpv) |
| self.assertEqual(self.go_cpv.cpf, self.matching_toolchain.go_cpf) |
| |
| def testCP(self): |
| """Test the GetCP method.""" |
| # pylint: disable=protected-access |
| # Use wrong CPV instances to make sure it's not using them since _GetCP |
| # is the "base case" for fetching the CPV objects. |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.go_cpv) |
| self.PatchObject(self.not_matching_toolchain, '_GetCPVObj', |
| return_value=self.go_cpv) |
| self.assertEqual('sys-devel/gcc', self.matching_toolchain._GetCP('gcc')) |
| self.assertEqual('cross-tc/gcc', self.not_matching_toolchain._GetCP('gcc')) |
| |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.go_cpv) |
| self.PatchObject(self.not_matching_toolchain, '_GetCPVObj', |
| return_value=self.go_cpv) |
| self.assertEqual('sys-libs/glibc', self.matching_toolchain._GetCP('glibc')) |
| self.assertEqual('cross-tc/glibc', |
| self.not_matching_toolchain._GetCP('glibc')) |
| |
| self.PatchObject(self.matching_toolchain, '_GetCPVObj', |
| return_value=self.gcc_cpv) |
| self.PatchObject(self.not_matching_toolchain, '_GetCPVObj', |
| return_value=self.gcc_cpv) |
| self.assertEqual('dev-lang/go', self.matching_toolchain._GetCP('go')) |
| self.assertEqual('cross-tc/go', self.not_matching_toolchain._GetCP('go')) |
| |
| |
| class ToolchainInstallerTest(cros_test_lib.MockTempDirTestCase): |
| """Tests for the toolchain installer class.""" |
| |
| def setUp(self): |
| # Setup the temp filesystem matching the expected layout. |
| D = cros_test_lib.Directory |
| filesystem = ( |
| D('build', ( |
| D('board', ( |
| D('etc', ( |
| D('portage', ( |
| D('profile', ('package.provided',)), |
| )), |
| # 'make.conf.board_setup', |
| )), |
| D('var', ( |
| D('cache', ( |
| D('edb', ('chromeos', 'chromeos.lock',)), |
| )), |
| D('lib', ( |
| D('portage', ( |
| D('pkgs', ()), |
| )), |
| )), |
| )), |
| )), |
| )), |
| ) |
| cros_test_lib.CreateOnDiskHierarchy(self.tempdir, filesystem) |
| self.sysroot = sysroot_lib.Sysroot(os.path.join(self.tempdir, |
| 'build/board')) |
| |
| # Build out the testing CPV objects. |
| self.gcc_cpv = portage_util.SplitCPV('sys-devel/gcc-1.2', strict=False) |
| self.libc_cpv = portage_util.SplitCPV('sys-libs/glibc-3.4.5', strict=False) |
| |
| self.go_cpv = portage_util.SplitCPV('dev-lang/go-6.7-r8', strict=False) |
| self.rpcsvc_cpv = portage_util.SplitCPV('net-libs/rpcsvc-proto-9.10', |
| strict=False) |
| |
| # pylint: disable=protected-access |
| self.go_toolchain = toolchain.ToolchainInfo('tc', 'tc') |
| self.go_toolchain._cpvs = {'gcc': self.gcc_cpv, |
| 'glibc': self.libc_cpv, |
| 'go': self.go_cpv, |
| 'rpcsvc': self.rpcsvc_cpv} |
| |
| self.no_go_toolchain = toolchain.ToolchainInfo('tc', 'tc') |
| self.no_go_toolchain._cpvs = {'gcc': self.gcc_cpv, |
| 'glibc': self.libc_cpv, |
| 'go': None, |
| 'rpcsvc': self.rpcsvc_cpv} |
| |
| self.different_toolchain = toolchain.ToolchainInfo('nottc', 'tc') |
| self.different_toolchain._cpvs = {'gcc': self.gcc_cpv, |
| 'glibc': self.libc_cpv, |
| 'go': self.go_cpv, |
| 'rpcsvc': None} |
| |
| pkgdir = os.path.join(self.tempdir, 'var/lib/portage/pkgs') |
| self.updater = toolchain.ToolchainInstaller(False, True, 'tc', pkgdir) |
| |
| # Avoid sudo password prompt for _WriteConfigs. |
| self.PatchObject(os, 'getuid', return_value=0) |
| self.PatchObject(os, 'geteuid', return_value=0) |
| |
| def testUpdateProvided(self): |
| """Test the updates to the package.provided file.""" |
| path = os.path.join(self.sysroot.path, |
| 'etc/portage/profile/package.provided') |
| |
| # pylint: disable=protected-access |
| # All 3 packages. |
| self.updater._UpdateProvided(self.sysroot, self.go_toolchain) |
| expected = ['sys-devel/gcc-1.2', |
| 'sys-libs/glibc-3.4.5', |
| 'dev-lang/go-6.7-r8', |
| 'net-libs/rpcsvc-proto-9.10'] |
| |
| for line in osutils.ReadFile(path).splitlines(): |
| self.assertIn(line, expected) |
| expected.remove(line) |
| |
| self.assertEqual([], expected) |
| |
| # No go package. |
| self.updater._UpdateProvided(self.sysroot, self.no_go_toolchain) |
| expected = ['sys-devel/gcc-1.2', |
| 'sys-libs/glibc-3.4.5', |
| 'net-libs/rpcsvc-proto-9.10'] |
| |
| for line in osutils.ReadFile(path).splitlines(): |
| self.assertIn(line, expected) |
| expected.remove(line) |
| |
| self.assertEqual([], expected) |
| |
| # Different toolchain. |
| self.updater._UpdateProvided(self.sysroot, self.different_toolchain) |
| expected = ['sys-devel/gcc-1.2', |
| 'sys-libs/glibc-3.4.5', |
| 'dev-lang/go-6.7-r8'] |
| |
| for line in osutils.ReadFile(path).splitlines(): |
| self.assertIn(line, expected) |
| expected.remove(line) |
| |
| self.assertEqual([], expected) |
| |
| def testWriteConfig(self): |
| """Test the sysroot configs are updated correctly.""" |
| # pylint: disable=protected-access |
| self.updater._WriteConfigs(self.sysroot, self.go_toolchain) |
| self.assertEqual('3.4.5', self.sysroot.GetCachedField('LIBC_VERSION')) |
| |
| def testInstallLibcFailures(self): |
| """Test the installer error handling.""" |
| # Test error thrown during toolchain installation. |
| # We want a ToolchainInstallError with the glibc info set. |
| error_result = cros_build_lib.CommandResult(returncode=1) |
| self.PatchObject(cros_build_lib, 'SudoRunCommand', |
| side_effect=cros_build_lib.RunCommandError('Error', |
| error_result)) |
| |
| try: |
| # pylint: disable=protected-access |
| self.updater._InstallLibc(self.sysroot, self.go_toolchain) |
| except toolchain.ToolchainInstallError as e: |
| self.assertTrue(e.failed_toolchain_info) |
| self.assertEqual(self.different_toolchain.libc_cpf, |
| e.failed_toolchain_info[0].cpf) |
| except Exception as e: |
| self.fail('Unexpected exception type thrown: %s' % type(e)) |
| else: |
| self.fail('_InstallLibc should have thrown an error.') |
| |
| # Test error thrown during cross toolchain installation. |
| self.PatchObject(cros_build_lib, 'SudoRunCommand') |
| # This is the error we're testing for, but _InstallLibc catches and |
| # modifies the error before re-raising it. |
| self.PatchObject(self.updater, '_ExtractLibc', |
| side_effect=toolchain.ToolchainInstallError('Error', |
| error_result)) |
| |
| try: |
| # pylint: disable=protected-access |
| self.updater._InstallLibc(self.sysroot, self.different_toolchain) |
| except toolchain.ToolchainInstallError as e: |
| # Make sure it did in fact modify the error to include the glibc CPV. |
| self.assertTrue(e.failed_toolchain_info) |
| self.assertEqual(self.go_toolchain.libc_cpf, |
| e.failed_toolchain_info[0].cpf) |
| except Exception as e: |
| self.fail('Unexpected exception type thrown: %s' % type(e)) |
| else: |
| self.fail('_InstallLibc should have thrown an error.') |