1# pylint: disable=g-bad-file-header 2# Copyright 2018 The Bazel Authors. All rights reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import os 17import tempfile 18import unittest 19 20from python.runfiles import runfiles 21 22 23class RunfilesTest(unittest.TestCase): 24 # """Unit tests for `runfiles.Runfiles`.""" 25 26 def testRlocationArgumentValidation(self): 27 r = runfiles.Create({"RUNFILES_DIR": "whatever"}) 28 self.assertRaises(ValueError, lambda: r.Rlocation(None)) 29 self.assertRaises(ValueError, lambda: r.Rlocation("")) 30 self.assertRaises(TypeError, lambda: r.Rlocation(1)) 31 self.assertRaisesRegex( 32 ValueError, "is not normalized", lambda: r.Rlocation("../foo") 33 ) 34 self.assertRaisesRegex( 35 ValueError, "is not normalized", lambda: r.Rlocation("foo/..") 36 ) 37 self.assertRaisesRegex( 38 ValueError, "is not normalized", lambda: r.Rlocation("foo/../bar") 39 ) 40 self.assertRaisesRegex( 41 ValueError, "is not normalized", lambda: r.Rlocation("./foo") 42 ) 43 self.assertRaisesRegex( 44 ValueError, "is not normalized", lambda: r.Rlocation("foo/.") 45 ) 46 self.assertRaisesRegex( 47 ValueError, "is not normalized", lambda: r.Rlocation("foo/./bar") 48 ) 49 self.assertRaisesRegex( 50 ValueError, "is not normalized", lambda: r.Rlocation("//foobar") 51 ) 52 self.assertRaisesRegex( 53 ValueError, "is not normalized", lambda: r.Rlocation("foo//") 54 ) 55 self.assertRaisesRegex( 56 ValueError, "is not normalized", lambda: r.Rlocation("foo//bar") 57 ) 58 self.assertRaisesRegex( 59 ValueError, 60 "is absolute without a drive letter", 61 lambda: r.Rlocation("\\foo"), 62 ) 63 64 def testCreatesManifestBasedRunfiles(self): 65 with _MockFile(contents=["a/b c/d"]) as mf: 66 r = runfiles.Create( 67 { 68 "RUNFILES_MANIFEST_FILE": mf.Path(), 69 "RUNFILES_DIR": "ignored when RUNFILES_MANIFEST_FILE has a value", 70 "TEST_SRCDIR": "always ignored", 71 } 72 ) 73 self.assertEqual(r.Rlocation("a/b"), "c/d") 74 self.assertIsNone(r.Rlocation("foo")) 75 76 def testManifestBasedRunfilesEnvVars(self): 77 with _MockFile(name="MANIFEST") as mf: 78 r = runfiles.Create( 79 { 80 "RUNFILES_MANIFEST_FILE": mf.Path(), 81 "TEST_SRCDIR": "always ignored", 82 } 83 ) 84 self.assertDictEqual( 85 r.EnvVars(), 86 { 87 "RUNFILES_MANIFEST_FILE": mf.Path(), 88 "RUNFILES_DIR": mf.Path()[: -len("/MANIFEST")], 89 "JAVA_RUNFILES": mf.Path()[: -len("/MANIFEST")], 90 }, 91 ) 92 93 with _MockFile(name="foo.runfiles_manifest") as mf: 94 r = runfiles.Create( 95 { 96 "RUNFILES_MANIFEST_FILE": mf.Path(), 97 "TEST_SRCDIR": "always ignored", 98 } 99 ) 100 self.assertDictEqual( 101 r.EnvVars(), 102 { 103 "RUNFILES_MANIFEST_FILE": mf.Path(), 104 "RUNFILES_DIR": ( 105 mf.Path()[: -len("foo.runfiles_manifest")] + "foo.runfiles" 106 ), 107 "JAVA_RUNFILES": ( 108 mf.Path()[: -len("foo.runfiles_manifest")] + "foo.runfiles" 109 ), 110 }, 111 ) 112 113 with _MockFile(name="x_manifest") as mf: 114 r = runfiles.Create( 115 { 116 "RUNFILES_MANIFEST_FILE": mf.Path(), 117 "TEST_SRCDIR": "always ignored", 118 } 119 ) 120 self.assertDictEqual( 121 r.EnvVars(), 122 { 123 "RUNFILES_MANIFEST_FILE": mf.Path(), 124 "RUNFILES_DIR": "", 125 "JAVA_RUNFILES": "", 126 }, 127 ) 128 129 def testCreatesDirectoryBasedRunfiles(self): 130 r = runfiles.Create( 131 { 132 "RUNFILES_DIR": "runfiles/dir", 133 "TEST_SRCDIR": "always ignored", 134 } 135 ) 136 self.assertEqual(r.Rlocation("a/b"), "runfiles/dir/a/b") 137 self.assertEqual(r.Rlocation("foo"), "runfiles/dir/foo") 138 139 def testDirectoryBasedRunfilesEnvVars(self): 140 r = runfiles.Create( 141 { 142 "RUNFILES_DIR": "runfiles/dir", 143 "TEST_SRCDIR": "always ignored", 144 } 145 ) 146 self.assertDictEqual( 147 r.EnvVars(), 148 { 149 "RUNFILES_DIR": "runfiles/dir", 150 "JAVA_RUNFILES": "runfiles/dir", 151 }, 152 ) 153 154 def testFailsToCreateManifestBasedBecauseManifestDoesNotExist(self): 155 def _Run(): 156 runfiles.Create({"RUNFILES_MANIFEST_FILE": "non-existing path"}) 157 158 self.assertRaisesRegex(IOError, "non-existing path", _Run) 159 160 def testFailsToCreateAnyRunfilesBecauseEnvvarsAreNotDefined(self): 161 with _MockFile(contents=["a b"]) as mf: 162 runfiles.Create( 163 { 164 "RUNFILES_MANIFEST_FILE": mf.Path(), 165 "RUNFILES_DIR": "whatever", 166 "TEST_SRCDIR": "always ignored", 167 } 168 ) 169 runfiles.Create( 170 { 171 "RUNFILES_DIR": "whatever", 172 "TEST_SRCDIR": "always ignored", 173 } 174 ) 175 self.assertIsNone(runfiles.Create({"TEST_SRCDIR": "always ignored"})) 176 self.assertIsNone(runfiles.Create({"FOO": "bar"})) 177 178 def testManifestBasedRlocation(self): 179 with _MockFile( 180 contents=[ 181 "Foo/runfile1", 182 "Foo/runfile2 C:/Actual Path\\runfile2", 183 "Foo/Bar/runfile3 D:\\the path\\run file 3.txt", 184 "Foo/Bar/Dir E:\\Actual Path\\Directory", 185 ] 186 ) as mf: 187 r = runfiles.CreateManifestBased(mf.Path()) 188 self.assertEqual(r.Rlocation("Foo/runfile1"), "Foo/runfile1") 189 self.assertEqual(r.Rlocation("Foo/runfile2"), "C:/Actual Path\\runfile2") 190 self.assertEqual( 191 r.Rlocation("Foo/Bar/runfile3"), "D:\\the path\\run file 3.txt" 192 ) 193 self.assertEqual( 194 r.Rlocation("Foo/Bar/Dir/runfile4"), 195 "E:\\Actual Path\\Directory/runfile4", 196 ) 197 self.assertEqual( 198 r.Rlocation("Foo/Bar/Dir/Deeply/Nested/runfile4"), 199 "E:\\Actual Path\\Directory/Deeply/Nested/runfile4", 200 ) 201 self.assertIsNone(r.Rlocation("unknown")) 202 if RunfilesTest.IsWindows(): 203 self.assertEqual(r.Rlocation("c:/foo"), "c:/foo") 204 self.assertEqual(r.Rlocation("c:\\foo"), "c:\\foo") 205 else: 206 self.assertEqual(r.Rlocation("/foo"), "/foo") 207 208 def testManifestBasedRlocationWithRepoMappingFromMain(self): 209 with _MockFile( 210 contents=[ 211 ",config.json,config.json~1.2.3", 212 ",my_module,_main", 213 ",my_protobuf,protobuf~3.19.2", 214 ",my_workspace,_main", 215 "protobuf~3.19.2,config.json,config.json~1.2.3", 216 "protobuf~3.19.2,protobuf,protobuf~3.19.2", 217 ] 218 ) as rm, _MockFile( 219 contents=[ 220 "_repo_mapping " + rm.Path(), 221 "config.json /etc/config.json", 222 "protobuf~3.19.2/foo/runfile C:/Actual Path\\protobuf\\runfile", 223 "_main/bar/runfile /the/path/./to/other//other runfile.txt", 224 "protobuf~3.19.2/bar/dir E:\\Actual Path\\Directory", 225 ], 226 ) as mf: 227 r = runfiles.CreateManifestBased(mf.Path()) 228 229 self.assertEqual( 230 r.Rlocation("my_module/bar/runfile", ""), 231 "/the/path/./to/other//other runfile.txt", 232 ) 233 self.assertEqual( 234 r.Rlocation("my_workspace/bar/runfile", ""), 235 "/the/path/./to/other//other runfile.txt", 236 ) 237 self.assertEqual( 238 r.Rlocation("my_protobuf/foo/runfile", ""), 239 "C:/Actual Path\\protobuf\\runfile", 240 ) 241 self.assertEqual( 242 r.Rlocation("my_protobuf/bar/dir", ""), "E:\\Actual Path\\Directory" 243 ) 244 self.assertEqual( 245 r.Rlocation("my_protobuf/bar/dir/file", ""), 246 "E:\\Actual Path\\Directory/file", 247 ) 248 self.assertEqual( 249 r.Rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", ""), 250 "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", 251 ) 252 253 self.assertIsNone(r.Rlocation("protobuf/foo/runfile")) 254 self.assertIsNone(r.Rlocation("protobuf/bar/dir")) 255 self.assertIsNone(r.Rlocation("protobuf/bar/dir/file")) 256 self.assertIsNone(r.Rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le")) 257 258 self.assertEqual( 259 r.Rlocation("_main/bar/runfile", ""), 260 "/the/path/./to/other//other runfile.txt", 261 ) 262 self.assertEqual( 263 r.Rlocation("protobuf~3.19.2/foo/runfile", ""), 264 "C:/Actual Path\\protobuf\\runfile", 265 ) 266 self.assertEqual( 267 r.Rlocation("protobuf~3.19.2/bar/dir", ""), "E:\\Actual Path\\Directory" 268 ) 269 self.assertEqual( 270 r.Rlocation("protobuf~3.19.2/bar/dir/file", ""), 271 "E:\\Actual Path\\Directory/file", 272 ) 273 self.assertEqual( 274 r.Rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ""), 275 "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", 276 ) 277 278 self.assertEqual(r.Rlocation("config.json", ""), "/etc/config.json") 279 self.assertIsNone(r.Rlocation("_main", "")) 280 self.assertIsNone(r.Rlocation("my_module", "")) 281 self.assertIsNone(r.Rlocation("protobuf", "")) 282 283 def testManifestBasedRlocationWithRepoMappingFromOtherRepo(self): 284 with _MockFile( 285 contents=[ 286 ",config.json,config.json~1.2.3", 287 ",my_module,_main", 288 ",my_protobuf,protobuf~3.19.2", 289 ",my_workspace,_main", 290 "protobuf~3.19.2,config.json,config.json~1.2.3", 291 "protobuf~3.19.2,protobuf,protobuf~3.19.2", 292 ] 293 ) as rm, _MockFile( 294 contents=[ 295 "_repo_mapping " + rm.Path(), 296 "config.json /etc/config.json", 297 "protobuf~3.19.2/foo/runfile C:/Actual Path\\protobuf\\runfile", 298 "_main/bar/runfile /the/path/./to/other//other runfile.txt", 299 "protobuf~3.19.2/bar/dir E:\\Actual Path\\Directory", 300 ], 301 ) as mf: 302 r = runfiles.CreateManifestBased(mf.Path()) 303 304 self.assertEqual( 305 r.Rlocation("protobuf/foo/runfile", "protobuf~3.19.2"), 306 "C:/Actual Path\\protobuf\\runfile", 307 ) 308 self.assertEqual( 309 r.Rlocation("protobuf/bar/dir", "protobuf~3.19.2"), 310 "E:\\Actual Path\\Directory", 311 ) 312 self.assertEqual( 313 r.Rlocation("protobuf/bar/dir/file", "protobuf~3.19.2"), 314 "E:\\Actual Path\\Directory/file", 315 ) 316 self.assertEqual( 317 r.Rlocation( 318 "protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" 319 ), 320 "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", 321 ) 322 323 self.assertIsNone(r.Rlocation("my_module/bar/runfile", "protobuf~3.19.2")) 324 self.assertIsNone(r.Rlocation("my_protobuf/foo/runfile", "protobuf~3.19.2")) 325 self.assertIsNone(r.Rlocation("my_protobuf/bar/dir", "protobuf~3.19.2")) 326 self.assertIsNone( 327 r.Rlocation("my_protobuf/bar/dir/file", "protobuf~3.19.2") 328 ) 329 self.assertIsNone( 330 r.Rlocation( 331 "my_protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" 332 ) 333 ) 334 335 self.assertEqual( 336 r.Rlocation("_main/bar/runfile", "protobuf~3.19.2"), 337 "/the/path/./to/other//other runfile.txt", 338 ) 339 self.assertEqual( 340 r.Rlocation("protobuf~3.19.2/foo/runfile", "protobuf~3.19.2"), 341 "C:/Actual Path\\protobuf\\runfile", 342 ) 343 self.assertEqual( 344 r.Rlocation("protobuf~3.19.2/bar/dir", "protobuf~3.19.2"), 345 "E:\\Actual Path\\Directory", 346 ) 347 self.assertEqual( 348 r.Rlocation("protobuf~3.19.2/bar/dir/file", "protobuf~3.19.2"), 349 "E:\\Actual Path\\Directory/file", 350 ) 351 self.assertEqual( 352 r.Rlocation( 353 "protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" 354 ), 355 "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", 356 ) 357 358 self.assertEqual( 359 r.Rlocation("config.json", "protobuf~3.19.2"), "/etc/config.json" 360 ) 361 self.assertIsNone(r.Rlocation("_main", "protobuf~3.19.2")) 362 self.assertIsNone(r.Rlocation("my_module", "protobuf~3.19.2")) 363 self.assertIsNone(r.Rlocation("protobuf", "protobuf~3.19.2")) 364 365 def testDirectoryBasedRlocation(self): 366 # The _DirectoryBased strategy simply joins the runfiles directory and the 367 # runfile's path on a "/". This strategy does not perform any normalization, 368 # nor does it check that the path exists. 369 r = runfiles.CreateDirectoryBased("foo/bar baz//qux/") 370 self.assertEqual(r.Rlocation("arg"), "foo/bar baz//qux/arg") 371 if RunfilesTest.IsWindows(): 372 self.assertEqual(r.Rlocation("c:/foo"), "c:/foo") 373 self.assertEqual(r.Rlocation("c:\\foo"), "c:\\foo") 374 else: 375 self.assertEqual(r.Rlocation("/foo"), "/foo") 376 377 def testDirectoryBasedRlocationWithRepoMappingFromMain(self): 378 with _MockFile( 379 name="_repo_mapping", 380 contents=[ 381 "_,config.json,config.json~1.2.3", 382 ",my_module,_main", 383 ",my_protobuf,protobuf~3.19.2", 384 ",my_workspace,_main", 385 "protobuf~3.19.2,config.json,config.json~1.2.3", 386 "protobuf~3.19.2,protobuf,protobuf~3.19.2", 387 ], 388 ) as rm: 389 dir = os.path.dirname(rm.Path()) 390 r = runfiles.CreateDirectoryBased(dir) 391 392 self.assertEqual( 393 r.Rlocation("my_module/bar/runfile", ""), dir + "/_main/bar/runfile" 394 ) 395 self.assertEqual( 396 r.Rlocation("my_workspace/bar/runfile", ""), dir + "/_main/bar/runfile" 397 ) 398 self.assertEqual( 399 r.Rlocation("my_protobuf/foo/runfile", ""), 400 dir + "/protobuf~3.19.2/foo/runfile", 401 ) 402 self.assertEqual( 403 r.Rlocation("my_protobuf/bar/dir", ""), dir + "/protobuf~3.19.2/bar/dir" 404 ) 405 self.assertEqual( 406 r.Rlocation("my_protobuf/bar/dir/file", ""), 407 dir + "/protobuf~3.19.2/bar/dir/file", 408 ) 409 self.assertEqual( 410 r.Rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", ""), 411 dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", 412 ) 413 414 self.assertEqual( 415 r.Rlocation("protobuf/foo/runfile", ""), dir + "/protobuf/foo/runfile" 416 ) 417 self.assertEqual( 418 r.Rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le", ""), 419 dir + "/protobuf/bar/dir/dir/de eply/nes ted/fi~le", 420 ) 421 422 self.assertEqual( 423 r.Rlocation("_main/bar/runfile", ""), dir + "/_main/bar/runfile" 424 ) 425 self.assertEqual( 426 r.Rlocation("protobuf~3.19.2/foo/runfile", ""), 427 dir + "/protobuf~3.19.2/foo/runfile", 428 ) 429 self.assertEqual( 430 r.Rlocation("protobuf~3.19.2/bar/dir", ""), 431 dir + "/protobuf~3.19.2/bar/dir", 432 ) 433 self.assertEqual( 434 r.Rlocation("protobuf~3.19.2/bar/dir/file", ""), 435 dir + "/protobuf~3.19.2/bar/dir/file", 436 ) 437 self.assertEqual( 438 r.Rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ""), 439 dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", 440 ) 441 442 self.assertEqual(r.Rlocation("config.json", ""), dir + "/config.json") 443 444 def testDirectoryBasedRlocationWithRepoMappingFromOtherRepo(self): 445 with _MockFile( 446 name="_repo_mapping", 447 contents=[ 448 "_,config.json,config.json~1.2.3", 449 ",my_module,_main", 450 ",my_protobuf,protobuf~3.19.2", 451 ",my_workspace,_main", 452 "protobuf~3.19.2,config.json,config.json~1.2.3", 453 "protobuf~3.19.2,protobuf,protobuf~3.19.2", 454 ], 455 ) as rm: 456 dir = os.path.dirname(rm.Path()) 457 r = runfiles.CreateDirectoryBased(dir) 458 459 self.assertEqual( 460 r.Rlocation("protobuf/foo/runfile", "protobuf~3.19.2"), 461 dir + "/protobuf~3.19.2/foo/runfile", 462 ) 463 self.assertEqual( 464 r.Rlocation("protobuf/bar/dir", "protobuf~3.19.2"), 465 dir + "/protobuf~3.19.2/bar/dir", 466 ) 467 self.assertEqual( 468 r.Rlocation("protobuf/bar/dir/file", "protobuf~3.19.2"), 469 dir + "/protobuf~3.19.2/bar/dir/file", 470 ) 471 self.assertEqual( 472 r.Rlocation( 473 "protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" 474 ), 475 dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", 476 ) 477 478 self.assertEqual( 479 r.Rlocation("my_module/bar/runfile", "protobuf~3.19.2"), 480 dir + "/my_module/bar/runfile", 481 ) 482 self.assertEqual( 483 r.Rlocation( 484 "my_protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" 485 ), 486 dir + "/my_protobuf/bar/dir/de eply/nes ted/fi~le", 487 ) 488 489 self.assertEqual( 490 r.Rlocation("_main/bar/runfile", "protobuf~3.19.2"), 491 dir + "/_main/bar/runfile", 492 ) 493 self.assertEqual( 494 r.Rlocation("protobuf~3.19.2/foo/runfile", "protobuf~3.19.2"), 495 dir + "/protobuf~3.19.2/foo/runfile", 496 ) 497 self.assertEqual( 498 r.Rlocation("protobuf~3.19.2/bar/dir", "protobuf~3.19.2"), 499 dir + "/protobuf~3.19.2/bar/dir", 500 ) 501 self.assertEqual( 502 r.Rlocation("protobuf~3.19.2/bar/dir/file", "protobuf~3.19.2"), 503 dir + "/protobuf~3.19.2/bar/dir/file", 504 ) 505 self.assertEqual( 506 r.Rlocation( 507 "protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" 508 ), 509 dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", 510 ) 511 512 self.assertEqual( 513 r.Rlocation("config.json", "protobuf~3.19.2"), dir + "/config.json" 514 ) 515 516 def testCurrentRepository(self): 517 # This test assumes that it is running without --enable_bzlmod as the 518 # correct result with Bzlmod would be the empty string - the canonical 519 # name # of the main repository. Without Bzlmod, the main repository is 520 # treated just like any other repository and has the name of its 521 # runfiles directory returned, which coincides with the name specified 522 # in the WORKSPACE file. 523 # 524 # Specify a fake runfiles directory to verify that its value isn't used 525 # by the function. 526 self.assertEqual( 527 runfiles.Create({"RUNFILES_DIR": "whatever"}).CurrentRepository(), 528 "rules_python", 529 ) 530 531 @staticmethod 532 def IsWindows(): 533 return os.name == "nt" 534 535 536class _MockFile(object): 537 def __init__(self, name=None, contents=None): 538 self._contents = contents or [] 539 self._name = name or "x" 540 self._path = None 541 542 def __enter__(self): 543 tmpdir = os.environ.get("TEST_TMPDIR") 544 self._path = os.path.join(tempfile.mkdtemp(dir=tmpdir), self._name) 545 with open(self._path, "wt") as f: 546 f.writelines(l + "\n" for l in self._contents) 547 return self 548 549 def __exit__(self, exc_type, exc_value, traceback): 550 os.remove(self._path) 551 os.rmdir(os.path.dirname(self._path)) 552 553 def Path(self): 554 return self._path 555 556 557if __name__ == "__main__": 558 unittest.main() 559