1#!/usr/bin/env python3 2# 3# Copyright 2021, The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Unit tests for bazel_mode.""" 18# pylint: disable=invalid-name 19# pylint: disable=missing-function-docstring 20# pylint: disable=too-many-lines 21 22import argparse 23import re 24import shlex 25import shutil 26import tempfile 27import unittest 28 29from pathlib import Path 30from typing import List 31from unittest import mock 32 33# pylint: disable=import-error 34from pyfakefs import fake_filesystem_unittest 35 36import bazel_mode 37import constants 38import module_info 39 40from test_finders import example_finder, test_finder_base, test_info 41from test_runners import atest_tf_test_runner 42 43 44ATEST_TF_RUNNER = atest_tf_test_runner.AtestTradefedTestRunner.NAME 45BAZEL_RUNNER = bazel_mode.BazelTestRunner.NAME 46MODULE_BUILD_TARGETS = {'foo1', 'foo2', 'foo3'} 47MODULE_NAME = 'foo' 48 49 50class GenerationTestFixture(fake_filesystem_unittest.TestCase): 51 """Fixture for workspace generation tests.""" 52 53 def setUp(self): 54 self.setUpPyfakefs() 55 56 self.src_root_path = Path('/src') 57 self.out_dir_path = self.src_root_path.joinpath('out') 58 self.out_dir_path.mkdir(parents=True) 59 self.product_out_path = self.out_dir_path.joinpath('product') 60 self.host_out_path = self.out_dir_path.joinpath('host') 61 self.workspace_out_path = self.out_dir_path.joinpath('workspace') 62 63 def create_workspace_generator(self, modules=None, enabled_features=None): 64 mod_info = self.create_module_info(modules) 65 66 generator = bazel_mode.WorkspaceGenerator( 67 self.src_root_path, 68 self.workspace_out_path, 69 self.product_out_path, 70 self.host_out_path, 71 self.out_dir_path, 72 mod_info, 73 enabled_features=enabled_features, 74 ) 75 76 return generator 77 78 def run_generator(self, mod_info, enabled_features=None): 79 generator = bazel_mode.WorkspaceGenerator( 80 self.src_root_path, 81 self.workspace_out_path, 82 self.product_out_path, 83 self.host_out_path, 84 self.out_dir_path, 85 mod_info, 86 enabled_features=enabled_features, 87 ) 88 89 generator.generate() 90 91 # pylint: disable=protected-access 92 @mock.patch.dict('os.environ', {constants.ANDROID_BUILD_TOP: '/'}) 93 def create_empty_module_info(self): 94 fake_temp_file_name = next(tempfile._get_candidate_names()) 95 self.fs.create_file(fake_temp_file_name, contents='{}') 96 return module_info.ModuleInfo(module_file=fake_temp_file_name) 97 98 def create_module_info(self, modules=None): 99 mod_info = self.create_empty_module_info() 100 modules = modules or [] 101 102 prerequisites = frozenset().union( 103 bazel_mode.TestTarget.DEVICE_TEST_PREREQUISITES, 104 bazel_mode.TestTarget.DEVICELESS_TEST_PREREQUISITES) 105 106 for module_name in prerequisites: 107 info = host_module(name=module_name, path='prebuilts') 108 mod_info.name_to_module_info[module_name] = info 109 110 for m in modules: 111 mod_info.name_to_module_info[m['module_name']] = m 112 113 return mod_info 114 115 def assertSymlinkTo(self, symlink_path, target_path): 116 self.assertEqual(symlink_path.resolve(strict=False), target_path) 117 118 def assertTargetInWorkspace(self, name, package=''): 119 build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel') 120 contents = build_file.read_text(encoding='utf8') 121 occurrences = len(self.find_target_by_name(name, contents)) 122 123 if occurrences == 1: 124 return 125 126 cardinality = 'Multiple' if occurrences else 'Zero' 127 self.fail( 128 f'{cardinality} targets named \'{name}\' found in \'{contents}\'' 129 ) 130 131 def assertTargetNotInWorkspace(self, name, package=''): 132 build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel') 133 134 if not build_file.exists(): 135 return 136 137 contents = build_file.read_text(encoding='utf8') 138 matches = self.find_target_by_name(name, contents) 139 140 if not matches: 141 return 142 143 self.fail( 144 f'Unexpectedly found target(s) named \'{name}\' in \'{contents}\'' 145 ) 146 147 def assertInBuildFile(self, substring, package=''): 148 build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel') 149 self.assertIn(substring, build_file.read_text(encoding='utf8')) 150 151 def assertNotInBuildFile(self, substring, package=''): 152 build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel') 153 self.assertNotIn(substring, build_file.read_text(encoding='utf8')) 154 155 def assertFileInWorkspace(self, relative_path, package=''): 156 path = self.workspace_out_path.joinpath(package, relative_path) 157 self.assertTrue(path.exists()) 158 159 def assertFileNotInWorkspace(self, relative_path, package=''): 160 path = self.workspace_out_path.joinpath(package, relative_path) 161 self.assertFalse(path.exists()) 162 163 def find_target_by_name(self, name: str, contents: str) -> List[str]: 164 return re.findall(rf'\bname\s*=\s*"{name}"', contents) 165 166 167class BasicWorkspaceGenerationTest(GenerationTestFixture): 168 """Tests for basic workspace generation and update.""" 169 170 def test_generate_workspace_when_nonexistent(self): 171 workspace_generator = self.create_workspace_generator() 172 shutil.rmtree(workspace_generator.workspace_out_path, 173 ignore_errors=True) 174 175 workspace_generator.generate() 176 177 self.assertTrue(workspace_generator.workspace_out_path.is_dir()) 178 179 def test_regenerate_workspace_when_features_changed(self): 180 workspace_generator = self.create_workspace_generator( 181 enabled_features={bazel_mode.Features.NULL_FEATURE}) 182 workspace_generator.generate() 183 workspace_stat = workspace_generator.workspace_out_path.stat() 184 185 workspace_generator = self.create_workspace_generator() 186 workspace_generator.generate() 187 new_workspace_stat = workspace_generator.workspace_out_path.stat() 188 189 self.assertNotEqual(workspace_stat, new_workspace_stat) 190 191 def test_not_regenerate_when_feature_does_not_affect_workspace(self): 192 workspace_generator = self.create_workspace_generator( 193 enabled_features={bazel_mode.Features.NULL_FEATURE}) 194 workspace_generator.generate() 195 workspace_stat = workspace_generator.workspace_out_path.stat() 196 197 parser = argparse.ArgumentParser() 198 bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features') 199 # pylint: disable=no-member 200 args = parser.parse_args([ 201 bazel_mode.Features.NULL_FEATURE.arg_flag, 202 '--experimental-bes-publish' 203 ]) 204 workspace_generator = self.create_workspace_generator( 205 enabled_features=set(args.bazel_mode_features)) 206 workspace_generator.generate() 207 new_workspace_stat = workspace_generator.workspace_out_path.stat() 208 209 self.assertEqual(workspace_stat, new_workspace_stat) 210 211 def test_not_regenerate_workspace_when_features_unchanged(self): 212 workspace_generator = self.create_workspace_generator( 213 enabled_features={bazel_mode.Features.NULL_FEATURE}) 214 workspace_generator.generate() 215 workspace_stat = workspace_generator.workspace_out_path.stat() 216 217 workspace_generator = self.create_workspace_generator( 218 enabled_features={bazel_mode.Features.NULL_FEATURE}) 219 workspace_generator.generate() 220 new_workspace_stat = workspace_generator.workspace_out_path.stat() 221 222 self.assertEqual(workspace_stat, new_workspace_stat) 223 224 def test_regenerate_workspace_when_module_info_deleted(self): 225 workspace_generator = self.create_workspace_generator() 226 workspace_generator.generate() 227 workspace_stat = workspace_generator.workspace_out_path.stat() 228 229 workspace_generator.mod_info.mod_info_file_path.unlink() 230 workspace_generator = self.create_workspace_generator() 231 workspace_generator.generate() 232 233 new_workspace_stat = workspace_generator.workspace_out_path.stat() 234 self.assertNotEqual(workspace_stat, new_workspace_stat) 235 236 def test_not_regenerate_workspace_when_module_info_unchanged(self): 237 workspace_generator1 = self.create_workspace_generator() 238 workspace_generator1.generate() 239 workspace_stat = workspace_generator1.workspace_out_path.stat() 240 241 workspace_generator2 = self.create_workspace_generator() 242 workspace_generator2.generate() 243 new_workspace_stat = workspace_generator2.workspace_out_path.stat() 244 245 self.assertEqual(workspace_stat, new_workspace_stat) 246 247 def test_not_regenerate_workspace_when_module_only_touched(self): 248 workspace_generator = self.create_workspace_generator() 249 workspace_generator.generate() 250 workspace_stat = workspace_generator.workspace_out_path.stat() 251 252 Path(workspace_generator.mod_info.mod_info_file_path).touch() 253 workspace_generator = self.create_workspace_generator() 254 workspace_generator.generate() 255 256 new_workspace_stat = workspace_generator.workspace_out_path.stat() 257 self.assertEqual(workspace_stat, new_workspace_stat) 258 259 def test_regenerate_workspace_when_module_info_changed(self): 260 workspace_generator = self.create_workspace_generator() 261 workspace_generator.generate() 262 workspace_stat = workspace_generator.workspace_out_path.stat() 263 264 mod_info_file_path = workspace_generator.mod_info.mod_info_file_path 265 with open(mod_info_file_path, 'a', encoding='utf8') as f: 266 f.write(' ') 267 workspace_generator = self.create_workspace_generator() 268 workspace_generator.generate() 269 270 new_workspace_stat = workspace_generator.workspace_out_path.stat() 271 self.assertNotEqual(workspace_stat, new_workspace_stat) 272 273 def test_regenerate_workspace_when_md5_file_removed(self): 274 workspace_generator = self.create_workspace_generator() 275 workspace_generator.generate() 276 workspace_stat = workspace_generator.workspace_out_path.stat() 277 278 workspace_generator.mod_info.mod_info_file_path.unlink() 279 workspace_generator = self.create_workspace_generator() 280 workspace_generator.generate() 281 282 new_workspace_stat = workspace_generator.workspace_out_path.stat() 283 self.assertNotEqual(workspace_stat, new_workspace_stat) 284 285 def test_scrub_old_workspace_when_regenerating(self): 286 workspace_generator = self.create_workspace_generator() 287 workspace_generator.generate() 288 some_file = workspace_generator.workspace_out_path.joinpath('some_file') 289 some_file.touch() 290 self.assertTrue(some_file.is_file()) 291 292 # Remove the md5 file to regenerate the workspace. 293 workspace_generator.mod_info.mod_info_file_path.unlink() 294 workspace_generator = self.create_workspace_generator() 295 workspace_generator.generate() 296 297 self.assertFalse(some_file.is_file()) 298 299 def test_generate_workspace_file(self): 300 gen = self.create_workspace_generator() 301 workspace_path = gen.workspace_out_path.joinpath('WORKSPACE') 302 303 gen.generate() 304 305 self.assertSymlinkTo( 306 workspace_path, 307 self.src_root_path.joinpath('tools/asuite/atest/bazel/WORKSPACE') 308 ) 309 310 def test_generate_bazelrc_file(self): 311 gen = self.create_workspace_generator() 312 bazelrc_path = gen.workspace_out_path.joinpath('.bazelrc') 313 314 gen.generate() 315 316 self.assertSymlinkTo( 317 bazelrc_path, 318 self.src_root_path.joinpath('tools/asuite/atest/bazel/bazelrc') 319 ) 320 321 def test_generate_rules_dir(self): 322 gen = self.create_workspace_generator() 323 rules_dir_path = gen.workspace_out_path.joinpath('bazel/rules') 324 325 gen.generate() 326 327 self.assertSymlinkTo( 328 rules_dir_path, 329 self.src_root_path.joinpath('tools/asuite/atest/bazel/rules') 330 ) 331 332 def test_generate_configs_dir(self): 333 gen = self.create_workspace_generator() 334 configs_dir_path = gen.workspace_out_path.joinpath('bazel/configs') 335 336 gen.generate() 337 338 self.assertSymlinkTo( 339 configs_dir_path, 340 self.src_root_path.joinpath('tools/asuite/atest/bazel/configs') 341 ) 342 343 def test_generate_host_unit_test_module_target(self): 344 mod_info = self.create_module_info(modules=[ 345 host_unit_test_module(name='hello_world_test') 346 ]) 347 348 self.run_generator(mod_info) 349 350 self.assertTargetInWorkspace('hello_world_test_host') 351 352 def test_not_generate_host_test_module_target(self): 353 mod_info = self.create_module_info(modules=[ 354 host_test_module(name='hello_world_test'), 355 ]) 356 357 self.run_generator(mod_info) 358 359 self.assertTargetNotInWorkspace('hello_world_test') 360 361 def test_not_generate_test_module_target_with_invalid_installed_path(self): 362 mod_info = self.create_module_info(modules=[ 363 test_module(name='hello_world_test', installed='out/invalid/path') 364 ]) 365 366 self.run_generator(mod_info) 367 368 self.assertTargetNotInWorkspace('hello_world_test_device') 369 self.assertTargetNotInWorkspace('hello_world_test_host') 370 371 def test_generate_variable_file(self): 372 gen = self.create_workspace_generator() 373 374 gen.generate() 375 376 self.assertFileInWorkspace('BUILD.bazel') 377 self.assertFileInWorkspace('constants.bzl') 378 379 380class MultiConfigTestModuleTestTargetGenerationTest(GenerationTestFixture): 381 """Tests for test target generation of test modules with multi-configs.""" 382 383 def test_generate_test_rule_imports(self): 384 mod_info = self.create_module_info(modules=[ 385 multi_config(host_unit_suite(test_module( 386 name='hello_world_test', path='example/tests'))), 387 ]) 388 389 self.run_generator(mod_info, enabled_features=set([ 390 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST])) 391 392 self.assertInBuildFile( 393 'load("//bazel/rules:tradefed_test.bzl",' 394 ' "tradefed_device_test", "tradefed_deviceless_test")\n', 395 package='example/tests', 396 ) 397 398 def test_not_generate_device_test_import_when_feature_disabled(self): 399 mod_info = self.create_module_info(modules=[ 400 multi_config(host_unit_suite(test_module( 401 name='hello_world_test', path='example/tests'))), 402 ]) 403 404 self.run_generator(mod_info) 405 406 self.assertInBuildFile( 407 'load("//bazel/rules:tradefed_test.bzl",' 408 ' "tradefed_deviceless_test")\n', 409 package='example/tests', 410 ) 411 412 def test_generate_test_targets(self): 413 mod_info = self.create_module_info(modules=[ 414 multi_config(host_unit_suite(test_module( 415 name='hello_world_test', path='example/tests'))), 416 ]) 417 418 self.run_generator(mod_info, enabled_features=set([ 419 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST])) 420 421 self.assertTargetInWorkspace('hello_world_test_device', 422 package='example/tests') 423 self.assertTargetInWorkspace('hello_world_test_host', 424 package='example/tests') 425 426 def test_not_generate_device_test_target_when_feature_disabled(self): 427 mod_info = self.create_module_info(modules=[ 428 multi_config(host_unit_suite(test_module( 429 name='hello_world_test', path='example/tests'))), 430 ]) 431 432 self.run_generator(mod_info) 433 434 self.assertTargetNotInWorkspace('hello_world_test_device', 435 package='example/tests') 436 self.assertTargetInWorkspace('hello_world_test_host', 437 package='example/tests') 438 439 440class DeviceTestModuleTestTargetGenerationTest(GenerationTestFixture): 441 """Tests for device test module test target generation.""" 442 443 def test_generate_device_driven_test_target(self): 444 mod_info = self.create_module_info(modules=[ 445 device_test_module( 446 name='hello_world_test', path='example/tests'), 447 ]) 448 449 self.run_generator(mod_info, enabled_features=set([ 450 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST])) 451 452 self.assertInBuildFile( 453 'load("//bazel/rules:tradefed_test.bzl",' 454 ' "tradefed_device_test")\n', 455 package='example/tests', 456 ) 457 self.assertTargetInWorkspace('hello_world_test_device', 458 package='example/tests') 459 460 def test_raise_when_prerequisite_not_in_module_info(self): 461 mod_info = self.create_module_info(modules=[ 462 device_test_module(), 463 ]) 464 del mod_info.name_to_module_info['aapt'] 465 466 with self.assertRaises(Exception) as context: 467 self.run_generator(mod_info, enabled_features=set([ 468 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST])) 469 470 self.assertIn('aapt', str(context.exception)) 471 472 473class HostUnitTestModuleTestTargetGenerationTest(GenerationTestFixture): 474 """Tests for host unit test module test target generation.""" 475 476 def test_generate_deviceless_test_import(self): 477 mod_info = self.create_module_info(modules=[ 478 host_unit_test_module(name='hello_world_test'), 479 ]) 480 481 self.run_generator(mod_info) 482 483 self.assertInBuildFile( 484 'load("//bazel/rules:tradefed_test.bzl",' 485 ' "tradefed_deviceless_test")\n' 486 ) 487 488 def test_generate_deviceless_test_target(self): 489 mod_info = self.create_module_info(modules=[ 490 host_unit_test_module( 491 name='hello_world_test', path='example/tests'), 492 ]) 493 494 self.run_generator(mod_info) 495 496 self.assertInBuildFile( 497 'tradefed_deviceless_test(\n' 498 ' name = "hello_world_test_host",\n' 499 ' test = "//example/tests:hello_world_test",\n' 500 ')', 501 package='example/tests', 502 ) 503 504 def test_generate_test_module_prebuilt(self): 505 mod_info = self.create_module_info(modules=[ 506 host_unit_test_module(name='hello_world_test'), 507 ]) 508 509 self.run_generator(mod_info) 510 511 self.assertTargetInWorkspace('hello_world_test') 512 513 def test_raise_when_prerequisite_not_in_module_info(self): 514 mod_info = self.create_module_info(modules=[ 515 host_unit_test_module(), 516 ]) 517 del mod_info.name_to_module_info['adb'] 518 519 with self.assertRaises(Exception) as context: 520 self.run_generator(mod_info) 521 522 self.assertIn('adb', str(context.exception)) 523 524 def test_raise_when_prerequisite_module_missing_path(self): 525 mod_info = self.create_module_info(modules=[ 526 host_unit_test_module(), 527 ]) 528 mod_info.name_to_module_info['adb'].get('path').clear() 529 530 with self.assertRaises(Exception) as context: 531 self.run_generator(mod_info) 532 533 self.assertIn('adb', str(context.exception)) 534 535 def test_warning_when_prerequisite_module_has_multiple_path(self): 536 mod_info = self.create_module_info(modules=[ 537 host_unit_test_module(), 538 ]) 539 mod_info.name_to_module_info['adb'].get('path').append('the/2nd/path') 540 541 with self.assertWarns(Warning) as context: 542 self.run_generator(mod_info) 543 544 self.assertIn('adb', str(context.warnings[0].message)) 545 546class ModulePrebuiltTargetGenerationTest(GenerationTestFixture): 547 """Tests for module prebuilt target generation.""" 548 549 def test_generate_prebuilt_import(self): 550 mod_info = self.create_module_info(modules=[ 551 supported_test_module(), 552 ]) 553 554 self.run_generator(mod_info) 555 556 self.assertInBuildFile( 557 'load("//bazel/rules:soong_prebuilt.bzl", "soong_prebuilt")\n' 558 ) 559 560 def test_generate_prebuilt_target_for_multi_config_test_module(self): 561 mod_info = self.create_module_info(modules=[ 562 multi_config(supported_test_module(name='libhello')), 563 ]) 564 565 self.run_generator(mod_info) 566 567 self.assertInBuildFile( 568 'soong_prebuilt(\n' 569 ' name = "libhello",\n' 570 ' module_name = "libhello",\n' 571 ' files = select({\n' 572 ' "//bazel/rules:device": glob(["libhello/device/**/*"]),\n' 573 ' "//bazel/rules:host": glob(["libhello/host/**/*"]),\n' 574 ' }),\n' 575 ')\n' 576 ) 577 578 def test_create_symlinks_to_testcases_for_multi_config_test_module(self): 579 module_name = 'hello_world_test' 580 mod_info = self.create_module_info(modules=[ 581 multi_config(supported_test_module(name=module_name)) 582 ]) 583 module_out_path = self.workspace_out_path.joinpath(module_name) 584 585 self.run_generator(mod_info) 586 587 self.assertSymlinkTo( 588 module_out_path.joinpath(f'host/testcases/{module_name}'), 589 self.host_out_path.joinpath(f'testcases/{module_name}')) 590 self.assertSymlinkTo( 591 module_out_path.joinpath(f'device/testcases/{module_name}'), 592 self.product_out_path.joinpath(f'testcases/{module_name}')) 593 594 def test_generate_files_for_host_only_test_module(self): 595 mod_info = self.create_module_info(modules=[ 596 host_only_config(supported_test_module(name='test1')), 597 ]) 598 599 self.run_generator(mod_info) 600 601 self.assertInBuildFile( 602 ' files = select({\n' 603 ' "//bazel/rules:host": glob(["test1/host/**/*"]),\n' 604 ' }),\n' 605 ) 606 607 def test_generate_files_for_device_only_test_module(self): 608 mod_info = self.create_module_info(modules=[ 609 device_only_config(supported_test_module(name='test1')), 610 ]) 611 612 self.run_generator(mod_info) 613 614 self.assertInBuildFile( 615 ' files = select({\n' 616 ' "//bazel/rules:device": glob(["test1/device/**/*"]),\n' 617 ' }),\n' 618 ) 619 620 def test_not_create_device_symlinks_for_host_only_test_module(self): 621 mod_info = self.create_module_info(modules=[ 622 host_only_config(supported_test_module(name='test1')), 623 ]) 624 625 self.run_generator(mod_info) 626 627 self.assertFileNotInWorkspace('test1/device') 628 629 def test_not_create_host_symlinks_for_device_test_module(self): 630 mod_info = self.create_module_info(modules=[ 631 device_only_config(supported_test_module(name='test1')), 632 ]) 633 634 self.run_generator(mod_info) 635 636 self.assertFileNotInWorkspace('test1/host') 637 638 639class ModuleSharedLibGenerationTest(GenerationTestFixture): 640 """Tests for module shared libs target generation.""" 641 642 def test_not_generate_runtime_deps_when_all_configs_incompatible(self): 643 mod_info = self.create_module_info(modules=[ 644 host_only_config(supported_test_module(shared_libs=['libdevice'])), 645 device_only_config(module(name='libdevice')), 646 ]) 647 648 self.run_generator(mod_info) 649 650 self.assertNotInBuildFile('runtime_deps') 651 652 def test_generate_runtime_deps_when_configs_compatible(self): 653 mod_info = self.create_module_info(modules=[ 654 multi_config(supported_test_module(shared_libs=['libmulti'])), 655 multi_config_module(name='libmulti'), 656 ]) 657 658 self.run_generator(mod_info) 659 660 self.assertInBuildFile( 661 ' runtime_deps = select({\n' 662 ' "//bazel/rules:device": [\n' 663 ' "//:libmulti",\n' 664 ' ],\n' 665 ' "//bazel/rules:host": [\n' 666 ' "//:libmulti",\n' 667 ' ],\n' 668 ' }),\n' 669 ) 670 671 def test_generate_runtime_deps_when_configs_partially_compatible(self): 672 mod_info = self.create_module_info(modules=[ 673 multi_config(supported_test_module(shared_libs=[ 674 'libhost', 675 ])), 676 host_module(name='libhost'), 677 ]) 678 679 self.run_generator(mod_info) 680 681 self.assertInBuildFile( 682 ' runtime_deps = select({\n' 683 ' "//bazel/rules:device": [\n' 684 ' ],\n' 685 ' "//bazel/rules:host": [\n' 686 ' "//:libhost",\n' 687 ' ],\n' 688 ' }),\n' 689 ) 690 691 def test_generate_runtime_deps_with_mixed_compatibility(self): 692 mod_info = self.create_module_info(modules=[ 693 multi_config(supported_test_module(shared_libs=[ 694 'libhost', 695 'libdevice', 696 'libmulti' 697 ])), 698 host_module(name='libhost'), 699 device_module(name='libdevice'), 700 multi_config_module(name='libmulti'), 701 ]) 702 703 self.run_generator(mod_info) 704 705 self.assertInBuildFile( 706 ' runtime_deps = select({\n' 707 ' "//bazel/rules:device": [\n' 708 ' "//:libdevice",\n' 709 ' "//:libmulti",\n' 710 ' ],\n' 711 ' "//bazel/rules:host": [\n' 712 ' "//:libhost",\n' 713 ' "//:libmulti",\n' 714 ' ],\n' 715 ' }),\n' 716 ) 717 718 def test_generate_runtime_deps_recursively(self): 719 mod_info = self.create_module_info(modules=[ 720 multi_config(supported_test_module(shared_libs=[ 721 'libdirect', 722 ])), 723 multi_config_module(name='libdirect', shared_libs=[ 724 'libtransitive', 725 ]), 726 multi_config_module(name='libtransitive'), 727 ]) 728 729 self.run_generator(mod_info) 730 731 self.assertTargetInWorkspace('libtransitive') 732 733 def test_generate_shared_runtime_deps_once(self): 734 mod_info = self.create_module_info(modules=[ 735 multi_config(supported_test_module(shared_libs=[ 736 'libleft', 737 'libright', 738 ])), 739 multi_config_module(name='libleft', shared_libs=[ 740 'libshared', 741 ]), 742 multi_config_module(name='libright', shared_libs=[ 743 'libshared', 744 ]), 745 multi_config_module(name='libshared'), 746 ]) 747 748 self.run_generator(mod_info) 749 750 self.assertTargetInWorkspace('libshared') 751 752 def test_generate_runtime_deps_in_order(self): 753 mod_info = self.create_module_info(modules=[ 754 supported_test_module(shared_libs=['libhello2', 'libhello1']), 755 host_module(name='libhello1'), 756 host_module(name='libhello2'), 757 ]) 758 759 self.run_generator(mod_info) 760 761 self.assertInBuildFile( 762 ' "//:libhello1",\n' 763 ' "//:libhello2",\n' 764 ) 765 766 def test_generate_target_for_shared_lib(self): 767 mod_info = self.create_module_info(modules=[ 768 supported_test_module(shared_libs=['libhello']), 769 host_module(name='libhello'), 770 ]) 771 772 self.run_generator(mod_info) 773 774 self.assertTargetInWorkspace('libhello') 775 776 def test_not_generate_for_missing_shared_lib_module(self): 777 mod_info = self.create_module_info(modules=[ 778 supported_test_module(shared_libs=['libhello']) 779 ]) 780 781 self.run_generator(mod_info) 782 783 self.assertNotInBuildFile(' "//:libhello",\n') 784 self.assertTargetNotInWorkspace('libhello') 785 786 def test_not_generate_when_shared_lib_uninstalled(self): 787 mod_info = self.create_module_info(modules=[ 788 supported_test_module(shared_libs=['libhello']), 789 host_module(name='libhello', installed=[]), 790 ]) 791 792 self.run_generator(mod_info) 793 794 self.assertNotInBuildFile(' "//:libhello",\n') 795 self.assertTargetNotInWorkspace('libhello') 796 797 def test_not_generate_when_shared_lib_installed_path_unsupported(self): 798 unsupported_install_path = 'out/other' 799 mod_info = self.create_module_info(modules=[ 800 supported_test_module(shared_libs=['libhello']), 801 shared_lib(module('libhello', 802 installed=[unsupported_install_path])), 803 ]) 804 805 self.run_generator(mod_info) 806 807 self.assertNotInBuildFile('"//:libhello",\n') 808 self.assertTargetNotInWorkspace('libhello') 809 810 def test_not_generate_when_shared_lib_install_path_ambiguous(self): 811 ambiguous_install_path = 'out/f1' 812 mod_info = self.create_module_info(modules=[ 813 supported_test_module(shared_libs=['libhello']), 814 module(name='libhello', installed=[ambiguous_install_path]), 815 ]) 816 817 self.run_generator(mod_info) 818 819 self.assertNotInBuildFile('"//:libhello",\n') 820 self.assertTargetNotInWorkspace('libhello') 821 822 def test_generate_target_for_rlib_dependency(self): 823 mod_info = self.create_module_info(modules=[ 824 supported_test_module(dependencies=['libhello']), 825 rlib(module(name='libhello')) 826 ]) 827 828 self.run_generator(mod_info) 829 830 self.assertInBuildFile( 831 'soong_uninstalled_prebuilt(\n' 832 ' name = "libhello",\n' 833 ' module_name = "libhello",\n' 834 ')\n' 835 ) 836 837 def test_generate_target_for_rlib_dylib_dependency(self): 838 mod_info = self.create_module_info(modules=[ 839 supported_test_module(dependencies=['libhello']), 840 rlib(module(name='libhello', dependencies=['libworld'])), 841 host_only_config(dylib(module(name='libworld'))) 842 ]) 843 844 self.run_generator(mod_info) 845 846 self.assertTargetInWorkspace('libworld') 847 848 def test_generate_target_for_dylib_dependency(self): 849 mod_info = self.create_module_info(modules=[ 850 supported_test_module(dependencies=['libhello']), 851 host_only_config(dylib(module(name='libhello'))) 852 ]) 853 854 self.run_generator(mod_info) 855 856 self.assertInBuildFile( 857 'soong_prebuilt(\n' 858 ' name = "libhello",\n' 859 ' module_name = "libhello",\n' 860 ) 861 862 def test_generate_target_for_uninstalled_dylib_dependency(self): 863 mod_info = self.create_module_info(modules=[ 864 supported_test_module(dependencies=['libhello']), 865 dylib(module(name='libhello', installed=[])) 866 ]) 867 868 self.run_generator(mod_info) 869 870 self.assertInBuildFile( 871 'soong_uninstalled_prebuilt(\n' 872 ' name = "libhello",\n' 873 ' module_name = "libhello",\n' 874 ')\n' 875 ) 876 877 def test_not_generate_target_for_non_runtime_dependency(self): 878 mod_info = self.create_module_info(modules=[ 879 supported_test_module(dependencies=['libhello']), 880 host_module(name='libhello', classes=['NOT_SUPPORTED']) 881 ]) 882 883 self.run_generator(mod_info) 884 885 self.assertNotInBuildFile('"//:libhello",\n') 886 self.assertTargetNotInWorkspace('libhello') 887 888 889 def test_generate_target_for_runtime_dependency(self): 890 mod_info = self.create_module_info(modules=[ 891 supported_test_module(runtime_dependencies=['libhello']), 892 host_only_config( 893 module(name='libhello', classes=['SHARED_LIBRARIES'])) 894 ]) 895 896 self.run_generator(mod_info) 897 898 self.assertInBuildFile( 899 ' runtime_deps = select({\n' 900 ' "//bazel/rules:host": [\n' 901 ' "//:libhello",\n' 902 ' ],\n' 903 ' }),\n' 904 ) 905 906class SharedLibPrebuiltTargetGenerationTest(GenerationTestFixture): 907 """Tests for runtime dependency module prebuilt target generation.""" 908 909 def test_create_multi_config_target_symlinks(self): 910 host_file1 = self.host_out_path.joinpath('a/b/f1') 911 host_file2 = self.host_out_path.joinpath('a/c/f2') 912 device_file1 = self.product_out_path.joinpath('a/b/f1') 913 mod_info = self.create_module_info(modules=[ 914 supported_test_module(shared_libs=['libhello']), 915 multi_config_module( 916 name='libhello', 917 installed=[str(host_file1), str(host_file2), str(device_file1)] 918 ) 919 ]) 920 package_path = self.workspace_out_path 921 922 self.run_generator(mod_info) 923 924 self.assertSymlinkTo( 925 package_path.joinpath('libhello/host/a/b/f1'), host_file1) 926 self.assertSymlinkTo( 927 package_path.joinpath('libhello/host/a/c/f2'), host_file2) 928 self.assertSymlinkTo( 929 package_path.joinpath('libhello/device/a/b/f1'), device_file1) 930 931 def test_create_symlinks_to_installed_path_for_non_tf_testable_deps(self): 932 host_file = self.host_out_path.joinpath('a/b/f1') 933 mod_info = self.create_module_info(modules=[ 934 supported_test_module(shared_libs=['libhello']), 935 host_module( 936 name='libhello', 937 installed=[str(host_file)], 938 auto_test_config=['true'] 939 ) 940 ]) 941 package_path = self.workspace_out_path 942 943 self.run_generator(mod_info) 944 945 self.assertSymlinkTo( 946 package_path.joinpath('libhello/host/a/b/f1'), host_file) 947 948 def test_create_symlinks_to_installed_path_for_lib_with_test_config(self): 949 host_file = self.host_out_path.joinpath('a/b/f1') 950 mod_info = self.create_module_info(modules=[ 951 supported_test_module(shared_libs=['libhello']), 952 host_module( 953 name='libhello', 954 installed=[str(host_file)], 955 path='src/lib' 956 ) 957 ]) 958 self.fs.create_file(Path('src/lib/AndroidTest.xml'), contents='') 959 package_path = self.workspace_out_path 960 961 self.run_generator(mod_info) 962 963 self.assertSymlinkTo( 964 package_path.joinpath('src/lib/libhello/host/a/b/f1'), host_file) 965 966 def test_generate_for_host_only_shared_lib_dependency(self): 967 mod_info = self.create_module_info(modules=[ 968 supported_test_module(shared_libs=['libhello']), 969 host_module(name='libhello'), 970 ]) 971 972 self.run_generator(mod_info) 973 974 self.assertInBuildFile( 975 ' files = select({\n' 976 ' "//bazel/rules:host": glob(["libhello/host/**/*"]),\n' 977 ' }),\n' 978 ) 979 self.assertFileNotInWorkspace('libhello/device') 980 981 def test_generate_for_device_only_shared_lib_dependency(self): 982 mod_info = self.create_module_info(modules=[ 983 supported_test_module(shared_libs=['libhello']), 984 device_module(name='libhello'), 985 ]) 986 987 self.run_generator(mod_info) 988 989 self.assertInBuildFile( 990 ' files = select({\n' 991 ' "//bazel/rules:device": glob(["libhello/device/**/*"]),\n' 992 ' }),\n' 993 ) 994 self.assertFileNotInWorkspace('libhello/host') 995 996 997class DataDependenciesGenerationTest(GenerationTestFixture): 998 """Tests for module data dependencies target generation.""" 999 1000 def test_generate_target_for_data_dependency(self): 1001 mod_info = self.create_module_info(modules=[ 1002 supported_test_module(data_dependencies=['libdata']), 1003 host_module(name='libdata'), 1004 ]) 1005 1006 self.run_generator(mod_info) 1007 1008 self.assertInBuildFile( 1009 ' data = select({\n' 1010 ' "//bazel/rules:host": [\n' 1011 ' "//:libdata",\n' 1012 ' ],\n' 1013 ' }),\n' 1014 ) 1015 self.assertTargetInWorkspace('libdata') 1016 1017 def test_not_generate_target_for_data_file(self): 1018 # Data files are included in "data", but not in "data_dependencies". 1019 mod_info = self.create_module_info(modules=[ 1020 supported_test_module(data=['libdata']), 1021 host_module(name='libdata'), 1022 ]) 1023 1024 self.run_generator(mod_info) 1025 1026 self.assertTargetNotInWorkspace('libdata') 1027 1028 1029@mock.patch.dict('os.environ', {constants.ANDROID_BUILD_TOP: '/'}) 1030def create_empty_module_info(): 1031 with fake_filesystem_unittest.Patcher() as patcher: 1032 # pylint: disable=protected-access 1033 fake_temp_file_name = next(tempfile._get_candidate_names()) 1034 patcher.fs.create_file(fake_temp_file_name, contents='{}') 1035 return module_info.ModuleInfo(module_file=fake_temp_file_name) 1036 1037 1038def create_module_info(modules=None): 1039 mod_info = create_empty_module_info() 1040 modules = modules or [] 1041 1042 for m in modules: 1043 mod_info.name_to_module_info[m['module_name']] = m 1044 1045 return mod_info 1046 1047 1048def host_unit_test_module(**kwargs): 1049 return host_unit_suite(host_test_module(**kwargs)) 1050 1051 1052# We use the below alias in situations where the actual type is irrelevant to 1053# the test as long as it is supported in Bazel mode. 1054supported_test_module = host_unit_test_module 1055 1056 1057def host_test_module(**kwargs): 1058 kwargs.setdefault('name', 'hello_world_test') 1059 return host_only_config(test_module(**kwargs)) 1060 1061 1062def device_test_module(**kwargs): 1063 kwargs.setdefault('name', 'hello_world_test') 1064 return device_only_config(test_module(**kwargs)) 1065 1066 1067def host_module(**kwargs): 1068 m = module(**kwargs) 1069 1070 if 'installed' in kwargs: 1071 return m 1072 1073 return host_only_config(m) 1074 1075 1076def device_module(**kwargs): 1077 m = module(**kwargs) 1078 1079 if 'installed' in kwargs: 1080 return m 1081 1082 return device_only_config(m) 1083 1084 1085def multi_config_module(**kwargs): 1086 m = module(**kwargs) 1087 1088 if 'installed' in kwargs: 1089 return m 1090 1091 return multi_config(m) 1092 1093 1094def test_module(**kwargs): 1095 kwargs.setdefault('name', 'hello_world_test') 1096 return test(module(**kwargs)) 1097 1098 1099# pylint: disable=too-many-arguments 1100def module( 1101 name=None, 1102 path=None, 1103 installed=None, 1104 classes=None, 1105 auto_test_config=None, 1106 shared_libs=None, 1107 dependencies=None, 1108 runtime_dependencies=None, 1109 data=None, 1110 data_dependencies=None, 1111): 1112 name = name or 'libhello' 1113 1114 m = {} 1115 1116 m['module_name'] = name 1117 m['class'] = classes 1118 m['path'] = [path or ''] 1119 m['installed'] = installed or [] 1120 m['is_unit_test'] = 'false' 1121 m['auto_test_config'] = auto_test_config or [] 1122 m['shared_libs'] = shared_libs or [] 1123 m['runtime_dependencies'] = runtime_dependencies or [] 1124 m['dependencies'] = dependencies or [] 1125 m['data'] = data or [] 1126 m['data_dependencies'] = data_dependencies or [] 1127 return m 1128 1129 1130def test(info): 1131 info['auto_test_config'] = ['true'] 1132 return info 1133 1134 1135def shared_lib(info): 1136 info['class'] = ['SHARED_LIBRARIES'] 1137 return info 1138 1139 1140def rlib(info): 1141 info['class'] = ['RLIB_LIBRARIES'] 1142 info['installed'] = [] 1143 return info 1144 1145 1146def dylib(info): 1147 info['class'] = ['DYLIB_LIBRARIES'] 1148 return info 1149 1150 1151def host_unit_suite(info): 1152 info = test(info) 1153 info.setdefault('compatibility_suites', []).append('host-unit-tests') 1154 return info 1155 1156 1157def multi_config(info): 1158 name = info.get('module_name', 'lib') 1159 info['installed'] = [ 1160 f'out/host/linux-x86/{name}/{name}.jar', 1161 f'out/product/vsoc_x86/{name}/{name}.apk', 1162 ] 1163 info['supported_variants'] = [ 1164 'DEVICE', 1165 'HOST', 1166 ] 1167 return info 1168 1169 1170def host_only_config(info): 1171 name = info.get('module_name', 'lib') 1172 info['installed'] = [ 1173 f'out/host/linux-x86/{name}/{name}.jar', 1174 ] 1175 info['supported_variants'] = [ 1176 'HOST', 1177 ] 1178 return info 1179 1180 1181def device_only_config(info): 1182 name = info.get('module_name', 'lib') 1183 info['installed'] = [ 1184 f'out/product/vsoc_x86/{name}/{name}.jar', 1185 ] 1186 info['supported_variants'] = [ 1187 'DEVICE', 1188 ] 1189 return info 1190 1191 1192class PackageTest(fake_filesystem_unittest.TestCase): 1193 """Tests for Package.""" 1194 1195 class FakeTarget(bazel_mode.Target): 1196 """Fake target used for tests.""" 1197 1198 def __init__(self, name, imports=None): 1199 self._name = name 1200 self._imports = imports or set() 1201 1202 def name(self): 1203 return self._name 1204 1205 def required_imports(self): 1206 return self._imports 1207 1208 def write_to_build_file(self, f): 1209 f.write(f'{self._name}\n') 1210 1211 1212 def setUp(self): 1213 self.setUpPyfakefs() 1214 self.workspace_out_path = Path('/workspace_out_path') 1215 self.workspace_out_path.mkdir() 1216 1217 def test_raise_when_adding_existing_target(self): 1218 target_name = '<fake_target>' 1219 package = bazel_mode.Package('p') 1220 package.add_target(self.FakeTarget(target_name)) 1221 1222 with self.assertRaises(Exception) as context: 1223 package.add_target(self.FakeTarget(target_name)) 1224 1225 self.assertIn(target_name, str(context.exception)) 1226 1227 def test_write_build_file_in_package_dir(self): 1228 package_path = 'abc/def' 1229 package = bazel_mode.Package(package_path) 1230 expected_path = self.workspace_out_path.joinpath( 1231 package_path, 'BUILD.bazel') 1232 1233 package.generate(self.workspace_out_path) 1234 1235 self.assertTrue(expected_path.exists()) 1236 1237 def test_write_load_statements_in_sorted_order(self): 1238 package = bazel_mode.Package('p') 1239 target1 = self.FakeTarget('target1', imports={ 1240 bazel_mode.Import('z.bzl', 'symbol1'), 1241 }) 1242 target2 = self.FakeTarget('target2', imports={ 1243 bazel_mode.Import('a.bzl', 'symbol2'), 1244 }) 1245 package.add_target(target1) 1246 package.add_target(target2) 1247 1248 package.generate(self.workspace_out_path) 1249 1250 self.assertIn('load("a.bzl", "symbol2")\nload("z.bzl", "symbol1")\n\n', 1251 self.package_build_file_text(package)) 1252 1253 def test_write_load_statements_with_symbols_grouped_by_bzl(self): 1254 package = bazel_mode.Package('p') 1255 target1 = self.FakeTarget('target1', imports={ 1256 bazel_mode.Import('a.bzl', 'symbol1'), 1257 bazel_mode.Import('a.bzl', 'symbol3'), 1258 }) 1259 target2 = self.FakeTarget('target2', imports={ 1260 bazel_mode.Import('a.bzl', 'symbol2'), 1261 }) 1262 package.add_target(target1) 1263 package.add_target(target2) 1264 1265 package.generate(self.workspace_out_path) 1266 1267 self.assertIn('load("a.bzl", "symbol1", "symbol2", "symbol3")\n\n', 1268 self.package_build_file_text(package)) 1269 1270 def test_write_targets_in_add_order(self): 1271 package = bazel_mode.Package('p') 1272 target1 = self.FakeTarget('target1') 1273 target2 = self.FakeTarget('target2') 1274 package.add_target(target2) # Added out of order. 1275 package.add_target(target1) 1276 1277 package.generate(self.workspace_out_path) 1278 1279 self.assertIn('target2\n\ntarget1\n', 1280 self.package_build_file_text(package)) 1281 1282 def test_generate_parent_package_when_nested_exists(self): 1283 parent_path = Path('parent') 1284 parent = bazel_mode.Package(parent_path.name) 1285 nested = bazel_mode.Package(parent_path.joinpath('nested')) 1286 nested.generate(self.workspace_out_path) 1287 1288 parent.generate(self.workspace_out_path) 1289 1290 self.assertTrue(self.workspace_out_path.joinpath(parent_path).is_dir()) 1291 1292 def package_build_file_text(self, package): 1293 return self.workspace_out_path.joinpath( 1294 package.path, 'BUILD.bazel').read_text(encoding='utf8') 1295 1296 1297class DecorateFinderMethodTest(GenerationTestFixture): 1298 """Tests for _decorate_find_method().""" 1299 1300 def setUp(self): 1301 self.setUpPyfakefs() 1302 1303 def test_host_unit_test_with_host_arg_runner_is_overridden(self): 1304 original_find_method = lambda obj, test_id:( 1305 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1306 runner=ATEST_TF_RUNNER)) 1307 mod_info = self.create_module_info(modules=[ 1308 host_unit_test_module(name=MODULE_NAME) 1309 ]) 1310 original_finder = self.create_finder(mod_info, original_find_method) 1311 new_finder = bazel_mode.create_new_finder( 1312 mod_info, original_finder, host=True) 1313 1314 test_infos = new_finder.find_method( 1315 new_finder.test_finder_instance, MODULE_NAME) 1316 1317 self.assertEqual(len(test_infos), 1) 1318 self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER) 1319 1320 def test_host_unit_test_without_host_arg_runner_is_overridden(self): 1321 original_find_method = lambda obj, test_id:( 1322 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1323 runner=ATEST_TF_RUNNER)) 1324 mod_info = self.create_module_info(modules=[ 1325 host_unit_test_module(name=MODULE_NAME) 1326 ]) 1327 original_finder = self.create_finder(mod_info, original_find_method) 1328 new_finder = bazel_mode.create_new_finder( 1329 mod_info, original_finder, host=False) 1330 1331 test_infos = new_finder.find_method( 1332 new_finder.test_finder_instance, MODULE_NAME) 1333 1334 self.assertEqual(len(test_infos), 1) 1335 self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER) 1336 1337 def test_device_test_with_host_arg_runner_is_preserved(self): 1338 original_find_method = lambda obj, test_id:( 1339 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1340 runner=ATEST_TF_RUNNER)) 1341 mod_info = self.create_module_info(modules=[ 1342 device_test_module(name=MODULE_NAME) 1343 ]) 1344 original_finder = self.create_finder(mod_info, original_find_method) 1345 new_finder = bazel_mode.create_new_finder( 1346 mod_info, 1347 original_finder, 1348 host=True, 1349 enabled_features=[ 1350 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST 1351 ] 1352 ) 1353 1354 test_infos = new_finder.find_method( 1355 new_finder.test_finder_instance, MODULE_NAME) 1356 1357 self.assertEqual(len(test_infos), 1) 1358 self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER) 1359 1360 def test_device_test_without_host_arg_runner_is_overridden(self): 1361 original_find_method = lambda obj, test_id:( 1362 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1363 runner=ATEST_TF_RUNNER)) 1364 mod_info = self.create_module_info(modules=[ 1365 device_test_module(name=MODULE_NAME) 1366 ]) 1367 original_finder = self.create_finder(mod_info, original_find_method) 1368 new_finder = bazel_mode.create_new_finder( 1369 mod_info, 1370 original_finder, 1371 host=False, 1372 enabled_features=[ 1373 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST 1374 ] 1375 ) 1376 1377 test_infos = new_finder.find_method( 1378 new_finder.test_finder_instance, MODULE_NAME) 1379 1380 self.assertEqual(len(test_infos), 1) 1381 self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER) 1382 1383 def test_multi_config_test_with_host_arg_runner_is_overridden(self): 1384 original_find_method = lambda obj, test_id:( 1385 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1386 runner=ATEST_TF_RUNNER)) 1387 mod_info = self.create_module_info(modules=[ 1388 multi_config(supported_test_module(name=MODULE_NAME)) 1389 ]) 1390 original_finder = self.create_finder(mod_info, original_find_method) 1391 new_finder = bazel_mode.create_new_finder( 1392 mod_info, 1393 original_finder, 1394 host=True, 1395 enabled_features=[ 1396 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST 1397 ] 1398 ) 1399 1400 test_infos = new_finder.find_method( 1401 new_finder.test_finder_instance, MODULE_NAME) 1402 1403 self.assertEqual(len(test_infos), 1) 1404 self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER) 1405 1406 def test_multi_config_test_without_host_arg_runner_is_overridden(self): 1407 original_find_method = lambda obj, test_id:( 1408 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1409 runner=ATEST_TF_RUNNER)) 1410 mod_info = self.create_module_info(modules=[ 1411 multi_config(supported_test_module(name=MODULE_NAME)) 1412 ]) 1413 original_finder = self.create_finder(mod_info, original_find_method) 1414 new_finder = bazel_mode.create_new_finder( 1415 mod_info, 1416 original_finder, 1417 host=False, 1418 enabled_features=[ 1419 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST 1420 ] 1421 ) 1422 1423 test_infos = new_finder.find_method( 1424 new_finder.test_finder_instance, MODULE_NAME) 1425 1426 self.assertEqual(len(test_infos), 1) 1427 self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER) 1428 1429 def test_host_non_unit_test_with_host_arg_runner_is_preserved(self): 1430 original_find_method = lambda obj, test_id:( 1431 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1432 runner=ATEST_TF_RUNNER)) 1433 mod_info = self.create_module_info(modules=[ 1434 host_test_module(name=MODULE_NAME) 1435 ]) 1436 original_finder = self.create_finder(mod_info, original_find_method) 1437 new_finder = bazel_mode.create_new_finder( 1438 mod_info, 1439 original_finder, 1440 host=True, 1441 enabled_features=[ 1442 bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST 1443 ] 1444 ) 1445 1446 test_infos = new_finder.find_method( 1447 new_finder.test_finder_instance, MODULE_NAME) 1448 1449 self.assertEqual(len(test_infos), 1) 1450 self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER) 1451 1452 def test_disable_device_driven_test_feature_runner_is_preserved(self): 1453 original_find_method = lambda obj, test_id:( 1454 self.create_single_test_infos(obj, test_id, test_name=MODULE_NAME, 1455 runner=ATEST_TF_RUNNER)) 1456 mod_info = self.create_module_info(modules=[ 1457 device_test_module(name=MODULE_NAME) 1458 ]) 1459 original_finder = self.create_finder(mod_info, original_find_method) 1460 new_finder = bazel_mode.create_new_finder( 1461 mod_info, original_finder, host=False) 1462 1463 test_infos = new_finder.find_method( 1464 new_finder.test_finder_instance, MODULE_NAME) 1465 1466 self.assertEqual(len(test_infos), 1) 1467 self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER) 1468 1469 # pylint: disable=unused-argument 1470 def create_single_test_infos(self, obj, test_id, test_name=MODULE_NAME, 1471 runner=ATEST_TF_RUNNER): 1472 """Create list of test_info.TestInfo.""" 1473 return [test_info.TestInfo(test_name, runner, MODULE_BUILD_TARGETS)] 1474 1475 def create_finder(self, mod_info, find_method): 1476 return test_finder_base.Finder( 1477 example_finder.ExampleFinder(mod_info), 1478 find_method, 'FINDER_NAME') 1479 1480class BazelTestRunnerTest(unittest.TestCase): 1481 """Tests for BazelTestRunner.""" 1482 1483 def test_return_empty_build_reqs_when_no_test_infos(self): 1484 run_command = self.mock_run_command(side_effect=Exception('')) 1485 runner = self.create_bazel_test_runner( 1486 modules=[ 1487 supported_test_module(name='test1', path='path1'), 1488 ], 1489 test_infos=[], 1490 run_command=run_command, 1491 ) 1492 1493 reqs = runner.get_test_runner_build_reqs() 1494 1495 self.assertFalse(reqs) 1496 1497 def test_query_bazel_test_targets_deps_with_host_arg(self): 1498 run_command = self.mock_run_command() 1499 runner = self.create_bazel_test_runner( 1500 modules=[ 1501 multi_config(host_unit_test_module(name='test1', path='path1')), 1502 multi_config(host_unit_test_module(name='test2', path='path2')), 1503 ], 1504 test_infos = [ 1505 test_info_of('test2'), 1506 test_info_of('test1'), # Intentionally out of order. 1507 ], 1508 run_command=run_command, 1509 host=True, 1510 ) 1511 1512 runner.get_test_runner_build_reqs() 1513 1514 call_args = run_command.call_args[0][0] 1515 self.assertIn( 1516 'deps(tests(//path1:test1_host + //path2:test2_host))', 1517 call_args, 1518 ) 1519 1520 def test_query_bazel_test_targets_deps_without_host_arg(self): 1521 run_command = self.mock_run_command() 1522 runner = self.create_bazel_test_runner( 1523 modules=[ 1524 multi_config(host_unit_test_module(name='test1', path='path1')), 1525 host_unit_test_module(name='test2', path='path2'), 1526 ], 1527 test_infos = [ 1528 test_info_of('test2'), 1529 test_info_of('test1'), 1530 ], 1531 run_command=run_command, 1532 ) 1533 1534 runner.get_test_runner_build_reqs() 1535 1536 call_args = run_command.call_args[0][0] 1537 self.assertIn( 1538 'deps(tests(//path1:test1_device + //path2:test2_host))', 1539 call_args, 1540 ) 1541 1542 def test_trim_whitespace_in_bazel_query_output(self): 1543 run_command = self.mock_run_command( 1544 return_value='\n'.join([' test1 ', 'test2 ', ' '])) 1545 runner = self.create_bazel_test_runner( 1546 modules=[ 1547 supported_test_module(name='test1', path='path1'), 1548 ], 1549 test_infos = [test_info_of('test1')], 1550 run_command=run_command, 1551 ) 1552 1553 reqs = runner.get_test_runner_build_reqs() 1554 1555 self.assertSetEqual({'test1', 'test2'}, reqs) 1556 1557 def test_generate_single_run_command(self): 1558 test_infos = [test_info_of('test1')] 1559 runner = self.create_bazel_test_runner_for_tests(test_infos) 1560 1561 cmd = runner.generate_run_commands(test_infos, {}) 1562 1563 self.assertEqual(1, len(cmd)) 1564 1565 def test_generate_run_command_containing_targets_with_host_arg(self): 1566 test_infos = [test_info_of('test1'), test_info_of('test2')] 1567 runner = self.create_bazel_test_runner( 1568 [ 1569 multi_config(host_unit_test_module(name='test1', path='path')), 1570 multi_config(host_unit_test_module(name='test2', path='path')), 1571 ], 1572 test_infos, 1573 host=True 1574 ) 1575 1576 cmd = runner.generate_run_commands(test_infos, {}) 1577 1578 self.assertTokensIn(['//path:test1_host', '//path:test2_host'], cmd[0]) 1579 1580 def test_generate_run_command_containing_targets_without_host_arg(self): 1581 test_infos = [test_info_of('test1'), test_info_of('test2')] 1582 runner = self.create_bazel_test_runner( 1583 [ 1584 multi_config(host_unit_test_module(name='test1', path='path')), 1585 host_unit_test_module(name='test2', path='path'), 1586 ], 1587 test_infos, 1588 ) 1589 1590 cmd = runner.generate_run_commands(test_infos, {}) 1591 1592 self.assertTokensIn(['//path:test1_device', '//path:test2_host'], 1593 cmd[0]) 1594 1595 def test_generate_run_command_with_multi_bazel_args(self): 1596 test_infos = [test_info_of('test1')] 1597 runner = self.create_bazel_test_runner_for_tests(test_infos) 1598 extra_args = {constants.BAZEL_ARG: [['--option1=value1'], 1599 ['--option2=value2']]} 1600 1601 cmd = runner.generate_run_commands(test_infos, extra_args) 1602 1603 self.assertTokensIn(['--option1=value1', '--option2=value2'], cmd[0]) 1604 1605 def test_generate_run_command_with_multi_custom_args(self): 1606 test_infos = [test_info_of('test1')] 1607 runner = self.create_bazel_test_runner_for_tests(test_infos) 1608 extra_args = {constants.CUSTOM_ARGS: ['-hello', '--world=value']} 1609 1610 cmd = runner.generate_run_commands(test_infos, extra_args) 1611 1612 self.assertTokensIn(['--test_arg=-hello', 1613 '--test_arg=--world=value'], cmd[0]) 1614 1615 def test_generate_run_command_with_custom_and_bazel_args(self): 1616 test_infos = [test_info_of('test1')] 1617 runner = self.create_bazel_test_runner_for_tests(test_infos) 1618 extra_args = {constants.CUSTOM_ARGS: ['-hello', '--world=value'], 1619 constants.BAZEL_ARG: [['--option1=value1']]} 1620 1621 cmd = runner.generate_run_commands(test_infos, extra_args) 1622 1623 self.assertTokensIn(['--test_arg=-hello', 1624 '--test_arg=--world=value', 1625 '--option1=value1'], cmd[0]) 1626 1627 def test_generate_run_command_with_tf_supported_host_arg(self): 1628 test_infos = [test_info_of('test1')] 1629 runner = self.create_bazel_test_runner_for_tests(test_infos) 1630 extra_args = {constants.HOST: True} 1631 1632 cmd = runner.generate_run_commands(test_infos, extra_args) 1633 1634 self.assertTokensIn(['--test_arg=-n', 1635 '--test_arg=--prioritize-host-config', 1636 '--test_arg=--skip-host-arch-check'], cmd[0]) 1637 1638 def test_generate_run_command_with_iterations_args(self): 1639 test_infos = [test_info_of('test1')] 1640 runner = self.create_bazel_test_runner_for_tests(test_infos) 1641 extra_args = {constants.ITERATIONS: 2} 1642 1643 cmd = runner.generate_run_commands(test_infos, extra_args) 1644 1645 self.assertTokensIn(['--runs_per_test=2'], cmd[0]) 1646 self.assertNotIn('--test_arg=--retry-strategy', shlex.split(cmd[0])) 1647 1648 def test_generate_run_command_with_testinfo_filter(self): 1649 test_filter = test_filter_of('class1', ['method1']) 1650 test_infos = [test_info_of('test1', test_filters=[test_filter])] 1651 runner = self.create_bazel_test_runner_for_tests(test_infos) 1652 1653 cmd = runner.generate_run_commands(test_infos, {}) 1654 1655 self.assertTokensIn(['--test_arg=--atest-include-filter', 1656 '--test_arg=test1:class1#method1'], cmd[0]) 1657 1658 def test_generate_run_command_with_bes_publish_enabled(self): 1659 test_infos = [test_info_of('test1')] 1660 extra_args = { 1661 constants.BAZEL_MODE_FEATURES: [ 1662 bazel_mode.Features.EXPERIMENTAL_BES_PUBLISH 1663 ] 1664 } 1665 build_metadata = bazel_mode.BuildMetadata( 1666 'master', 'aosp_cf_x86_64_phone-userdebug') 1667 env = { 1668 'ATEST_BAZELRC': '/dir/atest.bazelrc', 1669 'ATEST_BAZEL_BES_PUBLISH_CONFIG': 'bes_publish' 1670 } 1671 runner = self.create_bazel_test_runner_for_tests( 1672 test_infos, build_metadata=build_metadata, env=env) 1673 1674 cmd = runner.generate_run_commands( 1675 test_infos, 1676 extra_args, 1677 ) 1678 1679 self.assertTokensIn([ 1680 '--bazelrc=/dir/atest.bazelrc', 1681 '--config=bes_publish', 1682 '--build_metadata=ab_branch=master', 1683 '--build_metadata=ab_target=aosp_cf_x86_64_phone-userdebug' 1684 ], cmd[0]) 1685 1686 def create_bazel_test_runner(self, 1687 modules, 1688 test_infos, 1689 run_command=None, 1690 host=False, 1691 build_metadata=None, 1692 env=None): 1693 return bazel_mode.BazelTestRunner( 1694 'result_dir', 1695 mod_info=create_module_info(modules), 1696 test_infos=test_infos, 1697 src_top=Path('/src'), 1698 workspace_path=Path('/src/workspace'), 1699 run_command=run_command or self.mock_run_command(), 1700 extra_args={constants.HOST: host}, 1701 build_metadata = build_metadata, 1702 env = env 1703 ) 1704 1705 def create_bazel_test_runner_for_tests(self, 1706 test_infos, 1707 build_metadata=None, 1708 env=None): 1709 return self.create_bazel_test_runner( 1710 modules=[supported_test_module(name=t.test_name, path='path') 1711 for t in test_infos], 1712 test_infos=test_infos, 1713 build_metadata=build_metadata, 1714 env=env 1715 ) 1716 1717 def mock_run_command(self, **kwargs): 1718 return mock.create_autospec(bazel_mode.default_run_command, **kwargs) 1719 1720 def assertTokensIn(self, expected_tokens, s): 1721 tokens = shlex.split(s) 1722 for token in expected_tokens: 1723 self.assertIn(token, tokens) 1724 1725 1726class FeatureParserTest(unittest.TestCase): 1727 """Tests for parsing Bazel mode feature flags.""" 1728 1729 def test_parse_args_with_bazel_mode_feature(self): 1730 parser = argparse.ArgumentParser() 1731 bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features') 1732 # pylint: disable=no-member 1733 args = parser.parse_args([bazel_mode.Features.NULL_FEATURE.arg_flag]) 1734 1735 self.assertListEqual([bazel_mode.Features.NULL_FEATURE], 1736 args.bazel_mode_features) 1737 1738 def test_parse_args_without_bazel_mode_feature(self): 1739 parser = argparse.ArgumentParser() 1740 parser.add_argument('--foo', 1741 action='append_const', 1742 const='foo', 1743 dest='foo') 1744 bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features') 1745 args = parser.parse_args(['--foo']) 1746 1747 self.assertIsNone(args.bazel_mode_features) 1748 1749 1750def test_info_of(module_name, test_filters=None): 1751 return test_info.TestInfo( 1752 module_name, BAZEL_RUNNER, [], 1753 data={constants.TI_FILTER: frozenset(test_filters)} 1754 if test_filters else None) 1755 1756 1757def test_filter_of(class_name, methods=None): 1758 return test_info.TestFilter( 1759 class_name, frozenset(methods) if methods else frozenset()) 1760 1761 1762if __name__ == '__main__': 1763 unittest.main() 1764