atest: Add v1 support for roboelectric tests.
- Added in a new test runner: RobolectricTestRunner
- Updated atest_utils.build to take in env var to set while building.
- Updated build step to also require build_targets to be non-empty. That
way we can skip the build step for robolectric tests.
Bug: 73345500
Test: atest AliasLoaderTest
atest ./ (cwd: //packages/apps/KeyChain/robotests/src/com/android/keychain)
atest KeyChainRoboTests
atest RunKeyChainRoboTests
atest KeyChainServiceRoboTest.java
atest --wait-for-debugger AliasLoaderTest
atest hello_world_test
atest example/reboot
atest VtsCodelabHelloWorldTest
Change-Id: I1d4b6ef49550315763a349703bbf5e5d16fa76f7
diff --git a/atest/test_finders/module_finder.py b/atest/test_finders/module_finder.py
index 299db37..fd4c4f8 100644
--- a/atest/test_finders/module_finder.py
+++ b/atest/test_finders/module_finder.py
@@ -27,6 +27,7 @@
import test_finder_base
import test_finder_utils
from test_runners import atest_tf_test_runner
+from test_runners import robolectric_test_runner
from test_runners import vts_tf_test_runner
_JAVA_EXT = '.java'
@@ -47,6 +48,7 @@
"""Module finder class."""
NAME = 'MODULE'
_TEST_RUNNER = atest_tf_test_runner.AtestTradefedTestRunner.NAME
+ _ROBOLECTRIC_RUNNER = robolectric_test_runner.RobolectricTestRunner.NAME
_VTS_TEST_RUNNER = vts_tf_test_runner.VtsTradefedTestRunner.NAME
def __init__(self, module_info=None):
@@ -54,10 +56,49 @@
self.root_dir = os.environ.get(constants.ANDROID_BUILD_TOP)
self.module_info = module_info
+ def _is_testable_module(self, mod_info):
+ """Check if module is something we can test.
+
+ A module is testable if:
+ - it's installed.
+ - it's a robolectric module (or shares path with one).
+
+ Args:
+ mod_info: Dict of module info to check.
+
+ Returns:
+ True if we can test this module, False otherwise.
+ """
+ if not mod_info:
+ return False
+ # TODO: b/77286797
+ if mod_info.get('installed'):
+ return True
+ if self._is_robolectric_test(mod_info.get(constants.MODULE_NAME)):
+ return True
+ return False
+
+ def _get_first_testable_module(self, path):
+ """Returns first testable module given module path.
+
+ Args:
+ path: String path of module to look for.
+
+ Returns:
+ String of first installed module name.
+ """
+ for mod in self.module_info.get_module_names(path):
+ mod_info = self.module_info.get_module_info(mod)
+ if self._is_testable_module(mod_info):
+ return mod_info.get(constants.MODULE_NAME)
+ return None
+
def _is_vts_module(self, module_name):
"""Returns True if the module is a vts module, else False."""
mod_info = self.module_info.get_module_info(module_name)
- suites = mod_info.get('compatibility_suites', [])
+ suites = []
+ if mod_info:
+ suites = mod_info.get('compatibility_suites', [])
# Pull out all *ts (cts, tvts, etc) suites.
suites = [suite for suite in suites if suite not in _SUITES_TO_IGNORE]
return len(suites) == 1 and 'vts' in suites
@@ -93,6 +134,63 @@
test.build_targets.add(test.test_name)
return test
+ def _get_robolectric_test_name(self, module_name):
+ """Returns run robolectric module.
+
+ There are at least 2 modules in every robolectric module path, return
+ the module that we can run as a build target.
+
+ Arg:
+ module_name: String of module.
+
+ Returns:
+ String of module that is the run robolectric module, None if none
+ could be found.
+ """
+ module_name_info = self.module_info.get_module_info(module_name)
+ if not module_name_info:
+ return None
+ for mod in self.module_info.get_module_names(
+ module_name_info.get(constants.MODULE_PATH, [])[0]):
+ mod_info = self.module_info.get_module_info(mod)
+ if test_finder_utils.is_robolectric_module(mod_info):
+ return mod
+ return None
+
+ def _is_robolectric_test(self, module_name):
+ """Check if module is a robolectric test.
+
+ A module can be a robolectric test if the specified module has their
+ class set as ROBOLECTRIC (or shares their path with a module that does).
+
+ Args:
+ module_name: String of module to check.
+
+ Returns:
+ True if the module is a robolectric module, else False.
+ """
+ # Check 1, module class is ROBOLECTRIC
+ mod_info = self.module_info.get_module_info(module_name)
+ if mod_info and test_finder_utils.is_robolectric_module(mod_info):
+ return True
+ # Check 2, shared modules in the path have class ROBOLECTRIC_CLASS.
+ if self._get_robolectric_test_name(module_name):
+ return True
+ return False
+
+ def _update_to_robolectric_test_info(self, test):
+ """Update the fields for a robolectric test.
+
+ Args:
+ test: TestInfo to be updated with robolectric fields.
+
+ Returns:
+ TestInfo with robolectric fields.
+ """
+ test.test_runner = self._ROBOLECTRIC_RUNNER
+ test.test_name = self._get_robolectric_test_name(test.test_name)
+ return test
+
def _process_test_info(self, test):
"""Process the test info and return some fields updated/changed.
@@ -108,6 +206,11 @@
# Check if this is only a vts module.
if self._is_vts_module(test.test_name):
return self._update_to_vts_test_info(test)
+ elif self._is_robolectric_test(test.test_name):
+ return self._update_to_robolectric_test_info(test)
+ module_name = test.test_name
+ rel_config = test.data[constants.TI_REL_CONFIG]
+ test.build_targets = self._get_build_targets(module_name, rel_config)
return test
def _is_auto_gen_test_config(self, module_name):
@@ -153,14 +256,15 @@
Returns:
A populated TestInfo namedtuple if found, else None.
"""
- info = self.module_info.get_module_info(module_name)
- if info and info.get('installed'):
+ mod_info = self.module_info.get_module_info(module_name)
+ if self._is_testable_module(mod_info):
# path is a list with only 1 element.
- rel_config = os.path.join(info['path'][0], constants.MODULE_CONFIG)
+ rel_config = os.path.join(mod_info['path'][0],
+ constants.MODULE_CONFIG)
return self._process_test_info(test_info.TestInfo(
test_name=module_name,
test_runner=self._TEST_RUNNER,
- build_targets=self._get_build_targets(module_name, rel_config),
+ build_targets=set(),
data={constants.TI_REL_CONFIG: rel_config,
constants.TI_FILTER: frozenset()}))
return None
@@ -204,12 +308,12 @@
self.root_dir, test_dir, self.module_info)
rel_config = os.path.join(rel_module_dir, constants.MODULE_CONFIG)
if not module_name:
- module_name = self.module_info.get_module_name(os.path.dirname(
+ module_name = self._get_first_testable_module(os.path.dirname(
rel_config))
return self._process_test_info(test_info.TestInfo(
test_name=module_name,
test_runner=self._TEST_RUNNER,
- build_targets=self._get_build_targets(module_name, rel_config),
+ build_targets=set(),
data={constants.TI_FILTER: test_filter,
constants.TI_REL_CONFIG: rel_config}))
@@ -266,12 +370,12 @@
self.root_dir, package_path, self.module_info)
rel_config = os.path.join(rel_module_dir, constants.MODULE_CONFIG)
if not module_name:
- module_name = self.module_info.get_module_name(
+ module_name = self._get_first_testable_module(
os.path.dirname(rel_config))
return self._process_test_info(test_info.TestInfo(
test_name=module_name,
test_runner=self._TEST_RUNNER,
- build_targets=self._get_build_targets(module_name, rel_config),
+ build_targets=set(),
data={constants.TI_FILTER: test_filter,
constants.TI_REL_CONFIG: rel_config}))
@@ -321,7 +425,7 @@
self.root_dir, dir_path, self.module_info)
if not rel_module_dir:
return None
- module_name = self.module_info.get_module_name(rel_module_dir)
+ module_name = self._get_first_testable_module(rel_module_dir)
rel_config = os.path.join(rel_module_dir, constants.MODULE_CONFIG)
data = {constants.TI_REL_CONFIG: rel_config,
constants.TI_FILTER: frozenset()}
@@ -348,5 +452,5 @@
return self._process_test_info(test_info.TestInfo(
test_name=module_name,
test_runner=self._TEST_RUNNER,
- build_targets=self._get_build_targets(module_name, rel_config),
+ build_targets=set(),
data=data))