blob: d2e5b7d1a07e7de6dfd8482e776e32803b2ecde7 [file] [log] [blame]
Pedro Loureirodea35e12021-05-26 15:50:07 +00001#!/usr/bin/env python3
2#
3# Copyright (C) 2021 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
17import io
18import re
19import unittest
20import xml.etree.ElementTree as ET
21import zipfile
22
23import api_versions_trimmer
24
25
26def create_in_memory_zip_file(files):
27 f = io.BytesIO()
28 with zipfile.ZipFile(f, "w") as z:
29 for fname in files:
30 with z.open(fname, mode="w") as class_file:
31 class_file.write(b"")
32 return f
33
34
35def indent(elem, level=0):
36 i = "\n" + level * " "
37 j = "\n" + (level - 1) * " "
38 if len(elem):
39 if not elem.text or not elem.text.strip():
40 elem.text = i + " "
41 if not elem.tail or not elem.tail.strip():
42 elem.tail = i
43 for subelem in elem:
44 indent(subelem, level + 1)
45 if not elem.tail or not elem.tail.strip():
46 elem.tail = j
47 else:
48 if level and (not elem.tail or not elem.tail.strip()):
49 elem.tail = j
50 return elem
51
52
53def pretty_print(s):
54 tree = ET.parse(io.StringIO(s))
55 el = indent(tree.getroot())
56 res = ET.tostring(el).decode("utf-8")
57 # remove empty lines inside the result because this still breaks some
58 # comparisons
59 return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE)
60
61
62class ApiVersionsTrimmerUnittests(unittest.TestCase):
63
64 def setUp(self):
65 # so it prints diffs in long strings (xml files)
66 self.maxDiff = None
67
68 def test_read_classes(self):
69 f = create_in_memory_zip_file(
70 ["a/b/C.class",
71 "a/b/D.class",
72 ]
73 )
74 res = api_versions_trimmer.read_classes(f)
75 self.assertEqual({"a/b/C", "a/b/D"}, res)
76
77 def test_read_classes_ignore_dex(self):
78 f = create_in_memory_zip_file(
79 ["a/b/C.class",
80 "a/b/D.class",
81 "a/b/E.dex",
82 "f.dex",
83 ]
84 )
85 res = api_versions_trimmer.read_classes(f)
86 self.assertEqual({"a/b/C", "a/b/D"}, res)
87
88 def test_read_classes_ignore_manifest(self):
89 f = create_in_memory_zip_file(
90 ["a/b/C.class",
91 "a/b/D.class",
92 "META-INFO/G.class"
93 ]
94 )
95 res = api_versions_trimmer.read_classes(f)
96 self.assertEqual({"a/b/C", "a/b/D"}, res)
97
98 def test_filter_method_signature(self):
99 xml = """
100 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
101 """
102 method = ET.fromstring(xml)
103 classes_to_remove = {"android/accessibilityservice/GestureDescription"}
104 expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
105 api_versions_trimmer.filter_method_tag(method, classes_to_remove)
106 self.assertEqual(expected, method.get("name"))
107
108 def test_filter_method_signature_with_L_in_method(self):
109 xml = """
110 <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
111 """
112 method = ET.fromstring(xml)
113 classes_to_remove = {"android/accessibilityservice/GestureDescription"}
114 expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
115 api_versions_trimmer.filter_method_tag(method, classes_to_remove)
116 self.assertEqual(expected, method.get("name"))
117
118 def test_filter_method_signature_with_L_in_class(self):
119 xml = """
120 <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
121 """
122 method = ET.fromstring(xml)
123 classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"}
124 expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
125 api_versions_trimmer.filter_method_tag(method, classes_to_remove)
126 self.assertEqual(expected, method.get("name"))
127
128 def test_filter_method_signature_with_inner_class(self):
129 xml = """
130 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
131 """
132 method = ET.fromstring(xml)
133 classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"}
134 expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
135 api_versions_trimmer.filter_method_tag(method, classes_to_remove)
136 self.assertEqual(expected, method.get("name"))
137
138 def _run_filter_db_test(self, database_str, expected):
139 """Performs the pattern of testing the filter_lint_database method.
140
141 Filters instances of the class "a/b/C" (hard-coded) from the database string
142 and compares the result with the expected result (performs formatting of
143 the xml of both inputs)
144
145 Args:
146 database_str: string, the contents of the lint database (api-versions.xml)
147 expected: string, the expected result after filtering the original
148 database
149 """
150 database = io.StringIO(database_str)
151 classes_to_remove = {"a/b/C"}
152 output = io.BytesIO()
153 api_versions_trimmer.filter_lint_database(
154 database,
155 classes_to_remove,
156 output
157 )
158 expected = pretty_print(expected)
159 res = pretty_print(output.getvalue().decode("utf-8"))
160 self.assertEqual(expected, res)
161
162 def test_filter_lint_database_updates_method_signature_params(self):
163 self._run_filter_db_test(
164 database_str="""
165 <api version="2">
166 <!-- will be removed -->
167 <class name="a/b/C" since="1">
168 <extends name="java/lang/Object"/>
169 </class>
170
171 <class name="a/b/E" since="1">
172 <!-- extends will be modified -->
173 <extends name="a/b/C"/>
174 <!-- first parameter will be modified -->
175 <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/>
176 <!-- second should remain untouched -->
177 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
178sultCallback;Landroid/os/Handler;)Z" since="24"/>
179 </class>
180 </api>
181 """,
182 expected="""
183 <api version="2">
184 <class name="a/b/E" since="1">
185 <extends name="java/lang/Object"/>
186 <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
187 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
188sultCallback;Landroid/os/Handler;)Z" since="24"/>
189 </class>
190 </api>
191 """)
192
193 def test_filter_lint_database_updates_method_signature_return(self):
194 self._run_filter_db_test(
195 database_str="""
196 <api version="2">
197 <!-- will be removed -->
198 <class name="a/b/C" since="1">
199 <extends name="java/lang/Object"/>
200 </class>
201
202 <class name="a/b/E" since="1">
203 <!-- extends will be modified -->
204 <extends name="a/b/C"/>
205 <!-- return type should be changed -->
206 <method name="gestureIdToString(I)La/b/C;" since="24"/>
207 </class>
208 </api>
209 """,
210 expected="""
211 <api version="2">
212 <class name="a/b/E" since="1">
213
214 <extends name="java/lang/Object"/>
215
216 <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/>
217 </class>
218 </api>
219 """)
220
221 def test_filter_lint_database_removes_implements(self):
222 self._run_filter_db_test(
223 database_str="""
224 <api version="2">
225 <!-- will be removed -->
226 <class name="a/b/C" since="1">
227 <extends name="java/lang/Object"/>
228 </class>
229
230 <class name="a/b/D" since="1">
231 <extends name="java/lang/Object"/>
232 <implements name="a/b/C"/>
233 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
234sultCallback;Landroid/os/Handler;)Z" since="24"/>
235 </class>
236 </api>
237 """,
238 expected="""
239 <api version="2">
240
241 <class name="a/b/D" since="1">
242 <extends name="java/lang/Object"/>
243 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
244sultCallback;Landroid/os/Handler;)Z" since="24"/>
245 </class>
246 </api>
247 """)
248
249 def test_filter_lint_database_updates_extends(self):
250 self._run_filter_db_test(
251 database_str="""
252 <api version="2">
253 <!-- will be removed -->
254 <class name="a/b/C" since="1">
255 <extends name="java/lang/Object"/>
256 </class>
257
258 <class name="a/b/E" since="1">
259 <!-- extends will be modified -->
260 <extends name="a/b/C"/>
261 <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
262 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
263sultCallback;Landroid/os/Handler;)Z" since="24"/>
264 </class>
265 </api>
266 """,
267 expected="""
268 <api version="2">
269 <class name="a/b/E" since="1">
270 <extends name="java/lang/Object"/>
271 <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
272 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
273sultCallback;Landroid/os/Handler;)Z" since="24"/>
274 </class>
275 </api>
276 """)
277
278 def test_filter_lint_database_removes_class(self):
279 self._run_filter_db_test(
280 database_str="""
281 <api version="2">
282 <!-- will be removed -->
283 <class name="a/b/C" since="1">
284 <extends name="java/lang/Object"/>
285 </class>
286
287 <class name="a/b/D" since="1">
288 <extends name="java/lang/Object"/>
289 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
290sultCallback;Landroid/os/Handler;)Z" since="24"/>
291 </class>
292 </api>
293 """,
294 expected="""
295 <api version="2">
296
297 <class name="a/b/D" since="1">
298 <extends name="java/lang/Object"/>
299 <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
300sultCallback;Landroid/os/Handler;)Z" since="24"/>
301 </class>
302 </api>
303 """)
304
305
306if __name__ == "__main__":
yangbill8ebeef32021-09-27 21:42:53 +0800307 unittest.main(verbosity=2)