• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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