blob: 361de047c1ecbd7b344729de61a89e6c31f920fb [file] [log] [blame]
# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Sysroot service unittest."""
import contextlib
import datetime
import operator
import os
from pathlib import Path
import shutil
from typing import Optional, Union
from unittest import mock
from chromite.api.gen.chromiumos import prebuilts_cloud_pb2
from chromite.lib import build_target_lib
from chromite.lib import chroot_lib
from chromite.lib import constants
from chromite.lib import cpupower_helper
from chromite.lib import cros_build_lib
from chromite.lib import cros_sdk_lib
from chromite.lib import cros_test_lib
from chromite.lib import goma_lib
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 sysroot_lib
from chromite.lib.parser import package_info
from chromite.service import binhost
from chromite.service import sdk
from chromite.service import sysroot
from chromite.utils import os_util
class SetupBoardRunConfigTest(cros_test_lib.TestCase):
"""Tests for the SetupBoardRunConfig class."""
def testGetUpdateChrootArgs(self) -> None:
"""Test the update chroot args conversion method."""
# False/0/None tests.
instance = sysroot.SetupBoardRunConfig(
usepkg=False, jobs=None, update_toolchain=False, backtrack=1
)
args = instance.GetUpdateChrootArgs("board").GetArgList()
self.assertIn("--backtrack=1", args)
self.assertIn("--nousepkg", args)
self.assertIn("--skip_toolchain_update", args)
self.assertNotIn("--usepkg", args)
self.assertFalse(any(x.startswith("--jobs") for x in args))
# True/set values tests.
instance = sysroot.SetupBoardRunConfig(
usepkg=True, jobs=1, update_toolchain=True
)
args = instance.GetUpdateChrootArgs("board").GetArgList()
self.assertIn("--usepkg", args)
self.assertIn("--jobs=1", args)
self.assertNotIn("--nousepkg", args)
self.assertNotIn("--skip_toolchain_update", args)
class SetupBoardTest(cros_test_lib.MockTestCase):
"""Tests for SetupBoard."""
def setUp(self) -> None:
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=True)
def testFullRun(self) -> None:
"""Test a regular full run.
This method just checks that it's trying to create the sysroot and
install the toolchain by default.
"""
target_sysroot = sysroot_lib.Sysroot("/build/board")
create_mock = self.PatchObject(
sysroot, "Create", return_value=target_sysroot
)
install_toolchain_mock = self.PatchObject(sysroot, "InstallToolchain")
sysroot.SetupBoard(build_target_lib.BuildTarget("board"))
create_mock.assert_called_once()
install_toolchain_mock.assert_called_once()
def testRegenConfigs(self) -> None:
"""Test the regen configs install prevention."""
target_sysroot = sysroot_lib.Sysroot("/build/board")
create_mock = self.PatchObject(
sysroot, "Create", return_value=target_sysroot
)
install_toolchain_mock = self.PatchObject(sysroot, "InstallToolchain")
target = build_target_lib.BuildTarget("board")
configs = sysroot.SetupBoardRunConfig(regen_configs=True)
sysroot.SetupBoard(target, run_configs=configs)
# Should still try to create the sysroot, but should not try to install
# the toolchain.
create_mock.assert_called_once()
install_toolchain_mock.assert_not_called()
class CreateTest(cros_test_lib.RunCommandTempDirTestCase):
"""Create function tests."""
def setUp(self) -> None:
# Avoid sudo password prompt for config writing.
self.PatchObject(os_util, "is_root_user", return_value=True)
# It has to be run inside the chroot.
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=True)
# Don't write to the chroot.
self.PatchObject(cros_sdk_lib.ChrootUpdater, "ApplyUpdates")
# Don't test sdk.Update internals -- sdk_unittest.py handles that.
result = sdk.UpdateResult(return_code=0)
self.update_mock = self.PatchObject(sdk, "Update", return_value=result)
# A board we have a sysroot for already.
self.board = "board"
self.sysroot_path = os.path.join(self.tempdir, "build", self.board)
self.build_target = build_target_lib.BuildTarget(
self.board, build_root=self.sysroot_path
)
# A board we don't have a sysroot for yet.
self.unbuilt_board = "board2"
self.unbuilt_path = os.path.join(
self.tempdir, "build", self.unbuilt_board
)
self.unbuilt_target = build_target_lib.BuildTarget(
self.unbuilt_board, build_root=self.unbuilt_path
)
# Create the sysroot.
osutils.SafeMakedirs(self.sysroot_path)
def testUpdateChroot(self) -> None:
"""Test the update_chroot related handling."""
# Prevent it from doing anything else for this test.
self.PatchObject(sysroot, "_CreateSysrootSkeleton")
self.PatchObject(sysroot, "_InstallConfigs")
self.PatchObject(sysroot, "_InstallPortageConfigs")
self.PatchObject(
cros_sdk_lib,
"ChrootReadWrite",
side_effect=contextlib.contextmanager(lambda path="": (yield)),
)
# Make sure we have a board we haven't setup to avoid triggering the
# existing sysroot logic. That is entirely unrelated to the chroot
# update.
target = self.unbuilt_target
# Test no update case.
config = sysroot.SetupBoardRunConfig(upgrade_chroot=False)
get_args_patch = self.PatchObject(config, "GetUpdateChrootArgs")
sysroot.Create(target, config, None)
# The update chroot args not being fetched is a
# strong enough signal that the update wasn't run.
get_args_patch.assert_not_called()
# Test update case.
config = sysroot.SetupBoardRunConfig(upgrade_chroot=True)
sysroot.Create(target, config, None)
self.update_mock.assert_called_once()
def test_update_chroot_failure(self) -> None:
"""Test failure handling when update chroot fails."""
failed_pkgs = [
package_info.parse("foo/bar-1.2-r3"),
package_info.parse("cat/pkg-1.2-r3"),
]
result = sdk.UpdateResult(return_code=1, failed_pkgs=failed_pkgs)
self.update_mock.return_value = result
try:
sysroot.Create(
self.unbuilt_target, sysroot.SetupBoardRunConfig(), None
)
self.fail("Should have raised an UpdateChrootError.")
except sysroot.UpdateChrootError as e:
self.assertSequenceEqual(
sorted(failed_pkgs), sorted(e.failed_packages)
)
def testForce(self) -> None:
"""Test the force flag."""
# Prevent it from doing anything else for this test.
self.PatchObject(sysroot, "_CreateSysrootSkeleton")
self.PatchObject(sysroot, "_InstallConfigs")
self.PatchObject(sysroot, "_InstallPortageConfigs")
self.PatchObject(
cros_sdk_lib,
"ChrootReadWrite",
side_effect=contextlib.contextmanager(lambda path="": (yield)),
)
delete_patch = self.PatchObject(sysroot_lib.Sysroot, "Delete")
config = sysroot.SetupBoardRunConfig(force=False)
sysroot.Create(self.build_target, config, None)
delete_patch.assert_not_called()
self.update_mock.assert_called_once()
self.update_mock.reset_mock()
config = sysroot.SetupBoardRunConfig(force=True)
sysroot.Create(self.build_target, config, None)
delete_patch.assert_called_once()
self.update_mock.assert_called_once()
class CreateSimpleChromeSysrootTest(cros_test_lib.RunCommandTempDirTestCase):
"""Tests for CreateSimpleChromeSysroot."""
def setUp(self) -> None:
self.source_root = os.path.join(self.tempdir, "source_root")
osutils.SafeMakedirs(self.source_root)
self.PatchObject(constants, "SOURCE_ROOT", new=self.source_root)
# Configure {chroot,out}_path.
self.chroot_path = os.path.join(self.tempdir, "chroot_dir")
self.out_path = self.tempdir / "out_dir"
# Create output dir.
self.output_dir = os.path.join(self.tempdir, "output_dir")
osutils.SafeMakedirs(self.output_dir)
# Create chroot and build_target objs.
self.chroot = chroot_lib.Chroot(
path=self.chroot_path, out_path=self.out_path
)
self.build_target = build_target_lib.BuildTarget("target")
# Create the tmp dir.
osutils.SafeMakedirs(self.chroot.tmp)
def testCreateSimpleChromeSysroot(self) -> None:
# Mock the artifact copy.
tar_dest = os.path.join(self.output_dir, constants.CHROME_SYSROOT_TAR)
self.PatchObject(shutil, "copy", return_value=tar_dest)
# Call service, verify arguments passed to run.
sysroot.CreateSimpleChromeSysroot(
self.chroot, None, self.build_target, self.output_dir
)
self.rc.assertCommandCalled(
[
"cros_generate_sysroot",
"--out-dir",
mock.ANY,
"--board",
self.build_target.name,
"--package",
"chromeos-base/chromeos-chrome",
"--out-file",
"sysroot_chromeos-base_chromeos-chrome.tar.xz",
"--deps-only",
],
enter_chroot=True,
cwd=self.source_root,
chroot_args=mock.ANY,
extra_env=mock.ANY,
)
class CreateFuzzerSysrootTest(cros_test_lib.RunCommandTempDirTestCase):
"""Tests for CreateFuzzerSysroot."""
def setUp(self) -> None:
self.source_root = os.path.join(self.tempdir, "source_root")
osutils.SafeMakedirs(self.source_root)
self.PatchObject(constants, "SOURCE_ROOT", new=self.source_root)
# Configure {chroot,out}_path.
self.chroot_path = os.path.join(self.tempdir, "chroot_dir")
self.out_path = self.tempdir / "out_dir"
# Create output dir.
self.output_dir = os.path.join(self.tempdir, "output_dir")
osutils.SafeMakedirs(self.output_dir)
# Create chroot and build_target objs.
self.chroot = chroot_lib.Chroot(
path=self.chroot_path, out_path=self.out_path
)
self.build_target = build_target_lib.BuildTarget("target")
# Create the tmp dir.
osutils.SafeMakedirs(self.chroot.tmp)
def testCreateFuzzerSysroot(self) -> None:
"""Test the CreateFuzzerSysroot function under normal operation."""
# Mock the artifact copy.
tar_dest = os.path.join(self.output_dir, constants.CHROME_SYSROOT_TAR)
self.PatchObject(shutil, "copy", return_value=tar_dest)
# Call service, verify arguments passed to run.
sysroot.CreateFuzzerSysroot(
self.chroot, None, self.build_target, self.output_dir
)
self.rc.assertCommandCalled(
[
"cros_generate_sysroot",
"--out-dir",
mock.ANY,
"--board",
self.build_target.name,
"--package",
"virtual/target-fuzzers",
"--out-file",
"sysroot_virtual_target-os.tar.xz",
],
enter_chroot=True,
cwd=self.source_root,
chroot_args=mock.ANY,
extra_env=mock.ANY,
)
class ArchiveChromeEbuildEnvTest(cros_test_lib.MockTempDirTestCase):
"""ArchiveChromeEbuildEnv tests."""
def setUp(self) -> None:
# Create the chroot and sysroot instances.
self.chroot_path = os.path.join(self.tempdir, "chroot_dir")
self.out_path = self.tempdir / "out_dir"
self.chroot = chroot_lib.Chroot(
path=self.chroot_path, out_path=self.out_path
)
self.sysroot_path = os.path.join(self.chroot_path, "sysroot_dir")
self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
# Create the output directory.
self.output_dir = os.path.join(self.tempdir, "output_dir")
osutils.SafeMakedirs(self.output_dir)
# The sysroot's /var/db/pkg prefix for the chrome package directories.
var_db_pkg = self.chroot.full_path(
self.sysroot_path, portage_util.VDB_PATH
)
# Create the var/db/pkg dir so we have that much for no-chrome tests.
osutils.SafeMakedirs(var_db_pkg)
# Two versions of chrome to test the multiple version checks/handling.
chrome_v1 = "%s-1.0.0-r1" % constants.CHROME_PN
chrome_v2 = "%s-2.0.0-r1" % constants.CHROME_PN
# Build the two chrome version paths.
chrome_cat_dir = os.path.join(var_db_pkg, constants.CHROME_CN)
self.chrome_v1_dir = os.path.join(chrome_cat_dir, chrome_v1)
self.chrome_v2_dir = os.path.join(chrome_cat_dir, chrome_v2)
# Directory tuple for verifying the result archive contents.
self.expected_archive_contents = cros_test_lib.Directory(
"./", "environment"
)
# Create a environment.bz2 file to put into folders.
env_file = os.path.join(self.tempdir, "environment")
osutils.Touch(env_file)
cros_build_lib.run(["bzip2", env_file])
self.env_bz2 = "%s.bz2" % env_file
def _CreateChromeDir(self, path: str, populate: bool = True) -> None:
"""Setup a chrome package directory.
Args:
path: The full chrome package path.
populate: Whether to include the environment bz2.
"""
osutils.SafeMakedirs(path)
if populate:
shutil.copy(self.env_bz2, path)
def testSingleChromeVersion(self) -> None:
"""Test a successful single-version run."""
self._CreateChromeDir(self.chrome_v1_dir)
created = sysroot.CreateChromeEbuildEnv(
self.chroot, self.sysroot, None, self.output_dir
)
self.assertStartsWith(created, self.output_dir)
cros_test_lib.VerifyTarball(created, self.expected_archive_contents)
def testMultipleChromeVersions(self) -> None:
"""Test a successful multiple version run."""
# Create both directories, but don't populate the v1 dir so it'll hit an
# error if the wrong one is used.
self._CreateChromeDir(self.chrome_v1_dir, populate=False)
self._CreateChromeDir(self.chrome_v2_dir)
created = sysroot.CreateChromeEbuildEnv(
self.chroot, self.sysroot, None, self.output_dir
)
self.assertStartsWith(created, self.output_dir)
cros_test_lib.VerifyTarball(created, self.expected_archive_contents)
def testNoChrome(self) -> None:
"""Test no version of chrome present."""
self.assertIsNone(
sysroot.CreateChromeEbuildEnv(
self.chroot, self.sysroot, None, self.output_dir
)
)
class GenerateArchiveTest(cros_test_lib.RunCommandTempDirTestCase):
"""Tests for GenerateArchive."""
def setUp(self) -> None:
self.chroot_path = os.path.join(self.tempdir, "chroot_dir")
def testCreateSimpleChromeSysroot(self) -> None:
# A board for which we will create a simple chrome sysroot.
target = "board"
pkg_list = ["virtual/target-fuzzers"]
# Call service, verify arguments passed to run.
sysroot.GenerateArchive(self.chroot_path, target, pkg_list)
self.rc.assertCommandCalled(
[
"cros_generate_sysroot",
"--out-file",
constants.TARGET_SYSROOT_TAR,
"--out-dir",
mock.ANY,
"--board",
target,
"--package",
"virtual/target-fuzzers",
],
cwd=constants.SOURCE_ROOT,
)
class InstallToolchainTest(cros_test_lib.MockTempDirTestCase):
"""Tests for InstallToolchain."""
def setUp(self) -> None:
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=True)
# A board we have a sysroot for already.
self.board = "board"
self.sysroot_path = os.path.join(self.tempdir, "build", self.board)
self.build_target = build_target_lib.BuildTarget(
self.board, build_root=self.sysroot_path
)
self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
# A board we don't have a sysroot for yet.
self.unbuilt_board = "board2"
self.unbuilt_path = os.path.join(
self.tempdir, "build", self.unbuilt_board
)
self.unbuilt_target = build_target_lib.BuildTarget(
self.unbuilt_board, build_root=self.unbuilt_path
)
self.unbuilt_sysroot = sysroot_lib.Sysroot(self.unbuilt_path)
osutils.SafeMakedirs(self.sysroot_path)
def testNoSysroot(self) -> None:
"""Test handling of no sysroot."""
with self.assertRaises(ValueError):
sysroot.InstallToolchain(
self.unbuilt_target,
self.unbuilt_sysroot,
sysroot.SetupBoardRunConfig(),
)
def testLocalBuild(self) -> None:
"""Test the local build logic."""
update_patch = self.PatchObject(self.sysroot, "UpdateToolchain")
# Test False.
config = sysroot.SetupBoardRunConfig(usepkg=False, local_build=False)
sysroot.InstallToolchain(self.build_target, self.sysroot, config)
update_patch.assert_called_with(self.board, local_init=False)
# Test usepkg True.
update_patch.reset_mock()
config = sysroot.SetupBoardRunConfig(usepkg=True, local_build=False)
sysroot.InstallToolchain(self.build_target, self.sysroot, config)
update_patch.assert_called_with(self.board, local_init=True)
# Test local_build True.
update_patch.reset_mock()
config = sysroot.SetupBoardRunConfig(usepkg=False, local_build=True)
sysroot.InstallToolchain(self.build_target, self.sysroot, config)
update_patch.assert_called_with(self.board, local_init=True)
class BuildPackagesRunConfigTest(
cros_test_lib.RunCommandTestCase, cros_test_lib.LoggingTestCase
):
"""Tests for the BuildPackagesRunConfig."""
def testGetBuildPackagesExtraEnv(self) -> None:
"""Test the `cros build-packages` extra env."""
# Test the default config.
instance = sysroot.BuildPackagesRunConfig()
extra_env = instance.GetExtraEnv()
self.assertNotIn("USE_GOMA", extra_env)
self.assertNotIn("USE_REMOTEEXEC", extra_env)
# Test when use_flags are specified.
use_flags = ["flag1", "flag2"]
instance = sysroot.BuildPackagesRunConfig(use_flags=use_flags)
extra_env = instance.GetExtraEnv()
self.assertEqual(extra_env.get("USE"), instance.GetUseFlags())
def testGetPackages(self) -> None:
"""Test getting packages for the config."""
# Test the default config.
instance = sysroot.BuildPackagesRunConfig()
packages = instance.GetPackages()
self.assertIn("virtual/target-os", packages)
self.assertIn("virtual/target-os-dev", packages)
self.assertIn("virtual/target-os-factory", packages)
self.assertIn("virtual/target-os-test", packages)
self.assertIn("chromeos-base/autotest-all", packages)
# Test when packages are specified.
test_packages = ["test/package"]
instance = sysroot.BuildPackagesRunConfig(packages=test_packages)
packages = instance.GetPackages()
self.assertEqual(packages, test_packages)
def testGetForceLocalBuildPackages(self) -> None:
"""Test getting force local build packages for the config."""
test_sysroot_path = "/sysroot/path"
test_sysroot = sysroot_lib.Sysroot(test_sysroot_path)
get_reverse_dependencies_mock = self.PatchObject(
portage_util, "GetReverseDependencies"
)
# Test the default config.
instance = sysroot.BuildPackagesRunConfig()
self.rc.AddCmdResult(
["cros_list_modified_packages", "--sysroot", test_sysroot.path],
stdout="",
)
packages = instance.GetForceLocalBuildPackages(test_sysroot)
# Test when there are cros_workon packages and reverse dependencies
# but skipping base install packages and their reverse dependencies.
instance = sysroot.BuildPackagesRunConfig(incremental_build=False)
test_cros_list_modified_packages_output = (
"test/package1 test/package2\n"
)
self.rc.AddCmdResult(
["cros_list_modified_packages", "--sysroot", test_sysroot.path],
stdout=test_cros_list_modified_packages_output,
)
test_reverse_dependencies = [
package_info.parse("test/package3-1.0"),
package_info.parse("test/package4-2.0-r1"),
]
get_reverse_dependencies_mock.return_value = test_reverse_dependencies
packages = instance.GetForceLocalBuildPackages(test_sysroot)
self.assertIn("test/package1", packages)
self.assertIn("test/package2", packages)
self.assertIn("test/package3", packages)
self.assertIn("test/package4", packages)
# Test base install packages and their reverse dependency packages
# but skipping cros workon packages and their reverse dependencies.
instance = sysroot.BuildPackagesRunConfig(workon=False)
test_emerge_output = """\
[binary N] test/package1 ... to /build/sysroot/
[ebuild r U] chromeos-base/tast-build-deps ... to /build/sysroot/
[binary U] chromeos-base/chromeos-chrome ... /build/sysroot/"""
self.rc.AddCmdResult(
partial_mock.ListRegex(
f"parallel_emerge --sysroot={test_sysroot.path}"
),
stdout=test_emerge_output,
)
test_reverse_dependencies = [
package_info.parse("virtual/package3-1.0"),
package_info.parse("test/package4-2.0-r1"),
]
get_reverse_dependencies_mock.return_value = test_reverse_dependencies
packages = instance.GetForceLocalBuildPackages(test_sysroot)
self.assertIn("chromeos-base/tast-build-deps", packages)
self.assertIn("test/package4", packages)
# Test base install packages and their reverse dependencies are skipped
# when --no-withrevdeps is specified.
instance = sysroot.BuildPackagesRunConfig(incremental_build=False)
with cros_test_lib.LoggingCapturer() as logs:
instance.GetForceLocalBuildPackages(test_sysroot)
self.AssertLogsContain(
logs,
"Starting reverse dependency calculations...",
inverted=True,
)
def testGetEmergeFlags(self) -> None:
"""Test building the emerge flags."""
# Test the default config.
instance = sysroot.BuildPackagesRunConfig()
flags = instance.GetEmergeFlags()
self.assertIn("--with-test-deps", flags)
self.assertIn("--getbinpkg", flags)
self.assertIn("--with-bdeps", flags)
self.assertIn("--usepkg", flags)
# Test when use_any_chrome is specified.
instance = sysroot.BuildPackagesRunConfig(use_any_chrome=True)
flags = instance.GetEmergeFlags()
self.assertIn(
"--force-remote-binary=chromeos-base/chromeos-chrome", flags
)
self.assertIn("--force-remote-binary=chromeos-base/chrome-icu", flags)
# Test when usepkgonly is specified.
instance = sysroot.BuildPackagesRunConfig(usepkgonly=True)
flags = instance.GetEmergeFlags()
self.assertIn("--usepkgonly", flags)
# Test when jobs is specified.
instance = sysroot.BuildPackagesRunConfig(jobs=10)
flags = instance.GetEmergeFlags()
self.assertIn("--jobs=10", flags)
def testGetBuildPackagesRemoteExec(self) -> None:
"""Test the `cros build-packages` with remote execution."""
# Test the default config.
instance = sysroot.BuildPackagesRunConfig()
instance.use_remoteexec = True
reproxy_cfg_file = "reproxy_release.cfg"
instance.reproxy_cfg_file = reproxy_cfg_file
extra_env = instance.GetExtraEnv()
self.assertNotIn("USE_GOMA", extra_env)
self.assertEqual(extra_env.get("USE_REMOTEEXEC"), "true")
self.assertEqual(extra_env.get("REPROXY_CFG_FILE"), reproxy_cfg_file)
class BuildPackagesTest(
cros_test_lib.RunCommandTestCase, cros_test_lib.LoggingTestCase
):
"""Test BuildPackages function."""
def setUp(self) -> None:
# Currently just used to keep the parallel emerge status file from being
# created in the chroot. This probably isn't strictly necessary, but
# since we can otherwise run this test without a chroot existing at all
# and without touching the chroot folder, it's better to keep it out of
# there all together.
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=True)
self.board = "board"
self.target = build_target_lib.BuildTarget(self.board)
self.sysroot = sysroot_lib.Sysroot(self.target.root)
self.build_target_name_mock = self.PatchObject(
sysroot_lib.Sysroot, "build_target_name", return_value=self.board
)
self.binhost_lookup_service_data = (
prebuilts_cloud_pb2.BinhostLookupServiceData(
snapshot_shas=["test_snapshot_sha"], private=True
)
)
self.base_command = [
constants.CHROMITE_BIN_DIR / "parallel_emerge",
f"--sysroot={self.sysroot.path}",
f"--root={self.sysroot.path}",
]
# Prevent the test from switching the cpu governor.
self.PatchObject(cpupower_helper, "ModifyCpuGovernor")
# Prevent the test from remove files in the system.
self.PatchObject(cros_build_lib, "ClearShadowLocks")
self.portageq_envvar_mock = self.PatchObject(
portage_util, "PortageqEnvvar", return_value="gs://fake/binhost"
)
self.lookup_binhosts_mock = self.PatchObject(binhost, "lookup_binhosts")
self.PatchObject(portage_util, "RegenDependencyCache")
self.installed_packages_mock = self.PatchObject(
portage_util.PortageDB, "InstalledPackages"
)
self.clean_outdated_binpkgs_mock = self.PatchObject(
portage_util, "CleanOutdatedBinaryPackages"
)
# Prevent the test from performing gsutil actions.
self.get_creation_time_since_mock = self.PatchObject(
gs.GSContext,
"GetCreationTimeSince",
return_value=datetime.timedelta(days=10),
)
self.PatchObject(gs.GSContext, "InitializeCache")
def testSuccess(self) -> None:
"""Test successful run."""
config = sysroot.BuildPackagesRunConfig()
self.PatchObject(
config, "GetForceLocalBuildPackages", return_value=["test/package1"]
)
sdk_pkgs = " ".join(
sysroot._CRITICAL_SDK_PACKAGES # pylint: disable=protected-access
)
with cros_test_lib.LoggingCapturer() as logs:
sysroot.BuildPackages(self.target, self.sysroot, config)
# The rest of the command's args we test in
# BuildPackagesRunConfigTest, so just make sure we're calling the
# right command and pass the args not handled by the run config.
self.assertCommandContains(self.base_command)
self.assertCommandContains(
[
"--reinstall-atoms=test/package1",
"--usepkg-exclude=test/package1",
]
)
self.assertCommandContains(
[
f"--useoldpkg-atoms={sdk_pkgs}",
f"--rebuild-exclude={sdk_pkgs}",
]
)
# Verify the extra environment variables are passed correctly.
self.assertCommandContains([f"PKGDIR={self.sysroot.path}/packages"])
# Check and the logs contain the expected output.
self.AssertLogsContain(logs, "PORTAGE_BINHOST")
self.AssertLogsContain(logs, "Rebuilding Portage cache.")
self.AssertLogsContain(logs, "Cleaning stale binpkgs.")
self.AssertLogsContain(logs, "Merging board packages now.")
def testLogBinhostAgeThresholds(self) -> None:
"""Test the log output from _LogBinhostAge with different thresholds."""
config = sysroot.BuildPackagesRunConfig()
with cros_test_lib.LoggingCapturer() as logs:
self.lookup_binhosts_mock.return_value = []
# check for when the binhost was created within the threshold
sysroot.BuildPackages(self.target, self.sysroot, config)
self.get_creation_time_since_mock.assert_called_with(
path="gs://fake/binhost/Packages", since_date=mock.ANY
)
self.AssertLogsContain(
logs,
"PORTAGE_BINHOST gs://fake/binhost was created 10 days ago.",
)
# check for when the binhost was created outside of the threshold
self.get_creation_time_since_mock.return_value = datetime.timedelta(
days=31
)
sysroot.BuildPackages(self.target, self.sysroot, config)
self.AssertLogsContain(
logs,
"PORTAGE_BINHOST gs://fake/binhost was created more"
" than 30 days ago. Please repo sync for the latest build"
" artifacts.",
)
self.AssertLogsContain(
logs,
"PORTAGE_BINHOST gs://fake/binhost was created 31 days ago.",
)
def testLogBinHostAgeUrls(self) -> None:
"""Test the log output from _LogBinhostAge with different binhosts."""
config = sysroot.BuildPackagesRunConfig()
with cros_test_lib.LoggingCapturer() as logs:
self.lookup_binhosts_mock.return_value = []
# check that the gs url is formatted correctly when the binhost
# has a trailing slash
self.portageq_envvar_mock.return_value = "gs://fake/binhost/"
sysroot.BuildPackages(self.target, self.sysroot, config)
self.get_creation_time_since_mock.assert_called_with(
path="gs://fake/binhost/Packages", since_date=mock.ANY
)
self.AssertLogsContain(
logs,
"PORTAGE_BINHOST gs://fake/binhost/ was created 10 days ago.",
)
# check for when there is a GSNoSuchKey error for a malformed
# binhost url
self.get_creation_time_since_mock.side_effect = gs.GSNoSuchKey(
"Err"
)
sysroot.BuildPackages(self.target, self.sysroot, config)
self.AssertLogsContain(
logs,
"PORTAGE_BINHOST gs://fake/binhost/",
)
self.AssertLogsContain(
logs,
"Error getting the binhost age",
)
def testLookupService(self) -> None:
"""Test that binhosts from the lookup service are passed to portage."""
config = sysroot.BuildPackagesRunConfig()
self.lookup_binhosts_mock.return_value = ["gs://AAAA", "gs://BBBB"]
sysroot.BuildPackages(self.target, self.sysroot, config)
self.assertCommandContains(["PORTAGE_BINHOST=gs://AAAA gs://BBBB"])
def testEcleanBinpkgs(self) -> None:
"""Test that eclean is called with the expected packages."""
def assert_file_contents(
sysroot_path: Union[str, os.PathLike],
deep: bool,
exclusion_file: Optional[Union[str, os.PathLike]] = None,
) -> None:
if exclusion_file:
contents = osutils.ReadFile(exclusion_file)
self.assertEqual("cross-dev/package", contents)
self.assertEqual(self.sysroot.path, sysroot_path)
self.assertTrue(deep)
self.PatchObject(os.path, "exists", return_value=True)
self.installed_packages_mock.return_value = [
portage_util.InstalledPackage(None, "", "test", "package-1.0"),
portage_util.InstalledPackage(None, "", "cross-dev", "package-1.0"),
]
self.clean_outdated_binpkgs_mock.side_effect = assert_file_contents
config = sysroot.BuildPackagesRunConfig()
sysroot.BuildPackages(self.target, self.sysroot, config)
def testInstallDebugSymbols(self) -> None:
"""Test cros_install_debug_syms is called with the expected args."""
config = sysroot.BuildPackagesRunConfig(install_debug_symbols=True)
with cros_test_lib.LoggingCapturer() as logs:
sysroot.BuildPackages(self.target, self.sysroot, config)
self.assertCommandContains(
[
constants.CHROMITE_BIN_DIR / "cros_install_debug_syms",
f"--board={self.build_target_name_mock}",
"--all",
]
)
self.AssertLogsContain(logs, "Fetching the debug symbols.")
def testPackageFailure(self) -> None:
"""Test package failure handling."""
failed = ["cat/pkg", "foo/bar"]
cpvs = [package_info.SplitCPV(p, strict=False) for p in failed]
self.PatchObject(
portage_util, "ParseDieHookStatusFile", return_value=cpvs
)
config = sysroot.BuildPackagesRunConfig()
self.rc.AddCmdResult(
partial_mock.ListRegex("emerge.*--useoldpkg-atoms"), returncode=1
)
with self.assertRaises(sysroot_lib.PackageInstallError) as e:
sysroot.BuildPackages(self.target, self.sysroot, config)
self.assertEqual(cpvs, e.exception.failed_packages)
self.assertCommandContains(self.base_command)
def testEnsureBootstrapSuccess(self) -> None:
"""Ensure ensure_bootstrap gets called."""
config = sysroot.BuildPackagesRunConfig()
sysroot.BuildPackages(self.target, self.sysroot, config)
self.assertCommandCalled(
[constants.DEPOT_TOOLS_DIR / "ensure_bootstrap"]
)
def testEnsureBootstrapFailure(self) -> None:
"""Ensure BuildPackages fails when ensure_bootstrap fails."""
self.rc.AddCmdResult(
partial_mock.ListRegex("ensure_bootstrap"), returncode=1
)
config = sysroot.BuildPackagesRunConfig()
with self.assertRaises(cros_build_lib.RunCommandError):
sysroot.BuildPackages(self.target, self.sysroot, config)
self.assertCommandCalled(
[constants.DEPOT_TOOLS_DIR / "ensure_bootstrap"]
)
class GatherSymbolFilesTest(cros_test_lib.MockTempDirTestCase):
"""Base class for testing GatherSymbolFiles."""
SLIM_CONTENT = """
some junk
"""
FAT_CONTENT = """
STACK CFI 1234
some junk
STACK CFI 1234
"""
def createSymbolFile(self, filename, content=FAT_CONTENT, size=0) -> None:
"""Create a symbol file using content with minimum size."""
osutils.SafeMakedirs(os.path.dirname(filename))
# If a file size is given, force that to be the minimum file size.
# Create a sparse file so large files are practical.
with open(filename, "w+b") as f:
f.truncate(size)
f.seek(0)
f.write(content.encode("utf-8"))
def test_ListOutputOfGatherSymbolFiles(self) -> None:
"""Mimic how the controller materializes output of GatherSymbolFiles."""
# Create directory with some symbol files.
tar_tmp_dir = os.path.join(self.tempdir, "tar_tmp")
output_dir = os.path.join(self.tempdir, "output")
input_dir = os.path.join(self.tempdir, "input")
osutils.SafeMakedirs(output_dir)
self.createSymbolFile(os.path.join(input_dir, "a/b/c/file1.sym"))
self.createSymbolFile(os.path.join(input_dir, "a/b/c/d/file2.sym"))
self.createSymbolFile(os.path.join(input_dir, "a/file3.sym"))
self.createSymbolFile(os.path.join(input_dir, "a/b/c/d/e/file1.sym"))
# Call sysroot.GatherSymbolFiles to find symbol files under self.tempdir
# and copy them to output_dir.
symbol_files = list(
sysroot.GatherSymbolFiles(tar_tmp_dir, output_dir, [input_dir])
)
self.assertEqual(len(symbol_files), 4)
def test_GatherSymbolFiles(self) -> None:
"""Test that files are found and copied."""
# Create directory with some symbol files.
tar_tmp_dir = os.path.join(self.tempdir, "tar_tmp")
output_dir = os.path.join(self.tempdir, "output")
input_dir = os.path.join(self.tempdir, "input")
osutils.SafeMakedirs(output_dir)
self.createSymbolFile(os.path.join(input_dir, "a/b/c/file1.sym"))
self.createSymbolFile(os.path.join(input_dir, "a/b/c/d/file2.sym"))
self.createSymbolFile(os.path.join(input_dir, "a/file3.sym"))
self.createSymbolFile(os.path.join(input_dir, "a/b/c/d/e/file1.sym"))
# Call sysroot.GatherSymbolFiles to find symbol files under self.tempdir
# and copy them to output_dir.
symbol_files = list(
sysroot.GatherSymbolFiles(tar_tmp_dir, output_dir, [input_dir])
)
# Construct the expected symbol files. Note that the SymbolFileTuple
# field source_file_name is the full path to where a symbol file was
# found, while relative_path is the relative path (from the search)
# where it is created in the output directory.
expected_symbol_files = [
sysroot.SymbolFileTuple(
source_file_name=os.path.join(input_dir, "a/b/c/file1.sym"),
relative_path="a/b/c/file1.sym",
),
sysroot.SymbolFileTuple(
source_file_name=os.path.join(input_dir, "a/b/c/d/file2.sym"),
relative_path="a/b/c/d/file2.sym",
),
sysroot.SymbolFileTuple(
source_file_name=os.path.join(input_dir, "a/file3.sym"),
relative_path="a/file3.sym",
),
sysroot.SymbolFileTuple(
source_file_name=os.path.join(input_dir, "a/b/c/d/e/file1.sym"),
relative_path="a/b/c/d/e/file1.sym",
),
]
# Sort symbol_files and expected_output_files by the relative_path
# attribute.
symbol_files = sorted(
symbol_files, key=operator.attrgetter("relative_path")
)
expected_symbol_files = sorted(
expected_symbol_files, key=operator.attrgetter("relative_path")
)
# Compare the files to the expected files. This verifies the size and
# contents, and on failure shows the full contents.
self.assertEqual(symbol_files, expected_symbol_files)
# Verify that the files in output_dir match the SymbolFile
# relative_paths.
files_in_output_dir = self.getFilesWithRelativeDir(output_dir)
files_in_output_dir.sort()
symbol_file_relative_paths = [obj.relative_path for obj in symbol_files]
symbol_file_relative_paths.sort()
self.assertEqual(files_in_output_dir, symbol_file_relative_paths)
# Verify that the display_name of each symbol does not contain pathsep.
symbol_file_relative_paths = [
os.path.basename(obj.relative_path) for obj in symbol_files
]
for display_name in symbol_file_relative_paths:
self.assertEqual(-1, display_name.find(os.path.sep))
def test_GatherSymbolTarFiles(self) -> None:
"""Test that symbol files in tar files are extracted."""
output_dir = os.path.join(self.tempdir, "output")
osutils.SafeMakedirs(output_dir)
# Set up test input directory.
tarball_dir = os.path.join(self.tempdir, "some/path")
files_in_tarball = [
"dir1/fileZ.sym",
"dir2/fileY.sym",
"dir2/fileX.sym",
"fileA.sym",
"fileB.sym",
"fileC.sym",
]
for filename in files_in_tarball:
self.createSymbolFile(os.path.join(tarball_dir, filename))
temp_tarball_file_path = os.path.join(self.tempdir, "symfiles.tar")
cros_build_lib.CreateTarball(temp_tarball_file_path, tarball_dir)
# Now that we've created the tarball, remove the .sym files in
# the tarball dir and move the tarball to that dir.
for filename in files_in_tarball:
os.remove(os.path.join(tarball_dir, filename))
tarball_path = os.path.join(tarball_dir, "symfiles.tar")
shutil.move(temp_tarball_file_path, tarball_path)
# Execute sysroot.GatherSymbolFiles where the path contains the tarball.
symbol_files = list(
sysroot.GatherSymbolFiles(tarball_dir, output_dir, [tarball_path])
)
self.assertEqual(len(symbol_files), 6)
# Verify the symbol_file relative_paths.
symbol_file_relative_paths = [obj.relative_path for obj in symbol_files]
symbol_file_relative_paths.sort()
self.assertEqual(
symbol_file_relative_paths,
[
"dir1/fileZ.sym",
"dir2/fileX.sym",
"dir2/fileY.sym",
"fileA.sym",
"fileB.sym",
"fileC.sym",
],
)
# Verify the symbol_file source_file_names.
symbol_file_source_file_names = [
obj.source_file_name for obj in symbol_files
]
symbol_file_source_file_names.sort()
# Note that the expected symbol_file_source_names are the full path to
# the tarfile followed by the relative path, such as
# /tmp/chromite.test2ng92vzo/some/path/symfiles.tar/dir1/fileZ.sym
expected_symbol_file_source_names = [
os.path.join(tarball_path, "dir1/fileZ.sym"),
os.path.join(tarball_path, "dir2/fileX.sym"),
os.path.join(tarball_path, "dir2/fileY.sym"),
os.path.join(tarball_path, "fileA.sym"),
os.path.join(tarball_path, "fileB.sym"),
os.path.join(tarball_path, "fileC.sym"),
]
self.assertEqual(
symbol_file_source_file_names, expected_symbol_file_source_names
)
# Verify that the files in output_dir match the SymbolFile
# relative_paths.
files_in_output_dir = self.getFilesWithRelativeDir(output_dir)
files_in_output_dir.sort()
symbol_file_relative_paths = [obj.relative_path for obj in symbol_files]
symbol_file_relative_paths.sort()
self.assertEqual(files_in_output_dir, symbol_file_relative_paths)
# Verify that the display_name of each symbol does not contain pathsep.
symbol_file_relative_paths = [
os.path.basename(obj.relative_path) for obj in symbol_files
]
for display_name in symbol_file_relative_paths:
self.assertEqual(-1, display_name.find(os.path.sep))
def test_GatherSymbolTarFilesWithNonSymFiles(self) -> None:
"""Test that non-symbol files in tar files are not extracted."""
output_dir = os.path.join(self.tempdir, "output")
osutils.SafeMakedirs(output_dir)
# Set up test input directory.
tarball_dir = os.path.join(self.tempdir, "some/path")
files_in_tarball = [
"dir1/fileU.sym",
"dir1/fileU.txt",
"fileD.sym",
"fileD.txt",
]
for filename in files_in_tarball:
# we don't care about file contents, so we are using
# createSymbolFile for files whether they end with .sym or not.
self.createSymbolFile(os.path.join(tarball_dir, filename))
temp_tarball_file_path = os.path.join(self.tempdir, "symfiles.tar")
cros_build_lib.CreateTarball(temp_tarball_file_path, tarball_dir)
# Now that we've created the tarball, remove the .sym files in
# the tarball dir and move the tarball to that dir.
for filename in files_in_tarball:
os.remove(os.path.join(tarball_dir, filename))
tarball_path = os.path.join(tarball_dir, "symfiles.tar")
shutil.move(temp_tarball_file_path, tarball_path)
# Execute sysroot.GatherSymbolFiles where the path contains the tarball.
symbol_files = list(
sysroot.GatherSymbolFiles(tarball_dir, output_dir, [tarball_path])
)
# Verify the symbol_file relative_paths only has .sym files.
symbol_file_relative_paths = [obj.relative_path for obj in symbol_files]
symbol_file_relative_paths.sort()
self.assertEqual(
symbol_file_relative_paths, ["dir1/fileU.sym", "fileD.sym"]
)
for symfile in symbol_file_relative_paths:
extension = symfile.split(".")[1]
self.assertEqual(extension, "sym")
def test_GatherSymbolFileFullFilePaths(self) -> None:
"""Test full filepaths (.sym and .txt) only gather .sym files."""
tar_tmp_dir = os.path.join(self.tempdir, "tar_tmp")
output_dir = os.path.join(self.tempdir, "output")
input_dir = os.path.join(self.tempdir, "input")
osutils.SafeMakedirs(output_dir)
# We don't care about contents, so use createSymbolFiles for all files.
self.createSymbolFile(os.path.join(input_dir, "a_file.sym"))
self.createSymbolFile(os.path.join(input_dir, "a_file.txt"))
# Call sysroot.GatherSymbolFiles with full paths to files, some of which
# don't end in .sym, verify that only .sym files get copied to
# output_dir.
symbol_files = list(
sysroot.GatherSymbolFiles(
tar_tmp_dir,
output_dir,
[
os.path.join(input_dir, "a_file.sym"),
os.path.join(input_dir, "a_file.txt"),
],
)
)
# Construct the expected symbol files. Note that the SymbolFileTuple
# field source_file_name is the full path to where a symbol file was
# found, while relative_path is the relative path (from the search)
# where it is created in the output directory.
expected_symbol_files = [
sysroot.SymbolFileTuple(
source_file_name=os.path.join(input_dir, "a_file.sym"),
relative_path="a_file.sym",
)
]
# Compare the files to the expected files. This verifies the size and
# contents, and on failure shows the full contents.
self.assertEqual(symbol_files, expected_symbol_files)
# Verify that only a_file.sym is in the output_dir.
files_in_output_dir = self.getFilesWithRelativeDir(output_dir)
self.assertEqual(files_in_output_dir, ["a_file.sym"])
def getFilesWithRelativeDir(self, dest_dir):
"""Find all files below dest_dir using dir relative to dest_dir."""
relative_files = []
for path, __, files in os.walk(dest_dir):
for filename in files:
fullpath = os.path.join(path, filename)
relpath = os.path.relpath(fullpath, dest_dir)
relative_files.append(relpath)
return relative_files
class GenerateBreakpadSymbolsTest(cros_test_lib.RunCommandTempDirTestCase):
"""Base class for testing GenerateBreakpadSymbols."""
def setUp(self) -> None:
self.chroot_dir = os.path.join(self.tempdir, "chroot_dir")
osutils.SafeMakedirs(self.chroot_dir)
def test_generateBreakpadSymbols(self) -> None:
"""Verify calling the service layer invokes the script as expected."""
chroot = chroot_lib.Chroot(self.chroot_dir)
build_target = build_target_lib.BuildTarget("board")
# Call the method being tested.
sysroot.GenerateBreakpadSymbols(chroot, build_target, False, False, [])
self.rc.assertCommandContains(
[
"cros_generate_breakpad_symbols",
"--board=board",
"--jobs",
mock.ANY,
"--exclude-dir=firmware",
],
enter_chroot=True,
chroot_args=["--chroot", mock.ANY],
)
def test_generateBreakpadSymbolsWithDebug(self) -> None:
"""Verify that calling with debug invokes the script as expected."""
chroot = chroot_lib.Chroot(self.chroot_dir)
build_target = build_target_lib.BuildTarget("board")
# Call the method being tested.
sysroot.GenerateBreakpadSymbols(chroot, build_target, True, False, [])
self.rc.assertCommandContains(
[
"cros_generate_breakpad_symbols",
"--debug",
"--board=board",
"--jobs",
mock.ANY,
"--exclude-dir=firmware",
],
enter_chroot=True,
chroot_args=["--chroot", mock.ANY],
)
def test_generateBreakpadSymbolsWithIgnoreErrors(self) -> None:
"""Verify that calling with debug invokes the script as expected."""
chroot = chroot_lib.Chroot(self.chroot_dir)
build_target = build_target_lib.BuildTarget("board")
# Call the method being tested.
sysroot.GenerateBreakpadSymbols(chroot, build_target, False, True, [])
self.rc.assertCommandContains(
[
"cros_generate_breakpad_symbols",
"--ignore_errors",
"--board=board",
"--jobs",
mock.ANY,
"--exclude-dir=firmware",
],
enter_chroot=True,
chroot_args=["--chroot", mock.ANY],
)
def test_generateBreakpadSymbolsWithIgnoreIgnoreExpectedFiles(self) -> None:
"""Verify that calling with debug invokes the script as expected."""
chroot = chroot_lib.Chroot(self.chroot_dir)
build_target = build_target_lib.BuildTarget("board")
# Call the method being tested.
sysroot.GenerateBreakpadSymbols(
chroot, build_target, False, False, ["ASH_CHROME", "LIBC"]
)
self.rc.assertCommandContains(
[
"cros_generate_breakpad_symbols",
"--ignore_expected_file=ASH_CHROME",
"--ignore_expected_file=LIBC",
"--board=board",
"--jobs",
mock.ANY,
"--exclude-dir=firmware",
],
enter_chroot=True,
chroot_args=["--chroot", mock.ANY],
)
class BundleDebugSymbolsTest(cros_test_lib.MockTempDirTestCase):
"""Unittests for BundleDebugSymbols."""
def setUp(self) -> None:
# Configure {chroot,out,syroot}_path.
self.chroot_path = os.path.join(self.tempdir, "chroot_dir")
self.out_path = self.tempdir / "out_dir"
self.sysroot_path = os.path.join(self.chroot_path, "build/target")
# Has to be run outside the chroot.
self.PatchObject(cros_build_lib, "IsInsideChroot", return_value=False)
osutils.SafeMakedirs(self.sysroot_path)
# Create output dir.
self.output_dir = os.path.join(self.tempdir, "output_dir")
osutils.SafeMakedirs(self.output_dir)
# Create chroot, sysroot, and build_target objs.
self.chroot = chroot_lib.Chroot(
path=self.chroot_path, out_path=self.out_path
)
self.sysroot = sysroot_lib.Sysroot(path=self.sysroot_path)
self.build_target = build_target_lib.BuildTarget("target")
# Create the tmp dir.
osutils.SafeMakedirs(self.chroot.tmp)
def testBundleBreakpadDebugSymbols(self) -> None:
"""BundleBreakpadSymbols calls cbuildbot/commands with correct args."""
# Patch service layer functions.
generate_breakpad_symbols_patch = self.PatchObject(
sysroot,
"GenerateBreakpadSymbols",
return_value=cros_build_lib.CompletedProcess(
returncode=0, stdout=""
),
)
gather_symbol_files_patch = self.PatchObject(
sysroot,
"GatherSymbolFiles",
return_value=[
sysroot.SymbolFileTuple(
source_file_name="path/to/source/file1.sym",
relative_path="file1.sym",
)
],
)
tar_file = sysroot.BundleBreakpadSymbols(
self.chroot,
self.sysroot,
self.build_target,
self.output_dir,
False,
["ASH_CHROME"],
)
# Verify mock objects were called.
generate_breakpad_symbols_patch.assert_called_with(
self.chroot,
self.build_target,
debug=True,
ignore_errors=False,
ignore_expected_files=["ASH_CHROME"],
)
gather_symbol_files_patch.assert_called()
# Verify response proto contents and output directory contents.
self.assertIsNotNone(tar_file)
self.assertTrue(tar_file.endswith("/output_dir/debug_breakpad.tar.xz"))
def testBundleDebugSymbols(self) -> None:
"""BundleDebugSymbols calls cbuildbot/commands with correct args."""
# Patch service layer functions.
self.PatchObject(os.path, "exists", return_value=True)
self.PatchObject(os.path, "isdir", return_value=True)
create_tarball_patch = self.PatchObject(
cros_build_lib,
"CreateTarball",
return_value=cros_build_lib.CompletedProcess(
returncode=0, stdout=""
),
)
tar_file = sysroot.BundleDebugSymbols(
self.chroot,
self.sysroot,
build_target_lib.BuildTarget(None),
self.output_dir,
)
create_tarball_patch.assert_called()
# Verify response contents.
self.assertIsNotNone(tar_file)
self.assertTrue(tar_file.endswith("/output_dir/debug.tgz"))
def test_CollectBazelPerformanceArtifacts(monkeypatch, tmp_path) -> None:
"""CollectBazelPerformanceArtifacts copies the known set of files."""
# Configure {chroot,out,syroot}_path.
tempdir = tmp_path
chroot_path = tempdir / "chroot_dir"
out_path = tempdir / "out_dir"
sysroot_path = chroot_path / "build" / "target"
# Simulate being outside the chroot.
monkeypatch.setattr(cros_build_lib, "IsInsideChroot", lambda: False)
osutils.SafeMakedirs(sysroot_path)
# Create output dir.
output_dir = os.path.join(tempdir, "output_dir")
osutils.SafeMakedirs(output_dir)
# Create chroot, sysroot, and build_target objs.
chroot = chroot_lib.Chroot(path=chroot_path, out_path=out_path)
target_sysroot = sysroot_lib.Sysroot(path=sysroot_path)
build_target = build_target_lib.BuildTarget("target")
# Create Bazel performance files for testing
sysroot_bazel_files = (
sysroot.BAZEL_ALLPACKAGES_COMMAND_PROFILE_FILE,
sysroot.BAZEL_ALLPACKAGES_EXEC_LOG_FILE,
)
created_files = [
chroot.full_path(artifact) for artifact in sysroot_bazel_files
]
for created_file in created_files:
osutils.Touch(created_file, makedirs=True)
expected_files = [
tempdir / os.path.basename(file) for file in sysroot_bazel_files
]
# Collect the files and confirm we got what we expected
archived_files = sysroot.CollectBazelPerformanceArtifacts(
chroot,
target_sysroot,
build_target,
tempdir,
)
for expected_file in expected_files:
assert str(expected_file) in archived_files
assert expected_file.exists()
class RemoteExecutionTest(cros_test_lib.MockLoggingTestCase):
"""Unittests for remote execution context manager."""
def setUp(self) -> None:
self.goma_mock = self.PatchObject(goma_lib, "Goma", autospec=True)
self.goma_instance = self.goma_mock.return_value
def testGomaDir(self) -> None:
"""Test the case where GOMA env variable is defined."""
os.environ.update(
{
"GOMA_DIR": "goma/path",
"GOMA_TMP_DIR": "goma/tmp/dir",
"GLOG_log_dir": "glog/log/dir",
}
)
with sysroot.RemoteExecution(use_goma=True):
self.goma_mock.assert_called_once_with(
Path("goma/path"),
"goma/tmp/dir",
stage_name="BuildPackages",
log_dir="glog/log/dir",
)
self.goma_instance.Restart.assert_called_once()
self.goma_instance.Stop.assert_called_once()
def testGomaHomeDir(self) -> None:
"""Test the case where Home Path is used."""
self.PatchObject(Path, "home", return_value=Path("home"))
with sysroot.RemoteExecution(use_goma=True):
self.goma_mock.assert_called_once_with(
Path("home/goma"),
None,
stage_name="BuildPackages",
log_dir=None,
)
self.goma_instance.Restart.assert_called_once()
self.goma_instance.Stop.assert_called_once()
def testGomaException(self) -> None:
"""Test the case where GOMA interface raises exception."""
self.goma_mock.side_effect = ValueError()
with cros_test_lib.LoggingCapturer() as log:
with sysroot.RemoteExecution(use_goma=True):
self.AssertLogsMatch(log, ".*initialization error.*")
self.goma_instance.Restart.assert_not_called()
self.goma_instance.Stop.assert_not_called()
def testNoRemoteExec(self) -> None:
"""Test the case where no remoteexec is requested with env variable."""
os.environ.update(
{
"GOMA_DIR": "goma/path",
}
)
with sysroot.RemoteExecution(use_goma=False):
pass
self.goma_mock.assert_not_called()
def testNoRemoteExecNoEnv(self) -> None:
"""Test case where no remoteexec is requested without env variable."""
with sysroot.RemoteExecution(use_goma=False):
pass
self.goma_mock.assert_not_called()
class ArchiveSysrootTest(cros_test_lib.TempDirTestCase):
"""ArchiveSysroot tests."""
def setUp(self) -> None:
chroot_path = self.tempdir / "chroot"
self.chroot = chroot_lib.Chroot(path=chroot_path)
sysroot_path = chroot_path / "build" / "testBoard"
self.sysroot = sysroot_lib.Sysroot(sysroot_path)
self.dir_structure = [
cros_test_lib.Directory(".", []),
cros_test_lib.Directory("test", ["foo.bar"]),
]
def testArchiveSysroot(self) -> None:
"""Archive a sample folder and verify its contents."""
cros_test_lib.CreateOnDiskHierarchy(
self.chroot.full_path(self.sysroot.path), self.dir_structure
)
archive_file = sysroot.ArchiveSysroot(
self.chroot,
self.sysroot,
build_target_lib.BuildTarget("testBoard"),
self.tempdir,
)
self.assertTrue(Path(archive_file).exists())
self.assertEqual(
archive_file, self.tempdir / sysroot.SYSROOT_ARCHIVE_FILE
)
cros_test_lib.VerifyTarball(archive_file, self.dir_structure)
def testArchiveSysrootFailure(self) -> None:
"""Archive a sample folder that doesnt exists."""
archive_file = sysroot.ArchiveSysroot(
self.chroot,
self.sysroot,
build_target_lib.BuildTarget("testBoard"),
self.tempdir,
)
self.assertIsNone(archive_file)
class ExtractSysrootTest(cros_test_lib.TempDirTestCase):
"""ExtractSysroot tests."""
def setUp(self) -> None:
chroot_path = self.tempdir / "chroot"
self.chroot = chroot_lib.Chroot(path=chroot_path)
self.sysroot_path = chroot_path / "build" / "testBoard"
self.sysroot = sysroot_lib.Sysroot(self.sysroot_path)
self.sysroot_archive_path = chroot_path / "build" / "tmp"
self.sysroot_archive = sysroot_lib.Sysroot(self.sysroot_archive_path)
self.build_target = build_target_lib.BuildTarget("testBoard")
self.archive_dir_structure = [
cros_test_lib.Directory(".", []),
cros_test_lib.Directory("test", ["foo.bar"]),
]
self.sysroot_dir_structure = [
cros_test_lib.Directory(".", ["test", "test2"]),
]
self.expected_dir_structure = [
cros_test_lib.Directory("test", ["foo.bar"]),
]
def testExtractSysroot(self) -> None:
"""Extract a simple tar."""
sysroot_archive = None
cros_test_lib.CreateOnDiskHierarchy(
self.sysroot_archive_path, self.archive_dir_structure
)
sysroot_archive = sysroot.ArchiveSysroot(
self.chroot,
self.sysroot_archive,
build_target_lib.BuildTarget("tmp"),
self.tempdir,
)
cros_test_lib.CreateOnDiskHierarchy(
self.sysroot_path, self.sysroot_dir_structure
)
result = sysroot.ExtractSysroot(
self.chroot, self.sysroot, sysroot_archive
)
self.assertTrue(Path(result).exists())
self.assertEqual(str(result), self.sysroot.path)
cros_test_lib.VerifyOnDiskHierarchy(
self.sysroot_path, self.expected_dir_structure
)