1#  Copyright (C) 2023 The Android Open Source Project
2#
3#  Licensed under the Apache License, Version 2.0 (the "License");
4#  you may not use this file except in compliance with the License.
5#  You may obtain a copy of the License at
6#
7#       http://www.apache.org/licenses/LICENSE-2.0
8#
9#  Unless required by applicable law or agreed to in writing, software
10#  distributed under the License is distributed on an "AS IS" BASIS,
11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12#  See the License for the specific language governing permissions and
13#  limitations under the License.
14
15import unittest
16import xml.etree.ElementTree as ET
17import os
18import shutil
19
20from generate_adservices_public_xml import AdServicesUiUtil
21from generate_adservices_public_xml import ResourceCategory
22from generate_ota_public_xml import AdServicesOTAUtil
23
24
25class AdServiceUiTests(unittest.TestCase):
26    TEST_DIR = 'test_res/values/'
27    TEST_LAYOUT_DIR = 'test_res/layout/'
28
29    TEST_OTA_DIR = 'test_ota_res/values/'
30    TEST_OTA_LAYOUT_DIR = 'test_ota_res/layout/'
31    TEST_OTA_DRAWABLE_DIR = 'test_ota_res/drawable/'
32    TEST_OTA_COLOR_DIR = 'test_ota_res/color/'
33
34    TEST_STRINGS_FILE = 'test_res/values/strings.xml'
35    TEST_STRINGS_XML = '''
36    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
37    <string name="permlab_accessAdServicesTopics">access AdServices Topics API</string>
38    <string name="permdesc_accessAdServicesTopics">Allows an application to access AdServices Topics API.</string>
39    </resources>
40    '''
41
42    TEST_OTA_STRINGS_FILE = 'test_ota_res/values/strings.xml'
43    TEST_OTA_STRINGS_XML = '''
44    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
45    <string name="permlab_accessAdServicesTopics">Access AdServices Topics API</string>
46    </resources>
47    '''
48
49    TEST_DIMENS_FILE = 'test_res/values/dimens.xml'
50    TEST_DIMENS_XML = '''
51    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
52    <dimen name="disabled_button_alpha" format="float">0.7</dimen>
53    <dimen name="enabled_button_alpha" format="float">0.5</dimen>
54    </resources>
55    '''
56
57    TEST_OTA_DIMENS_FILE = 'test_ota_res/values/dimens.xml'
58    TEST_OTA_DIMENS_XML = '''
59    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
60    <dimen name="disabled_button_alpha" format="float">0.3</dimen>
61    </resources>
62    '''
63
64    TEST_LAYOUT_FILE = 'test_res/layout/layout.xml'
65    TEST_LAYOUT_XML = '''
66    <LinearLayout
67    xmlns:android="http://schemas.android.com/apk/res/android"
68    android:layout_width="match_parent"
69    android:layout_height="match_parent"
70    android:orientation="vertical">
71    <androidx.fragment.app.FragmentContainerView
72        android:id="@+id/fragment_container_view"
73        android:layout_width="match_parent"
74        android:layout_height="match_parent" />
75    </LinearLayout>
76    '''
77
78    TEST_OTA_LAYOUT_FILE = 'test_ota_res/layout/layout.xml'
79    TEST_OTA_LAYOUT_XML = '''
80    <LinearLayout
81    xmlns:android="http://schemas.android.com/apk/res/android"
82    android:layout_width="match_parent"
83    android:layout_height="match_parent"
84    android:orientation="horizontal">
85    <androidx.fragment.app.FragmentContainerView
86        android:id="@+id/fragment_container_view"
87        android:layout_width="wrap_content"
88        android:layout_height="match_parent" />
89    </LinearLayout>
90    '''
91
92    TEST_STRINGS_MAP_START = '0x7f017fff'
93    TEST_DIMENS_MAP_START = '0x7f027fff'
94    TEST_LAYOUT_MAP_START = '0x7f037fff'
95    TEST_ID_MAP_START = '0x7f047fff'
96
97    TEST_PUBLIC_FILE = 'test_res/values/public.xml'
98    TEST_PUBLIC_XML = '''
99    <resources>
100    <public type="string" name="permlab_accessAdServicesTopics" id="0x7f017fff" />
101    <public type="string" name="permdesc_accessAdServicesTopics" id="0x7f017ffe" />
102    <public type="dimen" name="disabled_button_alpha" id="0x7f027fff" />
103    <public type="dimen" name="enabled_button_alpha" id="0x7f027ffe" />
104    </resources>
105    '''
106
107    TEST_OTA_PUBLIC_FILE = 'test_ota_res/values/public.xml'
108    TEST_OTA_PUBLIC_XML = '''
109    <resources>
110    <public type="string" name="permlab_accessAdServicesTopics" id="0x7f017fff" />
111    <public type="dimen" name="disabled_button_alpha" id="0x7f027fff" />
112    </resources>
113    '''
114
115    util = AdServicesUiUtil()
116    ota_util = AdServicesOTAUtil()
117
118    def _write(self, xml, filepath):
119        f = open(filepath, "w")
120        f.write(xml)
121        f.close()
122
123    def _generate_test_files(self):
124        os.makedirs(self.TEST_DIR, exist_ok=True)
125        os.makedirs(self.TEST_LAYOUT_DIR, exist_ok=True)
126
127        self._write(self.TEST_STRINGS_XML, self.TEST_STRINGS_FILE)
128        self._write(self.TEST_DIMENS_XML, self.TEST_DIMENS_FILE)
129        self._write(self.TEST_PUBLIC_XML, self.TEST_PUBLIC_FILE)
130
131    def _generate_ota_test_files(self):
132        os.makedirs(self.TEST_OTA_DIR, exist_ok=True)
133        os.makedirs(self.TEST_OTA_LAYOUT_DIR, exist_ok=True)
134
135        self._write(self.TEST_OTA_STRINGS_XML, self.TEST_OTA_STRINGS_FILE)
136        self._write(self.TEST_OTA_DIMENS_XML, self.TEST_OTA_DIMENS_FILE)
137        self._write(self.TEST_OTA_PUBLIC_XML, self.TEST_OTA_PUBLIC_FILE)
138
139    def _delete_dir(self, dir):
140        if os.path.exists(dir):
141            shutil.rmtree(dir)
142
143    def _delete_all_test_files(self):
144        self._delete_dir('test_res')
145        self._delete_dir('test_ota_res')
146        self._delete_dir('__pycache__')
147
148    def _update_res_xml(self, res_dir, res_type, n):
149        root = ET.parse(res_dir).getroot()
150        for res_name in [f"test{res_type}{i}" for i in range(n)]:
151            added_element = ET.SubElement(root, res_type)
152            added_element.set('name', res_name)
153
154        ET.indent(root, space='    ')
155        with open(res_dir, "w+") as file:
156            file.write(ET.tostring(root, encoding="unicode"))
157
158    def _test_util_update_public_xml(self, new_res_count, public_xml, public_xml_dir):
159        old_root = ET.ElementTree(ET.fromstring(public_xml)).getroot()
160        old_mapping = {node.attrib['name']: node.attrib['id'] for node in old_root}
161
162        root = ET.parse(public_xml_dir).getroot()
163        mapping = {node.attrib['name']: node.attrib['id'] for node in root}
164
165        assert (len(old_mapping) + new_res_count == len(mapping))
166        assert (len(mapping) == len(set(mapping.values())))
167        for name, _id in mapping.items():
168            if name in old_mapping:
169                assert (_id == old_mapping[name])
170
171    def _test_util_compare_public_xmls(self, adservices_public_xml_dir, ota_public_xml_dir):
172        adservices_root = ET.parse(adservices_public_xml_dir).getroot()
173        adservices_mapping = {node.attrib['name']: node.attrib['id'] for node in adservices_root}
174
175        ota_root = ET.parse(ota_public_xml_dir).getroot()
176        ota_mapping = {node.attrib['name']: node.attrib['id'] for node in ota_root}
177
178        assert (len(adservices_mapping) >= len(ota_mapping))
179        assert (len(adservices_mapping) == len(set(adservices_mapping.values())))
180        assert (len(ota_mapping) == len(set(ota_mapping.values())))
181        for name, _id in ota_mapping.items():
182            assert (_id == adservices_mapping[name])
183
184    def test_adding_adservices_strings(self):
185        self._generate_test_files()
186
187        new_strings_count = 5
188        self._update_res_xml(self.TEST_STRINGS_FILE, 'string', new_strings_count)
189        self.util.update_public_xml(self.TEST_STRINGS_FILE, 'string', ResourceCategory.Value,
190                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
191
192        self._test_util_update_public_xml(new_strings_count, self.TEST_PUBLIC_XML,
193                                          self.TEST_PUBLIC_FILE)
194
195        self._delete_all_test_files()
196
197    def test_adding_adservices_dimens(self):
198        self._generate_test_files()
199
200        new_dimens_count = 2
201        self._update_res_xml(self.TEST_DIMENS_FILE, 'dimen', new_dimens_count)
202        self.util.update_public_xml(self.TEST_DIMENS_FILE, 'dimen', ResourceCategory.Value,
203                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
204
205        self._test_util_update_public_xml(new_dimens_count, self.TEST_PUBLIC_XML,
206                                          self.TEST_PUBLIC_FILE)
207
208        self._delete_all_test_files()
209
210    def test_adding_adservices_layouts(self):
211        self._generate_test_files()
212
213        new_layout_count = 1
214        self._write(self.TEST_LAYOUT_XML, self.TEST_LAYOUT_FILE)
215        self.util.update_public_xml(self.TEST_LAYOUT_DIR, 'layout', ResourceCategory.File,
216                                    self.TEST_LAYOUT_MAP_START, self.TEST_PUBLIC_FILE)
217
218        self._test_util_update_public_xml(new_layout_count, self.TEST_PUBLIC_XML,
219                                          self.TEST_PUBLIC_FILE)
220
221        self._delete_all_test_files()
222
223    def test_adding_adservices_multiple_res(self):
224        self._generate_test_files()
225
226        new_strings_count = 3
227        self._update_res_xml(self.TEST_STRINGS_FILE, 'string', new_strings_count)
228        self.util.update_public_xml(self.TEST_STRINGS_FILE, 'string', ResourceCategory.Value,
229                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
230
231        self._test_util_update_public_xml(new_strings_count, self.TEST_PUBLIC_XML,
232                                          self.TEST_PUBLIC_FILE)
233
234        new_dimens_count = 2
235        self._update_res_xml(self.TEST_DIMENS_FILE, 'dimen', new_dimens_count)
236        self.util.update_public_xml(self.TEST_DIMENS_FILE, 'dimen', ResourceCategory.Value,
237                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
238
239        self._test_util_update_public_xml(new_strings_count + new_dimens_count,
240                                          self.TEST_PUBLIC_XML,
241                                          self.TEST_PUBLIC_FILE)
242
243        new_layout_count = 1
244        self._write(self.TEST_LAYOUT_XML, self.TEST_LAYOUT_FILE)
245        self.util.update_public_xml(self.TEST_LAYOUT_DIR, 'layout', ResourceCategory.File,
246                                    self.TEST_LAYOUT_MAP_START, self.TEST_PUBLIC_FILE)
247
248        self._test_util_update_public_xml(new_strings_count + new_dimens_count + new_layout_count,
249                                          self.TEST_PUBLIC_XML,
250                                          self.TEST_PUBLIC_FILE)
251
252        self._delete_all_test_files()
253
254    def test_adding_ota_strings(self):
255        self._generate_test_files()
256        self._generate_ota_test_files()
257
258        new_strings_count = 5
259        self._update_res_xml(self.TEST_STRINGS_FILE, 'string', new_strings_count)
260        new_ota_strings_count = 3
261        self._update_res_xml(self.TEST_OTA_STRINGS_FILE, 'string', new_ota_strings_count)
262
263        self.util.update_public_xml(self.TEST_STRINGS_FILE, 'string', ResourceCategory.Value,
264                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
265        self.ota_util.update_ota_public_xml(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE,
266                                            self.TEST_OTA_DIR, self.TEST_OTA_LAYOUT_DIR,
267                                            self.TEST_OTA_DRAWABLE_DIR, self.TEST_OTA_COLOR_DIR)
268
269        self._test_util_compare_public_xmls(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE)
270
271        self._delete_all_test_files()
272
273    def test_adding_ota_dimens(self):
274        self._generate_test_files()
275        self._generate_ota_test_files()
276
277        new_dimens_count = 2
278        self._update_res_xml(self.TEST_DIMENS_FILE, 'dimen', new_dimens_count)
279        new_ota_dimens_count = 1
280        self._update_res_xml(self.TEST_OTA_DIMENS_FILE, 'dimen', new_ota_dimens_count)
281
282        self.util.update_public_xml(self.TEST_DIMENS_FILE, 'dimen', ResourceCategory.Value,
283                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
284        self.ota_util.update_ota_public_xml(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE,
285                                            self.TEST_OTA_DIR, self.TEST_OTA_LAYOUT_DIR,
286                                            self.TEST_OTA_DRAWABLE_DIR, self.TEST_OTA_COLOR_DIR)
287
288        self._test_util_compare_public_xmls(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE)
289
290        self._delete_all_test_files()
291
292    def test_adding_ota_layout(self):
293        self._generate_test_files()
294        self._generate_ota_test_files()
295
296        self._write(self.TEST_LAYOUT_XML, self.TEST_LAYOUT_FILE)
297        self._write(self.TEST_OTA_LAYOUT_XML, self.TEST_OTA_LAYOUT_FILE)
298
299        self.util.update_public_xml(self.TEST_LAYOUT_DIR, 'layout', ResourceCategory.File,
300                                    self.TEST_LAYOUT_MAP_START, self.TEST_PUBLIC_FILE)
301        self.util.update_public_xml(self.TEST_LAYOUT_DIR, 'id', ResourceCategory.Id,
302                                    self.TEST_ID_MAP_START, self.TEST_PUBLIC_FILE)
303
304        self.ota_util.update_ota_public_xml(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE,
305                                            self.TEST_OTA_DIR, self.TEST_OTA_LAYOUT_DIR,
306                                            self.TEST_OTA_DRAWABLE_DIR, self.TEST_OTA_COLOR_DIR)
307
308        self._test_util_compare_public_xmls(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE)
309
310        self._delete_all_test_files()
311
312    def test_adding_ota_multiple_res(self):
313        self._generate_test_files()
314        self._generate_ota_test_files()
315
316        new_strings_count = 3
317        self._update_res_xml(self.TEST_STRINGS_FILE, 'string', new_strings_count)
318        new_ota_strings_count = 2
319        self._update_res_xml(self.TEST_OTA_STRINGS_FILE, 'string', new_ota_strings_count)
320
321        self.util.update_public_xml(self.TEST_STRINGS_FILE, 'string', ResourceCategory.Value,
322                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
323        self.ota_util.update_ota_public_xml(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE,
324                                            self.TEST_OTA_DIR, self.TEST_OTA_LAYOUT_DIR,
325                                            self.TEST_OTA_DRAWABLE_DIR, self.TEST_OTA_COLOR_DIR)
326
327        self._test_util_compare_public_xmls(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE)
328
329        new_dimens_count = 1
330        self._update_res_xml(self.TEST_DIMENS_FILE, 'dimen', new_dimens_count)
331        new_ota_dimens_count = 1
332        self._update_res_xml(self.TEST_OTA_DIMENS_FILE, 'dimen', new_ota_dimens_count)
333
334        self.util.update_public_xml(self.TEST_DIMENS_FILE, 'dimen', ResourceCategory.Value,
335                                    self.TEST_DIMENS_MAP_START, self.TEST_PUBLIC_FILE)
336        self.ota_util.update_ota_public_xml(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE,
337                                            self.TEST_OTA_DIR, self.TEST_OTA_LAYOUT_DIR,
338                                            self.TEST_OTA_DRAWABLE_DIR, self.TEST_OTA_COLOR_DIR)
339
340        self._test_util_compare_public_xmls(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE)
341
342        self._write(self.TEST_LAYOUT_XML, self.TEST_LAYOUT_FILE)
343        self._write(self.TEST_OTA_LAYOUT_XML, self.TEST_OTA_LAYOUT_FILE)
344
345        self.util.update_public_xml(self.TEST_LAYOUT_DIR, 'layout', ResourceCategory.File,
346                                    self.TEST_LAYOUT_MAP_START, self.TEST_PUBLIC_FILE)
347        self.util.update_public_xml(self.TEST_LAYOUT_DIR, 'id', ResourceCategory.Id,
348                                    self.TEST_ID_MAP_START, self.TEST_PUBLIC_FILE)
349
350        self.ota_util.update_ota_public_xml(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE,
351                                            self.TEST_OTA_DIR, self.TEST_OTA_LAYOUT_DIR,
352                                            self.TEST_OTA_DRAWABLE_DIR, self.TEST_OTA_COLOR_DIR)
353
354        self._test_util_compare_public_xmls(self.TEST_PUBLIC_FILE, self.TEST_OTA_PUBLIC_FILE)
355
356        self._delete_all_test_files()
357
358
359if __name__ == '__main__':
360    unittest.main()
361