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