#!/usr/bin/env vpython3 # 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 import devil_chromium # pylint: disable=unused-import from devil.android import device_errors from devil.android import device_utils from devil.android.ndk import abis from devil.android.sdk import version_codes 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]) # pylint: disable=no-member device_utils.DeviceUtils.HealthyDevices.assert_called_with(device_arg=None) # pylint: enable=no-member 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) # pylint: disable=no-member device_utils.DeviceUtils.HealthyDevices.assert_called_with(device_arg=None) # pylint: enable=no-member 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]) # pylint: disable=no-member device_utils.DeviceUtils.HealthyDevices.assert_called_with( device_arg=user_specified_devices) # pylint: enable=no-member class _ListClassVerificationFailuresTest(unittest.TestCase): def testPathToDexForPlatformVersion_noPaths(self): sdk_int = version_codes.LOLLIPOP paths_to_apk = [] package_name = 'package.name' arch = abis.ARM_64 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 = abis.ARM_64 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 = abis.ARM_64 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 = abis.ARM_64 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 = abis.ARM_64 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 = abis.ARM_64 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)