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