blob: 42f86e11bebf48403372ab3c6800211063491322 [file] [log] [blame]
#!/usr/bin/env vpython3
# Copyright 2023 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.
import unittest
import test_env # for sys.path manipulation
from recipe_engine.config_types import Path, ResolvedBasePath, CheckoutBasePath
from recipe_engine.config_types import ResetGlobalVariableAssignments
class TestPathsPreGlobalInit(unittest.TestCase):
"""Test case for config_types.Path prior to recipe_engine/path module
initialization.
"""
def tearDown(self) -> None:
CheckoutBasePath._resolved = None
return super().tearDown()
def test_path_construction_resolved(self):
# Doesn't raise any errors
cachePath = Path(ResolvedBasePath('[CACHE]'))
assert isinstance(cachePath.base, ResolvedBasePath)
self.assertEqual(cachePath.base.resolved, '[CACHE]')
self.assertEqual(cachePath.pieces, ())
def test_path_construction_checkout(self):
checkoutPath = Path(CheckoutBasePath())
assert isinstance(checkoutPath.base, CheckoutBasePath)
def test_path_construction_error_base(self):
with self.assertRaisesRegex(ValueError, 'First argument'):
Path('yo') # type: ignore
def test_path_construction_error_pieces(self):
with self.assertRaisesRegex(ValueError, 'must only be `str`'):
Path(ResolvedBasePath('[CACHE]'), 100) # type: ignore
def test_path_construction_error_backslash(self):
with self.assertRaisesRegex(ValueError, 'contain backslash'):
Path(ResolvedBasePath('[CACHE]'), 'bad\\path')
def test_path_construction_resolved_pieces(self):
a = Path(ResolvedBasePath('[CACHE]'), 'hello', 'world')
self.assertEqual(a.pieces, ('hello', 'world'))
b = Path(ResolvedBasePath('[CACHE]'), 'hello/world')
self.assertEqual(b.pieces, ('hello', 'world'))
self.assertEqual(a, b)
def test_path_construction_checkout_pieces(self):
a = Path(CheckoutBasePath(), 'hello', 'world')
self.assertEqual(a.pieces, ('hello', 'world'))
b = Path(CheckoutBasePath(), 'hello/world')
self.assertEqual(b.pieces, ('hello', 'world'))
# Note that these can be compared when they are both based on
# CheckoutBasePath.
self.assertEqual(a, b)
def test_path_inequality_resolved(self):
p = Path(ResolvedBasePath('[CACHE]'))
self.assertLess(p / 'a', p / 'b')
self.assertLess(p / 'a', p / 'b' / 'c')
self.assertLess(p / 'a' / 'c', p / 'b' / 'c')
def test_path_inequality_checkout(self):
p = Path(CheckoutBasePath())
self.assertLess(p / 'a', p / 'b')
self.assertLess(p / 'a', p / 'b' / 'c')
self.assertLess(p / 'a' / 'c', p / 'b' / 'c')
def test_path_inequality_mismatch(self):
a = Path(CheckoutBasePath())
b = Path(ResolvedBasePath('[CACHE]'))
with self.assertRaisesRegex(ValueError, 'before checkout_dir is set'):
self.assertLess(a, b)
def test_path_equality_mismatch(self):
a = Path(CheckoutBasePath())
b = Path(ResolvedBasePath('[CACHE]'))
with self.assertRaisesRegex(ValueError, 'before checkout_dir is set'):
self.assertEqual(a, b)
def test_path_dots_removal(self):
p = Path(ResolvedBasePath('[CACHE]'))
self.assertEqual(p / 'hello', p / '.' / 'hello' / '.' / '.')
self.assertEqual(p, p / '.')
self.assertEqual(
p / 'some/hello',
# Note that no one would ever construct a path with all these styles,
# however it's important that all the various joinery/embedded slash
# styles result in the same Path because recipe code passes Paths around
# and joins to them in multiple methods, so while we would never see
# such a construction all in one line like this, it's possible that
# a Path is logically constructed in multiple places in this fashion.
(p / 'some/path/to/stuff' / '../..').join('etc', '..////.', '..',
'hello'))
def test_path_dots_removal_error(self):
p = Path(ResolvedBasePath('[CACHE]'))
with self.assertRaisesRegex(ValueError, 'going above the base'):
print(repr(p / '..'))
with self.assertRaisesRegex(ValueError, 'going above the base'):
print(repr(p / 'something' / '..///./..'))
def test_path_joinpath(self):
"""Tests for Path.joinpath()."""
base_path = Path(ResolvedBasePath('[START_DIR]'))
reference_path = base_path.joinpath('foo').joinpath('bar')
self.assertEqual(base_path / 'foo' / 'bar', reference_path)
def test_is_parent_of(self):
p = Path(ResolvedBasePath('[CACHE]'))
self.assertTrue(p.is_parent_of(p / 'a'))
self.assertTrue(p.is_parent_of(p / 'a' / 'b' / 'c'))
self.assertTrue((p / 'a').is_parent_of(p / 'a' / 'b' / 'c'))
def test_is_parent_of_mismatch(self):
p1 = Path(ResolvedBasePath('[CACHE]'))
p2 = Path(ResolvedBasePath('[CLEANUP]'))
self.assertFalse(p1.is_parent_of(p2))
self.assertFalse(p2.is_parent_of(p1))
def test_is_parent_of_checkout(self):
p1 = Path(CheckoutBasePath(), 'some')
p2 = Path(ResolvedBasePath('[CACHE]'), 'builder', 'src', 'some', 'thing')
with self.assertRaisesRegex(ValueError, 'checkout_dir is unset'):
p1.is_parent_of(p2)
with self.assertRaisesRegex(ValueError, 'checkout_dir is unset'):
p2.is_parent_of(p1)
CheckoutBasePath._resolved = Path(
ResolvedBasePath('[CACHE]'), 'builder', 'src')
self.assertTrue(p1.is_parent_of(p2))
self.assertFalse(p2.is_parent_of(p1))
def test_is_parent_of_checkout_mismatch(self):
p1 = Path(CheckoutBasePath(), 'some')
p2 = Path(ResolvedBasePath('[CLEANUP]'), 'unrelated')
CheckoutBasePath._resolved = Path(
ResolvedBasePath('[CACHE]'), 'builder', 'src')
self.assertFalse(p1.is_parent_of(p2))
self.assertFalse(p2.is_parent_of(p1))
def test_is_parent_of_sanity(self):
p = Path(ResolvedBasePath('[CLEANUP]'))
self.assertFalse((p / 'a').is_parent_of(p / 'ab'))
class TestPathsPostGlobalInit(unittest.TestCase):
"""Test case for config_types.Path."""
def tearDown(self):
ResetGlobalVariableAssignments()
return super().tearDown()
if __name__ == '__main__':
test_env.main()