xref: /aosp_15_r20/tools/asuite/atest/module_info_unittest.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1#!/usr/bin/env python3
2#
3# Copyright 2018, 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"""Unittests for module_info."""
18
19# pylint: disable=invalid-name
20# pylint: disable=missing-function-docstring
21# pylint: disable=too-many-lines
22
23import os
24from pathlib import Path
25import shutil
26import tempfile
27import unittest
28from unittest import mock
29from atest import constants
30from atest import module_info
31from atest import unittest_constants as uc
32from atest import unittest_utils
33from pyfakefs import fake_filesystem_unittest
34
35JSON_FILE_PATH = os.path.join(uc.TEST_DATA_DIR, uc.JSON_FILE)
36CC_DEP_PATH = os.path.join(uc.TEST_DATA_DIR, uc.CC_DEP_FILE)
37JAVA_DEP_PATH = os.path.join(uc.TEST_DATA_DIR, uc.JAVA_DEP_FILE)
38EXPECTED_MOD_TARGET = 'tradefed'
39EXPECTED_MOD_TARGET_PATH = ['tf/core']
40UNEXPECTED_MOD_TARGET = 'this_should_not_be_in_module-info.json'
41MOD_NO_PATH = 'module-no-path'
42PATH_TO_MULT_MODULES = 'shared/path/to/be/used'
43MULT_MOODULES_WITH_SHARED_PATH = ['module2', 'module1']
44PATH_TO_MULT_MODULES_WITH_MULTI_ARCH = 'shared/path/to/be/used2'
45TESTABLE_MODULES_WITH_SHARED_PATH = [
46    'multiarch1',
47    'multiarch2',
48    'multiarch3',
49    'multiarch3_32',
50]
51
52ROBO_MOD_PATH = ['/shared/robo/path']
53ROBO_MODULE = 'FooTests'
54ASSOCIATED_ROBO_MODULE = 'RunFooTests'
55ROBO_MODULE_INFO = {
56    constants.MODULE_NAME: ROBO_MODULE,
57    constants.MODULE_PATH: ROBO_MOD_PATH,
58    constants.MODULE_CLASS: [constants.MODULE_CLASS_JAVA_LIBRARIES],
59}
60ASSOCIATED_ROBO_MODULE_INFO = {
61    constants.MODULE_NAME: ASSOCIATED_ROBO_MODULE,
62    constants.MODULE_PATH: ROBO_MOD_PATH,
63    constants.MODULE_CLASS: [constants.MODULE_CLASS_ROBOLECTRIC],
64}
65MOD_PATH_INFO_DICT = {
66    ROBO_MOD_PATH[0]: [ASSOCIATED_ROBO_MODULE_INFO, ROBO_MODULE_INFO]
67}
68MOD_NAME_INFO_DICT = {
69    ASSOCIATED_ROBO_MODULE: ASSOCIATED_ROBO_MODULE_INFO,
70    ROBO_MODULE: ROBO_MODULE_INFO,
71}
72MOD_NAME1 = 'mod1'
73MOD_NAME2 = 'mod2'
74MOD_NAME3 = 'mod3'
75MOD_NAME4 = 'mod4'
76MOD_INFO_DICT = {}
77MODULE_INFO = {
78    constants.MODULE_NAME: 'random_name',
79    constants.MODULE_PATH: 'a/b/c/path',
80    constants.MODULE_CLASS: ['random_class'],
81}
82NAME_TO_MODULE_INFO = {'random_name': MODULE_INFO}
83
84MOBLY_MODULE = 'mobly-test'
85MOBLY_MODULE_NO_TAG = 'mobly-test-no-tag'
86
87# Mocking path allows str only, use os.path instead of Path.
88with tempfile.TemporaryDirectory() as temp_dir:
89  BUILD_TOP_DIR = temp_dir
90SOONG_OUT_DIR = os.path.join(BUILD_TOP_DIR, 'out/soong')
91PRODUCT_OUT_DIR = os.path.join(BUILD_TOP_DIR, 'out/target/product/vsoc_x86_64')
92HOST_OUT_DIR = os.path.join(BUILD_TOP_DIR, 'out/host/linux-x86')
93
94
95# TODO: (b/263199608) Suppress too-many-public-methods after refactoring.
96# pylint: disable=protected-access, too-many-public-methods
97class ModuleInfoUnittests(unittest.TestCase):
98  """Unit tests for module_info.py"""
99
100  def setUp(self) -> None:
101    for path in [BUILD_TOP_DIR, PRODUCT_OUT_DIR, SOONG_OUT_DIR, HOST_OUT_DIR]:
102      if not Path(path).is_dir():
103        Path(path).mkdir(parents=True)
104    shutil.copy2(JSON_FILE_PATH, PRODUCT_OUT_DIR)
105    self.json_file_path = Path(PRODUCT_OUT_DIR).joinpath(uc.JSON_FILE)
106    shutil.copy2(CC_DEP_PATH, SOONG_OUT_DIR)
107    self.cc_dep_path = Path(SOONG_OUT_DIR).joinpath(uc.CC_DEP_FILE)
108    shutil.copy2(JAVA_DEP_PATH, SOONG_OUT_DIR)
109    self.java_dep_path = Path(SOONG_OUT_DIR).joinpath(uc.JAVA_DEP_FILE)
110    self.merged_dep_path = Path(PRODUCT_OUT_DIR).joinpath(uc.MERGED_DEP_FILE)
111
112  def tearDown(self) -> None:
113    if self.merged_dep_path.is_file():
114      os.remove(self.merged_dep_path)
115
116  def test_target_name_is_relative_to_build_top(self):
117    build_top = '/src/build_top'
118    product_out = '/src/build_top/pout'
119    env_mock = {
120        constants.ANDROID_BUILD_TOP: build_top,
121        constants.ANDROID_PRODUCT_OUT: product_out,
122    }
123    expected_target = os.path.relpath(
124        os.path.join(product_out, 'module-info.json'), build_top
125    )
126
127    with mock.patch.dict('os.environ', env_mock, clear=True):
128      actual_target = module_info.get_module_info_target()
129
130      self.assertEqual(actual_target, expected_target)
131
132  def test_target_name_is_in_absolute_path(self):
133    build_top = '/src/build_top'
134    product_out = '/somewhere/pout'
135    env_mock = {
136        constants.ANDROID_BUILD_TOP: build_top,
137        constants.ANDROID_PRODUCT_OUT: product_out,
138    }
139    expected_target = os.path.join(product_out, 'module-info.json')
140
141    with mock.patch.dict('os.environ', env_mock, clear=True):
142      actual_target = module_info.get_module_info_target()
143
144      self.assertEqual(actual_target, expected_target)
145
146  @mock.patch.object(module_info.Loader, 'load')
147  def test_get_path_to_module_info(self, mock_load_module):
148    """Test that we correctly create the path to module info dict."""
149    mod_one = 'mod1'
150    mod_two = 'mod2'
151    mod_path_one = '/path/to/mod1'
152    mod_path_two = '/path/to/mod2'
153    mod_info_dict = {
154        mod_one: {
155            constants.MODULE_PATH: [mod_path_one],
156            constants.MODULE_NAME: mod_one,
157        },
158        mod_two: {
159            constants.MODULE_PATH: [mod_path_two],
160            constants.MODULE_NAME: mod_two,
161        },
162    }
163    mock_load_module.return_value = mod_info_dict
164    path_to_mod_info = {
165        mod_path_one: [{
166            constants.MODULE_NAME: mod_one,
167            constants.MODULE_PATH: [mod_path_one],
168        }],
169        mod_path_two: [{
170            constants.MODULE_NAME: mod_two,
171            constants.MODULE_PATH: [mod_path_two],
172        }],
173    }
174    self.assertDictEqual(
175        path_to_mod_info, module_info.get_path_to_module_info(mod_info_dict)
176    )
177
178  def test_is_module(self):
179    """Test that we get the module when it's properly loaded."""
180    # Load up the test json file and check that module is in it
181    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
182    self.assertTrue(mod_info.is_module(EXPECTED_MOD_TARGET))
183    self.assertFalse(mod_info.is_module(UNEXPECTED_MOD_TARGET))
184
185  def test_get_path(self):
186    """Test that we get the module path when it's properly loaded."""
187    # Load up the test json file and check that module is in it
188    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
189    self.assertEqual(
190        mod_info.get_paths(EXPECTED_MOD_TARGET), EXPECTED_MOD_TARGET_PATH
191    )
192    self.assertEqual(mod_info.get_paths(MOD_NO_PATH), [])
193
194  def test_get_module_names(self):
195    """test that we get the module name properly."""
196    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
197    self.assertEqual(
198        mod_info.get_module_names(EXPECTED_MOD_TARGET_PATH[0]),
199        [EXPECTED_MOD_TARGET],
200    )
201    unittest_utils.assert_strict_equal(
202        self,
203        mod_info.get_module_names(PATH_TO_MULT_MODULES),
204        MULT_MOODULES_WITH_SHARED_PATH,
205    )
206
207  def test_path_to_mod_info(self):
208    """test that we get the module name properly."""
209    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
210    module_list = []
211    for path_to_mod_info in mod_info.path_to_module_info[
212        PATH_TO_MULT_MODULES_WITH_MULTI_ARCH
213    ]:
214      module_list.append(path_to_mod_info.get(constants.MODULE_NAME))
215    module_list.sort()
216    TESTABLE_MODULES_WITH_SHARED_PATH.sort()
217    self.assertEqual(module_list, TESTABLE_MODULES_WITH_SHARED_PATH)
218
219  def test_is_suite_in_compatibility_suites(self):
220    """Test is_suite_in_compatibility_suites."""
221    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
222    info = {'compatibility_suites': []}
223    self.assertFalse(mod_info.is_suite_in_compatibility_suites('cts', info))
224    info2 = {'compatibility_suites': ['cts']}
225    self.assertTrue(mod_info.is_suite_in_compatibility_suites('cts', info2))
226    self.assertFalse(mod_info.is_suite_in_compatibility_suites('vts10', info2))
227    info3 = {'compatibility_suites': ['cts', 'vts10']}
228    self.assertTrue(mod_info.is_suite_in_compatibility_suites('cts', info3))
229    self.assertTrue(mod_info.is_suite_in_compatibility_suites('vts10', info3))
230    self.assertFalse(mod_info.is_suite_in_compatibility_suites('ats', info3))
231
232  def test_get_testable_modules(self):
233    """Test get_testable_modules."""
234    expected_testable_modules = {'Module1', 'Module2', 'Module3'}
235    expected_test_suite_modules = {'Module1', 'Module2'}
236    expected_null_suite_modules = {'Module3'}
237    mod_info = create_module_info(
238        modules=[
239            test_module(name='Module1', compatibility_suites=['test-suite']),
240            test_module(name='Module2', compatibility_suites=['test-suite']),
241            test_module(name='Module3'),
242            non_test_module(name='Dep1'),
243        ]
244    )
245
246    actual_all_testable_modules = mod_info.get_testable_modules()
247    actual_test_suite_modules = mod_info.get_testable_modules('test-suite')
248    actual_null_suite_modules = mod_info.get_testable_modules('null-suite')
249
250    self.assertEqual(actual_all_testable_modules, expected_testable_modules)
251    self.assertEqual(actual_test_suite_modules, expected_test_suite_modules)
252    self.assertEqual(actual_null_suite_modules, expected_null_suite_modules)
253
254  @mock.patch.dict(
255      'os.environ',
256      {
257          constants.ANDROID_BUILD_TOP: '/',
258          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
259      },
260  )
261  def test_is_mobly_test(self):
262    """Test is_mobly_test."""
263    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
264    self.assertTrue(
265        mod_info.is_mobly_module(mod_info.get_module_info(MOBLY_MODULE))
266    )
267    self.assertFalse(
268        mod_info.is_mobly_module(mod_info.get_module_info(MOBLY_MODULE_NO_TAG))
269    )
270
271  @mock.patch.dict(
272      'os.environ',
273      {
274          constants.ANDROID_BUILD_TOP: '/',
275          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
276      },
277  )
278  @mock.patch.object(module_info.ModuleInfo, 'get_robolectric_type')
279  def test_is_robolectric_test(self, mock_type):
280    """Test is_robolectric_test."""
281    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
282    mock_type.return_value = constants.ROBOTYPE_MODERN
283    self.assertTrue(mod_info.is_robolectric_test(ROBO_MODULE))
284    mock_type.return_value = constants.ROBOTYPE_LEGACY
285    self.assertTrue(mod_info.is_robolectric_test(ROBO_MODULE))
286    mock_type.return_value = 0
287    self.assertFalse(mod_info.is_robolectric_test(ROBO_MODULE))
288
289  @mock.patch.object(module_info.ModuleInfo, 'is_module')
290  def test_is_auto_gen_test_config(self, mock_is_module):
291    """Test is_auto_gen_test_config correctly detects the module."""
292    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
293    mock_is_module.return_value = True
294    is_auto_test_config = {'auto_test_config': [True]}
295    is_not_auto_test_config = {'auto_test_config': [False]}
296    is_not_auto_test_config_again = {'auto_test_config': []}
297    MOD_INFO_DICT[MOD_NAME1] = is_auto_test_config
298    MOD_INFO_DICT[MOD_NAME2] = is_not_auto_test_config
299    MOD_INFO_DICT[MOD_NAME3] = is_not_auto_test_config_again
300    MOD_INFO_DICT[MOD_NAME4] = {}
301    mod_info.name_to_module_info = MOD_INFO_DICT
302    self.assertTrue(mod_info.is_auto_gen_test_config(MOD_NAME1))
303    self.assertFalse(mod_info.is_auto_gen_test_config(MOD_NAME2))
304    self.assertFalse(mod_info.is_auto_gen_test_config(MOD_NAME3))
305    self.assertFalse(mod_info.is_auto_gen_test_config(MOD_NAME4))
306
307  def test_merge_build_system_infos(self):
308    """Test _merge_build_system_infos."""
309    loader = module_info.Loader(
310        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
311    )
312    mod_info_1 = {
313        constants.MODULE_NAME: 'module_1',
314        constants.MODULE_DEPENDENCIES: [],
315    }
316    name_to_mod_info = {'module_1': mod_info_1}
317    expect_deps = ['test_dep_level_1_1', 'test_dep_level_1_2']
318    name_to_mod_info = loader._merge_build_system_infos(
319        name_to_mod_info, java_bp_info_path=self.java_dep_path
320    )
321    self.assertEqual(
322        name_to_mod_info['module_1'].get(constants.MODULE_DEPENDENCIES),
323        expect_deps,
324    )
325
326  def test_merge_build_system_infos_missing_keys(self):
327    """Test _merge_build_system_infos for keys missing from module-info.json."""
328    loader = module_info.Loader(
329        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
330    )
331    name_to_mod_info = loader._merge_build_system_infos(
332        {}, java_bp_info_path=self.java_dep_path
333    )
334
335    expect_deps = ['test_dep_level_1_1']
336    self.assertEqual(
337        name_to_mod_info['not_in_module_info'].get(
338            constants.MODULE_DEPENDENCIES
339        ),
340        expect_deps,
341    )
342
343  def test_merge_dependency_with_ori_dependency(self):
344    """Test _merge_dependency."""
345    loader = module_info.Loader(
346        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
347    )
348    mod_info_1 = {
349        constants.MODULE_NAME: 'module_1',
350        constants.MODULE_DEPENDENCIES: ['ori_dep_1'],
351    }
352    name_to_mod_info = {'module_1': mod_info_1}
353    expect_deps = ['ori_dep_1', 'test_dep_level_1_1', 'test_dep_level_1_2']
354    name_to_mod_info = loader._merge_build_system_infos(
355        name_to_mod_info, java_bp_info_path=self.java_dep_path
356    )
357    self.assertEqual(
358        name_to_mod_info['module_1'].get(constants.MODULE_DEPENDENCIES),
359        expect_deps,
360    )
361
362  @mock.patch.dict(
363      'os.environ',
364      {
365          constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR,
366          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
367      },
368  )
369  def test_get_instrumentation_target_apps(self):
370    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
371    artifacts = {
372        'AmSlam': {
373            os.path.join(
374                uc.TEST_DATA_DIR,
375                'out/target/product/generic/data/app/AmSlam/AmSlam.apk',
376            )
377        }
378    }
379    # 1. If Android.bp is available, use `manifest` to determine the actual
380    # manifest.
381    bp_context = """android_test    {
382            name: "AmSlamTests",
383            manifest: 'AndroidManifest.xml',
384            instrumentation_for: "AmSlam"
385        }"""
386    bp_file = os.path.join(uc.TEST_DATA_DIR, 'foo/bar/AmSlam/test/Android.bp')
387    with open(bp_file, 'w', encoding='utf-8') as cache:
388      cache.write(bp_context)
389    self.assertEqual(
390        mod_info.get_instrumentation_target_apps('AmSlamTests'), artifacts
391    )
392    os.remove(bp_file)
393    # 2. If Android.bp is unavailable, search `AndroidManifest.xml`
394    # arbitrarily.
395    self.assertEqual(
396        mod_info.get_instrumentation_target_apps('AmSlamTests'), artifacts
397    )
398
399  @mock.patch.dict(
400      'os.environ',
401      {
402          constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR,
403          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
404      },
405  )
406  def test_get_target_module_by_pkg(self):
407    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
408    self.assertEqual(
409        'AmSlam',
410        mod_info.get_target_module_by_pkg(
411            package='c0m.andr0id.settingS',
412            search_from=Path(uc.TEST_DATA_DIR).joinpath('foo/bar/AmSlam/test'),
413        ),
414    )
415
416  @mock.patch.dict(
417      'os.environ',
418      {
419          constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR,
420          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
421      },
422  )
423  def test_get_artifact_map(self):
424    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
425    artifacts = {
426        'AmSlam': {
427            os.path.join(
428                uc.TEST_DATA_DIR,
429                'out/target/product/generic/data/app/AmSlam/AmSlam.apk',
430            )
431        }
432    }
433    self.assertEqual(mod_info.get_artifact_map('AmSlam'), artifacts)
434
435  @mock.patch.dict(
436      'os.environ',
437      {
438          constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR,
439          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
440      },
441  )
442  def test_get_filepath_from_module(self):
443    """Test for get_filepath_from_module."""
444    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
445
446    expected_filepath = Path(uc.TEST_DATA_DIR).joinpath(
447        'foo/bar/AmSlam', 'AndroidManifest.xml'
448    )
449    self.assertEqual(
450        mod_info.get_filepath_from_module('AmSlam', 'AndroidManifest.xml'),
451        expected_filepath,
452    )
453
454    expected_filepath = Path(uc.TEST_DATA_DIR).joinpath(
455        'foo/bar/AmSlam/test', 'AndroidManifest.xml'
456    )
457    self.assertEqual(
458        mod_info.get_filepath_from_module('AmSlamTests', 'AndroidManifest.xml'),
459        expected_filepath,
460    )
461
462  def test_get_module_dependency(self):
463    """Test get_module_dependency."""
464    loader = module_info.Loader(
465        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
466    )
467    mod_info = loader.load()
468    expect_deps = {
469        'test_dep_level_1_1',
470        'module_1',
471        'test_dep_level_1_2',
472        'test_dep_level_2_2',
473        'test_dep_level_2_1',
474        'module_2',
475    }
476    loader._merge_build_system_infos(
477        loader.name_to_module_info, java_bp_info_path=self.java_dep_path
478    )
479    self.assertEqual(
480        mod_info.get_module_dependency('dep_test_module'), expect_deps
481    )
482
483  def test_get_module_dependency_w_loop(self):
484    """Test get_module_dependency with problem dep file."""
485    loader = module_info.Loader(
486        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
487    )
488    mod_info = loader.load()
489    # Java dependency file with a endless loop define.
490    java_dep_file = os.path.join(
491        uc.TEST_DATA_DIR, 'module_bp_java_loop_deps.json'
492    )
493    expect_deps = {
494        'test_dep_level_1_1',
495        'module_1',
496        'test_dep_level_1_2',
497        'test_dep_level_2_2',
498        'test_dep_level_2_1',
499        'module_2',
500    }
501    loader._merge_build_system_infos(
502        loader.name_to_module_info, java_bp_info_path=java_dep_file
503    )
504    self.assertEqual(
505        mod_info.get_module_dependency('dep_test_module'), expect_deps
506    )
507
508  def test_get_install_module_dependency(self):
509    """Test get_install_module_dependency."""
510    loader = module_info.Loader(
511        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
512    )
513    mod_info = loader.load()
514    expect_deps = {'module_1', 'test_dep_level_2_1'}
515    loader._merge_build_system_infos(
516        loader.name_to_module_info, java_bp_info_path=self.java_dep_path
517    )
518    self.assertEqual(
519        mod_info.get_install_module_dependency('dep_test_module'), expect_deps
520    )
521
522  def test_cc_merge_build_system_infos(self):
523    """Test _merge_build_system_infos for cc."""
524    loader = module_info.Loader(
525        module_file=JSON_FILE_PATH, need_merge_fn=lambda: True
526    )
527    mod_info_1 = {
528        constants.MODULE_NAME: 'module_cc_1',
529        constants.MODULE_DEPENDENCIES: [],
530    }
531    name_to_mod_info = {'module_cc_1': mod_info_1}
532    expect_deps = ['test_cc_dep_level_1_1', 'test_cc_dep_level_1_2']
533    name_to_mod_info = loader._merge_build_system_infos(
534        name_to_mod_info, cc_bp_info_path=self.cc_dep_path
535    )
536    self.assertEqual(
537        name_to_mod_info['module_cc_1'].get(constants.MODULE_DEPENDENCIES),
538        expect_deps,
539    )
540
541  def test_is_unit_test(self):
542    """Test is_unit_test."""
543    module_name = 'myModule'
544    maininfo_with_unittest = {
545        constants.MODULE_NAME: module_name,
546        constants.MODULE_IS_UNIT_TEST: 'true',
547    }
548
549    self.assertTrue(module_info.ModuleInfo.is_unit_test(maininfo_with_unittest))
550
551  def test_is_host_unit_test(self):
552    """Test is_host_unit_test."""
553    module_name = 'myModule'
554    maininfo_with_host_unittest = {
555        constants.MODULE_NAME: module_name,
556        constants.MODULE_IS_UNIT_TEST: 'true',
557        'compatibility_suites': ['host-unit-tests'],
558        constants.MODULE_INSTALLED: uc.DEFAULT_INSTALL_PATH,
559        'auto_test_config': ['true'],
560    }
561
562    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
563
564    self.assertTrue(mod_info.is_host_unit_test(maininfo_with_host_unittest))
565
566  def test_is_device_driven_test(self):
567    module_name = 'myModule'
568    maininfo_with_device_driven_test = {
569        constants.MODULE_NAME: module_name,
570        constants.MODULE_TEST_CONFIG: [
571            os.path.join(uc.TEST_CONFIG_DATA_DIR, 'a.xml.data')
572        ],
573        constants.MODULE_INSTALLED: uc.DEFAULT_INSTALL_PATH,
574        'supported_variants': ['DEVICE'],
575    }
576    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
577
578    self.assertTrue(
579        mod_info.is_device_driven_test(maininfo_with_device_driven_test)
580    )
581
582  def test_not_device_driven_test_when_suite_is_robolectric_test(self):
583    module_name = 'myModule'
584    maininfo_with_device_driven_test = {
585        constants.MODULE_NAME: module_name,
586        constants.MODULE_TEST_CONFIG: [
587            os.path.join(uc.TEST_CONFIG_DATA_DIR, 'a.xml.data')
588        ],
589        constants.MODULE_INSTALLED: uc.DEFAULT_INSTALL_PATH,
590        'supported_variants': ['DEVICE'],
591        'compatibility_suites': ['robolectric-tests'],
592    }
593    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
594
595    self.assertFalse(
596        mod_info.is_device_driven_test(maininfo_with_device_driven_test)
597    )
598
599  def test_is_host_driven_test(self):
600    """Test is_host_driven_test."""
601    test_name = 'myModule'
602    expected_host_driven_info = {
603        constants.MODULE_NAME: test_name,
604        constants.MODULE_TEST_CONFIG: [
605            os.path.join(uc.TEST_CONFIG_DATA_DIR, 'a.xml.data')
606        ],
607        constants.MODULE_INSTALLED: uc.DEFAULT_INSTALL_PATH,
608        'supported_variants': ['HOST'],
609    }
610    mod_info = create_module_info([
611        module(
612            name=test_name,
613            test_config=[os.path.join(uc.TEST_CONFIG_DATA_DIR, 'a.xml.data')],
614            installed=uc.DEFAULT_INSTALL_PATH,
615            supported_variants=['HOST'],
616        )
617    ])
618
619    return_value = mod_info.is_host_driven_test(expected_host_driven_info)
620
621    self.assertTrue(return_value)
622
623  # TODO: (b/264015241) Stop mocking build variables.
624  # TODO: (b/263199608) Re-write the test after refactoring module-info.py
625  @mock.patch.dict(
626      'os.environ',
627      {
628          constants.ANDROID_BUILD_TOP: uc.ATEST_PKG_DIR,
629          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
630      },
631  )
632  def test_has_mainline_modules(self):
633    """Test has_mainline_modules."""
634    name1 = 'MainModule1'
635    mainline_module1 = ['foo2.apk', 'foo3.apk']
636    name2 = 'MainModule2'
637    mainline_module2 = ['foo1.apex']
638    name3 = 'MainModule3'
639
640    mod_info = module_info.load_from_file(module_file=JSON_FILE_PATH)
641    # found in 'test_mainlne_modules' attribute.
642    self.assertTrue(mod_info.has_mainline_modules(name1, mainline_module1))
643    # found in the value of 'mainline-param' in test_config.
644    self.assertTrue(mod_info.has_mainline_modules(name2, mainline_module2))
645    # cannot be found in both 'test_mainline_modules' and 'test_config'.
646    self.assertFalse(mod_info.has_mainline_modules(name3, mainline_module2))
647
648  # TODO: (b/264015241) Stop mocking build variables.
649  # TODO: (b/263199608) Re-write the test after refactoring module-info.py
650  @mock.patch.dict(
651      'os.environ',
652      {
653          constants.ANDROID_BUILD_TOP: os.path.dirname(__file__),
654          constants.ANDROID_PRODUCT_OUT: PRODUCT_OUT_DIR,
655      },
656  )
657  def test_get_module_info_for_multi_lib_module(self):
658    my_module_name = 'MyMultiArchTestModule'
659    multi_arch_json = os.path.join(
660        uc.TEST_DATA_DIR, 'multi_arch_module-info.json'
661    )
662    mod_info = module_info.load_from_file(module_file=multi_arch_json)
663
664    self.assertIsNotNone(mod_info.get_module_info(my_module_name))
665
666  def test_get_modules_by_include_deps_w_testable_module_only_false(self):
667    module_1 = module(
668        name='module_1',
669        dependencies=['dep1', 'dep2'],
670    )
671    module_2 = module(name='module_2', dependencies=['dep1', 'dep3'])
672    mod_info = create_module_info([module_1, module_2])
673
674    self.assertEqual(
675        {'module_1', 'module_2'},
676        mod_info.get_modules_by_include_deps(
677            {'dep1'}, testable_module_only=False
678        ),
679    )
680    self.assertEqual(
681        {'module_1'},
682        mod_info.get_modules_by_include_deps(
683            {'dep2'}, testable_module_only=False
684        ),
685    )
686    self.assertEqual(
687        {'module_2'},
688        mod_info.get_modules_by_include_deps(
689            {'dep3'}, testable_module_only=False
690        ),
691    )
692
693  @mock.patch.object(module_info.ModuleInfo, 'get_testable_modules')
694  def test_get_modules_by_include_deps_w_testable_module_only_true(
695      self, _testable_modules
696  ):
697    module_1 = module(
698        name='module_1',
699        dependencies=['dep1', 'dep2'],
700    )
701    module_2 = module(name='module_2', dependencies=['dep1', 'dep3'])
702    mod_info = create_module_info([module_1, module_2])
703    _testable_modules.return_value = []
704
705    self.assertEqual(
706        set(),
707        mod_info.get_modules_by_include_deps(
708            {'dep1'}, testable_module_only=True
709        ),
710    )
711
712  def test_get_modules_by_path_in_srcs_no_module_found(self):
713    module_1 = module(
714        name='module_1',
715        srcs=['path/src1', 'path/src2'],
716    )
717    module_2 = module(name='module_2', srcs=['path/src2', 'path/src3'])
718    mod_info = create_module_info([module_1, module_2])
719
720    self.assertEqual(set(), mod_info.get_modules_by_path_in_srcs('path/src4'))
721
722  def test_get_modules_by_path_in_srcs_one_module_found(self):
723    module_1 = module(
724        name='module_1',
725        srcs=['path/src1', 'path/src2'],
726    )
727    module_2 = module(name='module_2', srcs=['path/src2', 'path/src3'])
728    mod_info = create_module_info([module_1, module_2])
729
730    self.assertEqual(
731        {'module_1'}, mod_info.get_modules_by_path_in_srcs('path/src1')
732    )
733
734  def test_get_modules_by_path_in_srcs_multiple_module_found(self):
735    module_1 = module(
736        name='module_1',
737        srcs=['path/src1', 'path/src2'],
738    )
739    module_2 = module(name='module_2', srcs=['path/src2', 'path/src3'])
740    mod_info = create_module_info([module_1, module_2])
741
742    self.assertEqual(
743        {'module_1', 'module_2'},
744        mod_info.get_modules_by_path_in_srcs('path/src2'),
745    )
746
747  def test_contains_same_mainline_modules(self):
748    mainline_modules = {'A.apex', 'B.apk'}
749    self.assertTrue(
750        module_info.contains_same_mainline_modules(
751            mainline_modules, {'B.apk+A.apex'}
752        )
753    )
754    self.assertFalse(
755        module_info.contains_same_mainline_modules(
756            mainline_modules, {'B.apk+C.apex'}
757        )
758    )
759
760  def test_get_installed_paths_have_abs_path(self):
761    mod_info = create_module_info(
762        [module(name='my_module', installed=[Path('/a/b/c/d')])]
763    )
764
765    self.assertEqual(
766        mod_info.get_installed_paths('my_module'), [Path('/a/b/c/d')]
767    )
768
769  @mock.patch.dict(
770      'os.environ', {constants.ANDROID_BUILD_TOP: '/mocked/build_top'}
771  )
772  def test_get_installed_paths_have_relative_path(self):
773    mod_info = create_module_info(
774        [module(name='my_module', installed=['a/b/c/d'])]
775    )
776
777    self.assertEqual(
778        mod_info.get_installed_paths('my_module'),
779        [Path('/mocked/build_top/a/b/c/d')],
780    )
781
782  def test_get_code_under_test_module_name_is_not_found_in_module_info(self):
783    mod_info = create_module_info([
784        module(
785            name='my_module',
786            code_under_test='code_under_test_module',
787        )
788    ])
789
790    # module_that_is_not_in_module_info is not found in mod_info.
791    self.assertEqual(
792        mod_info.get_code_under_test('module_that_is_not_in_module_info'), [],
793    )
794
795  def test_get_code_under_test_code_under_test_is_not_defined_in_module_info(self):
796    mod_info = create_module_info([module(name='my_module')])
797
798    # my_module is found in mod_info but code_under_test is not defined.
799    self.assertEqual(
800        mod_info.get_code_under_test('my_module'), [],
801    )
802
803  def test_get_code_under_test_code_under_test_is_defined_in_module_info(self):
804    mod_info = create_module_info([
805        module(
806            name='my_module',
807            code_under_test='code_under_test_module',
808        )
809    ])
810
811    self.assertEqual(
812        mod_info.get_code_under_test('my_module'),
813        'code_under_test_module',
814    )
815
816
817class ModuleInfoTestFixture(fake_filesystem_unittest.TestCase):
818  """Fixture for ModuleInfo tests."""
819
820  def setUp(self):
821    self.setUpPyfakefs()
822
823  # pylint: disable=protected-access
824  def create_empty_module_info(self):
825    fake_temp_file_name = next(tempfile._get_candidate_names())
826    self.fs.create_file(fake_temp_file_name, contents='{}')
827    return module_info.load_from_file(module_file=fake_temp_file_name)
828
829  def create_module_info(self, modules=None):
830    mod_info = self.create_empty_module_info()
831    modules = modules or []
832
833    for m in modules:
834      mod_info.name_to_module_info[m['module_name']] = m
835      for path in m['path']:
836        if path in mod_info.path_to_module_info:
837          mod_info.path_to_module_info[path].append(m)
838        else:
839          mod_info.path_to_module_info[path] = [m]
840
841    return mod_info
842
843
844class HasTestConfonfigTest(ModuleInfoTestFixture):
845  """Tests has_test_config in various conditions."""
846
847  def test_return_true_if_test_config_is_not_empty(self):
848    test_module_info = module(test_config=['config_file'])
849    mod_info = self.create_module_info()
850
851    return_value = mod_info.has_test_config(test_module_info)
852
853    self.assertTrue(return_value)
854
855  def test_return_true_if_auto_test_config_is_not_empty(self):
856    test_module_info = module(auto_test_config=['no_empty'])
857    mod_info = self.create_module_info()
858
859    return_value = mod_info.has_test_config(test_module_info)
860
861    self.assertTrue(return_value)
862
863  def test_return_false_if_auto_test_config_and_test_config_empty(self):
864    test_module_info = module(test_config=[], auto_test_config=[])
865    mod_info = self.create_module_info()
866
867    return_value = mod_info.has_test_config(test_module_info)
868
869    self.assertFalse(return_value)
870
871
872class ModuleInfoCompatibilitySuiteTest(ModuleInfoTestFixture):
873  """Tests the compatibility suite in the module info."""
874
875  def test_return_true_if_suite_in_test(self):
876    test_module_info = module(compatibility_suites=['test_suite'])
877    mod_info = self.create_module_info()
878
879    return_value = mod_info.is_suite_in_compatibility_suites(
880        'test_suite', test_module_info
881    )
882
883    self.assertTrue(return_value)
884
885  def test_return_false_if_suite_not_in_test(self):
886    test_module_info = module(compatibility_suites=['no_suite'])
887    mod_info = self.create_module_info()
888
889    return_value = mod_info.is_suite_in_compatibility_suites(
890        'test_suite', test_module_info
891    )
892
893    self.assertFalse(return_value)
894
895  def test_return_false_when_mod_info_is_empty(self):
896    test_module_info = None
897    mod_info = self.create_module_info()
898
899    return_value = mod_info.is_suite_in_compatibility_suites(
900        'test_suite', test_module_info
901    )
902
903    self.assertFalse(return_value)
904
905  def test_return_false_when_mod_info_is_not_a_dict(self):
906    test_module_info = ['no_a_dict']
907    mod_info = self.create_module_info()
908
909    return_value = mod_info.is_suite_in_compatibility_suites(
910        'test_suite', test_module_info
911    )
912
913    self.assertFalse(return_value)
914
915
916class RobolectricTestNameTest(ModuleInfoTestFixture):
917  """Tests the Robolectric test name in the module info."""
918
919  def test_return_empty_for_a_modern_robolectric_test(self):
920    module_name = 'hello_world_test'
921    info = modern_robolectric_test_module(name=f'{module_name}')
922    mod_info = self.create_module_info(modules=[info])
923
924    return_module = mod_info.get_robolectric_test_name(info)
925
926    self.assertEqual('', return_module)
927
928  def test_return_related_robolectric_run_module_name(self):
929    module_name = 'hello_world_test'
930    run_module_name = f'Run{module_name}'
931    module_path = 'robolectric_path'
932    info = non_test_module(name=f'{module_name}', path=module_path)
933    mod_info = self.create_module_info(
934        modules=[
935            info,
936            robolectric_class_non_test_module(
937                name=f'{run_module_name}', path=module_path
938            ),
939        ]
940    )
941
942    return_module = mod_info.get_robolectric_test_name(info)
943
944    self.assertEqual(run_module_name, return_module)
945
946  def test_return_empty_when_no_related_robolectic_class_module(self):
947    module_name = 'hello_world_test'
948    run_module_name = f'Run{module_name}'
949    module_path = 'robolectric_path'
950    info = non_test_module(name=f'{module_name}', path=module_path)
951    mod_info = self.create_module_info(
952        modules=[
953            info,
954            non_test_module(name=f'{run_module_name}', path=module_path),
955        ]
956    )
957
958    return_module = mod_info.get_robolectric_test_name(info)
959
960    self.assertEqual('', return_module)
961
962  def test_return_empty_if_related_module_name_not_start_with_Run(self):
963    module_name = 'hello_world_test'
964    run_module_name = f'Not_Run{module_name}'
965    module_path = 'robolectric_path'
966    info = robolectric_class_non_test_module(
967        name=f'{run_module_name}', path=module_path
968    )
969    mod_info = self.create_module_info(
970        modules=[
971            non_test_module(name=f'{module_name}', path=module_path),
972            info,
973        ]
974    )
975
976    return_module = mod_info.get_robolectric_test_name(info)
977
978    self.assertEqual('', return_module)
979
980  def test_return_itself_for_a_robolectric_class_test_module(self):
981    module_name = 'Run_hello_world_test'
982    info = robolectric_class_non_test_module(name=f'{module_name}')
983    mod_info = self.create_module_info(modules=[info])
984
985    return_module = mod_info.get_robolectric_test_name(info)
986
987    self.assertEqual(module_name, return_module)
988
989  def test_return_empty_if_robolectric_class_module_not_start_with_Run(self):
990    module_name = 'hello_world_test'
991    info = robolectric_class_non_test_module(name=f'{module_name}')
992    mod_info = self.create_module_info(modules=[info])
993
994    return_module = mod_info.get_robolectric_test_name(info)
995
996    self.assertEqual('', return_module)
997
998  def test_return_0_when_no_mod_info(self):
999    module_name = 'hello_world_test'
1000    info = non_test_module(name=module_name)
1001    mod_info = self.create_module_info(modules=[info])
1002
1003    return_module = mod_info.get_robolectric_test_name(info)
1004
1005    self.assertEqual('', return_module)
1006
1007
1008class RobolectricTestTypeTest(ModuleInfoTestFixture):
1009  """Tests the Robolectric test type in the module info."""
1010
1011  def test_modern_robolectric_test_type(self):
1012    module_name = 'hello_world_test'
1013    mod_info = self.create_module_info(
1014        modules=[
1015            modern_robolectric_test_module(name=f'{module_name}'),
1016        ]
1017    )
1018
1019    return_value = mod_info.get_robolectric_type(module_name)
1020
1021    self.assertEqual(return_value, constants.ROBOTYPE_MODERN)
1022
1023  def test_return_modern_if_compliant_with_modern_and_legacy(self):
1024    module_name = 'hello_world_test'
1025    module_path = 'robolectric_path'
1026    run_module_name = f'Run{module_name}'
1027    mod_info = self.create_module_info(
1028        modules=[
1029            modern_robolectric_test_module(
1030                name=f'{module_name}', path=module_path
1031            ),
1032            robolectric_class_non_test_module(
1033                name=f'{run_module_name}', path=module_path
1034            ),
1035        ]
1036    )
1037
1038    return_value = mod_info.get_robolectric_type(module_name)
1039
1040    self.assertEqual(return_value, constants.ROBOTYPE_MODERN)
1041
1042  def test_not_modern_robolectric_test_if_suite_is_not_robolectric(self):
1043    module_name = 'hello_world_test'
1044    mod_info = self.create_module_info(
1045        modules=[
1046            non_test_module(
1047                name=f'{module_name}',
1048                compatibility_suites='not_robolectric_tests',
1049            ),
1050        ]
1051    )
1052
1053    return_value = mod_info.get_robolectric_type(module_name)
1054
1055    self.assertEqual(return_value, 0)
1056
1057  def test_legacy_robolectric_test_type_if_has_related_run_robolectric_class_module(
1058      self,
1059  ):
1060    module_name = 'hello_world_test'
1061    run_module_name = f'Run{module_name}'
1062    module_path = 'robolectric_path'
1063    mod_info = self.create_module_info(
1064        modules=[
1065            non_test_module(name=f'{module_name}', path=module_path),
1066            robolectric_class_non_test_module(
1067                name=f'{run_module_name}', path=module_path
1068            ),
1069        ]
1070    )
1071
1072    return_value = mod_info.get_robolectric_type(module_name)
1073
1074    self.assertEqual(return_value, constants.ROBOTYPE_LEGACY)
1075
1076  def test_not_legacy_robolectric_test_type_if_module_is_tradefed_testable(
1077      self,
1078  ):
1079    module_name = 'hello_world_test'
1080    run_module_name = f'Run{module_name}'
1081    module_path = 'robolectric_path'
1082    mod_info = self.create_module_info(
1083        modules=[
1084            test_module(name=f'{module_name}', path=module_path),
1085            robolectric_class_test_module(
1086                name=f'{run_module_name}', path=module_path
1087            ),
1088        ]
1089    )
1090
1091    return_value = mod_info.get_robolectric_type(module_name)
1092
1093    self.assertEqual(return_value, 0)
1094
1095  def test_robolectric_class_test_module(self):
1096    module_name = 'Run_hello_world_test'
1097    mod_info = self.create_module_info(
1098        modules=[
1099            robolectric_class_non_test_module(name=f'{module_name}'),
1100        ]
1101    )
1102
1103    return_value = mod_info.get_robolectric_type(module_name)
1104
1105    self.assertEqual(return_value, constants.ROBOTYPE_LEGACY)
1106
1107  def test_not_robolectric_test_if_module_name_not_start_with_Run(self):
1108    module_name = 'hello_world_test'
1109    mod_info = self.create_module_info(
1110        modules=[
1111            robolectric_class_non_test_module(name=f'{module_name}'),
1112        ]
1113    )
1114
1115    return_value = mod_info.get_robolectric_type(module_name)
1116
1117    self.assertEqual(return_value, 0)
1118
1119  def test_return_0_when_no_related_robolectic_class_module(self):
1120    module_name = 'hello_world_test'
1121    run_module_name = f'Run{module_name}'
1122    module_path = 'robolectric_path'
1123    mod_info = self.create_module_info(
1124        modules=[
1125            non_test_module(name=f'{module_name}', path=module_path),
1126            non_test_module(name=f'{run_module_name}', path=module_path),
1127        ]
1128    )
1129
1130    return_value = mod_info.get_robolectric_type(module_name)
1131
1132    self.assertEqual(return_value, 0)
1133
1134  def test_return_0_when_no_related_module_name_start_with_Run(self):
1135    module_name = 'hello_world_test'
1136    run_module_name = f'Not_Run{module_name}'
1137    module_path = 'robolectric_path'
1138    mod_info = self.create_module_info(
1139        modules=[
1140            non_test_module(name=f'{module_name}', path=module_path),
1141            robolectric_class_non_test_module(
1142                name=f'{run_module_name}', path=module_path
1143            ),
1144        ]
1145    )
1146
1147    return_value = mod_info.get_robolectric_type(module_name)
1148
1149    self.assertEqual(return_value, 0)
1150
1151  def test_return_0_when_no_mod_info(self):
1152    module_name = 'hello_world_test'
1153    mod_info = self.create_module_info()
1154
1155    return_value = mod_info.get_robolectric_type(module_name)
1156
1157    self.assertEqual(return_value, 0)
1158
1159
1160class IsLegacyRobolectricClassTest(ModuleInfoTestFixture):
1161  """Tests is_legacy_robolectric_class in various conditions."""
1162
1163  def test_return_true_if_module_class_is_robolectric(self):
1164    test_module_info = module(classes=[constants.MODULE_CLASS_ROBOLECTRIC])
1165    mod_info = self.create_module_info()
1166
1167    return_value = mod_info.is_legacy_robolectric_class(test_module_info)
1168
1169    self.assertTrue(return_value)
1170
1171  def test_return_false_if_module_class_is_not_robolectric(self):
1172    test_module_info = module(classes=['not_robolectric'])
1173    mod_info = self.create_module_info()
1174
1175    return_value = mod_info.is_legacy_robolectric_class(test_module_info)
1176
1177    self.assertFalse(return_value)
1178
1179  def test_return_false_if_module_class_is_empty(self):
1180    test_module_info = module(classes=[])
1181    mod_info = self.create_module_info()
1182
1183    return_value = mod_info.is_legacy_robolectric_class(test_module_info)
1184
1185    self.assertFalse(return_value)
1186
1187
1188class IsTestableModuleTest(ModuleInfoTestFixture):
1189  """Tests is_testable_module in various conditions."""
1190
1191  def test_return_true_for_tradefed_testable_module(self):
1192    info = test_module()
1193    mod_info = self.create_module_info()
1194
1195    return_value = mod_info.is_testable_module(info)
1196
1197    self.assertTrue(return_value)
1198
1199  def test_return_true_for_modern_robolectric_test_module(self):
1200    info = modern_robolectric_test_module()
1201    mod_info = self.create_module_info()
1202
1203    return_value = mod_info.is_testable_module(info)
1204
1205    self.assertTrue(return_value)
1206
1207  def test_return_true_for_legacy_robolectric_test_module(self):
1208    info = legacy_robolectric_test_module()
1209    mod_info = self.create_module_info(modules=[info])
1210
1211    return_value = mod_info.is_testable_module(info)
1212
1213    self.assertTrue(return_value)
1214
1215  def test_return_false_for_non_tradefed_testable_module(self):
1216    info = module(
1217        auto_test_config=[], test_config=[], installed=['installed_path']
1218    )
1219    mod_info = self.create_module_info()
1220
1221    return_value = mod_info.is_testable_module(info)
1222
1223    self.assertFalse(return_value)
1224
1225  def test_return_false_for_no_installed_path_module(self):
1226    info = module(auto_test_config=['true'], installed=[])
1227    mod_info = self.create_module_info()
1228
1229    return_value = mod_info.is_testable_module(info)
1230
1231    self.assertFalse(return_value)
1232
1233  def test_return_false_if_module_info_is_empty(self):
1234    info = {}
1235    mod_info = self.create_module_info()
1236
1237    return_value = mod_info.is_testable_module(info)
1238
1239    self.assertFalse(return_value)
1240
1241
1242def create_module_info(modules=None):
1243  name_to_module_info = {}
1244  modules = modules or []
1245
1246  for m in modules:
1247    name_to_module_info[m['module_name']] = m
1248
1249  return module_info.load_from_dict(name_to_module_info)
1250
1251
1252def test_module(**kwargs):
1253  kwargs.setdefault('name', 'hello_world_test')
1254  return test(module(**kwargs))
1255
1256
1257def non_test_module(**kwargs):
1258  kwargs.setdefault('name', 'not_a_test')
1259  return non_test(module(**kwargs))
1260
1261
1262def modern_robolectric_test_module(**kwargs):
1263  kwargs.setdefault('name', 'hello_world_test')
1264  return test(robolectric_tests_suite(module(**kwargs)))
1265
1266
1267def legacy_robolectric_test_module(**kwargs):
1268  kwargs.setdefault('name', 'Run_hello_world_test')
1269  return robolectric_class_non_test_module(**kwargs)
1270
1271
1272def robolectric_class_test_module(**kwargs):
1273  kwargs.setdefault('name', 'hello_world_test')
1274  return test(robolectric_class(module(**kwargs)))
1275
1276
1277def robolectric_class_non_test_module(**kwargs):
1278  kwargs.setdefault('name', 'hello_world_test')
1279  return non_test(robolectric_class(module(**kwargs)))
1280
1281
1282# pylint: disable=too-many-arguments, too-many-locals
1283def module(
1284    name=None,
1285    path=None,
1286    installed=None,
1287    classes=None,
1288    auto_test_config=None,
1289    test_config=None,
1290    shared_libs=None,
1291    dependencies=None,
1292    runtime_dependencies=None,
1293    data=None,
1294    data_dependencies=None,
1295    compatibility_suites=None,
1296    host_dependencies=None,
1297    srcs=None,
1298    supported_variants=None,
1299    code_under_test=None,
1300):
1301  name = name or 'libhello'
1302
1303  m = {}
1304
1305  m['module_name'] = name
1306  m['class'] = classes or ['ETC']
1307  m['path'] = [path or '']
1308  m['installed'] = installed or []
1309  m['is_unit_test'] = 'false'
1310  m['auto_test_config'] = auto_test_config or []
1311  m['test_config'] = test_config or []
1312  m['shared_libs'] = shared_libs or []
1313  m['runtime_dependencies'] = runtime_dependencies or []
1314  m['dependencies'] = dependencies or []
1315  m['data'] = data or []
1316  m['data_dependencies'] = data_dependencies or []
1317  m['compatibility_suites'] = compatibility_suites or []
1318  m['host_dependencies'] = host_dependencies or []
1319  m['srcs'] = srcs or []
1320  m['supported_variants'] = supported_variants or []
1321  m['code_under_test'] = code_under_test or []
1322  return m
1323
1324
1325def test(info):
1326  info['auto_test_config'] = ['true']
1327  info['installed'] = ['installed_path']
1328  return info
1329
1330
1331def non_test(info):
1332  info['auto_test_config'] = []
1333  info['installed'] = []
1334  return info
1335
1336
1337def robolectric_class(info):
1338  info['class'] = ['ROBOLECTRIC']
1339  return info
1340
1341
1342def robolectric_tests_suite(info):
1343  info = test(info)
1344  info.setdefault('compatibility_suites', []).append('robolectric-tests')
1345  return info
1346
1347
1348if __name__ == '__main__':
1349  unittest.main()
1350