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
17import queue
18import time
19
20from acts import asserts
21from acts.test_decorators import test_tracker_info
22from acts.test_utils.net import connectivity_const as cconsts
23from acts.test_utils.wifi.aware import aware_const as aconsts
24from acts.test_utils.wifi.aware import aware_test_utils as autils
25from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
26
27
28class DataPathStressTest(AwareBaseTest):
29
30  # Number of iterations on create/destroy Attach sessions.
31  ATTACH_ITERATIONS = 2
32
33  # Number of iterations on create/destroy NDP in each discovery session.
34  NDP_ITERATIONS = 50
35
36  # Maximum percentage of NDP setup failures over all iterations
37  MAX_FAILURE_PERCENTAGE = 1
38
39  def __init__(self, controllers):
40    AwareBaseTest.__init__(self, controllers)
41
42  ################################################################
43
44  def run_oob_ndp_stress(self, attach_iterations, ndp_iterations,
45      trigger_failure_on_index=None):
46    """Run NDP (NAN data-path) stress test creating and destroying Aware
47    attach sessions, discovery sessions, and NDPs.
48
49    Args:
50      attach_iterations: Number of attach sessions.
51      ndp_iterations: Number of NDP to be attempted per attach session.
52      trigger_failure_on_index: Trigger a failure on this NDP iteration (the
53                                mechanism is to request NDP on Initiator
54                                before issuing the requeest on the Responder).
55                                If None then no artificial failure triggered.
56    """
57    init_dut = self.android_devices[0]
58    init_dut.pretty_name = 'Initiator'
59    resp_dut = self.android_devices[1]
60    resp_dut.pretty_name = 'Responder'
61
62    ndp_init_setup_success = 0
63    ndp_init_setup_failures = 0
64    ndp_resp_setup_success = 0
65    ndp_resp_setup_failures = 0
66
67    for attach_iter in range(attach_iterations):
68      init_id = init_dut.droid.wifiAwareAttach(True)
69      autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
70      init_ident_event = autils.wait_for_event(
71          init_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
72      init_mac = init_ident_event['data']['mac']
73      time.sleep(self.device_startup_offset)
74      resp_id = resp_dut.droid.wifiAwareAttach(True)
75      autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
76      resp_ident_event = autils.wait_for_event(
77          resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
78      resp_mac = resp_ident_event['data']['mac']
79
80      # wait for for devices to synchronize with each other - there are no other
81      # mechanisms to make sure this happens for OOB discovery (except retrying
82      # to execute the data-path request)
83      time.sleep(autils.WAIT_FOR_CLUSTER)
84
85      for ndp_iteration in range(ndp_iterations):
86        if trigger_failure_on_index != ndp_iteration:
87          # Responder: request network
88          resp_req_key = autils.request_network(
89              resp_dut,
90              resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
91                  resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
92
93          # Wait a minimal amount of time to let the Responder configure itself
94          # and be ready for the request. While calling it first may be
95          # sufficient there are no guarantees that a glitch may slow the
96          # Responder slightly enough to invert the setup order.
97          time.sleep(1)
98
99          # Initiator: request network
100          init_req_key = autils.request_network(
101              init_dut,
102              init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
103                  init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
104        else:
105          # Initiator: request network
106          init_req_key = autils.request_network(
107              init_dut,
108              init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
109                  init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
110
111          # Wait a minimal amount of time to let the Initiator configure itself
112          # to guarantee failure!
113          time.sleep(2)
114
115          # Responder: request network
116          resp_req_key = autils.request_network(
117              resp_dut,
118              resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
119                  resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
120
121        # Initiator: wait for network formation
122        got_on_available = False
123        got_on_link_props = False
124        while not got_on_available or not got_on_link_props:
125          try:
126            nc_event = init_dut.ed.pop_event(cconsts.EVENT_NETWORK_CALLBACK,
127                                             autils.EVENT_NDP_TIMEOUT)
128            if nc_event['data'][
129                cconsts.NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
130              got_on_available = True
131            elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
132                  cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
133              got_on_link_props = True
134          except queue.Empty:
135            ndp_init_setup_failures = ndp_init_setup_failures + 1
136            init_dut.log.info('[Initiator] Timed out while waiting for '
137                              'EVENT_NETWORK_CALLBACK')
138            break
139
140        if got_on_available and got_on_link_props:
141          ndp_init_setup_success = ndp_init_setup_success + 1
142
143        # Responder: wait for network formation
144        got_on_available = False
145        got_on_link_props = False
146        while not got_on_available or not got_on_link_props:
147          try:
148            nc_event = resp_dut.ed.pop_event(cconsts.EVENT_NETWORK_CALLBACK,
149                                             autils.EVENT_NDP_TIMEOUT)
150            if nc_event['data'][
151                cconsts.NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
152              got_on_available = True
153            elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
154                  cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
155              got_on_link_props = True
156          except queue.Empty:
157            ndp_resp_setup_failures = ndp_resp_setup_failures + 1
158            init_dut.log.info('[Responder] Timed out while waiting for '
159                              'EVENT_NETWORK_CALLBACK')
160            break
161
162        if got_on_available and got_on_link_props:
163          ndp_resp_setup_success = ndp_resp_setup_success + 1
164
165        # clean-up
166        init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
167        resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
168
169      # clean-up at end of iteration
170      init_dut.droid.wifiAwareDestroy(init_id)
171      resp_dut.droid.wifiAwareDestroy(resp_id)
172
173    results = {}
174    results['ndp_init_setup_success'] = ndp_init_setup_success
175    results['ndp_init_setup_failures'] = ndp_init_setup_failures
176    results['ndp_resp_setup_success'] = ndp_resp_setup_success
177    results['ndp_resp_setup_failures'] = ndp_resp_setup_failures
178    max_failures = (
179        self.MAX_FAILURE_PERCENTAGE * attach_iterations * ndp_iterations / 100)
180    if max_failures == 0:
181      max_failures = 1
182    if trigger_failure_on_index is not None:
183      max_failures = max_failures + 1 # for the triggered failure
184    asserts.assert_true(
185      (ndp_init_setup_failures + ndp_resp_setup_failures) < (2 * max_failures),
186      'NDP setup failure rate exceeds threshold', extras=results)
187    asserts.explicit_pass("test_oob_ndp_stress* done", extras=results)
188
189  @test_tracker_info(uuid="a20a96ba-e71f-4d31-b850-b88a75381981")
190  def test_oob_ndp_stress(self):
191    """Run NDP (NAN data-path) stress test creating and destroying Aware
192    attach sessions, discovery sessions, and NDPs."""
193    self.run_oob_ndp_stress(self.ATTACH_ITERATIONS, self.NDP_ITERATIONS)
194
195  @test_tracker_info(uuid="1fb4a383-bf1a-411a-a904-489dd9e29c6a")
196  def test_oob_ndp_stress_failure_case(self):
197    """Run NDP (NAN data-path) stress test creating and destroying Aware
198    attach sessions, discovery sessions, and NDPs.
199
200    Verify recovery from failure by triggering an artifical failure and
201    verifying that all subsequent iterations succeed.
202    """
203    self.run_oob_ndp_stress(attach_iterations=1,
204                            ndp_iterations=10,
205                            trigger_failure_on_index=3)
206