def _test_run(state): '''Execute a single test suite. Args: state: test suite state collection, instance of TestState. Returns: True: if the test case ran successfully and passed. False: if the test case failed or suffered an error. Raises: AssertionError: If an assertion fails. ''' assert state.lldb assert state.lldb_module assert state.test test_failures = state.test.run(state.lldb, state.pid, state.lldb_module) if test_failures: log = util_log.get_logger() for test, err in test_failures: log.error('test %s:%s failed: %r' % (state.name, test, err)) return False return True
def _test_pre_run(state): '''This function is called before a test is executed (setup). Args: state: Test suite state collection, instance of TestState. Returns: True if the pre_run step completed without error. Currently the pre-run will launch the target test binary on the device and attach an lldb-server to it in platform mode. Raises: AssertionError: If an assertion fails. TestSuiteException: Previous processes of this apk required for this test could not be killed. ''' assert state.test assert state.bundle log = util_log.get_logger() log.info('running: {0}'.format(state.name)) # Remove any cached NDK scripts between tests state.bundle.delete_ndk_cache() # query our test case for the remote target app it needs # First try the legacy behaviour try: target_name = state.test.get_bundle_target() warnings.warn( "get_bundle_target() is deprecated and will be removed soon" " - use the `bundle_target` dictionary attribute instead") except AttributeError: try: target_name = state.test.bundle_target[state.bundle_type] except KeyError: raise TestIgnoredException() if target_name is None: # test case doesn't require a remote process to debug return True else: # find the pid of our remote test process state.pid = state.bundle.launch(target_name) if not state.pid: log.error('unable to get pid of target') return False state.android.kill_servers() # spawn lldb platform on the target device state.android.launch_lldb_platform(state.device_port) return True
def _get_test_case_class(module): '''Inspect a test case module and return the test case class. Args: module: A loaded test case module. ''' # We consider only subclasses of TestCase that have `test_` methods` log = util_log.get_logger() log.debug("loading test suites from %r", module) for name, klass in inspect.getmembers(module, inspect.isclass): for attr in dir(klass): if attr.startswith('test_'): log.info("Found test class %r", name) return klass else: log.debug("class %r has no test_ methods", name) return None
def _execute_test(state): '''Execute a test suite. Args: state: The current TestState object. ''' log = util_log.get_logger() state.test.setup(state.android) try: if not _test_pre_run(state): raise TestSuiteException('test_pre_run() failed') if not _test_run(state): raise TestSuiteException('test_run() failed') _test_post_run(state) log.info('Test passed') finally: state.test.post_run() state.test.teardown(state.android)
def main(): '''Test runner entry point.''' # re-open stdout with no buffering sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) android = None timer = None log = None # parse the command line (positional arguments only) truthy = lambda x: x.lower() in ('true', '1') parser = argparse.ArgumentParser( "Run a single RenderScript TestSuite against lldb") for name, formatter in ( ('test_name', str), ('log_file_path', str), ('adb_path', str), ('lldb_server_path_device', str), ('aosp_product_path', str), ('device_port', int), ('device', str), ('print_to_stdout', truthy), ('verbose', truthy), ('wimpy', truthy), ('timeout', int), ('bundle_type', str), ): parser.add_argument(name, type=formatter) args = parser.parse_args() try: # create utility classes harness.util_log.initialise( '%s(%s)' % (args.test_name, args.bundle_type), print_to_stdout=args.print_to_stdout, level=logging.INFO if not args.verbose else logging.DEBUG, file_path=args.log_file_path, file_mode='a') log = util_log.get_logger() log.debug('Logger initialised') android = harness.UtilAndroid(args.adb_path, args.lldb_server_path_device, args.device) # start the timeout counter timer = _initialise_timer(android, args.timeout) # startup lldb and register teardown handler atexit.register(UtilLLDB.stop) UtilLLDB.start() current_test_dir = get_test_dir(args.test_name) # load a test case module test_module = load_py_module( os.path.join(current_test_dir, args.test_name)) # inspect the test module and locate our test case class test_class = _get_test_case_class(test_module) # if our test inherits from TestBaseRemote, check we have a valid device if (hasattr(test_module, "TestBaseRemote") and issubclass(test_class, test_module.TestBaseRemote)): android.validate_device() # create an instance of our test case test_inst = test_class(args.device_port, args.device, timer, args.bundle_type, wimpy=args.wimpy) # instantiate a test target bundle bundle = harness.UtilBundle(android, args.aosp_product_path) # execute the test case try: for _ in range(2): try: # create an lldb instance lldb = UtilLLDB.create_debugger() # create state object to encapsulate instances state = TestState(android=android, bundle=bundle, lldb=lldb, lldb_module=UtilLLDB.get_module(), test=test_inst, pid=None, name=args.test_name, device_port=args.device_port, bundle_type=args.bundle_type) util_warnings.redirect_warnings() _execute_test(state) # tear down the lldb instance UtilLLDB.destroy_debugger(lldb) break except DisconnectedException as error: log.warning(error) log.warning('Trying again.') else: log.fatal('Not trying again, maximum retries exceeded.') raise TestSuiteException('Lost connection to lldb-server') finally: util_warnings.restore_warnings() _quit_test(util_constants.RC_TEST_OK, timer) except AssertionError: if log: log.critical('Internal test suite error', exc_info=1) print('Internal test suite error', file=sys.stderr) _quit_test(util_constants.RC_TEST_FATAL, timer) except TestIgnoredException: if log: log.warn("test ignored") _quit_test(util_constants.RC_TEST_IGNORED, timer) except TestSuiteException as error: if log: log.exception(str(error)) else: print(error, file=sys.stderr) _quit_test(util_constants.RC_TEST_FAIL, timer) # use a global exception handler to be sure that we will # exit safely and correctly except Exception: if log: log.exception('INTERNAL ERROR') else: import traceback print('Exception {0}'.format(traceback.format_exc()), file=sys.stderr) _quit_test(util_constants.RC_TEST_FATAL, timer) finally: if android: android.reset_all_props() if timer: timer.stop()