blob: a83953c6a4bd598f54f43aa5251f3ae684b08f33 [file] [log] [blame]
Simran Basi259a2b52017-06-21 16:14:07 -07001#!/usr/bin/env python
2#
3# Copyright 2017, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
mikehoranbe9102f2017-08-04 16:04:03 -070017"""
18Command line utility for running Android tests through TradeFederation.
Simran Basi259a2b52017-06-21 16:14:07 -070019
20atest helps automate the flow of building test modules across the Android
21code base and executing the tests via the TradeFederation test harness.
22
23atest is designed to support any test types that can be ran by TradeFederation.
24"""
25
mikehoranbe9102f2017-08-04 16:04:03 -070026import logging
Simran Basi259a2b52017-06-21 16:14:07 -070027import os
mikehoranbe9102f2017-08-04 16:04:03 -070028import subprocess
Simran Basi259a2b52017-06-21 16:14:07 -070029import sys
30
mikehoran63d61b42017-07-28 15:28:50 -070031import cli_translator
mikehoran43ed32d2017-08-18 17:13:36 -070032ANDROID_BUILD_TOP = 'ANDROID_BUILD_TOP'
mikehoranbe9102f2017-08-04 16:04:03 -070033EXPECTED_VARS = frozenset([
mikehoran43ed32d2017-08-18 17:13:36 -070034 ANDROID_BUILD_TOP,
35 'ANDROID_TARGET_OUT_TESTCASES',
36 'OUT'])
mikehoran63d61b42017-07-28 15:28:50 -070037EXIT_CODE_ENV_NOT_SETUP = 1
mikehoranbe9102f2017-08-04 16:04:03 -070038EXIT_CODE_BUILD_FAILURE = 2
mikehoran43ed32d2017-08-18 17:13:36 -070039BUILD_CMD = ['make', '-j', '-C', os.environ.get(ANDROID_BUILD_TOP)]
mikehoran75926242017-09-07 11:01:35 -070040TESTS_HELP_TEXT = '''Tests to run.
Simran Basi259a2b52017-06-21 16:14:07 -070041
mikehoran75926242017-09-07 11:01:35 -070042Ways to identify a test:
43MODULE NAME Examples: CtsJankDeviceTestCases
44CLASS NAME Examples: CtsDeviceJankUi, android.jank.cts.ui.CtsDeviceJankUi
45FILE PATH Examples: ., <rel_or_abs_path>/jank, <rel_or_abs_path>/CtsDeviceJankUi.java
46
47NOTE: CLASS NAME and FILE PATH currently will run the entire module, not just the class.
48'''
Simran Basi259a2b52017-06-21 16:14:07 -070049
mikehoran63d61b42017-07-28 15:28:50 -070050def _parse_args(argv):
51 """Parse command line arguments.
52
53 Args:
54 argv: A list of arguments.
55
56 Returns:
57 An argspace.Namespace class instance holding parsed args.
58 """
59 import argparse
60 parser = argparse.ArgumentParser(
mikehoran75926242017-09-07 11:01:35 -070061 description='Build and run Android tests locally.',
62 formatter_class=argparse.RawTextHelpFormatter)
63 parser.add_argument('tests', nargs='+', help=TESTS_HELP_TEXT)
64 parser.add_argument('-v', '--verbose', action='store_true',
mikehoranbe9102f2017-08-04 16:04:03 -070065 help='Display DEBUG level logging.')
mikehoran63d61b42017-07-28 15:28:50 -070066 return parser.parse_args(argv)
67
Simran Basi259a2b52017-06-21 16:14:07 -070068
mikehoranbe9102f2017-08-04 16:04:03 -070069def _configure_logging(verbose):
70 """Configure the logger.
71
72 Args:
73 verbose: A boolean. If true display DEBUG level logs.
74 """
75 if verbose:
76 logging.basicConfig(level=logging.DEBUG)
77 else:
78 logging.basicConfig(level=logging.INFO)
79
80
81def _missing_environment_variables():
82 """Verify the local environment has been set up to run atest.
83
84 Returns:
85 List of strings of any missing environment variables.
86 """
87 missing = filter(None, [x for x in EXPECTED_VARS if not os.environ.get(x)])
88 if missing:
89 logging.error('Local environment doesn\'t appear to have been '
90 'initialized. Did you remember to run lunch? Expected '
91 'Environment Variables: %s.', missing)
92 return missing
93
94
95def build_tests(build_targets, verbose=False):
96 """Shell out and make build_targets.
97
98 Args:
mikehoran43ed32d2017-08-18 17:13:36 -070099 build_targets: A set of strings of build targets to make.
mikehoranbe9102f2017-08-04 16:04:03 -0700100
101 Returns:
102 Boolean of whether build command was successful.
103 """
104 logging.info('Building tests')
mikehoran43ed32d2017-08-18 17:13:36 -0700105 cmd = BUILD_CMD + list(build_targets)
mikehoranbe9102f2017-08-04 16:04:03 -0700106 logging.debug('Executing command: %s', cmd)
107 try:
108 if verbose:
109 subprocess.check_call(cmd, stderr=subprocess.STDOUT)
110 else:
111 # TODO: Save output to a log file.
112 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
113 logging.info('Build successful')
114 return True
115 except subprocess.CalledProcessError as err:
116 logging.error('Error building: %s', build_targets)
117 if err.output:
118 logging.error(err.output)
119 return False
120
121
122def run_tests(run_commands):
123 """Shell out and execute tradefed run commands.
124
125 Args:
126 run_commands: A list of strings of Tradefed run commands.
127 """
128 logging.info('Running tests')
129 # TODO: Build result parser for run command. Until then display raw stdout.
130 for run_command in run_commands:
131 logging.debug('Executing command: %s', run_command)
132 subprocess.check_call(run_command, shell=True, stderr=subprocess.STDOUT)
133
134
Simran Basi259a2b52017-06-21 16:14:07 -0700135def main(argv):
mikehoran63d61b42017-07-28 15:28:50 -0700136 """Entry point of atest script.
Simran Basi259a2b52017-06-21 16:14:07 -0700137
mikehoran63d61b42017-07-28 15:28:50 -0700138 Args:
139 argv: A list of arguments.
Simran Basi259a2b52017-06-21 16:14:07 -0700140 """
mikehoran63d61b42017-07-28 15:28:50 -0700141 args = _parse_args(argv)
mikehoranbe9102f2017-08-04 16:04:03 -0700142 _configure_logging(args.verbose)
143 if _missing_environment_variables():
144 return EXIT_CODE_ENV_NOT_SETUP
mikehoran43ed32d2017-08-18 17:13:36 -0700145 repo_root = os.environ.get(ANDROID_BUILD_TOP)
146 translator = cli_translator.CLITranslator(root_dir=repo_root)
mikehoranbe9102f2017-08-04 16:04:03 -0700147 build_targets, run_commands = translator.translate(args.tests)
148 if not build_tests(build_targets, args.verbose):
149 return EXIT_CODE_BUILD_FAILURE
150 run_tests(run_commands)
mikehoran63d61b42017-07-28 15:28:50 -0700151
Simran Basi259a2b52017-06-21 16:14:07 -0700152
153if __name__ == '__main__':
154 sys.exit(main(sys.argv[1:]))