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 common_util.""" 18 19import logging 20import os 21import unittest 22from unittest import mock 23from xml.etree import ElementTree 24 25from aidegen import constant 26from aidegen import unittest_constants 27from aidegen.lib import common_util 28from aidegen.lib import errors 29 30from atest import module_info 31 32 33# pylint: disable=too-many-arguments 34# pylint: disable=protected-access 35class AidegenCommonUtilUnittests(unittest.TestCase): 36 """Unit tests for common_util.py""" 37 38 _TEST_XML_CONTENT = """<application><component name="ProjectJdkTable"> 39 40 <jdk version="2"> <name value="JDK_OTHER" /> 41 <type value="JavaSDK" /> </jdk> </component> 42</application> 43""" 44 _SAMPLE_XML_CONTENT = """<application> 45 <component name="ProjectJdkTable"> 46 <jdk version="2"> 47 <name value="JDK_OTHER"/> 48 <type value="JavaSDK"/> 49 </jdk> 50 </component> 51</application>""" 52 53 @mock.patch('os.getcwd') 54 @mock.patch('os.path.isabs') 55 @mock.patch.object(common_util, 'get_android_root_dir') 56 def test_get_related_paths(self, mock_get_root, mock_is_abspath, mock_cwd): 57 """Test get_related_paths with different conditions.""" 58 mod_info = mock.MagicMock() 59 mod_info.is_module.return_value = True 60 mod_info.get_paths.return_value = {} 61 mock_is_abspath.return_value = False 62 self.assertEqual((None, None), 63 common_util.get_related_paths( 64 mod_info, unittest_constants.TEST_MODULE)) 65 mock_get_root.return_value = unittest_constants.TEST_PATH 66 mod_info.get_paths.return_value = [unittest_constants.TEST_MODULE] 67 expected = (unittest_constants.TEST_MODULE, os.path.join( 68 unittest_constants.TEST_PATH, unittest_constants.TEST_MODULE)) 69 self.assertEqual( 70 expected, common_util.get_related_paths( 71 mod_info, unittest_constants.TEST_MODULE)) 72 mod_info.is_module.return_value = False 73 mod_info.get_module_names.return_value = True 74 self.assertEqual(expected, common_util.get_related_paths( 75 mod_info, unittest_constants.TEST_MODULE)) 76 self.assertEqual(('', unittest_constants.TEST_PATH), 77 common_util.get_related_paths( 78 mod_info, constant.WHOLE_ANDROID_TREE_TARGET)) 79 80 mod_info.is_module.return_value = False 81 mod_info.get_module_names.return_value = False 82 mock_is_abspath.return_value = True 83 mock_get_root.return_value = '/a' 84 self.assertEqual(('b/c', '/a/b/c'), 85 common_util.get_related_paths(mod_info, '/a/b/c')) 86 87 mock_is_abspath.return_value = False 88 mock_cwd.return_value = '/a' 89 mock_get_root.return_value = '/a' 90 self.assertEqual(('b/c', '/a/b/c'), 91 common_util.get_related_paths(mod_info, 'b/c')) 92 93 94 @mock.patch('os.getcwd') 95 @mock.patch.object(common_util, 'is_android_root') 96 @mock.patch.object(common_util, 'get_android_root_dir') 97 def test_get_related_paths_2( 98 self, mock_get_root, mock_is_root, mock_getcwd): 99 """Test get_related_paths with different conditions.""" 100 101 mock_get_root.return_value = '/a' 102 mod_info = mock.MagicMock() 103 104 # Test get_module_names returns False, user inputs a relative path of 105 # current directory. 106 mod_info.is_mod.return_value = False 107 rel_path = 'b/c/d' 108 abs_path = '/a/b/c/d' 109 mod_info.get_paths.return_value = [rel_path] 110 mod_info.get_module_names.return_value = False 111 mock_getcwd.return_value = '/a/b/c' 112 input_target = 'd' 113 # expected tuple: (rel_path, abs_path) 114 expected = (rel_path, abs_path) 115 result = common_util.get_related_paths(mod_info, input_target) 116 self.assertEqual(expected, result) 117 118 # Test user doesn't input target and current working directory is the 119 # android root folder. 120 mock_getcwd.return_value = '/a' 121 mock_is_root.return_value = True 122 expected = ('', '/a') 123 result = common_util.get_related_paths(mod_info, target=None) 124 self.assertEqual(expected, result) 125 126 # Test user doesn't input target and current working directory is not 127 # android root folder. 128 mock_getcwd.return_value = '/a/b' 129 mock_is_root.return_value = False 130 expected = ('b', '/a/b') 131 result = common_util.get_related_paths(mod_info, target=None) 132 self.assertEqual(expected, result) 133 result = common_util.get_related_paths(mod_info, target='.') 134 self.assertEqual(expected, result) 135 136 @mock.patch.object(common_util, 'is_android_root') 137 @mock.patch.object(common_util, 'get_related_paths') 138 def test_is_target_android_root(self, mock_get_rel, mock_get_root): 139 """Test is_target_android_root with different conditions.""" 140 mod_info = mock.MagicMock() 141 mock_get_rel.return_value = None, unittest_constants.TEST_PATH 142 mock_get_root.return_value = True 143 self.assertTrue( 144 common_util.is_target_android_root( 145 mod_info, [unittest_constants.TEST_MODULE])) 146 mock_get_rel.return_value = None, '' 147 mock_get_root.return_value = False 148 self.assertFalse( 149 common_util.is_target_android_root( 150 mod_info, [unittest_constants.TEST_MODULE])) 151 152 @mock.patch.object(common_util, 'get_android_root_dir') 153 @mock.patch.object(common_util, 'has_build_target') 154 @mock.patch('os.path.isdir') 155 @mock.patch.object(common_util, 'get_related_paths') 156 def test_check_module(self, mock_get, mock_isdir, mock_has_target, 157 mock_get_root): 158 """Test if _check_module raises errors with different conditions.""" 159 mod_info = mock.MagicMock() 160 mock_get.return_value = None, None 161 with self.assertRaises(errors.FakeModuleError) as ctx: 162 common_util.check_module(mod_info, unittest_constants.TEST_MODULE) 163 expected = common_util.FAKE_MODULE_ERROR.format( 164 unittest_constants.TEST_MODULE) 165 self.assertEqual(expected, str(ctx.exception)) 166 mock_get_root.return_value = unittest_constants.TEST_PATH 167 mock_get.return_value = None, unittest_constants.TEST_MODULE 168 with self.assertRaises(errors.ProjectOutsideAndroidRootError) as ctx: 169 common_util.check_module(mod_info, unittest_constants.TEST_MODULE) 170 expected = common_util.OUTSIDE_ROOT_ERROR.format( 171 unittest_constants.TEST_MODULE) 172 self.assertEqual(expected, str(ctx.exception)) 173 mock_get.return_value = None, unittest_constants.TEST_PATH 174 mock_isdir.return_value = False 175 with self.assertRaises(errors.ProjectPathNotExistError) as ctx: 176 common_util.check_module(mod_info, unittest_constants.TEST_MODULE) 177 expected = common_util.PATH_NOT_EXISTS_ERROR.format( 178 unittest_constants.TEST_MODULE) 179 self.assertEqual(expected, str(ctx.exception)) 180 mock_isdir.return_value = True 181 mock_has_target.return_value = False 182 mock_get.return_value = None, os.path.join(unittest_constants.TEST_PATH, 183 'test.jar') 184 with self.assertRaises(errors.NoModuleDefinedInModuleInfoError) as ctx: 185 common_util.check_module(mod_info, unittest_constants.TEST_MODULE) 186 expected = common_util.NO_MODULE_DEFINED_ERROR.format( 187 unittest_constants.TEST_MODULE) 188 self.assertEqual(expected, str(ctx.exception)) 189 self.assertFalse(common_util.check_module(mod_info, '', False)) 190 self.assertFalse(common_util.check_module(mod_info, 'nothing', False)) 191 192 @mock.patch.object(common_util, 'check_module') 193 def test_check_modules(self, mock_check): 194 """Test _check_modules with different module lists.""" 195 mod_info = mock.MagicMock() 196 common_util._check_modules(mod_info, []) 197 self.assertEqual(mock_check.call_count, 0) 198 common_util._check_modules(mod_info, ['module1', 'module2']) 199 self.assertEqual(mock_check.call_count, 2) 200 target = 'nothing' 201 mock_check.return_value = False 202 self.assertFalse(common_util._check_modules(mod_info, [target], False)) 203 204 @mock.patch.object(common_util, 'get_android_root_dir') 205 def test_get_abs_path(self, mock_get_root): 206 """Test get_abs_path handling.""" 207 mock_get_root.return_value = unittest_constants.TEST_DATA_PATH 208 self.assertEqual(unittest_constants.TEST_DATA_PATH, 209 common_util.get_abs_path('')) 210 test_path = os.path.join(unittest_constants.TEST_DATA_PATH, 'test.jar') 211 self.assertEqual(test_path, common_util.get_abs_path(test_path)) 212 self.assertEqual(test_path, common_util.get_abs_path('test.jar')) 213 214 def test_is_target(self): 215 """Test is_target handling.""" 216 self.assertTrue( 217 common_util.is_target('packages/apps/tests/test.a', ['.so', '.a'])) 218 self.assertTrue( 219 common_util.is_target('packages/apps/tests/test.so', ['.so', '.a'])) 220 self.assertFalse( 221 common_util.is_target( 222 'packages/apps/tests/test.jar', ['.so', '.a'])) 223 224 @mock.patch.object(logging, 'basicConfig') 225 def test_configure_logging(self, mock_log_config): 226 """Test configure_logging with different arguments.""" 227 common_util.configure_logging(True) 228 log_format = common_util._LOG_FORMAT 229 datefmt = common_util._DATE_FORMAT 230 level = common_util.logging.DEBUG 231 self.assertTrue( 232 mock_log_config.called_with( 233 level=level, format=log_format, datefmt=datefmt)) 234 common_util.configure_logging(False) 235 level = common_util.logging.INFO 236 self.assertTrue( 237 mock_log_config.called_with( 238 level=level, format=log_format, datefmt=datefmt)) 239 240 @mock.patch.object(common_util, '_check_modules') 241 @mock.patch.object(module_info, 'ModuleInfo') 242 def test_get_atest_module_info(self, mock_modinfo, mock_check_modules): 243 """Test get_atest_module_info handling.""" 244 common_util.get_atest_module_info() 245 self.assertEqual(mock_modinfo.call_count, 1) 246 mock_modinfo.reset_mock() 247 mock_check_modules.return_value = False 248 common_util.get_atest_module_info(['nothing']) 249 self.assertEqual(mock_modinfo.call_count, 2) 250 251 @mock.patch('builtins.open', create=True) 252 def test_read_file_content(self, mock_open): 253 """Test read_file_content handling.""" 254 expacted_data1 = 'Data1' 255 file_a = 'fileA' 256 mock_open.side_effect = [ 257 mock.mock_open(read_data=expacted_data1).return_value 258 ] 259 self.assertEqual(expacted_data1, common_util.read_file_content(file_a)) 260 mock_open.assert_called_once_with(file_a, encoding='utf8') 261 262 @mock.patch('os.getenv') 263 @mock.patch.object(common_util, 'get_android_root_dir') 264 def test_get_android_out_dir(self, mock_get_android_root_dir, mock_getenv): 265 """Test get_android_out_dir handling.""" 266 root = 'my/path-to-root/master' 267 default_root = 'out' 268 android_out_root = 'eng_out' 269 mock_get_android_root_dir.return_value = root 270 mock_getenv.side_effect = ['', ''] 271 self.assertEqual(default_root, common_util.get_android_out_dir()) 272 mock_getenv.side_effect = [android_out_root, ''] 273 self.assertEqual(android_out_root, common_util.get_android_out_dir()) 274 mock_getenv.side_effect = ['', default_root] 275 self.assertEqual(os.path.join(default_root, os.path.basename(root)), 276 common_util.get_android_out_dir()) 277 mock_getenv.side_effect = [android_out_root, default_root] 278 self.assertEqual(android_out_root, common_util.get_android_out_dir()) 279 280 def test_has_build_target(self): 281 """Test has_build_target handling.""" 282 mod_info = mock.MagicMock() 283 mod_info.path_to_module_info = {'a/b/c': {}} 284 rel_path = 'a/b' 285 self.assertTrue(common_util.has_build_target(mod_info, rel_path)) 286 rel_path = 'd/e' 287 self.assertFalse(common_util.has_build_target(mod_info, rel_path)) 288 289 @mock.patch('os.path.expanduser') 290 def test_remove_user_home_path(self, mock_expanduser): 291 """ Test replace the user home path to a constant string.""" 292 mock_expanduser.return_value = '/usr/home/a' 293 test_string = '/usr/home/a/test/dir' 294 expect_string = '$USER_HOME$/test/dir' 295 result_path = common_util.remove_user_home_path(test_string) 296 self.assertEqual(result_path, expect_string) 297 298 def test_io_error_handle(self): 299 """Test io_error_handle handling.""" 300 err = "It's an IO error." 301 def some_io_error_func(): 302 raise IOError(err) 303 with self.assertRaises(IOError) as context: 304 decorator = common_util.io_error_handle(some_io_error_func) 305 decorator() 306 self.assertTrue(err in context.exception) 307 308 @mock.patch.object(common_util, '_show_env_setup_msg_and_exit') 309 @mock.patch('os.environ.get') 310 def test_get_android_root_dir(self, mock_get_env, mock_show_msg): 311 """Test get_android_root_dir handling.""" 312 root = 'root' 313 mock_get_env.return_value = root 314 expected = common_util.get_android_root_dir() 315 self.assertEqual(root, expected) 316 root = '' 317 mock_get_env.return_value = root 318 common_util.get_android_root_dir() 319 self.assertTrue(mock_show_msg.called) 320 321 # pylint: disable=no-value-for-parameter 322 def test_check_args(self): 323 """Test check_args handling.""" 324 with self.assertRaises(TypeError): 325 decorator = common_util.check_args(name=str, text=str) 326 decorator(parse_rule(None, 'text')) 327 with self.assertRaises(TypeError): 328 decorator = common_util.check_args(name=str, text=str) 329 decorator(parse_rule('Paul', '')) 330 with self.assertRaises(TypeError): 331 decorator = common_util.check_args(name=str, text=str) 332 decorator(parse_rule(1, 2)) 333 334 @mock.patch.object(common_util, 'get_blueprint_json_path') 335 @mock.patch.object(common_util, 'get_android_out_dir') 336 @mock.patch.object(common_util, 'get_android_root_dir') 337 def test_get_blueprint_json_files_relative_dict( 338 self, mock_get_root, mock_get_out, mock_get_path): 339 """Test get_blueprint_json_files_relative_dict function,""" 340 mock_get_root.return_value = 'a/b' 341 mock_get_out.return_value = 'out' 342 mock_get_path.return_value = 'out/soong/bp_java_file' 343 path_compdb = os.path.join('a/b', 'out', 'soong', 344 constant.RELATIVE_COMPDB_PATH, 345 constant.COMPDB_JSONFILE_NAME) 346 data = { 347 constant.GEN_JAVA_DEPS: 'a/b/out/soong/bp_java_file', 348 constant.GEN_CC_DEPS: 'a/b/out/soong/bp_java_file', 349 constant.GEN_COMPDB: path_compdb, 350 constant.GEN_RUST: 'a/b/out/soong/bp_java_file' 351 } 352 self.assertEqual( 353 data, common_util.get_blueprint_json_files_relative_dict()) 354 355 @mock.patch('os.environ.get') 356 def test_get_lunch_target(self, mock_get_env): 357 """Test get_lunch_target.""" 358 mock_get_env.return_value = "test" 359 self.assertEqual( 360 common_util.get_lunch_target(), '{"lunch target": "test-test"}') 361 362 def test_to_pretty_xml(self): 363 """Test to_pretty_xml.""" 364 root = ElementTree.fromstring(self._TEST_XML_CONTENT) 365 pretty_xml = common_util.to_pretty_xml(root) 366 self.assertEqual(pretty_xml, self._SAMPLE_XML_CONTENT) 367 368 def test_to_to_boolean(self): 369 """Test to_boolean function with conditions.""" 370 self.assertTrue(common_util.to_boolean('True')) 371 self.assertTrue(common_util.to_boolean('true')) 372 self.assertTrue(common_util.to_boolean('T')) 373 self.assertTrue(common_util.to_boolean('t')) 374 self.assertTrue(common_util.to_boolean('1')) 375 self.assertFalse(common_util.to_boolean('False')) 376 self.assertFalse(common_util.to_boolean('false')) 377 self.assertFalse(common_util.to_boolean('F')) 378 self.assertFalse(common_util.to_boolean('f')) 379 self.assertFalse(common_util.to_boolean('0')) 380 self.assertFalse(common_util.to_boolean('')) 381 382 @mock.patch.object(os.path, 'exists') 383 @mock.patch.object(common_util, 'get_android_root_dir') 384 def test_find_git_root(self, mock_get_root, mock_exist): 385 """Test find_git_root.""" 386 mock_get_root.return_value = '/a/b' 387 mock_exist.return_value = True 388 self.assertEqual(common_util.find_git_root('c/d'), '/a/b/c/d') 389 mock_exist.return_value = False 390 self.assertEqual(common_util.find_git_root('c/d'), None) 391 392 def test_determine_language_ide(self): 393 """Test determine_language_ide function.""" 394 ide = 'u' 395 lang = 'u' 396 self.assertEqual((constant.JAVA, constant.IDE_INTELLIJ), 397 common_util.determine_language_ide(lang, ide)) 398 self.assertEqual((constant.JAVA, constant.IDE_INTELLIJ), 399 common_util.determine_language_ide( 400 lang, ide, ['some_module'])) 401 self.assertEqual((constant.C_CPP, constant.IDE_CLION), 402 common_util.determine_language_ide( 403 lang, ide, None, ['some_module'])) 404 self.assertEqual((constant.RUST, constant.IDE_VSCODE), 405 common_util.determine_language_ide( 406 lang, ide, None, None, ['some_module'])) 407 lang = 'j' 408 self.assertEqual((constant.JAVA, constant.IDE_INTELLIJ), 409 common_util.determine_language_ide(lang, ide)) 410 ide = 'c' 411 self.assertEqual((constant.C_CPP, constant.IDE_CLION), 412 common_util.determine_language_ide(lang, ide)) 413 ide = 'j' 414 lang = 'u' 415 self.assertEqual((constant.JAVA, constant.IDE_INTELLIJ), 416 common_util.determine_language_ide(lang, ide)) 417 lang = 'j' 418 self.assertEqual((constant.JAVA, constant.IDE_INTELLIJ), 419 common_util.determine_language_ide(lang, ide)) 420 ide = 'c' 421 self.assertEqual((constant.C_CPP, constant.IDE_CLION), 422 common_util.determine_language_ide(lang, ide)) 423 lang = 'c' 424 ide = 'u' 425 self.assertEqual((constant.C_CPP, constant.IDE_CLION), 426 common_util.determine_language_ide(lang, ide)) 427 ide = 'j' 428 self.assertEqual((constant.JAVA, constant.IDE_INTELLIJ), 429 common_util.determine_language_ide(lang, ide)) 430 431 @mock.patch('zipfile.ZipFile.extractall') 432 @mock.patch('zipfile.ZipFile') 433 def test_unzip_file(self, mock_zipfile, mock_extract): 434 """Test unzip_file function.""" 435 src = 'a/b/c.zip' 436 dest = 'a/b/d' 437 common_util.unzip_file(src, dest) 438 mock_zipfile.assert_called_with(src, 'r') 439 self.assertFalse(mock_extract.called) 440 441 @mock.patch('os.walk') 442 def test_check_java_or_kotlin_file_exists(self, mock_walk): 443 """Test check_java_or_kotlin_file_exists with conditions.""" 444 root_dir = 'a/path/to/dir' 445 folder = 'path/to/dir' 446 target = 'test.java' 447 abs_path = os.path.join(root_dir, folder) 448 mock_walk.return_value = [(root_dir, [folder], [target])] 449 self.assertTrue(common_util.check_java_or_kotlin_file_exists(abs_path)) 450 target = 'test.kt' 451 abs_path = os.path.join(root_dir, folder) 452 mock_walk.return_value = [(root_dir, [folder], [target])] 453 self.assertTrue(common_util.check_java_or_kotlin_file_exists(abs_path)) 454 target = 'test.cpp' 455 mock_walk.return_value = [(root_dir, [folder], [target])] 456 self.assertFalse(common_util.check_java_or_kotlin_file_exists(abs_path)) 457 458 # Only VS Code IDE supports Rust projects right now. 459 lang = 'r' 460 ide = 'u' 461 self.assertEqual((constant.RUST, constant.IDE_VSCODE), 462 common_util.determine_language_ide(lang, ide)) 463 lang = 'r' 464 ide = 'v' 465 self.assertEqual((constant.RUST, constant.IDE_VSCODE), 466 common_util.determine_language_ide(lang, ide)) 467 lang = 'r' 468 ide = 'j' 469 self.assertEqual((constant.RUST, constant.IDE_VSCODE), 470 common_util.determine_language_ide(lang, ide)) 471 lang = 'r' 472 ide = 'c' 473 self.assertEqual((constant.RUST, constant.IDE_VSCODE), 474 common_util.determine_language_ide(lang, ide)) 475 476 477# pylint: disable=unused-argument 478def parse_rule(self, name, text): 479 """A test function for test_check_args.""" 480 481 482if __name__ == '__main__': 483 unittest.main() 484