blob: 6cf1046566e3f035d8c2aa5fc373d175808f7e07 [file] [log] [blame]
Etan Cohen48adcd22017-06-08 16:59:23 -07001#!/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
17import json
18import pprint
Etan Cohend33917f2017-07-24 16:05:46 -070019import queue
20import threading
Etan Cohen48adcd22017-06-08 16:59:23 -070021import time
22
23from acts import asserts
24from acts.test_utils.net import connectivity_const as cconsts
25from acts.test_utils.wifi.aware import aware_const as aconsts
26from acts.test_utils.wifi.aware import aware_test_utils as autils
27from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
28
29
30class ThroughputTest(AwareBaseTest):
31 """Set of tests for Wi-Fi Aware to measure latency of Aware operations."""
32
Etan Cohen566792e2017-06-29 07:54:23 -070033 SERVICE_NAME = "GoogleTestServiceXYZ"
Etan Cohen48adcd22017-06-08 16:59:23 -070034
Etan Cohena1e8d502017-08-16 09:28:18 -070035 PASSPHRASE = "This is some random passphrase - very very secure!!"
36 PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
37
Etan Cohen48adcd22017-06-08 16:59:23 -070038 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
Etan Cohen566792e2017-06-29 07:54:23 -070052 def run_iperf_single_ndp_aware_only(self, use_ib, results):
Etan Cohen48adcd22017-06-08 16:59:23 -070053 """Measure iperf performance on a single NDP, with Aware enabled and no
Etan Cohen566792e2017-06-29 07:54:23 -070054 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 """
Etan Cohen48adcd22017-06-08 16:59:23 -070060 init_dut = self.android_devices[0]
Etan Cohen48adcd22017-06-08 16:59:23 -070061 resp_dut = self.android_devices[1]
Etan Cohen48adcd22017-06-08 16:59:23 -070062
Etan Cohen566792e2017-06-29 07:54:23 -070063 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)
Etan Cohen48adcd22017-06-08 16:59:23 -070076 self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
Etan Cohen48adcd22017-06-08 16:59:23 -070077 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
Etan Cohen48adcd22017-06-08 16:59:23 -070096 data_json = json.loads("".join(data))
Etan Cohen566792e2017-06-29 07:54:23 -070097 if "error" in data_json:
98 asserts.fail(
99 "iperf run failed: %s" % data_json["error"], extras=data_json)
Etan Cohen48adcd22017-06-08 16:59:23 -0700100 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"])
Etan Cohen566792e2017-06-29 07:54:23 -0700104
Etan Cohend33917f2017-07-24 16:05:46 -0700105 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
Etan Cohena1e8d502017-08-16 09:28:18 -0700215 ########################################################################
Etan Cohen566792e2017-06-29 07:54:23 -0700216
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(
Etan Cohend33917f2017-07-24 16:05:46 -0700231 "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)
Etan Cohena1e8d502017-08-16 09:28:18 -0700241
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)