• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19# pylint: disable=line-too-long
20
21import unittest
22import json
23import os
24import re
25import sys
26
27from importlib import reload
28from io import StringIO
29from unittest import mock
30
31import cli_translator as cli_t
32import constants
33import module_info
34import test_finder_handler
35import test_mapping
36import unittest_constants as uc
37import unittest_utils
38
39from metrics import metrics
40from test_finders import module_finder
41from test_finders import test_finder_base
42from test_finders import test_finder_utils
43
44
45# TEST_MAPPING related consts
46TEST_MAPPING_TOP_DIR = os.path.join(uc.TEST_DATA_DIR, 'test_mapping')
47TEST_MAPPING_DIR = os.path.join(TEST_MAPPING_TOP_DIR, 'folder1')
48TEST_1 = test_mapping.TestDetail({'name': 'test1', 'host': True})
49TEST_2 = test_mapping.TestDetail({'name': 'test2'})
50TEST_3 = test_mapping.TestDetail({'name': 'test3'})
51TEST_4 = test_mapping.TestDetail({'name': 'test4'})
52TEST_5 = test_mapping.TestDetail({'name': 'test5'})
53TEST_6 = test_mapping.TestDetail({'name': 'test6'})
54TEST_7 = test_mapping.TestDetail({'name': 'test7'})
55TEST_8 = test_mapping.TestDetail({'name': 'test8'})
56TEST_9 = test_mapping.TestDetail({'name': 'test9'})
57TEST_10 = test_mapping.TestDetail({'name': 'test10'})
58
59SEARCH_DIR_RE = re.compile(r'^find ([^ ]*).*$')
60
61
62#pylint: disable=unused-argument
63def gettestinfos_side_effect(test_names, test_mapping_test_details=None,
64                             is_rebuild_module_info=False):
65    """Mock return values for _get_test_info."""
66    test_infos = set()
67    for test_name in test_names:
68        if test_name == uc.MODULE_NAME:
69            test_infos.add(uc.MODULE_INFO)
70        if test_name == uc.CLASS_NAME:
71            test_infos.add(uc.CLASS_INFO)
72        if test_name == uc.HOST_UNIT_TEST_NAME_1:
73            test_infos.add(uc.MODULE_INFO_HOST_1)
74        if test_name == uc.HOST_UNIT_TEST_NAME_2:
75            test_infos.add(uc.MODULE_INFO_HOST_2)
76    return test_infos
77
78
79#pylint: disable=protected-access
80#pylint: disable=no-self-use
81class CLITranslatorUnittests(unittest.TestCase):
82    """Unit tests for cli_t.py"""
83
84    def setUp(self):
85        """Run before execution of every test"""
86        self.ctr = cli_t.CLITranslator()
87
88        # Create a mock of args.
89        self.args = mock.Mock
90        self.args.tests = []
91        # Test mapping related args
92        self.args.test_mapping = False
93        self.args.include_subdirs = False
94        self.args.enable_file_patterns = False
95        self.args.rebuild_module_info = False
96        # Cache finder related args
97        self.args.clear_cache = False
98        self.ctr.mod_info = mock.Mock
99        self.ctr.mod_info.name_to_module_info = {}
100
101    def tearDown(self):
102        """Run after execution of every test"""
103        reload(uc)
104
105    @mock.patch('builtins.input', return_value='n')
106    @mock.patch.object(module_finder.ModuleFinder, 'find_test_by_module_name')
107    @mock.patch.object(module_finder.ModuleFinder, 'get_fuzzy_searching_results')
108    @mock.patch.object(metrics, 'FindTestFinishEvent')
109    @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
110    # pylint: disable=too-many-locals
111    def test_get_test_infos(self, mock_getfindmethods, _metrics,
112                            mock_getfuzzyresults, mock_findtestbymodule,
113                            mock_input):
114        """Test _get_test_infos method."""
115        ctr = cli_t.CLITranslator()
116        find_method_return_module_info = lambda x, y: uc.MODULE_INFOS
117        # pylint: disable=invalid-name
118        find_method_return_module_class_info = (lambda x, test: uc.MODULE_INFOS
119                                                if test == uc.MODULE_NAME
120                                                else uc.CLASS_INFOS)
121        find_method_return_nothing = lambda x, y: None
122        one_test = [uc.MODULE_NAME]
123        mult_test = [uc.MODULE_NAME, uc.CLASS_NAME]
124
125        # Let's make sure we return what we expect.
126        expected_test_infos = {uc.MODULE_INFO}
127        mock_getfindmethods.return_value = [
128            test_finder_base.Finder(None, find_method_return_module_info, None)]
129        unittest_utils.assert_strict_equal(
130            self, ctr._get_test_infos(one_test), expected_test_infos)
131
132        # Check we receive multiple test infos.
133        expected_test_infos = {uc.MODULE_INFO, uc.CLASS_INFO}
134        mock_getfindmethods.return_value = [
135            test_finder_base.Finder(None, find_method_return_module_class_info,
136                                    None)]
137        unittest_utils.assert_strict_equal(
138            self, ctr._get_test_infos(mult_test), expected_test_infos)
139
140        # Check return null set when we have no tests found or multiple results.
141        mock_getfindmethods.return_value = [
142            test_finder_base.Finder(None, find_method_return_nothing, None)]
143        null_test_info = set()
144        mock_getfuzzyresults.return_value = []
145        self.assertEqual(null_test_info, ctr._get_test_infos(one_test))
146        self.assertEqual(null_test_info, ctr._get_test_infos(mult_test))
147
148        # Check returning test_info when the user says Yes.
149        mock_input.return_value = "Y"
150        mock_getfindmethods.return_value = [
151            test_finder_base.Finder(None, find_method_return_module_info, None)]
152        mock_getfuzzyresults.return_value = one_test
153        mock_findtestbymodule.return_value = uc.MODULE_INFO
154        unittest_utils.assert_strict_equal(
155            self, ctr._get_test_infos([uc.TYPO_MODULE_NAME]), {uc.MODULE_INFO})
156
157        # Check the method works for test mapping.
158        test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
159        test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
160        expected_test_infos = {uc.MODULE_INFO, uc.CLASS_INFO}
161        mock_getfindmethods.return_value = [
162            test_finder_base.Finder(None, find_method_return_module_class_info,
163                                    None)]
164        test_infos = ctr._get_test_infos(
165            mult_test, [test_detail1, test_detail2])
166        unittest_utils.assert_strict_equal(
167            self, test_infos, expected_test_infos)
168        for test_info in test_infos:
169            if test_info == uc.MODULE_INFO:
170                self.assertEqual(
171                    test_detail1.options,
172                    test_info.data[constants.TI_MODULE_ARG])
173            else:
174                self.assertEqual(
175                    test_detail2.options,
176                    test_info.data[constants.TI_MODULE_ARG])
177
178    @mock.patch.object(metrics, 'FindTestFinishEvent')
179    @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
180    def test_get_test_infos_2(self, mock_getfindmethods, _metrics):
181        """Test _get_test_infos method."""
182        ctr = cli_t.CLITranslator()
183        find_method_return_module_info2 = lambda x, y: uc.MODULE_INFOS2
184        find_method_ret_mod_cls_info2 = (
185            lambda x, test: uc.MODULE_INFOS2
186            if test == uc.MODULE_NAME else uc.CLASS_INFOS2)
187        one_test = [uc.MODULE_NAME]
188        mult_test = [uc.MODULE_NAME, uc.CLASS_NAME]
189        # Let's make sure we return what we expect.
190        expected_test_infos = {uc.MODULE_INFO, uc.MODULE_INFO2}
191        mock_getfindmethods.return_value = [
192            test_finder_base.Finder(None, find_method_return_module_info2,
193                                    None)]
194        unittest_utils.assert_strict_equal(
195            self, ctr._get_test_infos(one_test), expected_test_infos)
196        # Check we receive multiple test infos.
197        expected_test_infos = {uc.MODULE_INFO, uc.CLASS_INFO, uc.MODULE_INFO2,
198                               uc.CLASS_INFO2}
199        mock_getfindmethods.return_value = [
200            test_finder_base.Finder(None, find_method_ret_mod_cls_info2,
201                                    None)]
202        unittest_utils.assert_strict_equal(
203            self, ctr._get_test_infos(mult_test), expected_test_infos)
204        # Check the method works for test mapping.
205        test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
206        test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
207        expected_test_infos = {uc.MODULE_INFO, uc.CLASS_INFO, uc.MODULE_INFO2,
208                               uc.CLASS_INFO2}
209        mock_getfindmethods.return_value = [
210            test_finder_base.Finder(None, find_method_ret_mod_cls_info2,
211                                    None)]
212        test_infos = ctr._get_test_infos(
213            mult_test, [test_detail1, test_detail2])
214        unittest_utils.assert_strict_equal(
215            self, test_infos, expected_test_infos)
216        for test_info in test_infos:
217            if test_info in [uc.MODULE_INFO, uc.MODULE_INFO2]:
218                self.assertEqual(
219                    test_detail1.options,
220                    test_info.data[constants.TI_MODULE_ARG])
221            elif test_info in [uc.CLASS_INFO, uc.CLASS_INFO2]:
222                self.assertEqual(
223                    test_detail2.options,
224                    test_info.data[constants.TI_MODULE_ARG])
225
226    @mock.patch.dict('os.environ', {constants.ANDROID_BUILD_TOP:'/'})
227    @mock.patch.object(module_finder.ModuleFinder, 'get_fuzzy_searching_results')
228    @mock.patch.object(metrics, 'FindTestFinishEvent')
229    @mock.patch.object(test_finder_handler, 'get_find_methods_for_test')
230    def test_get_test_infos_with_mod_info(
231            self, mock_getfindmethods, _metrics, mock_getfuzzyresults,):
232        """Test _get_test_infos method."""
233        mod_info = module_info.ModuleInfo(
234            module_file=os.path.join(uc.TEST_DATA_DIR, uc.JSON_FILE))
235        ctr = cli_t.CLITranslator(module_info=mod_info)
236        null_test_info = set()
237        mock_getfindmethods.return_value = []
238        mock_getfuzzyresults.return_value = []
239        unittest_utils.assert_strict_equal(
240            self, ctr._get_test_infos('not_exist_module'), null_test_info)
241
242    @mock.patch.object(cli_t.CLITranslator, '_get_test_infos',
243                       side_effect=gettestinfos_side_effect)
244    def test_translate_class(self, _info):
245        """Test translate method for tests by class name."""
246        # Check that we can find a class.
247        self.args.tests = [uc.CLASS_NAME]
248        self.args.host_unit_test_only = False
249        targets, test_infos = self.ctr.translate(self.args)
250        unittest_utils.assert_strict_equal(
251            self, targets, uc.CLASS_BUILD_TARGETS)
252        unittest_utils.assert_strict_equal(self, test_infos, {uc.CLASS_INFO})
253
254    @mock.patch.object(cli_t.CLITranslator, '_get_test_infos',
255                       side_effect=gettestinfos_side_effect)
256    def test_translate_module(self, _info):
257        """Test translate method for tests by module or class name."""
258        # Check that we get all the build targets we expect.
259        self.args.tests = [uc.MODULE_NAME, uc.CLASS_NAME]
260        self.args.host_unit_test_only = False
261        targets, test_infos = self.ctr.translate(self.args)
262        unittest_utils.assert_strict_equal(
263            self, targets, uc.MODULE_CLASS_COMBINED_BUILD_TARGETS)
264        unittest_utils.assert_strict_equal(self, test_infos, {uc.MODULE_INFO,
265                                                              uc.CLASS_INFO})
266
267    @mock.patch.object(test_finder_utils, 'find_host_unit_tests', return_value=[])
268    @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
269    @mock.patch.object(cli_t.CLITranslator, '_get_test_infos',
270                       side_effect=gettestinfos_side_effect)
271    def test_translate_test_mapping(self, _info, mock_testmapping,
272        _find_unit_tests):
273        """Test translate method for tests in test mapping."""
274        # Check that test mappings feeds into get_test_info properly.
275        test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
276        test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
277        mock_testmapping.return_value = ([test_detail1, test_detail2], None)
278        self.args.tests = []
279        self.args.host = False
280        self.args.host_unit_test_only = False
281        targets, test_infos = self.ctr.translate(self.args)
282        unittest_utils.assert_strict_equal(
283            self, targets, uc.MODULE_CLASS_COMBINED_BUILD_TARGETS)
284        unittest_utils.assert_strict_equal(self, test_infos, {uc.MODULE_INFO,
285                                                              uc.CLASS_INFO})
286
287    @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
288    @mock.patch.object(cli_t.CLITranslator, '_get_test_infos',
289                       side_effect=gettestinfos_side_effect)
290    def test_translate_test_mapping_all(self, _info, mock_testmapping):
291        """Test translate method for tests in test mapping."""
292        # Check that test mappings feeds into get_test_info properly.
293        test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
294        test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
295        mock_testmapping.return_value = ([test_detail1, test_detail2], None)
296        self.args.tests = ['src_path:all']
297        self.args.test_mapping = True
298        self.args.host = False
299        targets, test_infos = self.ctr.translate(self.args)
300        unittest_utils.assert_strict_equal(
301            self, targets, uc.MODULE_CLASS_COMBINED_BUILD_TARGETS)
302        unittest_utils.assert_strict_equal(self, test_infos, {uc.MODULE_INFO,
303                                                              uc.CLASS_INFO})
304
305    def test_find_tests_by_test_mapping_presubmit(self):
306        """Test _find_tests_by_test_mapping method to locate presubmit tests."""
307        os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
308        with mock.patch.dict('os.environ', os_environ_mock, clear=True):
309            tests, all_tests = self.ctr._find_tests_by_test_mapping(
310                path=TEST_MAPPING_DIR, file_name='test_mapping_sample',
311                checked_files=set())
312        expected = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
313        expected_all_tests = {'presubmit': expected,
314                              'postsubmit': set(
315                                  [TEST_3, TEST_6, TEST_8, TEST_10]),
316                              'other_group': set([TEST_4])}
317        self.assertEqual(expected, tests)
318        self.assertEqual(expected_all_tests, all_tests)
319
320    def test_find_tests_by_test_mapping_postsubmit(self):
321        """Test _find_tests_by_test_mapping method to locate postsubmit tests.
322        """
323        os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
324        with mock.patch.dict('os.environ', os_environ_mock, clear=True):
325            tests, all_tests = self.ctr._find_tests_by_test_mapping(
326                path=TEST_MAPPING_DIR,
327                test_groups=[constants.TEST_GROUP_POSTSUBMIT],
328                file_name='test_mapping_sample', checked_files=set())
329        expected_presubmit = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
330        expected = set([TEST_3, TEST_6, TEST_8, TEST_10])
331        expected_all_tests = {'presubmit': expected_presubmit,
332                              'postsubmit': set(
333                                  [TEST_3, TEST_6, TEST_8, TEST_10]),
334                              'other_group': set([TEST_4])}
335        self.assertEqual(expected, tests)
336        self.assertEqual(expected_all_tests, all_tests)
337
338    def test_find_tests_by_test_mapping_all_group(self):
339        """Test _find_tests_by_test_mapping method to locate postsubmit tests.
340        """
341        os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
342        with mock.patch.dict('os.environ', os_environ_mock, clear=True):
343            tests, all_tests = self.ctr._find_tests_by_test_mapping(
344                path=TEST_MAPPING_DIR, test_groups=[constants.TEST_GROUP_ALL],
345                file_name='test_mapping_sample', checked_files=set())
346        expected_presubmit = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
347        expected = set([
348            TEST_1, TEST_2, TEST_3, TEST_4, TEST_5, TEST_6, TEST_7, TEST_8,
349            TEST_9, TEST_10])
350        expected_all_tests = {'presubmit': expected_presubmit,
351                              'postsubmit': set(
352                                  [TEST_3, TEST_6, TEST_8, TEST_10]),
353                              'other_group': set([TEST_4])}
354        self.assertEqual(expected, tests)
355        self.assertEqual(expected_all_tests, all_tests)
356
357    def test_find_tests_by_test_mapping_include_subdir(self):
358        """Test _find_tests_by_test_mapping method to include sub directory."""
359        os_environ_mock = {constants.ANDROID_BUILD_TOP: uc.TEST_DATA_DIR}
360        with mock.patch.dict('os.environ', os_environ_mock, clear=True):
361            tests, all_tests = self.ctr._find_tests_by_test_mapping(
362                path=TEST_MAPPING_TOP_DIR, file_name='test_mapping_sample',
363                include_subdirs=True, checked_files=set())
364        expected = set([TEST_1, TEST_2, TEST_5, TEST_7, TEST_9])
365        expected_all_tests = {'presubmit': expected,
366                              'postsubmit': set([
367                                  TEST_3, TEST_6, TEST_8, TEST_10]),
368                              'other_group': set([TEST_4])}
369        self.assertEqual(expected, tests)
370        self.assertEqual(expected_all_tests, all_tests)
371
372    @mock.patch('builtins.input', return_value='')
373    def test_confirm_running(self, mock_input):
374        """Test _confirm_running method."""
375        self.assertTrue(self.ctr._confirm_running([TEST_1]))
376        mock_input.return_value = 'N'
377        self.assertFalse(self.ctr._confirm_running([TEST_2]))
378
379    def test_print_fuzzy_searching_results(self):
380        """Test _print_fuzzy_searching_results"""
381        modules = [uc.MODULE_NAME, uc.MODULE2_NAME]
382        capture_output = StringIO()
383        sys.stdout = capture_output
384        self.ctr._print_fuzzy_searching_results(modules)
385        sys.stdout = sys.__stdout__
386        output = 'Did you mean the following modules?\n{0}\n{1}\n'.format(
387            uc.MODULE_NAME, uc.MODULE2_NAME)
388        self.assertEqual(capture_output.getvalue(), output)
389
390    def test_filter_comments(self):
391        """Test filter_comments method"""
392        file_with_comments = os.path.join(TEST_MAPPING_TOP_DIR,
393                                          'folder6',
394                                          'test_mapping_sample_with_comments')
395        file_with_comments_golden = os.path.join(TEST_MAPPING_TOP_DIR,
396                                                 'folder6',
397                                                 'test_mapping_sample_golden')
398        test_mapping_dict = json.loads(
399            self.ctr.filter_comments(file_with_comments))
400        test_mapping_dict_gloden = None
401        with open(file_with_comments_golden) as json_file:
402            test_mapping_dict_gloden = json.load(json_file)
403
404        self.assertEqual(test_mapping_dict, test_mapping_dict_gloden)
405
406    @mock.patch.dict('os.environ', {constants.ANDROID_BUILD_TOP:'/'})
407    @mock.patch.object(module_info.ModuleInfo, 'get_testable_modules')
408    def test_extract_testable_modules_by_wildcard(self, mock_mods):
409        """Test _extract_testable_modules_by_wildcard method."""
410        mod_info = module_info.ModuleInfo(
411            module_file=os.path.join(uc.TEST_DATA_DIR, uc.JSON_FILE))
412        ctr = cli_t.CLITranslator(module_info=mod_info)
413        mock_mods.return_value = ['test1', 'test2', 'test3', 'test11',
414                                  'Test22', 'Test100', 'aTest101']
415        # test '*'
416        expr1 = ['test*']
417        result1 = ['test1', 'test2', 'test3', 'test11']
418        self.assertEqual(ctr._extract_testable_modules_by_wildcard(expr1),
419                         result1)
420        # test '?'
421        expr2 = ['test?']
422        result2 = ['test1', 'test2', 'test3']
423        self.assertEqual(ctr._extract_testable_modules_by_wildcard(expr2),
424                         result2)
425        # test '*' and '?'
426        expr3 = ['*Test???']
427        result3 = ['Test100', 'aTest101']
428        self.assertEqual(ctr._extract_testable_modules_by_wildcard(expr3),
429                         result3)
430
431    @mock.patch.object(test_finder_utils, 'find_host_unit_tests',
432                       return_value=[uc.HOST_UNIT_TEST_NAME_1,
433                                     uc.HOST_UNIT_TEST_NAME_2])
434    @mock.patch.object(cli_t.CLITranslator, '_find_tests_by_test_mapping')
435    @mock.patch.object(cli_t.CLITranslator, '_get_test_infos',
436                       side_effect=gettestinfos_side_effect)
437    def test_translate_test_mapping_host_unit_test(self, _info, mock_testmapping,
438        _find_unit_tests):
439        """Test translate method for tests belong to host unit tests."""
440        # Check that test mappings feeds into get_test_info properly.
441        test_detail1 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST)
442        test_detail2 = test_mapping.TestDetail(uc.TEST_MAPPING_TEST_WITH_OPTION)
443        mock_testmapping.return_value = ([test_detail1, test_detail2], None)
444        self.args.tests = []
445        self.args.host = False
446        self.args.host_unit_test_only = False
447        _, test_infos = self.ctr.translate(self.args)
448        unittest_utils.assert_strict_equal(self,
449                                           test_infos,
450                                           {uc.MODULE_INFO,
451                                            uc.CLASS_INFO,
452                                            uc.MODULE_INFO_HOST_1,
453                                            uc.MODULE_INFO_HOST_2})
454
455if __name__ == '__main__':
456    unittest.main()
457