1#!/usr/bin/env python3
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# 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, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16"""
17Script for testing WiFi connection and disconnection in a loop
18
19"""
20
21import time
22
23from acts import signals
24from acts.controllers.access_point import setup_ap
25from acts.controllers.ap_lib import hostapd_constants
26from acts.controllers.ap_lib import hostapd_security
27from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
28from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
29from acts.utils import rand_ascii_str
30
31
32class ConnectionStressTest(WifiBaseTest):
33    # Default number of test iterations here.
34    # Override using parameter in config file.
35    # Eg: "connection_stress_test_iterations": "50"
36    num_of_iterations = 10
37    channel_2G = hostapd_constants.AP_DEFAULT_CHANNEL_2G
38    channel_5G = hostapd_constants.AP_DEFAULT_CHANNEL_5G
39
40    def setup_class(self):
41        super().setup_class()
42        self.ssid = rand_ascii_str(10)
43        self.fd = self.fuchsia_devices[0]
44        self.dut = create_wlan_device(self.fd)
45        self.access_point = self.access_points[0]
46        self.num_of_iterations = int(
47            self.user_params.get("connection_stress_test_iterations",
48                                 self.num_of_iterations))
49        self.log.info('iterations: %d' % self.num_of_iterations)
50
51    def teardown_test(self):
52        self.dut.reset_wifi()
53        self.download_ap_logs()
54        self.access_point.stop_all_aps()
55
56    def on_fail(self, test_name, begin_time):
57        super().on_fail(test_name, begin_time)
58        self.access_point.stop_all_aps()
59
60    def start_ap(self, profile, channel, security=None):
61        """Starts an Access Point
62
63        Args:
64            profile: Profile name such as 'whirlwind'
65            channel: Channel to operate on
66        """
67        self.log.info('Profile: %s, Channel: %d' % (profile, channel))
68        setup_ap(access_point=self.access_point,
69                 profile_name=profile,
70                 channel=channel,
71                 ssid=self.ssid,
72                 security=security)
73
74    def connect_disconnect(self,
75                           ap_config,
76                           ssid=None,
77                           password=None,
78                           negative_test=False):
79        """Helper to start an AP, connect DUT to it and disconnect
80
81        Args:
82            ap_config: Dictionary contaning profile name and channel
83            ssid: ssid to connect to
84            password: password for the ssid to connect to
85        """
86        security_mode = ap_config.get('security_mode', None)
87        target_security = hostapd_constants.SECURITY_STRING_TO_DEFAULT_TARGET_SECURITY.get(
88            security_mode, None)
89
90        if security_mode:
91            security_profile = hostapd_security.Security(
92                security_mode=ap_config['security_mode'],
93                password=ap_config['password'])
94        else:
95            security_profile = None
96
97        # Start AP
98        self.start_ap(ap_config['profile'],
99                      ap_config['channel'],
100                      security=security_profile)
101
102        failed = False
103        # Connect and Disconnect several times
104        for x in range(0, self.num_of_iterations):
105            if not ssid:
106                ssid = self.ssid
107            if negative_test:
108                if not self.dut.associate(ssid,
109                                          target_pwd=password,
110                                          target_security=target_security):
111                    self.log.info(
112                        'Attempt %d. Did not associate as expected.' % x)
113                else:
114                    self.log.error('Attempt %d. Negative test successfully '
115                                   'associated. Fail.' % x)
116                    failed = True
117            else:
118                # Connect
119                if self.dut.associate(ssid, target_pwd=password):
120                    self.log.info('Attempt %d. Successfully associated' % x)
121                else:
122                    self.log.error('Attempt %d. Failed to associate.' % x)
123                    failed = True
124                # Disconnect
125                self.dut.disconnect()
126
127            # Wait a second before trying again
128            time.sleep(1)
129
130        # Stop AP
131        self.access_point.stop_all_aps()
132        if failed:
133            raise signals.TestFailure(
134                'One or more association attempt failed.')
135
136    def test_whirlwind_2g(self):
137        self.connect_disconnect({
138            'profile': 'whirlwind',
139            'channel': self.channel_2G,
140            'security_mode': None
141        })
142
143    def test_whirlwind_5g(self):
144        self.connect_disconnect({
145            'profile': 'whirlwind',
146            'channel': self.channel_5G,
147            'security_mode': None
148        })
149
150    def test_whirlwind_11ab_2g(self):
151        self.connect_disconnect({
152            'profile': 'whirlwind_11ab_legacy',
153            'channel': self.channel_2G,
154            'security_mode': None
155        })
156
157    def test_whirlwind_11ab_5g(self):
158        self.connect_disconnect({
159            'profile': 'whirlwind_11ab_legacy',
160            'channel': self.channel_5G,
161            'security_mode': None
162        })
163
164    def test_whirlwind_11ag_2g(self):
165        self.connect_disconnect({
166            'profile': 'whirlwind_11ag_legacy',
167            'channel': self.channel_2G,
168            'security_mode': None
169        })
170
171    def test_whirlwind_11ag_5g(self):
172        self.connect_disconnect({
173            'profile': 'whirlwind_11ag_legacy',
174            'channel': self.channel_5G,
175            'security_mode': None
176        })
177
178    def test_wrong_ssid_whirlwind_2g(self):
179        self.connect_disconnect(
180            {
181                'profile': 'whirlwind',
182                'channel': self.channel_2G,
183                'security_mode': None
184            },
185            ssid=rand_ascii_str(20),
186            negative_test=True)
187
188    def test_wrong_ssid_whirlwind_5g(self):
189        self.connect_disconnect(
190            {
191                'profile': 'whirlwind',
192                'channel': self.channel_5G,
193                'security_mode': None
194            },
195            ssid=rand_ascii_str(20),
196            negative_test=True)
197
198    def test_wrong_password_whirlwind_2g(self):
199        self.connect_disconnect(
200            {
201                'profile': 'whirlwind',
202                'channel': self.channel_2G,
203                'security_mode': hostapd_constants.WPA2_STRING,
204                'password': rand_ascii_str(10)
205            },
206            password=rand_ascii_str(20),
207            negative_test=True)
208
209    def test_wrong_password_whirlwind_5g(self):
210        self.connect_disconnect(
211            {
212                'profile': 'whirlwind',
213                'channel': self.channel_5G,
214                'security_mode': hostapd_constants.WPA2_STRING,
215                'password': rand_ascii_str(10)
216            },
217            password=rand_ascii_str(20),
218            negative_test=True)
219