• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2021, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Unit tests for bazel_mode."""
18# pylint: disable=invalid-name
19# pylint: disable=missing-function-docstring
20# pylint: disable=too-many-lines
21
22import argparse
23import re
24import shlex
25import shutil
26import subprocess
27import tempfile
28import unittest
29
30from io import StringIO
31from pathlib import Path
32from typing import List
33from unittest import mock
34
35# pylint: disable=import-error
36from pyfakefs import fake_filesystem_unittest
37
38from atest import bazel_mode
39from atest import constants
40from atest import module_info
41
42from atest.test_finders import example_finder, test_finder_base, test_info
43from atest.test_runners import atest_tf_test_runner
44
45
46ATEST_TF_RUNNER = atest_tf_test_runner.AtestTradefedTestRunner.NAME
47BAZEL_RUNNER = bazel_mode.BazelTestRunner.NAME
48MODULE_BUILD_TARGETS = {'foo1', 'foo2', 'foo3'}
49MODULE_NAME = 'foo'
50
51
52class GenerationTestFixture(fake_filesystem_unittest.TestCase):
53    """Fixture for workspace generation tests."""
54
55    def setUp(self):
56        self.setUpPyfakefs()
57
58        self._src_root_path = Path('/src')
59        self.out_dir_path = self._src_root_path.joinpath('out')
60        self.out_dir_path.mkdir(parents=True)
61        self.product_out_path = self.out_dir_path.joinpath('product')
62        self.host_out_path = self.out_dir_path.joinpath('host')
63        self.workspace_out_path = self.out_dir_path.joinpath('workspace')
64
65        self._resource_root = self._src_root_path.joinpath(
66            'tools/asuite/atest/bazel')
67
68        self.workspace_md5_checksum = self.workspace_out_path.joinpath(
69            'workspace_md5_checksum')
70        self.resource_manager = bazel_mode.ResourceManager(
71            src_root_path=self._src_root_path,
72            resource_root_path=self._resource_root,
73            product_out_path=self.product_out_path,
74            md5_checksum_file_path = self.workspace_md5_checksum
75        )
76
77        bazel_rules = self.resource_manager.get_resource_file_path('rules')
78        bazel_rules.mkdir(parents=True)
79        self.rules_bzl_file = bazel_rules.joinpath('rules.bzl')
80        self.rules_bzl_file.touch()
81
82        bazel_configs = self.resource_manager.get_resource_file_path('configs')
83        bazel_configs.mkdir(parents=True)
84        bazel_configs.joinpath('configs.bzl').touch()
85
86        self.resource_manager.get_resource_file_path('WORKSPACE').touch()
87        self.resource_manager.get_resource_file_path('bazelrc').touch()
88
89    def create_workspace_generator(
90        self,
91        modules=None,
92        enabled_features=None,
93        jdk_path=None,
94    ):
95        mod_info = self.create_module_info(modules)
96
97        generator = bazel_mode.WorkspaceGenerator(
98            resource_manager=self.resource_manager,
99            workspace_out_path=self.workspace_out_path,
100            host_out_path=self.host_out_path,
101            build_out_dir=self.out_dir_path,
102            mod_info=mod_info,
103            jdk_path=jdk_path,
104            enabled_features=enabled_features,
105        )
106
107        return generator
108
109    def run_generator(self, mod_info, enabled_features=None, jdk_path=None):
110        generator = bazel_mode.WorkspaceGenerator(
111            resource_manager=self.resource_manager,
112            workspace_out_path=self.workspace_out_path,
113            host_out_path=self.host_out_path,
114            build_out_dir=self.out_dir_path,
115            mod_info=mod_info,
116            jdk_path=jdk_path,
117            enabled_features=enabled_features,
118        )
119
120        generator.generate()
121
122    # pylint: disable=protected-access
123    def create_empty_module_info(self):
124        fake_temp_file = self.product_out_path.joinpath(
125            next(tempfile._get_candidate_names()))
126        self.fs.create_file(fake_temp_file, contents='{}')
127        return module_info.ModuleInfo(module_file=fake_temp_file)
128
129    def create_module_info(self, modules=None):
130        mod_info = self.create_empty_module_info()
131        modules = modules or []
132
133        prerequisites = frozenset().union(
134            bazel_mode.TestTarget.DEVICE_TEST_PREREQUISITES,
135            bazel_mode.TestTarget.DEVICELESS_TEST_PREREQUISITES)
136
137        for module_name in prerequisites:
138            info = host_module(name=module_name, path='prebuilts')
139            info[constants.MODULE_INFO_ID] = module_name
140            mod_info.name_to_module_info[module_name] = info
141
142        for m in modules:
143            m[constants.MODULE_INFO_ID] = m['module_name']
144            mod_info.name_to_module_info[m['module_name']] = m
145            for path in m['path']:
146                if path in mod_info.path_to_module_info:
147                    mod_info.path_to_module_info[path].append(m)
148                else:
149                    mod_info.path_to_module_info[path] = [m]
150
151        return mod_info
152
153    def assertSymlinkTo(self, symlink_path, target_path):
154        self.assertEqual(symlink_path.resolve(strict=False), target_path)
155
156    def assertTargetInWorkspace(self, name, package=''):
157        build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
158        contents = build_file.read_text(encoding='utf8')
159        occurrences = len(self.find_target_by_name(name, contents))
160
161        if occurrences == 1:
162            return
163
164        cardinality = 'Multiple' if occurrences else 'Zero'
165        self.fail(
166            f'{cardinality} targets named \'{name}\' found in \'{contents}\''
167        )
168
169    def assertTargetNotInWorkspace(self, name, package=''):
170        build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
171
172        if not build_file.exists():
173            return
174
175        contents = build_file.read_text(encoding='utf8')
176        matches = self.find_target_by_name(name, contents)
177
178        if not matches:
179            return
180
181        self.fail(
182            f'Unexpectedly found target(s) named \'{name}\' in \'{contents}\''
183        )
184
185    def assertInBuildFile(self, substring, package=''):
186        build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
187        self.assertIn(substring, build_file.read_text(encoding='utf8'))
188
189    def assertNotInBuildFile(self, substring, package=''):
190        build_file = self.workspace_out_path.joinpath(package, 'BUILD.bazel')
191        self.assertNotIn(substring, build_file.read_text(encoding='utf8'))
192
193    def assertFileInWorkspace(self, relative_path, package=''):
194        path = self.workspace_out_path.joinpath(package, relative_path)
195        self.assertTrue(path.exists())
196
197    def assertDirInWorkspace(self, relative_path, package=''):
198        path = self.workspace_out_path.joinpath(package, relative_path)
199        self.assertTrue(path.is_dir())
200
201    def assertFileNotInWorkspace(self, relative_path, package=''):
202        path = self.workspace_out_path.joinpath(package, relative_path)
203        self.assertFalse(path.exists())
204
205    def find_target_by_name(self, name: str, contents: str) -> List[str]:
206        return re.findall(rf'\bname\s*=\s*"{name}"', contents)
207
208
209class BasicWorkspaceGenerationTest(GenerationTestFixture):
210    """Tests for basic workspace generation and update."""
211
212    def test_generate_workspace_when_nonexistent(self):
213        workspace_generator = self.create_workspace_generator()
214        shutil.rmtree(workspace_generator.workspace_out_path,
215                      ignore_errors=True)
216
217        workspace_generator.generate()
218
219        self.assertTrue(workspace_generator.workspace_out_path.is_dir())
220
221    def test_regenerate_workspace_when_features_changed(self):
222        workspace_generator = self.create_workspace_generator(
223            enabled_features={bazel_mode.Features.NULL_FEATURE})
224        workspace_generator.generate()
225        workspace_stat = workspace_generator.workspace_out_path.stat()
226
227        workspace_generator = self.create_workspace_generator()
228        workspace_generator.generate()
229        new_workspace_stat = workspace_generator.workspace_out_path.stat()
230
231        self.assertNotEqual(workspace_stat, new_workspace_stat)
232
233    def test_not_regenerate_when_feature_does_not_affect_workspace(self):
234        workspace_generator = self.create_workspace_generator(
235            enabled_features={bazel_mode.Features.NULL_FEATURE})
236        workspace_generator.generate()
237        workspace_stat = workspace_generator.workspace_out_path.stat()
238
239        parser = argparse.ArgumentParser()
240        bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features')
241        # pylint: disable=no-member
242        args = parser.parse_args([
243            bazel_mode.Features.NULL_FEATURE.arg_flag,
244            '--experimental-bes-publish'
245        ])
246        workspace_generator = self.create_workspace_generator(
247            enabled_features=set(args.bazel_mode_features))
248        workspace_generator.generate()
249        new_workspace_stat = workspace_generator.workspace_out_path.stat()
250
251        self.assertEqual(workspace_stat, new_workspace_stat)
252
253    def test_not_regenerate_workspace_when_features_unchanged(self):
254        workspace_generator = self.create_workspace_generator(
255            enabled_features={bazel_mode.Features.NULL_FEATURE})
256        workspace_generator.generate()
257        workspace_stat = workspace_generator.workspace_out_path.stat()
258
259        workspace_generator = self.create_workspace_generator(
260            enabled_features={bazel_mode.Features.NULL_FEATURE})
261        workspace_generator.generate()
262        new_workspace_stat = workspace_generator.workspace_out_path.stat()
263
264        self.assertEqual(workspace_stat, new_workspace_stat)
265
266    def test_regenerate_workspace_when_module_info_deleted(self):
267        workspace_generator = self.create_workspace_generator()
268        workspace_generator.generate()
269        workspace_stat = workspace_generator.workspace_out_path.stat()
270
271        workspace_generator.mod_info.mod_info_file_path.unlink()
272        workspace_generator = self.create_workspace_generator()
273        workspace_generator.generate()
274
275        new_workspace_stat = workspace_generator.workspace_out_path.stat()
276        self.assertNotEqual(workspace_stat, new_workspace_stat)
277
278    def test_not_regenerate_workspace_when_module_info_unchanged(self):
279        workspace_generator1 = self.create_workspace_generator()
280        workspace_generator1.generate()
281        workspace_stat = workspace_generator1.workspace_out_path.stat()
282
283        workspace_generator2 = self.create_workspace_generator()
284        workspace_generator2.generate()
285        new_workspace_stat = workspace_generator2.workspace_out_path.stat()
286
287        self.assertEqual(workspace_stat, new_workspace_stat)
288
289    def test_not_regenerate_workspace_when_module_only_touched(self):
290        workspace_generator = self.create_workspace_generator()
291        workspace_generator.generate()
292        workspace_stat = workspace_generator.workspace_out_path.stat()
293
294        Path(workspace_generator.mod_info.mod_info_file_path).touch()
295        workspace_generator = self.create_workspace_generator()
296        workspace_generator.generate()
297
298        new_workspace_stat = workspace_generator.workspace_out_path.stat()
299        self.assertEqual(workspace_stat, new_workspace_stat)
300
301    def test_regenerate_workspace_when_module_info_changed(self):
302        workspace_generator = self.create_workspace_generator()
303        workspace_generator.generate()
304        workspace_stat = workspace_generator.workspace_out_path.stat()
305
306        mod_info_file_path = workspace_generator.mod_info.mod_info_file_path
307        with open(mod_info_file_path, 'a', encoding='utf8') as f:
308            f.write(' ')
309        workspace_generator = self.create_workspace_generator()
310        workspace_generator.generate()
311
312        new_workspace_stat = workspace_generator.workspace_out_path.stat()
313        self.assertNotEqual(workspace_stat, new_workspace_stat)
314
315    def test_regenerate_workspace_when_md5_file_removed(self):
316        workspace_generator = self.create_workspace_generator()
317        workspace_generator.generate()
318        workspace_stat = workspace_generator.workspace_out_path.stat()
319
320        self.workspace_md5_checksum.unlink()
321        workspace_generator = self.create_workspace_generator()
322        workspace_generator.generate()
323        new_workspace_stat = workspace_generator.workspace_out_path.stat()
324
325        self.assertNotEqual(workspace_stat, new_workspace_stat)
326
327    def test_regenerate_workspace_when_md5_file_is_broken(self):
328        workspace_generator = self.create_workspace_generator()
329        workspace_generator.generate()
330        workspace_stat = workspace_generator.workspace_out_path.stat()
331
332        self.workspace_md5_checksum.write_text('broken checksum file')
333        workspace_generator = self.create_workspace_generator()
334        workspace_generator.generate()
335        new_workspace_stat = workspace_generator.workspace_out_path.stat()
336
337        self.assertNotEqual(workspace_stat, new_workspace_stat)
338
339    def test_not_regenerate_workspace_when_workspace_files_unaffected(self):
340        workspace_generator = self.create_workspace_generator()
341        workspace_generator.generate()
342        workspace_stat = workspace_generator.workspace_out_path.stat()
343
344        workspace_generator = self.create_workspace_generator()
345        workspace_generator.generate()
346        new_workspace_stat = workspace_generator.workspace_out_path.stat()
347
348        self.assertEqual(workspace_stat, new_workspace_stat)
349
350    def test_scrub_old_workspace_when_regenerating(self):
351        workspace_generator = self.create_workspace_generator()
352        workspace_generator.generate()
353        some_file = workspace_generator.workspace_out_path.joinpath('some_file')
354        some_file.touch()
355        self.assertTrue(some_file.is_file())
356
357        # Remove the module_info file to regenerate the workspace.
358        workspace_generator.mod_info.mod_info_file_path.unlink()
359        workspace_generator = self.create_workspace_generator()
360        workspace_generator.generate()
361
362        self.assertFalse(some_file.is_file())
363
364    def test_regenerate_workspace_when_resource_file_changed(self):
365        workspace_generator = self.create_workspace_generator()
366        workspace_generator.generate()
367        workspace_stat = workspace_generator.workspace_out_path.stat()
368
369        with open(self.rules_bzl_file, 'a', encoding='utf8') as f:
370            f.write(' ')
371        workspace_generator = self.create_workspace_generator()
372        workspace_generator.generate()
373
374        new_workspace_stat = workspace_generator.workspace_out_path.stat()
375        self.assertNotEqual(workspace_stat, new_workspace_stat)
376
377    def test_not_regenerate_workspace_when_resource_file_only_touched(self):
378        workspace_generator = self.create_workspace_generator()
379        workspace_generator.generate()
380        workspace_stat = workspace_generator.workspace_out_path.stat()
381
382        self.rules_bzl_file.touch()
383        workspace_generator = self.create_workspace_generator()
384        workspace_generator.generate()
385
386        new_workspace_stat = workspace_generator.workspace_out_path.stat()
387        self.assertEqual(workspace_stat, new_workspace_stat)
388
389    def test_copy_workspace_resources(self):
390        gen = self.create_workspace_generator()
391
392        gen.generate()
393
394        self.assertFileInWorkspace('WORKSPACE')
395        self.assertFileInWorkspace('.bazelrc')
396        self.assertDirInWorkspace('bazel/rules')
397        self.assertDirInWorkspace('bazel/configs')
398
399    def test_generated_target_name(self):
400        mod_info = self.create_module_info(modules=[
401            host_unit_test_module(name='hello_world_test')
402        ])
403        info = mod_info.get_module_info('hello_world_test')
404        info[constants.MODULE_INFO_ID] = 'new_hello_world_test'
405
406        self.run_generator(mod_info)
407
408        self.assertTargetInWorkspace('new_hello_world_test')
409        self.assertTargetNotInWorkspace('hello_world_test')
410
411    def test_generate_host_unit_test_module_target(self):
412        mod_info = self.create_module_info(modules=[
413            host_unit_test_module(name='hello_world_test')
414        ])
415
416        self.run_generator(mod_info)
417
418        self.assertTargetInWorkspace('hello_world_test_host')
419
420    def test_not_generate_host_test_module_target(self):
421        mod_info = self.create_module_info(modules=[
422            host_test_module(name='hello_world_test'),
423        ])
424
425        self.run_generator(mod_info)
426
427        self.assertTargetNotInWorkspace('hello_world_test')
428
429    def test_not_generate_test_module_target_with_invalid_installed_path(self):
430        mod_info = self.create_module_info(modules=[
431            test_module(name='hello_world_test', installed='out/invalid/path')
432        ])
433
434        self.run_generator(mod_info)
435
436        self.assertTargetNotInWorkspace('hello_world_test_device')
437        self.assertTargetNotInWorkspace('hello_world_test_host')
438
439    def test_generate_variable_file(self):
440        gen = self.create_workspace_generator()
441
442        gen.generate()
443
444        self.assertFileInWorkspace('BUILD.bazel')
445        self.assertFileInWorkspace('constants.bzl')
446
447
448class MultiConfigUnitTestModuleTestTargetGenerationTest(GenerationTestFixture):
449    """Tests for test target generation of test modules with multi-configs."""
450
451    def test_generate_test_rule_imports(self):
452        mod_info = self.create_module_info(modules=[
453            multi_config(host_unit_suite(test_module(
454                name='hello_world_test', path='example/tests'))),
455        ])
456
457        self.run_generator(mod_info, enabled_features=set([
458            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
459
460        self.assertInBuildFile(
461            'load("//bazel/rules:tradefed_test.bzl",'
462            ' "tradefed_device_driven_test", "tradefed_deviceless_test")\n',
463            package='example/tests',
464        )
465
466    def test_not_generate_device_test_import_when_feature_disabled(self):
467        mod_info = self.create_module_info(modules=[
468            multi_config(host_unit_suite(test_module(
469                name='hello_world_test', path='example/tests'))),
470        ])
471
472        self.run_generator(mod_info)
473
474        self.assertInBuildFile(
475            'load("//bazel/rules:tradefed_test.bzl",'
476            ' "tradefed_deviceless_test")\n',
477            package='example/tests',
478        )
479
480    def test_generate_test_targets(self):
481        mod_info = self.create_module_info(modules=[
482            multi_config(host_unit_suite(test_module(
483                name='hello_world_test', path='example/tests'))),
484        ])
485
486        self.run_generator(mod_info, enabled_features=set([
487            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
488
489        self.assertTargetInWorkspace('hello_world_test_device',
490                                     package='example/tests')
491        self.assertTargetInWorkspace('hello_world_test_host',
492                                     package='example/tests')
493
494    def test_not_generate_device_test_target_when_feature_disabled(self):
495        mod_info = self.create_module_info(modules=[
496            multi_config(host_unit_suite(test_module(
497                name='hello_world_test', path='example/tests'))),
498        ])
499
500        self.run_generator(mod_info)
501
502        self.assertTargetNotInWorkspace('hello_world_test_device',
503                                        package='example/tests')
504        self.assertTargetInWorkspace('hello_world_test_host',
505                                     package='example/tests')
506
507
508class DeviceTestModuleTestTargetGenerationTest(GenerationTestFixture):
509    """Tests for device test module test target generation."""
510
511    def test_generate_device_driven_test_target(self):
512        mod_info = self.create_module_info(modules=[
513            device_test_module(
514                name='hello_world_test', path='example/tests'),
515        ])
516
517        self.run_generator(mod_info, enabled_features=set([
518            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
519
520        self.assertInBuildFile(
521            'load("//bazel/rules:tradefed_test.bzl",'
522            ' "tradefed_device_driven_test")\n',
523            package='example/tests',
524        )
525        self.assertTargetInWorkspace('hello_world_test_device',
526                                     package='example/tests')
527
528    def test_generate_target_with_suites(self):
529        mod_info = self.create_module_info(modules=[
530            device_test_module(
531                name='hello_world_test',
532                path='example/tests',
533                compatibility_suites=['cts', 'mts']),
534        ])
535
536        self.run_generator(mod_info, enabled_features=set([
537            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
538
539        self.assertInBuildFile(
540            '    suites = [\n'
541            '        "cts",\n'
542            '        "mts",\n'
543            '    ],\n',
544            package='example/tests',
545        )
546
547    def test_generate_target_with_host_dependencies(self):
548        mod_info = self.create_module_info(modules=[
549            device_test_module(
550                name='hello_world_test',
551                path='example/tests',
552                host_dependencies=['vts_dep', 'cts_dep']),
553            host_module(name='vts_dep'),
554            host_module(name='cts_dep'),
555        ])
556
557        self.run_generator(mod_info, enabled_features=set([
558            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
559
560        self.assertInBuildFile(
561            '    tradefed_deps = [\n'
562            '        "//:cts_dep",\n'
563            '        "//:vts_dep",\n'
564            '    ],\n',
565            package='example/tests',
566        )
567
568    def test_generate_target_with_device_dependencies(self):
569        mod_info = self.create_module_info(modules=[
570            host_test_module(
571                name='hello_world_test',
572                path='example/tests',
573                target_dependencies=['helper_app']),
574            device_module(name='helper_app'),
575        ])
576
577        self.run_generator(mod_info, enabled_features=set([
578            bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST]))
579
580        self.assertInBuildFile(
581            '    device_data = [\n'
582            '        "//:helper_app",\n'
583            '    ],\n',
584            package='example/tests',
585        )
586
587    def test_generate_target_with_tags(self):
588        mod_info = self.create_module_info(modules=[
589            device_test_module(
590                name='hello_world_test',
591                path='example/tests',
592                test_options_tags=['no-remote']),
593        ])
594
595        self.run_generator(mod_info, enabled_features=set([
596            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
597
598        self.assertInBuildFile(
599            '    tags = [\n'
600            '        "no-remote",\n'
601            '    ],\n',
602            package='example/tests',
603        )
604
605    def test_generate_host_driven_test_target(self):
606        mod_info = self.create_module_info(modules=[
607            host_test_module(
608                name='hello_world_test', path='example/tests'),
609        ])
610
611        self.run_generator(mod_info, enabled_features=set([
612            bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST]))
613
614        self.assertInBuildFile(
615            'tradefed_host_driven_device_test(', package='example/tests')
616
617    def test_generate_multi_config_device_test_target(self):
618        mod_info = self.create_module_info(modules=[
619            multi_config(test_module(
620                name='hello_world_test', path='example/tests')),
621        ])
622
623        self.run_generator(mod_info, enabled_features=set([
624            bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST,
625            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
626
627        self.assertInBuildFile(
628            'load("//bazel/rules:tradefed_test.bzl", '
629            '"tradefed_device_driven_test", '
630            '"tradefed_host_driven_device_test")\n',
631            package='example/tests',
632        )
633        self.assertTargetInWorkspace('hello_world_test_device',
634                                     package='example/tests')
635        self.assertTargetInWorkspace('hello_world_test_host',
636                                     package='example/tests')
637
638    def test_not_generate_host_driven_test_target_when_feature_disabled(self):
639        mod_info = self.create_module_info(modules=[
640            multi_config(test_module(
641                name='hello_world_test', path='example/tests')),
642        ])
643
644        self.run_generator(mod_info, enabled_features=set([
645            bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
646
647        self.assertTargetInWorkspace('hello_world_test_device',
648                                     package='example/tests')
649        self.assertTargetNotInWorkspace('hello_world_test_host',
650                                        package='example/tests')
651
652    def test_raise_when_prerequisite_not_in_module_info(self):
653        mod_info = self.create_module_info(modules=[
654            device_test_module(),
655        ])
656        del mod_info.name_to_module_info['aapt']
657
658        with self.assertRaises(Exception) as context:
659            self.run_generator(mod_info, enabled_features=set([
660                bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST]))
661
662        self.assertIn('aapt', str(context.exception))
663
664
665class HostUnitTestModuleTestTargetGenerationTest(GenerationTestFixture):
666    """Tests for host unit test module test target generation."""
667
668    def test_generate_deviceless_test_import(self):
669        mod_info = self.create_module_info(modules=[
670            host_unit_test_module(name='hello_world_test'),
671        ])
672
673        self.run_generator(mod_info)
674
675        self.assertInBuildFile(
676            'load("//bazel/rules:tradefed_test.bzl",'
677            ' "tradefed_deviceless_test")\n'
678        )
679
680    def test_generate_deviceless_test_target(self):
681        mod_info = self.create_module_info(modules=[
682            host_unit_test_module(
683                name='hello_world_test', path='example/tests'),
684        ])
685
686        self.run_generator(mod_info)
687
688        self.assertInBuildFile(
689            'tradefed_deviceless_test(\n'
690            '    name = "hello_world_test_host",\n'
691            '    module_name = "hello_world_test",\n'
692            '    test = "//example/tests:hello_world_test",\n'
693            ')',
694            package='example/tests',
695        )
696
697    def test_generate_target_with_tags(self):
698        mod_info = self.create_module_info(modules=[
699            host_unit_test_module(
700                name='hello_world_test',
701                path='example/tests',
702                test_options_tags=['no-remote']),
703        ])
704
705        self.run_generator(mod_info)
706
707        self.assertInBuildFile(
708            '    tags = [\n'
709            '        "no-remote",\n'
710            '    ],\n',
711            package='example/tests',
712        )
713
714    def test_generate_test_module_prebuilt(self):
715        mod_info = self.create_module_info(modules=[
716            host_unit_test_module(name='hello_world_test'),
717        ])
718
719        self.run_generator(mod_info)
720
721        self.assertTargetInWorkspace('hello_world_test')
722
723    def test_raise_when_prerequisite_not_in_module_info(self):
724        mod_info = self.create_module_info(modules=[
725            host_unit_test_module(),
726        ])
727        del mod_info.name_to_module_info['adb']
728
729        with self.assertRaises(Exception) as context:
730            self.run_generator(mod_info)
731
732        self.assertIn('adb', str(context.exception))
733
734    def test_raise_when_prerequisite_module_missing_path(self):
735        mod_info = self.create_module_info(modules=[
736            host_unit_test_module(),
737        ])
738        mod_info.name_to_module_info['adb'].get('path').clear()
739
740        with self.assertRaises(Exception) as context:
741            self.run_generator(mod_info)
742
743        self.assertIn('adb', str(context.exception))
744
745    def test_warning_when_prerequisite_module_has_multiple_path(self):
746        mod_info = self.create_module_info(modules=[
747            host_unit_test_module(),
748        ])
749        mod_info.name_to_module_info['adb'].get('path').append('the/2nd/path')
750
751        with self.assertWarns(Warning) as context:
752            self.run_generator(mod_info)
753
754        self.assertIn('adb', str(context.warnings[0].message))
755
756
757class RobolectricTestModuleTestTargetGenerationTest(GenerationTestFixture):
758    """Tests for robolectric test module test target generation."""
759
760    def setUp(self):
761        super().setUp()
762        self.robolectric_template_path = self.resource_manager.\
763            get_resource_file_path(bazel_mode.ROBOLECTRIC_CONFIG, True)
764        self.fs.create_file(self.robolectric_template_path, contents='')
765        # ResourceManager only calculates md5 when registering files. So, it is
766        # necessary to call get_resource_file_path() again after writing files.
767        self.resource_manager.get_resource_file_path(
768            bazel_mode.ROBOLECTRIC_CONFIG, True)
769
770    def test_generate_robolectric_test_target(self):
771        module_name = 'hello_world_test'
772        mod_info = self.create_module_info(modules=[
773            robolectric_test_module(
774                name=f'{module_name}',
775                compatibility_suites='robolectric-tests'),
776        ])
777
778        self.run_generator(mod_info, enabled_features=set([
779            bazel_mode.Features.EXPERIMENTAL_ROBOLECTRIC_TEST]))
780
781        self.assertInBuildFile(
782            'load("//bazel/rules:tradefed_test.bzl",'
783            ' "tradefed_robolectric_test")\n',
784        )
785        self.assertTargetInWorkspace(f'{module_name}_host')
786
787    def test_not_generate_when_feature_disabled(self):
788        module_name = 'hello_world_test'
789        mod_info = self.create_module_info(modules=[
790            robolectric_test_module(
791                name=f'{module_name}',
792                compatibility_suites='robolectric-tests'),
793        ])
794
795        self.run_generator(mod_info)
796
797        self.assertTargetNotInWorkspace(f'{module_name}_host')
798
799    def test_not_generate_for_legacy_robolectric_test_type(self):
800        module_name = 'hello_world_test'
801        module_path = 'example/tests'
802        mod_info = self.create_module_info(modules=[
803            robolectric_test_module(
804                name=f'{module_name}', path=module_path),
805        ])
806
807        self.run_generator(mod_info, enabled_features=set([
808            bazel_mode.Features.EXPERIMENTAL_ROBOLECTRIC_TEST]))
809
810        self.assertFileNotInWorkspace('BUILD.bazel', package=f'{module_path}')
811
812    def test_generate_jdk_target(self):
813        gen = self.create_workspace_generator(jdk_path=Path('jdk_src_root'))
814
815        gen.generate()
816
817        self.assertInBuildFile(
818            'filegroup(\n'
819            f'    name = "{bazel_mode.JDK_NAME}",\n'
820            '    srcs = glob([\n'
821            f'        "{bazel_mode.JDK_NAME}_files/**",\n',
822            package=f'{bazel_mode.JDK_PACKAGE_NAME}'
823        )
824
825    def test_not_generate_jdk_target_when_no_jdk_path(self):
826        gen = self.create_workspace_generator(jdk_path=None)
827
828        gen.generate()
829
830        self.assertFileNotInWorkspace(
831            'BUILD.bazel', package=f'{bazel_mode.JDK_PACKAGE_NAME}')
832
833    def test_create_symlinks_to_jdk(self):
834        jdk_path = Path('jdk_path')
835        gen = self.create_workspace_generator(jdk_path=jdk_path)
836
837        gen.generate()
838
839        self.assertSymlinkTo(
840            self.workspace_out_path.joinpath(
841                f'{bazel_mode.JDK_PACKAGE_NAME}/{bazel_mode.JDK_NAME}_files'),
842            self.resource_manager.get_src_file_path(f'{jdk_path}'))
843
844    def test_generate_android_all_target(self):
845        gen = self.create_workspace_generator(jdk_path=Path('jdk_src_root'))
846
847        gen.generate()
848
849        self.assertInBuildFile(
850            'filegroup(\n'
851            '    name = "android-all",\n'
852            '    srcs = glob([\n'
853            '        "android-all_files/**",\n',
854            package='android-all'
855        )
856
857    def test_not_generate_android_all_target_when_no_jdk_path(self):
858        gen = self.create_workspace_generator(jdk_path=None)
859
860        gen.generate()
861
862        self.assertFileNotInWorkspace(
863            'BUILD.bazel', package='android-all')
864
865    def test_create_symlinks_to_android_all(self):
866        module_name = 'android-all'
867        gen = self.create_workspace_generator(jdk_path=Path('jdk_src_root'))
868
869        gen.generate()
870
871        self.assertSymlinkTo(
872            self.workspace_out_path.joinpath(
873                f'{module_name}/{module_name}_files'),
874            self.host_out_path.joinpath(f'testcases/{module_name}'))
875
876    def test_regenerate_workspace_when_robolectric_template_changed(self):
877        workspace_generator = self.create_workspace_generator()
878        workspace_generator.generate()
879        workspace_stat = workspace_generator.workspace_out_path.stat()
880
881        with open(self.robolectric_template_path, 'a', encoding='utf8') as f:
882            f.write(' ')
883        workspace_generator = self.create_workspace_generator()
884        workspace_generator.generate()
885
886        new_workspace_stat = workspace_generator.workspace_out_path.stat()
887        self.assertNotEqual(workspace_stat, new_workspace_stat)
888
889    def test_not_regenerate_workspace_when_robolectric_template_touched(self):
890        workspace_generator = self.create_workspace_generator()
891        workspace_generator.generate()
892        workspace_stat = workspace_generator.workspace_out_path.stat()
893
894        self.robolectric_template_path.touch()
895        workspace_generator = self.create_workspace_generator()
896        workspace_generator.generate()
897
898        new_workspace_stat = workspace_generator.workspace_out_path.stat()
899        self.assertEqual(workspace_stat, new_workspace_stat)
900
901
902class ModulePrebuiltTargetGenerationTest(GenerationTestFixture):
903    """Tests for module prebuilt target generation."""
904
905    def test_generate_prebuilt_import(self):
906        mod_info = self.create_module_info(modules=[
907            supported_test_module(),
908        ])
909
910        self.run_generator(mod_info)
911
912        self.assertInBuildFile(
913            'load("//bazel/rules:soong_prebuilt.bzl", "soong_prebuilt")\n'
914        )
915
916    def test_generate_prebuilt_target_for_multi_config_test_module(self):
917        mod_info = self.create_module_info(modules=[
918            multi_config(supported_test_module(name='libhello')),
919        ])
920
921        self.run_generator(mod_info)
922
923        self.assertInBuildFile(
924            'soong_prebuilt(\n'
925            '    name = "libhello",\n'
926            '    module_name = "libhello",\n'
927            '    files = select({\n'
928            '        "//bazel/rules:device": glob(["libhello/device/**/*"]),\n'
929            '        "//bazel/rules:host": glob(["libhello/host/**/*"]),\n'
930            '    }),\n'
931            '    suites = [\n'
932            '        "host-unit-tests",\n'
933            '    ],\n'
934            ')\n'
935        )
936
937    def test_create_symlinks_to_testcases_for_multi_config_test_module(self):
938        module_name = 'hello_world_test'
939        mod_info = self.create_module_info(modules=[
940            multi_config(supported_test_module(name=module_name))
941        ])
942        module_out_path = self.workspace_out_path.joinpath(module_name)
943
944        self.run_generator(mod_info)
945
946        self.assertSymlinkTo(
947            module_out_path.joinpath(f'host/testcases/{module_name}'),
948            self.host_out_path.joinpath(f'testcases/{module_name}'))
949        self.assertSymlinkTo(
950            module_out_path.joinpath(f'device/testcases/{module_name}'),
951            self.product_out_path.joinpath(f'testcases/{module_name}'))
952
953    def test_generate_files_for_host_only_test_module(self):
954        mod_info = self.create_module_info(modules=[
955            host_only_config(supported_test_module(name='test1')),
956        ])
957
958        self.run_generator(mod_info)
959
960        self.assertInBuildFile(
961            '    files = select({\n'
962            '        "//bazel/rules:host": glob(["test1/host/**/*"]),\n'
963            '    }),\n'
964        )
965
966    def test_generate_files_for_device_only_test_module(self):
967        mod_info = self.create_module_info(modules=[
968            device_only_config(supported_test_module(name='test1')),
969        ])
970
971        self.run_generator(mod_info)
972
973        self.assertInBuildFile(
974            '    files = select({\n'
975            '        "//bazel/rules:device": glob(["test1/device/**/*"]),\n'
976            '    }),\n'
977        )
978
979    def test_not_create_device_symlinks_for_host_only_test_module(self):
980        mod_info = self.create_module_info(modules=[
981            host_only_config(supported_test_module(name='test1')),
982        ])
983
984        self.run_generator(mod_info)
985
986        self.assertFileNotInWorkspace('test1/device')
987
988    def test_not_create_host_symlinks_for_device_test_module(self):
989        mod_info = self.create_module_info(modules=[
990            device_only_config(supported_test_module(name='test1')),
991        ])
992
993        self.run_generator(mod_info)
994
995        self.assertFileNotInWorkspace('test1/host')
996
997
998class ModuleSharedLibGenerationTest(GenerationTestFixture):
999    """Tests for module shared libs target generation."""
1000
1001    def test_not_generate_runtime_deps_when_all_configs_incompatible(self):
1002        mod_info = self.create_module_info(modules=[
1003            host_only_config(supported_test_module(shared_libs=['libdevice'])),
1004            device_only_config(module(name='libdevice')),
1005        ])
1006
1007        self.run_generator(mod_info)
1008
1009        self.assertNotInBuildFile('runtime_deps')
1010
1011    def test_generate_runtime_deps_when_configs_compatible(self):
1012        mod_info = self.create_module_info(modules=[
1013            multi_config(supported_test_module(shared_libs=['libmulti'])),
1014            multi_config_module(name='libmulti'),
1015        ])
1016
1017        self.run_generator(mod_info)
1018
1019        self.assertInBuildFile(
1020            '    runtime_deps = select({\n'
1021            '        "//bazel/rules:device": [\n'
1022            '            "//:libmulti",\n'
1023            '        ],\n'
1024            '        "//bazel/rules:host": [\n'
1025            '            "//:libmulti",\n'
1026            '        ],\n'
1027            '    }),\n'
1028        )
1029
1030    def test_generate_runtime_deps_when_configs_partially_compatible(self):
1031        mod_info = self.create_module_info(modules=[
1032            multi_config(supported_test_module(shared_libs=[
1033                'libhost',
1034            ])),
1035            host_module(name='libhost'),
1036        ])
1037
1038        self.run_generator(mod_info)
1039
1040        self.assertInBuildFile(
1041            '    runtime_deps = select({\n'
1042            '        "//bazel/rules:device": [\n'
1043            '        ],\n'
1044            '        "//bazel/rules:host": [\n'
1045            '            "//:libhost",\n'
1046            '        ],\n'
1047            '    }),\n'
1048        )
1049
1050    def test_generate_runtime_deps_with_mixed_compatibility(self):
1051        mod_info = self.create_module_info(modules=[
1052            multi_config(supported_test_module(shared_libs=[
1053                'libhost',
1054                'libdevice',
1055                'libmulti'
1056            ])),
1057            host_module(name='libhost'),
1058            device_module(name='libdevice'),
1059            multi_config_module(name='libmulti'),
1060        ])
1061
1062        self.run_generator(mod_info)
1063
1064        self.assertInBuildFile(
1065            '    runtime_deps = select({\n'
1066            '        "//bazel/rules:device": [\n'
1067            '            "//:libdevice",\n'
1068            '            "//:libmulti",\n'
1069            '        ],\n'
1070            '        "//bazel/rules:host": [\n'
1071            '            "//:libhost",\n'
1072            '            "//:libmulti",\n'
1073            '        ],\n'
1074            '    }),\n'
1075        )
1076
1077    def test_generate_runtime_deps_recursively(self):
1078        mod_info = self.create_module_info(modules=[
1079            multi_config(supported_test_module(shared_libs=[
1080                'libdirect',
1081            ])),
1082            multi_config_module(name='libdirect', shared_libs=[
1083                'libtransitive',
1084            ]),
1085            multi_config_module(name='libtransitive'),
1086        ])
1087
1088        self.run_generator(mod_info)
1089
1090        self.assertTargetInWorkspace('libtransitive')
1091
1092    def test_generate_shared_runtime_deps_once(self):
1093        mod_info = self.create_module_info(modules=[
1094            multi_config(supported_test_module(shared_libs=[
1095                'libleft',
1096                'libright',
1097            ])),
1098            multi_config_module(name='libleft', shared_libs=[
1099                'libshared',
1100            ]),
1101            multi_config_module(name='libright', shared_libs=[
1102                'libshared',
1103            ]),
1104            multi_config_module(name='libshared'),
1105        ])
1106
1107        self.run_generator(mod_info)
1108
1109        self.assertTargetInWorkspace('libshared')
1110
1111    def test_generate_runtime_deps_in_order(self):
1112        mod_info = self.create_module_info(modules=[
1113            supported_test_module(shared_libs=['libhello2', 'libhello1']),
1114            host_module(name='libhello1'),
1115            host_module(name='libhello2'),
1116        ])
1117
1118        self.run_generator(mod_info)
1119
1120        self.assertInBuildFile(
1121            '            "//:libhello1",\n'
1122            '            "//:libhello2",\n'
1123        )
1124
1125    def test_generate_target_for_shared_lib(self):
1126        mod_info = self.create_module_info(modules=[
1127            supported_test_module(shared_libs=['libhello']),
1128            host_module(name='libhello'),
1129        ])
1130
1131        self.run_generator(mod_info)
1132
1133        self.assertTargetInWorkspace('libhello')
1134
1135    def test_not_generate_for_missing_shared_lib_module(self):
1136        mod_info = self.create_module_info(modules=[
1137            supported_test_module(shared_libs=['libhello'])
1138        ])
1139
1140        self.run_generator(mod_info)
1141
1142        self.assertNotInBuildFile('            "//:libhello",\n')
1143        self.assertTargetNotInWorkspace('libhello')
1144
1145    def test_not_generate_when_shared_lib_uninstalled(self):
1146        mod_info = self.create_module_info(modules=[
1147            supported_test_module(shared_libs=['libhello']),
1148            host_module(name='libhello', installed=[]),
1149        ])
1150
1151        self.run_generator(mod_info)
1152
1153        self.assertNotInBuildFile('            "//:libhello",\n')
1154        self.assertTargetNotInWorkspace('libhello')
1155
1156    def test_not_generate_when_shared_lib_installed_path_unsupported(self):
1157        unsupported_install_path = 'out/other'
1158        mod_info = self.create_module_info(modules=[
1159            supported_test_module(shared_libs=['libhello']),
1160            shared_lib(module('libhello',
1161                              installed=[unsupported_install_path])),
1162        ])
1163
1164        self.run_generator(mod_info)
1165
1166        self.assertNotInBuildFile('"//:libhello",\n')
1167        self.assertTargetNotInWorkspace('libhello')
1168
1169    def test_not_generate_when_shared_lib_install_path_ambiguous(self):
1170        ambiguous_install_path = 'out/f1'
1171        mod_info = self.create_module_info(modules=[
1172            supported_test_module(shared_libs=['libhello']),
1173            module(name='libhello', installed=[ambiguous_install_path]),
1174        ])
1175
1176        self.run_generator(mod_info)
1177
1178        self.assertNotInBuildFile('"//:libhello",\n')
1179        self.assertTargetNotInWorkspace('libhello')
1180
1181    def test_generate_target_for_rlib_dependency(self):
1182        mod_info = self.create_module_info(modules=[
1183            multi_config(host_unit_suite(module(
1184                name='hello_world_test',
1185                dependencies=['libhost', 'libdevice']))),
1186            rlib(module(name='libhost', supported_variants=['HOST'])),
1187            rlib(module(name='libdevice', supported_variants=['DEVICE'])),
1188        ])
1189
1190        self.run_generator(mod_info)
1191
1192        self.assertInBuildFile(
1193            'soong_uninstalled_prebuilt(\n'
1194            '    name = "libhost",\n'
1195            '    module_name = "libhost",\n'
1196            ')\n'
1197        )
1198        self.assertInBuildFile(
1199            'soong_uninstalled_prebuilt(\n'
1200            '    name = "libdevice",\n'
1201            '    module_name = "libdevice",\n'
1202            ')\n'
1203        )
1204        self.assertInBuildFile(
1205            '    runtime_deps = select({\n'
1206            '        "//bazel/rules:device": [\n'
1207            '            "//:libdevice",\n'
1208            '        ],\n'
1209            '        "//bazel/rules:host": [\n'
1210            '            "//:libhost",\n'
1211            '        ],\n'
1212            '    }),\n'
1213        )
1214
1215    def test_generate_target_for_rlib_dylib_dependency(self):
1216        mod_info = self.create_module_info(modules=[
1217            supported_test_module(dependencies=['libhello']),
1218            rlib(module(name='libhello', dependencies=['libworld'])),
1219            host_only_config(dylib(module(name='libworld')))
1220        ])
1221
1222        self.run_generator(mod_info)
1223
1224        self.assertTargetInWorkspace('libworld')
1225
1226    def test_generate_target_for_dylib_dependency(self):
1227        mod_info = self.create_module_info(modules=[
1228            supported_test_module(dependencies=['libhello']),
1229            host_only_config(dylib(module(name='libhello')))
1230        ])
1231
1232        self.run_generator(mod_info)
1233
1234        self.assertInBuildFile(
1235            'soong_prebuilt(\n'
1236            '    name = "libhello",\n'
1237            '    module_name = "libhello",\n'
1238        )
1239
1240    def test_generate_target_for_uninstalled_dylib_dependency(self):
1241        mod_info = self.create_module_info(modules=[
1242            supported_test_module(dependencies=['libhello']),
1243            dylib(module(name='libhello', installed=[]))
1244        ])
1245
1246        self.run_generator(mod_info)
1247
1248        self.assertInBuildFile(
1249            'soong_uninstalled_prebuilt(\n'
1250            '    name = "libhello",\n'
1251            '    module_name = "libhello",\n'
1252            ')\n'
1253        )
1254
1255    def test_not_generate_target_for_non_runtime_dependency(self):
1256        mod_info = self.create_module_info(modules=[
1257            supported_test_module(dependencies=['libhello']),
1258            host_module(name='libhello', classes=['NOT_SUPPORTED'])
1259        ])
1260
1261        self.run_generator(mod_info)
1262
1263        self.assertNotInBuildFile('"//:libhello",\n')
1264        self.assertTargetNotInWorkspace('libhello')
1265
1266
1267    def test_generate_target_for_runtime_dependency(self):
1268        mod_info = self.create_module_info(modules=[
1269            supported_test_module(runtime_dependencies=['libhello']),
1270            host_only_config(
1271                module(name='libhello', classes=['SHARED_LIBRARIES']))
1272        ])
1273
1274        self.run_generator(mod_info)
1275
1276        self.assertInBuildFile(
1277            '    runtime_deps = select({\n'
1278            '        "//bazel/rules:host": [\n'
1279            '            "//:libhello",\n'
1280            '        ],\n'
1281            '    }),\n'
1282        )
1283
1284class SharedLibPrebuiltTargetGenerationTest(GenerationTestFixture):
1285    """Tests for runtime dependency module prebuilt target generation."""
1286
1287    def test_create_multi_config_target_symlinks(self):
1288        host_file1 = self.host_out_path.joinpath('a/b/f1')
1289        host_file2 = self.host_out_path.joinpath('a/c/f2')
1290        device_file1 = self.product_out_path.joinpath('a/b/f1')
1291        mod_info = self.create_module_info(modules=[
1292            supported_test_module(shared_libs=['libhello']),
1293            multi_config_module(
1294                name='libhello',
1295                installed=[str(host_file1), str(host_file2), str(device_file1)]
1296            )
1297        ])
1298        package_path = self.workspace_out_path
1299
1300        self.run_generator(mod_info)
1301
1302        self.assertSymlinkTo(
1303            package_path.joinpath('libhello/host/a/b/f1'), host_file1)
1304        self.assertSymlinkTo(
1305            package_path.joinpath('libhello/host/a/c/f2'), host_file2)
1306        self.assertSymlinkTo(
1307            package_path.joinpath('libhello/device/a/b/f1'), device_file1)
1308
1309    def test_create_symlinks_to_installed_path_for_non_tf_testable_deps(self):
1310        host_file = self.host_out_path.joinpath('a/b/f1')
1311        mod_info = self.create_module_info(modules=[
1312            supported_test_module(shared_libs=['libhello']),
1313            host_module(
1314                name='libhello',
1315                installed=[str(host_file)],
1316            )
1317        ])
1318        package_path = self.workspace_out_path
1319
1320        self.run_generator(mod_info)
1321
1322        self.assertSymlinkTo(
1323            package_path.joinpath('libhello/host/a/b/f1'), host_file)
1324
1325    def test_create_symlinks_to_installed_path_for_lib_with_test_config(self):
1326        host_file = self.host_out_path.joinpath('a/b/f1')
1327        mod_info = self.create_module_info(modules=[
1328            supported_test_module(shared_libs=['libhello']),
1329            host_module(
1330                name='libhello',
1331                installed=[str(host_file)],
1332                path='src/lib'
1333            )
1334        ])
1335        self.fs.create_file(Path('src/lib/AndroidTest.xml'), contents='')
1336        package_path = self.workspace_out_path
1337
1338        self.run_generator(mod_info)
1339
1340        self.assertSymlinkTo(
1341            package_path.joinpath('src/lib/libhello/host/a/b/f1'), host_file)
1342
1343    def test_generate_for_host_only_shared_lib_dependency(self):
1344        mod_info = self.create_module_info(modules=[
1345            supported_test_module(shared_libs=['libhello']),
1346            host_module(name='libhello'),
1347        ])
1348
1349        self.run_generator(mod_info)
1350
1351        self.assertInBuildFile(
1352            '    files = select({\n'
1353            '        "//bazel/rules:host": glob(["libhello/host/**/*"]),\n'
1354            '    }),\n'
1355        )
1356        self.assertFileNotInWorkspace('libhello/device')
1357
1358    def test_generate_for_device_only_shared_lib_dependency(self):
1359        mod_info = self.create_module_info(modules=[
1360            supported_test_module(shared_libs=['libhello']),
1361            device_module(name='libhello'),
1362        ])
1363
1364        self.run_generator(mod_info)
1365
1366        self.assertInBuildFile(
1367            '    files = select({\n'
1368            '        "//bazel/rules:device": glob(["libhello/device/**/*"]),\n'
1369            '    }),\n'
1370        )
1371        self.assertFileNotInWorkspace('libhello/host')
1372
1373
1374class DataDependenciesGenerationTest(GenerationTestFixture):
1375    """Tests for module data dependencies target generation."""
1376
1377    def test_generate_target_for_data_dependency(self):
1378        mod_info = self.create_module_info(modules=[
1379            supported_test_module(data_dependencies=['libdata']),
1380            host_module(name='libdata'),
1381        ])
1382
1383        self.run_generator(mod_info)
1384
1385        self.assertInBuildFile(
1386        '    data = select({\n'
1387        '        "//bazel/rules:host": [\n'
1388        '            "//:libdata",\n'
1389        '        ],\n'
1390        '    }),\n'
1391        )
1392        self.assertTargetInWorkspace('libdata')
1393
1394    def test_not_generate_target_for_data_file(self):
1395        # Data files are included in "data", but not in "data_dependencies".
1396        mod_info = self.create_module_info(modules=[
1397            supported_test_module(data=['libdata']),
1398            host_module(name='libdata'),
1399        ])
1400
1401        self.run_generator(mod_info)
1402
1403        self.assertTargetNotInWorkspace('libdata')
1404
1405
1406def create_empty_module_info():
1407    with fake_filesystem_unittest.Patcher() as patcher:
1408        # pylint: disable=protected-access
1409        fake_temp_file_name = next(tempfile._get_candidate_names())
1410        patcher.fs.create_file(fake_temp_file_name, contents='{}')
1411        return module_info.ModuleInfo(module_file=fake_temp_file_name)
1412
1413
1414def create_module_info(modules=None):
1415    mod_info = create_empty_module_info()
1416    modules = modules or []
1417
1418    for m in modules:
1419        mod_info.name_to_module_info[m['module_name']] = m
1420
1421    return mod_info
1422
1423
1424def host_unit_test_module(**kwargs):
1425    return host_unit_suite(host_test_module(**kwargs))
1426
1427
1428# We use the below alias in situations where the actual type is irrelevant to
1429# the test as long as it is supported in Bazel mode.
1430supported_test_module = host_unit_test_module
1431
1432
1433def host_test_module(**kwargs):
1434    kwargs.setdefault('name', 'hello_world_test')
1435    return host_only_config(test_module(**kwargs))
1436
1437
1438def device_test_module(**kwargs):
1439    kwargs.setdefault('name', 'hello_world_test')
1440    return device_only_config(test_module(**kwargs))
1441
1442
1443def robolectric_test_module(**kwargs):
1444    kwargs.setdefault('name', 'hello_world_test')
1445    return host_only_config(robolectric(test_module(**kwargs)))
1446
1447
1448def host_module(**kwargs):
1449    m = module(**kwargs)
1450
1451    if 'installed' in kwargs:
1452        return m
1453
1454    return host_only_config(m)
1455
1456
1457def device_module(**kwargs):
1458    m = module(**kwargs)
1459
1460    if 'installed' in kwargs:
1461        return m
1462
1463    return device_only_config(m)
1464
1465
1466def multi_config_module(**kwargs):
1467    m = module(**kwargs)
1468
1469    if 'installed' in kwargs:
1470        return m
1471
1472    return multi_config(m)
1473
1474
1475def test_module(**kwargs):
1476    kwargs.setdefault('name', 'hello_world_test')
1477    return test(module(**kwargs))
1478
1479
1480# TODO(b/274822450): Using a builder pattern to reduce the number of parameters
1481#  instead of disabling the warning.
1482# pylint: disable=too-many-arguments
1483# pylint: disable=too-many-locals
1484def module(
1485    name=None,
1486    path=None,
1487    installed=None,
1488    classes=None,
1489    auto_test_config=None,
1490    shared_libs=None,
1491    dependencies=None,
1492    runtime_dependencies=None,
1493    data=None,
1494    data_dependencies=None,
1495    compatibility_suites=None,
1496    host_dependencies=None,
1497    target_dependencies=None,
1498    test_options_tags=None,
1499    supported_variants=None,
1500):
1501    name = name or 'libhello'
1502
1503    m = {}
1504
1505    m['module_name'] = name
1506    m['class'] = classes or ['']
1507    m['path'] = [path or '']
1508    m['installed'] = installed or []
1509    m['is_unit_test'] = 'false'
1510    m['auto_test_config'] = auto_test_config or []
1511    m['shared_libs'] = shared_libs or []
1512    m['runtime_dependencies'] = runtime_dependencies or []
1513    m['dependencies'] = dependencies or []
1514    m['data'] = data or []
1515    m['data_dependencies'] = data_dependencies or []
1516    m['compatibility_suites'] = compatibility_suites or []
1517    m['host_dependencies'] = host_dependencies or []
1518    m['target_dependencies'] = target_dependencies or []
1519    m['test_options_tags'] = test_options_tags or []
1520    m['supported_variants'] = supported_variants or []
1521    return m
1522
1523
1524def test(info):
1525    info['auto_test_config'] = ['true']
1526    return info
1527
1528
1529def shared_lib(info):
1530    info['class'] = ['SHARED_LIBRARIES']
1531    return info
1532
1533
1534def rlib(info):
1535    info['class'] = ['RLIB_LIBRARIES']
1536    info['installed'] = []
1537    return info
1538
1539
1540def dylib(info):
1541    info['class'] = ['DYLIB_LIBRARIES']
1542    return info
1543
1544
1545def robolectric(info):
1546    info['class'] = ['ROBOLECTRIC']
1547    return info
1548
1549
1550def host_unit_suite(info):
1551    info = test(info)
1552    info.setdefault('compatibility_suites', []).append('host-unit-tests')
1553    return info
1554
1555
1556def multi_config(info):
1557    name = info.get('module_name', 'lib')
1558    info['installed'] = [
1559        f'out/host/linux-x86/{name}/{name}.jar',
1560        f'out/product/vsoc_x86/{name}/{name}.apk',
1561    ]
1562    info['supported_variants'] = [
1563        'DEVICE',
1564        'HOST',
1565    ]
1566    return info
1567
1568
1569def host_only_config(info):
1570    name = info.get('module_name', 'lib')
1571    info['installed'] = [
1572        f'out/host/linux-x86/{name}/{name}.jar',
1573    ]
1574    info['supported_variants'] = [
1575        'HOST',
1576    ]
1577    return info
1578
1579
1580def device_only_config(info):
1581    name = info.get('module_name', 'lib')
1582    info['installed'] = [
1583        f'out/product/vsoc_x86/{name}/{name}.jar',
1584    ]
1585    info['supported_variants'] = [
1586        'DEVICE',
1587    ]
1588    return info
1589
1590
1591class PackageTest(fake_filesystem_unittest.TestCase):
1592    """Tests for Package."""
1593
1594    class FakeTarget(bazel_mode.Target):
1595        """Fake target used for tests."""
1596
1597        def __init__(self, name, imports=None):
1598            self._name = name
1599            self._imports = imports or set()
1600
1601        def name(self):
1602            return self._name
1603
1604        def required_imports(self):
1605            return self._imports
1606
1607        def write_to_build_file(self, f):
1608            f.write(f'{self._name}\n')
1609
1610
1611    def setUp(self):
1612        self.setUpPyfakefs()
1613        self.workspace_out_path = Path('/workspace_out_path')
1614        self.workspace_out_path.mkdir()
1615
1616    def test_raise_when_adding_existing_target(self):
1617        target_name = '<fake_target>'
1618        package = bazel_mode.Package('p')
1619        package.add_target(self.FakeTarget(target_name))
1620
1621        with self.assertRaises(Exception) as context:
1622            package.add_target(self.FakeTarget(target_name))
1623
1624        self.assertIn(target_name, str(context.exception))
1625
1626    def test_write_build_file_in_package_dir(self):
1627        package_path = 'abc/def'
1628        package = bazel_mode.Package(package_path)
1629        expected_path = self.workspace_out_path.joinpath(
1630            package_path, 'BUILD.bazel')
1631
1632        package.generate(self.workspace_out_path)
1633
1634        self.assertTrue(expected_path.exists())
1635
1636    def test_write_load_statements_in_sorted_order(self):
1637        package = bazel_mode.Package('p')
1638        target1 = self.FakeTarget('target1', imports={
1639            bazel_mode.Import('z.bzl', 'symbol1'),
1640        })
1641        target2 = self.FakeTarget('target2', imports={
1642            bazel_mode.Import('a.bzl', 'symbol2'),
1643        })
1644        package.add_target(target1)
1645        package.add_target(target2)
1646
1647        package.generate(self.workspace_out_path)
1648
1649        self.assertIn('load("a.bzl", "symbol2")\nload("z.bzl", "symbol1")\n\n',
1650                      self.package_build_file_text(package))
1651
1652    def test_write_load_statements_with_symbols_grouped_by_bzl(self):
1653        package = bazel_mode.Package('p')
1654        target1 = self.FakeTarget('target1', imports={
1655            bazel_mode.Import('a.bzl', 'symbol1'),
1656            bazel_mode.Import('a.bzl', 'symbol3'),
1657        })
1658        target2 = self.FakeTarget('target2', imports={
1659            bazel_mode.Import('a.bzl', 'symbol2'),
1660        })
1661        package.add_target(target1)
1662        package.add_target(target2)
1663
1664        package.generate(self.workspace_out_path)
1665
1666        self.assertIn('load("a.bzl", "symbol1", "symbol2", "symbol3")\n\n',
1667                      self.package_build_file_text(package))
1668
1669    def test_write_targets_in_add_order(self):
1670        package = bazel_mode.Package('p')
1671        target1 = self.FakeTarget('target1')
1672        target2 = self.FakeTarget('target2')
1673        package.add_target(target2)  # Added out of order.
1674        package.add_target(target1)
1675
1676        package.generate(self.workspace_out_path)
1677
1678        self.assertIn('target2\n\ntarget1\n',
1679                      self.package_build_file_text(package))
1680
1681    def test_generate_parent_package_when_nested_exists(self):
1682        parent_path = Path('parent')
1683        parent = bazel_mode.Package(parent_path.name)
1684        nested = bazel_mode.Package(parent_path.joinpath('nested'))
1685        nested.generate(self.workspace_out_path)
1686
1687        parent.generate(self.workspace_out_path)
1688
1689        self.assertTrue(self.workspace_out_path.joinpath(parent_path).is_dir())
1690
1691    def package_build_file_text(self, package):
1692        return self.workspace_out_path.joinpath(
1693            package.path, 'BUILD.bazel').read_text(encoding='utf8')
1694
1695
1696class DecorateFinderMethodTest(GenerationTestFixture):
1697    """Tests for _decorate_find_method()."""
1698
1699    def test_host_unit_test_with_host_arg_runner_is_overridden(self):
1700        def original_find_method(obj, test_id):
1701            return self.create_single_test_infos(
1702                obj, test_id, test_name=MODULE_NAME,
1703                runner=ATEST_TF_RUNNER)
1704        mod_info = self.create_module_info(modules=[
1705            host_unit_test_module(name=MODULE_NAME)
1706        ])
1707        original_finder = self.create_finder(mod_info, original_find_method)
1708        new_finder = bazel_mode.create_new_finder(
1709            mod_info, original_finder, host=True)
1710
1711        test_infos = new_finder.find_method(
1712            new_finder.test_finder_instance, MODULE_NAME)
1713
1714        self.assertEqual(len(test_infos), 1)
1715        self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
1716
1717    def test_host_unit_test_without_host_arg_runner_is_overridden(self):
1718        def original_find_method(obj, test_id):
1719            return self.create_single_test_infos(
1720                obj, test_id, test_name=MODULE_NAME,
1721                runner=ATEST_TF_RUNNER)
1722        mod_info = self.create_module_info(modules=[
1723            host_unit_test_module(name=MODULE_NAME)
1724        ])
1725        original_finder = self.create_finder(mod_info, original_find_method)
1726        new_finder = bazel_mode.create_new_finder(
1727            mod_info, original_finder, host=False)
1728
1729        test_infos = new_finder.find_method(
1730            new_finder.test_finder_instance, MODULE_NAME)
1731
1732        self.assertEqual(len(test_infos), 1)
1733        self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
1734
1735    def test_device_test_with_host_arg_runner_is_preserved(self):
1736        def original_find_method(obj, test_id):
1737            return self.create_single_test_infos(
1738                obj, test_id, test_name=MODULE_NAME,
1739                runner=ATEST_TF_RUNNER)
1740        mod_info = self.create_module_info(modules=[
1741            device_test_module(name=MODULE_NAME)
1742        ])
1743        original_finder = self.create_finder(mod_info, original_find_method)
1744        new_finder = bazel_mode.create_new_finder(
1745            mod_info,
1746            original_finder,
1747            host=True,
1748            enabled_features=[
1749                bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST
1750            ]
1751        )
1752
1753        test_infos = new_finder.find_method(
1754            new_finder.test_finder_instance, MODULE_NAME)
1755
1756        self.assertEqual(len(test_infos), 1)
1757        self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER)
1758
1759    def test_device_test_without_host_arg_runner_is_overridden(self):
1760        def original_find_method(obj, test_id):
1761            return self.create_single_test_infos(
1762                obj, test_id, test_name=MODULE_NAME,
1763                runner=ATEST_TF_RUNNER)
1764        mod_info = self.create_module_info(modules=[
1765            device_test_module(name=MODULE_NAME)
1766        ])
1767        original_finder = self.create_finder(mod_info, original_find_method)
1768        new_finder = bazel_mode.create_new_finder(
1769            mod_info,
1770            original_finder,
1771            host=False,
1772            enabled_features=[
1773                bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST
1774            ]
1775        )
1776
1777        test_infos = new_finder.find_method(
1778            new_finder.test_finder_instance, MODULE_NAME)
1779
1780        self.assertEqual(len(test_infos), 1)
1781        self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
1782
1783    def test_multi_config_test_with_host_arg_runner_is_overridden(self):
1784        def original_find_method(obj, test_id):
1785            return self.create_single_test_infos(
1786                obj, test_id, test_name=MODULE_NAME,
1787                runner=ATEST_TF_RUNNER)
1788        mod_info = self.create_module_info(modules=[
1789            multi_config(supported_test_module(name=MODULE_NAME))
1790        ])
1791        original_finder = self.create_finder(mod_info, original_find_method)
1792        new_finder = bazel_mode.create_new_finder(
1793            mod_info,
1794            original_finder,
1795            host=True,
1796            enabled_features=[
1797                bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST
1798            ]
1799        )
1800
1801        test_infos = new_finder.find_method(
1802            new_finder.test_finder_instance, MODULE_NAME)
1803
1804        self.assertEqual(len(test_infos), 1)
1805        self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
1806
1807    def test_multi_config_test_without_host_arg_runner_is_overridden(self):
1808        def original_find_method(obj, test_id):
1809            return self.create_single_test_infos(
1810                obj, test_id, test_name=MODULE_NAME,
1811                runner=ATEST_TF_RUNNER)
1812        mod_info = self.create_module_info(modules=[
1813            multi_config(supported_test_module(name=MODULE_NAME))
1814        ])
1815        original_finder = self.create_finder(mod_info, original_find_method)
1816        new_finder = bazel_mode.create_new_finder(
1817            mod_info,
1818            original_finder,
1819            host=False,
1820            enabled_features=[
1821                bazel_mode.Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST
1822            ]
1823        )
1824
1825        test_infos = new_finder.find_method(
1826            new_finder.test_finder_instance, MODULE_NAME)
1827
1828        self.assertEqual(len(test_infos), 1)
1829        self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
1830
1831    def test_host_non_unit_test_with_host_arg_runner_is_overridden(self):
1832        def original_find_method(obj, test_id):
1833            return self.create_single_test_infos(
1834                obj, test_id, test_name=MODULE_NAME,
1835                runner=ATEST_TF_RUNNER)
1836        mod_info = self.create_module_info(modules=[
1837            host_test_module(name=MODULE_NAME)
1838        ])
1839        original_finder = self.create_finder(mod_info, original_find_method)
1840        new_finder = bazel_mode.create_new_finder(
1841            mod_info,
1842            original_finder,
1843            host=True,
1844            enabled_features=[
1845                bazel_mode.Features.EXPERIMENTAL_HOST_DRIVEN_TEST
1846            ]
1847        )
1848
1849        test_infos = new_finder.find_method(
1850            new_finder.test_finder_instance, MODULE_NAME)
1851
1852        self.assertEqual(len(test_infos), 1)
1853        self.assertEqual(test_infos[0].test_runner, BAZEL_RUNNER)
1854
1855    def test_disable_device_driven_test_feature_runner_is_preserved(self):
1856        def original_find_method(obj, test_id):
1857            return self.create_single_test_infos(
1858                obj, test_id, test_name=MODULE_NAME,
1859                runner=ATEST_TF_RUNNER)
1860        mod_info = self.create_module_info(modules=[
1861            device_test_module(name=MODULE_NAME)
1862        ])
1863        original_finder = self.create_finder(mod_info, original_find_method)
1864        new_finder = bazel_mode.create_new_finder(
1865            mod_info, original_finder, host=False)
1866
1867        test_infos = new_finder.find_method(
1868            new_finder.test_finder_instance, MODULE_NAME)
1869
1870        self.assertEqual(len(test_infos), 1)
1871        self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER)
1872
1873    def test_disable_host_driven_test_feature_runner_is_preserved(self):
1874        def original_find_method(obj, test_id):
1875            return self.create_single_test_infos(
1876                obj, test_id, test_name=MODULE_NAME,
1877                runner=ATEST_TF_RUNNER)
1878        mod_info = self.create_module_info(modules=[
1879            host_test_module(name=MODULE_NAME)
1880        ])
1881        original_finder = self.create_finder(mod_info, original_find_method)
1882        new_finder = bazel_mode.create_new_finder(
1883            mod_info, original_finder, host=True)
1884
1885        test_infos = new_finder.find_method(
1886            new_finder.test_finder_instance, MODULE_NAME)
1887
1888        self.assertEqual(len(test_infos), 1)
1889        self.assertEqual(test_infos[0].test_runner, ATEST_TF_RUNNER)
1890
1891    # pylint: disable=unused-argument
1892    def create_single_test_infos(self, obj, test_id, test_name=MODULE_NAME,
1893                                 runner=ATEST_TF_RUNNER):
1894        """Create list of test_info.TestInfo."""
1895        return [test_info.TestInfo(test_name, runner, MODULE_BUILD_TARGETS)]
1896
1897    def create_finder(self, mod_info, find_method):
1898        return test_finder_base.Finder(
1899            example_finder.ExampleFinder(mod_info),
1900            find_method, 'FINDER_NAME')
1901
1902class BazelTestRunnerTest(unittest.TestCase):
1903    """Tests for BazelTestRunner."""
1904
1905    def test_return_empty_build_reqs_when_no_test_infos(self):
1906        run_command = self.mock_run_command(side_effect=Exception(''))
1907        runner = self.create_bazel_test_runner(
1908            modules=[
1909                supported_test_module(name='test1', path='path1'),
1910            ],
1911            run_command=run_command,
1912        )
1913
1914        reqs = runner.get_test_runner_build_reqs([])
1915
1916        self.assertFalse(reqs)
1917
1918    def test_query_bazel_test_targets_deps_with_host_arg(self):
1919        query_file_contents = StringIO()
1920        def get_query_file_content(args: List[str], _) -> str:
1921            query_file_contents.write(_get_query_file_content(args))
1922            return ''
1923
1924        runner = self.create_bazel_test_runner(
1925            modules=[
1926                multi_config(host_unit_test_module(name='test1', path='path1')),
1927                multi_config(host_unit_test_module(name='test2', path='path2')),
1928                multi_config(test_module(name='test3', path='path3')),
1929            ],
1930            run_command=get_query_file_content,
1931            host=True,
1932        )
1933
1934        runner.get_test_runner_build_reqs([
1935            test_info_of('test2'),
1936            test_info_of('test1'),  # Intentionally out of order.
1937            test_info_of('test3'),
1938        ])
1939
1940        self.assertEqual(
1941            'deps(tests(//path1:test1_host + '
1942            '//path2:test2_host + '
1943            '//path3:test3_host))',
1944            query_file_contents.getvalue())
1945
1946    def test_query_bazel_test_targets_deps_without_host_arg(self):
1947        query_file_contents = StringIO()
1948        def get_query_file_content(args: List[str], _) -> str:
1949            query_file_contents.write(_get_query_file_content(args))
1950            return ''
1951
1952        runner = self.create_bazel_test_runner(
1953            modules=[
1954                multi_config(host_unit_test_module(name='test1', path='path1')),
1955                host_unit_test_module(name='test2', path='path2'),
1956                multi_config(test_module(name='test3', path='path3')),
1957            ],
1958            run_command=get_query_file_content,
1959        )
1960
1961        runner.get_test_runner_build_reqs([
1962            test_info_of('test2'),
1963            test_info_of('test1'),
1964            test_info_of('test3'),
1965        ])
1966
1967        self.assertEqual(
1968            'deps(tests(//path1:test1_device + '
1969            '//path2:test2_host + '
1970            '//path3:test3_device))',
1971            query_file_contents.getvalue())
1972
1973    def test_trim_whitespace_in_bazel_query_output(self):
1974        run_command = self.mock_run_command(
1975            return_value='\n'.join(['  test1:host  ', 'test2:device  ', '  ']))
1976        runner = self.create_bazel_test_runner(
1977            modules=[
1978                supported_test_module(name='test1', path='path1'),
1979            ],
1980            run_command=run_command,
1981        )
1982
1983        reqs = runner.get_test_runner_build_reqs([test_info_of('test1')])
1984
1985        self.assertSetEqual({'test1-host', 'test2-target'}, reqs)
1986
1987    def test_build_variants_in_bazel_query_output(self):
1988        run_command = self.mock_run_command(
1989            return_value='\n'.join([
1990                'test1:host',
1991                'test2:host', 'test2:device',
1992                'test3:device',
1993                'test4:host', 'test4:host',
1994            ]))
1995        runner = self.create_bazel_test_runner(
1996            modules=[
1997                supported_test_module(name='test1', path='path1'),
1998                supported_test_module(name='test2', path='path2'),
1999                supported_test_module(name='test3', path='path3'),
2000                supported_test_module(name='test4', path='path4'),
2001            ],
2002            run_command = run_command,
2003        )
2004
2005        reqs = runner.get_test_runner_build_reqs([
2006            test_info_of('test1'),
2007            test_info_of('test2'),
2008            test_info_of('test3'),
2009            test_info_of('test4')])
2010
2011        self.assertSetEqual(
2012            {'test1-host', 'test2', 'test3-target', 'test4-host'},
2013            reqs)
2014
2015    def test_generate_single_run_command(self):
2016        test_infos = [test_info_of('test1')]
2017        runner = self.create_bazel_test_runner_for_tests(test_infos)
2018
2019        cmd = runner.generate_run_commands(test_infos, {})
2020
2021        self.assertEqual(1, len(cmd))
2022
2023    def test_generate_run_command_containing_targets_with_host_arg(self):
2024        test_infos = [test_info_of('test1'),
2025                      test_info_of('test2'),
2026                      test_info_of('test3')]
2027        runner = self.create_bazel_test_runner(
2028            [
2029                multi_config(host_unit_test_module(name='test1', path='path')),
2030                multi_config(host_unit_test_module(name='test2', path='path')),
2031                multi_config(test_module(name='test3', path='path')),
2032            ],
2033            host=True
2034        )
2035
2036        cmd = runner.generate_run_commands(test_infos, {})
2037
2038        self.assertTokensIn(
2039            ['//path:test1_host', '//path:test2_host', '//path:test3_host'],
2040            cmd[0])
2041
2042    def test_generate_run_command_containing_targets_without_host_arg(self):
2043        test_infos = [test_info_of('test1'), test_info_of('test2')]
2044        runner = self.create_bazel_test_runner(
2045            [
2046                multi_config(host_unit_test_module(name='test1', path='path')),
2047                host_unit_test_module(name='test2', path='path'),
2048            ],
2049        )
2050
2051        cmd = runner.generate_run_commands(test_infos, {})
2052
2053        self.assertTokensIn(['//path:test1_device', '//path:test2_host'],
2054                            cmd[0])
2055
2056    def test_generate_run_command_with_multi_bazel_args(self):
2057        test_infos = [test_info_of('test1')]
2058        runner = self.create_bazel_test_runner_for_tests(test_infos)
2059        extra_args = {constants.BAZEL_ARG: [['--option1=value1'],
2060                                            ['--option2=value2']]}
2061
2062        cmd = runner.generate_run_commands(test_infos, extra_args)
2063
2064        self.assertTokensIn(['--option1=value1', '--option2=value2'], cmd[0])
2065
2066    def test_generate_run_command_with_multi_custom_args(self):
2067        test_infos = [test_info_of('test1')]
2068        runner = self.create_bazel_test_runner_for_tests(test_infos)
2069        extra_args = {constants.CUSTOM_ARGS: ['-hello', '--world=value']}
2070
2071        cmd = runner.generate_run_commands(test_infos, extra_args)
2072
2073        self.assertTokensIn(['--test_arg=-hello',
2074                             '--test_arg=--world=value'], cmd[0])
2075
2076    def test_generate_run_command_with_custom_and_bazel_args(self):
2077        test_infos = [test_info_of('test1')]
2078        runner = self.create_bazel_test_runner_for_tests(test_infos)
2079        extra_args = {constants.CUSTOM_ARGS: ['-hello', '--world=value'],
2080                      constants.BAZEL_ARG: [['--option1=value1']]}
2081
2082        cmd = runner.generate_run_commands(test_infos, extra_args)
2083
2084        self.assertTokensIn(['--test_arg=-hello',
2085                             '--test_arg=--world=value',
2086                             '--option1=value1'], cmd[0])
2087
2088    def test_generate_run_command_with_tf_supported_all_abi_arg(self):
2089        test_infos = [test_info_of('test1')]
2090        runner = self.create_bazel_test_runner_for_tests(test_infos)
2091        extra_args = {constants.ALL_ABI: True}
2092
2093        cmd = runner.generate_run_commands(test_infos, extra_args)
2094
2095        self.assertTokensIn(['--test_arg=--all-abi'], cmd[0])
2096
2097    def test_generate_run_command_with_iterations_args(self):
2098        test_infos = [test_info_of('test1')]
2099        runner = self.create_bazel_test_runner_for_tests(test_infos)
2100        extra_args = {constants.ITERATIONS: 2}
2101
2102        cmd = runner.generate_run_commands(test_infos, extra_args)
2103
2104        self.assertTokensIn(['--runs_per_test=2'], cmd[0])
2105        self.assertNotIn('--test_arg=--retry-strategy', shlex.split(cmd[0]))
2106
2107    def test_generate_run_command_with_testinfo_filter(self):
2108        test_filter = test_filter_of('class1', ['method1'])
2109        test_infos = [test_info_of('test1', test_filters=[test_filter])]
2110        runner = self.create_bazel_test_runner_for_tests(test_infos)
2111
2112        cmd = runner.generate_run_commands(test_infos, {})
2113
2114        self.assertTokensIn(['--test_arg=--atest-include-filter',
2115                             '--test_arg=test1:class1#method1'], cmd[0])
2116
2117    def test_generate_run_command_with_bes_publish_enabled(self):
2118        test_infos = [test_info_of('test1')]
2119        extra_args = {
2120            constants.BAZEL_MODE_FEATURES: [
2121                bazel_mode.Features.EXPERIMENTAL_BES_PUBLISH
2122            ]
2123        }
2124        build_metadata = bazel_mode.BuildMetadata(
2125            'master', 'aosp_cf_x86_64_phone-userdebug')
2126        env = {
2127            'ATEST_BAZELRC': '/dir/atest.bazelrc',
2128            'ATEST_BAZEL_BES_PUBLISH_CONFIG': 'bes_publish'
2129        }
2130        runner = self.create_bazel_test_runner_for_tests(
2131            test_infos, build_metadata=build_metadata, env=env)
2132
2133        cmd = runner.generate_run_commands(
2134            test_infos,
2135            extra_args,
2136        )
2137
2138        self.assertTokensIn([
2139            '--bazelrc=/dir/atest.bazelrc',
2140            '--config=bes_publish',
2141            '--build_metadata=ab_branch=master',
2142            '--build_metadata=ab_target=aosp_cf_x86_64_phone-userdebug'
2143        ], cmd[0])
2144
2145    def test_generate_run_command_with_remote_enabled(self):
2146        test_infos = [test_info_of('test1')]
2147        extra_args = {
2148            constants.BAZEL_MODE_FEATURES: [
2149                bazel_mode.Features.EXPERIMENTAL_REMOTE
2150            ]
2151        }
2152        env = {
2153            'ATEST_BAZELRC': '/dir/atest.bazelrc',
2154            'ATEST_BAZEL_REMOTE_CONFIG': 'remote'
2155        }
2156        runner = self.create_bazel_test_runner_for_tests(
2157            test_infos, env=env)
2158
2159        cmd = runner.generate_run_commands(
2160            test_infos,
2161            extra_args,
2162        )
2163
2164        self.assertTokensIn([
2165            '--config=remote',
2166        ], cmd[0])
2167
2168    def test_generate_run_command_with_verbose_args(self):
2169        test_infos = [test_info_of('test1')]
2170        runner = self.create_bazel_test_runner_for_tests(test_infos)
2171        extra_args = {constants.VERBOSE: True}
2172
2173        cmd = runner.generate_run_commands(test_infos, extra_args)
2174
2175        self.assertTokensIn(['--test_output=all'], cmd[0])
2176
2177    def create_bazel_test_runner(self,
2178                                 modules,
2179                                 run_command=None,
2180                                 host=False,
2181                                 build_metadata=None,
2182                                 env=None):
2183        return bazel_mode.BazelTestRunner(
2184            'result_dir',
2185            mod_info=create_module_info(modules),
2186            src_top=Path('/src'),
2187            workspace_path=Path('/src/workspace'),
2188            run_command=run_command or self.mock_run_command(),
2189            extra_args={constants.HOST: host},
2190            build_metadata = build_metadata,
2191            env = env
2192        )
2193
2194    def create_bazel_test_runner_for_tests(self,
2195                                           test_infos,
2196                                           build_metadata=None,
2197                                           env=None):
2198        return self.create_bazel_test_runner(
2199            modules=[supported_test_module(name=t.test_name, path='path')
2200                     for t in test_infos],
2201            build_metadata=build_metadata,
2202            env=env
2203        )
2204
2205    def create_completed_process(self, args, returncode, stdout):
2206        return subprocess.CompletedProcess(args, returncode, stdout)
2207
2208    def mock_run_command(self, **kwargs):
2209        return mock.create_autospec(bazel_mode.default_run_command, **kwargs)
2210
2211    def assertTokensIn(self, expected_tokens, s):
2212        tokens = shlex.split(s)
2213        for token in expected_tokens:
2214            self.assertIn(token, tokens)
2215
2216
2217class FeatureParserTest(unittest.TestCase):
2218    """Tests for parsing Bazel mode feature flags."""
2219
2220    def test_parse_args_with_bazel_mode_feature(self):
2221        parser = argparse.ArgumentParser()
2222        bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features')
2223        # pylint: disable=no-member
2224        args = parser.parse_args([bazel_mode.Features.NULL_FEATURE.arg_flag])
2225
2226        self.assertListEqual([bazel_mode.Features.NULL_FEATURE],
2227                             args.bazel_mode_features)
2228
2229    def test_parse_args_without_bazel_mode_feature(self):
2230        parser = argparse.ArgumentParser()
2231        parser.add_argument('--foo',
2232                            action='append_const',
2233                            const='foo',
2234                            dest='foo')
2235        bazel_mode.add_parser_arguments(parser, dest='bazel_mode_features')
2236        args = parser.parse_args(['--foo'])
2237
2238        self.assertIsNone(args.bazel_mode_features)
2239
2240
2241def test_info_of(module_name, test_filters=None):
2242    return test_info.TestInfo(
2243        module_name, BAZEL_RUNNER, [],
2244        data={constants.TI_FILTER: frozenset(test_filters)}
2245        if test_filters else None)
2246
2247
2248def test_filter_of(class_name, methods=None):
2249    return test_info.TestFilter(
2250        class_name, frozenset(methods) if methods else frozenset())
2251
2252
2253def _get_query_file_content(args: List[str]) -> str:
2254    for arg in args:
2255        if arg.startswith('--query_file='):
2256            return Path(arg.split('=')[1]).read_text()
2257
2258    raise Exception('Query file not found!')
2259
2260
2261if __name__ == '__main__':
2262    unittest.main()
2263