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