1#!/usr/bin/env vpython3 2# Copyright 2018 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import unittest 7 8import list_class_verification_failures as list_verification 9 10import devil_chromium # pylint: disable=unused-import 11from devil.android import device_errors 12from devil.android import device_utils 13from devil.android.ndk import abis 14from devil.android.sdk import version_codes 15 16import mock # pylint: disable=import-error 17 18 19def _CreateOdexLine(java_class_name, type_idx, verification_status): 20 """Create a rough approximation of a line of oatdump output.""" 21 return ('{type_idx}: L{java_class}; (offset=0xac) (type_idx={type_idx}) ' 22 '({verification}) ' 23 '(OatClassNoneCompiled)'.format(type_idx=type_idx, 24 java_class=java_class_name, 25 verification=verification_status)) 26 27 28def _ClassForName(name, classes): 29 return next(c for c in classes if c.name == name) 30 31 32class _DetermineDeviceToUseTest(unittest.TestCase): 33 34 def testDetermineDeviceToUse_emptyListWithOneAttachedDevice(self): 35 fake_attached_devices = ['123'] 36 user_specified_devices = [] 37 device_utils.DeviceUtils.HealthyDevices = mock.MagicMock( 38 return_value=fake_attached_devices) 39 result = list_verification.DetermineDeviceToUse(user_specified_devices) 40 self.assertEqual(result, fake_attached_devices[0]) 41 # pylint: disable=no-member 42 device_utils.DeviceUtils.HealthyDevices.assert_called_with(device_arg=None) 43 # pylint: enable=no-member 44 45 def testDetermineDeviceToUse_emptyListWithNoAttachedDevices(self): 46 user_specified_devices = [] 47 device_utils.DeviceUtils.HealthyDevices = mock.MagicMock( 48 side_effect=device_errors.NoDevicesError()) 49 with self.assertRaises(device_errors.NoDevicesError) as _: 50 list_verification.DetermineDeviceToUse(user_specified_devices) 51 # pylint: disable=no-member 52 device_utils.DeviceUtils.HealthyDevices.assert_called_with(device_arg=None) 53 # pylint: enable=no-member 54 55 def testDetermineDeviceToUse_oneElementListWithOneAttachedDevice(self): 56 user_specified_devices = ['123'] 57 fake_attached_devices = ['123'] 58 device_utils.DeviceUtils.HealthyDevices = mock.MagicMock( 59 return_value=fake_attached_devices) 60 result = list_verification.DetermineDeviceToUse(user_specified_devices) 61 self.assertEqual(result, fake_attached_devices[0]) 62 # pylint: disable=no-member 63 device_utils.DeviceUtils.HealthyDevices.assert_called_with( 64 device_arg=user_specified_devices) 65 # pylint: enable=no-member 66 67 68class _ListClassVerificationFailuresTest(unittest.TestCase): 69 70 def testPathToDexForPlatformVersion_noPaths(self): 71 sdk_int = version_codes.LOLLIPOP 72 paths_to_apk = [] 73 package_name = 'package.name' 74 arch = abis.ARM_64 75 76 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 77 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 78 79 with self.assertRaises(list_verification.DeviceOSError) as cm: 80 list_verification.FindOdexFiles(device, package_name) 81 message = str(cm.exception) 82 self.assertIn('Could not find data directory', message) 83 84 def testPathToDexForPlatformVersion_multiplePaths(self): 85 sdk_int = version_codes.LOLLIPOP 86 paths_to_apk = ['/first/path', '/second/path'] 87 package_name = 'package.name' 88 arch = abis.ARM_64 89 90 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 91 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 92 93 odex_files = list_verification.FindOdexFiles(device, package_name) 94 self.assertEqual(odex_files, [ 95 '/data/dalvik-cache/arm64/data@app@first@base.apk@classes.dex', 96 '/data/dalvik-cache/arm64/data@app@second@base.apk@classes.dex' 97 ]) 98 99 def testPathToDexForPlatformVersion_dalvikApiLevel(self): 100 sdk_int = version_codes.KITKAT 101 paths_to_apk = ['/some/path'] 102 package_name = 'package.name' 103 arch = abis.ARM_64 104 105 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 106 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 107 108 with self.assertRaises(list_verification.UnsupportedDeviceError) as _: 109 list_verification.FindOdexFiles(device, package_name) 110 111 def testPathToDexForPlatformVersion_lollipopArm(self): 112 sdk_int = version_codes.LOLLIPOP 113 package_name = 'package.name' 114 paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] 115 arch = 'arm' 116 117 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 118 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 119 device.FileExists = mock.MagicMock(return_value=True) 120 121 odex_files = list_verification.FindOdexFiles(device, package_name) 122 self.assertEqual( 123 odex_files, 124 ['/data/dalvik-cache/arm/data@app@package.name-1@base.apk@classes.dex']) 125 126 def testPathToDexForPlatformVersion_mashmallowArm(self): 127 sdk_int = version_codes.MARSHMALLOW 128 package_name = 'package.name' 129 paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] 130 arch = 'arm' 131 132 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 133 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 134 device.FileExists = mock.MagicMock(return_value=True) 135 136 odex_files = list_verification.FindOdexFiles(device, package_name) 137 self.assertEqual(odex_files, 138 ['/some/path/package.name-1/oat/arm/base.odex']) 139 140 def testPathToDexForPlatformVersion_mashmallowArm64(self): 141 sdk_int = version_codes.MARSHMALLOW 142 package_name = 'package.name' 143 paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] 144 arch = abis.ARM_64 145 146 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 147 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 148 device.FileExists = mock.MagicMock(return_value=True) 149 150 odex_files = list_verification.FindOdexFiles(device, package_name) 151 self.assertEqual(odex_files, 152 ['/some/path/package.name-1/oat/arm64/base.odex']) 153 154 def testPathToDexForPlatformVersion_pieNoOdexFile(self): 155 sdk_int = version_codes.PIE 156 package_name = 'package.name' 157 paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] 158 arch = abis.ARM_64 159 160 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 161 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 162 device.FileExists = mock.MagicMock(return_value=False) 163 164 with self.assertRaises(list_verification.DeviceOSError) as cm: 165 list_verification.FindOdexFiles(device, package_name) 166 message = str(cm.exception) 167 self.assertIn('you must run dex2oat on debuggable apps on >= P', message) 168 169 def testPathToDexForPlatformVersion_lowerApiLevelNoOdexFile(self): 170 sdk_int = version_codes.MARSHMALLOW 171 package_name = 'package.name' 172 paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] 173 arch = abis.ARM_64 174 175 device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) 176 device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) 177 device.FileExists = mock.MagicMock(return_value=False) 178 179 with self.assertRaises(list_verification.DeviceOSError) as _: 180 list_verification.FindOdexFiles(device, package_name) 181 182 def testListClasses_noProguardMap(self): 183 oatdump_output = [ 184 _CreateOdexLine('a.b.JavaClass1', 6, 'StatusVerified'), 185 _CreateOdexLine('a.b.JavaClass2', 7, 186 'StatusRetryVerificationAtRuntime'), 187 ] 188 189 classes = list_verification.ParseOatdump(oatdump_output, None) 190 self.assertEqual(2, len(classes)) 191 java_class_1 = _ClassForName('a.b.JavaClass1', classes) 192 java_class_2 = _ClassForName('a.b.JavaClass2', classes) 193 self.assertEqual(java_class_1.verification_status, 'Verified') 194 self.assertEqual(java_class_2.verification_status, 195 'RetryVerificationAtRuntime') 196 197 def testListClasses_proguardMap(self): 198 oatdump_output = [ 199 _CreateOdexLine('a.b.ObfuscatedJavaClass1', 6, 'StatusVerified'), 200 _CreateOdexLine('a.b.ObfuscatedJavaClass2', 7, 201 'StatusRetryVerificationAtRuntime'), 202 ] 203 204 mapping = { 205 'a.b.ObfuscatedJavaClass1': 'a.b.JavaClass1', 206 'a.b.ObfuscatedJavaClass2': 'a.b.JavaClass2', 207 } 208 classes = list_verification.ParseOatdump(oatdump_output, mapping) 209 self.assertEqual(2, len(classes)) 210 java_class_1 = _ClassForName('a.b.JavaClass1', classes) 211 java_class_2 = _ClassForName('a.b.JavaClass2', classes) 212 self.assertEqual(java_class_1.verification_status, 'Verified') 213 self.assertEqual(java_class_2.verification_status, 214 'RetryVerificationAtRuntime') 215 216 def testListClasses_noStatusPrefix(self): 217 oatdump_output = [ 218 _CreateOdexLine('a.b.JavaClass1', 6, 'Verified'), 219 _CreateOdexLine('a.b.JavaClass2', 7, 'RetryVerificationAtRuntime'), 220 ] 221 222 classes = list_verification.ParseOatdump(oatdump_output, None) 223 self.assertEqual(2, len(classes)) 224 java_class_1 = _ClassForName('a.b.JavaClass1', classes) 225 java_class_2 = _ClassForName('a.b.JavaClass2', classes) 226 self.assertEqual(java_class_1.verification_status, 'Verified') 227 self.assertEqual(java_class_2.verification_status, 228 'RetryVerificationAtRuntime') 229 230if __name__ == '__main__': 231 # Suppress logging messages. 232 unittest.main(buffer=True) 233