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
18"""Unittests for ide_util."""
19
20import os
21import shutil
22import subprocess
23import tempfile
24import unittest
25
26from unittest import mock
27from xml.etree import ElementTree
28
29from aidegen import aidegen_main
30from aidegen import unittest_constants
31from aidegen.lib import android_dev_os
32from aidegen.lib import common_util
33from aidegen.lib import config
34from aidegen.lib import errors
35from aidegen.lib import ide_common_util
36from aidegen.lib import ide_util
37from aidegen.lib import project_config
38from aidegen.lib import project_file_gen
39from aidegen.sdk import jdk_table
40
41
42# pylint: disable=too-many-public-methods
43# pylint: disable=protected-access
44# pylint: disable-msg=too-many-arguments
45# pylint: disable-msg=unused-argument
46class IdeUtilUnittests(unittest.TestCase):
47    """Unit tests for ide_util.py."""
48
49    _TEST_PRJ_PATH1 = ''
50    _TEST_PRJ_PATH2 = ''
51    _TEST_PRJ_PATH3 = ''
52    _TEST_PRJ_PATH4 = ''
53    _MODULE_XML_SAMPLE = ''
54    _TEST_DIR = None
55    _TEST_XML_CONTENT = """<application>
56  <component name="FileTypeManager" version="17">
57    <extensionMap>
58      <mapping ext="pi" type="Python"/>
59    </extensionMap>
60  </component>
61</application>"""
62    _TEST_XML_CONTENT_2 = """<application>
63  <component name="FileTypeManager" version="17">
64    <extensionMap>
65      <mapping ext="pi" type="Python"/>
66      <mapping pattern="test" type="a"/>
67      <mapping pattern="TEST_MAPPING" type="a"/>
68    </extensionMap>
69  </component>
70</application>"""
71
72    def setUp(self):
73        """Prepare the testdata related path."""
74        IdeUtilUnittests._TEST_PRJ_PATH1 = os.path.join(
75            unittest_constants.TEST_DATA_PATH, 'android_facet.iml')
76        IdeUtilUnittests._TEST_PRJ_PATH2 = os.path.join(
77            unittest_constants.TEST_DATA_PATH, 'project/test.java')
78        IdeUtilUnittests._TEST_PRJ_PATH3 = unittest_constants.TEST_DATA_PATH
79        IdeUtilUnittests._TEST_PRJ_PATH4 = os.path.join(
80            unittest_constants.TEST_DATA_PATH, '.idea')
81        IdeUtilUnittests._MODULE_XML_SAMPLE = os.path.join(
82            unittest_constants.TEST_DATA_PATH, 'modules.xml')
83        IdeUtilUnittests._TEST_DIR = tempfile.mkdtemp()
84
85    def tearDown(self):
86        """Clear the testdata related path."""
87        IdeUtilUnittests._TEST_PRJ_PATH1 = ''
88        IdeUtilUnittests._TEST_PRJ_PATH2 = ''
89        IdeUtilUnittests._TEST_PRJ_PATH3 = ''
90        IdeUtilUnittests._TEST_PRJ_PATH4 = ''
91        shutil.rmtree(IdeUtilUnittests._TEST_DIR)
92
93    @unittest.skip('Skip to use real command to launch IDEA.')
94    def test_run_intellij_sh_in_linux(self):
95        """Follow the target behavior, with sh to show UI, else raise err."""
96        sh_path = ide_util.IdeLinuxIntelliJ()._get_script_from_system()
97        if sh_path:
98            ide_util_obj = ide_util.IdeUtil()
99            ide_util_obj.config_ide(IdeUtilUnittests._TEST_PRJ_PATH1)
100            ide_util_obj.launch_ide()
101        else:
102            self.assertRaises(subprocess.CalledProcessError)
103
104    @mock.patch.object(ide_util, '_get_linux_ide')
105    @mock.patch.object(ide_util, '_get_mac_ide')
106    def test_get_ide(self, mock_mac, mock_linux):
107        """Test if _get_ide calls the correct respective functions."""
108        ide_util._get_ide(None, 'j', False, is_mac=True)
109        self.assertTrue(mock_mac.called)
110        ide_util._get_ide(None, 'j', False, is_mac=False)
111        self.assertTrue(mock_linux.called)
112
113    @mock.patch.object(ide_util.IdeBase, '_get_user_preference')
114    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
115    @mock.patch.object(ide_util.IdeEclipse, '_get_script_from_system')
116    @mock.patch.object(ide_util.IdeIntelliJ, '_get_preferred_version')
117    def test_get_mac_and_linux_ide(self, mock_preference, mock_path, mock_cfg,
118                                   mock_version):
119        """Test if _get_mac_ide and _get_linux_ide return correct IDE class."""
120        mock_preference.return_value = None
121        mock_path.return_value = 'path'
122        mock_cfg.return_value = None
123        mock_version.return_value = 'default'
124        self.assertIsInstance(ide_util._get_mac_ide(), ide_util.IdeMacIntelliJ)
125        self.assertIsInstance(ide_util._get_mac_ide(None, 's'),
126                              ide_util.IdeMacStudio)
127        self.assertIsInstance(ide_util._get_mac_ide(None, 'e'),
128                              ide_util.IdeMacEclipse)
129
130        self.assertIsInstance(ide_util._get_mac_ide(None, 'c'),
131                              ide_util.IdeMacCLion)
132        self.assertIsInstance(ide_util._get_linux_ide(),
133                              ide_util.IdeLinuxIntelliJ)
134        self.assertIsInstance(
135            ide_util._get_linux_ide(None, 's'), ide_util.IdeLinuxStudio)
136        self.assertIsInstance(
137            ide_util._get_linux_ide(None, 'e'), ide_util.IdeLinuxEclipse)
138        self.assertIsInstance(
139            ide_util._get_linux_ide(None, 'c'), ide_util.IdeLinuxCLion)
140
141    @mock.patch.object(ide_util.IdeIntelliJ, '_set_installed_path')
142    @mock.patch.object(ide_common_util, 'get_script_from_input_path')
143    @mock.patch.object(ide_util.IdeIntelliJ, '_get_script_from_system')
144    def test_init_ideintellij(self, mock_sys, mock_input, mock_set):
145        """Test IdeIntelliJ's __init__ method."""
146        ide_util.IdeLinuxIntelliJ()
147        self.assertTrue(mock_sys.called)
148        ide_util.IdeMacIntelliJ()
149        self.assertTrue(mock_sys.called)
150        ide_util.IdeLinuxIntelliJ('some_path')
151        self.assertTrue(mock_input.called)
152        self.assertTrue(mock_set.called)
153        ide_util.IdeMacIntelliJ('some_path')
154        self.assertTrue(mock_input.called)
155        self.assertTrue(mock_set.called)
156
157    @mock.patch.object(ide_util.IdeIntelliJ, '_get_preferred_version')
158    @mock.patch.object(ide_util.IdeIntelliJ, '_get_config_root_paths')
159    @mock.patch.object(ide_util.IdeBase, 'apply_optional_config')
160    def test_config_ide(self, mock_config, mock_paths, mock_preference):
161        """Test IDEA, IdeUtil.config_ide won't call base none implement api."""
162        # Mock SDkConfig flow to not to generate real jdk config file.
163        mock_preference.return_value = None
164        module_path = os.path.join(self._TEST_DIR, 'test')
165        idea_path = os.path.join(module_path, '.idea')
166        os.makedirs(idea_path)
167        shutil.copy(IdeUtilUnittests._MODULE_XML_SAMPLE, idea_path)
168        util_obj = ide_util.IdeUtil()
169        util_obj.config_ide(module_path)
170        self.assertFalse(mock_config.called)
171        self.assertFalse(mock_paths.called)
172
173    @mock.patch.object(ide_util.IdeIntelliJ, '_setup_ide')
174    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
175    @mock.patch.object(os.path, 'isfile')
176    @mock.patch.object(os.path, 'realpath')
177    @mock.patch.object(ide_common_util, 'get_script_from_input_path')
178    @mock.patch.object(ide_common_util, 'get_script_from_internal_path')
179    def test_get_linux_config_1(self, mock_path, mock_path2, mock_path3,
180                                mock_is_file, mock_cfg, mock_setup_ide):
181        """Test to get unique config path for linux IDEA case."""
182        if (not android_dev_os.AndroidDevOS.MAC ==
183                android_dev_os.AndroidDevOS.get_os_type()):
184            mock_path.return_value = ['/opt/intellij-ce-2018.3/bin/idea.sh']
185            mock_path2.return_value = ['/opt/intellij-ce-2018.3/bin/idea.sh']
186            mock_path3.return_value = '/opt/intellij-ce-2018.3/bin/idea.sh'
187            mock_is_file.return_value = True
188            mock_cfg.return_value = None
189            mock_setup_ide.return_value = None
190            ide_obj = ide_util.IdeLinuxIntelliJ('default_path')
191            self.assertEqual(1, len(ide_obj._get_config_root_paths()))
192        else:
193            self.assertTrue((android_dev_os.AndroidDevOS.MAC ==
194                             android_dev_os.AndroidDevOS.get_os_type()))
195
196    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
197    @mock.patch('glob.glob')
198    @mock.patch.object(ide_common_util, 'get_script_from_input_path')
199    @mock.patch.object(ide_common_util, 'get_script_from_internal_path')
200    def test_get_linux_config_2(self, mock_path, mock_path_2, mock_filter,
201                                mock_cfg):
202        """Test to get unique config path for linux IDEA case."""
203        if (not android_dev_os.AndroidDevOS.MAC ==
204                android_dev_os.AndroidDevOS.get_os_type()):
205            mock_path.return_value = ['/opt/intelliJ-ce-2018.3/bin/idea.sh']
206            mock_path_2.return_value = ['/opt/intelliJ-ce-2018.3/bin/idea.sh']
207            mock_cfg.return_value = None
208            ide_obj = ide_util.IdeLinuxIntelliJ()
209            mock_filter.called = False
210            ide_obj._get_config_root_paths()
211            self.assertFalse(mock_filter.called)
212        else:
213            self.assertTrue((android_dev_os.AndroidDevOS.MAC ==
214                             android_dev_os.AndroidDevOS.get_os_type()))
215
216    def test_get_mac_config_root_paths(self):
217        """Return None if there's no install path."""
218        if (android_dev_os.AndroidDevOS.MAC ==
219                android_dev_os.AndroidDevOS.get_os_type()):
220            mac_ide = ide_util.IdeMacIntelliJ()
221            mac_ide._installed_path = None
222            self.assertIsNone(mac_ide._get_config_root_paths())
223        else:
224            self.assertFalse((android_dev_os.AndroidDevOS.MAC ==
225                              android_dev_os.AndroidDevOS.get_os_type()))
226
227    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
228    @mock.patch('glob.glob')
229    @mock.patch.object(ide_common_util, 'get_script_from_input_path')
230    @mock.patch.object(ide_common_util, 'get_script_from_internal_path')
231    def test_get_linux_config_root(self, mock_path_1, mock_path_2, mock_filter,
232                                   mock_cfg):
233        """Test to go filter logic for self download case."""
234        mock_path_1.return_value = ['/usr/tester/IDEA/IC2018.3.3/bin']
235        mock_path_2.return_value = ['/usr/tester/IDEA/IC2018.3.3/bin']
236        mock_cfg.return_value = None
237        ide_obj = ide_util.IdeLinuxIntelliJ()
238        mock_filter.reset()
239        ide_obj._get_config_root_paths()
240        self.assertTrue(mock_filter.called)
241
242    @mock.patch.object(ide_util.IdeBase, '_add_test_mapping_file_type')
243    @mock.patch.object(config.IdeaProperties, 'set_max_file_size')
244    @mock.patch.object(project_file_gen, 'gen_enable_debugger_module')
245    @mock.patch.object(jdk_table.JDKTableXML, 'config_jdk_table_xml')
246    @mock.patch.object(ide_util.IdeBase, '_get_config_root_paths')
247    def test_apply_optional_config(self, mock_path, mock_config_xml,
248                                   mock_gen_debugger, mock_set_size,
249                                   mock_test_mapping):
250        """Test basic logic of apply_optional_config."""
251        ide = ide_util.IdeBase()
252        ide._installed_path = None
253        ide.apply_optional_config()
254        self.assertFalse(mock_path.called)
255        ide._installed_path = 'default_path'
256        ide.apply_optional_config()
257        self.assertTrue(mock_path.called)
258        mock_path.return_value = []
259        ide.apply_optional_config()
260        self.assertFalse(mock_config_xml.called)
261        mock_path.return_value = ['a']
262        mock_config_xml.return_value = False
263        ide.apply_optional_config()
264        self.assertFalse(mock_gen_debugger.called)
265        self.assertTrue(mock_set_size.called)
266        mock_config_xml.return_value = True
267        ide.apply_optional_config()
268        self.assertEqual(ide.config_folders, ['a'])
269        self.assertTrue(mock_gen_debugger.called)
270        self.assertTrue(mock_set_size.called)
271
272    @mock.patch('os.path.isfile')
273    @mock.patch.object(ElementTree.ElementTree, 'write')
274    @mock.patch.object(common_util, 'to_pretty_xml')
275    @mock.patch.object(common_util, 'file_generate')
276    @mock.patch.object(ElementTree, 'parse')
277    @mock.patch.object(ElementTree.ElementTree, 'getroot')
278    def test_add_test_mapping_file_type(self, mock_root, mock_parse,
279                                        mock_file_gen, mock_pretty_xml,
280                                        mock_write, mock_isfile):
281        """Test basic logic of _add_test_mapping_file_type."""
282        mock_isfile.return_value = False
283        self.assertFalse(mock_file_gen.called)
284
285        mock_isfile.return_value = True
286        mock_parse.return_value = None
287        self.assertFalse(mock_file_gen.called)
288
289        mock_parse.return_value = ElementTree.ElementTree()
290        mock_root.return_value = ElementTree.fromstring(
291            self._TEST_XML_CONTENT_2)
292        ide_obj = ide_util.IdeBase()
293        ide_obj._add_test_mapping_file_type('')
294        self.assertFalse(mock_file_gen.called)
295        mock_root.return_value = ElementTree.fromstring(self._TEST_XML_CONTENT)
296        ide_obj._add_test_mapping_file_type('')
297        self.assertTrue(mock_pretty_xml.called)
298        self.assertTrue(mock_file_gen.called)
299
300    @mock.patch('os.path.realpath')
301    @mock.patch('os.path.isfile')
302    def test_merge_symbolic_version(self, mock_isfile, mock_realpath):
303        """Test _merge_symbolic_version and _get_real_path."""
304        symbolic_path = ide_util.IdeLinuxIntelliJ._SYMBOLIC_VERSIONS[0]
305        original_path = 'intellij-ce-2019.1/bin/idea.sh'
306        mock_isfile.return_value = True
307        mock_realpath.return_value = original_path
308        ide_obj = ide_util.IdeLinuxIntelliJ('default_path')
309        merged_version = ide_obj._merge_symbolic_version(
310            [symbolic_path, original_path])
311        self.assertEqual(
312            merged_version[0], symbolic_path + ' -> ' + original_path)
313        self.assertEqual(
314            ide_obj._get_real_path(merged_version[0]), symbolic_path)
315
316    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
317    @mock.patch('os.path.isfile')
318    def test_get_application_path(self, mock_isfile, mock_cfg):
319        """Test _get_application_path."""
320        mock_cfg.return_value = None
321        ide_obj = ide_util.IdeLinuxIntelliJ('default_path')
322        mock_isfile.return_value = True
323        test_path = None
324        self.assertEqual(None, ide_obj._get_application_path(test_path))
325
326        test_path = 'a/b/c/d-e/f-gh/foo'
327        self.assertEqual(None, ide_obj._get_application_path(test_path))
328
329        test_path = 'a/b/c/d/intellij/foo'
330        self.assertEqual(None, ide_obj._get_application_path(test_path))
331
332        test_path = 'a/b/c/d/intellij-efg/foo'
333        self.assertEqual(None, ide_obj._get_application_path(test_path))
334
335        test_path = 'a/b/c/d/intellij-efg-hi/foo'
336        self.assertEqual(None, ide_obj._get_application_path(test_path))
337
338        test_path = 'a/b/c/d/intellij-ce-303/foo'
339        self.assertEqual('.IdeaIC303', ide_obj._get_application_path(test_path))
340
341        test_path = 'a/b/c/d/intellij-ue-303/foo'
342        self.assertEqual('.IntelliJIdea303', ide_obj._get_application_path(
343            test_path))
344
345    def test_get_ide_util_instance_with_no_launch(self):
346        """Test _get_ide_util_instance with no launch IDE."""
347        args = aidegen_main._parse_args(['tradefed', '-n'])
348        project_config.ProjectConfig(args)
349        self.assertEqual(ide_util.get_ide_util_instance(args), None)
350
351    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
352    @mock.patch.object(ide_util.IdeIntelliJ, '_get_preferred_version')
353    def test_get_ide_util_instance_with_success(self, mock_preference,
354                                                mock_cfg_write):
355        """Test _get_ide_util_instance with success."""
356        args = aidegen_main._parse_args(['tradefed'])
357        project_config.ProjectConfig(args)
358        mock_cfg_write.return_value = None
359        mock_preference.return_value = '1'
360        self.assertIsInstance(
361            ide_util.get_ide_util_instance(), ide_util.IdeUtil)
362
363    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
364    @mock.patch.object(ide_util.IdeIntelliJ, '_get_preferred_version')
365    @mock.patch.object(ide_util.IdeUtil, 'is_ide_installed')
366    def test_get_ide_util_instance_with_failure(
367            self, mock_installed, mock_preference, mock_cfg_write):
368        """Test _get_ide_util_instance with failure."""
369        args = aidegen_main._parse_args(['tradefed'])
370        project_config.ProjectConfig(args)
371        mock_installed.return_value = False
372        mock_cfg_write.return_value = None
373        mock_preference.return_value = '1'
374        with self.assertRaises(errors.IDENotExistError):
375            ide_util.get_ide_util_instance()
376
377    @mock.patch.object(ide_common_util, 'get_scripts_from_dir_path')
378    @mock.patch.object(ide_common_util, '_run_ide_sh')
379    @mock.patch('logging.info')
380    def test_ide_base(self, mock_log, mock_run_ide, mock_run_path):
381        """Test ide_base class."""
382        # Test raise NotImplementedError.
383        ide_base = ide_util.IdeBase()
384        with self.assertRaises(NotImplementedError):
385            ide_base._get_config_root_paths()
386
387        # Test ide_name.
388        ide_base._ide_name = 'a'
389        self.assertEqual(ide_base.ide_name, 'a')
390
391        # Test _get_ide_cmd.
392        ide_base._installed_path = '/a/b'
393        ide_base.project_abspath = '/x/y'
394        expected_result = 'nohup /a/b /x/y 2>/dev/null >&2 &'
395        self.assertEqual(ide_base._get_ide_cmd(), expected_result)
396
397        # Test launch_ide.
398        mock_run_ide.return_value = True
399        ide_base.launch_ide()
400        self.assertTrue(mock_log.called)
401
402        # Test _get_ide_from_environment_paths.
403        mock_run_path.return_value = '/a/b/idea.sh'
404        ide_base._bin_file_name = 'idea.sh'
405        expected_path = '/a/b/idea.sh'
406        ide_path = ide_base._get_ide_from_environment_paths()
407        self.assertEqual(ide_path, expected_path)
408
409    def test_ide_intellij(self):
410        """Test IdeIntelliJ class."""
411        # Test raise NotImplementedError.
412        ide_intellij = ide_util.IdeIntelliJ()
413        with self.assertRaises(NotImplementedError):
414            ide_intellij._get_config_root_paths()
415
416    @mock.patch.object(config.AidegenConfig, 'set_preferred_version')
417    @mock.patch.object(config.AidegenConfig, 'preferred_version')
418    @mock.patch.object(ide_common_util, 'ask_preference')
419    @mock.patch.object(config.AidegenConfig, 'deprecated_intellij_version')
420    @mock.patch.object(ide_util.IdeIntelliJ, '_get_all_versions')
421    def test_intellij_get_preferred_version(self,
422                                            mock_all_versions,
423                                            mock_deprecated_version,
424                                            mock_ask_preference,
425                                            mock_preference,
426                                            mock_write_cfg):
427        """Test _get_preferred_version for IdeIntelliJ class."""
428        mock_write_cfg.return_value = None
429        ide_intellij = ide_util.IdeIntelliJ()
430
431        # No IntelliJ version is installed.
432        mock_all_versions.return_value = ['/a/b', '/a/c']
433        mock_deprecated_version.return_value = True
434        version = ide_intellij._get_preferred_version()
435        self.assertEqual(version, None)
436
437        # Load default preferred version.
438        mock_all_versions.return_value = ['/a/b', '/a/c']
439        mock_deprecated_version.return_value = False
440        ide_intellij._config_reset = False
441        expected_result = '/a/b'
442        mock_preference.return_value = '/a/b'
443        version = ide_intellij._get_preferred_version()
444        self.assertEqual(version, expected_result)
445
446        # Asking user the preferred version.
447        mock_preference.reset()
448        mock_all_versions.return_value = ['/a/b', '/a/c']
449        mock_deprecated_version.return_value = False
450        ide_intellij._config_reset = True
451        mock_ask_preference.return_value = '/a/b'
452        version = ide_intellij._get_preferred_version()
453        expected_result = '/a/b'
454        self.assertEqual(version, expected_result)
455
456        mock_all_versions.return_value = ['/a/b', '/a/c']
457        mock_ask_preference.return_value = None
458        expected_result = '/a/b'
459        mock_preference.return_value = '/a/b'
460        version = ide_intellij._get_preferred_version()
461        self.assertEqual(version, expected_result)
462
463        # The all_versions list has only one version.
464        mock_all_versions.return_value = ['/a/b']
465        mock_deprecated_version.return_value = False
466        version = ide_intellij._get_preferred_version()
467        self.assertEqual(version, '/a/b')
468
469    @mock.patch.object(ide_util.IdeBase, '_add_test_mapping_file_type')
470    @mock.patch.object(ide_common_util, 'ask_preference')
471    @mock.patch.object(config.IdeaProperties, 'set_max_file_size')
472    @mock.patch.object(project_file_gen, 'gen_enable_debugger_module')
473    @mock.patch.object(ide_util.IdeStudio, '_get_config_root_paths')
474    def test_android_studio_class(self, mock_get_config_paths,
475                                  mock_gen_debugger, mock_set_size, mock_ask,
476                                  mock_add_file_type):
477        """Test IdeStudio."""
478        mock_get_config_paths.return_value = ['path1', 'path2']
479        mock_gen_debugger.return_value = True
480        mock_set_size.return_value = True
481        mock_ask.return_value = None
482        obj = ide_util.IdeStudio()
483        obj._installed_path = False
484        # Test the native IDE case.
485        obj.project_abspath = os.path.realpath(__file__)
486        obj.apply_optional_config()
487        self.assertEqual(obj.config_folders, [])
488
489        # Test the java IDE case.
490        obj.project_abspath = IdeUtilUnittests._TEST_DIR
491        obj.apply_optional_config()
492        self.assertEqual(obj.config_folders, [])
493
494        mock_get_config_paths.return_value = []
495        self.assertIsNone(obj.apply_optional_config())
496
497    @mock.patch.object(ide_common_util, 'ask_preference')
498    def test_studio_get_config_root_paths(self, mock_ask):
499        """Test the method _get_config_root_paths of IdeStudio."""
500        mock_ask.return_value = None
501        obj = ide_util.IdeStudio()
502        with self.assertRaises(NotImplementedError):
503            obj._get_config_root_paths()
504
505    @mock.patch.object(ide_util.IdeBase, 'apply_optional_config')
506    @mock.patch.object(os.path, 'isdir')
507    @mock.patch.object(os.path, 'isfile')
508    def test_studio_optional_config_apply(self, mock_isfile, mock_isdir,
509                                          mock_base_implement):
510        """Test IdeStudio.apply_optional_config."""
511        obj = ide_util.IdeStudio()
512        obj.project_abspath = None
513        # Test no project path case.
514        obj.apply_optional_config()
515        self.assertFalse(mock_isfile.called)
516        self.assertFalse(mock_isdir.called)
517        self.assertFalse(mock_base_implement.called)
518        # Test the native IDE case.
519        obj.project_abspath = '/'
520        mock_isfile.reset_mock()
521        mock_isdir.reset_mock()
522        mock_base_implement.reset_mock()
523        mock_isfile.return_value = True
524        obj.apply_optional_config()
525        self.assertTrue(mock_isfile.called)
526        self.assertFalse(mock_isdir.called)
527        self.assertFalse(mock_base_implement.called)
528        # Test the java IDE case.
529        mock_isfile.reset_mock()
530        mock_isdir.reset_mock()
531        mock_base_implement.reset_mock()
532        mock_isfile.return_value = False
533        mock_isdir.return_value = True
534        obj.apply_optional_config()
535        self.assertTrue(mock_base_implement.called)
536        # Test neither case.
537        mock_isfile.reset_mock()
538        mock_isdir.reset_mock()
539        mock_base_implement.reset_mock()
540        mock_isfile.return_value = False
541        mock_isdir.return_value = False
542        obj.apply_optional_config()
543        self.assertFalse(mock_base_implement.called)
544
545    @mock.patch.object(ide_common_util, 'ask_preference')
546    @mock.patch('os.getenv')
547    def test_linux_android_studio_class(self, mock_get_home, mock_ask):
548        """Test the method _get_config_root_paths of IdeLinuxStudio."""
549        mock_get_home.return_value = self._TEST_DIR
550        studio_config_dir1 = os.path.join(self._TEST_DIR, '.AndroidStudio3.0')
551        studio_config_dir2 = os.path.join(self._TEST_DIR, '.AndroidStudio3.1')
552        os.makedirs(studio_config_dir1)
553        os.makedirs(studio_config_dir2)
554        expected_result = [studio_config_dir1, studio_config_dir2]
555        mock_ask.return_value = None
556        obj = ide_util.IdeLinuxStudio()
557        config_paths = obj._get_config_root_paths()
558        self.assertEqual(sorted(config_paths), sorted(expected_result))
559
560    @mock.patch('os.getenv')
561    def test_mac_android_studio_class(self, mock_get_home):
562        """Test the method _get_config_root_paths of IdeMacStudio."""
563        mock_get_home.return_value = self._TEST_DIR
564        studio_config_dir1 = os.path.join(self._TEST_DIR, 'Library',
565                                          'Preferences', 'AndroidStudio3.0')
566        studio_config_dir2 = os.path.join(self._TEST_DIR, 'Library',
567                                          'Preferences', 'AndroidStudio3.1')
568        os.makedirs(studio_config_dir1)
569        os.makedirs(studio_config_dir2)
570        expected_result = [studio_config_dir1, studio_config_dir2]
571
572        obj = ide_util.IdeMacStudio()
573        config_paths = obj._get_config_root_paths()
574        self.assertEqual(sorted(config_paths), sorted(expected_result))
575
576    @mock.patch('os.access')
577    @mock.patch('glob.glob')
578    def test_eclipse_get_script_from_system(self, mock_glob, mock_file_access):
579        """Test IdeEclipse _get_script_from_system method."""
580        eclipse = ide_util.IdeEclipse()
581
582        # Test no binary path in _get_script_from_system.
583        eclipse._bin_paths = []
584        expacted_result = None
585        test_result = eclipse._get_script_from_system()
586        self.assertEqual(test_result, expacted_result)
587
588        # Test get the matched binary from _get_script_from_system.
589        mock_glob.return_value = ['/a/b/eclipse']
590        mock_file_access.return_value = True
591        eclipse._bin_paths = ['/a/b/eclipse']
592        expacted_result = '/a/b/eclipse'
593        test_result = eclipse._get_script_from_system()
594        self.assertEqual(test_result, expacted_result)
595
596        # Test no matched binary from _get_script_from_system.
597        mock_glob.return_value = []
598        eclipse._bin_paths = ['/a/b/eclipse']
599        expacted_result = None
600        test_result = eclipse._get_script_from_system()
601        self.assertEqual(test_result, expacted_result)
602
603        # Test the matched binary cannot be executed.
604        mock_glob.return_value = ['/a/b/eclipse']
605        mock_file_access.return_value = False
606        eclipse._bin_paths = ['/a/b/eclipse']
607        expacted_result = None
608        test_result = eclipse._get_script_from_system()
609        self.assertEqual(test_result, expacted_result)
610
611    @mock.patch('builtins.input')
612    @mock.patch('os.path.exists')
613    def test_eclipse_get_ide_cmd(self, mock_exists, mock_input):
614        """Test IdeEclipse _get_ide_cmd method."""
615        # Test open the IDE with the default Eclipse workspace.
616        eclipse = ide_util.IdeEclipse()
617        eclipse.cmd = ['eclipse']
618        mock_exists.return_value = True
619        expacted_result = ('eclipse -data '
620                           '~/Documents/AIDEGen_Eclipse_workspace '
621                           '2>/dev/null >&2 &')
622        test_result = eclipse._get_ide_cmd()
623        self.assertEqual(test_result, expacted_result)
624
625        # Test running command without the default workspace.
626        eclipse.cmd = ['eclipse']
627        mock_exists.return_value = False
628        mock_input.return_value = 'n'
629        expacted_result = 'eclipse 2>/dev/null >&2 &'
630        test_result = eclipse._get_ide_cmd()
631        self.assertEqual(test_result, expacted_result)
632
633    @mock.patch.object(ide_util.IdeUtil, 'is_ide_installed')
634    @mock.patch.object(project_config.ProjectConfig, 'get_instance')
635    def test_get_ide_util_instance(self, mock_config, mock_ide_installed):
636        """Test get_ide_util_instance."""
637        # Test is_launch_ide conditions.
638        mock_instance = mock_config.return_value
639        mock_instance.is_launch_ide = False
640        ide_util.get_ide_util_instance()
641        self.assertFalse(mock_ide_installed.called)
642        mock_instance.is_launch_ide = True
643        ide_util.get_ide_util_instance()
644        self.assertTrue(mock_ide_installed.called)
645
646        # Test ide is not installed.
647        mock_ide_installed.return_value = False
648        with self.assertRaises(errors.IDENotExistError):
649            ide_util.get_ide_util_instance()
650
651    @mock.patch.object(ide_util.IdeLinuxVSCode, '_init_installed_path')
652    @mock.patch.object(ide_util.IdeLinuxVSCode, '_get_possible_bin_paths')
653    def test_ide_linux_vscode(self, mock_get_pos, mock_init_inst):
654        """Test IdeLinuxVSCode class."""
655        ide_util.IdeLinuxVSCode()
656        self.assertTrue(mock_get_pos.called)
657        self.assertTrue(mock_init_inst.called)
658
659    @mock.patch.object(ide_util.IdeMacVSCode, '_init_installed_path')
660    @mock.patch.object(ide_util.IdeMacVSCode, '_get_possible_bin_paths')
661    def test_ide_mac_vscode(self, mock_get_pos, mock_init_inst):
662        """Test IdeMacVSCode class."""
663        ide_util.IdeMacVSCode()
664        self.assertTrue(mock_get_pos.called)
665        self.assertTrue(mock_init_inst.called)
666
667    def test_get_all_versions(self):
668        """Test _get_all_versions."""
669        ide = ide_util.IdeIntelliJ()
670        test_result = ide._get_all_versions('a', 'b')
671        self.assertEqual(test_result, ['a', 'b'])
672
673    @mock.patch('logging.warning')
674    def test_get_ide_version(self, mock_warn):
675        """Test _get_ide_version with conditions."""
676        self.assertEqual(
677            None, ide_util.IdeIntelliJ._get_ide_version('intellij-efg-hi'))
678        self.assertTrue(mock_warn.called)
679        mock_warn.reset_mock()
680        self.assertEqual(
681            '2020.1',
682            ide_util.IdeIntelliJ._get_ide_version('intellij-ue-2020.1'))
683        self.assertFalse(mock_warn.called)
684        mock_warn.reset_mock()
685        self.assertEqual(
686            '303', ide_util.IdeIntelliJ._get_ide_version('intellij-ue-303'))
687        self.assertFalse(mock_warn.called)
688
689    @mock.patch('os.path.join')
690    def test_get_config_dir(self, mock_join):
691        """Test _get_config_dir with conditions."""
692        config_folder_name = 'IntelliJ2020.1'
693        ide_version = '2020.1'
694        ide_util.IdeIntelliJ._get_config_dir(ide_version, config_folder_name)
695        self.assertTrue(
696            mock_join.called_with(
697                os.getenv('HOME'), '.config', 'JetBrains', config_folder_name))
698        mock_join.reset_mock()
699        config_folder_name = 'IntelliJ2019.3'
700        ide_version = '2019.3'
701        self.assertTrue(
702            mock_join.called_with(
703                os.getenv('HOME'), config_folder_name, 'config'))
704        mock_join.reset_mock()
705        self.assertEqual(None, ide_util.IdeIntelliJ._get_config_dir(
706            'Not-a-float', config_folder_name))
707        self.assertFalse(mock_join.called)
708
709    def test_get_config_folder_name(self):
710        """Test _get_config_folder_name with conditions."""
711        config_folder_name = 'intellij-ce-2019.3'
712        pre_folder = '.IdeaIC'
713        ide_version = '2019.3'
714        expected = ''.join([pre_folder, ide_version])
715        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
716            config_folder_name))
717        config_folder_name = 'intellij-ue-2019.3'
718        pre_folder = '.IntelliJIdea'
719        expected = ''.join([pre_folder, ide_version])
720        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
721            config_folder_name))
722        config_folder_name = 'intellij-ce-2020.1'
723        pre_folder = 'IdeaIC'
724        ide_version = '2020.1'
725        expected = ''.join([pre_folder, ide_version])
726        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
727            config_folder_name))
728        config_folder_name = 'intellij-ue-2020.1'
729        pre_folder = 'IntelliJIdea'
730        expected = ''.join([pre_folder, ide_version])
731        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
732            config_folder_name))
733        config_folder_name = 'intellij-ue-2020.1.4'
734        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
735            config_folder_name))
736        config_folder_name = 'intellij-unknown'
737        expected = None
738        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
739            config_folder_name))
740        config_folder_name = 'intellij-ue-NotAFloat'
741        self.assertEqual(expected, ide_util.IdeIntelliJ._get_config_folder_name(
742            config_folder_name))
743
744
745if __name__ == '__main__':
746    unittest.main()
747