# Copyright 2018 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import tempfile import unittest from typing import Any, List, Optional from python.runfiles import runfiles class RunfilesTest(unittest.TestCase): """Unit tests for `rules_python.python.runfiles.Runfiles`.""" def testRlocationArgumentValidation(self) -> None: r = runfiles.Create({"RUNFILES_DIR": "whatever"}) assert r is not None # mypy doesn't understand the unittest api. self.assertRaises(ValueError, lambda: r.Rlocation(None)) # type: ignore self.assertRaises(ValueError, lambda: r.Rlocation("")) self.assertRaises(TypeError, lambda: r.Rlocation(1)) # type: ignore self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("../foo") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("foo/..") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("foo/../bar") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("./foo") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("foo/.") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("foo/./bar") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("//foobar") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("foo//") ) self.assertRaisesRegex( ValueError, "is not normalized", lambda: r.Rlocation("foo//bar") ) self.assertRaisesRegex( ValueError, "is absolute without a drive letter", lambda: r.Rlocation("\\foo"), ) def testCreatesManifestBasedRunfiles(self) -> None: with _MockFile(contents=["a/b c/d"]) as mf: r = runfiles.Create( { "RUNFILES_MANIFEST_FILE": mf.Path(), "RUNFILES_DIR": "ignored when RUNFILES_MANIFEST_FILE has a value", "TEST_SRCDIR": "always ignored", } ) assert r is not None # mypy doesn't understand the unittest api. self.assertEqual(r.Rlocation("a/b"), "c/d") self.assertIsNone(r.Rlocation("foo")) def testManifestBasedRunfilesEnvVars(self) -> None: with _MockFile(name="MANIFEST") as mf: r = runfiles.Create( { "RUNFILES_MANIFEST_FILE": mf.Path(), "TEST_SRCDIR": "always ignored", } ) assert r is not None # mypy doesn't understand the unittest api. self.assertDictEqual( r.EnvVars(), { "RUNFILES_MANIFEST_FILE": mf.Path(), "RUNFILES_DIR": mf.Path()[: -len("/MANIFEST")], "JAVA_RUNFILES": mf.Path()[: -len("/MANIFEST")], }, ) with _MockFile(name="foo.runfiles_manifest") as mf: r = runfiles.Create( { "RUNFILES_MANIFEST_FILE": mf.Path(), "TEST_SRCDIR": "always ignored", } ) assert r is not None # mypy doesn't understand the unittest api. self.assertDictEqual( r.EnvVars(), { "RUNFILES_MANIFEST_FILE": mf.Path(), "RUNFILES_DIR": ( mf.Path()[: -len("foo.runfiles_manifest")] + "foo.runfiles" ), "JAVA_RUNFILES": ( mf.Path()[: -len("foo.runfiles_manifest")] + "foo.runfiles" ), }, ) with _MockFile(name="x_manifest") as mf: r = runfiles.Create( { "RUNFILES_MANIFEST_FILE": mf.Path(), "TEST_SRCDIR": "always ignored", } ) assert r is not None # mypy doesn't understand the unittest api. self.assertDictEqual( r.EnvVars(), { "RUNFILES_MANIFEST_FILE": mf.Path(), "RUNFILES_DIR": "", "JAVA_RUNFILES": "", }, ) def testCreatesDirectoryBasedRunfiles(self) -> None: r = runfiles.Create( { "RUNFILES_DIR": "runfiles/dir", "TEST_SRCDIR": "always ignored", } ) assert r is not None # mypy doesn't understand the unittest api. self.assertEqual(r.Rlocation("a/b"), "runfiles/dir/a/b") self.assertEqual(r.Rlocation("foo"), "runfiles/dir/foo") def testDirectoryBasedRunfilesEnvVars(self) -> None: r = runfiles.Create( { "RUNFILES_DIR": "runfiles/dir", "TEST_SRCDIR": "always ignored", } ) assert r is not None # mypy doesn't understand the unittest api. self.assertDictEqual( r.EnvVars(), { "RUNFILES_DIR": "runfiles/dir", "JAVA_RUNFILES": "runfiles/dir", }, ) def testFailsToCreateManifestBasedBecauseManifestDoesNotExist(self) -> None: def _Run(): runfiles.Create({"RUNFILES_MANIFEST_FILE": "non-existing path"}) self.assertRaisesRegex(IOError, "non-existing path", _Run) def testFailsToCreateAnyRunfilesBecauseEnvvarsAreNotDefined(self) -> None: with _MockFile(contents=["a b"]) as mf: runfiles.Create( { "RUNFILES_MANIFEST_FILE": mf.Path(), "RUNFILES_DIR": "whatever", "TEST_SRCDIR": "always ignored", } ) runfiles.Create( { "RUNFILES_DIR": "whatever", "TEST_SRCDIR": "always ignored", } ) self.assertIsNone(runfiles.Create({"TEST_SRCDIR": "always ignored"})) self.assertIsNone(runfiles.Create({"FOO": "bar"})) def testManifestBasedRlocation(self) -> None: with _MockFile( contents=[ "Foo/runfile1", "Foo/runfile2 C:/Actual Path\\runfile2", "Foo/Bar/runfile3 D:\\the path\\run file 3.txt", "Foo/Bar/Dir E:\\Actual Path\\Directory", ] ) as mf: r = runfiles.CreateManifestBased(mf.Path()) self.assertEqual(r.Rlocation("Foo/runfile1"), "Foo/runfile1") self.assertEqual(r.Rlocation("Foo/runfile2"), "C:/Actual Path\\runfile2") self.assertEqual( r.Rlocation("Foo/Bar/runfile3"), "D:\\the path\\run file 3.txt" ) self.assertEqual( r.Rlocation("Foo/Bar/Dir/runfile4"), "E:\\Actual Path\\Directory/runfile4", ) self.assertEqual( r.Rlocation("Foo/Bar/Dir/Deeply/Nested/runfile4"), "E:\\Actual Path\\Directory/Deeply/Nested/runfile4", ) self.assertIsNone(r.Rlocation("unknown")) if RunfilesTest.IsWindows(): self.assertEqual(r.Rlocation("c:/foo"), "c:/foo") self.assertEqual(r.Rlocation("c:\\foo"), "c:\\foo") else: self.assertEqual(r.Rlocation("/foo"), "/foo") def testManifestBasedRlocationWithRepoMappingFromMain(self) -> None: with _MockFile( contents=[ ",config.json,config.json~1.2.3", ",my_module,_main", ",my_protobuf,protobuf~3.19.2", ",my_workspace,_main", "protobuf~3.19.2,config.json,config.json~1.2.3", "protobuf~3.19.2,protobuf,protobuf~3.19.2", ] ) as rm, _MockFile( contents=[ "_repo_mapping " + rm.Path(), "config.json /etc/config.json", "protobuf~3.19.2/foo/runfile C:/Actual Path\\protobuf\\runfile", "_main/bar/runfile /the/path/./to/other//other runfile.txt", "protobuf~3.19.2/bar/dir E:\\Actual Path\\Directory", ], ) as mf: r = runfiles.CreateManifestBased(mf.Path()) self.assertEqual( r.Rlocation("my_module/bar/runfile", ""), "/the/path/./to/other//other runfile.txt", ) self.assertEqual( r.Rlocation("my_workspace/bar/runfile", ""), "/the/path/./to/other//other runfile.txt", ) self.assertEqual( r.Rlocation("my_protobuf/foo/runfile", ""), "C:/Actual Path\\protobuf\\runfile", ) self.assertEqual( r.Rlocation("my_protobuf/bar/dir", ""), "E:\\Actual Path\\Directory" ) self.assertEqual( r.Rlocation("my_protobuf/bar/dir/file", ""), "E:\\Actual Path\\Directory/file", ) self.assertEqual( r.Rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", ""), "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", ) self.assertIsNone(r.Rlocation("protobuf/foo/runfile")) self.assertIsNone(r.Rlocation("protobuf/bar/dir")) self.assertIsNone(r.Rlocation("protobuf/bar/dir/file")) self.assertIsNone(r.Rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le")) self.assertEqual( r.Rlocation("_main/bar/runfile", ""), "/the/path/./to/other//other runfile.txt", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/foo/runfile", ""), "C:/Actual Path\\protobuf\\runfile", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir", ""), "E:\\Actual Path\\Directory" ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir/file", ""), "E:\\Actual Path\\Directory/file", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ""), "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", ) self.assertEqual(r.Rlocation("config.json", ""), "/etc/config.json") self.assertIsNone(r.Rlocation("_main", "")) self.assertIsNone(r.Rlocation("my_module", "")) self.assertIsNone(r.Rlocation("protobuf", "")) def testManifestBasedRlocationWithRepoMappingFromOtherRepo(self) -> None: with _MockFile( contents=[ ",config.json,config.json~1.2.3", ",my_module,_main", ",my_protobuf,protobuf~3.19.2", ",my_workspace,_main", "protobuf~3.19.2,config.json,config.json~1.2.3", "protobuf~3.19.2,protobuf,protobuf~3.19.2", ] ) as rm, _MockFile( contents=[ "_repo_mapping " + rm.Path(), "config.json /etc/config.json", "protobuf~3.19.2/foo/runfile C:/Actual Path\\protobuf\\runfile", "_main/bar/runfile /the/path/./to/other//other runfile.txt", "protobuf~3.19.2/bar/dir E:\\Actual Path\\Directory", ], ) as mf: r = runfiles.CreateManifestBased(mf.Path()) self.assertEqual( r.Rlocation("protobuf/foo/runfile", "protobuf~3.19.2"), "C:/Actual Path\\protobuf\\runfile", ) self.assertEqual( r.Rlocation("protobuf/bar/dir", "protobuf~3.19.2"), "E:\\Actual Path\\Directory", ) self.assertEqual( r.Rlocation("protobuf/bar/dir/file", "protobuf~3.19.2"), "E:\\Actual Path\\Directory/file", ) self.assertEqual( r.Rlocation( "protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" ), "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", ) self.assertIsNone(r.Rlocation("my_module/bar/runfile", "protobuf~3.19.2")) self.assertIsNone(r.Rlocation("my_protobuf/foo/runfile", "protobuf~3.19.2")) self.assertIsNone(r.Rlocation("my_protobuf/bar/dir", "protobuf~3.19.2")) self.assertIsNone( r.Rlocation("my_protobuf/bar/dir/file", "protobuf~3.19.2") ) self.assertIsNone( r.Rlocation( "my_protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" ) ) self.assertEqual( r.Rlocation("_main/bar/runfile", "protobuf~3.19.2"), "/the/path/./to/other//other runfile.txt", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/foo/runfile", "protobuf~3.19.2"), "C:/Actual Path\\protobuf\\runfile", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir", "protobuf~3.19.2"), "E:\\Actual Path\\Directory", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir/file", "protobuf~3.19.2"), "E:\\Actual Path\\Directory/file", ) self.assertEqual( r.Rlocation( "protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" ), "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", ) self.assertEqual( r.Rlocation("config.json", "protobuf~3.19.2"), "/etc/config.json" ) self.assertIsNone(r.Rlocation("_main", "protobuf~3.19.2")) self.assertIsNone(r.Rlocation("my_module", "protobuf~3.19.2")) self.assertIsNone(r.Rlocation("protobuf", "protobuf~3.19.2")) def testDirectoryBasedRlocation(self) -> None: # The _DirectoryBased strategy simply joins the runfiles directory and the # runfile's path on a "/". This strategy does not perform any normalization, # nor does it check that the path exists. r = runfiles.CreateDirectoryBased("foo/bar baz//qux/") self.assertEqual(r.Rlocation("arg"), "foo/bar baz//qux/arg") if RunfilesTest.IsWindows(): self.assertEqual(r.Rlocation("c:/foo"), "c:/foo") self.assertEqual(r.Rlocation("c:\\foo"), "c:\\foo") else: self.assertEqual(r.Rlocation("/foo"), "/foo") def testDirectoryBasedRlocationWithRepoMappingFromMain(self) -> None: with _MockFile( name="_repo_mapping", contents=[ "_,config.json,config.json~1.2.3", ",my_module,_main", ",my_protobuf,protobuf~3.19.2", ",my_workspace,_main", "protobuf~3.19.2,config.json,config.json~1.2.3", "protobuf~3.19.2,protobuf,protobuf~3.19.2", ], ) as rm: dir = os.path.dirname(rm.Path()) r = runfiles.CreateDirectoryBased(dir) self.assertEqual( r.Rlocation("my_module/bar/runfile", ""), dir + "/_main/bar/runfile" ) self.assertEqual( r.Rlocation("my_workspace/bar/runfile", ""), dir + "/_main/bar/runfile" ) self.assertEqual( r.Rlocation("my_protobuf/foo/runfile", ""), dir + "/protobuf~3.19.2/foo/runfile", ) self.assertEqual( r.Rlocation("my_protobuf/bar/dir", ""), dir + "/protobuf~3.19.2/bar/dir" ) self.assertEqual( r.Rlocation("my_protobuf/bar/dir/file", ""), dir + "/protobuf~3.19.2/bar/dir/file", ) self.assertEqual( r.Rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", ""), dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ) self.assertEqual( r.Rlocation("protobuf/foo/runfile", ""), dir + "/protobuf/foo/runfile" ) self.assertEqual( r.Rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le", ""), dir + "/protobuf/bar/dir/dir/de eply/nes ted/fi~le", ) self.assertEqual( r.Rlocation("_main/bar/runfile", ""), dir + "/_main/bar/runfile" ) self.assertEqual( r.Rlocation("protobuf~3.19.2/foo/runfile", ""), dir + "/protobuf~3.19.2/foo/runfile", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir", ""), dir + "/protobuf~3.19.2/bar/dir", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir/file", ""), dir + "/protobuf~3.19.2/bar/dir/file", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ""), dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ) self.assertEqual(r.Rlocation("config.json", ""), dir + "/config.json") def testDirectoryBasedRlocationWithRepoMappingFromOtherRepo(self) -> None: with _MockFile( name="_repo_mapping", contents=[ "_,config.json,config.json~1.2.3", ",my_module,_main", ",my_protobuf,protobuf~3.19.2", ",my_workspace,_main", "protobuf~3.19.2,config.json,config.json~1.2.3", "protobuf~3.19.2,protobuf,protobuf~3.19.2", ], ) as rm: dir = os.path.dirname(rm.Path()) r = runfiles.CreateDirectoryBased(dir) self.assertEqual( r.Rlocation("protobuf/foo/runfile", "protobuf~3.19.2"), dir + "/protobuf~3.19.2/foo/runfile", ) self.assertEqual( r.Rlocation("protobuf/bar/dir", "protobuf~3.19.2"), dir + "/protobuf~3.19.2/bar/dir", ) self.assertEqual( r.Rlocation("protobuf/bar/dir/file", "protobuf~3.19.2"), dir + "/protobuf~3.19.2/bar/dir/file", ) self.assertEqual( r.Rlocation( "protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" ), dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ) self.assertEqual( r.Rlocation("my_module/bar/runfile", "protobuf~3.19.2"), dir + "/my_module/bar/runfile", ) self.assertEqual( r.Rlocation( "my_protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" ), dir + "/my_protobuf/bar/dir/de eply/nes ted/fi~le", ) self.assertEqual( r.Rlocation("_main/bar/runfile", "protobuf~3.19.2"), dir + "/_main/bar/runfile", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/foo/runfile", "protobuf~3.19.2"), dir + "/protobuf~3.19.2/foo/runfile", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir", "protobuf~3.19.2"), dir + "/protobuf~3.19.2/bar/dir", ) self.assertEqual( r.Rlocation("protobuf~3.19.2/bar/dir/file", "protobuf~3.19.2"), dir + "/protobuf~3.19.2/bar/dir/file", ) self.assertEqual( r.Rlocation( "protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" ), dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ) self.assertEqual( r.Rlocation("config.json", "protobuf~3.19.2"), dir + "/config.json" ) def testCurrentRepository(self) -> None: # Under bzlmod, the current repository name is the empty string instead # of the name in the workspace file. if bool(int(os.environ["BZLMOD_ENABLED"])): expected = "" else: expected = "rules_python" r = runfiles.Create({"RUNFILES_DIR": "whatever"}) assert r is not None # mypy doesn't understand the unittest api. self.assertEqual(r.CurrentRepository(), expected) @staticmethod def IsWindows() -> bool: return os.name == "nt" class _MockFile: def __init__( self, name: Optional[str] = None, contents: Optional[List[Any]] = None ) -> None: self._contents = contents or [] self._name = name or "x" self._path: Optional[str] = None def __enter__(self) -> Any: tmpdir = os.environ.get("TEST_TMPDIR") self._path = os.path.join(tempfile.mkdtemp(dir=tmpdir), self._name) with open(self._path, "wt") as f: f.writelines(l + "\n" for l in self._contents) return self def __exit__( self, exc_type: Any, # pylint: disable=unused-argument exc_value: Any, # pylint: disable=unused-argument traceback: Any, # pylint: disable=unused-argument ) -> None: if self._path: os.remove(self._path) os.rmdir(os.path.dirname(self._path)) def Path(self) -> str: assert self._path is not None return self._path if __name__ == "__main__": unittest.main()