diff options
Diffstat (limited to 'api/api_versions_trimmer_unittests.py')
-rw-r--r-- | api/api_versions_trimmer_unittests.py | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py new file mode 100644 index 000000000000..4eb929ea1b5d --- /dev/null +++ b/api/api_versions_trimmer_unittests.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +import re +import unittest +import xml.etree.ElementTree as ET +import zipfile + +import api_versions_trimmer + + +def create_in_memory_zip_file(files): + f = io.BytesIO() + with zipfile.ZipFile(f, "w") as z: + for fname in files: + with z.open(fname, mode="w") as class_file: + class_file.write(b"") + return f + + +def indent(elem, level=0): + i = "\n" + level * " " + j = "\n" + (level - 1) * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for subelem in elem: + indent(subelem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = j + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = j + return elem + + +def pretty_print(s): + tree = ET.parse(io.StringIO(s)) + el = indent(tree.getroot()) + res = ET.tostring(el).decode("utf-8") + # remove empty lines inside the result because this still breaks some + # comparisons + return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE) + + +class ApiVersionsTrimmerUnittests(unittest.TestCase): + + def setUp(self): + # so it prints diffs in long strings (xml files) + self.maxDiff = None + + def test_read_classes(self): + f = create_in_memory_zip_file( + ["a/b/C.class", + "a/b/D.class", + ] + ) + res = api_versions_trimmer.read_classes(f) + self.assertEqual({"a/b/C", "a/b/D"}, res) + + def test_read_classes_ignore_dex(self): + f = create_in_memory_zip_file( + ["a/b/C.class", + "a/b/D.class", + "a/b/E.dex", + "f.dex", + ] + ) + res = api_versions_trimmer.read_classes(f) + self.assertEqual({"a/b/C", "a/b/D"}, res) + + def test_read_classes_ignore_manifest(self): + f = create_in_memory_zip_file( + ["a/b/C.class", + "a/b/D.class", + "META-INFO/G.class" + ] + ) + res = api_versions_trimmer.read_classes(f) + self.assertEqual({"a/b/C", "a/b/D"}, res) + + def test_filter_method_signature(self): + xml = """ + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/GestureDescription"} + expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def test_filter_method_signature_with_L_in_method(self): + xml = """ + <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/GestureDescription"} + expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def test_filter_method_signature_with_L_in_class(self): + xml = """ + <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"} + expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def test_filter_method_signature_with_inner_class(self): + xml = """ + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/> + """ + method = ET.fromstring(xml) + classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"} + expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" + api_versions_trimmer.filter_method_tag(method, classes_to_remove) + self.assertEqual(expected, method.get("name")) + + def _run_filter_db_test(self, database_str, expected): + """Performs the pattern of testing the filter_lint_database method. + + Filters instances of the class "a/b/C" (hard-coded) from the database string + and compares the result with the expected result (performs formatting of + the xml of both inputs) + + Args: + database_str: string, the contents of the lint database (api-versions.xml) + expected: string, the expected result after filtering the original + database + """ + database = io.StringIO(database_str) + classes_to_remove = {"a/b/C"} + output = io.BytesIO() + api_versions_trimmer.filter_lint_database( + database, + classes_to_remove, + output + ) + expected = pretty_print(expected) + res = pretty_print(output.getvalue().decode("utf-8")) + self.assertEqual(expected, res) + + def test_filter_lint_database_updates_method_signature_params(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/E" since="1"> + <!-- extends will be modified --> + <extends name="a/b/C"/> + <!-- first parameter will be modified --> + <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/> + <!-- second should remain untouched --> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + <class name="a/b/E" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_updates_method_signature_return(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/E" since="1"> + <!-- extends will be modified --> + <extends name="a/b/C"/> + <!-- return type should be changed --> + <method name="gestureIdToString(I)La/b/C;" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + <class name="a/b/E" since="1"> + + <extends name="java/lang/Object"/> + + <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_removes_implements(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <implements name="a/b/C"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_updates_extends(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/E" since="1"> + <!-- extends will be modified --> + <extends name="a/b/C"/> + <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + <class name="a/b/E" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + def test_filter_lint_database_removes_class(self): + self._run_filter_db_test( + database_str=""" + <api version="2"> + <!-- will be removed --> + <class name="a/b/C" since="1"> + <extends name="java/lang/Object"/> + </class> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """, + expected=""" + <api version="2"> + + <class name="a/b/D" since="1"> + <extends name="java/lang/Object"/> + <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe +sultCallback;Landroid/os/Handler;)Z" since="24"/> + </class> + </api> + """) + + +if __name__ == "__main__": + unittest.main() |