• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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