Merge "Capture bugreport in sandbox rather than parent"
diff --git a/atest/atest.py b/atest/atest.py
index 8611e21..9271a32 100755
--- a/atest/atest.py
+++ b/atest/atest.py
@@ -563,17 +563,17 @@
# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-return-statements
-def main(argv, results_dir):
+def main(argv, results_dir, args):
"""Entry point of atest script.
Args:
argv: A list of arguments.
results_dir: A directory which stores the ATest execution information.
+ args: An argspace.Namespace class instance holding parsed args.
Returns:
Exit code.
"""
- args = _parse_args(argv)
_configure_logging(args.verbose)
_validate_args(args)
metrics_utils.get_start_time()
@@ -602,7 +602,8 @@
mod_info = module_info.ModuleInfo(force_build=args.rebuild_module_info)
if args.rebuild_module_info:
_run_extra_tasks(join=True)
- translator = cli_translator.CLITranslator(module_info=mod_info)
+ translator = cli_translator.CLITranslator(module_info=mod_info,
+ print_cache_msg=not args.clear_cache)
if args.list_modules:
_print_testable_modules(mod_info, args.list_modules)
return constants.EXIT_CODE_SUCCESS
@@ -707,14 +708,16 @@
if __name__ == '__main__':
RESULTS_DIR = make_test_run_dir()
+ ARGS = _parse_args(sys.argv[1:])
with atest_execution_info.AtestExecutionInfo(sys.argv[1:],
- RESULTS_DIR) as result_file:
+ RESULTS_DIR,
+ ARGS) as result_file:
metrics_base.MetricsBase.tool_name = constants.TOOL_NAME
- EXIT_CODE = main(sys.argv[1:], RESULTS_DIR)
+ EXIT_CODE = main(sys.argv[1:], RESULTS_DIR, ARGS)
DETECTOR = bug_detector.BugDetector(sys.argv[1:], EXIT_CODE)
metrics.LocalDetectEvent(
detect_type=constants.DETECT_TYPE_BUG_DETECTED,
result=DETECTOR.caught_result)
if result_file:
- print('Execution detail has saved in %s' % result_file.name)
+ print("Run 'atest --history' to review test result history.")
sys.exit(EXIT_CODE)
diff --git a/atest/atest_execution_info.py b/atest/atest_execution_info.py
index a92fe1f..39cd3c1 100644
--- a/atest/atest_execution_info.py
+++ b/atest/atest_execution_info.py
@@ -100,6 +100,25 @@
pass
+def has_non_test_options(args):
+ """
+ check whether non-test option in the args.
+
+ Args:
+ args: An argspace.Namespace class instance holding parsed args.
+
+ Returns:
+ True, if args has at least one non-test option.
+ False, otherwise.
+ """
+ return (args.collect_tests_only
+ or args.dry_run
+ or args.help
+ or args.history
+ or args.info
+ or args.version)
+
+
class AtestExecutionInfo(object):
"""Class that stores the whole test progress information in JSON format.
@@ -139,12 +158,13 @@
result_reporters = []
- def __init__(self, args, work_dir):
+ def __init__(self, args, work_dir, args_ns):
"""Initialise an AtestExecutionInfo instance.
Args:
args: Command line parameters.
- work_dir : The directory for saving information.
+ work_dir: The directory for saving information.
+ args_ns: An argspace.Namespace class instance holding parsed args.
Returns:
A json format string.
@@ -152,6 +172,7 @@
self.args = args
self.work_dir = work_dir
self.result_file = None
+ self.args_ns = args_ns
def __enter__(self):
"""Create and return information file object."""
@@ -168,7 +189,8 @@
self.result_file.write(AtestExecutionInfo.
_generate_execution_detail(self.args))
self.result_file.close()
- symlink_latest_result(self.work_dir)
+ if not has_non_test_options(self.args_ns):
+ symlink_latest_result(self.work_dir)
main_module = sys.modules.get(_MAIN_MODULE_KEY)
main_exit_code = getattr(main_module, _EXIT_CODE_ATTR,
constants.EXIT_CODE_ERROR)
diff --git a/atest/atest_utils.py b/atest/atest_utils.py
index 9a7178e..f1be007 100644
--- a/atest/atest_utils.py
+++ b/atest/atest_utils.py
@@ -16,6 +16,7 @@
Utility functions for atest.
"""
+
from __future__ import print_function
import hashlib
@@ -36,13 +37,6 @@
from metrics import metrics_base
from metrics import metrics_utils
-try:
- # If PYTHON2
- from urllib2 import urlopen
-except ImportError:
- metrics_utils.handle_exc_and_send_exit_event(
- constants.IMPORT_FAILURE)
- from urllib.request import urlopen
_BASH_RESET_CODE = '\033[0m\n'
# Arbitrary number to limit stdout for failed runs in _run_limited_output.
@@ -204,6 +198,13 @@
# TODO: Also check if we have a slow connection to result server.
if constants.RESULT_SERVER:
try:
+ try:
+ # If PYTHON2
+ from urllib2 import urlopen
+ except ImportError:
+ metrics_utils.handle_exc_and_send_exit_event(
+ constants.IMPORT_FAILURE)
+ from urllib.request import urlopen
urlopen(constants.RESULT_SERVER,
timeout=constants.RESULT_SERVER_TIMEOUT).close()
return True
diff --git a/atest/cli_translator.py b/atest/cli_translator.py
index abd1f85..f11b34b 100644
--- a/atest/cli_translator.py
+++ b/atest/cli_translator.py
@@ -61,14 +61,21 @@
3. If test files found, generate Build Targets and the Run Command.
"""
- def __init__(self, module_info=None):
+ def __init__(self, module_info=None, print_cache_msg=True):
"""CLITranslator constructor
Args:
module_info: ModuleInfo class that has cached module-info.json.
+ print_cache_msg: Boolean whether printing clear cache message or not.
+ True will print message while False won't print.
"""
self.mod_info = module_info
self.enable_file_patterns = False
+ self.msg = ''
+ if print_cache_msg:
+ self.msg = ('(Test info has been cached for speeding up the next '
+ 'run, if test info need to be updated, please add -c '
+ 'to clean the old cache.)')
# pylint: disable=too-many-locals
def _find_test_infos(self, test, tm_test_detail):
@@ -136,9 +143,7 @@
# non-test_mapping tests.
if test_infos and not tm_test_detail:
atest_utils.update_test_info_cache(test, test_infos)
- print('Test info has been cached for speeding up the next run, if '
- 'test info need to be updated, please add -c to clean the '
- 'old cache.')
+ print(self.msg)
return test_infos
def _fuzzy_search_and_msg(self, test, find_test_err_msg):
diff --git a/atest/constants_default.py b/atest/constants_default.py
index 80581db..1df9e2d 100644
--- a/atest/constants_default.py
+++ b/atest/constants_default.py
@@ -182,14 +182,14 @@
# TreeHugger TEST_MAPPING SUITE_PLANS
TEST_MAPPING_SUITES = ['device-tests', 'general-tests']
-# VTS TF
-VTS_TF_MODULE = 'vts-tradefed'
+# VTS10 TF
+VTS_TF_MODULE = 'vts10-tradefed'
-# VTS-Core TF
-VTS_CORE_TF_MODULE = 'vts-core-tradefed'
+# VTS TF
+VTS_CORE_TF_MODULE = 'vts-tradefed'
# VTS suite set
-VTS_CORE_SUITE = 'vts-core'
+VTS_CORE_SUITE = 'vts'
# ATest TF
ATEST_TF_MODULE = 'atest-tradefed'
diff --git a/atest/docs/developer_workflow.md b/atest/docs/developer_workflow.md
index 578ec1b..d3c2a32 100644
--- a/atest/docs/developer_workflow.md
+++ b/atest/docs/developer_workflow.md
@@ -6,7 +6,7 @@
1. [Identify the code you should work on](#identify-the-code-you-should-work-on)
2. [Working on the Python Code](#working-on-the-python-code)
3. [Working on the TradeFed Code](#working-on-the-tradefed-code)
-4. [Working on the VTS-TradeFed Code](#working-on-the-vts-tradefed-code)
+4. [Working on the VTS10-TradeFed Code](#working-on-the-vts10-tradefed-code)
5. [Working on the Robolectric Code](#working-on-the-robolectric-code)
@@ -44,7 +44,7 @@
Each test runner will have a different workflow. Atest currently
supports the following test runners:
- TradeFed
-- VTS-TradeFed
+- VTS10-TradeFed
- Robolectric
@@ -121,22 +121,22 @@
Before submitting code you should run all the TradeFed tests.
-## <a name="working-on-the-vts-tradefed-code">Working on the VTS-TradeFed Code</a>
+## <a name="working-on-the-vts10-tradefed-code">Working on the VTS10-TradeFed Code</a>
-##### Where does the VTS-TradeFed code live?
+##### Where does the VTS10-TradeFed code live?
-The VTS-Tradefed code lives here: `test/vts/tools/vts-tradefed/`
+The VTS10-Tradefed code lives here: `test/vts/tools/vts-tradefed/`
(path relative to android repo root)
##### Writing tests
-You shouldn't need to edit vts-tradefed code, so there is no
-need to write vts-tradefed tests. Reach out to the vts team
+You shouldn't need to edit vts10-tradefed code, so there is no
+need to write vts10 tests. Reach out to the vts team
if you need information on their unittests.
##### Running tests
-Again, you shouldn't need to change vts-tradefed code.
+Again, you shouldn't need to change vts10-tradefed code.
## <a name="working-on-the-robolectric-code">Working on the Robolectric Code</a>
diff --git a/atest/module_info_unittest.py b/atest/module_info_unittest.py
index 2570ab2..4e48977 100755
--- a/atest/module_info_unittest.py
+++ b/atest/module_info_unittest.py
@@ -160,10 +160,10 @@
self.assertFalse(mod_info.is_suite_in_compatibility_suites("cts", info))
info2 = {'compatibility_suites': ["cts"]}
self.assertTrue(mod_info.is_suite_in_compatibility_suites("cts", info2))
- self.assertFalse(mod_info.is_suite_in_compatibility_suites("vts", info2))
- info3 = {'compatibility_suites': ["cts", "vts"]}
+ self.assertFalse(mod_info.is_suite_in_compatibility_suites("vts10", info2))
+ info3 = {'compatibility_suites': ["cts", "vts10"]}
self.assertTrue(mod_info.is_suite_in_compatibility_suites("cts", info3))
- self.assertTrue(mod_info.is_suite_in_compatibility_suites("vts", info3))
+ self.assertTrue(mod_info.is_suite_in_compatibility_suites("vts10", info3))
self.assertFalse(mod_info.is_suite_in_compatibility_suites("ats", info3))
@mock.patch.object(module_info.ModuleInfo, 'is_testable_module')
diff --git a/atest/test_data/test_commands.json b/atest/test_data/test_commands.json
index bdd2390..2b6c008 100644
--- a/atest/test_data/test_commands.json
+++ b/atest/test_data/test_commands.json
@@ -36,7 +36,7 @@
"atest_tradefed.sh template/atest_local_min --template:map test=atest --include-filter hello_world_test --log-level WARN --skip-loading-config-jar --logcat-on-failure --no-enable-granular-attempts"
],
"VtsCodelabHelloWorldTest": [
-"vts-tradefed run commandAndExit vts-staging-default -m VtsCodelabHelloWorldTest --skip-all-system-status-check --skip-preconditions --primary-abi-only"
+"vts10-tradefed run commandAndExit vts-staging-default -m VtsCodelabHelloWorldTest --skip-all-system-status-check --skip-preconditions --primary-abi-only"
],
"aidegen_unittests": [
"atest_tradefed.sh template/atest_local_min --template:map test=atest --atest-log-file-path=/tmp/atest_run_1568627341_v33kdA/log --include-filter aidegen_unittests --log-level WARN"
@@ -56,4 +56,4 @@
"CarMessengerRoboTests": [
"./build/soong/soong_ui.bash --make-mode RunCarMessengerRoboTests"
]
-}
\ No newline at end of file
+}
diff --git a/atest/test_finders/module_finder.py b/atest/test_finders/module_finder.py
index f24e2ee..965688b 100644
--- a/atest/test_finders/module_finder.py
+++ b/atest/test_finders/module_finder.py
@@ -75,26 +75,26 @@
return test_finder_utils.extract_test_from_tests(testable_modules)
def _is_vts_module(self, module_name):
- """Returns True if the module is a vts module, else False."""
+ """Returns True if the module is a vts10 module, else False."""
mod_info = self.module_info.get_module_info(module_name)
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
+ return len(suites) == 1 and 'vts10' in suites
def _update_to_vts_test_info(self, test):
- """Fill in the fields with vts specific info.
+ """Fill in the fields with vts10 specific info.
- We need to update the runner to use the vts runner and also find the
+ We need to update the runner to use the vts10 runner and also find the
test specific dependencies.
Args:
- test: TestInfo to update with vts specific details.
+ test: TestInfo to update with vts10 specific details.
Return:
- TestInfo that is ready for the vts test runner.
+ TestInfo that is ready for the vts10 test runner.
"""
test.test_runner = self._VTS_TEST_RUNNER
config_file = os.path.join(self.root_dir,
@@ -106,7 +106,7 @@
# If we're not an absolute custom out dir, get relative out dir path.
if custom_out_dir is None or not os.path.isabs(custom_out_dir):
out_dir = os.path.relpath(out_dir, self.root_dir)
- vts_out_dir = os.path.join(out_dir, 'vts', 'android-vts', 'testcases')
+ vts_out_dir = os.path.join(out_dir, 'vts10', 'android-vts10', 'testcases')
# Parse dependency of default staging plans.
xml_paths = test_finder_utils.search_integration_dirs(
constants.VTS_STAGING_PLAN,
@@ -116,7 +116,7 @@
for xml_path in xml_paths:
vts_xmls |= test_finder_utils.get_plans_from_vts_xml(xml_path)
for config_file in vts_xmls:
- # Add in vts test build targets.
+ # Add in vts10 test build targets.
test.build_targets |= test_finder_utils.get_targets_from_vts_xml(
config_file, vts_out_dir, self.module_info)
test.build_targets.add('vts-test-core')
@@ -139,7 +139,7 @@
def _process_test_info(self, test):
"""Process the test info and return some fields updated/changed.
- We need to check if the test found is a special module (like vts) and
+ We need to check if the test found is a special module (like vts10) and
update the test_info fields (like test_runner) appropriately.
Args:
@@ -156,7 +156,7 @@
test.module_class = mod_info['class']
test.install_locations = test_finder_utils.get_install_locations(
mod_info['installed'])
- # Check if this is only a vts module.
+ # Check if this is only a vts10 module.
if self._is_vts_module(test.test_name):
return self._update_to_vts_test_info(test)
elif self.module_info.is_robolectric_test(test.test_name):
diff --git a/atest/test_finders/module_finder_unittest.py b/atest/test_finders/module_finder_unittest.py
index e7cbf00..6d11bec 100755
--- a/atest/test_finders/module_finder_unittest.py
+++ b/atest/test_finders/module_finder_unittest.py
@@ -91,11 +91,11 @@
def test_is_vts_module(self):
"""Test _load_module_info_file regular operation."""
mod_name = 'mod'
- is_vts_module_info = {'compatibility_suites': ['vts', 'tests']}
+ is_vts_module_info = {'compatibility_suites': ['vts10', 'tests']}
self.mod_finder.module_info.get_module_info.return_value = is_vts_module_info
self.assertTrue(self.mod_finder._is_vts_module(mod_name))
- is_not_vts_module = {'compatibility_suites': ['vts', 'cts']}
+ is_not_vts_module = {'compatibility_suites': ['vts10', 'cts']}
self.mod_finder.module_info.get_module_info.return_value = is_not_vts_module
self.assertFalse(self.mod_finder._is_vts_module(mod_name))
diff --git a/atest/test_finders/test_finder_utils.py b/atest/test_finders/test_finder_utils.py
index 2e8ec64..a6a2475 100644
--- a/atest/test_finders/test_finder_utils.py
+++ b/atest/test_finders/test_finder_utils.py
@@ -586,7 +586,7 @@
We're going to pull the following bits of info:
- Parse any .apk files listed in the config file.
- - Parse option value for "test-module-name" (for vts tests).
+ - Parse option value for "test-module-name" (for vts10 tests).
- Look for the perf script.
Args:
@@ -627,14 +627,14 @@
def _get_vts_push_group_targets(push_file, rel_out_dir):
- """Retrieve vts push group build targets.
+ """Retrieve vts10 push group build targets.
A push group file is a file that list out test dependencies and other push
group files. Go through the push file and gather all the test deps we need.
Args:
push_file: Name of the push file in the VTS
- rel_out_dir: Abs path to the out dir to help create vts build targets.
+ rel_out_dir: Abs path to the out dir to help create vts10 build targets.
Returns:
Set of string which represent build targets.
@@ -678,7 +678,7 @@
def _get_vts_binary_src_target(value, rel_out_dir):
- """Parse out the vts binary src target.
+ """Parse out the vts10 binary src target.
The value can be in the following pattern:
- {_32bit,_64bit,_IPC32_32bit}::DATA/target (DATA/target)
@@ -727,9 +727,9 @@
option_tags = xml_root.findall('.//include')
if not option_tags:
return plans
- # Currently, all vts xmls live in the same dir :
+ # Currently, all vts10 xmls live in the same dir :
# https://android.googlesource.com/platform/test/vts/+/master/tools/vts-tradefed/res/config/
- # If the vts plans start using folders to organize the plans, the logic here
+ # If the vts10 plans start using folders to organize the plans, the logic here
# should be changed.
xml_dir = os.path.dirname(xml_file)
for tag in option_tags:
@@ -739,9 +739,9 @@
def get_targets_from_vts_xml(xml_file, rel_out_dir, module_info):
- """Parse a vts xml for test dependencies we need to build.
+ """Parse a vts10 xml for test dependencies we need to build.
- We have a separate vts parsing function because we make a big assumption
+ We have a separate vts10 parsing function because we make a big assumption
on the targets (the way they're formatted and what they represent) and we
also create these build targets in a very special manner as well.
The 6 options we're looking for are:
@@ -754,7 +754,7 @@
Args:
module_info: ModuleInfo class used to verify targets are valid modules.
- rel_out_dir: Abs path to the out dir to help create vts build targets.
+ rel_out_dir: Abs path to the out dir to help create vts10 build targets.
xml_file: abs path to xml file.
Returns:
@@ -770,7 +770,7 @@
if module_info.is_module(value):
targets.add(value)
else:
- logging.warning('vts test module (%s) not present in module '
+ logging.warning('vts10 test module (%s) not present in module '
'info, skipping build', value)
elif name == _VTS_BINARY_SRC:
targets.add(_get_vts_binary_src_target(value, rel_out_dir))
@@ -797,7 +797,7 @@
# and then append the _VTS_TEST_FILE value to targets to build.
target = os.path.join(rel_out_dir, value)
# If value is just an APK, specify the path that we expect it to be in
- # e.g. out/host/linux-x86/vts/android-vts/testcases/DATA/app/test_file/test_file.apk
+ # e.g. out/host/linux-x86/vts10/android-vts10/testcases/DATA/app/test_file/test_file.apk
head, _ = os.path.split(value)
if not head:
target = os.path.join(rel_out_dir, _VTS_OUT_DATA_APP_PATH,
diff --git a/atest/test_finders/test_info.py b/atest/test_finders/test_info.py
index d872576..707f49a 100644
--- a/atest/test_finders/test_info.py
+++ b/atest/test_finders/test_info.py
@@ -47,7 +47,7 @@
test_finder: String of test finder.
compatibility_suites: A list of compatibility_suites. It's a
snippet of compatibility_suites in module_info. e.g.
- ["device-tests", "vts-core"]
+ ["device-tests", "vts10"]
"""
self.test_name = test_name
self.test_runner = test_runner
diff --git a/atest/test_runners/atest_tf_test_runner.py b/atest/test_runners/atest_tf_test_runner.py
index 5380423..6f26c6d 100644
--- a/atest/test_runners/atest_tf_test_runner.py
+++ b/atest/test_runners/atest_tf_test_runner.py
@@ -107,7 +107,7 @@
key_path = os.path.join(self.root_dir, ape_api_key)
if ape_api_key and os.path.exists(key_path):
logging.debug('Set APE_API_KEY: %s', ape_api_key)
- os.environ['APE_API_KEY'] = ape_api_key
+ os.environ['APE_API_KEY'] = key_path
else:
logging.debug('APE_API_KEY not set, some GTS tests may fail'
' without authentication.')
diff --git a/atest/test_runners/atest_tf_test_runner_unittest.py b/atest/test_runners/atest_tf_test_runner_unittest.py
index d1968e8..5344ba0 100755
--- a/atest/test_runners/atest_tf_test_runner_unittest.py
+++ b/atest/test_runners/atest_tf_test_runner_unittest.py
@@ -306,13 +306,13 @@
self.tr._try_set_gts_authentication_key()
mock_exist.assert_not_called()
- @mock.patch('constants.GTS_GOOGLE_SERVICE_ACCOUNT')
- @mock.patch('os.path.exists')
- def test_try_set_gts_authentication_key_not_set(self, mock_exist, mock_key):
+ @mock.patch('os.path.join', return_value='/tmp/file_not_exist.json')
+ def test_try_set_gts_authentication_key_not_set(self, _):
"""Test try_set_authentication_key_not_set method."""
- # Test key neither exists nor set by user.
- mock_exist.return_value = False
- mock_key.return_value = ''
+ # Delete the environment variable if it's set. This is fine for this
+ # method because it's for validating the APE_API_KEY isn't set.
+ if os.environ.get('APE_API_KEY'):
+ del os.environ['APE_API_KEY']
self.tr._try_set_gts_authentication_key()
self.assertEqual(os.environ.get('APE_API_KEY'), None)
diff --git a/atest/test_runners/vts_tf_test_runner.py b/atest/test_runners/vts_tf_test_runner.py
index 9e6801b..c1f53e0 100644
--- a/atest/test_runners/vts_tf_test_runner.py
+++ b/atest/test_runners/vts_tf_test_runner.py
@@ -28,15 +28,15 @@
class VtsTradefedTestRunner(atest_tf_test_runner.AtestTradefedTestRunner):
"""TradeFed Test Runner class."""
NAME = 'VtsTradefedTestRunner'
- EXECUTABLE = 'vts-tradefed'
+ EXECUTABLE = 'vts10-tradefed'
_RUN_CMD = ('{exe} run commandAndExit {plan} -m {test} {args}')
- _BUILD_REQ = {'vts-tradefed-standalone'}
+ _BUILD_REQ = {'vts10-tradefed-standalone'}
_DEFAULT_ARGS = ['--skip-all-system-status-check',
'--skip-preconditions',
'--primary-abi-only']
def __init__(self, results_dir, **kwargs):
- """Init stuff for vts tradefed runner class."""
+ """Init stuff for vts10 tradefed runner class."""
super(VtsTradefedTestRunner, self).__init__(results_dir, **kwargs)
self.run_cmd_dict = {'exe': self.EXECUTABLE,
'test': '',
@@ -74,10 +74,10 @@
return ret_code
def _parse_extra_args(self, extra_args):
- """Convert the extra args into something vts-tf can understand.
+ """Convert the extra args into something vts10-tf can understand.
We want to transform the top-level args from atest into specific args
- that vts-tradefed supports. The only arg we take as is is EXTRA_ARG
+ that vts10-tradefed supports. The only arg we take as is is EXTRA_ARG
since that is what the user intentionally wants to pass to the test
runner.
@@ -114,7 +114,7 @@
extra_args: Dict of extra args to add to test run.
Returns:
- A List of strings that contains the vts-tradefed run command.
+ A List of strings that contains the vts10-tradefed run command.
"""
cmds = []
args = self._DEFAULT_ARGS
diff --git a/atest_tradefed.sh b/atest_tradefed.sh
index 4b66fdb..eb6cf53 100755
--- a/atest_tradefed.sh
+++ b/atest_tradefed.sh
@@ -34,7 +34,7 @@
hosttestlib.jar
cts-tradefed.jar
sts-tradefed.jar
- vts-core-tradefed.jar
+ vts-tradefed.jar
vts10-tradefed.jar
csuite-harness.jar
tradefed-isolation.jar
diff --git a/proto/test_record.proto b/proto/test_record.proto
index 96553c7..6f5a6ae 100644
--- a/proto/test_record.proto
+++ b/proto/test_record.proto
@@ -127,4 +127,7 @@
// A free-formed text that can help debugging the issue at hand.
string debug_help_message = 10;
-}
\ No newline at end of file
+
+ // The fully-qualified name of the exception class associated with the error.
+ string error_type = 20;
+}
diff --git a/src/com/android/tradefed/device/DeviceManager.java b/src/com/android/tradefed/device/DeviceManager.java
index 390e636..7e08c4f 100644
--- a/src/com/android/tradefed/device/DeviceManager.java
+++ b/src/com/android/tradefed/device/DeviceManager.java
@@ -108,7 +108,7 @@
*
* <p>serial2 offline
*/
- private static final String DEVICE_LIST_PATTERN = ".*\n(%s)\\s+(device|offline).*";
+ private static final String DEVICE_LIST_PATTERN = ".*\n(%s)\\s+(device|offline|recovery).*";
private Semaphore mConcurrentFlashLock = null;
@@ -619,6 +619,8 @@
if (d != null) {
DeviceEventResponse r = d.handleAllocationEvent(DeviceEvent.FORCE_ALLOCATE_REQUEST);
if (r.stateChanged && r.allocationState == DeviceAllocationState.Allocated) {
+ // Wait for the fastboot state to be updated once to update the IDevice.
+ d.getMonitor().waitForDeviceBootloaderStateUpdate();
return d;
}
}
diff --git a/src/com/android/tradefed/log/BaseLeveledLogOutput.java b/src/com/android/tradefed/log/BaseLeveledLogOutput.java
index c22468d..91219cb 100644
--- a/src/com/android/tradefed/log/BaseLeveledLogOutput.java
+++ b/src/com/android/tradefed/log/BaseLeveledLogOutput.java
@@ -20,6 +20,8 @@
import com.android.tradefed.config.Option;
import com.android.tradefed.log.LogUtil.CLog;
+import com.google.common.collect.ImmutableMap;
+
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -52,6 +54,15 @@
)
private Map<String, LogLevel> mComponentVerbosity = new HashMap<>();
+ @Option(
+ name = "force-verbosity-map",
+ description = "Enforce a pre-set verbosity of some component to avoid extreme logging.")
+ private boolean mEnableForcedVerbosity = true;
+
+ // Add components we have less control over (ddmlib for example) to ensure they don't flood
+ // us. This will still write to the log.
+ private Map<String, LogLevel> mForcedVerbosity = ImmutableMap.of("ddms", LogLevel.WARN);
+
private Map<String, LogLevel> mVerbosityMap = new HashMap<>();
/** Initialize the components filters based on the invocation {@link IConfiguration}. */
@@ -102,12 +113,22 @@
receiver.put(objTag, components.get(component));
}
}
- // Add components we have less control over (ddmlib for example) to ensure they don't flood
- // us. This will still write to the log.
- mVerbosityMap.put("ddms", LogLevel.WARN);
+ if (shouldForceVerbosity()) {
+ mVerbosityMap.putAll(mForcedVerbosity);
+ }
}
/** {@inheritDoc} */
@Override
public abstract ILeveledLogOutput clone();
+
+ /** Whether or not to enforce the verbosity map. */
+ public boolean shouldForceVerbosity() {
+ return mEnableForcedVerbosity;
+ }
+
+ /** Returns the map of the forced verbosity. */
+ public Map<String, LogLevel> getForcedVerbosityMap() {
+ return mForcedVerbosity;
+ }
}
diff --git a/src/com/android/tradefed/log/BaseStreamLogger.java b/src/com/android/tradefed/log/BaseStreamLogger.java
index 5027386..ec5adb6 100644
--- a/src/com/android/tradefed/log/BaseStreamLogger.java
+++ b/src/com/android/tradefed/log/BaseStreamLogger.java
@@ -22,22 +22,10 @@
import java.io.IOException;
import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
/** A {@link ILeveledLogOutput} that directs log messages to an output stream and to stdout. */
public abstract class BaseStreamLogger<OS extends OutputStream> extends BaseLeveledLogOutput {
- /**
- * Map of log tag to a level they are forced at for writing to log file purpose. This ensure
- * that some logs we have less control over can still be regulated.
- */
- private static final Map<String, LogLevel> FORCED_LOG_LEVEL = new HashMap<>();
-
- static {
- FORCED_LOG_LEVEL.put("ddms", LogLevel.WARN);
- }
-
@Option(name = "log-level", description = "the minimum log level to log.")
private LogLevel mLogLevel = LogLevel.DEBUG;
@@ -109,8 +97,8 @@
// Determines whether a message should be written to the output stream.
private boolean shouldWrite(String tag, LogLevel messageLogLevel, LogLevel invocationLogLevel) {
- LogLevel forcedLevel = FORCED_LOG_LEVEL.get(tag);
- if (forcedLevel == null) {
+ LogLevel forcedLevel = getForcedVerbosityMap().get(tag);
+ if (forcedLevel == null || !shouldForceVerbosity()) {
return true;
}
// Use the highest level of our forced and invocation to decide if we should log the
diff --git a/src/com/android/tradefed/testtype/suite/BaseTestSuite.java b/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
index 0c8bc66..550ac65 100644
--- a/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
+++ b/src/com/android/tradefed/testtype/suite/BaseTestSuite.java
@@ -180,6 +180,13 @@
)
private Set<ModuleParameters> mExcludedModuleParameters = new HashSet<>();
+ @Option(
+ name = "fail-on-everything-filtered",
+ description =
+ "Whether or not to fail the invocation in case test filter returns"
+ + " an empty result.")
+ private boolean mFailOnEverythingFiltered = false;
+
private SuiteModuleLoader mModuleRepo;
private Map<String, List<SuiteTestFilter>> mIncludeFiltersParsed = new HashMap<>();
private Map<String, List<SuiteTestFilter>> mExcludeFiltersParsed = new HashMap<>();
@@ -285,7 +292,19 @@
// Finally add the full test cases directory in case there is no special sub-dir.
testsDirectories.add(testsDir);
// Actual loading of the configurations.
- return loadingStrategy(abis, testsDirectories, mSuitePrefix, mSuiteTag);
+ LinkedHashMap<String, IConfiguration> loadedTests =
+ loadingStrategy(abis, testsDirectories, mSuitePrefix, mSuiteTag);
+
+ if (mFailOnEverythingFiltered
+ && loadedTests.isEmpty()
+ && !mIncludeFiltersParsed.isEmpty()) {
+ throw new IllegalStateException(
+ String.format(
+ "Include filter '%s' was specified"
+ + " but resulted in an empty test set.",
+ includeFilter));
+ }
+ return loadedTests;
} catch (DeviceNotAvailableException | FileNotFoundException e) {
throw new RuntimeException(e);
}
diff --git a/tests/src/com/android/tradefed/device/DeviceManagerTest.java b/tests/src/com/android/tradefed/device/DeviceManagerTest.java
index f81d999..3d1d564 100644
--- a/tests/src/com/android/tradefed/device/DeviceManagerTest.java
+++ b/tests/src/com/android/tradefed/device/DeviceManagerTest.java
@@ -201,6 +201,8 @@
EasyMock.expect(mMockIDevice.getSerialNumber()).andStubReturn(DEVICE_SERIAL);
EasyMock.expect(mMockStateMonitor.getSerialNumber()).andStubReturn(DEVICE_SERIAL);
+ mMockStateMonitor.waitForDeviceBootloaderStateUpdate();
+ EasyMock.expectLastCall().anyTimes();
EasyMock.expect(mMockIDevice.isEmulator()).andStubReturn(Boolean.FALSE);
EasyMock.expect(mMockTestDevice.getMacAddress()).andStubReturn(MAC_ADDRESS);
EasyMock.expect(mMockTestDevice.getSimState()).andStubReturn(SIM_STATE);
@@ -1046,6 +1048,67 @@
assertEquals(1, manager.getDeviceList().size());
}
+ /** Ensure that an unavailable device in recovery mode is released properly. */
+ @Test
+ public void testFreeDevice_recovery() {
+ EasyMock.expect(mMockIDevice.isEmulator()).andStubReturn(Boolean.FALSE);
+ EasyMock.expect(mMockIDevice.getState()).andReturn(DeviceState.ONLINE);
+ EasyMock.expect(mMockStateMonitor.waitForDeviceShell(EasyMock.anyLong()))
+ .andReturn(Boolean.TRUE);
+ mMockStateMonitor.setState(TestDeviceState.NOT_AVAILABLE);
+
+ CommandResult stubAdbDevices = new CommandResult(CommandStatus.SUCCESS);
+ stubAdbDevices.setStdout("List of devices attached\nserial\trecovery\n");
+ EasyMock.expect(
+ mMockRunUtil.runTimedCmd(
+ EasyMock.anyLong(), EasyMock.eq("adb"), EasyMock.eq("devices")))
+ .andReturn(stubAdbDevices);
+
+ replayMocks();
+ IManagedTestDevice testDevice = new TestDevice(mMockIDevice, mMockStateMonitor, null);
+ DeviceManager manager = createDeviceManagerNoInit();
+ manager.init(
+ null,
+ null,
+ new ManagedTestDeviceFactory(false, null, null) {
+ @Override
+ public IManagedTestDevice createDevice(IDevice idevice) {
+ mMockTestDevice.setIDevice(idevice);
+ return testDevice;
+ }
+
+ @Override
+ protected CollectingOutputReceiver createOutputReceiver() {
+ return new CollectingOutputReceiver() {
+ @Override
+ public String getOutput() {
+ return "/system/bin/pm";
+ }
+ };
+ }
+
+ @Override
+ public void setFastbootEnabled(boolean enable) {
+ // ignore
+ }
+ });
+
+ mDeviceListener.deviceConnected(mMockIDevice);
+
+ IManagedTestDevice device = (IManagedTestDevice) manager.allocateDevice(mDeviceSelections);
+ assertNotNull(device);
+ // Device becomes unavailable
+ device.setDeviceState(TestDeviceState.NOT_AVAILABLE);
+ // A freed 'unavailable' device becomes UNAVAILABLE state
+ manager.freeDevice(device, FreeDeviceState.UNAVAILABLE);
+ // Ensure device cannot be allocated again
+ ITestDevice device2 = manager.allocateDevice(mDeviceSelections);
+ assertNull(device2);
+ verifyMocks();
+ // We still have the device in the list because device is not lost.
+ assertEquals(1, manager.getDeviceList().size());
+ }
+
/**
* Test that when freeing an Unavailable device that is not in 'adb devices' we correctly remove
* it from our tracking list.
diff --git a/tests/src/com/android/tradefed/testtype/suite/BaseTestSuiteTest.java b/tests/src/com/android/tradefed/testtype/suite/BaseTestSuiteTest.java
index a32553a..0e56621 100644
--- a/tests/src/com/android/tradefed/testtype/suite/BaseTestSuiteTest.java
+++ b/tests/src/com/android/tradefed/testtype/suite/BaseTestSuiteTest.java
@@ -309,6 +309,43 @@
assertTrue(configMap.containsKey("armeabi-v7a suite/stub1"));
}
+ /**
+ * Test for {@link BaseTestSuite#loadTests()} implementation, for configuration with include
+ * filter set to a non-existent test.
+ */
+ @Test
+ public void testLoadTests_emptyFilter() throws Exception {
+ OptionSetter setter = new OptionSetter(mRunner);
+ setter.setOptionValue("include-filter", "Doesntexist");
+ setter.setOptionValue("suite-config-prefix", "suite");
+ setter.setOptionValue("run-suite-tag", "example-suite");
+ LinkedHashMap<String, IConfiguration> configMap = mRunner.loadTests();
+ assertEquals(0, configMap.size());
+ }
+
+ /**
+ * Test for {@link BaseTestSuite#loadTests()} implementation, for configuration with include
+ * filter set to a non-existent test and enabled failure on empty test set.
+ */
+ @Test
+ public void testLoadTests_emptyFilterWithFailOnEmpty() throws Exception {
+ OptionSetter setter = new OptionSetter(mRunner);
+ setter.setOptionValue("include-filter", "Doesntexist");
+ setter.setOptionValue("fail-on-everything-filtered", "true");
+ setter.setOptionValue("suite-config-prefix", "suite");
+ setter.setOptionValue("run-suite-tag", "example-suite");
+ try {
+ mRunner.loadTests();
+ fail("Should have thrown exception");
+ } catch (IllegalStateException ex) {
+ assertEquals(
+ "Include filter '{arm64-v8a Doesntexist=[Doesntexist], armeabi-v7a "
+ + "Doesntexist=[Doesntexist]}' was specified but resulted in "
+ + "an empty test set.",
+ ex.getMessage());
+ }
+ }
+
/** Test that when splitting, the instance of the implementation is used. */
@Test
public void testSplit() throws Exception {