def run_reboot_test(self, settings): """Runs a reboot test based on a given config. 1. Setups up a network, associates the dut, and saves the network. 2. Verifies the dut receives ip address(es). 3. Verifies traffic between DUT and AP (IPerf client and server). 4. Reboots (hard or soft) the device (dut or ap). - If the ap was rebooted, setup the same network again. 5. Wait for reassociation or timeout. 6. If reassocation occurs: - Verifies the dut receives ip address(es). - Verifies traffic between DUT and AP (IPerf client and server). 7. Logs time to reconnect (or failure to reconnect) 8. If stress testing, repeats steps 4 - 7 for N loops. Args: settings: dictionary containing the following values: reboot_device: string ('dut' or 'ap') of the device to reboot. reboot_type: string ('soft' or 'hard') of how to reboot the reboot_device. band: string ('2g' or '5g') of band to setup. ipv4: True if using ipv4 (dhcp), else False. ipv6: True if using ipv6 (radvd), else False. Optional: interrupt: if True, the DUT will be pinging the AP in a parallel process when the reboot occurs. This is used to compare reconnect times when idle to active. test_name: name of the test, used when stress testing. loops: number of times to perform test, used when stress testing. Raises: ValueError, if ipv4 and ipv6 are both False ValueError, if band is not '2g' or '5g' ValueError, if reboot_device is not 'dut' or 'ap' ValueError, if reboot_type is not 'soft' or 'hard' """ loops = settings.get('loops', 1) passed_count = 0 ipv4 = settings['ipv4'] ipv6 = settings['ipv6'] reboot_device = settings['reboot_device'] reboot_type = settings['reboot_type'] band = settings['band'] interrupt = settings.get('interrupt', None) # Validate test settings. if not ipv4 and not ipv6: raise ValueError('Either ipv4, ipv6, or both must be True.') if reboot_device != DUT and reboot_device != AP: raise ValueError('Invalid reboot device: %s' % reboot_device) if reboot_type != SOFT and reboot_type != HARD: raise ValueError('Invalid reboot type: %s' % reboot_type) if band != BAND_2G and band != BAND_5G: raise ValueError('Invalid band: %s' % band) self.setup_save_and_connect_to_network(self.ssid, band, ipv4=ipv4, ipv6=ipv6) self.wait_for_dut_network_connection(self.ssid) dut_test_interface = self.iperf_client_on_dut.test_interface if ipv4: self.wait_until_dut_gets_ipv4_addr(dut_test_interface) if ipv6: self.wait_until_dut_gets_ipv6_addr(dut_test_interface) self.iperf_server_on_ap = self.setup_iperf_server_on_ap(band) self.iperf_server_on_ap.start() if ipv4: self.verify_traffic_between_dut_and_ap(self.iperf_server_on_ap, self.iperf_client_on_dut) if ipv6: self.verify_traffic_between_dut_and_ap(self.iperf_server_on_ap, self.iperf_client_on_dut, ip_version=IPV6) # Looping reboots for stress testing for run in range(loops): run += 1 self.log.info('Starting run %s of %s.' % (run, loops)) # Ping from DUT to AP during AP reboot if interrupt: if ipv4: self.start_dut_ping_process(self.iperf_server_on_ap) if ipv6: self.start_dut_ping_process(self.iperf_server_on_ap, ip_version=IPV6) # DUT reboots if reboot_device == DUT: if type(self.iperf_client_on_dut ) == iperf_client.IPerfClientOverSsh: self.iperf_client_on_dut.close_ssh() if reboot_type == SOFT: self.dut.device.reboot() elif reboot_type == HARD: self.dut.hard_power_cycle(self.pdu_devices) # AP reboots elif reboot_device == AP: if reboot_type == SOFT: self.log.info('Cleanly stopping ap.') self.access_point.stop_all_aps() elif reboot_type == HARD: self.iperf_server_on_ap.close_ssh() self.access_point.hard_power_cycle(self.pdu_devices) self.setup_ap(self.ssid, band, ipv4=ipv4, ipv6=ipv6) self.prepare_dut_for_reconnection() uptime = time.time() try: self.wait_for_dut_network_connection(self.ssid) time_to_reconnect = time.time() - uptime if ipv4: self.wait_until_dut_gets_ipv4_addr(dut_test_interface) if ipv6: self.wait_until_dut_gets_ipv6_addr(dut_test_interface) self.iperf_server_on_ap.start() if ipv4: self.verify_traffic_between_dut_and_ap( self.iperf_server_on_ap, self.iperf_client_on_dut) if ipv6: self.verify_traffic_between_dut_and_ap( self.iperf_server_on_ap, self.iperf_client_on_dut, ip_version=IPV6) except ConnectionError as err: self.log_and_continue(run, error=err) else: passed_count += 1 self.log_and_continue(run, time_to_reconnect=time_to_reconnect) if passed_count == loops: asserts.explicit_pass( 'Test Summary: device successfully reconnected to network %s ' '%s/%s times.' % (self.ssid, passed_count, loops)) else: asserts.fail( 'Test Summary: device failed reconnection test. Reconnected to ' 'network %s %s/%s times.' % (self.ssid, passed_count, loops))
def wait_channel_ready(self): future = grpc.channel_ready_future(self.grpc_channel) try: future.result(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) except grpc.FutureTimeoutError: asserts.fail("[%s] wait channel ready timeout" % self.label)
def test_func(self): asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA) never_call()
def test_ipv6_tethering(self): """ IPv6 tethering test Steps: 1. Start wifi tethering on hotspot device 2. Verify IPv6 address on hotspot device (VZW & TMO only) 3. Connect tethered device to hotspot device 4. Verify IPv6 address on the client's link properties (VZW only) 5. Verify ping on client using ping6 which should pass (VZW only) 6. Disable mobile data on provider and verify that link properties does not have IPv6 address and default route (VZW only) """ # Start wifi tethering on the hotspot device wutils.toggle_wifi_off_and_on(self.hotspot_device) self._start_wifi_tethering() # Verify link properties on hotspot device self.log.info("Check IPv6 properties on the hotspot device. " "Verizon & T-mobile should have IPv6 in link properties") self._verify_ipv6_tethering(self.hotspot_device) # Connect the client to the SSID wutils.wifi_connect(self.tethered_devices[0], self.network) # Need to wait atleast 2 seconds for IPv6 address to # show up in the link properties time.sleep(WAIT_TIME) # Verify link properties on tethered device self.log.info("Check IPv6 properties on the tethered device. " "Device should have IPv6 if carrier is Verizon") self._verify_ipv6_tethering(self.tethered_devices[0]) # Verify ping6 on tethered device ping_result = self._verify_ping(self.tethered_devices[0], wutils.DEFAULT_PING_ADDR, True) if self._supports_ipv6_tethering(self.hotspot_device): asserts.assert_true(ping_result, "Ping6 failed on the client") else: asserts.assert_true(not ping_result, "Ping6 failed as expected") # Disable mobile data on hotspot device # and verify the link properties on tethered device self.log.info("Disabling mobile data to verify ipv6 default route") self.hotspot_device.droid.telephonyToggleDataConnection(False) asserts.assert_equal( self.hotspot_device.droid.telephonyGetDataConnectionState(), tel_defines.DATA_STATE_CONNECTED, "Could not disable cell data") time.sleep( WAIT_TIME) # wait until the IPv6 is removed from link properties result = self.tethered_devices[ 0].droid.connectivityHasIPv6DefaultRoute() self.hotspot_device.droid.telephonyToggleDataConnection(True) if result: asserts.fail( "Found IPv6 default route in link properties:Data off") self.log.info("Did not find IPv6 address in link properties") # Disable wifi tethering wutils.stop_wifi_tethering(self.hotspot_device)
def pass_fail_check_rssi_accuracy(self, postprocessed_results, rssi_under_test, absolute_accuracy): """Check the test result and decide if it passed or failed. Checks the RSSI test result and compares and compute its deviation from the predicted RSSI. This computation is done for all reported RSSI values. The test fails if any of the RSSI values specified in rssi_under_test have an average error beyond what is specified in the configuration file. Args: postprocessed_results: compiled arrays of RSSI measurements rssi_under_test: list of RSSIs under test, i.e., can cause test to fail absolute_accuracy: boolean indicating whether to look at absolute RSSI accuracy, or centered RSSI accuracy. Centered accuracy is computed after systematic RSSI shifts are removed. """ test_failed = False test_message = "" if absolute_accuracy: error_type = "absolute" else: error_type = "centered" for key, val in postprocessed_results.items(): # Compute the error metrics ignoring invalid RSSI readings # If all readings invalid, set error to RSSI_ERROR_VAL if "rssi" in key and "predicted" not in key: filtered_error = [x for x in val["error"] if not math.isnan(x)] if filtered_error: avg_shift = statistics.mean(filtered_error) if absolute_accuracy: avg_error = statistics.mean( [abs(x) for x in filtered_error]) else: avg_error = statistics.mean( [abs(x - avg_shift) for x in filtered_error]) else: avg_error = RSSI_ERROR_VAL avg_shift = RSSI_ERROR_VAL rssi_failure = (avg_error > self.test_params["abs_tolerance"] ) or math.isnan(avg_error) if rssi_failure and key in rssi_under_test: test_message = test_message + ( "{} failed. Average {} error is {:.2f} dB. " "Average shift is {:.2f} dB.\n").format( key, error_type, avg_error, avg_shift) test_failed = True elif rssi_failure: test_message = test_message + ( "{} failed (ignored). Average {} error is {:.2f} dB. " "Average shift is {:.2f} dB.\n").format( key, error_type, avg_error, avg_shift) else: test_message = test_message + ( "{} passed. Average {} error is {:.2f} dB. " "Average shift is {:.2f} dB.\n").format( key, error_type, avg_error, avg_shift) if test_failed: asserts.fail(test_message) asserts.explicit_pass(test_message)
def test_something(self): asserts.fail(MSG_EXPECTED_EXCEPTION, extras=MOCK_EXTRA)
def run_iperf_max_ndi_aware_only(self, sec_configs, results): """Measure iperf performance on multiple NDPs between 2 devices using different security configurations (and hence different NDIs). Test with Aware enabled and no infrastructure connection - i.e. device is not associated to an AP. The security configuration can be: - None: open - String: passphrase - otherwise: PMK (byte array) Args: sec_configs: list of security configurations results: Dictionary into which to place test results. """ init_dut = self.android_devices[0] init_dut.pretty_name = "Initiator" resp_dut = self.android_devices[1] resp_dut.pretty_name = "Responder" asserts.skip_if( init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] < len(sec_configs) or resp_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] < len(sec_configs), "Initiator or Responder do not support multiple NDIs") init_id, init_mac = autils.attach_with_identity(init_dut) resp_id, resp_mac = autils.attach_with_identity(resp_dut) # wait for for devices to synchronize with each other - there are no other # mechanisms to make sure this happens for OOB discovery (except retrying # to execute the data-path request) time.sleep(autils.WAIT_FOR_CLUSTER) resp_req_keys = [] init_req_keys = [] resp_aware_ifs = [] init_aware_ifs = [] resp_aware_ipv6s = [] init_aware_ipv6s = [] for sec in sec_configs: # Responder: request network resp_req_key = autils.request_network( resp_dut, autils.get_network_specifier(resp_dut, resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, sec)) resp_req_keys.append(resp_req_key) # Initiator: request network init_req_key = autils.request_network( init_dut, autils.get_network_specifier(init_dut, init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, sec)) init_req_keys.append(init_req_key) # Wait for network init_net_event = autils.wait_for_event_with_keys( init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT, (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), (cconsts.NETWORK_CB_KEY_ID, init_req_key)) resp_net_event = autils.wait_for_event_with_keys( resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT, (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED), (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) resp_aware_ifs.append( resp_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]) init_aware_ifs.append( init_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]) resp_aware_ipv6s.append( autils.get_ipv6_addr(resp_dut, resp_aware_ifs[-1])) init_aware_ipv6s.append( autils.get_ipv6_addr(init_dut, init_aware_ifs[-1])) self.log.info("Initiator interfaces/ipv6: %s / %s", init_aware_ifs, init_aware_ipv6s) self.log.info("Responder interfaces/ipv6: %s / %s", resp_aware_ifs, resp_aware_ipv6s) # create threads, start them, and wait for all to finish base_port = 5000 q = queue.Queue() threads = [] for i in range(len(sec_configs)): threads.append( threading.Thread(target=self.run_iperf, args=(q, init_dut, resp_dut, resp_aware_ifs[i], init_aware_ipv6s[i], base_port + i))) for thread in threads: thread.start() for thread in threads: thread.join() # release requests for resp_req_key in resp_req_keys: resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key) for init_req_key in init_req_keys: init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) # collect data for i in range(len(sec_configs)): results[i] = {} result, data = q.get() asserts.assert_true( result, "Failure starting/running iperf3 in client mode") self.log.debug(pprint.pformat(data)) data_json = json.loads("".join(data)) if "error" in data_json: asserts.fail("iperf run failed: %s" % data_json["error"], extras=data_json) results[i]["tx_rate"] = data_json["end"]["sum_sent"][ "bits_per_second"] results[i]["rx_rate"] = data_json["end"]["sum_received"][ "bits_per_second"] self.log.info("iPerf3: Sent = %d bps Received = %d bps", results[i]["tx_rate"], results[i]["rx_rate"])
def run(self, test_names=None): """Runs test cases within a test class by the order they appear in the execution list. One of these test cases lists will be executed, shown here in priority order: 1. The test_names list, which is passed from cmd line. Invalid names are guarded by cmd line arg parsing. 2. The self.tests list defined in test class. Invalid names are ignored. 3. All function that matches test case naming convention in the test class. Args: test_names: A list of string that are test case names requested in cmd line. Returns: The test results object of this class. """ self.log.info("==========> %s <==========", self.TAG) # Devise the actual test cases to run in the test class. if not test_names: if self.tests: # Specified by run list in class. test_names = list(self.tests) else: # No test case specified by user, execute all in the test class test_names = self._get_all_test_names() self.results.requested = test_names tests = self._get_test_funcs(test_names) # A TestResultRecord used for when setup_class fails. class_record = records.TestResultRecord("setup_class", self.TAG) class_record.test_begin() # Setup for the class. try: if self._setup_class() is False: asserts.fail("Failed to setup %s." % self.TAG) except Exception as e: self.log.exception("Failed to setup %s.", self.TAG) class_record.test_fail(e) self._exec_procedure_func(self._on_fail, class_record) self._exec_func(self.teardown_class) self.results.fail_class(class_record) return self.results # Run tests in order. try: for test_name, test_func in tests: self.exec_one_testcase(test_name, test_func, self.cli_args) return self.results except signals.TestAbortClass: return self.results except signals.TestAbortAll as e: # Piggy-back test results on this exception object so we don't lose # results from this test class. setattr(e, "results", self.results) raise e finally: self._exec_func(self.teardown_class) self.log.info("Summary for test class %s: %s", self.TAG, self.results.summary_str())
def test_nan_data_path(self): """Perform NAN configuration, discovery, data-path setup, and data transfer. Configuration: 2 devices, one acting as Publisher (P) and the other as Subscriber (S) Logical steps: * P & S initiate NAN clustering (if not already up) * P & S wait for NAN connection confirmation * P starts publishing * S starts subscribing * S waits for a match (discovery) notification * S sends a message to P * P waits for message * P creates a NAN network to S as RESPONDER * P sends a message to S * S waits for message * S creates a NAN network to P as INITIATOR (order important!) * Both P & S wait for events confirming network set up * P registers NSD service * S discovers NSD service and obtains P's IP address * run iperf3 between P (server) and S (client) * unregister network callback on S """ # Configure Test self.publisher = self.android_devices[0] self.subscriber = self.android_devices[1] sub2pub_msg = "Get ready!" pub2sub_msg = "Ready!" test_token = "Token / <some magic string>" # Start Test pub_connect_id = self.exec_connect(self.publisher, self.config_request1, "publisher") sub_connect_id = self.exec_connect(self.subscriber, self.config_request2, "subscriber") # Discovery: publish + subscribe + wait for match pub_id = self.publisher.droid.wifiNanPublish(pub_connect_id, self.publish_config) sub_id = self.subscriber.droid.wifiNanSubscribe( sub_connect_id, self.subscribe_config) def filter_callbacks(event, key, name): return event['data'][key] == name try: event_sub_match = self.subscriber.ed.pop_event( nan_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT) self.log.info('%s: %s', nan_const.SESSION_CB_ON_SERVICE_DISCOVERED, event_sub_match['data']) except queue.Empty: asserts.fail('Timed out while waiting for %s on Subscriber' % nan_const.SESSION_CB_ON_SERVICE_DISCOVERED) self.log.debug(event_sub_match) # S sends message to P self.reliable_tx(self.subscriber, sub_id, event_sub_match['data']['peerId'], sub2pub_msg) try: event_pub_rx = self.publisher.ed.pop_event( nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT) except queue.Empty: asserts.fail('Timed out while waiting for %s on publisher' % nan_const.SESSION_CB_ON_MESSAGE_RECEIVED) self.log.info('%s: %s', nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, event_pub_rx['data']) asserts.assert_equal(event_pub_rx['data']['messageAsString'], sub2pub_msg, "Subscriber -> publisher message corrupted") # P requests a NAN network as RESPONDER pub_ns = self.publisher.droid.wifiNanCreateNetworkSpecifier( nan_const.DATA_PATH_RESPONDER, pub_id, event_pub_rx['data']['peerId'], test_token) self.log.info("Publisher network specifier - '%s'", pub_ns) self.network_req['NetworkSpecifier'] = pub_ns pub_req_key = self.publisher.droid.connectivityRequestNetwork( self.network_req) # P sends message to S self.reliable_tx(self.publisher, pub_id, event_pub_rx['data']['peerId'], pub2sub_msg) try: event_sub_rx = self.subscriber.ed.pop_event( nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT) except queue.Empty: asserts.fail('Timed out while waiting for %s on subscriber' % nan_const.SESSION_CB_ON_MESSAGE_RECEIVED) self.log.info('%s: %s', nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, event_sub_rx['data']) asserts.assert_equal(event_sub_rx['data']['messageAsString'], pub2sub_msg, "Publisher -> subscriber message corrupted") # S requests a NAN network as INITIATOR sub_ns = self.subscriber.droid.wifiNanCreateNetworkSpecifier( nan_const.DATA_PATH_INITIATOR, sub_id, event_sub_rx['data']['peerId'], test_token) self.log.info("Subscriber network specifier - '%s'", sub_ns) self.network_req['NetworkSpecifier'] = sub_ns sub_req_key = self.subscriber.droid.connectivityRequestNetwork( self.network_req) # Wait until both S and P get confirmation that network formed try: event_network = self.subscriber.ed.wait_for_event( con_const.EVENT_NETWORK_CALLBACK, filter_callbacks, EVENT_TIMEOUT, key=con_const.NETWORK_CB_KEY_EVENT, name=con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED) self.log.info('Subscriber %s: %s', con_const.EVENT_NETWORK_CALLBACK, event_network['data']) except queue.Empty: asserts.fail('Timed out while waiting for %s/%s on Subscriber' % (con_const.EVENT_NETWORK_CALLBACK, con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED)) self.log.debug(event_network) sub_nan_if = event_network['data']['interfaceName'] try: event_network = self.publisher.ed.wait_for_event( con_const.EVENT_NETWORK_CALLBACK, filter_callbacks, EVENT_TIMEOUT, key=con_const.NETWORK_CB_KEY_EVENT, name=con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED) self.log.info('Publisher %s: %s', con_const.EVENT_NETWORK_CALLBACK, event_network['data']) except queue.Empty: asserts.fail('Timed out while waiting for %s/%s on Publisher' % (con_const.EVENT_NETWORK_CALLBACK, con_const.NETWORK_CB_LINK_PROPERTIES_CHANGED)) self.log.debug(event_network) try: # P registers NSD service (i.e. starts publishing) nsd_reg = self.publisher.droid.nsdRegisterService( self.nsd_service_info) try: event_nsd = self.publisher.ed.wait_for_event( nsd_const.REG_LISTENER_EVENT, filter_callbacks, EVENT_TIMEOUT, key=nsd_const.REG_LISTENER_CALLBACK, name=nsd_const.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED) self.log.info( 'Publisher %s: %s', nsd_const.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED, event_nsd['data']) except queue.Empty: asserts.fail( 'Timed out while waiting for %s on Publisher' % nsd_const.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED) # S starts NSD discovery nsd_discovery = self.subscriber.droid.nsdDiscoverServices( self.nsd_service_info[nsd_const.NSD_SERVICE_INFO_SERVICE_TYPE]) try: event_nsd = self.subscriber.ed.wait_for_event( nsd_const.DISCOVERY_LISTENER_EVENT, filter_callbacks, EVENT_TIMEOUT, key=nsd_const.DISCOVERY_LISTENER_DATA_CALLBACK, name=nsd_const.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND) self.log.info( 'Subscriber %s: %s', nsd_const.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND, event_nsd['data']) except queue.Empty: asserts.fail( 'Timed out while waiting for %s on Subscriber' % nsd_const.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND) # S resolves IP address of P from NSD service discovery self.subscriber.droid.nsdResolveService(event_nsd['data']) try: event_nsd = self.subscriber.ed.wait_for_event( nsd_const.RESOLVE_LISTENER_EVENT, filter_callbacks, EVENT_TIMEOUT, key=nsd_const.RESOLVE_LISTENER_DATA_CALLBACK, name=nsd_const.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED) self.log.info( 'Subscriber %s: %s', nsd_const.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED, event_nsd['data']) except queue.Empty: asserts.fail( 'Timed out while waiting for %s on Subscriber' % nsd_const.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED) # mDNS returns first character as '/' - strip out to get clean IPv6 pub_ipv6_from_nsd = event_nsd['data'][ nsd_const.NSD_SERVICE_INFO_HOST][1:] finally: # Stop NSD if nsd_reg is not None: self.publisher.droid.nsdUnregisterService(nsd_reg) if nsd_discovery is not None: self.subscriber.droid.nsdStopServiceDiscovery(nsd_discovery) # P starts iPerf server result, data = self.publisher.run_iperf_server("-D") asserts.assert_true(result, "Can't start iperf3 server") # S starts iPerf client result, data = self.subscriber.run_iperf_client( "%s%%%s" % (pub_ipv6_from_nsd, sub_nan_if), "-6 -J") self.log.debug(data) asserts.assert_true(result, "Failure starting/running iperf3 in client mode") self.log.debug(pprint.pformat(data)) data_json = json.loads(''.join(data)) self.log.info('iPerf3: Sent = %d bps Received = %d bps', data_json['end']['sum_sent']['bits_per_second'], data_json['end']['sum_received']['bits_per_second']) # S terminates data-path (only have to do on one end) self.subscriber.droid.connectivityUnregisterNetworkCallback( sub_req_key)
def run_iperf_max_ndp_aware_only(self, results): """Measure iperf performance on the max number of concurrent OOB NDPs, with Aware enabled and no infrastructure connection - i.e. device is not associated to an AP. Note: the test requires MAX_NDP + 1 devices to be validated. If these are not available the test will fail. Args: results: Dictionary into which to place test results. """ dut = self.android_devices[0] # get max NDP: using first available device (assumes all devices are the # same) max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS] asserts.assert_true( len(self.android_devices) > max_ndp, 'Needed %d devices to run the test, have %d' % (max_ndp + 1, len(self.android_devices))) # create all NDPs dut_aware_if = None dut_ipv6 = None peers_aware_ifs = [] peers_ipv6s = [] dut_requests = [] peers_requests = [] for i in range(max_ndp): (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6, resp_ipv6) = autils.create_oob_ndp(dut, self.android_devices[i + 1]) self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if) self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6, resp_ipv6) dut_requests.append(init_req_key) peers_requests.append(resp_req_key) if dut_aware_if is None: dut_aware_if = init_aware_if else: asserts.assert_equal( dut_aware_if, init_aware_if, "DUT (Initiator) interface changed on subsequent NDPs!?") if dut_ipv6 is None: dut_ipv6 = init_ipv6 else: asserts.assert_equal( dut_ipv6, init_ipv6, "DUT (Initiator) IPv6 changed on subsequent NDPs!?") peers_aware_ifs.append(resp_aware_if) peers_ipv6s.append(resp_ipv6) # create threads, start them, and wait for all to finish base_port = 5000 q = queue.Queue() threads = [] for i in range(max_ndp): threads.append( threading.Thread(target=self.run_iperf, args=(q, dut, self.android_devices[i + 1], peers_aware_ifs[i], dut_ipv6, base_port + i))) for thread in threads: thread.start() for thread in threads: thread.join() # cleanup for i in range(max_ndp): dut.droid.connectivityUnregisterNetworkCallback(dut_requests[i]) self.android_devices[ i + 1].droid.connectivityUnregisterNetworkCallback( peers_requests[i]) # collect data for i in range(max_ndp): results[i] = {} result, data = q.get() asserts.assert_true( result, "Failure starting/running iperf3 in client mode") self.log.debug(pprint.pformat(data)) data_json = json.loads("".join(data)) if "error" in data_json: asserts.fail("iperf run failed: %s" % data_json["error"], extras=data_json) results[i]["tx_rate"] = data_json["end"]["sum_sent"][ "bits_per_second"] results[i]["rx_rate"] = data_json["end"]["sum_received"][ "bits_per_second"] self.log.info("iPerf3: Sent = %d bps Received = %d bps", results[i]["tx_rate"], results[i]["rx_rate"])
def test_nan_messaging(self): """Perform NAN configuration, discovery, and large message exchange. Configuration: 2 devices, one acting as Publisher (P) and the other as Subscriber (S) Logical steps: * P & S initiate NAN clustering (if not already up) * P & S wait for NAN connection confirmation * P starts publishing * S starts subscribing * S waits for a match (discovery) notification * S sends XX messages to P * S confirms that all XXX messages were transmitted * P confirms that all XXX messages are received """ self.publisher = self.android_devices[0] self.subscriber = self.android_devices[1] num_async_messages = 100 # Start Test pub_connect_id = self.exec_connect(self.publisher, self.config_request1, "publisher") sub_connect_id = self.exec_connect(self.subscriber, self.config_request2, "subscriber") pub_id = self.publisher.droid.wifiNanPublish(pub_connect_id, self.publish_config) sub_id = self.subscriber.droid.wifiNanSubscribe( sub_connect_id, self.subscribe_config) try: event_sub_match = self.subscriber.ed.pop_event( nan_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT) self.log.info('%s: %s', nan_const.SESSION_CB_ON_SERVICE_DISCOVERED, event_sub_match['data']) except queue.Empty: asserts.fail('Timed out while waiting for %s on Subscriber' % nan_const.SESSION_CB_ON_SERVICE_DISCOVERED) self.log.debug(event_sub_match) # send all messages at once for i in range(num_async_messages): self.msg_id = self.msg_id + 1 self.subscriber.droid.wifiNanSendMessage( sub_id, event_sub_match['data']['peerId'], "msg %s" % i, self.msg_id, nan_const.MAX_TX_RETRIES) # wait for all messages to be transmitted correctly num_tx_ok = 0 num_tx_fail = 0 events_regex = '%s|%s' % (nan_const.SESSION_CB_ON_MESSAGE_SEND_FAILED, nan_const.SESSION_CB_ON_MESSAGE_SENT) while (num_tx_ok + num_tx_fail) < num_async_messages: try: events = self.subscriber.ed.pop_events(events_regex, EVENT_TIMEOUT) for event in events: if event['name'] == nan_const.SESSION_CB_ON_MESSAGE_SENT: num_tx_ok = num_tx_ok + 1 if event[ 'name'] == nan_const.SESSION_CB_ON_MESSAGE_SEND_FAILED: num_tx_fail = num_tx_fail + 1 except queue.Empty: self.log.warning( 'Timed out while waiting for %s on Subscriber' ' - %d events received', events_regex, num_tx_ok + num_tx_fail) break self.log.info('Transmission stats: %d success, %d fail', num_tx_ok, num_tx_fail) # validate that all messages are received (not just the correct # number of messages - since on occassion there may be duplicates # received). num_received = 0 num_unique_received = 0 messages = {} while num_unique_received != num_async_messages: try: event = self.publisher.ed.pop_event( nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT) msg = event['data']['messageAsString'] num_received = num_received + 1 if msg not in messages: num_unique_received = num_unique_received + 1 messages[msg] = 0 messages[msg] = messages[msg] + 1 self.log.debug('%s: %s', nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, msg) except queue.Empty: asserts.fail('Timed out while waiting for %s on Publisher: %d ' 'messages received, %d unique message' % (nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, num_received, num_unique_received)) self.log.info('Reception stats: %d received (%d unique)', num_received, num_unique_received) if num_received > num_unique_received: self.log.info('%d duplicate receptions of %d messages: %s', num_received - num_unique_received, num_received, messages) self.publisher.droid.wifiNanDestroyDiscoverySession(pub_id) self.subscriber.droid.wifiNanDestroyDiscoverySession(sub_id)
def run_nan_discovery_session(self, discovery_config): """Perform NAN configuration, discovery, and message exchange. Configuration: 2 devices, one acting as Publisher (P) and the other as Subscriber (S) Logical steps: * P & S initiate NAN clustering (if not already up) * P & S wait for NAN connection confirmation * P starts publishing * S starts subscribing * S waits for a match (discovery) notification * S sends a message to P, confirming that sent successfully * P waits for a message and confirms that received (uncorrupted) * P sends a message to S, confirming that sent successfully * S waits for a message and confirms that received (uncorrupted) Args: discovery_configs: Array of (publish_config, subscribe_config) Returns: True if discovery succeeds, else false. """ # Configure Test self.publisher = self.android_devices[0] self.subscriber = self.android_devices[1] sub2pub_msg = "How are you doing? 你好嗎?" pub2sub_msg = "Doing ok - thanks! 做的不錯 - 謝謝!" # Start Test pub_connect_id = self.exec_connect(self.publisher, self.config_request1, "publisher") sub_connect_id = self.exec_connect(self.subscriber, self.config_request2, "subscriber") try: pub_id = self.publisher.droid.wifiNanPublish( pub_connect_id, discovery_config[0]) sub_id = self.subscriber.droid.wifiNanSubscribe( sub_connect_id, discovery_config[1]) try: event_sub_match = self.subscriber.ed.pop_event( nan_const.SESSION_CB_ON_SERVICE_DISCOVERED, EVENT_TIMEOUT) self.log.info('%s: %s', nan_const.SESSION_CB_ON_SERVICE_DISCOVERED, event_sub_match['data']) except queue.Empty: asserts.fail('Timed out while waiting for %s on Subscriber' % nan_const.SESSION_CB_ON_SERVICE_DISCOVERED) self.log.debug(event_sub_match) self.reliable_tx(self.subscriber, sub_id, event_sub_match['data']['peerId'], sub2pub_msg) try: event_pub_rx = self.publisher.ed.pop_event( nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT) self.log.info('%s: %s', nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, event_pub_rx['data']) asserts.assert_equal( event_pub_rx['data']['messageAsString'], sub2pub_msg, "Subscriber -> publisher message corrupted") except queue.Empty: asserts.fail('Timed out while waiting for %s on publisher' % nan_const.SESSION_CB_ON_MESSAGE_RECEIVED) self.reliable_tx(self.publisher, pub_id, event_pub_rx['data']['peerId'], pub2sub_msg) try: event_sub_rx = self.subscriber.ed.pop_event( nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, EVENT_TIMEOUT) self.log.info('%s: %s', nan_const.SESSION_CB_ON_MESSAGE_RECEIVED, event_sub_rx['data']) asserts.assert_equal( event_sub_rx['data']['messageAsString'], pub2sub_msg, "Publisher -> subscriber message corrupted") except queue.Empty: asserts.fail('Timed out while waiting for %s on subscriber' % nan_const.SESSION_CB_ON_MESSAGE_RECEIVED) finally: self.publisher.droid.wifiNanDestroyDiscoverySession(pub_id) self.subscriber.droid.wifiNanDestroyDiscoverySession(sub_id)
def start_dpp_as_initiator_enrollee(self, security, use_mac, cause_timeout=False, invalid_config=False): """ Test Easy Connect (DPP) as initiator enrollee. 1. Enable wifi, if needed 2. Start DPP as responder-configurator on helper device 3. Start DPP as initiator enrollee on dut 4. Check if configuration received successfully 5. Delete the URI from helper device 6. Remove the config. Args: security: Security type, a string "SAE" or "PSK" use_mac: A boolean indicating whether to use the device's MAC address (if True) or use a Broadcast (if False). cause_timeout: Intentionally don't start the responder to cause a timeout invalid_config: Responder to intentionally send malformed configuration """ if not self.dut.droid.wifiIsEasyConnectSupported(): self.log.warning("Easy Connect is not supported on device!") return wutils.wifi_toggle_state(self.dut, True) if use_mac: mac = autils.get_mac_addr(self.helper_dev, "wlan0") else: mac = None # Generate a URI with default info and channel uri_id = self.gen_uri(self.helper_dev, mac=mac) # Get the URI. This is equal to scanning a QR code configurator_uri = self.get_uri(self.helper_dev, uri_id) if not cause_timeout: # Start DPP as an configurator-responder for STA on helper device ssid = self.start_responder_configurator( self.helper_dev, security=security, invalid_config=invalid_config) else: self.log.info( "Not starting a responder configurator on helper device, on purpose") ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) self.log.info("Starting DPP in Enrollee-Initiator mode") # Start DPP as enrollee-initiator on dut self.dut.droid.startEasyConnectAsEnrolleeInitiator(configurator_uri) network_id = 0 start_time = time.time() while time.time() < start_time + self.DPP_TEST_TIMEOUT: dut_event = self.dut.ed.pop_event(self.DPP_TEST_EVENT_DPP_CALLBACK, self.DPP_TEST_TIMEOUT) if dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_ENROLLEE_SUCCESS: if cause_timeout or invalid_config: asserts.fail( "Unexpected DPP success, status code: %s" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) else: self.dut.log.info("DPP Configuration received success") network_id = dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_NETWORK_ID] self.dut.log.info("NetworkID: %d" % network_id) break if dut_event[self.DPP_TEST_EVENT_DATA][ self .DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS: asserts.fail( "DPP failure, unexpected result: %s" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) break if dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_PROGRESS: self.dut.log.info("DPP progress event") val = dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS] if val == 0: self.dut.log.info("DPP Authentication success") elif val == 1: self.dut.log.info("DPP Response pending") continue if dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_FAILURE: if cause_timeout or invalid_config: self.dut.log.info( "Error %s occurred, as expected" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) else: asserts.fail( "DPP failure, status code: %s" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) break asserts.fail("Unknown message received") # Clear all pending events. self.dut.ed.clear_all_events() # Stop responder self.stop_responder(self.helper_dev) # Delete URI self.del_uri(self.helper_dev, uri_id) if not (invalid_config or cause_timeout): # Check that the saved network is what we expect asserts.assert_true( self.check_network_config_saved(ssid, security, network_id), "Could not find the expected network: %s" % ssid) asserts.assert_true( self.forget_network(network_id), "Test network not deleted from configured networks.")
def start_dpp_as_initiator_configurator(self, security, use_mac, responder_chan="81/1", responder_freq=2412, net_role=DPP_TEST_NETWORK_ROLE_STA, cause_timeout=False, fail_authentication=False, invalid_uri=False): """ Test Easy Connect (DPP) as initiator configurator. 1. Enable wifi, if needed 2. Create and save a random config. 3. Generate a URI using the helper device 4. Start DPP as responder-enrollee on helper device 5. Start DPP as initiator configurator on dut 6. Check if configurator sent successfully 7. Delete the URI from helper device 8. Remove the config. Args: security: Security type, a string "SAE" or "PSK" use_mac: A boolean indicating whether to use the device's MAC address (if True) or use a Broadcast (if False). responder_chan: Responder channel to specify in the URI responder_freq: Frequency that the Responder would actually listen on. Note: To succeed, there must be a correlation between responder_chan, which is what the URI advertises, and responder_freq which is the actual frequency. See: https://en.wikipedia.org/wiki/List_of_WLAN_channels net_role: Network role, a string "sta" or "ap" cause_timeout: Intentionally don't start the responder to cause a timeout fail_authentication: Fail authentication by corrupting the responder's key invalid_uri: Use garbage string instead of a URI """ if not self.dut.droid.wifiIsEasyConnectSupported(): self.log.warning("Easy Connect is not supported on device!") return wutils.wifi_toggle_state(self.dut, True) test_network_id = self.create_and_save_wifi_network_config(security) if use_mac: mac = autils.get_mac_addr(self.helper_dev, "wlan0") else: mac = None if invalid_uri: enrollee_uri = "dskjgnkdjfgnkdsjfgnsDFGDIFGKDSJFGFDbgjdsnbkjdfnkbgsdfgFDSGSDfgesouhgureho" \ "iu3ht98368903434089ut4958763094u0934ujg094j5oifegjfds" else: # Generate a URI with default info and channel uri_id = self.gen_uri(self.helper_dev, chan=responder_chan, mac=mac) # Get the URI. This is equal to scanning a QR code enrollee_uri = self.get_uri(self.helper_dev, uri_id) # Corrupt the responder key if required if fail_authentication: enrollee_uri = enrollee_uri[:80] + "DeAdBeeF" + enrollee_uri[88:] self.log.info("Corrupted enrollee URI: %s" % enrollee_uri) if not cause_timeout: # Start DPP as an enrolle-responder for STA on helper device self.start_responder_enrollee(self.helper_dev, freq=responder_freq, net_role=net_role) else: self.log.info("Not starting DPP responder on purpose") self.log.info("Starting DPP in Configurator-Initiator mode") # Start DPP as configurator-initiator on dut self.dut.droid.startEasyConnectAsConfiguratorInitiator(enrollee_uri, test_network_id, net_role) start_time = time.time() while time.time() < start_time + self.DPP_TEST_TIMEOUT: dut_event = self.dut.ed.pop_event(self.DPP_TEST_EVENT_DPP_CALLBACK, self.DPP_TEST_TIMEOUT) if dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_TYPE] \ == self.DPP_TEST_EVENT_ENROLLEE_SUCCESS: asserts.fail("DPP failure, unexpected result!") break if dut_event[self.DPP_TEST_EVENT_DATA][ self .DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS: if cause_timeout or fail_authentication or invalid_uri: asserts.fail( "Unexpected DPP success, status code: %s" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) else: val = dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_STATUS] if val == 0: self.dut.log.info("DPP Configuration sent success") break if dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_PROGRESS: self.dut.log.info("DPP progress event") val = dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS] if val == 0: self.dut.log.info("DPP Authentication success") elif val == 1: self.dut.log.info("DPP Response pending") continue if dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_FAILURE: if cause_timeout or fail_authentication or invalid_uri: self.dut.log.info( "Error %s occurred, as expected" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) else: asserts.fail( "DPP failure, status code: %s" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) break # Clear all pending events. self.dut.ed.clear_all_events() # Stop responder self.stop_responder(self.helper_dev) if not invalid_uri: # Delete URI self.del_uri(self.helper_dev, uri_id) asserts.assert_true( self.forget_network(test_network_id), "Test network not deleted from configured networks.")
def start_responder_configurator(self, device, freq=2412, net_role=DPP_TEST_NETWORK_ROLE_STA, security=DPP_TEST_SECURITY_SAE, invalid_config=False): """Start a responder on helper device Args: device: Device object freq: Frequency to listen on net_role: Network role to configure security: Security type: SAE or PSK invalid_config: Send invalid configuration (negative test) Returns: ssid: SSID name of the network to be configured """ if not net_role or (net_role != self.DPP_TEST_NETWORK_ROLE_STA and net_role != self.DPP_TEST_NETWORK_ROLE_AP): asserts.fail("start_responder: Must specify net_role sta or ap") self.log.info("Starting Responder in Configurator mode, frequency %sMHz" % freq) conf = "conf=%s-" % net_role use_psk = False if security == self.DPP_TEST_SECURITY_SAE: if not self.dut.droid.wifiIsWpa3SaeSupported(): self.log.warning("SAE not supported on device! reverting to PSK") security = self.DPP_TEST_SECURITY_PSK_PASSPHRASE if security == self.DPP_TEST_SECURITY_SAE: conf += self.WPA_SUPPLICANT_SECURITY_SAE elif security == self.DPP_TEST_SECURITY_PSK_PASSPHRASE: conf += self.WPA_SUPPLICANT_SECURITY_PSK else: conf += self.WPA_SUPPLICANT_SECURITY_PSK use_psk = True ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) self.log.debug("SSID = %s" % ssid) ssid_encoded = binascii.hexlify(ssid.encode()).decode() if use_psk: psk = utils.rand_ascii_str(16) if not invalid_config: psk_encoded = binascii.b2a_hex(psk.encode()).decode() else: # Use the psk as is without hex encoding, will make it invalid psk_encoded = psk self.log.debug("PSK = %s" % psk) else: password = utils.rand_ascii_str(8) if not invalid_config: password_encoded = binascii.b2a_hex(password.encode()).decode() else: # Use the password as is without hex encoding, will make it invalid password_encoded = password self.log.debug("Password = %s" % password) conf += " ssid=%s" % ssid_encoded if password: # SAE password or PSK passphrase conf += " pass=%s" % password_encoded else: # PSK conf += " psk=%s" % psk_encoded # Stop responder first self.stop_responder(device) cmd = "wpa_cli set dpp_configurator_params guard=1 %s" % conf device.log.debug("Command used: %s" % cmd) result = self.helper_dev.adb.shell(cmd) if "FAIL" in result: asserts.fail( "start_responder_configurator: Failure. Command used: %s" % cmd) cmd = "wpa_cli DPP_LISTEN %d role=configurator netrole=%s" % (freq, net_role) device.log.debug("Command used: %s" % cmd) result = self.helper_dev.adb.shell(cmd) if "FAIL" in result: asserts.fail( "start_responder_configurator: Failure. Command used: %s" % cmd) device.log.info("Started responder in configurator mode") return ssid