| # 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() |