xref: /aosp_15_r20/tools/asuite/atest/cli_translator_unittest.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1#!/usr/bin/env python3
2#
3# Copyright 2017, 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 cli_translator."""
18
19from importlib import reload
20from io import StringIO
21import json
22import os
23import re
24import sys
25import tempfile
26import unittest
27from unittest import mock
28from atest import arg_parser
29from atest import atest_utils
30from atest import cli_translator as cli_t
31from atest import constants
32from atest import module_info
33from atest import test_finder_handler
34from atest import test_mapping
35from atest import unittest_constants as uc
36from atest import unittest_utils
37from atest.metrics import metrics
38from atest.test_finders import module_finder
39from atest.test_finders import test_finder_base
40from atest.test_finders import test_finder_utils
41from pyfakefs import fake_filesystem_unittest
42
43
44# TEST_MAPPING related consts
45TEST_MAPPING_TOP_DIR = os.path.join(uc.TEST_DATA_DIR, 'test_mapping')
46TEST_MAPPING_DIR = os.path.join(TEST_MAPPING_TOP_DIR, 'folder1')
47TEST_1 = test_mapping.TestDetail({'name': 'test1', 'host': True})
48TEST_2 = test_mapping.TestDetail({'name': 'test2'})
49TEST_3 = test_mapping.TestDetail({'name': 'test3'})
50TEST_4 = test_mapping.TestDetail({'name': 'test4'})
51TEST_5 = test_mapping.TestDetail({'name': 'test5'})
52TEST_6 = test_mapping.TestDetail({'name': 'test6'})
53TEST_7 = test_mapping.TestDetail({'name': 'test7'})
54TEST_8 = test_mapping.TestDetail({'name': 'test8'})
55TEST_9 = test_mapping.TestDetail({'name': 'test9'})
56TEST_10 = test_mapping.TestDetail({'name': 'test10'})
57
58SEARCH_DIR_RE = re.compile(r'^find ([^ ]*).*$')
59BUILD_TOP_DIR = tempfile.TemporaryDirectory().name
60PRODUCT_OUT_DIR = os.path.join(BUILD_TOP_DIR, 'out/target/product/vsoc_x86_64')
61HOST_OUT_DIR = os.path.join(BUILD_TOP_DIR, 'out/host/linux-x86')
62
63
64# pylint: disable=unused-argument
65def gettestinfos_side_effect(
66    test_names, test_mapping_test_details=None, is_rebuild_module_info=False
67):
68  """Mock return values for _get_test_info."""
69  test_infos = []
70  for test_name in test_names:
71    if test_name == uc.MODULE_NAME:
72      test_infos.append(uc.MODULE_INFO)
73    if test_name == uc.CLASS_NAME:
74      test_infos.append(uc.CLASS_INFO)
75    if test_name == uc.HOST_UNIT_TEST_NAME_1:
76      test_infos.append(uc.MODULE_INFO_HOST_1)
77    if test_name == uc.HOST_UNIT_TEST_NAME_2:
78      test_infos.append(uc.MODULE_INFO_HOST_2)
79  return test_infos
80
81
82# pylint: disable=protected-access
83class CLITranslatorUnittests(unittest.TestCase):
84  """Unit tests for cli_t.py"""
85
86  def setUp(self):
87    """Run before execution of every test"""
88    self.ctr = cli_t.CLITranslator()
89
90    # Create a mock of args.
91    self.args = arg_parser.create_atest_arg_parser().parse_args()
92    self.args.tests = []
93    # Test mapping related args
94    self.args.test_mapping = False
95    self.args.include_subdirs = False
96    self.args.enable_file_patterns = False
97    self.args.rebuild_module_info = False
98    # Cache finder related args
99    self.args.clear_cache = False
100    self.ctr.mod_info = mock.Mock
101    self.ctr.mod_info.name_to_module_info = {}
102
103  def tearDown(self):
104    """Run after execution of every test"""
105    reload(uc)
106
107  @mock.patch.object(atest_utils, 'update_test_info_cache')
108  @mock.patch('builtins.input', return_value='n')
109  @mock.patch.object(module_finder.ModuleFinder, 'find_test_by_module_name')
110  @mock.patch.object(module_finder.ModuleFinder, 'get_fuzzy_searching_results')
111  @mock.patch.object(metrics, 'FindTestFinishEvent')
112  @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
113  # pylint: disable=too-many-locals
114  def test_get_test_infos(
115      self,
116      mock_getfindmethods,
117      _metrics,
118      mock_getfuzzyresults,
119      mock_findtestbymodule,
120      mock_input,
121      _mock_update_test_info,
122  ):
123    """Test _get_test_infos method."""
124    ctr = cli_t.CLITranslator()
125    find_method_return_module_info = lambda x, y: uc.MODULE_INFOS
126    # pylint: disable=invalid-name
127    find_method_return_module_class_info = (
128        lambda x, test: uc.MODULE_INFOS
129        if test == uc.MODULE_NAME
130        else uc.CLASS_INFOS
131    )
132    find_method_return_nothing = lambda x, y: None
133    one_test = [uc.MODULE_NAME]
134    mult_test = [uc.MODULE_NAME, uc.CLASS_NAME]
135
136    # Let's make sure we return what we expect.
137    expected_test_infos = [uc.MODULE_INFO]
138    mock_getfindmethods.return_value = [
139        test_finder_base.Finder(None, find_method_return_module_info, None)
140    ]
141    unittest_utils.assert_equal_testinfo_lists(
142        self, ctr._get_test_infos(one_test), expected_test_infos
143    )
144
145    # Check we receive multiple test infos.
146    expected_test_infos = [uc.MODULE_INFO, uc.CLASS_INFO]
147    mock_getfindmethods.return_value = [
148        test_finder_base.Finder(
149            None, find_method_return_module_class_info, None
150        )
151    ]
152    unittest_utils.assert_equal_testinfo_lists(
153        self, ctr._get_test_infos(mult_test), expected_test_infos
154    )
155
156    # Check return null set when we have no tests found or multiple results.
157    mock_getfindmethods.return_value = [
158        test_finder_base.Finder(None, find_method_return_nothing, None)
159    ]
160    null_test_info = []
161    mock_getfuzzyresults.return_value = []
162    self.assertEqual(null_test_info, ctr._get_test_infos(one_test))
163    self.assertEqual(null_test_info, ctr._get_test_infos(mult_test))
164
165    # Check returning test_info when the user says Yes.
166    mock_input.return_value = 'Y'
167    mock_getfindmethods.return_value = [
168        test_finder_base.Finder(None, find_method_return_module_info, None)
169    ]
170    mock_getfuzzyresults.return_value = one_test
171    mock_findtestbymodule.return_value = uc.MODULE_INFO
172    unittest_utils.assert_equal_testinfo_lists(
173        self, ctr._get_test_infos([uc.TYPO_MODULE_NAME]), [uc.MODULE_INFO]
174    )
175
176    # Check the method works for test mapping.
177    test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
178    test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
179    expected_test_infos = [uc.MODULE_INFO, uc.CLASS_INFO]
180    mock_getfindmethods.return_value = [
181        test_finder_base.Finder(
182            None, find_method_return_module_class_info, None
183        )
184    ]
185    test_infos = ctr._get_test_infos(mult_test, [test_detail1, test_detail2])
186    unittest_utils.assert_equal_testinfo_lists(
187        self, test_infos, expected_test_infos
188    )
189    for test_info in test_infos:
190      if test_info == uc.MODULE_INFO:
191        self.assertEqual(
192            test_detail1.options, test_info.data[constants.TI_MODULE_ARG]
193        )
194      else:
195        self.assertEqual(
196            test_detail2.options, test_info.data[constants.TI_MODULE_ARG]
197        )
198
199  @mock.patch.object(atest_utils, 'update_test_info_cache')
200  @mock.patch.object(metrics, 'FindTestFinishEvent')
201  @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
202  def test_get_test_infos_2(
203      self, mock_getfindmethods, _metrics, _mock_update_test_info
204  ):
205    """Test _get_test_infos method."""
206    ctr = cli_t.CLITranslator()
207    find_method_return_module_info2 = lambda x, y: uc.MODULE_INFOS2
208    find_method_ret_mod_cls_info2 = (
209        lambda x, test: uc.MODULE_INFOS2
210        if test == uc.MODULE_NAME
211        else uc.CLASS_INFOS2
212    )
213    one_test = [uc.MODULE_NAME]
214    mult_test = [uc.MODULE_NAME, uc.CLASS_NAME]
215    # Let's make sure we return what we expect.
216    expected_test_infos = [uc.MODULE_INFO, uc.MODULE_INFO2]
217    mock_getfindmethods.return_value = [
218        test_finder_base.Finder(None, find_method_return_module_info2, None)
219    ]
220    unittest_utils.assert_equal_testinfo_lists(
221        self, ctr._get_test_infos(one_test), expected_test_infos
222    )
223    # Check we receive multiple test infos.
224    expected_test_infos = [
225        uc.MODULE_INFO,
226        uc.MODULE_INFO2,
227        uc.CLASS_INFO,
228        uc.CLASS_INFO2,
229    ]
230    mock_getfindmethods.return_value = [
231        test_finder_base.Finder(None, find_method_ret_mod_cls_info2, None)
232    ]
233    unittest_utils.assert_equal_testinfo_lists(
234        self, ctr._get_test_infos(mult_test), expected_test_infos
235    )
236    # Check the method works for test mapping.
237    test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
238    test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
239    expected_test_infos = [
240        uc.MODULE_INFO,
241        uc.MODULE_INFO2,
242        uc.CLASS_INFO,
243        uc.CLASS_INFO2,
244    ]
245    mock_getfindmethods.return_value = [
246        test_finder_base.Finder(None, find_method_ret_mod_cls_info2, None)
247    ]
248    test_infos = ctr._get_test_infos(mult_test, [test_detail1, test_detail2])
249    unittest_utils.assert_equal_testinfo_lists(
250        self, test_infos, expected_test_infos
251    )
252    for test_info in test_infos:
253      if test_info in [uc.MODULE_INFO, uc.MODULE_INFO2]:
254        self.assertEqual(
255            test_detail1.options, test_info.data[constants.TI_MODULE_ARG]
256        )
257      elif test_info in [uc.CLASS_INFO, uc.CLASS_INFO2]:
258        self.assertEqual(
259            test_detail2.options, test_info.data[constants.TI_MODULE_ARG]
260        )
261
262  @mock.patch.object(module_finder.ModuleFinder, 'get_fuzzy_searching_results')
263  @mock.patch.object(metrics, 'FindTestFinishEvent')
264  @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
265  def test_get_test_infos_with_mod_info(
266      self,
267      mock_getfindmethods,
268      _metrics,
269      mock_getfuzzyresults,
270  ):
271    """Test _get_test_infos method."""
272    mod_info = module_info.load_from_file(
273        module_file=os.path.join(uc.TEST_DATA_DIR, uc.JSON_FILE)
274    )
275    ctr = cli_t.CLITranslator(mod_info=mod_info)
276    null_test_info = []
277    mock_getfindmethods.return_value = []
278    mock_getfuzzyresults.return_value = []
279    unittest_utils.assert_equal_testinfo_lists(
280        self, ctr._get_test_infos('not_exist_module'), null_test_info
281    )
282
283  @mock.patch.object(
284      test_finder_utils, 'find_host_unit_tests', return_value=set()
285  )
286  @mock.patch.object(
287      cli_t.CLITranslator,
288      '_get_test_infos',
289      side_effect=gettestinfos_side_effect,
290  )
291  def test_translate_class(self, _info, _find):
292    """Test translate method for tests by class name."""
293    # Check that we can find a class.
294    self.args.tests = [uc.CLASS_NAME]
295    self.args.host_unit_test_only = False
296    test_infos = self.ctr.translate(self.args)
297    unittest_utils.assert_strict_equal(
298        self, _gather_build_targets(test_infos), uc.CLASS_BUILD_TARGETS
299    )
300    unittest_utils.assert_equal_testinfo_lists(
301        self, test_infos, [uc.CLASS_INFO]
302    )
303
304  @mock.patch.object(
305      test_finder_utils, 'find_host_unit_tests', return_value=set()
306  )
307  @mock.patch.object(
308      cli_t.CLITranslator,
309      '_get_test_infos',
310      side_effect=gettestinfos_side_effect,
311  )
312  def test_translate_module(self, _info, _find):
313    """Test translate method for tests by module or class name."""
314    # Check that we get all the build targets we expect.
315    self.args.tests = [uc.MODULE_NAME, uc.CLASS_NAME]
316    self.args.host_unit_test_only = False
317    test_infos = self.ctr.translate(self.args)
318    unittest_utils.assert_strict_equal(
319        self,
320        _gather_build_targets(test_infos),
321        uc.MODULE_CLASS_COMBINED_BUILD_TARGETS,
322    )
323    unittest_utils.assert_equal_testinfo_lists(
324        self, test_infos, [uc.MODULE_INFO, uc.CLASS_INFO]
325    )
326
327  @mock.patch.object(os, 'getcwd', return_value='/src/build_top/somewhere')
328  @mock.patch.object(test_finder_utils, 'find_host_unit_tests', return_value=[])
329  @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
330  @mock.patch.object(
331      cli_t.CLITranslator,
332      '_get_test_infos',
333      side_effect=gettestinfos_side_effect,
334  )
335  def test_translate_test_mapping(
336      self, _info, mock_testmapping, _find_unit_tests, _getcwd
337  ):
338    """Test translate method for tests in test mapping."""
339    # Check that test mappings feeds into get_test_info properly.
340    test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
341    test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
342    mock_testmapping.return_value = ([test_detail1, test_detail2], None)
343    self.args.tests = []
344    self.args.host = False
345    self.args.host_unit_test_only = False
346    test_infos = self.ctr.translate(self.args)
347    unittest_utils.assert_strict_equal(
348        self,
349        _gather_build_targets(test_infos),
350        uc.MODULE_CLASS_COMBINED_BUILD_TARGETS,
351    )
352    unittest_utils.assert_equal_testinfo_lists(
353        self, test_infos, [uc.MODULE_INFO, uc.CLASS_INFO]
354    )
355
356  @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
357  @mock.patch.object(
358      cli_t.CLITranslator,
359      '_get_test_infos',
360      side_effect=gettestinfos_side_effect,
361  )
362  def test_translate_test_mapping_all(self, _info, mock_testmapping):
363    """Test translate method for tests in test mapping."""
364    # Check that test mappings feeds into get_test_info properly.
365    test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
366    test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
367    mock_testmapping.return_value = ([test_detail1, test_detail2], None)
368    self.args.tests = ['src_path:all']
369    self.args.test_mapping = True
370    self.args.host = False
371    test_infos = self.ctr.translate(self.args)
372    unittest_utils.assert_strict_equal(
373        self,
374        _gather_build_targets(test_infos),
375        uc.MODULE_CLASS_COMBINED_BUILD_TARGETS,
376    )
377    unittest_utils.assert_equal_testinfo_lists(
378        self, test_infos, [uc.MODULE_INFO, uc.CLASS_INFO]
379    )
380
381  def test_find_tests_by_test_mapping_presubmit(self):
382    """Test _find_tests_by_test_mapping method to locate presubmit tests."""
383    # TODO: (b/264015241) Stop mocking build variables.
384    os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
385    with mock.patch.dict('os.environ', os_environ_mock, clear=True):
386      tests, all_tests = self.ctr._find_tests_by_test_mapping(
387          path=TEST_MAPPING_DIR,
388          file_name='test_mapping_sample',
389          checked_files=set(),
390      )
391    expected = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
392    expected_all_tests = {
393        'presubmit': expected,
394        'postsubmit': set([TEST_3, TEST_6, TEST_8, TEST_10]),
395        'other_group': set([TEST_4]),
396    }
397    self.assertEqual(expected, tests)
398    self.assertEqual(expected_all_tests, all_tests)
399
400  def test_find_tests_by_test_mapping_postsubmit(self):
401    """Test _find_tests_by_test_mapping method to locate postsubmit tests."""
402    # TODO: (b/264015241) Stop mocking build variables.
403    os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
404    with mock.patch.dict('os.environ', os_environ_mock, clear=True):
405      tests, all_tests = self.ctr._find_tests_by_test_mapping(
406          path=TEST_MAPPING_DIR,
407          test_groups=[constants.TEST_GROUP_POSTSUBMIT],
408          file_name='test_mapping_sample',
409          checked_files=set(),
410      )
411    expected_presubmit = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
412    expected = set([TEST_3, TEST_6, TEST_8, TEST_10])
413    expected_all_tests = {
414        'presubmit': expected_presubmit,
415        'postsubmit': set([TEST_3, TEST_6, TEST_8, TEST_10]),
416        'other_group': set([TEST_4]),
417    }
418    self.assertEqual(expected, tests)
419    self.assertEqual(expected_all_tests, all_tests)
420
421  def test_find_tests_by_test_mapping_all_group(self):
422    """Test _find_tests_by_test_mapping method to locate postsubmit tests."""
423    # TODO: (b/264015241) Stop mocking build variables.
424    os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
425    with mock.patch.dict('os.environ', os_environ_mock, clear=True):
426      tests, all_tests = self.ctr._find_tests_by_test_mapping(
427          path=TEST_MAPPING_DIR,
428          test_groups=[constants.TEST_GROUP_ALL],
429          file_name='test_mapping_sample',
430          checked_files=set(),
431      )
432    expected_presubmit = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
433    expected = set([
434        TEST_1,
435        TEST_2,
436        TEST_3,
437        TEST_4,
438        TEST_5,
439        TEST_6,
440        TEST_7,
441        TEST_8,
442        TEST_9,
443        TEST_10,
444    ])
445    expected_all_tests = {
446        'presubmit': expected_presubmit,
447        'postsubmit': set([TEST_3, TEST_6, TEST_8, TEST_10]),
448        'other_group': set([TEST_4]),
449    }
450    self.assertEqual(expected, tests)
451    self.assertEqual(expected_all_tests, all_tests)
452
453  def test_find_tests_by_test_mapping_include_subdir(self):
454    """Test _find_tests_by_test_mapping method to include sub directory."""
455    # TODO: (b/264015241) Stop mocking build variables.
456    os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
457    with mock.patch.dict('os.environ', os_environ_mock, clear=True):
458      tests, all_tests = self.ctr._find_tests_by_test_mapping(
459          path=TEST_MAPPING_TOP_DIR,
460          file_name='test_mapping_sample',
461          include_subdirs=True,
462          checked_files=set(),
463      )
464    expected = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
465    expected_all_tests = {
466        'presubmit': expected,
467        'postsubmit': set([TEST_3, TEST_6, TEST_8, TEST_10]),
468        'other_group': set([TEST_4]),
469    }
470    self.assertEqual(expected, tests)
471    self.assertEqual(expected_all_tests, all_tests)
472
473  @mock.patch('builtins.input', return_value='')
474  def test_confirm_running(self, mock_input):
475    """Test _confirm_running method."""
476    self.assertTrue(self.ctr._confirm_running([TEST_1]))
477    mock_input.return_value = 'N'
478    self.assertFalse(self.ctr._confirm_running([TEST_2]))
479
480  def test_print_fuzzy_searching_results(self):
481    """Test _print_fuzzy_searching_results"""
482    modules = [uc.MODULE_NAME, uc.MODULE2_NAME]
483    capture_output = StringIO()
484    sys.stdout = capture_output
485    self.ctr._print_fuzzy_searching_results(modules)
486    sys.stdout = sys.__stdout__
487    output = 'Did you mean the following modules?\n{0}\n{1}\n'.format(
488        uc.MODULE_NAME, uc.MODULE2_NAME
489    )
490    self.assertEqual(capture_output.getvalue(), output)
491
492  def test_filter_comments(self):
493    """Test filter_comments method"""
494    file_with_comments = os.path.join(
495        TEST_MAPPING_TOP_DIR, 'folder6', 'test_mapping_sample_with_comments'
496    )
497    file_with_comments_golden = os.path.join(
498        TEST_MAPPING_TOP_DIR, 'folder6', 'test_mapping_sample_golden'
499    )
500    test_mapping_dict = json.loads(self.ctr.filter_comments(file_with_comments))
501    test_mapping_dict_gloden = None
502    with open(file_with_comments_golden) as json_file:
503      test_mapping_dict_gloden = json.load(json_file)
504
505    self.assertEqual(test_mapping_dict, test_mapping_dict_gloden)
506
507  @mock.patch.object(module_info.ModuleInfo, 'get_testable_modules')
508  def test_extract_testable_modules_by_wildcard(self, mock_mods):
509    """Test _extract_testable_modules_by_wildcard method."""
510    mod_info = module_info.load_from_file(
511        module_file=os.path.join(uc.TEST_DATA_DIR, uc.JSON_FILE)
512    )
513    ctr = cli_t.CLITranslator(mod_info=mod_info)
514    mock_mods.return_value = [
515        'test1',
516        'test2',
517        'test3',
518        'test11',
519        'Test22',
520        'Test100',
521        'aTest101',
522    ]
523    # test '*'
524    expr1 = ['test*']
525    result1 = ['test1', 'test2', 'test3', 'test11']
526    self.assertEqual(ctr._extract_testable_modules_by_wildcard(expr1), result1)
527    # test '?'
528    expr2 = ['test?']
529    result2 = ['test1', 'test2', 'test3']
530    self.assertEqual(ctr._extract_testable_modules_by_wildcard(expr2), result2)
531    # test '*' and '?'
532    expr3 = ['*Test???']
533    result3 = ['Test100', 'aTest101']
534    self.assertEqual(ctr._extract_testable_modules_by_wildcard(expr3), result3)
535
536  @mock.patch.object(os, 'getcwd', return_value='/src/build_top/somewhere')
537  @mock.patch.object(
538      test_finder_utils,
539      'find_host_unit_tests',
540      return_value=[uc.HOST_UNIT_TEST_NAME_1, uc.HOST_UNIT_TEST_NAME_2],
541  )
542  @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
543  @mock.patch.object(
544      cli_t.CLITranslator,
545      '_get_test_infos',
546      side_effect=gettestinfos_side_effect,
547  )
548  def test_translate_test_mapping_host_unit_test(
549      self, _info, mock_testmapping, _find_unit_tests, _getcwd
550  ):
551    """Test translate method for tests belong to host unit tests."""
552    # Check that test mappings feeds into get_test_info properly.
553    test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
554    test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
555    mock_testmapping.return_value = ([test_detail1, test_detail2], None)
556    self.args.tests = []
557    self.args.host = False
558    self.args.host_unit_test_only = False
559    test_infos = self.ctr.translate(self.args)
560    unittest_utils.assert_equal_testinfo_lists(
561        self,
562        test_infos,
563        [
564            uc.MODULE_INFO,
565            uc.CLASS_INFO,
566            uc.MODULE_INFO_HOST_1,
567            uc.MODULE_INFO_HOST_2,
568        ],
569    )
570
571  @mock.patch.object(
572      test_finder_utils,
573      'find_host_unit_tests',
574      return_value=[uc.HOST_UNIT_TEST_NAME_1, uc.HOST_UNIT_TEST_NAME_2],
575  )
576  @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
577  @mock.patch.object(
578      cli_t.CLITranslator,
579      '_get_test_infos',
580      side_effect=gettestinfos_side_effect,
581  )
582  def test_translate_test_mapping_without_host_unit_test(
583      self, _info, mock_testmapping, _find_unit_tests
584  ):
585    """Test translate method not using host unit tests if test_mapping arg ."""
586    # Check that test mappings feeds into get_test_info properly.
587    test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
588    test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
589    mock_testmapping.return_value = ([test_detail1, test_detail2], None)
590    self.args.tests = []
591    self.args.host = False
592    self.args.test_mapping = True
593    self.args.host_unit_test_only = False
594    test_infos = self.ctr.translate(self.args)
595    unittest_utils.assert_equal_testinfo_lists(
596        self, test_infos, [uc.MODULE_INFO, uc.CLASS_INFO]
597    )
598
599
600class ParseTestIdentifierTest(unittest.TestCase):
601  """Test parse_test_identifier with different test names."""
602
603  def test_no_mainline_modules(self):
604    """non-mainline module testing."""
605    given = 'testName'
606
607    identifier = cli_t.parse_test_identifier(given)
608
609    self.assertEqual('testName', identifier.test_name)
610    self.assertEqual([], identifier.module_names)
611    self.assertEqual([], identifier.binary_names)
612
613  def test_single_mainline_module(self):
614    """only one mainline module."""
615    given = 'testName[Module1.apk]'
616
617    identifier = cli_t.parse_test_identifier(given)
618
619    self.assertEqual('testName', identifier.test_name)
620    self.assertEqual(['Module1'], identifier.module_names)
621    self.assertEqual(['Module1.apk'], identifier.binary_names)
622
623  def test_multiple_mainline_modules(self):
624    """multiple mainline modules."""
625    given = 'testName[Module1.apk+Module2.apex]'
626
627    identifier = cli_t.parse_test_identifier(given)
628
629    self.assertEqual('testName', identifier.test_name)
630    self.assertEqual(['Module1', 'Module2'], identifier.module_names)
631    self.assertEqual(['Module1.apk', 'Module2.apex'], identifier.binary_names)
632
633  def test_missing_closing_bracket(self):
634    """test the brackets are not in pair"""
635    given = 'testName[Module1.apk+Module2.apex'
636
637    identifier = cli_t.parse_test_identifier(given)
638
639    self.assertEqual(given, identifier.test_name)
640    self.assertEqual([], identifier.module_names)
641    self.assertEqual([], identifier.binary_names)
642
643
644class VerifyMainlineModuleTest(fake_filesystem_unittest.TestCase):
645  """test verify_mainline_modules sub-methods."""
646
647  def setUp(self):
648    """Setup func."""
649    self.setUpPyfakefs()
650
651  def test_verified_mainline_modules_no_brackets(self):
652    """True for it's not in mainline module pattern. (no brackets)"""
653    test_name = 'module0'
654    mod_info = create_module_info([
655        module(name=test_name),
656    ])
657
658    test_identifier = cli_t.parse_test_identifier(test_name)
659    translator = cli_t.CLITranslator(mod_info)
660
661    self.assertTrue(translator._verified_mainline_modules(test_identifier))
662
663  def test_verified_mainline_modules_not_valid_test_module(self):
664    """False for test_name is not a module."""
665    test_name = 'module1[foo.apk+goo.apk]'
666    mod_info = create_module_info([
667        module(name='module_1'),
668        module(name='foo'),
669        module(name='goo'),
670    ])
671
672    test_identifier = cli_t.parse_test_identifier(test_name)
673    translator = cli_t.CLITranslator(mod_info)
674
675    self.assertFalse(translator._verified_mainline_modules(test_identifier))
676
677  def test_verified_mainline_modules_not_valid_mainline_module(self):
678    """False for mainline_modules are not modules."""
679    test_name = 'module2[foo.apk+goo.apk]'
680    mod_info = create_module_info([module(name='module2')])
681
682    test_identifier = cli_t.parse_test_identifier(test_name)
683    translator = cli_t.CLITranslator(mod_info)
684
685    self.assertFalse(translator._verified_mainline_modules(test_identifier))
686
687  def test_verified_mainline_modules_no_test_mainline_modules(self):
688    """False for no definition in `test_mainline_modules` attribute."""
689    test_name = 'module3[foo.apk+goo.apex]'
690    mod_info = create_module_info([
691        module(name='module3', test_mainline_modules=[]),
692        module(name='foo', installed=['out/path/foo.apk']),
693        module(name='goo', installed=['out/path/goo.apex']),
694    ])
695
696    test_identifier = cli_t.parse_test_identifier(test_name)
697    translator = cli_t.CLITranslator(mod_info)
698
699    self.assertFalse(translator._verified_mainline_modules(test_identifier))
700
701  def test_verified_mainline_modules_test_mainline_modules(self):
702    """True for definition in `test_mainline_modules` attribute."""
703    test_name = 'module4[foo.apk+goo.apex]'
704    mod_info = create_module_info([
705        module(name='module4', test_mainline_modules=['foo.apk+goo.apex']),
706        module(name='foo', installed=['out/path/foo.apk']),
707        module(name='goo', installed=['out/path/goo.apex']),
708    ])
709
710    test_identifier = cli_t.parse_test_identifier(test_name)
711    translator = cli_t.CLITranslator(mod_info)
712
713    self.assertTrue(translator._verified_mainline_modules(test_identifier))
714
715  def test_verified_mainline_modules_were_in_test_config(self):
716    """True for mainline modules were defined in the test_config."""
717    test_name = 'module5[foo.apk+goo.apex]'
718    mainline_config = 'out/module3/AndroidTest.xml'
719    self.fs.create_file(
720        mainline_config,
721        contents="""
722            <configuration description="Mainline Module tests">
723                <option name="config"
724                        key="parameter" value="value_1" />
725                <option name="config-descriptor:metadata"
726                        key="mainline-param" value="foo.apk+goo.apex" />
727            </configuration>
728            """,
729    )
730    mod_info = create_module_info([
731        module(
732            name='module5',
733            test_config=[mainline_config],
734            test_mainline_modules=[],
735            auto_test_config=[],
736        ),
737        module(name='foo', installed=['out/path/foo.apk']),
738        module(name='goo', installed=['out/path/goo.apex']),
739    ])
740
741    test_identifier = cli_t.parse_test_identifier(test_name)
742    translator = cli_t.CLITranslator(mod_info)
743
744    self.assertTrue(translator._verified_mainline_modules(test_identifier))
745
746  def test_verified_mainline_modules_were_in_auto_config(self):
747    """True for 'auto_test_config'=[True]"""
748    test_name = 'module6[foo.apk+goo.apex]'
749    mod_info = create_module_info([
750        module(
751            name='module6',
752            test_config=['somewhere/AndroidTest.xml'],
753            auto_test_config=[True],
754        ),
755        module(name='foo', installed=['out/path/foo.apk']),
756        module(name='goo', installed=['out/path/goo.apex']),
757    ])
758
759    test_identifier = cli_t.parse_test_identifier(test_name)
760    translator = cli_t.CLITranslator(mod_info)
761
762    self.assertTrue(translator._verified_mainline_modules(test_identifier))
763
764  def test_verified_mainline_modules_have_no_association(self):
765    """False for null auto_test_config and null `mainline-param` in the test_config."""
766    test_name = 'module7[foo.apk+goo.apex]'
767    config = 'out/module7/AndroidTest.xml'
768    self.fs.create_file(
769        config,
770        contents="""
771            <configuration description="Mainline Module tests">
772                <option name="config"
773                        key="parameter" value="value_1" />
774            </configuration>
775            """,
776    )
777    mod_info = create_module_info([
778        module(name='module7', test_config=[config], auto_test_config=[]),
779        module(name='foo', installed=['out/path/foo.apk']),
780        module(name='goo', installed=['out/path/goo.apex']),
781    ])
782
783    test_identifier = cli_t.parse_test_identifier(test_name)
784    translator = cli_t.CLITranslator(mod_info)
785
786    self.assertFalse(translator._verified_mainline_modules(test_identifier))
787
788  def test_verified_mainline_modules_were_in_auto_config(self):
789    """False for the given mainline is a capex file."""
790    test_name = 'module8[foo.apk+goo.apex]'
791    mod_info = create_module_info([
792        module(name='module8', test_mainline_modules=['foo.apk+goo.apex']),
793        module(name='foo', installed=['out/path/foo.apk']),
794        module(name='goo', installed=['out/path/goo.capex']),
795    ])
796
797    test_identifier = cli_t.parse_test_identifier(test_name)
798    translator = cli_t.CLITranslator(mod_info)
799
800    self.assertFalse(translator._verified_mainline_modules(test_identifier))
801
802
803def create_module_info(modules=None):
804  """wrapper func for creating module_info.ModuleInfo"""
805  name_to_module_info = {}
806  modules = modules or []
807
808  for m in modules:
809    name_to_module_info[m['module_name']] = m
810
811  return module_info.load_from_dict(name_to_module_info)
812
813
814def module(
815    name=None,
816    path=None,
817    installed=None,
818    classes=None,
819    auto_test_config=None,
820    test_config=None,
821    shared_libs=None,
822    dependencies=None,
823    runtime_dependencies=None,
824    data=None,
825    data_dependencies=None,
826    compatibility_suites=None,
827    host_dependencies=None,
828    srcs=None,
829    supported_variants=None,
830    test_mainline_modules=None,
831):
832  """return a module info dict."""
833  name = name or 'libhello'
834
835  m = {}
836
837  m['module_name'] = name
838  m['class'] = classes or ['ETC']
839  m['path'] = [path or '']
840  m['installed'] = installed or []
841  m['is_unit_test'] = 'false'
842  m['auto_test_config'] = auto_test_config or []
843  m['test_config'] = test_config or []
844  m['shared_libs'] = shared_libs or []
845  m['runtime_dependencies'] = runtime_dependencies or []
846  m['dependencies'] = dependencies or []
847  m['data'] = data or []
848  m['data_dependencies'] = data_dependencies or []
849  m['compatibility_suites'] = compatibility_suites or []
850  m['host_dependencies'] = host_dependencies or []
851  m['srcs'] = srcs or []
852  m['supported_variants'] = supported_variants or []
853  m['test_mainline_modules'] = test_mainline_modules or []
854  return m
855
856
857def _gather_build_targets(test_infos):
858  targets = set()
859  for t_info in test_infos:
860    targets |= t_info.build_targets
861  return targets
862
863
864if __name__ == '__main__':
865  unittest.main()
866