1#!/usr/bin/env python3
2#
3# Copyright 2020, 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 JDKTableXML class."""
18
19import os
20import shutil
21import tempfile
22import unittest
23
24from unittest import mock
25from xml.etree import ElementTree
26
27from aidegen.lib import aidegen_metrics
28from aidegen.lib import common_util
29from aidegen.lib import xml_util
30from aidegen.sdk import android_sdk
31from aidegen.sdk import jdk_table
32
33
34# pylint: disable=protected-access
35class JDKTableXMLUnittests(unittest.TestCase):
36    """Unit tests for JDKTableXML class."""
37
38    _JDK_TABLE_XML = 'jdk.table.xml'
39    _CONFIG_FILE = '/path/to/jdk.table.xml'
40    _JDK_CONTENT = '<jdk />'
41    _JDK_PATH = '/path/to/JDK'
42    _DEFULAT_ANDROID_SDK_PATH = '/path/to/Android/SDK'
43
44    def setUp(self):
45        """Prepare the JDKTableXML class."""
46        JDKTableXMLUnittests._TEST_DIR = tempfile.mkdtemp()
47        self.jdk_table_xml = jdk_table.JDKTableXML(
48            self._CONFIG_FILE, self._JDK_CONTENT, self._JDK_PATH,
49            self._DEFULAT_ANDROID_SDK_PATH)
50
51    def tearDown(self):
52        """Clear the JDKTableXML class."""
53        self.jdk_table_xml = None
54        shutil.rmtree(JDKTableXMLUnittests._TEST_DIR)
55
56    @mock.patch.object(common_util, 'file_generate')
57    @mock.patch('os.path.exists')
58    @mock.patch.object(ElementTree, 'parse')
59    def test_init(self, mock_parse, mock_exists, mock_gen_file):
60        """Test initialize the attributes."""
61        self.assertEqual(self.jdk_table_xml._platform_version, None)
62        self.assertEqual(self.jdk_table_xml._android_sdk_version, None)
63        self.assertEqual(self.jdk_table_xml._modify_config, False)
64        mock_exists.return_value = True
65        mock_parse.return_value = None
66        jdk_table.JDKTableXML(None, None, None, None)
67        self.assertTrue(mock_parse.called)
68        mock_exists.return_value = False
69        jdk_table.JDKTableXML(None, None, None, None)
70        self.assertTrue(mock_parse.called)
71        self.assertTrue(mock_gen_file.called)
72
73    def test_android_sdk_version(self):
74        """Test android_sdk_version."""
75        self.assertEqual(self.jdk_table_xml.android_sdk_version, None)
76
77    def test_check_structure(self):
78        """Test _check_structure."""
79        tmp_file = os.path.join(self._TEST_DIR, self._JDK_TABLE_XML)
80        xml_str = ('<application>\n</application>')
81        with open(tmp_file, 'w') as tmp_jdk_xml:
82            tmp_jdk_xml.write(xml_str)
83        self.jdk_table_xml._xml = ElementTree.parse(tmp_file)
84        self.assertFalse(self.jdk_table_xml._check_structure())
85        xml_str = ('<application>\n'
86                   '  <component>\n'
87                   '  </component>\n'
88                   '</application>')
89        with open(tmp_file, 'w') as tmp_jdk_xml:
90            tmp_jdk_xml.write(xml_str)
91        self.jdk_table_xml._xml = ElementTree.parse(tmp_file)
92        self.assertFalse(self.jdk_table_xml._check_structure())
93        xml_str = ('<application>\n'
94                   '  <component name="ProjectJdkTable">\n'
95                   '  </component>\n'
96                   '</application>')
97        with open(tmp_file, 'w') as tmp_jdk_xml:
98            tmp_jdk_xml.write(xml_str)
99        self.jdk_table_xml._xml = ElementTree.parse(tmp_file)
100        self.assertTrue(self.jdk_table_xml._check_structure())
101
102    @mock.patch.object(jdk_table.JDKTableXML, '_check_jdk18_in_xml')
103    def test_generate_jdk_config_string(self, mock_jdk_exists):
104        """Test _generate_jdk_config_string."""
105        mock_jdk_exists.return_value = True
106        self.jdk_table_xml._generate_jdk_config_string()
107        self.assertFalse(self.jdk_table_xml._modify_config)
108        mock_jdk_exists.return_value = False
109        expected_result = (b'<application>\n'
110                           b'  <component name="ProjectJdkTable">\n'
111                           b'    <jdk />\n'
112                           b'  </component>\n'
113                           b'</application>')
114        self.jdk_table_xml._generate_jdk_config_string()
115        test_result = ElementTree.tostring(self.jdk_table_xml._xml.getroot())
116        self.assertTrue(self.jdk_table_xml._modify_config)
117        self.assertEqual(test_result, expected_result)
118        xml_str = ('<application>\n'
119                   '  <component name="ProjectJdkTable">\n'
120                   '    <jdk>\n'
121                   '      <name value="test" />\n'
122                   '      <type value="JavaSDK" />\n'
123                   '    </jdk>\n'
124                   '  </component>\n'
125                   '</application>')
126        expected_result = (b'<application>\n'
127                           b'  <component name="ProjectJdkTable">\n'
128                           b'    <jdk>\n'
129                           b'      <name value="test" />\n'
130                           b'      <type value="JavaSDK" />\n'
131                           b'    </jdk>\n'
132                           b'    <jdk />\n'
133                           b'  </component>\n'
134                           b'</application>')
135        tmp_file = os.path.join(self._TEST_DIR, self._JDK_TABLE_XML)
136        with open(tmp_file, 'w') as tmp_jdk_xml:
137            tmp_jdk_xml.write(xml_str)
138        self.jdk_table_xml._xml = ElementTree.parse(tmp_file)
139        self.jdk_table_xml._generate_jdk_config_string()
140        test_result = ElementTree.tostring(self.jdk_table_xml._xml.getroot())
141        self.assertEqual(test_result, expected_result)
142
143    @mock.patch.object(jdk_table.JDKTableXML, '_override_xml')
144    @mock.patch.object(ElementTree.ElementTree, 'write')
145    @mock.patch.object(jdk_table.JDKTableXML, '_generate_jdk_config_string')
146    @mock.patch.object(jdk_table.JDKTableXML, '_generate_sdk_config_string')
147    @mock.patch.object(jdk_table.JDKTableXML, '_check_structure')
148    def test_config_jdk_table_xml(self, mock_check_structure, mock_gen_jdk,
149                                  mock_gen_sdk, mock_xml_write, mock_override):
150        """Test config_jdk_table_xml."""
151        mock_check_structure.return_value = True
152        self.jdk_table_xml.config_jdk_table_xml()
153        self.assertTrue(mock_gen_jdk.called)
154        self.assertTrue(mock_gen_sdk.called)
155        self.jdk_table_xml._modify_config = False
156        self.jdk_table_xml.config_jdk_table_xml()
157        self.assertFalse(mock_xml_write.called)
158        self.jdk_table_xml._modify_config = True
159        self.jdk_table_xml._android_sdk_version = 'test'
160        self.jdk_table_xml.config_jdk_table_xml()
161        self.assertTrue(mock_xml_write.called)
162        mock_check_structure.return_value = False
163        mock_override.return_value = False
164        self.assertFalse(self.jdk_table_xml.config_jdk_table_xml())
165        mock_check_structure.return_value = False
166        mock_override.return_value = True
167        self.assertTrue(mock_gen_jdk.called)
168
169    def test_check_jdk18_in_xml(self):
170        """Test _check_jdk18_in_xml."""
171        xml_str = ('<test><jdk><name value="JDK18" /><type value="JavaSDK" />'
172                   '</jdk></test>')
173        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
174        self.assertTrue(self.jdk_table_xml._check_jdk18_in_xml())
175        xml_str = ('<test><jdk><name value="test" /><type value="JavaSDK" />'
176                   '</jdk></test>')
177        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
178        self.assertFalse(self.jdk_table_xml._check_jdk18_in_xml())
179        xml_str = ('<test><jdk><name value="test" /></jdk></test>')
180        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
181        self.assertFalse(self.jdk_table_xml._check_jdk18_in_xml())
182
183    @mock.patch.object(android_sdk.AndroidSDK, 'is_android_sdk_path')
184    def test_check_android_sdk_in_xml(self, mock_is_android_sdk):
185        """Test _check_android_sdk_in_xml."""
186        self.jdk_table_xml._sdk._platform_mapping = {
187            'android-29': {
188                'api_level': 29,
189                'code_name': '29',
190            },
191        }
192        mock_is_android_sdk.return_value = True
193        xml_str = ('<test><jdk><name value="JDK18" /><type value="JavaSDK" />'
194                   '</jdk></test>')
195        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
196        self.assertFalse(self.jdk_table_xml._check_android_sdk_in_xml())
197        xml_str = ('<test><jdk><name value="Android SDK 29 platform" />'
198                   '<type value="Android SDK" />'
199                   '<additional jdk="JDK18" sdk="android-29" />'
200                   '</jdk></test>')
201        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
202        self.assertFalse(self.jdk_table_xml._check_android_sdk_in_xml())
203        xml_str = ('<test><jdk><name value="Android SDK 28 platform" />'
204                   '<type value="Android SDK" />'
205                   '<homePath value="/path/to/Android/SDK" />'
206                   '<additional jdk="JDK18" sdk="android-28" />'
207                   '</jdk></test>')
208        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
209        self.assertFalse(self.jdk_table_xml._check_android_sdk_in_xml())
210        xml_str = ('<test><jdk><name value="Android SDK 29 platform" />'
211                   '<type value="Android SDK" />'
212                   '<homePath value="/path/to/Android/SDK" />'
213                   '<additional jdk="JDK18" sdk="android-29" />'
214                   '</jdk></test>')
215        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
216        self.assertTrue(self.jdk_table_xml._check_android_sdk_in_xml())
217        mock_is_android_sdk.return_value = False
218        self.jdk_table_xml._xml = ElementTree.fromstring(xml_str)
219        self.assertFalse(self.jdk_table_xml._check_android_sdk_in_xml())
220
221    @mock.patch.object(aidegen_metrics, 'send_exception_metrics')
222    @mock.patch.object(android_sdk.AndroidSDK, 'path_analysis')
223    @mock.patch.object(common_util, 'read_file_content')
224    @mock.patch.object(jdk_table.JDKTableXML, '_check_android_sdk_in_xml')
225    def test_generate_sdk_config_string(self, mock_sdk_in_xml, mock_read_file,
226                                        mock_path_analysis, mock_metrics):
227        """Test _generate_sdk_config_string."""
228        mock_sdk_in_xml.return_value = True
229        self.jdk_table_xml._generate_sdk_config_string()
230        self.assertFalse(self.jdk_table_xml._modify_config)
231        mock_sdk_in_xml.return_value = False
232        mock_path_analysis.return_value = False
233        self.jdk_table_xml._generate_sdk_config_string()
234        self.assertTrue(mock_metrics.called)
235        mock_path_analysis.return_value = True
236        mock_read_file.return_value = ''
237        self.jdk_table_xml._generate_sdk_config_string()
238        self.assertTrue(self.jdk_table_xml._modify_config)
239
240    @mock.patch.object(aidegen_metrics, 'send_exception_metrics')
241    @mock.patch('builtins.input')
242    def test_override_xml(self, mock_input, mock_metrics):
243        """Test _override_xml."""
244        mock_input.side_effect = ['1', 'n']
245        self.assertFalse(self.jdk_table_xml._override_xml())
246        self.assertEqual(mock_input.call_count, 2)
247        mock_input.side_effect = ['n']
248        result = self.jdk_table_xml._override_xml()
249        self.assertFalse(result)
250        mock_input.side_effect = ['y']
251        expected_result = (b'<application>\n'
252                           b'  <component name="ProjectJdkTable">\n'
253                           b'  </component>\n'
254                           b'</application>')
255        self.jdk_table_xml._override_xml()
256        test_result = ElementTree.tostring(self.jdk_table_xml._xml.getroot())
257        self.assertEqual(test_result, expected_result)
258        self.assertTrue(mock_metrics.called)
259
260    @mock.patch.object(xml_util, 'parse_xml')
261    @mock.patch.object(aidegen_metrics, 'send_exception_metrics')
262    @mock.patch('builtins.input')
263    def test_skip_send_metrics(self, mock_input, mock_metrics, mock_parse):
264        """Test _override_xml."""
265        mock_input.side_effect = ['y']
266        self.jdk_table_xml._xml = None
267        self.jdk_table_xml._override_xml()
268        self.assertFalse(mock_metrics.called)
269        self.assertTrue(mock_parse.called)
270
271
272if __name__ == '__main__':
273    unittest.main()
274