xref: /aosp_15_r20/tools/asuite/atest/bazel_mode_unittest.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
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
23from io import StringIO
24from pathlib import Path
25import re
26import shlex
27import shutil
28import subprocess
29import tempfile
30from typing import List
31import unittest
32from unittest import mock
33from atest import bazel_mode
34from atest import constants
35from atest import module_info
36from atest.test_finders import example_finder, test_finder_base, test_info
37from atest.test_runners import atest_tf_test_runner
38from pyfakefs import fake_filesystem_unittest
39
40
41ATEST_TF_RUNNER = atest_tf_test_runner.AtestTradefedTestRunner.NAME
42BAZEL_RUNNER = bazel_mode.BazelTestRunner.NAME
43MODULE_BUILD_TARGETS = {'foo1', 'foo2', 'foo3'}
44MODULE_NAME = 'foo'
45
46
47class GenerationTestFixture(fake_filesystem_unittest.TestCase):
48  """Fixture for workspace generation tests."""
49
50  def setUp(self):
51    self.setUpPyfakefs()
52
53    self._src_root_path = Path('/src')
54    self.out_dir_path = self._src_root_path.joinpath('out')
55    self.out_dir_path.mkdir(parents=True)
56    self.product_out_path = self.out_dir_path.joinpath('product')
57    self.host_out_path = self.out_dir_path.joinpath('host')
58    self.workspace_out_path = self.out_dir_path.joinpath('workspace')
59
60    self._resource_root = self._src_root_path.joinpath(
61        'tools/asuite/atest/bazel'
62    )
63
64    self.workspace_md5_checksum = self.workspace_out_path.joinpath(
65        'workspace_md5_checksum'
66    )
67    self.resource_manager = bazel_mode.ResourceManager(
68        src_root_path=self._src_root_path,
69        resource_root_path=self._resource_root,
70        product_out_path=self.product_out_path,
71        md5_checksum_file_path=self.workspace_md5_checksum,
72    )
73
74    bazel_rules = self.resource_manager.get_resource_file_path('rules')
75    bazel_rules.mkdir(parents=True)
76    self.rules_bzl_file = bazel_rules.joinpath('rules.bzl')
77    self.rules_bzl_file.touch()
78
79    bazel_configs = self.resource_manager.get_resource_file_path('configs')
80    bazel_configs.mkdir(parents=True)
81    bazel_configs.joinpath('configs.bzl').touch()
82
83    self.resource_manager.get_resource_file_path('WORKSPACE').touch()
84    self.resource_manager.get_resource_file_path('bazelrc').touch()
85    self.resource_manager.get_resource_file_path('bazel.sh').touch()
86
87    rules_python = self.resource_manager.get_src_file_path(
88        'external/bazelbuild-rules_python'
89    )
90    rules_python.mkdir(parents=True)
91    rules_java = self.resource_manager.get_src_file_path(
92        'external/bazelbuild-rules_java'
93    )
94    rules_java.mkdir(parents=True)
95
96  def create_workspace_generator(
97      self,
98      modules=None,
99      enabled_features=None,
100      jdk_path=None,
101  ):
102    mod_info = self.create_module_info(modules)
103
104    generator = bazel_mode.WorkspaceGenerator(
105        resource_manager=self.resource_manager,
106        workspace_out_path=self.workspace_out_path,
107        host_out_path=self.host_out_path,
108        build_out_dir=self.out_dir_path,
109        mod_info=mod_info,
110        jdk_path=jdk_path,
111        enabled_features=enabled_features,
112    )
113
114    return generator
115
116  def run_generator(self, mod_info, enabled_features=None, jdk_path=None):
117    generator = bazel_mode.WorkspaceGenerator(
118        resource_manager=self.resource_manager,
119        workspace_out_path=self.workspace_out_path,
120        host_out_path=self.host_out_path,
121        build_out_dir=self.out_dir_path,
122        mod_info=mod_info,
123        jdk_path=jdk_path,
124        enabled_features=enabled_features,
125    )
126
127    generator.generate()
128
129  # pylint: disable=protected-access
130  def create_empty_module_info(self):
131    fake_temp_file = self.product_out_path.joinpath(
132        next(tempfile._get_candidate_names())
133    )
134    self.fs.create_file(fake_temp_file, contents='{}')
135    return module_info.load_from_file(module_file=fake_temp_file)
136
137  def create_module_info(self, modules=None):
138    mod_info = self.create_empty_module_info()
139    modules = modules or []
140
141    prerequisites = frozenset().union(
142        bazel_mode.TestTarget.DEVICE_TEST_PREREQUISITES,
143        bazel_mode.TestTarget.DEVICELESS_TEST_PREREQUISITES,
144    )
145
146    for module_name in prerequisites:
147      info = host_module(name=module_name, path='prebuilts')
148      info[constants.MODULE_INFO_ID] = module_name
149      mod_info.name_to_module_info[module_name] = info
150
151    for m in modules:
152      m[constants.MODULE_INFO_ID] = m['module_name']
153      mod_info.name_to_module_info[m['module_name']] = m
154      for path in m['path']:
155        if path in mod_info.path_to_module_info:
156          mod_info.path_to_module_info[path].append(m)
157        else:
158          mod_info.path_to_module_info[path] = [m]
159
160    return mod_info
161
162  def assertSymlinkTo(self, symlink_path, target_path):
163    self.assertEqual(symlink_path.resolve(strict=False), target_path)
164
165  def assertTargetInWorkspace(self, name, package=''):
166    build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
167    contents = build_file.read_text(encoding='utf8')
168    occurrences = len(self.find_target_by_name(name, contents))
169
170    if occurrences == 1:
171      return
172
173    cardinality = 'Multiple' if occurrences else 'Zero'
174    self.fail(f"{cardinality} targets named '{name}' found in '{contents}'")
175
176  def assertTargetNotInWorkspace(self, name, package=''):
177    build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
178
179    if not build_file.exists():
180      return
181
182    contents = build_file.read_text(encoding='utf8')
183    matches = self.find_target_by_name(name, contents)
184
185    if not matches:
186      return
187
188    self.fail(f"Unexpectedly found target(s) named '{name}' in '{contents}'")
189
190  def assertInBuildFile(self, substring, package=''):
191    build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
192    self.assertIn(substring, build_file.read_text(encoding='utf8'))
193
194  def assertNotInBuildFile(self, substring, package=''):
195    build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
196    self.assertNotIn(substring, build_file.read_text(encoding='utf8'))
197
198  def assertFileInWorkspace(self, relative_path, package=''):
199    path = self.workspace_out_path.joinpath(package, relative_path)
200    self.assertTrue(path.exists())
201
202  def assertDirInWorkspace(self, relative_path, package=''):
203    path = self.workspace_out_path.joinpath(package, relative_path)
204    self.assertTrue(path.is_dir())
205
206  def assertFileNotInWorkspace(self, relative_path, package=''):
207    path = self.workspace_out_path.joinpath(package, relative_path)
208    self.assertFalse(path.exists())
209
210  def find_target_by_name(self, name: str, contents: str) -> List[str]:
211    return re.findall(rf'\bname\s*=\s*"{name}"', contents)
212
213  def add_device_def_to_filesystem(self):
214    bazel_device_def = self.resource_manager.get_resource_file_path(
215        'device_def'
216    )
217    bazel_device_def.mkdir(parents=True)
218    bazel_device_def.joinpath('device_def.bzl').touch()
219
220
221class BasicWorkspaceGenerationTest(GenerationTestFixture):
222  """Tests for basic workspace generation and update."""
223
224  def test_generate_workspace_when_nonexistent(self):
225    workspace_generator = self.create_workspace_generator()
226    shutil.rmtree(workspace_generator.workspace_out_path, ignore_errors=True)
227
228    workspace_generator.generate()
229
230    self.assertTrue(workspace_generator.workspace_out_path.is_dir())
231
232  def test_regenerate_workspace_when_features_changed(self):
233    workspace_generator = self.create_workspace_generator(
234        enabled_features={bazel_mode.Features.NULL_FEATURE}
235    )
236    workspace_generator.generate()
237    workspace_stat = workspace_generator.workspace_out_path.stat()
238
239    workspace_generator = self.create_workspace_generator()
240    workspace_generator.generate()
241    new_workspace_stat = workspace_generator.workspace_out_path.stat()
242
243    self.assertNotEqual(workspace_stat, new_workspace_stat)
244
245  def test_not_regenerate_when_feature_does_not_affect_workspace(self):
246    workspace_generator = self.create_workspace_generator(
247        enabled_features={bazel_mode.Features.NULL_FEATURE}
248    )
249    workspace_generator.generate()
250    workspace_stat = workspace_generator.workspace_out_path.stat()
251
252    parser = argparse.ArgumentParser()
253    bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features')
254    # pylint: disable=no-member
255    args = parser.parse_args([
256        bazel_mode.Features.NULL_FEATURE.arg_flag,
257        '--experimental-bes-publish',
258    ])
259    workspace_generator = self.create_workspace_generator(
260        enabled_features=set(args.bazel_mode_features)
261    )
262    workspace_generator.generate()
263    new_workspace_stat = workspace_generator.workspace_out_path.stat()
264
265    self.assertEqual(workspace_stat, new_workspace_stat)
266
267  def test_not_regenerate_workspace_when_features_unchanged(self):
268    workspace_generator = self.create_workspace_generator(
269        enabled_features={bazel_mode.Features.NULL_FEATURE}
270    )
271    workspace_generator.generate()
272    workspace_stat = workspace_generator.workspace_out_path.stat()
273
274    workspace_generator = self.create_workspace_generator(
275        enabled_features={bazel_mode.Features.NULL_FEATURE}
276    )
277    workspace_generator.generate()
278    new_workspace_stat = workspace_generator.workspace_out_path.stat()
279
280    self.assertEqual(workspace_stat, new_workspace_stat)
281
282  def test_regenerate_workspace_when_module_info_deleted(self):
283    workspace_generator = self.create_workspace_generator()
284    workspace_generator.generate()
285    workspace_stat = workspace_generator.workspace_out_path.stat()
286
287    workspace_generator.mod_info.mod_info_file_path.unlink()
288    workspace_generator = self.create_workspace_generator()
289    workspace_generator.generate()
290
291    new_workspace_stat = workspace_generator.workspace_out_path.stat()
292    self.assertNotEqual(workspace_stat, new_workspace_stat)
293
294  def test_not_regenerate_workspace_when_module_info_unchanged(self):
295    workspace_generator1 = self.create_workspace_generator()
296    workspace_generator1.generate()
297    workspace_stat = workspace_generator1.workspace_out_path.stat()
298
299    workspace_generator2 = self.create_workspace_generator()
300    workspace_generator2.generate()
301    new_workspace_stat = workspace_generator2.workspace_out_path.stat()
302
303    self.assertEqual(workspace_stat, new_workspace_stat)
304
305  def test_not_regenerate_workspace_when_module_only_touched(self):
306    workspace_generator = self.create_workspace_generator()
307    workspace_generator.generate()
308    workspace_stat = workspace_generator.workspace_out_path.stat()
309
310    Path(workspace_generator.mod_info.mod_info_file_path).touch()
311    workspace_generator = self.create_workspace_generator()
312    workspace_generator.generate()
313
314    new_workspace_stat = workspace_generator.workspace_out_path.stat()
315    self.assertEqual(workspace_stat, new_workspace_stat)
316
317  def test_regenerate_workspace_when_module_info_changed(self):
318    workspace_generator = self.create_workspace_generator()
319    workspace_generator.generate()
320    workspace_stat = workspace_generator.workspace_out_path.stat()
321
322    mod_info_file_path = workspace_generator.mod_info.mod_info_file_path
323    with open(mod_info_file_path, 'a', encoding='utf8') as f:
324      f.write(' ')
325    workspace_generator = self.create_workspace_generator()
326    workspace_generator.generate()
327
328    new_workspace_stat = workspace_generator.workspace_out_path.stat()
329    self.assertNotEqual(workspace_stat, new_workspace_stat)
330
331  def test_regenerate_workspace_when_md5_file_removed(self):
332    workspace_generator = self.create_workspace_generator()
333    workspace_generator.generate()
334    workspace_stat = workspace_generator.workspace_out_path.stat()
335
336    self.workspace_md5_checksum.unlink()
337    workspace_generator = self.create_workspace_generator()
338    workspace_generator.generate()
339    new_workspace_stat = workspace_generator.workspace_out_path.stat()
340
341    self.assertNotEqual(workspace_stat, new_workspace_stat)
342
343  def test_regenerate_workspace_when_md5_file_is_broken(self):
344    workspace_generator = self.create_workspace_generator()
345    workspace_generator.generate()
346    workspace_stat = workspace_generator.workspace_out_path.stat()
347
348    self.workspace_md5_checksum.write_text('broken checksum file')
349    workspace_generator = self.create_workspace_generator()
350    workspace_generator.generate()
351    new_workspace_stat = workspace_generator.workspace_out_path.stat()
352
353    self.assertNotEqual(workspace_stat, new_workspace_stat)
354
355  def test_not_regenerate_workspace_when_workspace_files_unaffected(self):
356    workspace_generator = self.create_workspace_generator()
357    workspace_generator.generate()
358    workspace_stat = workspace_generator.workspace_out_path.stat()
359
360    workspace_generator = self.create_workspace_generator()
361    workspace_generator.generate()
362    new_workspace_stat = workspace_generator.workspace_out_path.stat()
363
364    self.assertEqual(workspace_stat, new_workspace_stat)
365
366  def test_scrub_old_workspace_when_regenerating(self):
367    workspace_generator = self.create_workspace_generator()
368    workspace_generator.generate()
369    some_file = workspace_generator.workspace_out_path.joinpath('some_file')
370    some_file.touch()
371    self.assertTrue(some_file.is_file())
372
373    # Remove the module_info file to regenerate the workspace.
374    workspace_generator.mod_info.mod_info_file_path.unlink()
375    workspace_generator = self.create_workspace_generator()
376    workspace_generator.generate()
377
378    self.assertFalse(some_file.is_file())
379
380  def test_regenerate_workspace_when_resource_file_changed(self):
381    workspace_generator = self.create_workspace_generator()
382    workspace_generator.generate()
383    workspace_stat = workspace_generator.workspace_out_path.stat()
384
385    with open(self.rules_bzl_file, 'a', encoding='utf8') as f:
386      f.write(' ')
387    workspace_generator = self.create_workspace_generator()
388    workspace_generator.generate()
389
390    new_workspace_stat = workspace_generator.workspace_out_path.stat()
391    self.assertNotEqual(workspace_stat, new_workspace_stat)
392
393  def test_not_regenerate_workspace_when_resource_file_only_touched(self):
394    workspace_generator = self.create_workspace_generator()
395    workspace_generator.generate()
396    workspace_stat = workspace_generator.workspace_out_path.stat()
397
398    self.rules_bzl_file.touch()
399    workspace_generator = self.create_workspace_generator()
400    workspace_generator.generate()
401
402    new_workspace_stat = workspace_generator.workspace_out_path.stat()
403    self.assertEqual(workspace_stat, new_workspace_stat)
404
405  def test_copy_workspace_resources(self):
406    gen = self.create_workspace_generator()
407
408    gen.generate()
409
410    self.assertFileInWorkspace('WORKSPACE')
411    self.assertFileInWorkspace('.bazelrc')
412    self.assertDirInWorkspace('bazel/rules')
413    self.assertDirInWorkspace('bazel/configs')
414
415  def test_generated_target_name(self):
416    mod_info = self.create_module_info(
417        modules=[host_unit_test_module(name='hello_world_test')]
418    )
419    info = mod_info.get_module_info('hello_world_test')
420    info[constants.MODULE_INFO_ID] = 'new_hello_world_test'
421
422    self.run_generator(mod_info)
423
424    self.assertTargetInWorkspace('new_hello_world_test')
425    self.assertTargetNotInWorkspace('hello_world_test')
426
427  def test_generate_host_unit_test_module_target(self):
428    mod_info = self.create_module_info(
429        modules=[host_unit_test_module(name='hello_world_test')]
430    )
431
432    self.run_generator(mod_info)
433
434    self.assertTargetInWorkspace('hello_world_test_host')
435
436  def test_not_generate_host_test_module_target(self):
437    mod_info = self.create_module_info(
438        modules=[
439            host_test_module(name='hello_world_test'),
440        ]
441    )
442
443    self.run_generator(mod_info)
444
445    self.assertTargetNotInWorkspace('hello_world_test')
446
447  def test_not_generate_test_module_target_with_invalid_installed_path(self):
448    mod_info = self.create_module_info(
449        modules=[
450            test_module(name='hello_world_test', installed='out/invalid/path')
451        ]
452    )
453
454    self.run_generator(mod_info)
455
456    self.assertTargetNotInWorkspace('hello_world_test_device')
457    self.assertTargetNotInWorkspace('hello_world_test_host')
458
459  def test_generate_variable_file(self):
460    gen = self.create_workspace_generator()
461
462    gen.generate()
463
464    self.assertFileInWorkspace('BUILD.bazel')
465    self.assertFileInWorkspace('constants.bzl')
466
467
468class MultiConfigUnitTestModuleTestTargetGenerationTest(GenerationTestFixture):
469  """Tests for test target generation of test modules with multi-configs."""
470
471  def setUp(self):
472    super().setUp()
473    super().add_device_def_to_filesystem()
474
475  def test_generate_test_rule_imports(self):
476    mod_info = self.create_module_info(
477        modules=[
478            multi_config(
479                host_unit_suite(
480                    test_module(name='hello_world_test', path='example/tests')
481                )
482            ),
483        ]
484    )
485
486    self.run_generator(
487        mod_info,
488        enabled_features=set(
489            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
490        ),
491    )
492
493    self.assertInBuildFile(
494        'load("//bazel/rules:tradefed_test.bzl",'
495        ' "tradefed_device_driven_test", "tradefed_deviceless_test")\n',
496        package='example/tests',
497    )
498
499  def test_not_generate_device_test_import_when_feature_disabled(self):
500    mod_info = self.create_module_info(
501        modules=[
502            multi_config(
503                host_unit_suite(
504                    test_module(name='hello_world_test', path='example/tests')
505                )
506            ),
507        ]
508    )
509
510    self.run_generator(mod_info)
511
512    self.assertInBuildFile(
513        'load("//bazel/rules:tradefed_test.bzl", "tradefed_deviceless_test")\n',
514        package='example/tests',
515    )
516
517  def test_generate_test_targets(self):
518    mod_info = self.create_module_info(
519        modules=[
520            multi_config(
521                host_unit_suite(
522                    test_module(name='hello_world_test', path='example/tests')
523                )
524            ),
525        ]
526    )
527
528    self.run_generator(
529        mod_info,
530        enabled_features=set(
531            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
532        ),
533    )
534
535    self.assertTargetInWorkspace(
536        'hello_world_test_device', package='example/tests'
537    )
538    self.assertTargetInWorkspace(
539        'hello_world_test_host', package='example/tests'
540    )
541
542  def test_not_generate_device_test_target_when_feature_disabled(self):
543    mod_info = self.create_module_info(
544        modules=[
545            multi_config(
546                host_unit_suite(
547                    test_module(name='hello_world_test', path='example/tests')
548                )
549            ),
550        ]
551    )
552
553    self.run_generator(mod_info)
554
555    self.assertTargetNotInWorkspace(
556        'hello_world_test_device', package='example/tests'
557    )
558    self.assertTargetInWorkspace(
559        'hello_world_test_host', package='example/tests'
560    )
561
562
563class DeviceTestModuleTestTargetGenerationTest(GenerationTestFixture):
564  """Tests for device test module test target generation."""
565
566  def setUp(self):
567    super().setUp()
568    super().add_device_def_to_filesystem()
569
570  def test_generate_device_driven_test_target(self):
571    mod_info = self.create_module_info(
572        modules=[
573            device_test_module(name='hello_world_test', path='example/tests'),
574        ]
575    )
576
577    self.run_generator(
578        mod_info,
579        enabled_features=set(
580            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
581        ),
582    )
583
584    self.assertInBuildFile(
585        'load("//bazel/rules:tradefed_test.bzl",'
586        ' "tradefed_device_driven_test")\n',
587        package='example/tests',
588    )
589    self.assertDirInWorkspace('device_def')
590    self.assertTargetInWorkspace(
591        'hello_world_test_device', package='example/tests'
592    )
593
594  def test_generate_target_with_suites(self):
595    mod_info = self.create_module_info(
596        modules=[
597            device_test_module(
598                name='hello_world_test',
599                path='example/tests',
600                compatibility_suites=['cts', 'mts'],
601            ),
602        ]
603    )
604
605    self.run_generator(
606        mod_info,
607        enabled_features=set(
608            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
609        ),
610    )
611
612    self.assertInBuildFile(
613        '    suites = [\n        "cts",\n        "mts",\n    ],\n',
614        package='example/tests',
615    )
616
617  def test_generate_target_with_host_dependencies(self):
618    mod_info = self.create_module_info(
619        modules=[
620            device_test_module(
621                name='hello_world_test',
622                path='example/tests',
623                host_dependencies=['vts_dep', 'cts_dep'],
624            ),
625            host_module(name='vts_dep'),
626            host_module(name='cts_dep'),
627        ]
628    )
629
630    self.run_generator(
631        mod_info,
632        enabled_features=set(
633            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
634        ),
635    )
636
637    self.assertInBuildFile(
638        '    tradefed_deps = [\n'
639        '        "//:cts_dep",\n'
640        '        "//:vts_dep",\n'
641        '    ],\n',
642        package='example/tests',
643    )
644
645  def test_generate_target_with_device_dependencies(self):
646    mod_info = self.create_module_info(
647        modules=[
648            host_test_module(
649                name='hello_world_test',
650                path='example/tests',
651                target_dependencies=['helper_app'],
652            ),
653            device_module(name='helper_app'),
654        ]
655    )
656
657    self.run_generator(
658        mod_info,
659        enabled_features=set(
660            [bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST]
661        ),
662    )
663
664    self.assertInBuildFile(
665        '    device_data = [\n        "//:helper_app",\n    ],\n',
666        package='example/tests',
667    )
668
669  def test_generate_target_with_tags(self):
670    mod_info = self.create_module_info(
671        modules=[
672            device_test_module(
673                name='hello_world_test',
674                path='example/tests',
675                test_options_tags=['no-remote'],
676            ),
677        ]
678    )
679
680    self.run_generator(
681        mod_info,
682        enabled_features=set(
683            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
684        ),
685    )
686
687    self.assertInBuildFile(
688        '    tags = [\n        "no-remote",\n    ],\n',
689        package='example/tests',
690    )
691
692  def test_generate_host_driven_test_target(self):
693    mod_info = self.create_module_info(
694        modules=[
695            host_test_module(name='hello_world_test', path='example/tests'),
696        ]
697    )
698
699    self.run_generator(
700        mod_info,
701        enabled_features=set(
702            [bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST]
703        ),
704    )
705
706    self.assertInBuildFile(
707        'tradefed_host_driven_device_test(', package='example/tests'
708    )
709
710  def test_generate_multi_config_device_test_target(self):
711    mod_info = self.create_module_info(
712        modules=[
713            multi_config(
714                test_module(name='hello_world_test', path='example/tests')
715            ),
716        ]
717    )
718
719    self.run_generator(
720        mod_info,
721        enabled_features=set([
722            bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST,
723            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST,
724        ]),
725    )
726
727    self.assertInBuildFile(
728        'load("//bazel/rules:tradefed_test.bzl", '
729        '"tradefed_device_driven_test", '
730        '"tradefed_host_driven_device_test")\n',
731        package='example/tests',
732    )
733    self.assertTargetInWorkspace(
734        'hello_world_test_device', package='example/tests'
735    )
736    self.assertTargetInWorkspace(
737        'hello_world_test_host', package='example/tests'
738    )
739
740  def test_not_generate_host_driven_test_target_when_feature_disabled(self):
741    mod_info = self.create_module_info(
742        modules=[
743            multi_config(
744                test_module(name='hello_world_test', path='example/tests')
745            ),
746        ]
747    )
748
749    self.run_generator(
750        mod_info,
751        enabled_features=set(
752            [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
753        ),
754    )
755
756    self.assertTargetInWorkspace(
757        'hello_world_test_device', package='example/tests'
758    )
759    self.assertTargetNotInWorkspace(
760        'hello_world_test_host', package='example/tests'
761    )
762
763  def test_raise_when_prerequisite_not_in_module_info(self):
764    mod_info = self.create_module_info(
765        modules=[
766            device_test_module(),
767        ]
768    )
769    del mod_info.name_to_module_info['aapt']
770
771    with self.assertRaises(Exception) as context:
772      self.run_generator(
773          mod_info,
774          enabled_features=set(
775              [bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]
776          ),
777      )
778
779    self.assertIn('aapt', str(context.exception))
780
781
782class HostUnitTestModuleTestTargetGenerationTest(GenerationTestFixture):
783  """Tests for host unit test module test target generation."""
784
785  def test_generate_deviceless_test_import(self):
786    mod_info = self.create_module_info(
787        modules=[
788            host_unit_test_module(name='hello_world_test'),
789        ]
790    )
791
792    self.run_generator(mod_info)
793
794    self.assertInBuildFile(
795        'load("//bazel/rules:tradefed_test.bzl", "tradefed_deviceless_test")\n'
796    )
797
798  def test_generate_deviceless_test_target(self):
799    mod_info = self.create_module_info(
800        modules=[
801            host_unit_test_module(
802                name='hello_world_test', path='example/tests'
803            ),
804        ]
805    )
806
807    self.run_generator(mod_info)
808
809    self.assertInBuildFile(
810        'tradefed_deviceless_test(\n'
811        '    name = "hello_world_test_host",\n'
812        '    module_name = "hello_world_test",\n'
813        '    test = "//example/tests:hello_world_test",\n'
814        ')',
815        package='example/tests',
816    )
817
818  def test_generate_target_with_tags(self):
819    mod_info = self.create_module_info(
820        modules=[
821            host_unit_test_module(
822                name='hello_world_test',
823                path='example/tests',
824                test_options_tags=['no-remote'],
825            ),
826        ]
827    )
828
829    self.run_generator(mod_info)
830
831    self.assertInBuildFile(
832        '    tags = [\n        "no-remote",\n    ],\n',
833        package='example/tests',
834    )
835
836  def test_generate_test_module_prebuilt(self):
837    mod_info = self.create_module_info(
838        modules=[
839            host_unit_test_module(name='hello_world_test'),
840        ]
841    )
842
843    self.run_generator(mod_info)
844
845    self.assertTargetInWorkspace('hello_world_test')
846
847  def test_raise_when_prerequisite_not_in_module_info(self):
848    mod_info = self.create_module_info(
849        modules=[
850            host_unit_test_module(),
851        ]
852    )
853    del mod_info.name_to_module_info['adb']
854
855    with self.assertRaises(Exception) as context:
856      self.run_generator(mod_info)
857
858    self.assertIn('adb', str(context.exception))
859
860  def test_raise_when_prerequisite_module_missing_path(self):
861    mod_info = self.create_module_info(
862        modules=[
863            host_unit_test_module(),
864        ]
865    )
866    mod_info.name_to_module_info['adb'].get('path').clear()
867
868    with self.assertRaises(Exception) as context:
869      self.run_generator(mod_info)
870
871    self.assertIn('adb', str(context.exception))
872
873  def test_warning_when_prerequisite_module_has_multiple_path(self):
874    mod_info = self.create_module_info(
875        modules=[
876            host_unit_test_module(),
877        ]
878    )
879    mod_info.name_to_module_info['adb'].get('path').append('the/2nd/path')
880
881    with self.assertWarns(Warning) as context:
882      self.run_generator(mod_info)
883
884    self.assertIn('adb', str(context.warnings[0].message))
885
886
887class RemoteAvdTestTargetGenerationTest(GenerationTestFixture):
888  """Unit tests for generating Bazel targets on remote AVD."""
889
890  def setUp(self):
891    super().setUp()
892    super().add_device_def_to_filesystem()
893
894  def test_generate_remote_avd_test_target(self):
895    mod_info = self.create_module_info(
896        modules=[
897            device_test_module(name='hello_world_test', path='example/tests'),
898        ]
899    )
900
901    self.run_generator(
902        mod_info,
903        enabled_features=set([
904            bazel_mode.Features.EXPERIMENTAL_REMOTE_AVD,
905            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST,
906        ]),
907    )
908
909    self.assertInBuildFile(
910        'load("//bazel/rules:tradefed_test.bzl",'
911        ' "tradefed_device_driven_test")\n',
912        package='example/tests',
913    )
914    self.assertDirInWorkspace('device_def')
915    self.assertTargetInWorkspace(
916        'hello_world_test_device', package='example/tests'
917    )
918
919  def test_generate_remote_avd_test_target_no_device_test_flag(self):
920    mod_info = self.create_module_info(
921        modules=[
922            device_test_module(name='hello_world_test', path='example/tests'),
923        ]
924    )
925
926    with self.assertRaises(Exception) as context:
927      self.run_generator(
928          mod_info,
929          enabled_features=set([bazel_mode.Features.EXPERIMENTAL_REMOTE_AVD]),
930      )
931
932    self.assertIn(
933        '--experimental-device-driven-test" flag is not set',
934        str(context.exception),
935    )
936
937
938class RobolectricTestModuleTestTargetGenerationTest(GenerationTestFixture):
939  """Tests for robolectric test module test target generation."""
940
941  def setUp(self):
942    super().setUp()
943    self.robolectric_template_path = (
944        self.resource_manager.get_resource_file_path(
945            bazel_mode.ROBOLECTRIC_CONFIG, True
946        )
947    )
948    self.fs.create_file(self.robolectric_template_path, contents='')
949    # ResourceManager only calculates md5 when registering files. So, it is
950    # necessary to call get_resource_file_path() again after writing files.
951    self.resource_manager.get_resource_file_path(
952        bazel_mode.ROBOLECTRIC_CONFIG, True
953    )
954
955  def test_generate_robolectric_test_target(self):
956    module_name = 'hello_world_test'
957    mod_info = self.create_module_info(
958        modules=[
959            robolectric_test_module(
960                name=f'{module_name}', compatibility_suites='robolectric-tests'
961            ),
962        ]
963    )
964
965    self.run_generator(
966        mod_info,
967        enabled_features=set(
968            [bazel_mode.Features.EXPERIMENTAL_ROBOLECTRIC_TEST]
969        ),
970    )
971
972    self.assertInBuildFile(
973        'load("//bazel/rules:tradefed_test.bzl",'
974        ' "tradefed_robolectric_test")\n',
975    )
976    self.assertTargetInWorkspace(f'{module_name}_host')
977
978  def test_not_generate_when_feature_disabled(self):
979    module_name = 'hello_world_test'
980    mod_info = self.create_module_info(
981        modules=[
982            robolectric_test_module(
983                name=f'{module_name}', compatibility_suites='robolectric-tests'
984            ),
985        ]
986    )
987
988    self.run_generator(mod_info)
989
990    self.assertTargetNotInWorkspace(f'{module_name}_host')
991
992  def test_not_generate_for_legacy_robolectric_test_type(self):
993    module_name = 'hello_world_test'
994    module_path = 'example/tests'
995    mod_info = self.create_module_info(
996        modules=[
997            robolectric_test_module(name=f'{module_name}', path=module_path),
998        ]
999    )
1000
1001    self.run_generator(
1002        mod_info,
1003        enabled_features=set(
1004            [bazel_mode.Features.EXPERIMENTAL_ROBOLECTRIC_TEST]
1005        ),
1006    )
1007
1008    self.assertFileNotInWorkspace('BUILD.bazel', package=f'{module_path}')
1009
1010  def test_generate_jdk_target(self):
1011    gen = self.create_workspace_generator(jdk_path=Path('jdk_src_root'))
1012
1013    gen.generate()
1014
1015    self.assertInBuildFile(
1016        'filegroup(\n'
1017        f'    name = "{bazel_mode.JDK_NAME}",\n'
1018        '    srcs = glob([\n'
1019        f'        "{bazel_mode.JDK_NAME}_files/**",\n',
1020        package=f'{bazel_mode.JDK_PACKAGE_NAME}',
1021    )
1022
1023  def test_not_generate_jdk_target_when_no_jdk_path(self):
1024    gen = self.create_workspace_generator(jdk_path=None)
1025
1026    gen.generate()
1027
1028    self.assertFileNotInWorkspace(
1029        'BUILD.bazel', package=f'{bazel_mode.JDK_PACKAGE_NAME}'
1030    )
1031
1032  def test_create_symlinks_to_jdk(self):
1033    jdk_path = Path('jdk_path')
1034    gen = self.create_workspace_generator(jdk_path=jdk_path)
1035
1036    gen.generate()
1037
1038    self.assertSymlinkTo(
1039        self.workspace_out_path.joinpath(
1040            f'{bazel_mode.JDK_PACKAGE_NAME}/{bazel_mode.JDK_NAME}_files'
1041        ),
1042        self.resource_manager.get_src_file_path(f'{jdk_path}'),
1043    )
1044
1045  def test_generate_android_all_target(self):
1046    gen = self.create_workspace_generator(jdk_path=Path('jdk_src_root'))
1047
1048    gen.generate()
1049
1050    self.assertInBuildFile(
1051        'filegroup(\n'
1052        '    name = "android-all",\n'
1053        '    srcs = glob([\n'
1054        '        "android-all_files/**",\n',
1055        package='android-all',
1056    )
1057
1058  def test_not_generate_android_all_target_when_no_jdk_path(self):
1059    gen = self.create_workspace_generator(jdk_path=None)
1060
1061    gen.generate()
1062
1063    self.assertFileNotInWorkspace('BUILD.bazel', package='android-all')
1064
1065  def test_create_symlinks_to_android_all(self):
1066    module_name = 'android-all'
1067    gen = self.create_workspace_generator(jdk_path=Path('jdk_src_root'))
1068
1069    gen.generate()
1070
1071    self.assertSymlinkTo(
1072        self.workspace_out_path.joinpath(f'{module_name}/{module_name}_files'),
1073        self.host_out_path.joinpath(f'testcases/{module_name}'),
1074    )
1075
1076  def test_regenerate_workspace_when_robolectric_template_changed(self):
1077    workspace_generator = self.create_workspace_generator()
1078    workspace_generator.generate()
1079    workspace_stat = workspace_generator.workspace_out_path.stat()
1080
1081    with open(self.robolectric_template_path, 'a', encoding='utf8') as f:
1082      f.write(' ')
1083    workspace_generator = self.create_workspace_generator()
1084    workspace_generator.generate()
1085
1086    new_workspace_stat = workspace_generator.workspace_out_path.stat()
1087    self.assertNotEqual(workspace_stat, new_workspace_stat)
1088
1089  def test_not_regenerate_workspace_when_robolectric_template_touched(self):
1090    workspace_generator = self.create_workspace_generator()
1091    workspace_generator.generate()
1092    workspace_stat = workspace_generator.workspace_out_path.stat()
1093
1094    self.robolectric_template_path.touch()
1095    workspace_generator = self.create_workspace_generator()
1096    workspace_generator.generate()
1097
1098    new_workspace_stat = workspace_generator.workspace_out_path.stat()
1099    self.assertEqual(workspace_stat, new_workspace_stat)
1100
1101
1102class ModulePrebuiltTargetGenerationTest(GenerationTestFixture):
1103  """Tests for module prebuilt target generation."""
1104
1105  def test_generate_prebuilt_import(self):
1106    mod_info = self.create_module_info(
1107        modules=[
1108            supported_test_module(),
1109        ]
1110    )
1111
1112    self.run_generator(mod_info)
1113
1114    self.assertInBuildFile(
1115        'load("//bazel/rules:soong_prebuilt.bzl", "soong_prebuilt")\n'
1116    )
1117
1118  def test_generate_prebuilt_target_for_multi_config_test_module(self):
1119    mod_info = self.create_module_info(
1120        modules=[
1121            multi_config(supported_test_module(name='libhello')),
1122        ]
1123    )
1124
1125    self.run_generator(mod_info)
1126
1127    self.assertInBuildFile(
1128        'soong_prebuilt(\n'
1129        '    name = "libhello",\n'
1130        '    module_name = "libhello",\n'
1131        '    files = select({\n'
1132        '        "//bazel/rules:device": glob(["libhello/device/**/*"]),\n'
1133        '        "//bazel/rules:host": glob(["libhello/host/**/*"]),\n'
1134        '    }),\n'
1135        '    suites = [\n'
1136        '        "host-unit-tests",\n'
1137        '    ],\n'
1138        ')\n'
1139    )
1140
1141  def test_create_symlinks_to_testcases_for_multi_config_test_module(self):
1142    module_name = 'hello_world_test'
1143    mod_info = self.create_module_info(
1144        modules=[multi_config(supported_test_module(name=module_name))]
1145    )
1146    module_out_path = self.workspace_out_path.joinpath(module_name)
1147
1148    self.run_generator(mod_info)
1149
1150    self.assertSymlinkTo(
1151        module_out_path.joinpath(f'host/testcases/{module_name}'),
1152        self.host_out_path.joinpath(f'testcases/{module_name}'),
1153    )
1154    self.assertSymlinkTo(
1155        module_out_path.joinpath(f'device/testcases/{module_name}'),
1156        self.product_out_path.joinpath(f'testcases/{module_name}'),
1157    )
1158
1159  def test_generate_files_for_host_only_test_module(self):
1160    mod_info = self.create_module_info(
1161        modules=[
1162            host_only_config(supported_test_module(name='test1')),
1163        ]
1164    )
1165
1166    self.run_generator(mod_info)
1167
1168    self.assertInBuildFile(
1169        '    files = select({\n'
1170        '        "//bazel/rules:host": glob(["test1/host/**/*"]),\n'
1171        '    }),\n'
1172    )
1173
1174  def test_generate_files_for_device_only_test_module(self):
1175    mod_info = self.create_module_info(
1176        modules=[
1177            device_only_config(supported_test_module(name='test1')),
1178        ]
1179    )
1180
1181    self.run_generator(mod_info)
1182
1183    self.assertInBuildFile(
1184        '    files = select({\n'
1185        '        "//bazel/rules:device": glob(["test1/device/**/*"]),\n'
1186        '    }),\n'
1187    )
1188
1189  def test_not_create_device_symlinks_for_host_only_test_module(self):
1190    mod_info = self.create_module_info(
1191        modules=[
1192            host_only_config(supported_test_module(name='test1')),
1193        ]
1194    )
1195
1196    self.run_generator(mod_info)
1197
1198    self.assertFileNotInWorkspace('test1/device')
1199
1200  def test_not_create_host_symlinks_for_device_test_module(self):
1201    mod_info = self.create_module_info(
1202        modules=[
1203            device_only_config(supported_test_module(name='test1')),
1204        ]
1205    )
1206
1207    self.run_generator(mod_info)
1208
1209    self.assertFileNotInWorkspace('test1/host')
1210
1211
1212class ModuleSharedLibGenerationTest(GenerationTestFixture):
1213  """Tests for module shared libs target generation."""
1214
1215  def test_not_generate_runtime_deps_when_all_configs_incompatible(self):
1216    mod_info = self.create_module_info(
1217        modules=[
1218            host_only_config(supported_test_module(shared_libs=['libdevice'])),
1219            device_only_config(module(name='libdevice')),
1220        ]
1221    )
1222
1223    self.run_generator(mod_info)
1224
1225    self.assertNotInBuildFile('runtime_deps')
1226
1227  def test_generate_runtime_deps_when_configs_compatible(self):
1228    mod_info = self.create_module_info(
1229        modules=[
1230            multi_config(supported_test_module(shared_libs=['libmulti'])),
1231            multi_config_module(name='libmulti'),
1232        ]
1233    )
1234
1235    self.run_generator(mod_info)
1236
1237    self.assertInBuildFile(
1238        '    runtime_deps = select({\n'
1239        '        "//bazel/rules:device": [\n'
1240        '            "//:libmulti",\n'
1241        '        ],\n'
1242        '        "//bazel/rules:host": [\n'
1243        '            "//:libmulti",\n'
1244        '        ],\n'
1245        '    }),\n'
1246    )
1247
1248  def test_generate_runtime_deps_when_configs_partially_compatible(self):
1249    mod_info = self.create_module_info(
1250        modules=[
1251            multi_config(
1252                supported_test_module(
1253                    shared_libs=[
1254                        'libhost',
1255                    ]
1256                )
1257            ),
1258            host_module(name='libhost'),
1259        ]
1260    )
1261
1262    self.run_generator(mod_info)
1263
1264    self.assertInBuildFile(
1265        '    runtime_deps = select({\n'
1266        '        "//bazel/rules:device": [\n'
1267        '        ],\n'
1268        '        "//bazel/rules:host": [\n'
1269        '            "//:libhost",\n'
1270        '        ],\n'
1271        '    }),\n'
1272    )
1273
1274  def test_generate_runtime_deps_with_mixed_compatibility(self):
1275    mod_info = self.create_module_info(
1276        modules=[
1277            multi_config(
1278                supported_test_module(
1279                    shared_libs=['libhost', 'libdevice', 'libmulti']
1280                )
1281            ),
1282            host_module(name='libhost'),
1283            device_module(name='libdevice'),
1284            multi_config_module(name='libmulti'),
1285        ]
1286    )
1287
1288    self.run_generator(mod_info)
1289
1290    self.assertInBuildFile(
1291        '    runtime_deps = select({\n'
1292        '        "//bazel/rules:device": [\n'
1293        '            "//:libdevice",\n'
1294        '            "//:libmulti",\n'
1295        '        ],\n'
1296        '        "//bazel/rules:host": [\n'
1297        '            "//:libhost",\n'
1298        '            "//:libmulti",\n'
1299        '        ],\n'
1300        '    }),\n'
1301    )
1302
1303  def test_generate_runtime_deps_recursively(self):
1304    mod_info = self.create_module_info(
1305        modules=[
1306            multi_config(
1307                supported_test_module(
1308                    shared_libs=[
1309                        'libdirect',
1310                    ]
1311                )
1312            ),
1313            multi_config_module(
1314                name='libdirect',
1315                shared_libs=[
1316                    'libtransitive',
1317                ],
1318            ),
1319            multi_config_module(name='libtransitive'),
1320        ]
1321    )
1322
1323    self.run_generator(mod_info)
1324
1325    self.assertTargetInWorkspace('libtransitive')
1326
1327  def test_generate_shared_runtime_deps_once(self):
1328    mod_info = self.create_module_info(
1329        modules=[
1330            multi_config(
1331                supported_test_module(
1332                    shared_libs=[
1333                        'libleft',
1334                        'libright',
1335                    ]
1336                )
1337            ),
1338            multi_config_module(
1339                name='libleft',
1340                shared_libs=[
1341                    'libshared',
1342                ],
1343            ),
1344            multi_config_module(
1345                name='libright',
1346                shared_libs=[
1347                    'libshared',
1348                ],
1349            ),
1350            multi_config_module(name='libshared'),
1351        ]
1352    )
1353
1354    self.run_generator(mod_info)
1355
1356    self.assertTargetInWorkspace('libshared')
1357
1358  def test_generate_runtime_deps_in_order(self):
1359    mod_info = self.create_module_info(
1360        modules=[
1361            supported_test_module(shared_libs=['libhello2', 'libhello1']),
1362            host_module(name='libhello1'),
1363            host_module(name='libhello2'),
1364        ]
1365    )
1366
1367    self.run_generator(mod_info)
1368
1369    self.assertInBuildFile(
1370        '            "//:libhello1",\n            "//:libhello2",\n'
1371    )
1372
1373  def test_generate_target_for_shared_lib(self):
1374    mod_info = self.create_module_info(
1375        modules=[
1376            supported_test_module(shared_libs=['libhello']),
1377            host_module(name='libhello'),
1378        ]
1379    )
1380
1381    self.run_generator(mod_info)
1382
1383    self.assertTargetInWorkspace('libhello')
1384
1385  def test_not_generate_for_missing_shared_lib_module(self):
1386    mod_info = self.create_module_info(
1387        modules=[supported_test_module(shared_libs=['libhello'])]
1388    )
1389
1390    self.run_generator(mod_info)
1391
1392    self.assertNotInBuildFile('            "//:libhello",\n')
1393    self.assertTargetNotInWorkspace('libhello')
1394
1395  def test_not_generate_when_shared_lib_uninstalled(self):
1396    mod_info = self.create_module_info(
1397        modules=[
1398            supported_test_module(shared_libs=['libhello']),
1399            host_module(name='libhello', installed=[]),
1400        ]
1401    )
1402
1403    self.run_generator(mod_info)
1404
1405    self.assertNotInBuildFile('            "//:libhello",\n')
1406    self.assertTargetNotInWorkspace('libhello')
1407
1408  def test_not_generate_when_shared_lib_installed_path_unsupported(self):
1409    unsupported_install_path = 'out/other'
1410    mod_info = self.create_module_info(
1411        modules=[
1412            supported_test_module(shared_libs=['libhello']),
1413            shared_lib(
1414                module('libhello', installed=[unsupported_install_path])
1415            ),
1416        ]
1417    )
1418
1419    self.run_generator(mod_info)
1420
1421    self.assertNotInBuildFile('"//:libhello",\n')
1422    self.assertTargetNotInWorkspace('libhello')
1423
1424  def test_not_generate_when_shared_lib_install_path_ambiguous(self):
1425    ambiguous_install_path = 'out/f1'
1426    mod_info = self.create_module_info(
1427        modules=[
1428            supported_test_module(shared_libs=['libhello']),
1429            module(name='libhello', installed=[ambiguous_install_path]),
1430        ]
1431    )
1432
1433    self.run_generator(mod_info)
1434
1435    self.assertNotInBuildFile('"//:libhello",\n')
1436    self.assertTargetNotInWorkspace('libhello')
1437
1438  def test_generate_target_for_rlib_dependency(self):
1439    mod_info = self.create_module_info(
1440        modules=[
1441            multi_config(
1442                host_unit_suite(
1443                    module(
1444                        name='hello_world_test',
1445                        dependencies=['libhost', 'libdevice'],
1446                    )
1447                )
1448            ),
1449            rlib(module(name='libhost', supported_variants=['HOST'])),
1450            rlib(module(name='libdevice', supported_variants=['DEVICE'])),
1451        ]
1452    )
1453
1454    self.run_generator(mod_info)
1455
1456    self.assertInBuildFile(
1457        'soong_uninstalled_prebuilt(\n'
1458        '    name = "libhost",\n'
1459        '    module_name = "libhost",\n'
1460        ')\n'
1461    )
1462    self.assertInBuildFile(
1463        'soong_uninstalled_prebuilt(\n'
1464        '    name = "libdevice",\n'
1465        '    module_name = "libdevice",\n'
1466        ')\n'
1467    )
1468    self.assertInBuildFile(
1469        '    runtime_deps = select({\n'
1470        '        "//bazel/rules:device": [\n'
1471        '            "//:libdevice",\n'
1472        '        ],\n'
1473        '        "//bazel/rules:host": [\n'
1474        '            "//:libhost",\n'
1475        '        ],\n'
1476        '    }),\n'
1477    )
1478
1479  def test_generate_target_for_rlib_dylib_dependency(self):
1480    mod_info = self.create_module_info(
1481        modules=[
1482            supported_test_module(dependencies=['libhello']),
1483            rlib(module(name='libhello', dependencies=['libworld'])),
1484            host_only_config(dylib(module(name='libworld'))),
1485        ]
1486    )
1487
1488    self.run_generator(mod_info)
1489
1490    self.assertTargetInWorkspace('libworld')
1491
1492  def test_generate_target_for_dylib_dependency(self):
1493    mod_info = self.create_module_info(
1494        modules=[
1495            supported_test_module(dependencies=['libhello']),
1496            host_only_config(dylib(module(name='libhello'))),
1497        ]
1498    )
1499
1500    self.run_generator(mod_info)
1501
1502    self.assertInBuildFile(
1503        'soong_prebuilt(\n'
1504        '    name = "libhello",\n'
1505        '    module_name = "libhello",\n'
1506    )
1507
1508  def test_generate_target_for_uninstalled_dylib_dependency(self):
1509    mod_info = self.create_module_info(
1510        modules=[
1511            supported_test_module(dependencies=['libhello']),
1512            dylib(module(name='libhello', installed=[])),
1513        ]
1514    )
1515
1516    self.run_generator(mod_info)
1517
1518    self.assertInBuildFile(
1519        'soong_uninstalled_prebuilt(\n'
1520        '    name = "libhello",\n'
1521        '    module_name = "libhello",\n'
1522        ')\n'
1523    )
1524
1525  def test_not_generate_target_for_non_runtime_dependency(self):
1526    mod_info = self.create_module_info(
1527        modules=[
1528            supported_test_module(dependencies=['libhello']),
1529            host_module(name='libhello', classes=['NOT_SUPPORTED']),
1530        ]
1531    )
1532
1533    self.run_generator(mod_info)
1534
1535    self.assertNotInBuildFile('"//:libhello",\n')
1536    self.assertTargetNotInWorkspace('libhello')
1537
1538  def test_generate_target_for_runtime_dependency(self):
1539    mod_info = self.create_module_info(
1540        modules=[
1541            supported_test_module(runtime_dependencies=['libhello']),
1542            host_only_config(
1543                module(name='libhello', classes=['SHARED_LIBRARIES'])
1544            ),
1545        ]
1546    )
1547
1548    self.run_generator(mod_info)
1549
1550    self.assertInBuildFile(
1551        '    runtime_deps = select({\n'
1552        '        "//bazel/rules:host": [\n'
1553        '            "//:libhello",\n'
1554        '        ],\n'
1555        '    }),\n'
1556    )
1557
1558
1559class SharedLibPrebuiltTargetGenerationTest(GenerationTestFixture):
1560  """Tests for runtime dependency module prebuilt target generation."""
1561
1562  def test_create_multi_config_target_symlinks(self):
1563    host_file1 = self.host_out_path.joinpath('a/b/f1')
1564    host_file2 = self.host_out_path.joinpath('a/c/f2')
1565    device_file1 = self.product_out_path.joinpath('a/b/f1')
1566    mod_info = self.create_module_info(
1567        modules=[
1568            supported_test_module(shared_libs=['libhello']),
1569            multi_config_module(
1570                name='libhello',
1571                installed=[str(host_file1), str(host_file2), str(device_file1)],
1572            ),
1573        ]
1574    )
1575    package_path = self.workspace_out_path
1576
1577    self.run_generator(mod_info)
1578
1579    self.assertSymlinkTo(
1580        package_path.joinpath('libhello/host/a/b/f1'), host_file1
1581    )
1582    self.assertSymlinkTo(
1583        package_path.joinpath('libhello/host/a/c/f2'), host_file2
1584    )
1585    self.assertSymlinkTo(
1586        package_path.joinpath('libhello/device/a/b/f1'), device_file1
1587    )
1588
1589  def test_create_symlinks_to_installed_path_for_non_tf_testable_deps(self):
1590    host_file = self.host_out_path.joinpath('a/b/f1')
1591    mod_info = self.create_module_info(
1592        modules=[
1593            supported_test_module(shared_libs=['libhello']),
1594            host_module(
1595                name='libhello',
1596                installed=[str(host_file)],
1597            ),
1598        ]
1599    )
1600    package_path = self.workspace_out_path
1601
1602    self.run_generator(mod_info)
1603
1604    self.assertSymlinkTo(
1605        package_path.joinpath('libhello/host/a/b/f1'), host_file
1606    )
1607
1608  def test_create_symlinks_to_installed_path_for_lib_with_test_config(self):
1609    host_file = self.host_out_path.joinpath('a/b/f1')
1610    mod_info = self.create_module_info(
1611        modules=[
1612            supported_test_module(shared_libs=['libhello']),
1613            host_module(
1614                name='libhello', installed=[str(host_file)], path='src/lib'
1615            ),
1616        ]
1617    )
1618    self.fs.create_file(Path('src/lib/AndroidTest.xml'), contents='')
1619    package_path = self.workspace_out_path
1620
1621    self.run_generator(mod_info)
1622
1623    self.assertSymlinkTo(
1624        package_path.joinpath('src/lib/libhello/host/a/b/f1'), host_file
1625    )
1626
1627  def test_generate_for_host_only_shared_lib_dependency(self):
1628    mod_info = self.create_module_info(
1629        modules=[
1630            supported_test_module(shared_libs=['libhello']),
1631            host_module(name='libhello'),
1632        ]
1633    )
1634
1635    self.run_generator(mod_info)
1636
1637    self.assertInBuildFile(
1638        '    files = select({\n'
1639        '        "//bazel/rules:host": glob(["libhello/host/**/*"]),\n'
1640        '    }),\n'
1641    )
1642    self.assertFileNotInWorkspace('libhello/device')
1643
1644  def test_generate_for_device_only_shared_lib_dependency(self):
1645    mod_info = self.create_module_info(
1646        modules=[
1647            supported_test_module(shared_libs=['libhello']),
1648            device_module(name='libhello'),
1649        ]
1650    )
1651
1652    self.run_generator(mod_info)
1653
1654    self.assertInBuildFile(
1655        '    files = select({\n'
1656        '        "//bazel/rules:device": glob(["libhello/device/**/*"]),\n'
1657        '    }),\n'
1658    )
1659    self.assertFileNotInWorkspace('libhello/host')
1660
1661
1662class DataDependenciesGenerationTest(GenerationTestFixture):
1663  """Tests for module data dependencies target generation."""
1664
1665  def test_generate_target_for_data_dependency(self):
1666    mod_info = self.create_module_info(
1667        modules=[
1668            supported_test_module(data_dependencies=['libdata']),
1669            host_module(name='libdata'),
1670        ]
1671    )
1672
1673    self.run_generator(mod_info)
1674
1675    self.assertInBuildFile(
1676        '    data = select({\n'
1677        '        "//bazel/rules:host": [\n'
1678        '            "//:libdata",\n'
1679        '        ],\n'
1680        '    }),\n'
1681    )
1682    self.assertTargetInWorkspace('libdata')
1683
1684  def test_not_generate_target_for_data_file(self):
1685    # Data files are included in "data", but not in "data_dependencies".
1686    mod_info = self.create_module_info(
1687        modules=[
1688            supported_test_module(data=['libdata']),
1689            host_module(name='libdata'),
1690        ]
1691    )
1692
1693    self.run_generator(mod_info)
1694
1695    self.assertTargetNotInWorkspace('libdata')
1696
1697
1698def create_empty_module_info():
1699  return module_info.load_from_dict({})
1700
1701
1702def create_module_info(modules=None):
1703  mod_info = create_empty_module_info()
1704  modules = modules or []
1705
1706  for m in modules:
1707    mod_info.name_to_module_info[m['module_name']] = m
1708
1709  return mod_info
1710
1711
1712def host_unit_test_module(**kwargs):
1713  return host_unit_suite(host_test_module(**kwargs))
1714
1715
1716# We use the below alias in situations where the actual type is irrelevant to
1717# the test as long as it is supported in Bazel mode.
1718supported_test_module = host_unit_test_module
1719
1720
1721def host_test_module(**kwargs):
1722  kwargs.setdefault('name', 'hello_world_test')
1723  return host_only_config(test_module(**kwargs))
1724
1725
1726def device_test_module(**kwargs):
1727  kwargs.setdefault('name', 'hello_world_test')
1728  return device_only_config(test_module(**kwargs))
1729
1730
1731def robolectric_test_module(**kwargs):
1732  kwargs.setdefault('name', 'hello_world_test')
1733  return host_only_config(robolectric(test_module(**kwargs)))
1734
1735
1736def host_module(**kwargs):
1737  m = module(**kwargs)
1738
1739  if 'installed' in kwargs:
1740    return m
1741
1742  return host_only_config(m)
1743
1744
1745def device_module(**kwargs):
1746  m = module(**kwargs)
1747
1748  if 'installed' in kwargs:
1749    return m
1750
1751  return device_only_config(m)
1752
1753
1754def multi_config_module(**kwargs):
1755  m = module(**kwargs)
1756
1757  if 'installed' in kwargs:
1758    return m
1759
1760  return multi_config(m)
1761
1762
1763def test_module(**kwargs):
1764  kwargs.setdefault('name', 'hello_world_test')
1765  return test(module(**kwargs))
1766
1767
1768# TODO(b/274822450): Using a builder pattern to reduce the number of parameters
1769#  instead of disabling the warning.
1770# pylint: disable=too-many-arguments
1771# pylint: disable=too-many-locals
1772def module(
1773    name=None,
1774    path=None,
1775    installed=None,
1776    classes=None,
1777    auto_test_config=None,
1778    shared_libs=None,
1779    dependencies=None,
1780    runtime_dependencies=None,
1781    data=None,
1782    data_dependencies=None,
1783    compatibility_suites=None,
1784    host_dependencies=None,
1785    target_dependencies=None,
1786    test_options_tags=None,
1787    supported_variants=None,
1788):
1789  name = name or 'libhello'
1790
1791  m = {}
1792
1793  m['module_name'] = name
1794  m['class'] = classes or ['']
1795  m['path'] = [path or '']
1796  m['installed'] = installed or []
1797  m['is_unit_test'] = 'false'
1798  m['auto_test_config'] = auto_test_config or []
1799  m['shared_libs'] = shared_libs or []
1800  m['runtime_dependencies'] = runtime_dependencies or []
1801  m['dependencies'] = dependencies or []
1802  m['data'] = data or []
1803  m['data_dependencies'] = data_dependencies or []
1804  m['compatibility_suites'] = compatibility_suites or []
1805  m['host_dependencies'] = host_dependencies or []
1806  m['target_dependencies'] = target_dependencies or []
1807  m['test_options_tags'] = test_options_tags or []
1808  m['supported_variants'] = supported_variants or []
1809  return m
1810
1811
1812def test(info):
1813  info['auto_test_config'] = ['true']
1814  return info
1815
1816
1817def shared_lib(info):
1818  info['class'] = ['SHARED_LIBRARIES']
1819  return info
1820
1821
1822def rlib(info):
1823  info['class'] = ['RLIB_LIBRARIES']
1824  info['installed'] = []
1825  return info
1826
1827
1828def dylib(info):
1829  info['class'] = ['DYLIB_LIBRARIES']
1830  return info
1831
1832
1833def robolectric(info):
1834  info['class'] = ['ROBOLECTRIC']
1835  return info
1836
1837
1838def host_unit_suite(info):
1839  info = test(info)
1840  info.setdefault('compatibility_suites', []).append('host-unit-tests')
1841  return info
1842
1843
1844def multi_config(info):
1845  name = info.get('module_name', 'lib')
1846  info['installed'] = [
1847      f'out/host/linux-x86/{name}/{name}.jar',
1848      f'out/product/vsoc_x86/{name}/{name}.apk',
1849  ]
1850  info['supported_variants'] = [
1851      'DEVICE',
1852      'HOST',
1853  ]
1854  return info
1855
1856
1857def host_only_config(info):
1858  name = info.get('module_name', 'lib')
1859  info['installed'] = [
1860      f'out/host/linux-x86/{name}/{name}.jar',
1861  ]
1862  info['supported_variants'] = [
1863      'HOST',
1864  ]
1865  return info
1866
1867
1868def device_only_config(info):
1869  name = info.get('module_name', 'lib')
1870  info['installed'] = [
1871      f'out/product/vsoc_x86/{name}/{name}.jar',
1872  ]
1873  info['supported_variants'] = [
1874      'DEVICE',
1875  ]
1876  return info
1877
1878
1879class PackageTest(fake_filesystem_unittest.TestCase):
1880  """Tests for Package."""
1881
1882  class FakeTarget(bazel_mode.Target):
1883    """Fake target used for tests."""
1884
1885    def __init__(self, name, imports=None):
1886      self._name = name
1887      self._imports = imports or set()
1888
1889    def name(self):
1890      return self._name
1891
1892    def required_imports(self):
1893      return self._imports
1894
1895    def write_to_build_file(self, f):
1896      f.write(f'{self._name}\n')
1897
1898  def setUp(self):
1899    self.setUpPyfakefs()
1900    self.workspace_out_path = Path('/workspace_out_path')
1901    self.workspace_out_path.mkdir()
1902
1903  def test_raise_when_adding_existing_target(self):
1904    target_name = '<fake_target>'
1905    package = bazel_mode.Package('p')
1906    package.add_target(self.FakeTarget(target_name))
1907
1908    with self.assertRaises(Exception) as context:
1909      package.add_target(self.FakeTarget(target_name))
1910
1911    self.assertIn(target_name, str(context.exception))
1912
1913  def test_write_build_file_in_package_dir(self):
1914    package_path = 'abc/def'
1915    package = bazel_mode.Package(package_path)
1916    expected_path = self.workspace_out_path.joinpath(
1917        package_path, 'BUILD.bazel'
1918    )
1919
1920    package.generate(self.workspace_out_path)
1921
1922    self.assertTrue(expected_path.exists())
1923
1924  def test_write_load_statements_in_sorted_order(self):
1925    package = bazel_mode.Package('p')
1926    target1 = self.FakeTarget(
1927        'target1',
1928        imports={
1929            bazel_mode.Import('z.bzl', 'symbol1'),
1930        },
1931    )
1932    target2 = self.FakeTarget(
1933        'target2',
1934        imports={
1935            bazel_mode.Import('a.bzl', 'symbol2'),
1936        },
1937    )
1938    package.add_target(target1)
1939    package.add_target(target2)
1940
1941    package.generate(self.workspace_out_path)
1942
1943    self.assertIn(
1944        'load("a.bzl", "symbol2")\nload("z.bzl", "symbol1")\n\n',
1945        self.package_build_file_text(package),
1946    )
1947
1948  def test_write_load_statements_with_symbols_grouped_by_bzl(self):
1949    package = bazel_mode.Package('p')
1950    target1 = self.FakeTarget(
1951        'target1',
1952        imports={
1953            bazel_mode.Import('a.bzl', 'symbol1'),
1954            bazel_mode.Import('a.bzl', 'symbol3'),
1955        },
1956    )
1957    target2 = self.FakeTarget(
1958        'target2',
1959        imports={
1960            bazel_mode.Import('a.bzl', 'symbol2'),
1961        },
1962    )
1963    package.add_target(target1)
1964    package.add_target(target2)
1965
1966    package.generate(self.workspace_out_path)
1967
1968    self.assertIn(
1969        'load("a.bzl", "symbol1", "symbol2", "symbol3")\n\n',
1970        self.package_build_file_text(package),
1971    )
1972
1973  def test_write_targets_in_add_order(self):
1974    package = bazel_mode.Package('p')
1975    target1 = self.FakeTarget('target1')
1976    target2 = self.FakeTarget('target2')
1977    package.add_target(target2)  # Added out of order.
1978    package.add_target(target1)
1979
1980    package.generate(self.workspace_out_path)
1981
1982    self.assertIn('target2\n\ntarget1\n', self.package_build_file_text(package))
1983
1984  def test_generate_parent_package_when_nested_exists(self):
1985    parent_path = Path('parent')
1986    parent = bazel_mode.Package(parent_path.name)
1987    nested = bazel_mode.Package(parent_path.joinpath('nested'))
1988    nested.generate(self.workspace_out_path)
1989
1990    parent.generate(self.workspace_out_path)
1991
1992    self.assertTrue(self.workspace_out_path.joinpath(parent_path).is_dir())
1993
1994  def package_build_file_text(self, package):
1995    return self.workspace_out_path.joinpath(
1996        package.path, 'BUILD.bazel'
1997    ).read_text(encoding='utf8')
1998
1999
2000class DecorateFinderMethodTest(GenerationTestFixture):
2001  """Tests for _decorate_find_method()."""
2002
2003  def test_host_unit_test_with_host_arg_runner_is_overridden(self):
2004    def original_find_method(obj, test_id):
2005      return self.create_single_test_infos(
2006          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2007      )
2008
2009    mod_info = self.create_module_info(
2010        modules=[host_unit_test_module(name=MODULE_NAME)]
2011    )
2012    original_finder = self.create_finder(mod_info, original_find_method)
2013    new_finder = bazel_mode.create_new_finder(
2014        mod_info, original_finder, host=True
2015    )
2016
2017    test_infos = new_finder.find_method(
2018        new_finder.test_finder_instance, MODULE_NAME
2019    )
2020
2021    self.assertEqual(len(test_infos), 1)
2022    self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
2023
2024  def test_host_unit_test_without_host_arg_runner_is_overridden(self):
2025    def original_find_method(obj, test_id):
2026      return self.create_single_test_infos(
2027          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2028      )
2029
2030    mod_info = self.create_module_info(
2031        modules=[host_unit_test_module(name=MODULE_NAME)]
2032    )
2033    original_finder = self.create_finder(mod_info, original_find_method)
2034    new_finder = bazel_mode.create_new_finder(
2035        mod_info, original_finder, host=False
2036    )
2037
2038    test_infos = new_finder.find_method(
2039        new_finder.test_finder_instance, MODULE_NAME
2040    )
2041
2042    self.assertEqual(len(test_infos), 1)
2043    self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
2044
2045  def test_device_test_with_host_arg_runner_is_preserved(self):
2046    def original_find_method(obj, test_id):
2047      return self.create_single_test_infos(
2048          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2049      )
2050
2051    mod_info = self.create_module_info(
2052        modules=[device_test_module(name=MODULE_NAME)]
2053    )
2054    original_finder = self.create_finder(mod_info, original_find_method)
2055    new_finder = bazel_mode.create_new_finder(
2056        mod_info,
2057        original_finder,
2058        host=True,
2059        enabled_features=[bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST],
2060    )
2061
2062    test_infos = new_finder.find_method(
2063        new_finder.test_finder_instance, MODULE_NAME
2064    )
2065
2066    self.assertEqual(len(test_infos), 1)
2067    self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER)
2068
2069  def test_device_test_without_host_arg_runner_is_overridden(self):
2070    def original_find_method(obj, test_id):
2071      return self.create_single_test_infos(
2072          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2073      )
2074
2075    mod_info = self.create_module_info(
2076        modules=[device_test_module(name=MODULE_NAME)]
2077    )
2078    original_finder = self.create_finder(mod_info, original_find_method)
2079    new_finder = bazel_mode.create_new_finder(
2080        mod_info,
2081        original_finder,
2082        host=False,
2083        enabled_features=[bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST],
2084    )
2085
2086    test_infos = new_finder.find_method(
2087        new_finder.test_finder_instance, MODULE_NAME
2088    )
2089
2090    self.assertEqual(len(test_infos), 1)
2091    self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
2092
2093  def test_multi_config_test_with_host_arg_runner_is_overridden(self):
2094    def original_find_method(obj, test_id):
2095      return self.create_single_test_infos(
2096          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2097      )
2098
2099    mod_info = self.create_module_info(
2100        modules=[multi_config(supported_test_module(name=MODULE_NAME))]
2101    )
2102    original_finder = self.create_finder(mod_info, original_find_method)
2103    new_finder = bazel_mode.create_new_finder(
2104        mod_info,
2105        original_finder,
2106        host=True,
2107        enabled_features=[bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST],
2108    )
2109
2110    test_infos = new_finder.find_method(
2111        new_finder.test_finder_instance, MODULE_NAME
2112    )
2113
2114    self.assertEqual(len(test_infos), 1)
2115    self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
2116
2117  def test_multi_config_test_without_host_arg_runner_is_overridden(self):
2118    def original_find_method(obj, test_id):
2119      return self.create_single_test_infos(
2120          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2121      )
2122
2123    mod_info = self.create_module_info(
2124        modules=[multi_config(supported_test_module(name=MODULE_NAME))]
2125    )
2126    original_finder = self.create_finder(mod_info, original_find_method)
2127    new_finder = bazel_mode.create_new_finder(
2128        mod_info,
2129        original_finder,
2130        host=False,
2131        enabled_features=[bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST],
2132    )
2133
2134    test_infos = new_finder.find_method(
2135        new_finder.test_finder_instance, MODULE_NAME
2136    )
2137
2138    self.assertEqual(len(test_infos), 1)
2139    self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
2140
2141  def test_host_non_unit_test_with_host_arg_runner_is_overridden(self):
2142    def original_find_method(obj, test_id):
2143      return self.create_single_test_infos(
2144          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2145      )
2146
2147    mod_info = self.create_module_info(
2148        modules=[host_test_module(name=MODULE_NAME)]
2149    )
2150    original_finder = self.create_finder(mod_info, original_find_method)
2151    new_finder = bazel_mode.create_new_finder(
2152        mod_info,
2153        original_finder,
2154        host=True,
2155        enabled_features=[bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST],
2156    )
2157
2158    test_infos = new_finder.find_method(
2159        new_finder.test_finder_instance, MODULE_NAME
2160    )
2161
2162    self.assertEqual(len(test_infos), 1)
2163    self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
2164
2165  def test_disable_device_driven_test_feature_runner_is_preserved(self):
2166    def original_find_method(obj, test_id):
2167      return self.create_single_test_infos(
2168          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2169      )
2170
2171    mod_info = self.create_module_info(
2172        modules=[device_test_module(name=MODULE_NAME)]
2173    )
2174    original_finder = self.create_finder(mod_info, original_find_method)
2175    new_finder = bazel_mode.create_new_finder(
2176        mod_info, original_finder, host=False
2177    )
2178
2179    test_infos = new_finder.find_method(
2180        new_finder.test_finder_instance, MODULE_NAME
2181    )
2182
2183    self.assertEqual(len(test_infos), 1)
2184    self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER)
2185
2186  def test_disable_host_driven_test_feature_runner_is_preserved(self):
2187    def original_find_method(obj, test_id):
2188      return self.create_single_test_infos(
2189          obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2190      )
2191
2192    mod_info = self.create_module_info(
2193        modules=[host_test_module(name=MODULE_NAME)]
2194    )
2195    original_finder = self.create_finder(mod_info, original_find_method)
2196    new_finder = bazel_mode.create_new_finder(
2197        mod_info, original_finder, host=True
2198    )
2199
2200    test_infos = new_finder.find_method(
2201        new_finder.test_finder_instance, MODULE_NAME
2202    )
2203
2204    self.assertEqual(len(test_infos), 1)
2205    self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER)
2206
2207  # pylint: disable=unused-argument
2208  def create_single_test_infos(
2209      self, obj, test_id, test_name=MODULE_NAME, runner=ATEST_TF_RUNNER
2210  ):
2211    """Create list of test_info.TestInfo."""
2212    return [test_info.TestInfo(test_name, runner, MODULE_BUILD_TARGETS)]
2213
2214  def create_finder(self, mod_info, find_method):
2215    return test_finder_base.Finder(
2216        example_finder.ExampleFinder(mod_info), find_method, 'FINDER_NAME'
2217    )
2218
2219
2220class BazelTestRunnerTest(fake_filesystem_unittest.TestCase):
2221  """Tests for BazelTestRunner."""
2222
2223  def test_return_empty_build_reqs_when_no_test_infos(self):
2224    run_command = self.mock_run_command(side_effect=Exception(''))
2225    runner = self.create_bazel_test_runner(
2226        modules=[
2227            supported_test_module(name='test1', path='path1'),
2228        ],
2229        run_command=run_command,
2230    )
2231
2232    reqs = runner.get_test_runner_build_reqs([])
2233
2234    self.assertFalse(reqs)
2235
2236  def test_query_bazel_test_targets_deps_with_host_arg(self):
2237    query_file_contents = StringIO()
2238
2239    def get_query_file_content(args: List[str], _) -> str:
2240      query_file_contents.write(_get_query_file_content(args))
2241      return ''
2242
2243    runner = self.create_bazel_test_runner(
2244        modules=[
2245            multi_config(host_unit_test_module(name='test1', path='path1')),
2246            multi_config(host_unit_test_module(name='test2', path='path2')),
2247            multi_config(test_module(name='test3', path='path3')),
2248        ],
2249        run_command=get_query_file_content,
2250        host=True,
2251    )
2252
2253    runner.get_test_runner_build_reqs([
2254        test_info_of('test2'),
2255        test_info_of('test1'),  # Intentionally out of order.
2256        test_info_of('test3'),
2257    ])
2258
2259    self.assertEqual(
2260        'deps(tests(//path1:test1_host + '
2261        '//path2:test2_host + '
2262        '//path3:test3_host))',
2263        query_file_contents.getvalue(),
2264    )
2265
2266  def test_query_bazel_test_targets_deps_without_host_arg(self):
2267    query_file_contents = StringIO()
2268
2269    def get_query_file_content(args: List[str], _) -> str:
2270      query_file_contents.write(_get_query_file_content(args))
2271      return ''
2272
2273    runner = self.create_bazel_test_runner(
2274        modules=[
2275            multi_config(host_unit_test_module(name='test1', path='path1')),
2276            host_unit_test_module(name='test2', path='path2'),
2277            multi_config(test_module(name='test3', path='path3')),
2278        ],
2279        run_command=get_query_file_content,
2280    )
2281
2282    runner.get_test_runner_build_reqs([
2283        test_info_of('test2'),
2284        test_info_of('test1'),
2285        test_info_of('test3'),
2286    ])
2287
2288    self.assertEqual(
2289        'deps(tests(//path1:test1_device + '
2290        '//path2:test2_host + '
2291        '//path3:test3_device))',
2292        query_file_contents.getvalue(),
2293    )
2294
2295  def test_trim_whitespace_in_bazel_query_output(self):
2296    run_command = self.mock_run_command(
2297        return_value='\n'.join(['  test1:host  ', 'test2:device  ', '  '])
2298    )
2299    runner = self.create_bazel_test_runner(
2300        modules=[
2301            supported_test_module(name='test1', path='path1'),
2302        ],
2303        run_command=run_command,
2304    )
2305
2306    reqs = runner.get_test_runner_build_reqs([test_info_of('test1')])
2307
2308    self.assertSetEqual({'test1-host', 'test2-target'}, reqs)
2309
2310  def test_build_variants_in_bazel_query_output(self):
2311    run_command = self.mock_run_command(
2312        return_value='\n'.join([
2313            'test1:host',
2314            'test2:host',
2315            'test2:device',
2316            'test3:device',
2317            'test4:host',
2318            'test4:host',
2319        ])
2320    )
2321    runner = self.create_bazel_test_runner(
2322        modules=[
2323            supported_test_module(name='test1', path='path1'),
2324            supported_test_module(name='test2', path='path2'),
2325            supported_test_module(name='test3', path='path3'),
2326            supported_test_module(name='test4', path='path4'),
2327        ],
2328        run_command=run_command,
2329    )
2330
2331    reqs = runner.get_test_runner_build_reqs([
2332        test_info_of('test1'),
2333        test_info_of('test2'),
2334        test_info_of('test3'),
2335        test_info_of('test4'),
2336    ])
2337
2338    self.assertSetEqual(
2339        {'test1-host', 'test2', 'test3-target', 'test4-host'}, reqs
2340    )
2341
2342  def test_generate_single_run_command(self):
2343    test_infos = [test_info_of('test1')]
2344    runner = self.create_bazel_test_runner_for_tests(test_infos)
2345
2346    cmd = runner.generate_run_commands(test_infos, {})
2347
2348    self.assertEqual(1, len(cmd))
2349
2350  def test_generate_run_command_containing_targets_with_host_arg(self):
2351    test_infos = [
2352        test_info_of('test1'),
2353        test_info_of('test2'),
2354        test_info_of('test3'),
2355    ]
2356    runner = self.create_bazel_test_runner(
2357        [
2358            multi_config(host_unit_test_module(name='test1', path='path')),
2359            multi_config(host_unit_test_module(name='test2', path='path')),
2360            multi_config(test_module(name='test3', path='path')),
2361        ],
2362        host=True,
2363    )
2364
2365    cmd = runner.generate_run_commands(test_infos, {})
2366
2367    self.assertTokensIn(
2368        ['//path:test1_host', '//path:test2_host', '//path:test3_host'], cmd[0]
2369    )
2370
2371  def test_generate_run_command_containing_targets_without_host_arg(self):
2372    test_infos = [test_info_of('test1'), test_info_of('test2')]
2373    runner = self.create_bazel_test_runner(
2374        [
2375            multi_config(host_unit_test_module(name='test1', path='path')),
2376            host_unit_test_module(name='test2', path='path'),
2377        ],
2378    )
2379
2380    cmd = runner.generate_run_commands(test_infos, {})
2381
2382    self.assertTokensIn(['//path:test1_device', '//path:test2_host'], cmd[0])
2383
2384  def test_generate_run_command_with_multi_bazel_args(self):
2385    test_infos = [test_info_of('test1')]
2386    runner = self.create_bazel_test_runner_for_tests(test_infos)
2387    extra_args = {
2388        constants.BAZEL_ARG: [['--option1=value1'], ['--option2=value2']]
2389    }
2390
2391    cmd = runner.generate_run_commands(test_infos, extra_args)
2392
2393    self.assertTokensIn(['--option1=value1', '--option2=value2'], cmd[0])
2394
2395  def test_generate_run_command_with_multi_custom_args(self):
2396    test_infos = [test_info_of('test1')]
2397    runner = self.create_bazel_test_runner_for_tests(test_infos)
2398    extra_args = {constants.CUSTOM_ARGS: ['-hello', '--world=value']}
2399
2400    cmd = runner.generate_run_commands(test_infos, extra_args)
2401
2402    self.assertTokensIn(
2403        ['--test_arg=-hello', '--test_arg=--world=value'], cmd[0]
2404    )
2405
2406  def test_generate_run_command_with_custom_and_bazel_args(self):
2407    test_infos = [test_info_of('test1')]
2408    runner = self.create_bazel_test_runner_for_tests(test_infos)
2409    extra_args = {
2410        constants.CUSTOM_ARGS: ['-hello', '--world=value'],
2411        constants.BAZEL_ARG: [['--option1=value1']],
2412    }
2413
2414    cmd = runner.generate_run_commands(test_infos, extra_args)
2415
2416    self.assertTokensIn(
2417        ['--test_arg=-hello', '--test_arg=--world=value', '--option1=value1'],
2418        cmd[0],
2419    )
2420
2421  def test_generate_run_command_removes_serial(self):
2422    test_infos = [test_info_of('test1')]
2423    runner = self.create_bazel_test_runner_for_tests(test_infos)
2424    extra_args = {constants.CUSTOM_ARGS: ['--serial=0.0.0.0']}
2425
2426    cmd = runner.generate_run_commands(test_infos, extra_args)
2427
2428    self.assertNotIn('--test-arg=--serial', shlex.split(cmd[0]))
2429    self.assertNotIn('--test-arg=--0.0.0.0', shlex.split(cmd[0]))
2430
2431  def test_generate_run_command_with_tf_supported_all_abi_arg(self):
2432    test_infos = [test_info_of('test1')]
2433    runner = self.create_bazel_test_runner_for_tests(test_infos)
2434    extra_args = {constants.ALL_ABI: True}
2435
2436    cmd = runner.generate_run_commands(test_infos, extra_args)
2437
2438    self.assertTokensIn(['--test_arg=--all-abi'], cmd[0])
2439
2440  def test_generate_run_command_with_iterations_args(self):
2441    test_infos = [test_info_of('test1')]
2442    runner = self.create_bazel_test_runner_for_tests(test_infos)
2443    extra_args = {constants.ITERATIONS: 2}
2444
2445    cmd = runner.generate_run_commands(test_infos, extra_args)
2446
2447    self.assertTokensIn(['--runs_per_test=2'], cmd[0])
2448    self.assertNotIn('--test_arg=--retry-strategy', shlex.split(cmd[0]))
2449
2450  def test_generate_run_command_with_testinfo_filter(self):
2451    test_filter = test_filter_of('class1', ['method1'])
2452    test_infos = [test_info_of('test1', test_filters=[test_filter])]
2453    runner = self.create_bazel_test_runner_for_tests(test_infos)
2454
2455    cmd = runner.generate_run_commands(test_infos, {})
2456
2457    self.assertTokensIn(
2458        [
2459            '--test_arg=--atest-include-filter',
2460            '--test_arg=test1:class1#method1',
2461        ],
2462        cmd[0],
2463    )
2464
2465  def test_generate_run_command_with_bes_publish_enabled(self):
2466    test_infos = [test_info_of('test1')]
2467    extra_args = {
2468        constants.BAZEL_MODE_FEATURES: [
2469            bazel_mode.Features.EXPERIMENTAL_BES_PUBLISH
2470        ]
2471    }
2472    build_metadata = bazel_mode.BuildMetadata(
2473        'master', 'aosp_cf_x86_64_phone-userdebug'
2474    )
2475    env = {
2476        'ATEST_BAZELRC': '/dir/atest.bazelrc',
2477        'ATEST_BAZEL_BES_PUBLISH_CONFIG': 'bes_publish',
2478    }
2479    runner = self.create_bazel_test_runner_for_tests(
2480        test_infos, build_metadata=build_metadata, env=env
2481    )
2482
2483    cmd = runner.generate_run_commands(
2484        test_infos,
2485        extra_args,
2486    )
2487
2488    self.assertTokensIn(
2489        [
2490            '--bazelrc=/dir/atest.bazelrc',
2491            '--config=bes_publish',
2492            '--build_metadata=ab_branch=master',
2493            '--build_metadata=ab_target=aosp_cf_x86_64_phone-userdebug',
2494        ],
2495        cmd[0],
2496    )
2497
2498  def test_generate_run_command_with_no_bazel_detailed_summary(self):
2499    test_infos = [test_info_of('test1')]
2500    extra_args = {
2501        constants.BAZEL_MODE_FEATURES: [
2502            bazel_mode.Features.NO_BAZEL_DETAILED_SUMMARY
2503        ]
2504    }
2505    runner = self.create_bazel_test_runner_for_tests(test_infos)
2506
2507    cmd = runner.generate_run_commands(test_infos, extra_args)
2508
2509    self.assertTokensNotIn(
2510        [
2511            '--test_summary=detailed',
2512        ],
2513        cmd[0],
2514    )
2515
2516  def test_generate_run_command_without_no_bazel_detailed_summary(self):
2517    test_infos = [test_info_of('test1')]
2518    extra_args = {}
2519    runner = self.create_bazel_test_runner_for_tests(test_infos)
2520
2521    cmd = runner.generate_run_commands(test_infos, extra_args)
2522
2523    self.assertTokensIn(
2524        [
2525            '--test_summary=detailed',
2526        ],
2527        cmd[0],
2528    )
2529
2530  def test_generate_run_command_with_return_until_failure(self):
2531    test_infos = [test_info_of('test1')]
2532    extra_args = {constants.RERUN_UNTIL_FAILURE: 5}
2533    runner = self.create_bazel_test_runner_for_tests(test_infos)
2534
2535    cmd = runner.generate_run_commands(test_infos, extra_args)
2536
2537    self.assertTokensIn(
2538        [
2539            '--test_arg=--retry-strategy',
2540            '--test_arg=RERUN_UNTIL_FAILURE',
2541            '--test_arg=--max-testcase-run-count',
2542            '--test_arg=5',
2543        ],
2544        cmd[0],
2545    )
2546
2547  def test_not_zip_test_output_files_when_bes_publish_not_enabled(self):
2548    test_infos = [test_info_of('test1')]
2549    extra_args = {}
2550    runner = self.create_bazel_test_runner_for_tests(test_infos)
2551
2552    cmd = runner.generate_run_commands(
2553        test_infos,
2554        extra_args,
2555    )
2556
2557    self.assertTokensIn(
2558        [
2559            '--nozip_undeclared_test_outputs',
2560        ],
2561        cmd[0],
2562    )
2563
2564  def test_zip_test_output_files_when_bes_publish_enabled(self):
2565    test_infos = [test_info_of('test1')]
2566    extra_args = {
2567        constants.BAZEL_MODE_FEATURES: [
2568            bazel_mode.Features.EXPERIMENTAL_BES_PUBLISH
2569        ]
2570    }
2571    build_metadata = bazel_mode.BuildMetadata(
2572        'master', 'aosp_cf_x86_64_phone-userdebug'
2573    )
2574    env = {
2575        'ATEST_BAZELRC': '/dir/atest.bazelrc',
2576        'ATEST_BAZEL_BES_PUBLISH_CONFIG': 'bes_publish',
2577    }
2578    runner = self.create_bazel_test_runner_for_tests(
2579        test_infos, build_metadata=build_metadata, env=env
2580    )
2581
2582    cmd = runner.generate_run_commands(
2583        test_infos,
2584        extra_args,
2585    )
2586
2587    self.assertTokensNotIn(
2588        [
2589            '--nozip_undeclared_test_outputs',
2590        ],
2591        cmd[0],
2592    )
2593
2594  def test_generate_run_command_with_remote_enabled(self):
2595    test_infos = [test_info_of('test1')]
2596    extra_args = {
2597        constants.BAZEL_MODE_FEATURES: [bazel_mode.Features.EXPERIMENTAL_REMOTE]
2598    }
2599    env = {
2600        'ATEST_BAZELRC': '/dir/atest.bazelrc',
2601        'ATEST_BAZEL_REMOTE_CONFIG': 'remote_deviceless',
2602    }
2603    runner = self.create_bazel_test_runner_for_tests(test_infos, env=env)
2604
2605    cmd = runner.generate_run_commands(
2606        test_infos,
2607        extra_args,
2608    )
2609
2610    self.assertTokensIn(
2611        [
2612            '--config=remote_deviceless',
2613        ],
2614        cmd[0],
2615    )
2616
2617  def test_generate_run_command_with_remote_avd_enabled(self):
2618    test_infos = [test_info_of('test1')]
2619    extra_args = {
2620        constants.BAZEL_MODE_FEATURES: [
2621            bazel_mode.Features.EXPERIMENTAL_REMOTE_AVD
2622        ]
2623    }
2624    env = {
2625        'ATEST_BAZELRC': '/dir/atest.bazelrc',
2626        'ATEST_BAZEL_REMOTE_AVD_CONFIG': 'remote_avd',
2627    }
2628    runner = self.create_bazel_test_runner_for_tests(test_infos, env=env)
2629
2630    cmd = runner.generate_run_commands(
2631        test_infos,
2632        extra_args,
2633    )
2634
2635    self.assertTokensIn(
2636        [
2637            '--config=remote_avd',
2638        ],
2639        cmd[0],
2640    )
2641
2642  def test_generate_run_command_with_remote_avd_config_not_found(self):
2643    test_infos = [test_info_of('test1')]
2644    extra_args = {
2645        constants.BAZEL_MODE_FEATURES: [
2646            bazel_mode.Features.EXPERIMENTAL_REMOTE_AVD
2647        ]
2648    }
2649    env = {
2650        'ATEST_BAZELRC': '/dir/atest.bazelrc',
2651    }
2652    runner = self.create_bazel_test_runner_for_tests(test_infos, env=env)
2653
2654    with self.assertRaises(Exception) as context:
2655      runner.generate_run_commands(
2656          test_infos,
2657          extra_args,
2658      )
2659
2660    self.assertIn(
2661        'ATEST_BAZEL_REMOTE_AVD_CONFIG environment variable is not set.',
2662        str(context.exception),
2663    )
2664
2665  def test_generate_run_command_with_verbose_args(self):
2666    test_infos = [test_info_of('test1')]
2667    runner = self.create_bazel_test_runner_for_tests(test_infos)
2668    extra_args = {constants.VERBOSE: True}
2669
2670    cmd = runner.generate_run_commands(test_infos, extra_args)
2671
2672    self.assertTokensIn(['--test_output=all'], cmd[0])
2673
2674  def test_disable_test_result_caching_with_wait_for_debug_args(self):
2675    test_infos = [test_info_of('test1')]
2676    runner = self.create_bazel_test_runner_for_tests(test_infos)
2677    extra_args = {constants.WAIT_FOR_DEBUGGER: True}
2678
2679    cmd = runner.generate_run_commands(test_infos, extra_args)
2680
2681    self.assertTokensIn(
2682        ['--test_arg=--wait-for-debugger', '--cache_test_results=no'], cmd[0]
2683    )
2684
2685  def test_cache_test_results_arg_not_used_with_wait_for_debug_args(self):
2686    test_infos = [test_info_of('test1')]
2687    runner = self.create_bazel_test_runner_for_tests(test_infos)
2688    extra_args = {
2689        constants.WAIT_FOR_DEBUGGER: True,
2690        constants.BAZEL_ARG: [['--cache_test_resultsfoo']],
2691    }
2692
2693    cmd = runner.generate_run_commands(test_infos, extra_args)
2694
2695    self.assertTokensNotIn(['--cache_test_resultsfoo'], cmd[0])
2696
2697  def test_retrieve_test_output_info_for_host_test(self):
2698    test_infos = [test_info_of('test1')]
2699    runner = self.create_bazel_test_runner_for_tests(test_infos)
2700
2701    output_file_path, package_name, target_suffix = (
2702        runner.retrieve_test_output_info(test_infos[0])
2703    )
2704
2705    self.assertEqual(
2706        f'/src/workspace/{bazel_mode.BAZEL_TEST_LOGS_DIR_NAME}'
2707        f'/path/test1_host/{bazel_mode.TEST_OUTPUT_DIR_NAME}',
2708        str(output_file_path),
2709    )
2710    self.assertEqual('path', package_name)
2711    self.assertEqual('host', target_suffix)
2712
2713  def test_retrieve_test_output_info_for_device_driven_test(self):
2714    runner = self.create_bazel_test_runner(
2715        modules=[
2716            multi_config(device_test_module(name='test1', path='path1')),
2717        ],
2718    )
2719
2720    output_file_path, package_name, target_suffix = (
2721        runner.retrieve_test_output_info(test_info_of('test1'))
2722    )
2723
2724    self.assertEqual(
2725        f'/src/workspace/{bazel_mode.BAZEL_TEST_LOGS_DIR_NAME}'
2726        f'/path1/test1_device/{bazel_mode.TEST_OUTPUT_DIR_NAME}',
2727        str(output_file_path),
2728    )
2729    self.assertEqual('path1', package_name)
2730    self.assertEqual('device', target_suffix)
2731
2732  def test_result_dir_symlink_to_test_output_dir(self):
2733    self.setUpPyfakefs()
2734    test_infos = [test_info_of('test1')]
2735    runner = self.create_bazel_test_runner_for_tests(test_infos)
2736
2737    runner.organize_test_logs(test_infos)
2738
2739    self.assertSymlinkTo(
2740        Path('result_dir/log/path/test1_host'),
2741        Path(
2742            f'/src/workspace/{bazel_mode.BAZEL_TEST_LOGS_DIR_NAME}'
2743            f'/path/test1_host/{bazel_mode.TEST_OUTPUT_DIR_NAME}'
2744        ),
2745    )
2746
2747  def test_not_create_result_log_dir_when_test_output_zip_exist(self):
2748    self.setUpPyfakefs()
2749    test_infos = [test_info_of('test1')]
2750    runner = self.create_bazel_test_runner_for_tests(test_infos)
2751    test_output_zip = Path(
2752        f'/src/workspace/{bazel_mode.BAZEL_TEST_LOGS_DIR_NAME}'
2753        f'/path/test1_host/{bazel_mode.TEST_OUTPUT_DIR_NAME}'
2754        f'/{bazel_mode.TEST_OUTPUT_ZIP_NAME}'
2755    )
2756    self.fs.create_file(test_output_zip, contents='')
2757
2758    runner.organize_test_logs(test_infos)
2759
2760    self.assertFalse(Path('result_dir/log/').exists())
2761
2762  def test_avoid_result_dir_symlink_duplication(self):
2763    self.setUpPyfakefs()
2764    test_infos = [test_info_of('test1')]
2765    old_symlink_to_dir = Path(tempfile.mkdtemp())
2766    log_path = Path('result_dir/log/path/test1_host')
2767    log_path.parent.mkdir(parents=True, exist_ok=True)
2768    log_path.symlink_to(old_symlink_to_dir)
2769    runner = self.create_bazel_test_runner_for_tests(test_infos)
2770
2771    runner.organize_test_logs(test_infos)
2772
2773    self.assertSymlinkTo(log_path, old_symlink_to_dir)
2774
2775  def create_bazel_test_runner(
2776      self,
2777      modules,
2778      run_command=None,
2779      host=False,
2780      build_metadata=None,
2781      env=None,
2782      enable_features=None,
2783  ):
2784    return bazel_mode.BazelTestRunner(
2785        'result_dir',
2786        mod_info=create_module_info(modules),
2787        src_top=Path('/src'),
2788        workspace_path=Path('/src/workspace'),
2789        run_command=run_command or self.mock_run_command(),
2790        extra_args={constants.HOST: host},
2791        build_metadata=build_metadata,
2792        env=env,
2793        generate_workspace_fn=lambda *_: None,
2794        enabled_features=enable_features or [],
2795    )
2796
2797  def create_bazel_test_runner_for_tests(
2798      self, test_infos, build_metadata=None, env=None
2799  ):
2800    return self.create_bazel_test_runner(
2801        modules=[
2802            supported_test_module(name=t.test_name, path='path')
2803            for t in test_infos
2804        ],
2805        build_metadata=build_metadata,
2806        env=env,
2807    )
2808
2809  def create_completed_process(self, args, returncode, stdout):
2810    return subprocess.CompletedProcess(args, returncode, stdout)
2811
2812  def mock_run_command(self, **kwargs):
2813    return mock.create_autospec(bazel_mode.default_run_command, **kwargs)
2814
2815  def assertTokensIn(self, expected_tokens, s):
2816    tokens = shlex.split(s)
2817    for token in expected_tokens:
2818      self.assertIn(token, tokens)
2819
2820  def assertTokensNotIn(self, unexpected_tokens, s):
2821    tokens = shlex.split(s)
2822    for token in unexpected_tokens:
2823      self.assertNotIn(token, tokens)
2824
2825  def assertSymlinkTo(self, symlink_path, target_path):
2826    self.assertEqual(symlink_path.resolve(strict=False), target_path)
2827
2828
2829class FeatureParserTest(unittest.TestCase):
2830  """Tests for parsing Bazel mode feature flags."""
2831
2832  def test_parse_args_with_bazel_mode_feature(self):
2833    parser = argparse.ArgumentParser()
2834    bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features')
2835    # pylint: disable=no-member
2836    args = parser.parse_args([bazel_mode.Features.NULL_FEATURE.arg_flag])
2837
2838    self.assertListEqual(
2839        [bazel_mode.Features.NULL_FEATURE], args.bazel_mode_features
2840    )
2841
2842  def test_parse_args_without_bazel_mode_feature(self):
2843    parser = argparse.ArgumentParser()
2844    parser.add_argument('--foo', action='append_const', const='foo', dest='foo')
2845    bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features')
2846    args = parser.parse_args(['--foo'])
2847
2848    self.assertIsNone(args.bazel_mode_features)
2849
2850
2851def test_info_of(module_name, test_filters=None):
2852  return test_info.TestInfo(
2853      module_name,
2854      BAZEL_RUNNER,
2855      [],
2856      data={constants.TI_FILTER: frozenset(test_filters)}
2857      if test_filters
2858      else None,
2859  )
2860
2861
2862def test_filter_of(class_name, methods=None):
2863  return test_info.TestFilter(
2864      class_name, frozenset(methods) if methods else frozenset()
2865  )
2866
2867
2868def _get_query_file_content(args: List[str]) -> str:
2869  for arg in args:
2870    if arg.startswith('--query_file='):
2871      return Path(arg.split('=')[1]).read_text(encoding='utf-8')
2872
2873  raise FileNotFoundError('Query file not found!')
2874
2875
2876if __name__ == '__main__':
2877  unittest.main()
2878