1# Copyright 2023 The Bazel Authors. All rights reserved. 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 os 16import subprocess 17import unittest 18import zipfile 19 20 21class R8IntegrationTest(unittest.TestCase): 22 """Tests Bazel's R8 integration.""" 23 24 def _r8_integration_check( 25 self, apk, expect_unused_activity_resource, expect_unused_activity_class 26 ): 27 tmp = os.environ["TEST_TMPDIR"] 28 apk_directory = ( 29 "test/rules/android_binary_internal/r8_integration/java/com/basicapp" 30 ) 31 apk_tmp = os.path.join(tmp, apk) 32 classes_dex = os.path.join(apk_tmp, "classes.dex") 33 with zipfile.ZipFile(os.path.join(apk_directory, apk)) as zf: 34 apk_files = zf.namelist() 35 zf.extract("classes.dex", apk_tmp) 36 37 self.assertEqual( 38 expect_unused_activity_resource, 39 "res/layout/unused_activity.xml" in apk_files, 40 ) 41 42 build_tools_dir = "external/androidsdk-supplemental/build-tools" 43 build_tools_version = os.listdir(build_tools_dir)[0] 44 dexdump = os.path.join(build_tools_dir, build_tools_version, "dexdump") 45 46 dexdump_proc = subprocess.run( 47 [dexdump, classes_dex], 48 stdout=subprocess.PIPE, 49 stderr=subprocess.PIPE, 50 check=True, 51 ) 52 dexdump_stdout = str(dexdump_proc.stdout) 53 self.assertEqual( 54 expect_unused_activity_class, 55 "Class descriptor : 'Lcom/basicapp/UnusedActivity;'" in dexdump_stdout, 56 ) 57 58 # In all cases, the Lib2WithSpecsActivity class should be in the app, 59 # since lib2_proguard.cfg (an indirect dependency) specifies to keep it. 60 self.assertIn( 61 "Class descriptor : 'Lcom/basicapp/Lib2WithSpecsActivity;'", 62 dexdump_stdout, 63 ) 64 65 def test_r8_integration(self): 66 # No R8, so unused resources and unused classes should be in the app 67 self._r8_integration_check( 68 "basic_app_no_R8.apk", 69 expect_unused_activity_resource=True, 70 expect_unused_activity_class=True, 71 ) 72 73 # Run R8, don't shrink, so unused class should not be in the app but unused 74 # resource should remain. 75 self._r8_integration_check( 76 "basic_app_R8_no_shrink.apk", 77 expect_unused_activity_resource=True, 78 expect_unused_activity_class=False, 79 ) 80 81 # Run R8 and shrinkings, so unused classes and resources should not be in 82 # the app. 83 self._r8_integration_check( 84 "basic_app_R8_shrink.apk", 85 expect_unused_activity_resource=False, 86 expect_unused_activity_class=False, 87 ) 88 89 90if __name__ == "__main__": 91 unittest.main() 92