1#! /usr/bin/env python 2# Copyright 2017 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import collections 7import os 8import unittest 9 10from devil import devil_env 11from devil.android import apk_helper 12from devil.android.ndk import abis 13from devil.utils import mock_calls 14 15with devil_env.SysPath(devil_env.PYMOCK_PATH): 16 import mock # pylint: disable=import-error 17 18# pylint: disable=line-too-long 19_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android 20 E: manifest (line=1) 21 A: android:versionCode(0x0101021b)=(type 0x10)0x166de1ea 22 A: android:versionName(0x0101021c)="75.0.3763.0" (Raw: "75.0.3763.0") 23 A: package="org.chromium.abc" (Raw: "org.chromium.abc") 24 A: split="random_split" (Raw: "random_split") 25 E: uses-sdk (line=2) 26 A: android:minSdkVersion(0x0101020c)=(type 0x10)0x15 27 A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1c 28 E: uses-permission (line=2) 29 A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET") 30 E: uses-permission (line=3) 31 A: android:name(0x01010003)="android.permission.READ_EXTERNAL_STORAGE" (Raw: "android.permission.READ_EXTERNAL_STORAGE") 32 E: uses-permission (line=4) 33 A: android:name(0x01010003)="android.permission.ACCESS_FINE_LOCATION" (Raw: "android.permission.ACCESS_FINE_LOCATION") 34 E: application (line=5) 35 E: activity (line=6) 36 A: android:name(0x01010003)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName") 37 A: android:exported(0x01010010)=(type 0x12)0xffffffff 38 E: service (line=7) 39 A: android:name(0x01010001)="org.chromium.RandomService" (Raw: "org.chromium.RandomService") 40 A: android:isolatedProcess(0x01010888)=(type 0x12)0xffffffff 41 E: activity (line=173) 42 A: android:name(0x01010003)=".MainActivity" (Raw: ".MainActivity") 43 E: intent-filter (line=177) 44 E: action (line=178) 45 A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN") 46 E: category (line=180) 47 A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT") 48 E: category (line=181) 49 A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER") 50 E: activity-alias (line=173) 51 A: android:name(0x01010003)="org.chromium.ViewActivity" (Raw: "org.chromium.ViewActivity") 52 A: android:targetActivity(0x01010202)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName") 53 E: intent-filter (line=191) 54 E: action (line=192) 55 A: android:name(0x01010003)="android.intent.action.VIEW" (Raw: "android.intent.action.VIEW") 56 E: data (line=198) 57 A: android:scheme(0x01010027)="http" (Raw: "http") 58 E: data (line=199) 59 A: android:scheme(0x01010027)="https" (Raw: "https") 60 E: meta-data (line=43) 61 A: android:name(0x01010003)="name1" (Raw: "name1") 62 A: android:value(0x01010024)="value1" (Raw: "value1") 63 E: meta-data (line=43) 64 A: android:name(0x01010003)="name2" (Raw: "name2") 65 A: android:value(0x01010024)="value2" (Raw: "value2") 66 E: instrumentation (line=8) 67 A: android:label(0x01010001)="abc" (Raw: "abc") 68 A: android:name(0x01010003)="org.chromium.RandomJUnit4TestRunner" (Raw: "org.chromium.RandomJUnit4TestRunner") 69 A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge") 70 A: junit4=(type 0x12)0xffffffff (Raw: "true") 71 E: instrumentation (line=9) 72 A: android:label(0x01010001)="abc" (Raw: "abc") 73 A: android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner") 74 A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge") 75""" 76 77_NO_ISOLATED_SERVICES = """N: android=http://schemas.android.com/apk/res/android 78 E: manifest (line=1) 79 A: package="org.chromium.abc" (Raw: "org.chromium.abc") 80 E: application (line=5) 81 E: activity (line=6) 82 A: android:name(0x01010003)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName") 83 A: android:exported(0x01010010)=(type 0x12)0xffffffff 84 E: service (line=7) 85 A: android:name(0x01010001)="org.chromium.RandomService" (Raw: "org.chromium.RandomService") 86""" 87 88_NO_SERVICES = """N: android=http://schemas.android.com/apk/res/android 89 E: manifest (line=1) 90 A: package="org.chromium.abc" (Raw: "org.chromium.abc") 91 E: application (line=5) 92 E: activity (line=6) 93 A: android:name(0x01010003)="org.chromium.ActivityName" (Raw: "org.chromium.ActivityName") 94 A: android:exported(0x01010010)=(type 0x12)0xffffffff 95""" 96 97_NO_APPLICATION = """N: android=http://schemas.android.com/apk/res/android 98 E: manifest (line=1) 99 A: package="org.chromium.abc" (Raw: "org.chromium.abc") 100""" 101 102_SINGLE_INSTRUMENTATION_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android 103 E: manifest (line=1) 104 A: package="org.chromium.xyz" (Raw: "org.chromium.xyz") 105 E: instrumentation (line=8) 106 A: android:label(0x01010001)="xyz" (Raw: "xyz") 107 A: android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner") 108 A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge") 109""" 110 111_SINGLE_J4_INSTRUMENTATION_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android 112 E: manifest (line=1) 113 A: package="org.chromium.xyz" (Raw: "org.chromium.xyz") 114 E: instrumentation (line=8) 115 A: android:label(0x01010001)="xyz" (Raw: "xyz") 116 A: android:name(0x01010003)="org.chromium.RandomJ4TestRunner" (Raw: "org.chromium.RandomJ4TestRunner") 117 A: android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge") 118 A: junit4=(type 0x12)0xffffffff (Raw: "true") 119""" 120 121_TARGETING_PRE_RELEASE_Q_MANIFEST_DUMP = """N: android=http://schemas.android.com/apk/res/android 122 E: manifest (line=1) 123 A: package="org.chromium.xyz" (Raw: "org.chromium.xyz") 124 E: uses-sdk (line=2) 125 A: android:minSdkVersion(0x0101020c)="Q" (Raw: "Q") 126 A: android:targetSdkVersion(0x01010270)="Q" (Raw: "Q") 127""" 128 129_NO_NAMESPACE_MANIFEST_DUMP = """E: manifest (line=1) 130 A: package="org.chromium.xyz" (Raw: "org.chromium.xyz") 131 E: instrumentation (line=8) 132 A: http://schemas.android.com/apk/res/android:label(0x01010001)="xyz" (Raw: "xyz") 133 A: http://schemas.android.com/apk/res/android:name(0x01010003)="org.chromium.RandomTestRunner" (Raw: "org.chromium.RandomTestRunner") 134 A: http://schemas.android.com/apk/res/android:targetPackage(0x01010021)="org.chromium.random_package" (Raw:"org.chromium.random_pacakge") 135""" 136 137# pylint: enable=line-too-long 138 139 140def _MockAaptDump(manifest_dump): 141 return mock.patch( 142 'devil.android.sdk.aapt.Dump', 143 mock.Mock(side_effect=None, return_value=manifest_dump.split('\n'))) 144 145 146def _MockListApkPaths(files): 147 return mock.patch('devil.android.apk_helper.ApkHelper._ListApkPaths', 148 mock.Mock(side_effect=None, return_value=files)) 149 150 151class _MockDeviceUtils(object): 152 def __init__(self): 153 self.product_cpu_abi = abis.ARM_64 154 self.product_cpu_abis = [abis.ARM_64, abis.ARM] 155 self.pixel_density = 500 156 self.build_version_sdk = 28 157 158 def GetLocale(self): 159 # pylint: disable=no-self-use 160 return ('en', 'US') 161 162 def GetFeatures(self): 163 # pylint: disable=no-self-use 164 return [ 165 'android.hardware.wifi', 166 'android.hardware.nfc', 167 ] 168 169 170class ApkHelperTest(mock_calls.TestCase): 171 def testToHelperApk(self): 172 apk = apk_helper.ToHelper('abc.apk') 173 self.assertTrue(isinstance(apk, apk_helper.ApkHelper)) 174 175 def testToHelperApks(self): 176 apk = apk_helper.ToHelper('abc.apks') 177 self.assertTrue(isinstance(apk, apk_helper.ApksHelper)) 178 179 def testToHelperBundleScript(self): 180 apk = apk_helper.ToHelper('abc_bundle') 181 self.assertTrue(isinstance(apk, apk_helper.BundleScriptHelper)) 182 183 def testToHelperSplitApk(self): 184 apk = apk_helper.ToSplitHelper('abc.apk', ['a.apk', 'b.apk']) 185 self.assertTrue(isinstance(apk, apk_helper.SplitApkHelper)) 186 187 def testToHelperSplitException(self): 188 with self.assertRaises(apk_helper.ApkHelperError): 189 apk_helper.ToSplitHelper( 190 apk_helper.ToHelper('abc.apk'), ['a.apk', 'b.apk']) 191 192 def testGetInstrumentationName(self): 193 with _MockAaptDump(_MANIFEST_DUMP): 194 helper = apk_helper.ApkHelper('') 195 with self.assertRaises(apk_helper.ApkHelperError): 196 helper.GetInstrumentationName() 197 198 def testGetActivityName(self): 199 with _MockAaptDump(_MANIFEST_DUMP): 200 helper = apk_helper.ApkHelper('') 201 self.assertEquals(helper.GetActivityName(), 202 'org.chromium.abc.MainActivity') 203 204 def testGetViewActivityName(self): 205 with _MockAaptDump(_MANIFEST_DUMP): 206 helper = apk_helper.ApkHelper('') 207 self.assertEquals(helper.GetViewActivityName(), 208 'org.chromium.ViewActivity') 209 210 def testGetAllInstrumentations(self): 211 with _MockAaptDump(_MANIFEST_DUMP): 212 helper = apk_helper.ApkHelper('') 213 all_instrumentations = helper.GetAllInstrumentations() 214 self.assertEquals(len(all_instrumentations), 2) 215 self.assertEquals(all_instrumentations[0]['android:name'], 216 'org.chromium.RandomJUnit4TestRunner') 217 self.assertEquals(all_instrumentations[1]['android:name'], 218 'org.chromium.RandomTestRunner') 219 220 def testGetPackageName(self): 221 with _MockAaptDump(_MANIFEST_DUMP): 222 helper = apk_helper.ApkHelper('') 223 self.assertEquals(helper.GetPackageName(), 'org.chromium.abc') 224 225 def testGetPermssions(self): 226 with _MockAaptDump(_MANIFEST_DUMP): 227 helper = apk_helper.ApkHelper('') 228 all_permissions = helper.GetPermissions() 229 self.assertEquals(len(all_permissions), 3) 230 self.assertTrue('android.permission.INTERNET' in all_permissions) 231 self.assertTrue( 232 'android.permission.READ_EXTERNAL_STORAGE' in all_permissions) 233 self.assertTrue( 234 'android.permission.ACCESS_FINE_LOCATION' in all_permissions) 235 236 def testGetSplitName(self): 237 with _MockAaptDump(_MANIFEST_DUMP): 238 helper = apk_helper.ApkHelper('') 239 self.assertEquals(helper.GetSplitName(), 'random_split') 240 241 def testHasIsolatedProcesses_noApplication(self): 242 with _MockAaptDump(_NO_APPLICATION): 243 helper = apk_helper.ApkHelper('') 244 self.assertFalse(helper.HasIsolatedProcesses()) 245 246 def testHasIsolatedProcesses_noServices(self): 247 with _MockAaptDump(_NO_SERVICES): 248 helper = apk_helper.ApkHelper('') 249 self.assertFalse(helper.HasIsolatedProcesses()) 250 251 def testHasIsolatedProcesses_oneNotIsolatedProcess(self): 252 with _MockAaptDump(_NO_ISOLATED_SERVICES): 253 helper = apk_helper.ApkHelper('') 254 self.assertFalse(helper.HasIsolatedProcesses()) 255 256 def testHasIsolatedProcesses_oneIsolatedProcess(self): 257 with _MockAaptDump(_MANIFEST_DUMP): 258 helper = apk_helper.ApkHelper('') 259 self.assertTrue(helper.HasIsolatedProcesses()) 260 261 def testGetSingleInstrumentationName(self): 262 with _MockAaptDump(_SINGLE_INSTRUMENTATION_MANIFEST_DUMP): 263 helper = apk_helper.ApkHelper('') 264 self.assertEquals('org.chromium.RandomTestRunner', 265 helper.GetInstrumentationName()) 266 267 def testGetSingleJUnit4InstrumentationName(self): 268 with _MockAaptDump(_SINGLE_J4_INSTRUMENTATION_MANIFEST_DUMP): 269 helper = apk_helper.ApkHelper('') 270 self.assertEquals('org.chromium.RandomJ4TestRunner', 271 helper.GetInstrumentationName()) 272 273 def testGetAllMetadata(self): 274 with _MockAaptDump(_MANIFEST_DUMP): 275 helper = apk_helper.ApkHelper('') 276 self.assertEquals([('name1', 'value1'), ('name2', 'value2')], 277 helper.GetAllMetadata()) 278 279 def testGetVersionCode(self): 280 with _MockAaptDump(_MANIFEST_DUMP): 281 helper = apk_helper.ApkHelper('') 282 self.assertEquals(376300010, helper.GetVersionCode()) 283 284 def testGetVersionName(self): 285 with _MockAaptDump(_MANIFEST_DUMP): 286 helper = apk_helper.ApkHelper('') 287 self.assertEquals('75.0.3763.0', helper.GetVersionName()) 288 289 def testGetMinSdkVersion_integerValue(self): 290 with _MockAaptDump(_MANIFEST_DUMP): 291 helper = apk_helper.ApkHelper('') 292 self.assertEquals('21', helper.GetMinSdkVersion()) 293 294 def testGetMinSdkVersion_stringValue(self): 295 with _MockAaptDump(_TARGETING_PRE_RELEASE_Q_MANIFEST_DUMP): 296 helper = apk_helper.ApkHelper('') 297 self.assertEquals('Q', helper.GetMinSdkVersion()) 298 299 def testGetTargetSdkVersion_integerValue(self): 300 with _MockAaptDump(_MANIFEST_DUMP): 301 helper = apk_helper.ApkHelper('') 302 self.assertEquals('28', helper.GetTargetSdkVersion()) 303 304 def testGetTargetSdkVersion_stringValue(self): 305 with _MockAaptDump(_TARGETING_PRE_RELEASE_Q_MANIFEST_DUMP): 306 helper = apk_helper.ApkHelper('') 307 self.assertEquals('Q', helper.GetTargetSdkVersion()) 308 309 def testGetSingleInstrumentationName_strippedNamespaces(self): 310 with _MockAaptDump(_NO_NAMESPACE_MANIFEST_DUMP): 311 helper = apk_helper.ApkHelper('') 312 self.assertEquals('org.chromium.RandomTestRunner', 313 helper.GetInstrumentationName()) 314 315 def testGetArchitectures(self): 316 AbiPair = collections.namedtuple('AbiPair', ['abi32bit', 'abi64bit']) 317 for abi_pair in [ 318 AbiPair('lib/' + abis.ARM, 'lib/' + abis.ARM_64), 319 AbiPair('lib/' + abis.X86, 'lib/' + abis.X86_64) 320 ]: 321 with _MockListApkPaths([abi_pair.abi32bit]): 322 helper = apk_helper.ApkHelper('') 323 self.assertEquals( 324 set([ 325 os.path.basename(abi_pair.abi32bit), 326 os.path.basename(abi_pair.abi64bit) 327 ]), set(helper.GetAbis())) 328 with _MockListApkPaths([abi_pair.abi32bit, abi_pair.abi64bit]): 329 helper = apk_helper.ApkHelper('') 330 self.assertEquals( 331 set([ 332 os.path.basename(abi_pair.abi32bit), 333 os.path.basename(abi_pair.abi64bit) 334 ]), set(helper.GetAbis())) 335 with _MockListApkPaths([abi_pair.abi64bit]): 336 helper = apk_helper.ApkHelper('') 337 self.assertEquals( 338 set([os.path.basename(abi_pair.abi64bit)]), set(helper.GetAbis())) 339 340 def testGetSplitsApk(self): 341 apk = apk_helper.ToHelper('abc.apk') 342 with apk.GetApkPaths(_MockDeviceUtils()) as apk_paths: 343 self.assertEquals(apk_paths, ['abc.apk']) 344 345 def testGetSplitsApkModulesException(self): 346 apk = apk_helper.ToHelper('abc.apk') 347 with self.assertRaises(apk_helper.ApkHelperError): 348 apk.GetApkPaths(None, modules=['a']) 349 350 def testGetSplitsApks(self): 351 apk = apk_helper.ToHelper('abc.apks') 352 with self.assertCalls( 353 (mock.call.tempfile.mkdtemp(), 354 '/tmp'), 355 (mock.call.devil.android.sdk.bundletool.ExtractApks( 356 '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'], [('en', 'US')], 357 ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28, None)), 358 (mock.call.os.listdir('/tmp'), ['base-master.apk', 'foo-master.apk']), 359 (mock.call.shutil.rmtree('/tmp')), 360 ),\ 361 apk.GetApkPaths(_MockDeviceUtils()) as apk_paths: 362 self.assertEquals(apk_paths, 363 ['/tmp/base-master.apk', '/tmp/foo-master.apk']) 364 365 def testGetSplitsApksWithModules(self): 366 apk = apk_helper.ToHelper('abc.apks') 367 with self.assertCalls( 368 (mock.call.tempfile.mkdtemp(), 369 '/tmp'), 370 (mock.call.devil.android.sdk.bundletool.ExtractApks( 371 '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'], [('en', 'US')], 372 ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28, 373 ['bar'])), 374 (mock.call.os.listdir('/tmp'), 375 ['base-master.apk', 'foo-master.apk', 'bar-master.apk']), 376 (mock.call.shutil.rmtree('/tmp')), 377 ),\ 378 apk.GetApkPaths(_MockDeviceUtils(), ['bar']) as apk_paths: 379 self.assertEquals(apk_paths, [ 380 '/tmp/base-master.apk', '/tmp/foo-master.apk', '/tmp/bar-master.apk' 381 ]) 382 383 def testGetSplitsApksWithAdditionalLocales(self): 384 apk = apk_helper.ToHelper('abc.apks') 385 with self.assertCalls( 386 (mock.call.tempfile.mkdtemp(), 387 '/tmp'), 388 (mock.call.devil.android.sdk.bundletool.ExtractApks( 389 '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'], 390 [('en', 'US'), ('es', 'ES'), ('fr', 'CA')], 391 ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28, None)), 392 (mock.call.os.listdir('/tmp'), 393 ['base-master.apk', 'base-es.apk', 'base-fr.apk']), 394 (mock.call.shutil.rmtree('/tmp')), 395 ),\ 396 apk.GetApkPaths(_MockDeviceUtils(), 397 additional_locales=['es-ES', 'fr-CA']) as apk_paths: 398 self.assertEquals( 399 apk_paths, 400 ['/tmp/base-master.apk', '/tmp/base-es.apk', '/tmp/base-fr.apk']) 401 402 def testGetSplitsApksWithAdditionalLocalesIncorrectFormat(self): 403 apk = apk_helper.ToHelper('abc.apks') 404 with self.assertRaises(apk_helper.ApkHelperError): 405 apk.GetApkPaths(_MockDeviceUtils(), additional_locales=['es']) 406 407 def testGetSplitsSplitApk(self): 408 apk = apk_helper.ToSplitHelper('base.apk', 409 ['split1.apk', 'split2.apk', 'split3.apk']) 410 device = _MockDeviceUtils() 411 with self.assertCalls( 412 (mock.call.devil.android.sdk.split_select.SelectSplits( 413 device, 414 'base.apk', ['split1.apk', 'split2.apk', 'split3.apk'], 415 allow_cached_props=False), ['split2.apk'])),\ 416 apk.GetApkPaths(device) as apk_paths: 417 self.assertEquals(apk_paths, ['base.apk', 'split2.apk']) 418 419 def testGetSplitsBundleScript(self): 420 apk = apk_helper.ToHelper('abc_bundle') 421 device = _MockDeviceUtils() 422 with self.assertCalls( 423 (mock.call.tempfile.mkstemp(suffix='.apks'), (0, '/tmp/abc.apks')), 424 (mock.call.devil.utils.cmd_helper.GetCmdStatusOutputAndError([ 425 'abc_bundle', 'build-bundle-apks', '--output-apks', '/tmp/abc.apks' 426 ]), (0, '', '')), 427 (mock.call.tempfile.mkdtemp(), '/tmp2'), 428 (mock.call.devil.android.sdk.bundletool.ExtractApks( 429 '/tmp2', '/tmp/abc.apks', ['arm64-v8a', 'armeabi-v7a'], 430 [('en', 'US')], ['android.hardware.wifi', 'android.hardware.nfc'], 431 500, 28, ['bar'])), 432 (mock.call.os.listdir('/tmp2'), ['base-master.apk', 'bar-master.apk']), 433 (mock.call.os.path.isfile('/tmp/abc.apks'), True), 434 (mock.call.os.remove('/tmp/abc.apks')), 435 (mock.call.os.path.isfile('/tmp2'), False), 436 (mock.call.os.path.isdir('/tmp2'), True), 437 (mock.call.shutil.rmtree('/tmp2')), 438 ),\ 439 apk.GetApkPaths(device, modules=['bar']) as apk_paths: 440 self.assertEquals(apk_paths, 441 ['/tmp2/base-master.apk', '/tmp2/bar-master.apk']) 442 443 444if __name__ == '__main__': 445 unittest.main(verbosity=2) 446