def pc_can_ping(self, count=3): """Run ping traffic from PC side. Logic steps are 1. PC ping the usb server ip. 2. Check the packet loss rate. Args: count : count for ping test. Returns: True: If no packet loss. False: Otherwise. """ ip = self.get_dut_tethering_ip() ping = job.run('ping -c {} {}'.format(count, ip), ignore_status=True).stdout self.log.info(ping) return '0% packet loss' in ping
def main(): if COMMIT_ID_ENV_KEY not in os.environ: logging.error('Missing commit id in environment.') exit(1) # get list of *.proto and *_pb2.py files from commit, then compare proto_files = [] proto_gen_files = [] git_cmd = GIT_FILE_NAMES_CMD % os.environ[COMMIT_ID_ENV_KEY] lines = job.run(git_cmd).stdout.splitlines() for line in lines: match = re.match(r'(\S+)\s+(.*)', line) status, f = match.group(1), match.group(2) if status != 'D': if f.endswith('.proto'): proto_files.append(os.path.abspath(f)) if f.endswith('_pb2.py'): proto_gen_files.append(os.path.abspath(f)) exit(not verify_protos_update_generated_files(proto_files, proto_gen_files))
def get_rndis_interface(self): """Check rndis interface after usb tethering enable. Returns: Usb tethering interface from Android device. Raises: TestFailure when unable to find correct usb tethering interface. """ time.sleep(DEFAULT_SETTLE_TIME) check_usb_tethering = job.run('ifconfig').stdout # A regex that stores the tethering interface in group 1. tethered_interface_regex = r'^(enp.*?):.*?broadcast 192.168.42.255' match = re.search(tethered_interface_regex, check_usb_tethering, re.DOTALL + re.MULTILINE) if match: return match.group(1) else: raise signals.TestFailure( 'Unable to find tethering interface. The device may not be tethered.' )
def _exec_fastboot_cmd(self, name, arg_str, ignore_status=False, timeout=60): command = ' '.join((self.fastboot_str, name, arg_str)) if self.ssh_connection: result = self.connection.run(command, ignore_status=True, timeout=timeout) else: result = job.run(command, ignore_status=True, timeout=timeout) ret, out, err = result.exit_status, result.stdout, result.stderr # TODO: This is only a temporary workaround for b/34815412. # fastboot getvar outputs to stderr instead of stdout if "getvar" in command: out = err if ret == 0 or ignore_status: return out else: raise FastbootError( cmd=command, stdout=out, stderr=err, ret_code=ret)
def start(self, extra_args='', tag=''): """Starts iperf server on local machine. 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._iperf_process is not None: return self._current_log_file = self._get_full_file_path(tag) # Run an iperf3 server on the hinted port with JSON output. command = ['iperf3', '-s', '-p', str(self._hinted_port), '-J'] command.extend(shlex.split(extra_args)) if self._last_opened_file: self._last_opened_file.close() self._last_opened_file = open(self._current_log_file, 'w') self._iperf_process = subprocess.Popen(command, stdout=self._last_opened_file, stderr=subprocess.DEVNULL) for attempts_left in reversed(range(3)): try: self._port = int( _get_port_from_ss_output( job.run('ss -l -p -n | grep iperf').stdout, self._iperf_process.pid)) break except ProcessLookupError: if attempts_left == 0: raise logging.debug('iperf3 process not started yet.') time.sleep(.01)
def test_run_no_shell(self, popen): """Test that we handle running without a wrapping shell.""" result = job.run(['echo', 'TEST']) self.assertTrue(result.stdout.startswith('TEST'))
def test_run_with_ignored_error(self, popen): """Test that we can ignore exit status on request.""" result = job.run('exit 1', ignore_status=True) self.assertEqual(result.exit_status, 1)
def test_run_stderr(self, popen): """Test that we can read process stderr.""" result = job.run('echo TEST 1>&2') self.assertEqual(len(result.stdout), 0) self.assertTrue(result.stderr.startswith('TEST')) self.assertFalse(result.stdout)
def test_run_success(self, popen): """Test running a simple shell command.""" result = job.run('echo TEST') self.assertTrue(result.stdout.startswith('TEST'))
def pull_test_results(self, testplan_directory): """ Downloads the test reports from the remote host and parses the test summary to obtain the results. Args: testplan_directory: directory where to look for reports generated by the test equipment in the remote computer Returns: a JSON object containing the test results """ if not testplan_directory: raise ValueError('Invalid testplan directory.') # Download test reports from the remote host job.run('wget -r --user={} --password={} -P {} ftp://{}/{}'.format( self.ftp_user, self.ftp_pass, logging.log_path, self.remote_server_ip, testplan_directory)) # Open the testplan directory testplan_path = os.path.join(logging.log_path, self.remote_server_ip, testplan_directory) # Find the report.json file in the testcase folder dir_list = os.listdir(testplan_path) xml_path = None for dir in dir_list: if 'TestCaseName' in dir: xml_path = os.path.join(testplan_path, dir, 'SummaryReport.xml') break if not xml_path: raise RuntimeError('Could not find testcase directory.') # Return the obtained report as a dictionary xml_tree = ElementTree.ElementTree() xml_tree.parse(source=xml_path) results_dictionary = {} col_iterator = xml_tree.iter('column') for col in col_iterator: # Look in the text of the first child for the required metrics if col.text == '2D position error [m]': results_dictionary[self.POS_ERROR_KEY] = { 'min': float(next(col_iterator).text), 'med': float(next(col_iterator).text), 'avg': float(next(col_iterator).text), 'max': float(next(col_iterator).text) } elif col.text == 'Time to first fix [s]': results_dictionary[self.TTFF_KEY] = { 'min': float(next(col_iterator).text), 'med': float(next(col_iterator).text), 'avg': float(next(col_iterator).text), 'max': float(next(col_iterator).text) } message_iterator = xml_tree.iter('message') for message in message_iterator: # Look for the line showing sensitivity if message.text: # The typo in 'successfull' is intended as it is present in the # test logs generated by the Contest system. match = re.search( '(?<=Margin search completed, the lowest ' 'successfull output power is )-?\d+.?\d+' '(?= dBm)', message.text) if match: results_dictionary[self.SENSITIVITY_KEY] = float( match.group(0)) break return results_dictionary
def args(self, *args, **kwargs): return job.run(' '.join((self.fastboot_str, ) + args), **kwargs).stdout
def run(self, command, timeout=60, ignore_status=False, env=None, io_encoding='utf-8', attempts=2): """Runs a remote command over ssh. Will ssh to a remote host and run a command. This method will block until the remote command is finished. Args: command: The command to execute over ssh. Can be either a string or a list. timeout: number seconds to wait for command to finish. ignore_status: bool True to ignore the exit code of the remote subprocess. Note that if you do ignore status codes, you should handle non-zero exit codes explicitly. env: dict environment variables to setup on the remote host. io_encoding: str unicode encoding of command output. attempts: Number of attempts before giving up on command failures. Returns: A job.Result containing the results of the ssh command. Raises: job.TimeoutError: When the remote command took to long to execute. Error: When the ssh connection failed to be created. CommandError: Ssh worked, but the command had an error executing. """ if attempts == 0: return None if env is None: env = {} try: self.setup_master_ssh(self._settings.connect_timeout) except Error: self.log.warning('Failed to create master ssh connection, using ' 'normal ssh connection.') extra_options = {'BatchMode': True} if self._master_ssh_proc: extra_options['ControlPath'] = self.socket_path identifier = str(uuid.uuid4()) full_command = 'echo "CONNECTED: %s"; %s' % (identifier, command) terminal_command = self._formatter.format_command( full_command, env, self._settings, extra_options=extra_options) dns_retry_count = 2 while True: result = job.run(terminal_command, ignore_status=True, timeout=timeout, io_encoding=io_encoding) output = result.stdout # Check for a connected message to prevent false negatives. valid_connection = re.search('^CONNECTED: %s' % identifier, output, flags=re.MULTILINE) if valid_connection: # Remove the first line that contains the connect message. line_index = output.find('\n') + 1 if line_index == 0: line_index = len(output) real_output = output[line_index:].encode(io_encoding) result = job.Result(command=result.command, stdout=real_output, stderr=result._raw_stderr, exit_status=result.exit_status, duration=result.duration, did_timeout=result.did_timeout, encoding=io_encoding) if result.exit_status and not ignore_status: raise job.Error(result) return result error_string = result.stderr had_dns_failure = (result.exit_status == 255 and re.search( r'^ssh: .*: Name or service not known', error_string, flags=re.MULTILINE)) if had_dns_failure: dns_retry_count -= 1 if not dns_retry_count: raise Error('DNS failed to find host.', result) self.log.debug('Failed to connect to host, retrying...') else: break had_timeout = re.search( r'^ssh: connect to host .* port .*: ' r'Connection timed out\r$', error_string, flags=re.MULTILINE) if had_timeout: raise Error('Ssh timed out.', result) permission_denied = 'Permission denied' in error_string if permission_denied: raise Error('Permission denied.', result) unknown_host = re.search( r'ssh: Could not resolve hostname .*: ' r'Name or service not known', error_string, flags=re.MULTILINE) if unknown_host: raise Error('Unknown host.', result) self.log.error('An unknown error has occurred. Job result: %s' % result) ping_output = job.run('ping %s -c 3 -w 1' % self._settings.hostname, ignore_status=True) self.log.error('Ping result: %s' % ping_output) if attempts > 1: self._cleanup_master_ssh() self.run(command, timeout, ignore_status, env, io_encoding, attempts - 1) raise Error('The job failed for unknown reasons.', result)
def _make_phone_call(self, call_verification_func=None): ads = self.android_devices[:] if not self.single_phone_test: random.shuffle(ads) the_number = self.result_info["Call Total"] + 1 duration = random.randrange(self.min_phone_call_duration, self.max_phone_call_duration) result = True test_name = "%s_No_%s_phone_call" % (self.test_name, the_number) log_msg = "[Test Case] %s" % test_name self.log.info("%s for %s seconds begin", log_msg, duration) begin_time = get_device_epoch_time(ads[0]) for ad in self.android_devices: if self.user_params.get("turn_on_tcpdump", True): start_adb_tcpdump(ad, interface="any", mask="all") if not getattr(ad, "droid", None): ad.droid, ad.ed = ad.get_droid() ad.ed.start() else: try: if not ad.droid.is_live: ad.droid, ad.ed = ad.get_droid() ad.ed.start() else: ad.ed.clear_all_events() except Exception: ad.log.info("Create new sl4a session for phone call") ad.droid, ad.ed = ad.get_droid() ad.ed.start() ad.droid.logI("%s begin" % log_msg) start_qxdm_loggers(self.log, self.android_devices, begin_time) failure_reasons = set() self.dut_incall = True if self.single_phone_test: call_setup_result = initiate_call( self.log, self.dut, self.call_server_number, incall_ui_display=INCALL_UI_DISPLAY_BACKGROUND ) and wait_for_in_call_active(self.dut, 60, 3) else: call_setup_result = call_setup_teardown( self.log, ads[0], ads[1], ad_hangup=None, verify_caller_func=call_verification_func, verify_callee_func=call_verification_func, wait_time_in_call=0, incall_ui_display=INCALL_UI_DISPLAY_BACKGROUND) if not call_setup_result: call_logs = ads[0].search_logcat( "ActivityManager: START u0 {act=android.intent.action.CALL", begin_time) messaging_logs = ads[0].search_logcat( "com.google.android.apps.messaging/.ui.conversation.ConversationActivity", begin_time) if call_logs and messaging_logs: if messaging_logs[-1]["datetime_obj"] - call_logs[-1]["datetime_obj"] < 5: ads[0].log.info( "Call setup failure due to simultaneous activities") self.result_info[ "Call Setup Failure With Simultaneous Activity"] += 1 return True self.log.error("%s: Setup Call failed.", log_msg) failure_reasons.add("Setup") result = False else: elapsed_time = 0 check_interval = 5 while (elapsed_time < duration): check_interval = min(check_interval, duration - elapsed_time) time.sleep(check_interval) elapsed_time += check_interval time_message = "at <%s>/<%s> second." % (elapsed_time, duration) for ad in ads: if not call_verification_func(self.log, ad): ad.log.warning("Call is NOT in correct %s state at %s", call_verification_func.__name__, time_message) if call_verification_func.__name__ == "is_phone_in_call_iwlan": if is_phone_in_call(self.log, ad): if getattr(ad, "data_rat_state_error_count", 0) < 1: setattr(ad, "data_rat_state_error_count", 1) continue failure_reasons.add("Maintenance") last_call_drop_reason(ad, begin_time) hangup_call(self.log, ads[0]) result = False else: ad.log.info("Call is in correct %s state at %s", call_verification_func.__name__, time_message) if not result: break if not hangup_call(self.log, ads[0]): failure_reasons.add("Teardown") result = False for ad in ads: if not wait_for_call_id_clearing(ad, []) or ad.droid.telecomIsInCall(): ad.log.error("Fail to hang up call") failure_reasons.add("Teardown") result = False self.result_info["Call Total"] += 1 for ad in self.android_devices: try: ad.droid.logI("%s end" % log_msg) except: pass self.log.info("%s end", log_msg) self.dut_incall = False if not result: self.log.info("%s failed", log_msg) if self.gps_log_file: gps_info = job.run( "tail %s" % self.gps_log_file, ignore_status=True) if gps_info.stdout: gps_log_path = os.path.join(self.log_path, test_name, "gps_logs.txt") utils.create_dir(gps_log_path) job.run( "tail %s > %s" % (self.gps_log_file, gps_log_path), ignore_status=True) self.log.info("gps log:\n%s", gps_info.stdout) else: self.log.warning("Fail to get gps log %s", self.user_params["gps_log_file"]) for reason in failure_reasons: self.result_info["Call %s Failure" % reason] += 1 for ad in ads: log_path = os.path.join(self.log_path, test_name, "%s_binder_logs" % ad.serial) utils.create_dir(log_path) ad.pull_files(BINDER_LOGS, log_path) try: self._take_bug_report(test_name, begin_time) except Exception as e: self.log.exception(e) for ad in ads: if ad.droid.telecomIsInCall(): hangup_call_by_adb(ad) else: self.log.info("%s test succeed", log_msg) self.result_info["Call Success"] += 1 if self.result_info["Call Total"] % 50 == 0: for ad in ads: synchronize_device_time(ad) if not check_is_wifi_connected(self.log, ad, self.wifi_network_ssid): ensure_wifi_connected(self.log, ad, self.wifi_network_ssid, self.wifi_network_pass) force_connectivity_metrics_upload(ad) time.sleep(300) wifi_toggle_state(self.log, ad, False) if self.get_binder_logs: log_path = os.path.join(self.log_path, "%s_binder_logs" % test_name, "%s_binder_logs" % ad.serial) utils.create_dir(log_path) ad.pull_files(BINDER_LOGS, log_path) return result