| # Copyright 2018 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | import unittest | 
 |  | 
 | import list_class_verification_failures as list_verification | 
 |  | 
 | from pylib.constants import host_paths | 
 |  | 
 | import devil_chromium  # pylint: disable=unused-import | 
 | from devil.android import device_errors | 
 | from devil.android import device_utils | 
 | from devil.android.sdk import version_codes | 
 |  | 
 | with host_paths.SysPath(host_paths.PYMOCK_PATH): | 
 |   import mock  # pylint: disable=import-error | 
 |  | 
 |  | 
 | def _CreateOdexLine(java_class_name, type_idx, verification_status): | 
 |   """Create a rough approximation of a line of oatdump output.""" | 
 |   return ('{type_idx}: L{java_class}; (offset=0xac) (type_idx={type_idx}) ' | 
 |           '({verification}) ' | 
 |           '(OatClassNoneCompiled)'.format(type_idx=type_idx, | 
 |                                           java_class=java_class_name, | 
 |                                           verification=verification_status)) | 
 |  | 
 |  | 
 | def _ClassForName(name, classes): | 
 |   return next(c for c in classes if c.name == name) | 
 |  | 
 |  | 
 | class _DetermineDeviceToUseTest(unittest.TestCase): | 
 |  | 
 |   def testDetermineDeviceToUse_emptyListWithOneAttachedDevice(self): | 
 |     fake_attached_devices = ['123'] | 
 |     user_specified_devices = [] | 
 |     device_utils.DeviceUtils.HealthyDevices = mock.MagicMock( | 
 |         return_value=fake_attached_devices) | 
 |     result = list_verification.DetermineDeviceToUse(user_specified_devices) | 
 |     self.assertEqual(result, fake_attached_devices[0]) | 
 |     device_utils.DeviceUtils.HealthyDevices.assert_called_with(device_arg=None) | 
 |  | 
 |   def testDetermineDeviceToUse_emptyListWithNoAttachedDevices(self): | 
 |     user_specified_devices = [] | 
 |     device_utils.DeviceUtils.HealthyDevices = mock.MagicMock( | 
 |         side_effect=device_errors.NoDevicesError()) | 
 |     with self.assertRaises(device_errors.NoDevicesError) as _: | 
 |       list_verification.DetermineDeviceToUse(user_specified_devices) | 
 |     device_utils.DeviceUtils.HealthyDevices.assert_called_with(device_arg=None) | 
 |  | 
 |   def testDetermineDeviceToUse_oneElementListWithOneAttachedDevice(self): | 
 |     user_specified_devices = ['123'] | 
 |     fake_attached_devices = ['123'] | 
 |     device_utils.DeviceUtils.HealthyDevices = mock.MagicMock( | 
 |         return_value=fake_attached_devices) | 
 |     result = list_verification.DetermineDeviceToUse(user_specified_devices) | 
 |     self.assertEqual(result, fake_attached_devices[0]) | 
 |     device_utils.DeviceUtils.HealthyDevices.assert_called_with( | 
 |         device_arg=user_specified_devices) | 
 |  | 
 |  | 
 | class _ListClassVerificationFailuresTest(unittest.TestCase): | 
 |  | 
 |   def testPathToDexForPlatformVersion_noPaths(self): | 
 |     sdk_int = version_codes.LOLLIPOP | 
 |     paths_to_apk = [] | 
 |     package_name = 'package.name' | 
 |     arch = 'arm64-v8a' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |  | 
 |     with self.assertRaises(list_verification.DeviceOSError) as cm: | 
 |       list_verification.PathToDexForPlatformVersion(device, package_name) | 
 |     message = str(cm.exception) | 
 |     self.assertIn('Could not find data directory', message) | 
 |  | 
 |   def testPathToDexForPlatformVersion_multiplePaths(self): | 
 |     sdk_int = version_codes.LOLLIPOP | 
 |     paths_to_apk = ['/first/path', '/second/path'] | 
 |     package_name = 'package.name' | 
 |     arch = 'arm64-v8a' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |  | 
 |     with self.assertRaises(list_verification.DeviceOSError) as cm: | 
 |       list_verification.PathToDexForPlatformVersion(device, package_name) | 
 |     message = str(cm.exception) | 
 |     self.assertIn('Expected exactly one path for', message) | 
 |  | 
 |   def testPathToDexForPlatformVersion_dalvikApiLevel(self): | 
 |     sdk_int = version_codes.KITKAT | 
 |     paths_to_apk = ['/some/path'] | 
 |     package_name = 'package.name' | 
 |     arch = 'arm64-v8a' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |  | 
 |     with self.assertRaises(list_verification.UnsupportedDeviceError) as _: | 
 |       list_verification.PathToDexForPlatformVersion(device, package_name) | 
 |  | 
 |   def testPathToDexForPlatformVersion_lollipopArm(self): | 
 |     sdk_int = version_codes.LOLLIPOP | 
 |     package_name = 'package.name' | 
 |     paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] | 
 |     arch = 'arm' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |     device.FileExists = mock.MagicMock(return_value=True) | 
 |  | 
 |     odex_file = list_verification.PathToDexForPlatformVersion(device, | 
 |                                                               package_name) | 
 |     self.assertEqual(odex_file, | 
 |                      ('/data/dalvik-cache/arm/data@app' | 
 |                       '@package.name-1@base.apk@classes.dex')) | 
 |  | 
 |   def testPathToDexForPlatformVersion_mashmallowArm(self): | 
 |     sdk_int = version_codes.MARSHMALLOW | 
 |     package_name = 'package.name' | 
 |     paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] | 
 |     arch = 'arm' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |     device.FileExists = mock.MagicMock(return_value=True) | 
 |  | 
 |     odex_file = list_verification.PathToDexForPlatformVersion(device, | 
 |                                                               package_name) | 
 |     self.assertEqual(odex_file, | 
 |                      '/some/path/package.name-1/oat/arm/base.odex') | 
 |  | 
 |   def testPathToDexForPlatformVersion_mashmallowArm64(self): | 
 |     sdk_int = version_codes.MARSHMALLOW | 
 |     package_name = 'package.name' | 
 |     paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] | 
 |     arch = 'arm64-v8a' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |     device.FileExists = mock.MagicMock(return_value=True) | 
 |  | 
 |     odex_file = list_verification.PathToDexForPlatformVersion(device, | 
 |                                                               package_name) | 
 |     self.assertEqual(odex_file, | 
 |                      '/some/path/package.name-1/oat/arm64/base.odex') | 
 |  | 
 |   def testPathToDexForPlatformVersion_pieNoOdexFile(self): | 
 |     sdk_int = version_codes.PIE | 
 |     package_name = 'package.name' | 
 |     paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] | 
 |     arch = 'arm64-v8a' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |     device.FileExists = mock.MagicMock(return_value=False) | 
 |  | 
 |     with self.assertRaises(list_verification.DeviceOSError) as cm: | 
 |       list_verification.PathToDexForPlatformVersion(device, package_name) | 
 |     message = str(cm.exception) | 
 |     self.assertIn('you must run dex2oat on debuggable apps on >= P', message) | 
 |  | 
 |   def testPathToDexForPlatformVersion_lowerApiLevelNoOdexFile(self): | 
 |     sdk_int = version_codes.MARSHMALLOW | 
 |     package_name = 'package.name' | 
 |     paths_to_apk = ['/some/path/{}-1/base.apk'.format(package_name)] | 
 |     arch = 'arm64-v8a' | 
 |  | 
 |     device = mock.Mock(build_version_sdk=sdk_int, product_cpu_abi=arch) | 
 |     device.GetApplicationPaths = mock.MagicMock(return_value=paths_to_apk) | 
 |     device.FileExists = mock.MagicMock(return_value=False) | 
 |  | 
 |     with self.assertRaises(list_verification.DeviceOSError) as _: | 
 |       list_verification.PathToDexForPlatformVersion(device, package_name) | 
 |  | 
 |   def testListClasses_noProguardMap(self): | 
 |     oatdump_output = [ | 
 |         _CreateOdexLine('a.b.JavaClass1', 6, 'StatusVerified'), | 
 |         _CreateOdexLine('a.b.JavaClass2', 7, | 
 |                         'StatusRetryVerificationAtRuntime'), | 
 |     ] | 
 |  | 
 |     classes = list_verification.ListClassesAndVerificationStatus(oatdump_output, | 
 |                                                                  None) | 
 |     self.assertEqual(2, len(classes)) | 
 |     java_class_1 = _ClassForName('a.b.JavaClass1', classes) | 
 |     java_class_2 = _ClassForName('a.b.JavaClass2', classes) | 
 |     self.assertEqual(java_class_1.verification_status, 'Verified') | 
 |     self.assertEqual(java_class_2.verification_status, | 
 |                      'RetryVerificationAtRuntime') | 
 |  | 
 |   def testListClasses_proguardMap(self): | 
 |     oatdump_output = [ | 
 |         _CreateOdexLine('a.b.ObfuscatedJavaClass1', 6, 'StatusVerified'), | 
 |         _CreateOdexLine('a.b.ObfuscatedJavaClass2', 7, | 
 |                         'StatusRetryVerificationAtRuntime'), | 
 |     ] | 
 |  | 
 |     mapping = { | 
 |         'a.b.ObfuscatedJavaClass1': 'a.b.JavaClass1', | 
 |         'a.b.ObfuscatedJavaClass2': 'a.b.JavaClass2', | 
 |     } | 
 |     classes = list_verification.ListClassesAndVerificationStatus(oatdump_output, | 
 |                                                                  mapping) | 
 |     self.assertEqual(2, len(classes)) | 
 |     java_class_1 = _ClassForName('a.b.JavaClass1', classes) | 
 |     java_class_2 = _ClassForName('a.b.JavaClass2', classes) | 
 |     self.assertEqual(java_class_1.verification_status, 'Verified') | 
 |     self.assertEqual(java_class_2.verification_status, | 
 |                      'RetryVerificationAtRuntime') | 
 |  | 
 |   def testListClasses_noStatusPrefix(self): | 
 |     oatdump_output = [ | 
 |         _CreateOdexLine('a.b.JavaClass1', 6, 'Verified'), | 
 |         _CreateOdexLine('a.b.JavaClass2', 7, 'RetryVerificationAtRuntime'), | 
 |     ] | 
 |  | 
 |     classes = list_verification.ListClassesAndVerificationStatus(oatdump_output, | 
 |                                                                  None) | 
 |     self.assertEqual(2, len(classes)) | 
 |     java_class_1 = _ClassForName('a.b.JavaClass1', classes) | 
 |     java_class_2 = _ClassForName('a.b.JavaClass2', classes) | 
 |     self.assertEqual(java_class_1.verification_status, 'Verified') | 
 |     self.assertEqual(java_class_2.verification_status, | 
 |                      'RetryVerificationAtRuntime') | 
 |  | 
 | if __name__ == '__main__': | 
 |   # Suppress logging messages. | 
 |   unittest.main(buffer=True) |