blob: aac30ae5b729c75bc52c6a50306e8ca9bf09fdc3 [file] [log] [blame]
# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from recipe_engine import recipe_test_api
from recipe_engine.config_types import CheckoutBasePath, Path, ResolvedBasePath
# Avoid circular import.
if TYPE_CHECKING: # pragma: no cover
from .api import NamedBasePathsType
@dataclass(frozen=True)
class UnvalidatedPath:
base: str
pieces: tuple[str, ...]
def join(self, *pieces: str) -> UnvalidatedPath:
return UnvalidatedPath(self.base, self.pieces + pieces)
class PathTestApi(recipe_test_api.RecipeTestApi):
def exists(self, *paths: Path):
"""This is an alias for `files_exist`."""
return self.files_exist(*paths)
@recipe_test_api.mod_test_data
@staticmethod
def files_exist(*paths: Path | UnvalidatedPath):
"""This mocks the path module to believe that the given `paths` exist as
FILES prior to the start of the recipe.
To mock the existence of paths which are generated DURING the execution of
the recipe, use recipe_engine/path.mock_* functions.
This sets the type of paths to be 'FILE'. If you want to mock the existence
of a directory, use dirs_exist().
"""
assert all(isinstance(p, (Path, UnvalidatedPath)) for p in paths)
return list(paths)
@recipe_test_api.mod_test_data
@staticmethod
def dirs_exist(*paths: Path | UnvalidatedPath):
"""This mocks the path module to believe that the given `paths` exist as
DIRECTORIES prior to the start of the recipe.
To mock the existence of paths which are generated DURING the execution of
the recipe, use recipe_engine/path.mock_* functions.
This sets the type of paths to be 'DIRECTORY'. If you want to mock the
existence of a file, use exists().
"""
assert all(isinstance(p, (Path, UnvalidatedPath)) for p in paths)
return list(paths)
def __getitem__(self, name: NamedBasePathsType) -> Path:
"""This gets a Path based on `name` for passing to the `exists`,
`files_exist` or `dirs_exist` methods on this test API.
DEPRECATED: Use the following @properties on this module instead:
* start_dir
* tmp_base_dir
* cache_dir
* cleanup_dir
* home_dir
* checkout_dir (but use of checkout_dir is generally discouraged - just
pass the Paths around instead of using this global variable).
"""
match name:
case 'cache':
return self.cache_dir
case 'cleanup':
return self.cleanup_dir
case 'home':
return self.home_dir
case 'start_dir':
return self.start_dir
case 'tmp_base':
return self.tmp_base_dir
case 'checkout':
return self.checkout_dir
# Avoid circular import.
from .api import PathApi # pragma: no cover
if name not in PathApi.NamedBasePaths: # pragma: no cover
raise ValueError(
f'Unknown base path {name!r} - allowed names are {PathApi.NamedBasePaths!r}'
)
@property
def start_dir(self) -> Path:
return Path(ResolvedBasePath('[START_DIR]'))
@property
def tmp_base_dir(self) -> Path:
return Path(ResolvedBasePath('[TMP_BASE]'))
@property
def cache_dir(self) -> Path:
return Path(ResolvedBasePath('[CACHE]'))
@property
def cleanup_dir(self) -> Path:
return Path(ResolvedBasePath('[CLEANUP]'))
@property
def home_dir(self) -> Path:
return Path(ResolvedBasePath('[HOME]'))
@property
def checkout_dir(self) -> Path:
return Path(CheckoutBasePath())
def cast_to_path(self, strpath: str) -> UnvalidatedPath:
"""Allows an absolute path to be used to mock the existence.
This path will be validated to be absolute by the path module when it loads
the mocked data. This will validate and split `strpath` according to the
mocked OS (e.g. using '\\' and '/' and parsing Windows drive on Windows,
using '/' on *nix).
These paths are ONLY good for .exists, .files_exist and .dirs_exist on this
test API.
"""
return UnvalidatedPath(strpath, ())