blob: 22ab0a43ac9004e0dfeeb0ead57ed5f77f020329 [file] [log] [blame]
# Copyright 2015 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test the path_util module."""
import itertools
import os
from pathlib import Path
from typing import Optional
from unittest import mock
import pytest
from chromite.lib import constants
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
from chromite.lib import git
from chromite.lib import osutils
from chromite.lib import partial_mock
from chromite.lib import path_util
from chromite.utils import xdg_util
FAKE_SOURCE_PATH = "/path/to/source/tree"
FAKE_OUT_PATH = FAKE_SOURCE_PATH / constants.DEFAULT_OUT_DIR
FAKE_REPO_PATH = "/path/to/repo"
CUSTOM_SOURCE_PATH = "/custom/source/path"
CUSTOM_CHROOT_PATH = "/custom/chroot/path"
CUSTOM_OUT_PATH = Path("/custom/out/path")
class DetermineCheckoutTest(cros_test_lib.MockTempDirTestCase):
"""Verify functionality for figuring out what checkout we're in."""
def setUp(self) -> None:
self.rc_mock = cros_test_lib.RunCommandMock()
self.StartPatcher(self.rc_mock)
self.rc_mock.SetDefaultCmdResult()
def RunTest(
self, dir_struct, subdir, expected_root, expected_type, expected_src
) -> None:
"""Run a test with specific parameters and expected results."""
cros_test_lib.CreateOnDiskHierarchy(self.tempdir, dir_struct)
search_path = self.tempdir / subdir
checkout_info = path_util.DetermineCheckout(search_path)
full_root = expected_root
if expected_root is not None:
full_root = os.path.join(self.tempdir, expected_root)
full_src = expected_src
if expected_src is not None:
full_src = os.path.join(self.tempdir, expected_src)
self.assertEqual(checkout_info.root, full_root)
self.assertEqual(checkout_info.type, expected_type)
self.assertEqual(checkout_info.chrome_src_dir, full_src)
def testGclientRepo(self) -> None:
"""Recognizes a GClient repo checkout."""
dir_struct = [
"a/.gclient",
"a/b/.repo/",
"a/b/c/.gclient",
"a/b/c/d/somefile",
]
self.RunTest(
dir_struct,
"a/b/c",
"a/b/c",
path_util.CheckoutType.GCLIENT,
"a/b/c/src",
)
self.RunTest(
dir_struct,
"a/b/c/d",
"a/b/c",
path_util.CheckoutType.GCLIENT,
"a/b/c/src",
)
self.RunTest(
dir_struct, "a/b", "a/b", path_util.CheckoutType.REPO, None
)
self.RunTest(
dir_struct, "a", "a", path_util.CheckoutType.GCLIENT, "a/src"
)
def testGitUnderGclient(self) -> None:
"""Recognizes a chrome git checkout by gclient."""
self.rc_mock.AddCmdResult(
partial_mock.In("config"), stdout=constants.CHROMIUM_GOB_URL
)
dir_struct = [
"a/.gclient",
"a/src/.git/",
]
self.RunTest(
dir_struct, "a/src", "a", path_util.CheckoutType.GCLIENT, "a/src"
)
def testGitUnderRepo(self) -> None:
"""Recognizes a chrome git checkout by repo."""
self.rc_mock.AddCmdResult(
partial_mock.In("config"), stdout=constants.CHROMIUM_GOB_URL
)
dir_struct = [
"a/.repo/",
"a/b/.git/",
]
self.RunTest(dir_struct, "a/b", "a", path_util.CheckoutType.REPO, None)
def testBadGit1(self) -> None:
""".git is not a directory."""
self.RunTest(
["a/.git"], "a", None, path_util.CheckoutType.UNKNOWN, None
)
def testBadGit2(self) -> None:
"""'git config' returns nothing."""
self.RunTest(
["a/.repo/", "a/b/.git/"],
"a/b",
"a",
path_util.CheckoutType.REPO,
None,
)
def testBadGit3(self) -> None:
"""'git config' returns error."""
self.rc_mock.AddCmdResult(partial_mock.In("config"), returncode=5)
self.RunTest(
["a/.git/"], "a", None, path_util.CheckoutType.UNKNOWN, None
)
class TestCheckoutInfoTracksMain(cros_test_lib.TestCase):
"""Tests for CheckoutInfo.tracks_main property."""
def test_track_main_citc(self) -> None:
"""CITC checkouts always track main (for now)."""
info = path_util.CheckoutInfo(path_util.CheckoutType.CITC, "", "")
assert info.tracks_main
def test_track_main_gclient(self) -> None:
"""Gclient checkouts aren't our tree, and therefore don't track main."""
info = path_util.CheckoutInfo(path_util.CheckoutType.GCLIENT, "", "")
assert not info.tracks_main
def test_track_main_repo(self) -> None:
"""A repo checkout to the "snapshot" branch should track main."""
info = path_util.CheckoutInfo(
path_util.CheckoutType.REPO, "/path/to/source", ""
)
mock_obj = mock.Mock()
mock_obj.manifest_branch = "snapshot"
with mock.patch.object(
git.ManifestCheckout, "Cached", return_value=mock_obj
):
assert info.tracks_main
def test_track_stable_repo(self) -> None:
"""A repo checkout to the "stable" branch should not track main."""
info = path_util.CheckoutInfo(
path_util.CheckoutType.REPO, "/path/to/source", ""
)
mock_obj = mock.Mock()
mock_obj.manifest_branch = "stable"
with mock.patch.object(
git.ManifestCheckout, "Cached", return_value=mock_obj
):
assert not info.tracks_main
@pytest.mark.parametrize(
["checkout", "expected_binary"],
[
(
path_util.CheckoutInfo(
path_util.CheckoutType.REPO, FAKE_SOURCE_PATH, None
),
Path(FAKE_SOURCE_PATH) / ".repo" / "repo" / "repo",
),
(
path_util.CheckoutInfo(
path_util.CheckoutType.CITC, FAKE_SOURCE_PATH, None
),
None,
),
],
)
def test_checkout_repo_binary(
checkout: path_util.CheckoutInfo, expected_binary: Optional[Path]
):
"""Test the CheckoutInfo.repo_binary property."""
assert checkout.repo_binary == expected_binary
class FindCacheDirTest(cros_test_lib.TestCase):
"""Test cache dir specification and finding functionality."""
@pytest.fixture(autouse=True)
def init_temp_dir(
self,
tmp_path: Path,
run_mock: cros_test_lib.RunCommandMock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
dir_struct = [
"repo/.repo/",
"repo/manifest/",
"gclient/.gclient",
]
cros_test_lib.CreateOnDiskHierarchy(tmp_path, dir_struct)
# pylint: disable=attribute-defined-outside-init
self.repo_root = tmp_path / "repo"
self.gclient_root = tmp_path / "gclient"
self.nocheckout_root = tmp_path / "nothing"
self.rc_mock = run_mock
self.monkeypatch = monkeypatch
def testRepoRoot(self) -> None:
"""Test when we are inside a repo checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.repo_root)
self.assertEqual(
path_util.FindCacheDir(),
os.path.join(self.repo_root, path_util.GENERAL_CACHE_DIR),
)
def testGclientRoot(self) -> None:
"""Test when we are inside a gclient checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.gclient_root)
self.assertEqual(
path_util.FindCacheDir(),
os.path.join(
self.gclient_root, "src", "build", path_util.CHROME_CACHE_DIR
),
)
def testTempdir(self) -> None:
"""Test when we are not in any checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.nocheckout_root)
self.assertStartsWith(
path_util.FindCacheDir(), os.path.expanduser("~/")
)
class FindConfigDirTest(cros_test_lib.TestCase):
"""Test cache dir specification and finding functionality."""
@pytest.fixture(autouse=True)
def init_temp_dir(
self,
tmp_path: Path,
run_mock: cros_test_lib.RunCommandMock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
dir_struct = [
"repo/.repo/",
"repo/manifest/",
"gclient/.gclient",
"cog/chrome-internal/",
"cog/.citc/",
]
cros_test_lib.CreateOnDiskHierarchy(tmp_path, dir_struct)
osutils.WriteFile(tmp_path / "cog" / ".citc" / "workspace_id", "user/1")
# pylint: disable=attribute-defined-outside-init
self.repo_root = tmp_path / "repo"
self.gclient_root = tmp_path / "gclient"
self.nocheckout_root = tmp_path / "nothing"
self.cog_root = tmp_path / "cog" / "chrome-internal"
self.rc_mock = run_mock
self.monkeypatch = monkeypatch
def testRepoRoot(self) -> None:
"""Test when we are inside a repo checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.repo_root)
self.assertEqual(
path_util.find_config_dir_for_checkout(),
self.repo_root / path_util.GENERAL_CONFIG_DIR,
)
def testGclientRoot(self) -> None:
"""Test when we are inside a gclient checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.gclient_root)
self.assertEqual(
path_util.find_config_dir_for_checkout(),
self.gclient_root / "src" / "build" / path_util.GENERAL_CONFIG_DIR,
)
def testCitcRoot(self) -> None:
"""Test when we are inside a Cog checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.cog_root)
self.monkeypatch.setattr(
cros_build_lib, "AssertOutsideChroot", lambda: True
)
self.assertEqual(
path_util.find_config_dir_for_checkout(),
Path("~/.config/chromite/cog/user/1").expanduser(),
)
def testTempdir(self) -> None:
"""Test when we are not in any checkout."""
self.monkeypatch.setattr(constants, "SOURCE_ROOT", self.nocheckout_root)
self.assertEqual(
path_util.find_config_dir_for_checkout(),
Path("~/.config/chromite/unknown_checkout").expanduser(),
)
class GetLogDirTest(cros_test_lib.MockTestCase):
"""get_log_dir tests."""
@mock.patch("chromite.lib.cros_build_lib.IsInsideChroot", return_value=True)
def test_inside(self, _) -> None:
"""Test inside the SDK."""
assert path_util.get_log_dir() == Path("/var/log")
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def test_outside(self, _) -> None:
"""Test outside the SDK."""
log_dir = path_util.get_log_dir()
expected_suffix = constants.DEFAULT_OUT_DIR / "sdk" / "logs"
assert str(log_dir).endswith(str(expected_suffix))
class TestPathResolver(cros_test_lib.MockTempDirTestCase):
"""Tests of ChrootPathResolver class."""
def setUp(self) -> None:
self.PatchObject(constants, "SOURCE_ROOT", new=FAKE_SOURCE_PATH)
self.PatchObject(constants, "DEFAULT_OUT_PATH", new=FAKE_OUT_PATH)
self.PatchObject(
path_util, "GetCacheDir", return_value="/path/to/cache"
)
self.PatchObject(
path_util.ChrootPathResolver,
"_GetCachePath",
return_value="/path/to/cache",
)
self.PatchObject(
git,
"FindRepoDir",
return_value=os.path.join(FAKE_REPO_PATH, ".fake_repo"),
)
self.chroot_path = None
self.out_path = None
def FakeCwd(self, base_path):
return os.path.join(base_path, "somewhere/in/there")
def SetChrootPath(
self, source_path, chroot_path=None, out_path=None
) -> None:
"""Set and fake the chroot path."""
self.chroot_path = chroot_path or os.path.join(
source_path, constants.DEFAULT_CHROOT_DIR
)
self.out_path = out_path or (source_path / constants.DEFAULT_OUT_DIR)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testSourcePathInChrootInbound(self, _) -> None:
"""Test regular behavior if chroot_path is inside source_path."""
self.SetChrootPath(constants.SOURCE_ROOT)
resolver = path_util.ChrootPathResolver(
source_from_path_repo=False,
chroot_path=self.chroot_path,
out_path=self.out_path,
)
self.assertEqual(
os.path.join(self.chroot_path, "some/file"),
resolver.FromChroot(os.path.join("/some/file")),
)
self.assertEqual(
os.path.join("/other/file"),
resolver.ToChroot(os.path.join(self.chroot_path, "other/file")),
)
@mock.patch("chromite.lib.cros_build_lib.IsInsideChroot", return_value=True)
def testInsideChroot(self, _) -> None:
"""Tests {To,From}Chroot() call from inside the chroot."""
self.SetChrootPath(constants.SOURCE_ROOT)
resolver = path_util.ChrootPathResolver()
self.assertEqual(
os.path.realpath("some/path"), resolver.ToChroot("some/path")
)
self.assertEqual(
os.path.realpath("/some/path"), resolver.ToChroot("/some/path")
)
self.assertEqual(
os.path.realpath("/tmp/path"), resolver.ToChroot("/tmp/path")
)
self.assertEqual(
os.path.realpath("some/path"), resolver.FromChroot("some/path")
)
self.assertEqual(
os.path.realpath("/some/path"), resolver.FromChroot("/some/path")
)
self.assertEqual(
os.path.realpath("/tmp/path"), resolver.FromChroot("/tmp/path")
)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testOutsideChrootInbound(self, _) -> None:
"""Tests ToChroot() calls from outside the chroot."""
for source_path, source_from_path_repo in itertools.product(
(None, CUSTOM_SOURCE_PATH), (False, True)
):
if source_from_path_repo:
actual_source_path = FAKE_REPO_PATH
else:
actual_source_path = source_path or constants.SOURCE_ROOT
fake_cwd = self.FakeCwd(actual_source_path)
self.PatchObject(os, "getcwd", return_value=fake_cwd)
self.SetChrootPath(actual_source_path)
resolver = path_util.ChrootPathResolver(
source_path=source_path,
source_from_path_repo=source_from_path_repo,
)
source_rel_cwd = os.path.relpath(fake_cwd, actual_source_path)
# Case: path inside the chroot space.
self.assertEqual(
"/some/path",
resolver.ToChroot(os.path.join(self.chroot_path, "some/path")),
)
# Case: the cache directory.
self.assertEqual(
str(constants.CHROOT_CACHE_ROOT),
resolver.ToChroot(path_util.GetCacheDir()),
)
# Case: path inside the cache directory.
self.assertEqual(
os.path.join(constants.CHROOT_CACHE_ROOT, "some/path"),
resolver.ToChroot(
os.path.join(path_util.GetCacheDir(), "some/path")
),
)
# Case: absolute path inside the source tree.
if source_from_path_repo:
self.assertEqual(
os.path.join(constants.CHROOT_SOURCE_ROOT, "some/path"),
resolver.ToChroot(
os.path.join(FAKE_REPO_PATH, "some/path")
),
)
else:
self.assertEqual(
os.path.join(constants.CHROOT_SOURCE_ROOT, "some/path"),
resolver.ToChroot(
os.path.join(actual_source_path, "some/path")
),
)
# Case: relative path inside the source tree.
if source_from_path_repo:
self.assertEqual(
os.path.join(
constants.CHROOT_SOURCE_ROOT,
source_rel_cwd,
"some/path",
),
resolver.ToChroot("some/path"),
)
else:
self.assertEqual(
os.path.join(
constants.CHROOT_SOURCE_ROOT,
source_rel_cwd,
"some/path",
),
resolver.ToChroot("some/path"),
)
# Case: unreachable, path with improper source root prefix.
with self.assertRaises(ValueError):
resolver.ToChroot(
os.path.join(actual_source_path + "-foo", "some/path")
)
# Case: unreachable (random).
with self.assertRaises(ValueError):
resolver.ToChroot("/some/path")
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testOutsideCustomChrootInbound(self, _) -> None:
"""Tests ToChroot() calls from outside a custom chroot."""
self.SetChrootPath(
constants.SOURCE_ROOT, CUSTOM_CHROOT_PATH, out_path=CUSTOM_OUT_PATH
)
resolver = path_util.ChrootPathResolver(
chroot_path=CUSTOM_CHROOT_PATH, out_path=CUSTOM_OUT_PATH
)
# Case: path inside the chroot space.
self.assertEqual(
"/some/path",
resolver.ToChroot(os.path.join(self.chroot_path, "some/path")),
)
# Case: path from source root
self.assertEqual(
os.path.join(constants.CHROOT_SOURCE_ROOT, "some/path"),
resolver.ToChroot(os.path.join(constants.SOURCE_ROOT, "some/path")),
)
# Case: not mapped to chroot
with self.assertRaises(ValueError):
resolver.ToChroot("/random/file")
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testOutsideChrootOutbound(self, _) -> None:
"""Tests FromChroot() calls from outside the chroot."""
self.PatchObject(
os, "getcwd", return_value=self.FakeCwd(FAKE_SOURCE_PATH)
)
self.SetChrootPath(constants.SOURCE_ROOT)
resolver = path_util.ChrootPathResolver()
# Case: source root path.
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "some/path"),
resolver.FromChroot(
os.path.join(constants.CHROOT_SOURCE_ROOT, "some/path")
),
)
# Case: cyclic source/chroot sub-path elimination.
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "some/path"),
resolver.FromChroot(
os.path.join(
constants.CHROOT_SOURCE_ROOT,
constants.DEFAULT_CHROOT_DIR,
constants.CHROOT_SOURCE_ROOT.relative_to("/"),
constants.DEFAULT_CHROOT_DIR,
constants.CHROOT_SOURCE_ROOT.relative_to("/"),
"some/path",
)
),
)
# Case: the cache directory.
self.assertEqual(
path_util.GetCacheDir(),
resolver.FromChroot(constants.CHROOT_CACHE_ROOT),
)
# Case: path inside the cache directory.
self.assertEqual(
os.path.join(path_util.GetCacheDir(), "some/path"),
resolver.FromChroot(
os.path.join(constants.CHROOT_CACHE_ROOT, "some/path")
),
)
# Case: non-rooted chroot paths.
self.assertEqual(
os.path.join(self.chroot_path, "some/path"),
resolver.FromChroot("/some/path"),
)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testOutsideCustomChrootOutbound(self, _) -> None:
"""Tests FromChroot() calls from outside the chroot."""
self.PatchObject(
os, "getcwd", return_value=self.FakeCwd(FAKE_SOURCE_PATH)
)
self.SetChrootPath(
constants.SOURCE_ROOT,
chroot_path=CUSTOM_CHROOT_PATH,
out_path=CUSTOM_OUT_PATH,
)
resolver = path_util.ChrootPathResolver(
chroot_path=CUSTOM_CHROOT_PATH, out_path=CUSTOM_OUT_PATH
)
# Case: source root path.
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "some/path"),
resolver.FromChroot(
os.path.join(constants.CHROOT_SOURCE_ROOT, "some/path")
),
)
# Case: cyclic source/chroot sub-path
self.assertEqual(
os.path.join(
constants.SOURCE_ROOT,
constants.DEFAULT_CHROOT_DIR,
constants.CHROOT_SOURCE_ROOT.relative_to("/"),
constants.DEFAULT_CHROOT_DIR,
constants.CHROOT_SOURCE_ROOT.relative_to("/"),
"some/path",
),
resolver.FromChroot(
os.path.join(
constants.CHROOT_SOURCE_ROOT,
constants.DEFAULT_CHROOT_DIR,
constants.CHROOT_SOURCE_ROOT.relative_to("/"),
constants.DEFAULT_CHROOT_DIR,
constants.CHROOT_SOURCE_ROOT.relative_to("/"),
"some/path",
)
),
)
# Case: path inside the cache directory.
self.assertEqual(
os.path.join(path_util.GetCacheDir(), "some/path"),
resolver.FromChroot(
os.path.join(constants.CHROOT_CACHE_ROOT, "some/path")
),
)
# Case: non-rooted chroot paths.
self.assertEqual(
os.path.join(self.chroot_path, "some/path"),
resolver.FromChroot("/some/path"),
)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testCurrentDir(self, _) -> None:
"""Tests chroot translation with the current dir."""
# Current directory is "out" directory.
self.SetChrootPath(
constants.SOURCE_ROOT, CUSTOM_CHROOT_PATH, out_path=Path(".")
)
resolver = path_util.ChrootPathResolver(
chroot_path=self.chroot_path,
out_path=self.out_path,
)
self.assertEqual(
os.path.realpath("foo"),
resolver.FromChroot(constants.CHROOT_OUT_ROOT / "foo"),
)
self.assertEqual(
str(constants.CHROOT_OUT_ROOT / "foo"),
resolver.ToChroot("foo"),
)
# Current directory is "chroot" directory.
self.SetChrootPath(constants.SOURCE_ROOT, ".", out_path=CUSTOM_OUT_PATH)
resolver = path_util.ChrootPathResolver(
chroot_path=self.chroot_path,
out_path=self.out_path,
)
self.assertEqual(
os.path.realpath("."),
resolver.FromChroot("/"),
)
self.assertEqual(
"/",
resolver.ToChroot("."),
)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testOutsideChrootOutdir(self, _) -> None:
"""Tests {To,From}Chroot() call from outside chroot with an out_dir."""
self.SetChrootPath(constants.SOURCE_ROOT)
resolver = path_util.ChrootPathResolver()
self.assertEqual(
"/build/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/build/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/build/foo"),
resolver.FromChroot("/build/foo"),
)
self.assertEqual(
"/home/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/home/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/home/foo"),
resolver.FromChroot("/home/foo"),
)
self.assertEqual(
"/tmp/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/tmp/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/tmp/foo"),
resolver.FromChroot("/tmp/foo"),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/tmp"),
resolver.FromChroot("/tmp"),
)
self.assertEqual(
"/tmp",
resolver.ToChroot(os.path.join(constants.SOURCE_ROOT, "out/tmp")),
)
self.assertEqual(
"/run/lock/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/sdk/run/lock/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/sdk/run/lock/foo"),
resolver.FromChroot("/run/lock/foo"),
)
self.assertEqual(
"/var/cache/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/sdk/cache/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/sdk/cache/foo"),
resolver.FromChroot("/var/cache/foo"),
)
self.assertEqual(
"/var/log/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/sdk/logs/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/sdk/logs/foo"),
resolver.FromChroot("/var/log/foo"),
)
self.assertEqual(
"/var/tmp/foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/sdk/tmp/foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/sdk/tmp/foo"),
resolver.FromChroot("/var/tmp/foo"),
)
self.assertEqual(
"/usr/local/bin/emerge-foo",
resolver.ToChroot(
os.path.join(constants.SOURCE_ROOT, "out/sdk/bin/emerge-foo")
),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/sdk/bin/emerge-foo"),
resolver.FromChroot("/usr/local/bin/emerge-foo"),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out/foo"),
resolver.FromChroot(os.path.join(constants.CHROOT_OUT_ROOT, "foo")),
)
self.assertEqual(
os.path.join(constants.CHROOT_OUT_ROOT, "foo"),
resolver.ToChroot(os.path.join(constants.SOURCE_ROOT, "out/foo")),
)
self.assertEqual(
os.path.join(constants.SOURCE_ROOT, "out"),
resolver.FromChroot(constants.CHROOT_OUT_ROOT),
)
self.assertEqual(
str(constants.CHROOT_OUT_ROOT),
resolver.ToChroot(os.path.join(constants.SOURCE_ROOT, "out")),
)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testBySourcePath(self, _) -> None:
"""Provide only source_path=, and derive chroot/ and out/."""
source_path = CUSTOM_SOURCE_PATH
resolver = path_util.ChrootPathResolver(
source_path=source_path, source_from_path_repo=False
)
self.assertEqual(
os.path.join(source_path, constants.DEFAULT_CHROOT_DIR),
resolver.FromChroot("/"),
)
self.assertEqual(
"/",
resolver.ToChroot(
os.path.join(source_path, constants.DEFAULT_CHROOT_DIR)
),
)
self.assertEqual(
str(source_path / constants.DEFAULT_OUT_DIR),
resolver.FromChroot(constants.CHROOT_OUT_ROOT),
)
self.assertEqual(
str(constants.CHROOT_OUT_ROOT),
resolver.ToChroot(source_path / constants.DEFAULT_OUT_DIR),
)
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testSymlinkedPath(self, _) -> None:
"""Resolve a symlinked path."""
original_realpath = os.path.realpath
self.PatchObject(
os.path,
"realpath",
side_effect=lambda path: "/usr/wrongpath/foo"
if path == "/bin/foo"
else original_realpath(path),
)
# Double check the mock.
self.assertEqual("/usr/wrongpath/foo", os.path.realpath("/bin/foo"))
self.SetChrootPath(
None,
chroot_path=self.tempdir / "chroot",
out_path=self.tempdir / "out",
)
resolver = path_util.ChrootPathResolver(
chroot_path=self.chroot_path, out_path=self.out_path
)
source = Path(self.chroot_path) / "usr" / "bin" / "foo"
target = Path(self.chroot_path) / "bin" / "foo"
osutils.Touch(source, makedirs=True)
osutils.SafeSymlink("usr/bin", Path(self.chroot_path) / "bin")
# On inbound, translate symlinks on the host side, before chroot
# translation.
self.assertEqual("/usr/bin/foo", resolver.ToChroot(target))
# On outbound, don't resolve symlinks at all. See b/308121733#comment5.
self.assertEqual(str(target), resolver.FromChroot("/bin/foo"))
@mock.patch(
"chromite.lib.cros_build_lib.IsInsideChroot", return_value=False
)
def testNonDefaultChrootPathInsideSourcePath(self, _) -> None:
"""Test custom chroot behavior if chroot_path is inside source_path."""
source_path = constants.SOURCE_ROOT
self.SetChrootPath(
source_path,
chroot_path=os.path.join(source_path, "my-special-custom-chroot"),
)
resolver = path_util.ChrootPathResolver(
chroot_path=self.chroot_path,
out_path=self.out_path,
)
from_chroot = resolver.FromChroot(os.path.join("/some/file"))
self.assertIn("/my-special-custom-chroot/", from_chroot)
self.assertEqual(
os.path.join(self.chroot_path, "some/file"),
from_chroot,
)
self.assertEqual(
os.path.join("/other/file"),
resolver.ToChroot(os.path.join(self.chroot_path, "other/file")),
)
def test_normalize_paths_to_source_root_collapsing_sub_paths() -> None:
"""Test normalize removes sub paths."""
actual_paths = path_util.normalize_paths_to_source_root(
[
os.path.join(constants.SOURCE_ROOT, "foo"),
os.path.join(constants.SOURCE_ROOT, "ab", "cd"),
os.path.join(constants.SOURCE_ROOT, "foo", "bar"),
]
)
expected_paths = {"ab/cd", "foo"}
assert set(actual_paths) == expected_paths
actual_paths = path_util.normalize_paths_to_source_root(
[
os.path.join(constants.SOURCE_ROOT, "foo", "bar"),
os.path.join(constants.SOURCE_ROOT, "ab", "cd"),
os.path.join(constants.SOURCE_ROOT, "foo", "bar", ".."),
os.path.join(constants.SOURCE_ROOT, "ab", "cde"),
]
)
expected_paths = {"ab/cd", "ab/cde", "foo"}
assert set(actual_paths) == expected_paths
def test_normalize_paths_to_source_root_formatting_directory_paths(
tmp_path,
) -> None:
"""Test normalize correctly handles /path/to/file and /path/to/dir/."""
foo_dir = tmp_path / "foo"
foo_dir.mkdir()
bar_baz_dir = tmp_path / "bar" / "baz"
bar_baz_dir.mkdir(parents=True)
ab_dir = tmp_path / "ab"
ab_dir.mkdir()
ab_cd_file = ab_dir / "cd"
osutils.WriteFile(ab_cd_file, "alphabet")
expected_paths = [
str(ab_cd_file.relative_to(tmp_path)),
str(bar_baz_dir.relative_to(tmp_path)),
str(foo_dir.relative_to(tmp_path)),
]
actual_paths = path_util.normalize_paths_to_source_root(
[
str(foo_dir) + "/",
str(ab_cd_file),
str(bar_baz_dir) + "/",
],
source_root=tmp_path,
)
assert actual_paths == expected_paths
def test_expand_directories_in_git(tmp_path) -> None:
"""Test ExpandDirectories when given a dir in a git repo."""
files_in_dir = [Path("foo.txt"), Path("bar.txt")]
with mock.patch("chromite.lib.git.FindGitTopLevel", return_value=tmp_path):
with mock.patch(
"chromite.lib.git.LsFiles", return_value=files_in_dir
) as ls_files:
result = set(path_util.ExpandDirectories([tmp_path]))
assert result == set(files_in_dir)
ls_files.assert_called_once_with(files=[tmp_path], untracked=True)
def test_expand_directories_not_git(tmp_path) -> None:
"""Test ExpandDirectories when given a dir outside a git repo."""
subdir = tmp_path / "subdir"
subdir.mkdir()
files_in_dir = [tmp_path / "foo.txt", subdir / "bar.txt"]
for f in files_in_dir:
osutils.Touch(f)
with mock.patch("chromite.lib.git.FindGitTopLevel", return_value=None):
result = set(path_util.ExpandDirectories([tmp_path]))
assert result == set(files_in_dir)
def test_expand_directories_file(tmp_path) -> None:
"""Test ExpandDirectories when given a regular file."""
file_path = tmp_path / "foo.txt"
osutils.Touch(file_path)
result = list(path_util.ExpandDirectories([file_path]))
assert result == [file_path]
def test_get_citc_chroot_path(monkeypatch) -> None:
monkeypatch.setattr(path_util, "read_workspace_id", lambda: "user/1")
monkeypatch.setattr(
path_util,
"DetermineCheckout",
lambda: path_util.CheckoutInfo(
path_util.CheckoutType.CITC, FAKE_SOURCE_PATH, ""
),
)
assert (
path_util.get_citc_chroot_path()
== xdg_util.STATE_HOME
/ "cros"
/ "cog"
/ "workspaces"
/ "user"
/ "1"
/ constants.DEFAULT_CHROOT_DIR
)
def test_get_citc_out_path(monkeypatch) -> None:
monkeypatch.setattr(path_util, "read_workspace_id", lambda: "user/1")
monkeypatch.setattr(
path_util,
"DetermineCheckout",
lambda: path_util.CheckoutInfo(
path_util.CheckoutType.CITC, FAKE_SOURCE_PATH, ""
),
)
assert (
path_util.get_citc_out_path()
== xdg_util.STATE_HOME
/ "cros"
/ "cog"
/ "workspaces"
/ "user"
/ "1"
/ constants.DEFAULT_OUT_DIR
)
def test_get_citc_path_raises_error_inside_chroot(monkeypatch) -> None:
monkeypatch.delenv("CROS_COG_WORKSPACE_ID", raising=False)
monkeypatch.setattr(cros_build_lib, "AssertOutsideChroot", lambda: False)
with pytest.raises(AssertionError):
path_util.get_citc_workspace_path()
def test_get_citc_path_from_env_var(monkeypatch) -> None:
monkeypatch.setattr(cros_build_lib, "AssertOutsideChroot", lambda: False)
monkeypatch.setenv("CROS_COG_WORKSPACE_ID", "user/1")
assert path_util.read_workspace_id() == "user/1"
def test_get_citc_path_raises_error_repo_checkout(monkeypatch) -> None:
monkeypatch.setattr(cros_build_lib, "AssertOutsideChroot", lambda: True)
monkeypatch.setattr(
path_util,
"DetermineCheckout",
lambda: path_util.CheckoutInfo(
path_util.CheckoutType.REPO, FAKE_SOURCE_PATH, None
),
)
with pytest.raises(AssertionError):
path_util.get_citc_workspace_path()