Home | History | Annotate | Download | only in performance
      1 #!/usr/bin/python3.4
      2 #
      3 #   Copyright 2017 - The Android Open Source Project
      4 #
      5 #   Licensed under the Apache License, Version 2.0 (the "License");
      6 #   you may not use this file except in compliance with the License.
      7 #   You may obtain a copy of the License at
      8 #
      9 #       http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 #   Unless required by applicable law or agreed to in writing, software
     12 #   distributed under the License is distributed on an "AS IS" BASIS,
     13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 #   See the License for the specific language governing permissions and
     15 #   limitations under the License.
     16 
     17 import json
     18 import pprint
     19 import queue
     20 import threading
     21 import time
     22 
     23 from acts import asserts
     24 from acts.test_utils.net import connectivity_const as cconsts
     25 from acts.test_utils.wifi.aware import aware_const as aconsts
     26 from acts.test_utils.wifi.aware import aware_test_utils as autils
     27 from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
     28 
     29 
     30 class ThroughputTest(AwareBaseTest):
     31   """Set of tests for Wi-Fi Aware to measure latency of Aware operations."""
     32 
     33   SERVICE_NAME = "GoogleTestServiceXYZ"
     34 
     35   PASSPHRASE = "This is some random passphrase - very very secure!!"
     36   PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
     37 
     38   def __init__(self, controllers):
     39     AwareBaseTest.__init__(self, controllers)
     40 
     41   def request_network(self, dut, ns):
     42     """Request a Wi-Fi Aware network.
     43 
     44     Args:
     45       dut: Device
     46       ns: Network specifier
     47     Returns: the request key
     48     """
     49     network_req = {"TransportType": 5, "NetworkSpecifier": ns}
     50     return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
     51 
     52   def run_iperf_single_ndp_aware_only(self, use_ib, results):
     53     """Measure iperf performance on a single NDP, with Aware enabled and no
     54     infrastructure connection - i.e. device is not associated to an AP.
     55 
     56     Args:
     57       use_ib: True to use in-band discovery, False to use out-of-band discovery.
     58       results: Dictionary into which to place test results.
     59     """
     60     init_dut = self.android_devices[0]
     61     resp_dut = self.android_devices[1]
     62 
     63     if use_ib:
     64       # note: Publisher = Responder, Subscribe = Initiator
     65       (resp_req_key, init_req_key, resp_aware_if,
     66        init_aware_if, resp_ipv6, init_ipv6) = autils.create_ib_ndp(
     67            resp_dut, init_dut,
     68            autils.create_discovery_config(self.SERVICE_NAME,
     69                                           aconsts.PUBLISH_TYPE_UNSOLICITED),
     70            autils.create_discovery_config(self.SERVICE_NAME,
     71                                           aconsts.SUBSCRIBE_TYPE_PASSIVE),
     72            self.device_startup_offset)
     73     else:
     74       (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
     75       resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
     76     self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
     77     self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
     78                   resp_ipv6)
     79 
     80     # Run iperf3
     81     result, data = init_dut.run_iperf_server("-D")
     82     asserts.assert_true(result, "Can't start iperf3 server")
     83 
     84     result, data = resp_dut.run_iperf_client(
     85         "%s%%%s" % (init_ipv6, resp_aware_if), "-6 -J")
     86     self.log.debug(data)
     87     asserts.assert_true(result,
     88                         "Failure starting/running iperf3 in client mode")
     89     self.log.debug(pprint.pformat(data))
     90 
     91     # clean-up
     92     resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
     93     init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
     94 
     95     # Collect results
     96     data_json = json.loads("".join(data))
     97     if "error" in data_json:
     98       asserts.fail(
     99           "iperf run failed: %s" % data_json["error"], extras=data_json)
    100     results["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"]
    101     results["rx_rate"] = data_json["end"]["sum_received"]["bits_per_second"]
    102     self.log.info("iPerf3: Sent = %d bps Received = %d bps", results["tx_rate"],
    103                   results["rx_rate"])
    104 
    105   def run_iperf(self, q, dut, peer_dut, peer_aware_if, dut_ipv6, port):
    106     """Runs iperf and places results in the queue.
    107 
    108     Args:
    109       q: The queue into which to place the results
    110       dut: The DUT on which to run the iperf server command.
    111       peer_dut: The DUT on which to run the iperf client command.
    112       peer_aware_if: The interface on the DUT.
    113       dut_ipv6: The IPv6 address of the server.
    114       port: The port to use for the server and client.
    115     """
    116     result, data = dut.run_iperf_server("-D -p %d" % port)
    117     asserts.assert_true(result, "Can't start iperf3 server")
    118 
    119     result, data = peer_dut.run_iperf_client(
    120         "%s%%%s" % (dut_ipv6, peer_aware_if), "-6 -J -p %d" % port)
    121     self.log.debug(data)
    122     q.put((result, data))
    123 
    124   def run_iperf_max_ndp_aware_only(self, results):
    125     """Measure iperf performance on the max number of concurrent OOB NDPs, with
    126     Aware enabled and no infrastructure connection - i.e. device is not
    127     associated to an AP.
    128 
    129     Note: the test requires MAX_NDP + 1 devices to be validated. If these are
    130     not available the test will fail.
    131 
    132     Args:
    133       results: Dictionary into which to place test results.
    134     """
    135     dut = self.android_devices[0]
    136 
    137     # get max NDP: using first available device (assumes all devices are the
    138     # same)
    139     max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS]
    140     asserts.assert_true(len(self.android_devices) > max_ndp,
    141                         'Needed %d devices to run the test, have %d' %
    142                         (max_ndp + 1, len(self.android_devices)))
    143 
    144     # create all NDPs
    145     dut_aware_if = None
    146     dut_ipv6 = None
    147     peers_aware_ifs = []
    148     peers_ipv6s = []
    149     dut_requests = []
    150     peers_requests = []
    151     for i in range(max_ndp):
    152       (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
    153        resp_ipv6) = autils.create_oob_ndp(dut, self.android_devices[i + 1])
    154       self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
    155       self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
    156                     resp_ipv6)
    157 
    158       dut_requests.append(init_req_key)
    159       peers_requests.append(resp_req_key)
    160       if dut_aware_if is None:
    161         dut_aware_if = init_aware_if
    162       else:
    163         asserts.assert_equal(
    164             dut_aware_if, init_aware_if,
    165             "DUT (Initiator) interface changed on subsequent NDPs!?")
    166       if dut_ipv6 is None:
    167         dut_ipv6 = init_ipv6
    168       else:
    169         asserts.assert_equal(
    170             dut_ipv6, init_ipv6,
    171             "DUT (Initiator) IPv6 changed on subsequent NDPs!?")
    172       peers_aware_ifs.append(resp_aware_if)
    173       peers_ipv6s.append(resp_ipv6)
    174 
    175     # create threads, start them, and wait for all to finish
    176     base_port = 5000
    177     q = queue.Queue()
    178     threads = []
    179     for i in range(max_ndp):
    180       threads.append(
    181           threading.Thread(
    182               target=self.run_iperf,
    183               args=(q, dut, self.android_devices[i + 1], peers_aware_ifs[i],
    184                     dut_ipv6, base_port + i)))
    185 
    186     for thread in threads:
    187       thread.start()
    188 
    189     for thread in threads:
    190       thread.join()
    191 
    192     # cleanup
    193     for i in range(max_ndp):
    194       dut.droid.connectivityUnregisterNetworkCallback(dut_requests[i])
    195       self.android_devices[i + 1].droid.connectivityUnregisterNetworkCallback(
    196           peers_requests[i])
    197 
    198     # collect data
    199     for i in range(max_ndp):
    200       results[i] = {}
    201       result, data = q.get()
    202       asserts.assert_true(result,
    203                           "Failure starting/running iperf3 in client mode")
    204       self.log.debug(pprint.pformat(data))
    205       data_json = json.loads("".join(data))
    206       if "error" in data_json:
    207         asserts.fail(
    208             "iperf run failed: %s" % data_json["error"], extras=data_json)
    209       results[i]["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"]
    210       results[i]["rx_rate"] = data_json["end"]["sum_received"][
    211           "bits_per_second"]
    212       self.log.info("iPerf3: Sent = %d bps Received = %d bps",
    213                     results[i]["tx_rate"], results[i]["rx_rate"])
    214 
    215   ########################################################################
    216 
    217   def test_iperf_single_ndp_aware_only_ib(self):
    218     """Measure throughput using iperf on a single NDP, with Aware enabled and
    219     no infrastructure connection. Use in-band discovery."""
    220     results = {}
    221     self.run_iperf_single_ndp_aware_only(use_ib=True, results=results)
    222     asserts.explicit_pass(
    223         "test_iperf_single_ndp_aware_only_ib passes", extras=results)
    224 
    225   def test_iperf_single_ndp_aware_only_oob(self):
    226     """Measure throughput using iperf on a single NDP, with Aware enabled and
    227     no infrastructure connection. Use out-of-band discovery."""
    228     results = {}
    229     self.run_iperf_single_ndp_aware_only(use_ib=False, results=results)
    230     asserts.explicit_pass(
    231         "test_iperf_single_ndp_aware_only_oob passes", extras=results)
    232 
    233   def test_iperf_max_ndp_aware_only_oob(self):
    234     """Measure throughput using iperf on all possible concurrent NDPs, with
    235     Aware enabled and no infrastructure connection. Use out-of-band discovery.
    236     """
    237     results = {}
    238     self.run_iperf_max_ndp_aware_only(results=results)
    239     asserts.explicit_pass(
    240         "test_iperf_max_ndp_aware_only_oob passes", extras=results)
    241 
    242   ########################################################################
    243 
    244   def run_iperf_max_ndi_aware_only(self, sec_configs, results):
    245     """Measure iperf performance on multiple NDPs between 2 devices using
    246     different security configurations (and hence different NDIs). Test with
    247     Aware enabled and no infrastructure connection - i.e. device is not
    248     associated to an AP.
    249 
    250     The security configuration can be:
    251     - None: open
    252     - String: passphrase
    253     - otherwise: PMK (byte array)
    254 
    255     Args:
    256       sec_configs: list of security configurations
    257       results: Dictionary into which to place test results.
    258     """
    259     init_dut = self.android_devices[0]
    260     init_dut.pretty_name = "Initiator"
    261     resp_dut = self.android_devices[1]
    262     resp_dut.pretty_name = "Responder"
    263 
    264     asserts.skip_if(init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]
    265                     < len(sec_configs) or
    266                     resp_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]
    267                     < len(sec_configs),
    268                     "Initiator or Responder do not support multiple NDIs")
    269 
    270 
    271     init_id, init_mac = autils.attach_with_identity(init_dut)
    272     resp_id, resp_mac = autils.attach_with_identity(resp_dut)
    273 
    274     # wait for for devices to synchronize with each other - there are no other
    275     # mechanisms to make sure this happens for OOB discovery (except retrying
    276     # to execute the data-path request)
    277     time.sleep(autils.WAIT_FOR_CLUSTER)
    278 
    279     resp_req_keys = []
    280     init_req_keys = []
    281     resp_aware_ifs = []
    282     init_aware_ifs = []
    283     resp_aware_ipv6s = []
    284     init_aware_ipv6s = []
    285 
    286     for sec in sec_configs:
    287       # Responder: request network
    288       resp_req_key = autils.request_network(resp_dut,
    289                                             autils.get_network_specifier(
    290                                                 resp_dut, resp_id,
    291                                                 aconsts.DATA_PATH_RESPONDER,
    292                                                 init_mac, sec))
    293       resp_req_keys.append(resp_req_key)
    294 
    295       # Initiator: request network
    296       init_req_key = autils.request_network(init_dut,
    297                                             autils.get_network_specifier(
    298                                                 init_dut, init_id,
    299                                                 aconsts.DATA_PATH_INITIATOR,
    300                                                 resp_mac, sec))
    301       init_req_keys.append(init_req_key)
    302 
    303       # Wait for network
    304       init_net_event = autils.wait_for_event_with_keys(
    305           init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
    306           (cconsts.NETWORK_CB_KEY_EVENT,
    307            cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
    308           (cconsts.NETWORK_CB_KEY_ID, init_req_key))
    309       resp_net_event = autils.wait_for_event_with_keys(
    310           resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
    311           (cconsts.NETWORK_CB_KEY_EVENT,
    312            cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
    313           (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
    314 
    315       resp_aware_ifs.append(
    316           resp_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
    317       init_aware_ifs.append(
    318           init_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
    319 
    320       resp_aware_ipv6s.append(
    321           autils.get_ipv6_addr(resp_dut, resp_aware_ifs[-1]))
    322       init_aware_ipv6s.append(
    323           autils.get_ipv6_addr(init_dut, init_aware_ifs[-1]))
    324 
    325     self.log.info("Initiator interfaces/ipv6: %s / %s", init_aware_ifs,
    326                   init_aware_ipv6s)
    327     self.log.info("Responder interfaces/ipv6: %s / %s", resp_aware_ifs,
    328                   resp_aware_ipv6s)
    329 
    330     # create threads, start them, and wait for all to finish
    331     base_port = 5000
    332     q = queue.Queue()
    333     threads = []
    334     for i in range(len(sec_configs)):
    335       threads.append(
    336           threading.Thread(
    337               target=self.run_iperf,
    338               args=(q, init_dut, resp_dut, resp_aware_ifs[i], init_aware_ipv6s[
    339                   i], base_port + i)))
    340 
    341     for thread in threads:
    342       thread.start()
    343 
    344     for thread in threads:
    345       thread.join()
    346 
    347     # release requests
    348     for resp_req_key in resp_req_keys:
    349       resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
    350     for init_req_key in init_req_keys:
    351       init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
    352 
    353 
    354     # collect data
    355     for i in range(len(sec_configs)):
    356       results[i] = {}
    357       result, data = q.get()
    358       asserts.assert_true(result,
    359                           "Failure starting/running iperf3 in client mode")
    360       self.log.debug(pprint.pformat(data))
    361       data_json = json.loads("".join(data))
    362       if "error" in data_json:
    363         asserts.fail(
    364             "iperf run failed: %s" % data_json["error"], extras=data_json)
    365       results[i]["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"]
    366       results[i]["rx_rate"] = data_json["end"]["sum_received"][
    367         "bits_per_second"]
    368       self.log.info("iPerf3: Sent = %d bps Received = %d bps",
    369                     results[i]["tx_rate"], results[i]["rx_rate"])
    370 
    371   def test_iperf_max_ndi_aware_only_passphrases(self):
    372     """Test throughput for multiple NDIs configured with different passphrases.
    373     """
    374     results = {}
    375     self.run_iperf_max_ndi_aware_only(
    376         [self.PASSPHRASE, self.PASSPHRASE2], results=results)
    377     asserts.explicit_pass(
    378         "test_iperf_max_ndi_aware_only_passphrases passes", extras=results)
    379