def create_output_excerpts(self, test_info): """Convenient method for creating excerpts of adb logcat. This moves the current content of `self.adb_logcat_file_path` to the log directory specific to the current test. Call this method at the end of: `setup_class`, `teardown_test`, and `teardown_class`. Args: test_info: `self.current_test_info` in a Mobly test. Returns: List of strings, the absolute paths to excerpt files. """ self.pause() dest_path = test_info.output_path utils.create_dir(dest_path) filename = self._ad.generate_filename(self.OUTPUT_FILE_TYPE, test_info, 'txt') excerpt_file_path = os.path.join(dest_path, filename) shutil.move(self.adb_logcat_file_path, excerpt_file_path) self._ad.log.debug('logcat excerpt created at: %s', excerpt_file_path) self.resume() return [excerpt_file_path]
def test_cat_adb_log_with_unicode(self, mock_timestamp_getter, stop_proc_mock, start_proc_mock, FastbootProxy, MockAdbProxy): """Verifies that AndroidDevice.cat_adb_log loads the correct adb log file, locates the correct adb log lines within the given time range, and writes the lines to the correct output file. """ mock_serial = '1' ad = android_device.AndroidDevice(serial=mock_serial) logcat_service = logcat.Logcat(ad) logcat_service._enable_logpersist() # Direct the log path of the ad to a temp dir to avoid racing. logcat_service._ad._log_path = self.tmp_dir # Expect error if attempted to cat adb log before starting adb logcat. expected_msg = ('.* Attempting to cat adb log when none' ' has been collected.') with self.assertRaisesRegex(logcat.Error, expected_msg): logcat_service.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME) logcat_service.start() utils.create_dir(ad.log_path) mock_adb_log_path = logcat_service.adb_logcat_file_path with io.open(mock_adb_log_path, 'w', encoding='utf-8') as f: f.write(MOCK_ADB_UNICODE_LOGCAT) cat_file_path = logcat_service.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME) with io.open(cat_file_path, 'r', encoding='utf-8') as f: actual_cat = f.read() self.assertEqual(actual_cat, ''.join(MOCK_ADB_UNICODE_LOGCAT_CAT_RESULT)) # Stops adb logcat. logcat_service.stop()
def start_adb_logcat(self): """Starts a standing adb logcat collection in separate subprocesses and save the logcat in a file. """ if self._adb_logcat_process: raise DeviceError( self, 'Logcat thread is already running, cannot start another one.') # Disable adb log spam filter for rootable. Have to stop and clear # settings first because 'start' doesn't support --clear option before # Android N. if self.is_rootable: self.adb.shell('logpersist.stop --clear') self.adb.shell('logpersist.start') f_name = 'adblog,%s,%s.txt' % (self.model, self.serial) utils.create_dir(self.log_path) logcat_file_path = os.path.join(self.log_path, f_name) try: extra_params = self.adb_logcat_param except AttributeError: extra_params = '-b all' cmd = 'adb -s %s logcat -v threadtime %s >> %s' % ( self.serial, extra_params, logcat_file_path) self._adb_logcat_process = utils.start_standing_subprocess(cmd) self.adb_logcat_file_path = logcat_file_path
def test_AndroidDevice_cat_adb_log_with_unicode( self, mock_timestamp_getter, stop_proc_mock, start_proc_mock, FastbootProxy, MockAdbProxy): """Verifies that AndroidDevice.cat_adb_log loads the correct adb log file, locates the correct adb log lines within the given time range, and writes the lines to the correct output file. """ mock_serial = '1' ad = android_device.AndroidDevice(serial=mock_serial) # Direct the log path of the ad to a temp dir to avoid racing. ad._log_path_base = self.tmp_dir # Expect error if attempted to cat adb log before starting adb logcat. expected_msg = ('.* Attempting to cat adb log when none' ' has been collected.') with self.assertRaisesRegex(android_device.Error, expected_msg): ad.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME) ad.start_adb_logcat() utils.create_dir(ad.log_path) mock_adb_log_path = os.path.join(ad.log_path, 'adblog,%s,%s.txt' % (ad.model, ad.serial)) with io.open(mock_adb_log_path, 'w', encoding='utf-8') as f: f.write(MOCK_ADB_UNICODE_LOGCAT) ad.cat_adb_log('some_test', MOCK_ADB_LOGCAT_BEGIN_TIME) cat_file_path = os.path.join( ad.log_path, 'AdbLogExcerpts', ('some_test,02-29 14-02-20.123,%s,%s.txt') % (ad.model, ad.serial)) with io.open(cat_file_path, 'r', encoding='utf-8') as f: actual_cat = f.read() self.assertEqual(actual_cat, ''.join(MOCK_ADB_UNICODE_LOGCAT_CAT_RESULT)) # Stops adb logcat. ad.stop_adb_logcat()
def start_adb_logcat(self, clear_log=True): """Starts a standing adb logcat collection in separate subprocesses and save the logcat in a file. This clears the previous cached logcat content on device. Args: clear: If True, clear device log before starting logcat. """ if self._adb_logcat_process: raise DeviceError( self, 'Logcat thread is already running, cannot start another one.') if clear_log: self._clear_adb_log() # Disable adb log spam filter for rootable devices. Have to stop and # clear settings first because 'start' doesn't support --clear option # before Android N. if self.is_rootable: self.adb.shell('logpersist.stop --clear') self.adb.shell('logpersist.start') f_name = 'adblog,%s,%s.txt' % (self.model, self.serial) utils.create_dir(self.log_path) logcat_file_path = os.path.join(self.log_path, f_name) try: extra_params = self.adb_logcat_param except AttributeError: extra_params = '' cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % (adb.ADB, self.serial, extra_params, logcat_file_path) process = utils.start_standing_subprocess(cmd, shell=True) self._adb_logcat_process = process self.adb_logcat_file_path = logcat_file_path
def __init__(self, configs): """Constructor of BaseTestClass. The constructor takes a config_parser.TestRunConfig object and which has all the information needed to execute this test class, like log_path and controller configurations. For details, see the definition of class config_parser.TestRunConfig. Args: configs: A config_parser.TestRunConfig object. """ self.tests = [] class_identifier = self.__class__.__name__ if configs.test_class_name_suffix: class_identifier = '%s_%s' % (class_identifier, configs.test_class_name_suffix) if self.TAG is None: self.TAG = class_identifier # Set params. self.root_output_path = configs.log_path self.log_path = os.path.join(self.root_output_path, class_identifier) utils.create_dir(self.log_path) # Deprecated, use 'testbed_name' self.test_bed_name = configs.test_bed_name self.testbed_name = configs.testbed_name self.user_params = configs.user_params self.results = records.TestResult() self.summary_writer = configs.summary_writer self._generated_test_table = collections.OrderedDict() self._controller_manager = controller_manager.ControllerManager( class_name=self.TAG, controller_configs=configs.controller_configs) self.controller_configs = self._controller_manager.controller_configs
def start_adb_logcat(self, clear_log=True): """Starts a standing adb logcat collection in separate subprocesses and save the logcat in a file. This clears the previous cached logcat content on device. Args: clear: If True, clear device log before starting logcat. """ if self._adb_logcat_process: raise DeviceError( self, 'Logcat thread is already running, cannot start another one.') if clear_log: self._clear_adb_log() self._enable_logpersist() f_name = 'adblog,%s,%s.txt' % (self.model, self._normalized_serial) utils.create_dir(self.log_path) logcat_file_path = os.path.join(self.log_path, f_name) try: extra_params = self.adb_logcat_param except AttributeError: extra_params = '' cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % ( adb.ADB, self.serial, extra_params, logcat_file_path) process = utils.start_standing_subprocess(cmd, shell=True) self._adb_logcat_process = process self.adb_logcat_file_path = logcat_file_path
def create_output_excerpts(self, test_info): """Convenient method for creating excerpts of adb logcat. This copies logcat lines from self.adb_logcat_file_path to an excerpt file, starting from the location where the previous excerpt ended. Call this method at the end of: `setup_class`, `teardown_test`, and `teardown_class`. Args: test_info: `self.current_test_info` in a Mobly test. Returns: List of strings, the absolute paths to excerpt files. """ dest_path = test_info.output_path utils.create_dir(dest_path) filename = self._ad.generate_filename(self.OUTPUT_FILE_TYPE, test_info, 'txt') excerpt_file_path = os.path.join(dest_path, filename) with io.open(excerpt_file_path, 'w', encoding='utf-8', errors='replace') as out: while True: line = self._adb_logcat_file_obj.readline() if not line: break out.write(line) self._ad.log.debug('logcat excerpt created at: %s', excerpt_file_path) return [excerpt_file_path]
def take_bug_report(self, test_name=None, begin_time=None, timeout=300, destination=None): """Takes a bug report on the device and stores it in a file. Args: test_name: Name of the test method that triggered this bug report. begin_time: Timestamp of when the test started. If not set, then this will default to the current time. timeout: float, the number of seconds to wait for bugreport to complete, default is 5min. destination: string, path to the directory where the bugreport should be saved. Returns: A string that is the absolute path to the bug report on the host. """ prefix = DEFAULT_BUG_REPORT_NAME if test_name: prefix = '%s,%s' % (DEFAULT_BUG_REPORT_NAME, test_name) if begin_time is None: begin_time = mobly_logger.get_log_file_timestamp() new_br = True try: stdout = self.adb.shell('bugreportz -v').decode('utf-8') # This check is necessary for builds before N, where adb shell's ret # code and stderr are not propagated properly. if 'not found' in stdout: new_br = False except adb.AdbError: new_br = False if destination is None: destination = os.path.join(self.log_path, 'BugReports') br_path = utils.abs_path(destination) utils.create_dir(br_path) filename = self.generate_filename(prefix, str(begin_time), 'txt') if new_br: filename = filename.replace('.txt', '.zip') full_out_path = os.path.join(br_path, filename) # in case device restarted, wait for adb interface to return self.wait_for_boot_completion() self.log.debug('Start taking bugreport.') if new_br: out = self.adb.shell('bugreportz', timeout=timeout).decode('utf-8') if not out.startswith('OK'): raise DeviceError(self, 'Failed to take bugreport: %s' % out) br_out_path = out.split(':')[1].strip() self.adb.pull([br_out_path, full_out_path]) else: # shell=True as this command redirects the stdout to a local file # using shell redirection. self.adb.bugreport(' > "%s"' % full_out_path, shell=True, timeout=timeout) self.log.debug('Bugreport taken at %s.', full_out_path) return full_out_path
def setup_test_logger(log_path, prefix=None, alias='latest'): """Customizes the root logger for a test run. In addition to configuring the Mobly logging handlers, this also sets two attributes on the `logging` module for the output directories: root_output_path: path to the directory for the entire test run. log_path: same as `root_output_path` outside of a test class run. In the context of a test class run, this is the output directory for files specific to a test class. Args: log_path: string, the location of the report file. prefix: optional string, a prefix for each log line in terminal. alias: optional string, The name of the alias to use for the latest log directory. If a falsy value is provided, then the alias directory will not be created, which is useful to save storage space when the storage system (e.g. ZIP files) does not properly support shortcut/symlinks. """ utils.create_dir(log_path) _setup_test_logger(log_path, prefix) logging.info('Test output folder: "%s"', log_path) if alias: create_latest_log_alias(log_path, alias=alias)
def run(self): """Executes tests. This will instantiate controller and test classes, execute tests, and print a summary. This meethod should usually be called within the runner's `mobly_logger` context. If you must use this method outside of the context, you should make sure `self._test_run_metadata.generate_test_run_log_path` is called before each invocation of `run`. Raises: Error: if no tests have previously been added to this runner using add_test_class(...). """ if not self._test_run_infos: raise Error('No tests to execute.') # Officially starts the test run. self._test_run_metadata.set_start_point() # Ensure the log path exists. Necessary if `run` is used outside of the # `mobly_logger` context. utils.create_dir(self._test_run_metadata.root_output_path) summary_writer = records.TestSummaryWriter( os.path.join(self._test_run_metadata.root_output_path, records.OUTPUT_FILE_SUMMARY)) try: for test_run_info in self._test_run_infos: # Set up the test-specific config test_config = test_run_info.config.copy() test_config.log_path = self._test_run_metadata.root_output_path test_config.summary_writer = summary_writer test_config.test_class_name_suffix = test_run_info.test_class_name_suffix try: self._run_test_class(config=test_config, test_class=test_run_info.test_class, tests=test_run_info.tests) except signals.TestAbortAll as e: logging.warning( 'Abort all subsequent test classes. Reason: %s', e) raise finally: summary_writer.dump(self.results.summary_dict(), records.TestSummaryEntryType.SUMMARY) self._test_run_metadata.set_end_point() # Show the test run summary. summary_lines = [ f'Summary for test run {self._test_run_metadata.run_id}:', f'Total time elapsed {self._test_run_metadata.time_elapsed_sec}s', f'Artifacts are saved in "{self._test_run_metadata.root_output_path}"', f'Test results: {self.results.summary_str()}' ] logging.info('\n'.join(summary_lines))
def take_bug_report(self, test_name, begin_time, timeout=300, destination=None): """Takes a bug report on the device and stores it in a file. Args: test_name: Name of the test method that triggered this bug report. begin_time: Timestamp of when the test started. timeout: float, the number of seconds to wait for bugreport to complete, default is 5min. destination: string, path to the directory where the bugreport should be saved. """ new_br = True try: stdout = self.adb.shell('bugreportz -v').decode('utf-8') # This check is necessary for builds before N, where adb shell's ret # code and stderr are not propagated properly. if 'not found' in stdout: new_br = False except adb.AdbError: new_br = False if destination: br_path = utils.abs_path(destination) else: br_path = os.path.join(self.log_path, 'BugReports') utils.create_dir(br_path) base_name = ',%s,%s.txt' % (begin_time, self._normalized_serial) if new_br: base_name = base_name.replace('.txt', '.zip') test_name_len = utils.MAX_FILENAME_LEN - len(base_name) out_name = test_name[:test_name_len] + base_name full_out_path = os.path.join(br_path, out_name.replace(' ', r'\ ')) # in case device restarted, wait for adb interface to return self.wait_for_boot_completion() self.log.info('Taking bugreport for %s.', test_name) if new_br: out = self.adb.shell('bugreportz', timeout=timeout).decode('utf-8') if not out.startswith('OK'): raise DeviceError(self, 'Failed to take bugreport: %s' % out) br_out_path = out.split(':')[1].strip() self.adb.pull([br_out_path, full_out_path]) else: # shell=True as this command redirects the stdout to a local file # using shell redirection. self.adb.bugreport(' > "%s"' % full_out_path, shell=True, timeout=timeout) self.log.info('Bugreport for %s taken at %s.', test_name, full_out_path)
def cat_adb_log(self, tag, begin_time): """Takes an excerpt of the adb logcat log from a certain time point to current time. .. deprecated:: 1.10 Use :func:`create_output_excerpts` instead. Args: tag: An identifier of the time period, usualy the name of a test. begin_time: Logline format timestamp of the beginning of the time period. Returns: String, full path to the excerpt file created. """ if not self.adb_logcat_file_path: raise Error( self._ad, 'Attempting to cat adb log when none has been collected.') end_time = mobly_logger.get_log_line_timestamp() self._ad.log.debug('Extracting adb log from logcat.') adb_excerpt_path = os.path.join(self._ad.log_path, 'AdbLogExcerpts') utils.create_dir(adb_excerpt_path) out_name = '%s,%s.txt' % (tag, begin_time) out_name = mobly_logger.sanitize_filename(out_name) full_adblog_path = os.path.join(adb_excerpt_path, out_name) with io.open(full_adblog_path, 'w', encoding='utf-8') as out: in_file = self.adb_logcat_file_path with io.open(in_file, 'r', encoding='utf-8', errors='replace') as f: in_range = False while True: line = None try: line = f.readline() if not line: break except: continue line_time = line[:mobly_logger.log_line_timestamp_len] if not mobly_logger.is_valid_logline_timestamp(line_time): continue if self._is_timestamp_in_range(line_time, begin_time, end_time): in_range = True if not line.endswith('\n'): line += '\n' out.write(line) else: if in_range: break return full_adblog_path
def setup_test_logger(log_path, prefix=None, filename=None): """Customizes the root logger for a test run. Args: log_path: Location of the report file. prefix: A prefix for each log line in terminal. filename: Name of the files. The default is the time the objects are requested. """ utils.create_dir(log_path) _setup_test_logger(log_path, prefix) logging.info('Test output folder: "%s"', log_path) create_latest_log_alias(log_path)
def _setup_test_logger(log_path, prefix=None, filename=None): """Customizes the root logger for a test run. The logger object has a stream handler and a file handler. The stream handler logs INFO level to the terminal, the file handler logs DEBUG level to files. Args: log_path: Location of the log file. prefix: A prefix for each log line in terminal. filename: Name of the log file. The default is the time the logger is requested. """ log = logging.getLogger() kill_test_logger(log) log.propagate = False log.setLevel(logging.DEBUG) # Log info to stream terminal_format = log_line_format if prefix: terminal_format = '[%s] %s' % (prefix, log_line_format) c_formatter = logging.Formatter(terminal_format, log_line_time_format) ch = logging.StreamHandler(sys.stdout) ch.setFormatter(c_formatter) ch.setLevel(logging.INFO) # Log everything to file f_formatter = logging.Formatter(log_line_format, log_line_time_format) # All the logs of this test class go into one directory if filename is None: filename = get_log_file_timestamp() utils.create_dir(log_path) # TODO(#270): Deprecate `test_run_details.txt` when we remove old output # format support. fh = logging.FileHandler(os.path.join(log_path, 'test_run_details.txt')) fh.setFormatter(f_formatter) fh.setLevel(logging.DEBUG) log.addHandler(fh) # Write logger output to files fh_info = logging.FileHandler( os.path.join(log_path, records.OUTPUT_FILE_INFO_LOG)) fh_info.setFormatter(f_formatter) fh_info.setLevel(logging.INFO) fh_debug = logging.FileHandler( os.path.join(log_path, records.OUTPUT_FILE_DEBUG_LOG)) fh_debug.setFormatter(f_formatter) fh_debug.setLevel(logging.DEBUG) log.addHandler(ch) log.addHandler(fh_info) log.addHandler(fh_debug) log.log_path = log_path logging.log_path = log_path
def setup_test_logger(log_path, prefix=None, filename=None): """Customizes the root logger for a test run. Args: log_path: Location of the report file. prefix: A prefix for each log line in terminal. filename: Name of the files. The default is the time the objects are requested. """ if filename is None: filename = get_log_file_timestamp() create_dir(log_path) logger = _setup_test_logger(log_path, prefix, filename) create_latest_log_alias(log_path)
def cat_adb_log(self, tag, begin_time): """Takes an excerpt of the adb logcat log from a certain time point to current time. Args: tag: An identifier of the time period, usualy the name of a test. begin_time: Logline format timestamp of the beginning of the time period. """ if not self.adb_logcat_file_path: raise Error( self._ad, 'Attempting to cat adb log when none has been collected.') end_time = mobly_logger.get_log_line_timestamp() self._ad.log.debug('Extracting adb log from logcat.') adb_excerpt_path = os.path.join(self._ad.log_path, 'AdbLogExcerpts') utils.create_dir(adb_excerpt_path) f_name = os.path.basename(self.adb_logcat_file_path) out_name = f_name.replace('adblog,', '').replace('.txt', '') out_name = ',%s,%s.txt' % (begin_time, out_name) out_name = out_name.replace(':', '-') tag_len = utils.MAX_FILENAME_LEN - len(out_name) tag = tag[:tag_len] out_name = tag + out_name full_adblog_path = os.path.join(adb_excerpt_path, out_name) with io.open(full_adblog_path, 'w', encoding='utf-8') as out: in_file = self.adb_logcat_file_path with io.open(in_file, 'r', encoding='utf-8', errors='replace') as f: in_range = False while True: line = None try: line = f.readline() if not line: break except: continue line_time = line[:mobly_logger.log_line_timestamp_len] if not mobly_logger.is_valid_logline_timestamp(line_time): continue if self._is_timestamp_in_range(line_time, begin_time, end_time): in_range = True if not line.endswith('\n'): line += '\n' out.write(line) else: if in_range: break
def _start(self): """The actual logic of starting logcat.""" self._enable_logpersist() logcat_file_path = self._config.output_file_path if not logcat_file_path: f_name = self._ad.generate_filename(self.OUTPUT_FILE_TYPE, extension_name='txt') logcat_file_path = os.path.join(self._ad.log_path, f_name) utils.create_dir(os.path.dirname(logcat_file_path)) cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % ( adb.ADB, self._ad.serial, self._config.logcat_params, logcat_file_path) process = utils.start_standing_subprocess(cmd, shell=True) self._adb_logcat_process = process self.adb_logcat_file_path = logcat_file_path
def _start(self): """The actual logic of starting logcat.""" self._enable_logpersist() logcat_file_path = self._configs.output_file_path if not logcat_file_path: f_name = 'adblog,%s,%s.txt' % (self._ad.model, self._ad._normalized_serial) logcat_file_path = os.path.join(self._ad.log_path, f_name) utils.create_dir(os.path.dirname(logcat_file_path)) cmd = '"%s" -s %s logcat -v threadtime %s >> "%s"' % ( adb.ADB, self._ad.serial, self._configs.logcat_params, logcat_file_path) process = utils.start_standing_subprocess(cmd, shell=True) self._adb_logcat_process = process self.adb_logcat_file_path = logcat_file_path
def save_to_text_file(monsoon_data, file_path): """Save multiple MonsoonData objects to a text file. Args: monsoon_data: A list of MonsoonData objects to write to a text file. file_path: The full path of the file to save to, including the file name. """ if not monsoon_data: raise MonsoonError("Attempting to write empty Monsoon data to " "file, abort") utils.create_dir(os.path.dirname(file_path)) with io.open(file_path, 'w', encoding='utf-8') as f: for md in monsoon_data: f.write(str(md)) f.write(MonsoonData.delimiter)
def setup_test_logger(log_path, prefix=None, alias='latest'): """Customizes the root logger for a test run. Args: log_path: string, the location of the report file. prefix: optional string, a prefix for each log line in terminal. alias: optional string, The name of the alias to use for the latest log directory. If a falsy value is provided, then the alias directory will not be created, which is useful to save storage space when the storage system (e.g. ZIP files) does not properly support shortcut/symlinks. """ utils.create_dir(log_path) _setup_test_logger(log_path, prefix) logging.info('Test output folder: "%s"', log_path) if alias: create_latest_log_alias(log_path, alias=alias)
def create_per_test_excerpt(self, current_test_info): """Convenient method for creating excerpts of adb logcat. To use this feature, call this method at the end of: `setup_class`, `teardown_test`, and `teardown_class`. This moves the current content of `self.adb_logcat_file_path` to the log directory specific to the current test. Args: current_test_info: `self.current_test_info` in a Mobly test. """ self.pause() dest_path = current_test_info.output_path utils.create_dir(dest_path) self._ad.log.debug('AdbLog exceprt location: %s', dest_path) shutil.move(self.adb_logcat_file_path, dest_path) self.resume()
def take_screenshot(self, destination): """Takes a screenshot of the device. Args: destination: string, full path to the directory to save in. Returns: string, full path to the screenshot file on the host. """ filename = self.generate_filename('screenshot', extension_name='png') device_path = os.path.join('/storage/emulated/0/', filename) self.adb.shell(['screencap', '-p', device_path], timeout=TAKE_SCREENSHOT_TIMEOUT_SECOND) utils.create_dir(destination) self.adb.pull([device_path, destination]) pic_path = os.path.join(destination, filename) self.log.debug('Screenshot taken, saved on the host: %s', pic_path) self.adb.shell(['rm', device_path]) return pic_path
def run(self): """Executes tests. This will instantiate controller and test classes, execute tests, and print a summary. Raises: Error: if no tests have previously been added to this runner using add_test_class(...). """ if not self._test_run_infos: raise Error('No tests to execute.') # Ensure the log path exists. Necessary if `run` is used outside of the # `mobly_logger` context. utils.create_dir(self._log_path) summary_writer = records.TestSummaryWriter( os.path.join(self._log_path, records.OUTPUT_FILE_SUMMARY)) try: for test_run_info in self._test_run_infos: # Set up the test-specific config test_config = test_run_info.config.copy() test_config.log_path = self._log_path test_config.summary_writer = summary_writer test_config.test_class_name_suffix = test_run_info.test_class_name_suffix try: self._run_test_class(config=test_config, test_class=test_run_info.test_class, tests=test_run_info.tests) except signals.TestAbortAll as e: logging.warning( 'Abort all subsequent test classes. Reason: %s', e) raise finally: summary_writer.dump(self.results.summary_dict(), records.TestSummaryEntryType.SUMMARY) # Stop and show summary. msg = '\nSummary for test run %s@%s: %s\n' % ( self._test_bed_name, self._start_time, self.results.summary_str()) logging.info(msg.strip())
def _start(self): """The actual logic of starting logcat.""" self._enable_logpersist() if self._config.output_file_path: self._close_logcat_file() self.adb_logcat_file_path = self._config.output_file_path if not self.adb_logcat_file_path: f_name = self._ad.generate_filename(self.OUTPUT_FILE_TYPE, extension_name='txt') logcat_file_path = os.path.join(self._ad.log_path, f_name) self.adb_logcat_file_path = logcat_file_path utils.create_dir(os.path.dirname(self.adb_logcat_file_path)) # In debugging mode of IntelijIDEA, "patch_args" remove # double quotes in args if starting and ending with it. # Add spaces at beginning and at last to fix this issue. cmd = ' "%s" -s %s logcat -v threadtime -T 1 %s >> "%s" ' % ( adb.ADB, self._ad.serial, self._config.logcat_params, self.adb_logcat_file_path) process = utils.start_standing_subprocess(cmd, shell=True) self._adb_logcat_process = process
def start(self, extra_args="", tag=""): """Starts iperf server on specified port. Args: extra_args: A string representing extra arguments to start iperf server with. tag: Appended to log file name to identify logs from different iperf runs. """ if self.started: return utils.create_dir(self.log_path) if tag: tag = tag + ',' out_file_name = "IPerfServer,{},{}{}.log".format( self.port, tag, len(self.log_files)) full_out_path = os.path.join(self.log_path, out_file_name) cmd = '%s %s > %s' % (self.iperf_str, extra_args, full_out_path) self.iperf_process = utils.start_standing_subprocess(cmd, shell=True) self.log_files.append(full_out_path) self.started = True
def _setup_test_logger(log_path, prefix=None, filename=None): """Customizes the root logger for a test run. The logger object has a stream handler and a file handler. The stream handler logs INFO level to the terminal, the file handler logs DEBUG level to files. Args: log_path: Location of the log file. prefix: A prefix for each log line in terminal. filename: Name of the log file. The default is the time the logger is requested. """ log = logging.getLogger() kill_test_logger(log) log.propagate = False log.setLevel(logging.DEBUG) # Log info to stream terminal_format = log_line_format if prefix: terminal_format = "[{}] {}".format(prefix, log_line_format) c_formatter = logging.Formatter(terminal_format, log_line_time_format) ch = logging.StreamHandler(sys.stdout) ch.setFormatter(c_formatter) ch.setLevel(logging.INFO) # Log everything to file f_formatter = logging.Formatter(log_line_format, log_line_time_format) # All the logs of this test class go into one directory if filename is None: filename = get_log_file_timestamp() create_dir(log_path) fh = logging.FileHandler(os.path.join(log_path, 'test_run_details.txt')) fh.setFormatter(f_formatter) fh.setLevel(logging.DEBUG) log.addHandler(ch) log.addHandler(fh) log.log_path = log_path logging.log_path = log_path
def take_bug_report(self, test_name, begin_time): """Takes a bug report on the device and stores it in a file. Args: test_name: Name of the test case that triggered this bug report. begin_time: Logline format timestamp taken when the test started. """ new_br = True try: stdout = self.adb.shell("bugreportz -v").decode("utf-8") # This check is necessary for builds before N, where adb shell's ret # code and stderr are not propagated properly. if "not found" in stdout: new_br = False except adb.AdbError: new_br = False br_path = os.path.join(self.log_path, "BugReports") utils.create_dir(br_path) base_name = ",{},{}.txt".format(begin_time, self.serial) if new_br: base_name = base_name.replace(".txt", ".zip") test_name_len = utils.MAX_FILENAME_LEN - len(base_name) out_name = test_name[:test_name_len] + base_name full_out_path = os.path.join(br_path, out_name.replace(' ', '\ ')) # in case device restarted, wait for adb interface to return self.wait_for_boot_completion() self.log.info("Taking bugreport for %s.", test_name) if new_br: out = self.adb.shell("bugreportz").decode("utf-8") if not out.startswith("OK"): raise Error("Failed to take bugreport on %s: %s" % (self.serial, out)) br_out_path = out.split(':')[1].strip() self.adb.pull("%s %s" % (br_out_path, full_out_path)) else: self.adb.bugreport(" > {}".format(full_out_path)) self.log.info("Bugreport for %s taken at %s.", test_name, full_out_path)
def start_adb_logcat(self): """Starts a standing adb logcat collection in separate subprocesses and save the logcat in a file. """ if self.is_adb_logcat_on: raise Error(("Android device {} already has an adb " "logcat thread going on. Cannot start " "another one.").format(self.serial)) # Disable adb log spam filter. Have to stop and clear settings first # because 'start' doesn't support --clear option before Android N. self.adb.shell("logpersist.stop --clear") self.adb.shell("logpersist.start") f_name = "adblog,{},{}.txt".format(self.model, self.serial) utils.create_dir(self.log_path) logcat_file_path = os.path.join(self.log_path, f_name) try: extra_params = self.adb_logcat_param except AttributeError: extra_params = "-b all" cmd = "adb -s {} logcat -v threadtime {} >> {}".format( self.serial, extra_params, logcat_file_path) self.adb_logcat_process = utils.start_standing_subprocess(cmd) self.adb_logcat_file_path = logcat_file_path
def take_bug_report(self, test_name, begin_time): """Takes a bug report on the device and stores it in a file. Args: test_name: Name of the test case that triggered this bug report. begin_time: Logline format timestamp taken when the test started. """ new_br = True try: stdout = self.adb.shell('bugreportz -v').decode('utf-8') # This check is necessary for builds before N, where adb shell's ret # code and stderr are not propagated properly. if 'not found' in stdout: new_br = False except adb.AdbError: new_br = False br_path = os.path.join(self.log_path, 'BugReports') utils.create_dir(br_path) base_name = ',%s,%s.txt' % (begin_time, self.serial) if new_br: base_name = base_name.replace('.txt', '.zip') test_name_len = utils.MAX_FILENAME_LEN - len(base_name) out_name = test_name[:test_name_len] + base_name full_out_path = os.path.join(br_path, out_name.replace(' ', r'\ ')) # in case device restarted, wait for adb interface to return self.wait_for_boot_completion() self.log.info('Taking bugreport for %s.', test_name) if new_br: out = self.adb.shell('bugreportz').decode('utf-8') if not out.startswith('OK'): raise DeviceError(self, 'Failed to take bugreport: %s' % out) br_out_path = out.split(':')[1].strip() self.adb.pull('%s %s' % (br_out_path, full_out_path)) else: self.adb.bugreport(' > %s' % full_out_path) self.log.info('Bugreport for %s taken at %s.', test_name, full_out_path)