Android: Store test data in an app's data directory
This CL adds the --store-data-in-app-directory android test harness. Android versions R+ now have scoped storage, meaning apps cannot access all data on the external storage drive. Therefore we will have to store test data in the test app's data directory. Bug: 1324649 Change-Id: Ia539865de6e187efe23c048e9da21f3030c2593e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708044 Reviewed-by: Richard Coles <torne@chromium.org> Reviewed-by: Peter Wen <wnwen@chromium.org> Commit-Queue: Rakib Hasan <rmhasan@google.com> Cr-Commit-Position: refs/heads/main@{#1031519}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c7f505f6d0
commit
eeec896f03
base/test/android/javatests/src/org/chromium/base/test
build/android
@@ -638,6 +638,9 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
|
||||
if (file.getName().equals("lib")) {
|
||||
continue;
|
||||
}
|
||||
if (file.getName().equals("chromium_tests_root")) {
|
||||
continue;
|
||||
}
|
||||
if (file.getName().equals("incremental-install-files")) {
|
||||
continue;
|
||||
}
|
||||
|
@@ -606,6 +606,7 @@ class InstrumentationTestInstance(test_instance.TestInstance):
|
||||
self._data_deps = None
|
||||
self._data_deps_delegate = None
|
||||
self._runtime_deps_path = None
|
||||
self._store_data_in_app_directory = False
|
||||
self._initializeDataDependencyAttributes(args, data_deps_delegate)
|
||||
|
||||
self._annotations = None
|
||||
@@ -782,7 +783,7 @@ class InstrumentationTestInstance(test_instance.TestInstance):
|
||||
self._data_deps = []
|
||||
self._data_deps_delegate = data_deps_delegate
|
||||
self._runtime_deps_path = args.runtime_deps_path
|
||||
|
||||
self._store_data_in_app_directory = args.store_data_in_app_directory
|
||||
if not self._runtime_deps_path:
|
||||
logging.warning('No data dependencies will be pushed.')
|
||||
|
||||
@@ -1001,6 +1002,10 @@ class InstrumentationTestInstance(test_instance.TestInstance):
|
||||
def skia_gold_properties(self):
|
||||
return self._skia_gold_properties
|
||||
|
||||
@property
|
||||
def store_data_in_app_directory(self):
|
||||
return self._store_data_in_app_directory
|
||||
|
||||
@property
|
||||
def store_tombstones(self):
|
||||
return self._store_tombstones
|
||||
|
@@ -84,7 +84,7 @@ def handle_shard_failures_with(on_failure):
|
||||
return decorator
|
||||
|
||||
|
||||
def place_nomedia_on_device(dev, device_root):
|
||||
def place_nomedia_on_device(dev, device_root, run_as=None, as_root=False):
|
||||
"""Places .nomedia file in test data root.
|
||||
|
||||
This helps to prevent system from scanning media files inside test data.
|
||||
@@ -94,8 +94,14 @@ def place_nomedia_on_device(dev, device_root):
|
||||
device_root: Base path on device to place .nomedia file.
|
||||
"""
|
||||
|
||||
dev.RunShellCommand(['mkdir', '-p', device_root], check_return=True)
|
||||
dev.WriteFile('%s/.nomedia' % device_root, 'https://crbug.com/796640')
|
||||
dev.RunShellCommand(['mkdir', '-p', device_root],
|
||||
run_as=run_as,
|
||||
as_root=as_root,
|
||||
check_return=True)
|
||||
dev.WriteFile('%s/.nomedia' % device_root,
|
||||
'https://crbug.com/796640',
|
||||
run_as=run_as,
|
||||
as_root=as_root)
|
||||
|
||||
|
||||
# TODO(1262303): After Telemetry is supported by python3 we can re-add
|
||||
|
@@ -191,14 +191,22 @@ class LocalDeviceInstrumentationTestRun(
|
||||
self._shared_prefs_to_restore = []
|
||||
self._skia_gold_session_manager = None
|
||||
self._skia_gold_work_dir = None
|
||||
self._target_package = _GetTargetPackageName(test_instance.test_apk)
|
||||
|
||||
#override
|
||||
def TestPackage(self):
|
||||
return self._test_instance.suite
|
||||
|
||||
def _GetDataStorageRootDirectory(self, device):
|
||||
if self._test_instance.store_data_in_app_directory:
|
||||
# TODO(rmhasan): Add check to makes sure api level > 27. Selinux
|
||||
# policy on Oreo does not allow app to read files from app data dir
|
||||
# that were not put there by the app.
|
||||
return device.GetApplicationDataDirectory(self._target_package)
|
||||
return device.GetExternalStoragePath()
|
||||
|
||||
#override
|
||||
def SetUp(self):
|
||||
target_package = _GetTargetPackageName(self._test_instance.test_apk)
|
||||
|
||||
@local_device_environment.handle_shard_failures_with(
|
||||
self._env.DenylistDevice)
|
||||
@@ -383,7 +391,7 @@ class LocalDeviceInstrumentationTestRun(
|
||||
cmd = ['am', 'set-debug-app', '--persistent']
|
||||
if self._test_instance.wait_for_java_debugger:
|
||||
cmd.append('-w')
|
||||
cmd.append(target_package)
|
||||
cmd.append(self._target_package)
|
||||
dev.RunShellCommand(cmd, check_return=True)
|
||||
|
||||
@trace_event.traced
|
||||
@@ -418,20 +426,32 @@ class LocalDeviceInstrumentationTestRun(
|
||||
|
||||
@instrumentation_tracing.no_tracing
|
||||
def push_test_data(dev):
|
||||
device_root = posixpath.join(dev.GetExternalStoragePath(),
|
||||
'chromium_tests_root')
|
||||
test_data_root_dir = posixpath.join(
|
||||
self._GetDataStorageRootDirectory(dev), 'chromium_tests_root')
|
||||
host_device_tuples_substituted = [
|
||||
(h, local_device_test_run.SubstituteDeviceRoot(d, device_root))
|
||||
for h, d in host_device_tuples]
|
||||
(h,
|
||||
local_device_test_run.SubstituteDeviceRoot(d, test_data_root_dir))
|
||||
for h, d in host_device_tuples
|
||||
]
|
||||
logging.info('Pushing data dependencies.')
|
||||
for h, d in host_device_tuples_substituted:
|
||||
logging.debug(' %r -> %r', h, d)
|
||||
local_device_environment.place_nomedia_on_device(dev, device_root)
|
||||
|
||||
as_root = self._test_instance.store_data_in_app_directory
|
||||
local_device_environment.place_nomedia_on_device(dev,
|
||||
test_data_root_dir,
|
||||
as_root=as_root)
|
||||
dev.PushChangedFiles(host_device_tuples_substituted,
|
||||
delete_device_stale=True)
|
||||
delete_device_stale=True,
|
||||
as_root=as_root)
|
||||
|
||||
if not host_device_tuples_substituted:
|
||||
dev.RunShellCommand(['rm', '-rf', device_root], check_return=True)
|
||||
dev.RunShellCommand(['mkdir', '-p', device_root], check_return=True)
|
||||
dev.RunShellCommand(['rm', '-rf', test_data_root_dir],
|
||||
check_return=True,
|
||||
as_root=as_root)
|
||||
dev.RunShellCommand(['mkdir', '-p', test_data_root_dir],
|
||||
check_return=True,
|
||||
as_root=as_root)
|
||||
|
||||
@trace_event.traced
|
||||
def create_flag_changer(dev):
|
||||
@@ -493,7 +513,7 @@ class LocalDeviceInstrumentationTestRun(
|
||||
if self._test_instance.wait_for_java_debugger:
|
||||
logging.warning('*' * 80)
|
||||
logging.warning('Waiting for debugger to attach to process: %s',
|
||||
target_package)
|
||||
self._target_package)
|
||||
logging.warning('*' * 80)
|
||||
|
||||
#override
|
||||
@@ -798,6 +818,9 @@ class LocalDeviceInstrumentationTestRun(
|
||||
self._CreateFlagChangerIfNeeded(device)
|
||||
self._flag_changers[str(device)].PushFlags(add=flags_to_add)
|
||||
|
||||
if self._test_instance.store_data_in_app_directory:
|
||||
extras.update({'fetchTestDataFromAppDataDir': 'true'})
|
||||
|
||||
time_ms = lambda: int(time.time() * 1e3)
|
||||
start_ms = time_ms()
|
||||
|
||||
@@ -1084,11 +1107,15 @@ class LocalDeviceInstrumentationTestRun(
|
||||
logging.info('Could not get tests from pickle: %s', e)
|
||||
logging.info('Getting tests by having %s list them.',
|
||||
self._test_instance.junit4_runner_class)
|
||||
# We need to use GetAppWritablePath instead of GetExternalStoragePath
|
||||
# here because we will not have applied legacy storage workarounds on R+
|
||||
# yet.
|
||||
# TODO(rmhasan): Figure out how to create the temp file inside the test
|
||||
# app's data directory. Currently when the temp file is created read
|
||||
# permissions are only given to the app's user id. Therefore we can't
|
||||
# pull the file from the device.
|
||||
def list_tests(d):
|
||||
def _run(dev):
|
||||
# We need to use GetAppWritablePath instead of GetExternalStoragePath
|
||||
# here because we will not have applied legacy storage workarounds on R+
|
||||
# yet.
|
||||
with device_temp_file.DeviceTempFile(
|
||||
dev.adb, suffix='.json',
|
||||
dir=dev.GetAppWritablePath()) as dev_test_list_json:
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
|
||||
import unittest
|
||||
import mock # pylint: disable=import-error
|
||||
|
||||
from pylib.base import base_test_result
|
||||
from pylib.base import mock_environment
|
||||
@@ -164,6 +165,33 @@ class LocalDeviceInstrumentationTestRunTest(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
local_device_instrumentation_test_run._ReplaceUncommonChars(original)
|
||||
|
||||
def testStoreDataInAppDir(self):
|
||||
env = mock.MagicMock()
|
||||
test_instance = mock.MagicMock()
|
||||
test_instance.store_data_in_app_directory = True
|
||||
device = mock.MagicMock()
|
||||
|
||||
device.GetApplicationDataDirectory.return_value = 'app_dir'
|
||||
device.GetExternalStoragePath.return_value = 'external_dir'
|
||||
test_run = (
|
||||
local_device_instrumentation_test_run.LocalDeviceInstrumentationTestRun(
|
||||
env, test_instance))
|
||||
self.assertEqual(test_run._GetDataStorageRootDirectory(device), 'app_dir')
|
||||
|
||||
def testStoreDataInExternalDir(self):
|
||||
env = mock.MagicMock()
|
||||
test_instance = mock.MagicMock()
|
||||
test_instance.store_data_in_app_directory = False
|
||||
device = mock.MagicMock()
|
||||
|
||||
device.GetApplicationDataDirectory.return_value = 'app_dir'
|
||||
device.GetExternalStoragePath.return_value = 'external_dir'
|
||||
test_run = (
|
||||
local_device_instrumentation_test_run.LocalDeviceInstrumentationTestRun(
|
||||
env, test_instance))
|
||||
self.assertEqual(test_run._GetDataStorageRootDirectory(device),
|
||||
'external_dir')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
@@ -471,6 +471,11 @@ def AddInstrumentationTestOptions(parser):
|
||||
parser.add_argument(
|
||||
'--apk-under-test',
|
||||
help='Path or name of the apk under test.')
|
||||
parser.add_argument(
|
||||
'--store-data-in-app-directory',
|
||||
action='store_true',
|
||||
help='Store test data in the application\'s data directory. By default '
|
||||
'the test data is stored in the external storage folder.')
|
||||
parser.add_argument(
|
||||
'--module',
|
||||
action='append',
|
||||
|
Reference in New Issue
Block a user