Home | History | Annotate | Download | only in lldb
      1 #!/usr/bin/env python
      2 
      3 # Copyright (C) 2016 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 
     17 '''Main test suite execution script.'''
     18 import argparse
     19 import inspect
     20 import logging
     21 import os
     22 import signal
     23 import subprocess
     24 import sys
     25 import time
     26 import collections
     27 import xml.etree.ElementTree as ET
     28 
     29 from config import Config
     30 from tests.harness import util_constants
     31 from tests.harness.exception import TestSuiteException, FailFastException
     32 from tests.harness import UtilAndroid
     33 from tests.harness import UtilBundle
     34 from tests.harness import util_log
     35 from tests.harness.util_functions import load_py_module
     36 from tests.harness.decorators import deprecated
     37 
     38 # For some reason pylint is not able to understand the class returned by
     39 # from util_log.get_logger() and generates a lot of false warnings
     40 #pylint: disable=maybe-no-member
     41 
     42 EMU_PROC = None
     43 
     44 def _parse_args():
     45     '''Parse the command line arguments.
     46 
     47     Returns:
     48         A namespace object that contains the options specified to run_tests on
     49         the command line.
     50     '''
     51 
     52     parser = argparse.ArgumentParser(description='Run the test suite.')
     53 
     54     parser.add_argument('--config', '-c',
     55                         metavar='path',
     56                         help='Path to a custom config file.')
     57     parser.add_argument('--device', '-d',
     58                         help='Specify the device id of the device to test on.')
     59     parser.add_argument('--test', '-t',
     60                         metavar='path',
     61                         help='Specify a specific test to run.')
     62     group = parser.add_mutually_exclusive_group()
     63     group.add_argument('--wimpy', '-w',
     64                         action='store_true',
     65                         default=None,
     66                         help='Test only a core subset of features.')
     67     group.add_argument('--app-types',
     68                         default=['java', 'cpp', 'jni'],
     69                         nargs='*',
     70                         help='Specify a list of Android app types against which'
     71                              ' to run the tests',
     72                         dest='bundle_types')
     73     parser.add_argument('--install-only',
     74                         action='store_true',
     75                         default=False,
     76                         help='It only runs the pre-run stage of the test suite.'
     77                              ' It installs the required APKs but does not '
     78                              'execute the tests.',
     79                         dest='install_only')
     80     parser.add_argument('--no-install', '-n',
     81                         action='store_true',
     82                         default=False,
     83                         help='Stop the test suite installing apks to device.',
     84                         dest='noinstall')
     85     parser.add_argument('--no-uninstall',
     86                         action='store_true',
     87                         default=False,
     88                         help='Stop the test suite uninstalling apks after '
     89                              'completion.',
     90                         dest='nouninstall')
     91     parser.add_argument('--print-to-stdout',
     92                         action='store_true',
     93                         default=False,
     94                         help='Print all logging information to standard out.',
     95                         dest='print_to_stdout')
     96     parser.add_argument('--verbose', '-v',
     97                         action='store_true',
     98                         default=None,
     99                         help='Store extra info in the log.')
    100     parser.add_argument('--fail-fast',
    101                         action='store_true',
    102                         default=False,
    103                         help='Exit the test suite immediately on the first failure.')
    104     parser.add_argument('--run-emu',
    105                         action='store_true',
    106                         default=None,
    107                         help='Spawn an emulator and run the test suite on that.'
    108                              ' Specify the emulator command line in the config'
    109                              ' file or with -emu-cmd.',
    110                         dest='run_emu')
    111 
    112     # Get the properties of the Config class and add a command line argument
    113     # for each.
    114     this_module = sys.modules[__name__]
    115     for member_name, member_obj in inspect.getmembers(Config):
    116         if (inspect.isdatadescriptor(member_obj) and
    117             member_name not in ['__weakref__', 'device', 'verbose']):
    118 
    119             # List type properties can take one or more arguments
    120             num_args = None
    121             if (isinstance(member_obj, property)
    122                 and isinstance(member_obj.fget(Config), list)):
    123                 num_args = '+'
    124 
    125             opt_name = member_name.replace('_', '-')
    126 
    127             setattr(this_module, opt_name, '')
    128 
    129             parser.add_argument('--' + opt_name,
    130                                 nargs=num_args,
    131                                 help=member_obj.__doc__,
    132                                 dest=member_name)
    133 
    134     return parser.parse_args()
    135 
    136 
    137 def _choice(first_choice, second_choice):
    138     '''Return first_choice if it is not None otherwise return second_choice.
    139 
    140     Args:
    141         first_choice: The first choice value.
    142         second_choice: The alternative value.
    143 
    144     Returns:
    145         The first argument if it is not None, and the second otherwise.
    146     '''
    147     return first_choice if first_choice else second_choice
    148 
    149 
    150 class State(object):
    151     '''This class manages all objects required by the test suite.'''
    152 
    153     # pylint: disable=too-many-instance-attributes
    154     # Since this is a state class many attributes are expected.
    155 
    156     def __init__(self):
    157         '''State constructor.
    158 
    159         Raises:
    160             TestSuiteException: When unable to load config file.
    161 
    162             AssertionError: When assertions fail.
    163         '''
    164 
    165         # Parse the command line options
    166         args = _parse_args()
    167 
    168         # create a config instance
    169         if args.config:
    170             # use the user supplied
    171             config = State.load_user_configuration(args.config)
    172         else:
    173             # use the default configuration
    174             config = Config()
    175 
    176         # save the test blacklist
    177         self.blacklist = _choice(args.blacklist, config.blacklist)
    178 
    179         # Allow any of the command line arguments to override the
    180         # values in the config file.
    181         self.adb_path = _choice(args.adb_path, config.adb_path)
    182 
    183         self.host_port = int(_choice(args.host_port, config.host_port))
    184 
    185         self.device = _choice(args.device, config.device)
    186 
    187         self.user_specified_device = self.device
    188 
    189         self.device_port = int(_choice(args.device_port, config.device_port))
    190 
    191         self.lldb_server_path_device = _choice(args.lldb_server_path_device,
    192                                                config.lldb_server_path_device)
    193 
    194         self.lldb_server_path_host = _choice(args.lldb_server_path_host,
    195                                              config.lldb_server_path_host)
    196 
    197         self.aosp_product_path = _choice(args.aosp_product_path,
    198                                          config.aosp_product_path)
    199 
    200         self.log_file_path = _choice(args.log_file_path, config.log_file_path)
    201 
    202         self.results_file_path = _choice(args.results_file_path,
    203                                          config.results_file_path)
    204 
    205         self.lldb_path = _choice(args.lldb_path, config.lldb_path)
    206         self.print_to_stdout = args.print_to_stdout
    207         self.verbose = _choice(args.verbose, config.verbose)
    208         self.timeout = int(_choice(args.timeout, config.timeout))
    209         self.emu_cmd = _choice(args.emu_cmd, config.emu_cmd)
    210         self.run_emu = args.run_emu
    211         self.wimpy = args.wimpy
    212         self.bundle_types = args.bundle_types if not self.wimpy else ['java']
    213         self.fail_fast = args.fail_fast
    214 
    215         # validate the param "verbose"
    216         if not isinstance(self.verbose, bool):
    217             raise TestSuiteException('The parameter "verbose" should be a '
    218                                      'boolean: {0}'.format(self.verbose))
    219 
    220         # create result array
    221         self.results = dict()
    222         self.single_test = args.test
    223 
    224         # initialise the logging facility
    225         log_level = logging.INFO if not self.verbose else logging.DEBUG
    226         util_log.initialise("driver",
    227                             print_to_stdout=self.print_to_stdout,
    228                             level=log_level,
    229                             file_mode='w', # open for write
    230                             file_path=self.log_file_path
    231                             )
    232         log = util_log.get_logger()
    233 
    234         if self.run_emu and not self.emu_cmd:
    235             log.TestSuiteException('Need to specify --emu-cmd (or specify a'
    236                 ' value in the config file) if using --run-emu.')
    237 
    238         # create a results file
    239         self.results_file = open(self.results_file_path, 'w')
    240 
    241         # create an android helper object
    242         self.android = UtilAndroid(self.adb_path,
    243                                    self.lldb_server_path_device,
    244                                    self.device)
    245         assert self.android
    246 
    247         # create a test bundle
    248         self.bundle = UtilBundle(self.android,
    249                                  self.aosp_product_path)
    250         assert self.bundle
    251 
    252         # save the no pushing option
    253         assert isinstance(args.noinstall, bool)
    254         self.noinstall = args.noinstall
    255 
    256         assert isinstance(args.nouninstall, bool)
    257         self.nouninstall = args.nouninstall
    258 
    259         # install only option
    260         assert type(args.install_only) is bool
    261         self.install_only = args.install_only
    262         if self.install_only:
    263             log.log_and_print('Option --install-only set. The test APKs will '
    264                               'be installed on the device but the tests will '
    265                               'not be executed.')
    266             if self.noinstall:
    267                 raise TestSuiteException('Conflicting options given: '
    268                                          '--install-only and --no-install')
    269 
    270         # TCP port modifier which is used to increment the port number used for
    271         # each test case to avoid collisions.
    272         self.port_mod = 0
    273 
    274         # total number of test files that have been executed
    275         self.test_count = 0
    276 
    277     def get_android(self):
    278         '''Return the android ADB helper instance.
    279 
    280         Returns:
    281             The android ADB helper, instance of UtilAndroid.
    282         '''
    283         assert self.android
    284         return self.android
    285 
    286     def get_bundle(self):
    287         '''Return the test executable bundle.
    288 
    289         Returns:
    290             The test exectable collection, instance of UtilBundle.
    291         '''
    292         return self.bundle
    293 
    294     def add_result(self, name, app_type, result):
    295         '''Add a test result to the collection.
    296 
    297         Args:
    298             name: String name of the test that has executed.
    299             app_type: type of app i.e. java, jni, or cpp
    300             result: String result of the test, "pass", "fail", "error".
    301         '''
    302         key = (name, app_type)
    303         assert key not in self.results
    304         self.results[key] = result
    305 
    306     def get_single_test(self):
    307         '''Get the name of the single test to run.
    308 
    309         Returns:
    310             A string that is the name of the python file containing the test to
    311             be run. If all tests are to be run this returns None.
    312         '''
    313         return self.single_test
    314 
    315     @staticmethod
    316     def load_user_configuration(path):
    317         '''Load the test suite config from the give path.
    318 
    319         Instantiate the Config class found in the module at the given path.
    320         If no suitable class is available, it raises a TestSuiteException.
    321 
    322         Args:
    323             path: String location of the module.
    324 
    325         Returns:
    326             an instance of the Config class, defined in the module.
    327 
    328         Raises:
    329             TestSuiteException: when unable to import the module or when a
    330                                 subclass of Config is not found inside it.
    331         '''
    332 
    333         # load the module
    334         config_module = load_py_module(path)
    335         if not config_module:
    336             raise TestSuiteException('Unable to import the module from "%s"'
    337                                      % (path))
    338 
    339         # look for a subclass of Config
    340         for name, value in inspect.getmembers(config_module):
    341             if (inspect.isclass(value)
    342                 and name != 'Config'
    343                 and issubclass(value, Config)):
    344                 # that's our candidate
    345                 return value()
    346 
    347         # otherwise there are no valid candidates
    348         raise TestSuiteException('The provided user configuration is not '
    349                                  'valid. The module must define a subclass '
    350                                  'of Config')
    351 
    352 
    353 def _kill_emulator():
    354     ''' Kill the emulator process. '''
    355     global EMU_PROC
    356     if EMU_PROC:
    357         try:
    358             EMU_PROC.terminate()
    359         except OSError:
    360             # can't kill a dead proc
    361             log = util_log.get_logger()
    362             log.debug('Trying to kill an emulator but it is already dead.')
    363 
    364 
    365 def _check_emulator_terminated():
    366     ''' Throw an exception if the emulator process has ended.
    367 
    368     Raises:
    369         TestSuiteException: If the emulator process has ended.
    370     '''
    371     global EMU_PROC
    372     assert EMU_PROC
    373     if EMU_PROC.poll():
    374         stdout, stderr = EMU_PROC.communicate()
    375         raise TestSuiteException('The emulator terminated with output:'
    376             '\nstderr: {0}\nstdout: {1}.'.format(stderr, stdout))
    377 
    378 
    379 @deprecated()
    380 def _launch_emulator(state):
    381     '''Launch the emulator and wait for it to boot.
    382 
    383     Args:
    384         emu_cmd: The command line to run the emulator.
    385 
    386     Raises:
    387         TestSuiteException: If an emulator already exists or the emulator
    388                             process terminated before we could connect to it, or
    389                             we failed to copy lldb-server to the emulator.
    390     '''
    391     global EMU_PROC
    392     android = state.android
    393     if state.user_specified_device:
    394         if android.device_with_substring_exists(state.user_specified_device):
    395             raise TestSuiteException(
    396                 'A device with name {0} already exists.',
    397                 state.user_specified_device)
    398     else:
    399         if android.device_with_substring_exists('emulator'):
    400             raise TestSuiteException('An emulator already exists.')
    401 
    402     assert state.emu_cmd
    403     EMU_PROC = subprocess.Popen(state.emu_cmd.split(),
    404                                 stdout=None,
    405                                 stderr=subprocess.STDOUT)
    406 
    407     log = util_log.get_logger()
    408     log.info('Launching emulator with command line {0}'.format(state.emu_cmd))
    409 
    410     tries_number = 180
    411     tries = tries_number
    412     found_device = False
    413     while not found_device:
    414         try:
    415             android.validate_device(False, 'emulator')
    416             found_device = True
    417         except TestSuiteException as ex:
    418             tries -= 1
    419             if tries == 0:
    420                 # Avoid infinitely looping if the emulator won't boot
    421                 log.warning(
    422                     'Giving up trying to validate device after {0} tries.'
    423                     .format(tries_number))
    424                 raise ex
    425             _check_emulator_terminated()
    426             # wait a bit and try again, maybe it has now booted
    427             time.sleep(10)
    428 
    429     tries = 500
    430     while not android.is_booted():
    431         tries -= 1
    432         if tries == 0:
    433             # Avoid infinitely looping if the emulator won't boot
    434             raise TestSuiteException('The emulator has failed to boot.')
    435         _check_emulator_terminated()
    436         time.sleep(5)
    437 
    438     # Need to be root before we can push lldb-server
    439     android.adb_root()
    440     android.wait_for_device()
    441 
    442     # Push the lldb-server executable to the device.
    443     output = android.adb('push {0} {1}'.format(state.lldb_server_path_host,
    444                                                state.lldb_server_path_device))
    445 
    446     if 'failed to copy' in output or 'No such file or directory' in output:
    447         raise TestSuiteException(
    448             'unable to push lldb-server to the emulator: {0}.'
    449             .format(output))
    450 
    451     output = android.shell('chmod a+x {0}'
    452                            .format(state.lldb_server_path_device))
    453 
    454     if 'No such file or directory' in output:
    455         raise TestSuiteException('Failed to copy lldb-server to the emulator.')
    456 
    457 
    458 def _restart_emulator(state):
    459     '''Kill the emulator and start a new instance.
    460 
    461     Args:
    462         state: Test suite state collection, instance of State.
    463     '''
    464     _kill_emulator()
    465     _launch_emulator(state)
    466 
    467 
    468 def _run_test(state, name, bundle_type):
    469     '''Execute a single test case.
    470 
    471     Args:
    472         state: Test suite state collection, instance of State.
    473         name: String file name of the test to execute.
    474         bundle_type: string for the installed app type (cpp|jni|java)
    475 
    476     Raises:
    477         AssertionError: When assertion fails.
    478     '''
    479     assert isinstance(name, str)
    480 
    481     try:
    482         state.android.check_adb_alive()
    483     except TestSuiteException as expt:
    484         global EMU_PROC
    485         if EMU_PROC:
    486             _restart_emulator(state)
    487         else:
    488             raise expt
    489 
    490     log = util_log.get_logger()
    491     sys.stdout.write('Running {0}\r'.format(name))
    492     sys.stdout.flush()
    493     log.info('Running {0}'.format(name))
    494 
    495     run_tests_dir = os.path.dirname(os.path.realpath(__file__))
    496     run_test_path = os.path.join(run_tests_dir, 'tests', 'run_test.py')
    497 
    498     # Forward port for lldb-server on the device to our host
    499     hport = int(state.host_port) + state.port_mod
    500     dport = int(state.device_port) + state.port_mod
    501     state.android.forward_port(hport, dport)
    502     state.port_mod += 1
    503 
    504     log.debug('Giving up control to {0}...'.format(name))
    505 
    506     params = map(str, [
    507         sys.executable,
    508         run_test_path,
    509         name,
    510         state.log_file_path,
    511         state.adb_path,
    512         state.lldb_server_path_device,
    513         state.aosp_product_path,
    514         dport,
    515         state.android.get_device_id(),
    516         state.print_to_stdout,
    517         state.verbose,
    518         state.wimpy,
    519         state.timeout,
    520         bundle_type
    521     ])
    522 
    523     return_code = subprocess.call(params)
    524     state.test_count += 1
    525     state.android.remove_port_forwarding()
    526     log.seek_to_end()
    527 
    528     # report in sys.stdout the result
    529     success = return_code == util_constants.RC_TEST_OK
    530     status_handlers = collections.defaultdict(lambda: ('error', log.error), (
    531             (util_constants.RC_TEST_OK, ('pass', log.info)),
    532             (util_constants.RC_TEST_TIMEOUT, ('timeout', log.error)),
    533             (util_constants.RC_TEST_IGNORED, ('ignored', log.info)),
    534             (util_constants.RC_TEST_FAIL, ('fail', log.critical))
    535         )
    536     )
    537     status_name, status_logger = status_handlers[return_code]
    538     log.info('Running %s: %s', name, status_name.upper())
    539     status_logger("Test %r: %s", name, status_name)
    540 
    541     # Special case for ignored tests - just return now
    542     if return_code == util_constants.RC_TEST_IGNORED:
    543         return
    544 
    545     state.add_result(name, bundle_type, status_name)
    546 
    547     if state.fail_fast and not success:
    548         raise FailFastException(name)
    549 
    550     # print a running total pass rate
    551     passes = sum(1 for key, value in state.results.items() if value == 'pass')
    552     log.info('Current pass rate: %s of %s executed.', passes, len(state.results))
    553 
    554 
    555 def _check_lldbserver_exists(state):
    556     '''Check lldb-server exists on the target device and it is executable.
    557 
    558     Raises:
    559         TestSuiteError: If lldb-server does not exist on the target.
    560     '''
    561     assert state
    562 
    563     message = 'Unable to verify valid lldb-server on target'
    564 
    565     android = state.get_android()
    566     assert android
    567 
    568     cmd = state.lldb_server_path_device
    569     out = android.shell(cmd, False)
    570     if not isinstance(out, str):
    571         raise TestSuiteException(message)
    572     if out.find('Usage:') < 0:
    573         raise TestSuiteException(message)
    574 
    575 
    576 def _suite_pre_run(state):
    577     '''This function is executed before the test cases are run (setup).
    578 
    579     Args:
    580         state: Test suite state collection, instance of State.
    581 
    582     Return:
    583         True if the pre_run step completes without error.
    584         Checks made:
    585             - Validating that adb exists and runs.
    586             - Validating that a device is attached.
    587             - We have root access to the device.
    588             - All test binaries were pushed to the device.
    589             - The port for lldb-server was forwarded correctly.
    590 
    591     Raises:
    592         AssertionError: When assertions fail.
    593     '''
    594     assert state
    595     log = util_log.get_logger()
    596 
    597     try:
    598         android = state.get_android()
    599         bundle = state.get_bundle()
    600         assert android
    601         assert bundle
    602 
    603         # validate ADB helper class
    604         android.validate_adb()
    605         log.log_and_print('Located ADB')
    606 
    607         if state.run_emu:
    608             log.log_and_print('Launching emulator...')
    609             _launch_emulator(state)
    610             log.log_and_print('Started emulator ' + android.device)
    611         else:
    612             android.validate_device()
    613             log.log_and_print('Located device ' + android.device)
    614 
    615         if state.noinstall and not state.single_test:
    616             bundle.check_apps_installed(state.wimpy)
    617 
    618         # elevate to root user
    619         android.adb_root()
    620         android.wait_for_device()
    621         # check that lldb-server exists on device
    622         android.kill_servers()
    623         _check_lldbserver_exists(state)
    624 
    625         if not state.noinstall:
    626             # push all tests to the device
    627             log.log_and_print('Pushing all tests...')
    628             bundle.push_all()
    629             log.log_and_print('Pushed all tests')
    630         log.log_and_print('Pre run complete')
    631 
    632     except TestSuiteException as expt:
    633         log.exception('Test suite pre run failure')
    634 
    635         # Even if we are logging the error, it may be helpful and more
    636         # immediate to find out the error into the terminal
    637         log.log_and_print('ERROR: Unable to set up the test suite: %s\n'
    638                           % expt.message, logging.ERROR)
    639 
    640         return False
    641     return True
    642 
    643 
    644 def _suite_post_run(state):
    645     '''This function is executed after the test cases have run (teardown).
    646 
    647     Args:
    648         state: Test suite state collection, instance of State.
    649     Returns:
    650         Number of failures
    651     '''
    652     log = util_log.get_logger()
    653 
    654     if not state.noinstall and not state.nouninstall:
    655         if state.wimpy:
    656             state.bundle.uninstall_all_apk()
    657         else:
    658             state.bundle.uninstall_all()
    659         log.log_and_print('Uninstalled/Deleted all tests')
    660 
    661     total = 0
    662     passes = 0
    663     failures = 0
    664 
    665     results = ET.Element('testsuite')
    666     results.attrib['name'] = 'LLDB RS Test Suite'
    667 
    668     for key, value in state.results.items():
    669         total += 1
    670         if value == 'pass':
    671             passes += 1
    672         else:
    673             failures += 1
    674 
    675         # test case name, followed by pass, failure or error elements
    676         testcase = ET.Element('testcase')
    677         testcase.attrib['name'] = "%s:%s" % key
    678         result_element = ET.Element(value)
    679         result_element.text = "%s:%s" % key
    680         testcase.append(result_element)
    681         results.append(testcase)
    682 
    683     assert passes + failures == total, 'Invalid test results status'
    684     if failures:
    685         log.log_and_print(
    686             'The following failures occurred:\n%s\n' %
    687             '\n'.join('failed: %s:%s' % test_spec
    688                 for test_spec, result in state.results.items() if result != 'pass'
    689         ))
    690 
    691     log.log_and_print('{0} of {1} passed'.format(passes, total))
    692     if total:
    693         log.log_and_print('{0}% rate'.format((passes*100)/total))
    694 
    695     results.attrib['tests'] = str(total)
    696     state.results_file.write(ET.tostring(results, encoding='iso-8859-1'))
    697 
    698     return failures
    699 
    700 
    701 def _discover_tests(state):
    702     '''Discover all tests in the tests directory.
    703 
    704     Returns:
    705         List of strings, test file names from the 'tests' directory.
    706     '''
    707     tests = []
    708 
    709     single_test = state.get_single_test()
    710     if single_test is None:
    711         file_dir = os.path.dirname(os.path.realpath(__file__))
    712         tests_dir = os.path.join(file_dir, 'tests')
    713 
    714         for sub_dir in os.listdir(tests_dir):
    715             current_test_dir = os.path.join(tests_dir, sub_dir)
    716             if os.path.isdir(current_test_dir):
    717                 dir_name = os.path.basename(current_test_dir)
    718 
    719                 if dir_name == 'harness':
    720                     continue
    721 
    722                 for item in os.listdir(current_test_dir):
    723                     if (item.startswith('test')
    724                         and item.endswith('.py')
    725                         and not item in state.blacklist):
    726                         tests.append(item)
    727     else:
    728         if single_test.endswith('.py'):
    729             tests.append(single_test)
    730         else:
    731             tests.append(single_test + '.py')
    732 
    733     return tests
    734 
    735 
    736 def _deduce_python_path(state):
    737     '''Try to deduce the PYTHONPATH environment variable via the LLDB binary.
    738 
    739     Args:
    740         state: Test suite state collection, instance of State.
    741 
    742     Returns:
    743         True if PYTHONPATH has been updated, False otherwise.
    744 
    745     Raises:
    746         TestSuiteException: If lldb path provided in the config or command line
    747                             is incorrect.
    748         AssertionError: If an assertion fails.
    749     '''
    750 
    751     lldb_path = state.lldb_path
    752     if not lldb_path:
    753         # lldb may not be provided in preference of a manual $PYTHONPATH
    754         return False
    755 
    756     params = [lldb_path, '-P']
    757 
    758     try:
    759         proc = subprocess.Popen(params, stdout=subprocess.PIPE)
    760     except OSError as err:
    761         error_string = 'Could not run lldb at %s: %s' % (lldb_path, str(err))
    762         raise TestSuiteException(error_string)
    763 
    764     stdout = proc.communicate()[0]
    765     if stdout:
    766         os.environ['PYTHONPATH'] = stdout.strip()
    767         return True
    768 
    769     return False
    770 
    771 
    772 def main():
    773     '''The lldb-renderscript test suite entry point.'''
    774     log = None
    775 
    776     try:
    777         # parse the command line
    778         state = State()
    779         assert state
    780 
    781         # logging is initialised in State()
    782         log = util_log.get_logger()
    783 
    784         # if we can, set PYTHONPATH for lldb bindings
    785         if not _deduce_python_path(state):
    786             log.log_and_print('Unable to deduce PYTHONPATH', logging.WARN)
    787 
    788         # pre run step
    789         if not _suite_pre_run(state):
    790             raise TestSuiteException('Test suite pre-run step failed')
    791         # discover all tests and execute them
    792         tests = _discover_tests(state)
    793         log.log_and_print('Found {0} tests'.format(len(tests)))
    794         if state.install_only:
    795             log.log_and_print('Test applications installed. Terminating due to '
    796                               '--install-only option')
    797         else:
    798             # run the tests
    799             for bundle_type in state.bundle_types:
    800                 log.info("Running bundle type '%s'", bundle_type)
    801                 for item in tests:
    802                     _run_test(state, item, bundle_type)
    803                 # post run step
    804             quit(0 if _suite_post_run(state) == 0 else 1)
    805 
    806     except AssertionError:
    807         if log:
    808             log.exception('Internal test suite error')
    809 
    810         print('Internal test suite error')
    811         quit(1)
    812 
    813     except FailFastException:
    814         log.exception('Early exit after first test failure')
    815         quit(1)
    816 
    817     except TestSuiteException as error:
    818         if log:
    819             log.exception('Test suite exception')
    820 
    821         print('{0}'.format(str(error)))
    822         quit(2)
    823 
    824     finally:
    825         _kill_emulator()
    826         logging.shutdown()
    827 
    828 def signal_handler(_, _unused):
    829     '''Signal handler for SIGINT, caused by the user typing Ctrl-C.'''
    830     # pylint: disable=unused-argument
    831     # pylint: disable=protected-access
    832     print('Ctrl+C!')
    833     os._exit(1)
    834 
    835 
    836 # execution trampoline
    837 if __name__ == '__main__':
    838     signal.signal(signal.SIGINT, signal_handler)
    839     main()
    840