• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
2#
3#   Copyright 2018 - 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 pprint
18import queue
19import threading
20import time
21
22import acts.base_test
23import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
24import acts_contrib.test_utils.tel.tel_test_utils as tutils
25
26from acts import asserts
27from acts import signals
28from acts import utils
29from acts.test_decorators import test_tracker_info
30from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
31from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
32from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
33WifiEnums = wutils.WifiEnums
34
35WAIT_FOR_AUTO_CONNECT = 40
36WAIT_BEFORE_CONNECTION = 30
37
38TIMEOUT = 5
39PING_ADDR = 'www.google.com'
40
41class WifiStressTest(WifiBaseTest):
42    """WiFi Stress test class.
43
44    Test Bed Requirement:
45    * Two Android device
46    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
47      network.
48    """
49    def __init__(self, configs):
50        super().__init__(configs)
51        self.enable_packet_log = True
52
53    def setup_class(self):
54        super().setup_class()
55
56        self.dut = self.android_devices[0]
57        # Note that test_stress_softAP_startup_and_stop_5g will always fail
58        # when testing with a single device.
59        if len(self.android_devices) > 1:
60            self.dut_client = self.android_devices[1]
61        else:
62            self.dut_client = None
63        wutils.wifi_test_device_init(self.dut)
64        req_params = []
65        opt_param = [
66            "open_network", "reference_networks", "iperf_server_address",
67            "stress_count", "stress_hours", "attn_vals", "pno_interval",
68            "iperf_server_port"]
69        self.unpack_userparams(
70            req_param_names=req_params, opt_param_names=opt_param)
71
72        if "AccessPoint" in self.user_params:
73            self.legacy_configure_ap_and_start(ap_count=2)
74        elif "OpenWrtAP" in self.user_params:
75            self.configure_openwrt_ap_and_start(open_network=True,
76                                                wpa_network=True,
77                                                ap_count=2)
78        asserts.assert_true(
79            len(self.reference_networks) > 0,
80            "Need at least one reference network with psk.")
81        self.wpa_2g = self.reference_networks[0]["2g"]
82        self.wpa_5g = self.reference_networks[0]["5g"]
83        self.open_2g = self.open_network[0]["2g"]
84        self.open_5g = self.open_network[0]["5g"]
85        self.networks = [self.wpa_2g, self.wpa_5g, self.open_2g, self.open_5g]
86
87    def setup_test(self):
88        super().setup_test()
89        self.dut.droid.wakeLockAcquireBright()
90        self.dut.droid.wakeUpNow()
91
92    def teardown_test(self):
93        super().teardown_test()
94        if self.dut.droid.wifiIsApEnabled():
95            wutils.stop_wifi_tethering(self.dut)
96        self.dut.droid.wakeLockRelease()
97        self.dut.droid.goToSleepNow()
98        wutils.reset_wifi(self.dut)
99
100    def teardown_class(self):
101        wutils.reset_wifi(self.dut)
102        if "AccessPoint" in self.user_params:
103            del self.user_params["reference_networks"]
104            del self.user_params["open_network"]
105
106    """Helper Functions"""
107
108    def scan_and_connect_by_ssid(self, ad, network):
109        """Scan for network and connect using network information.
110
111        Args:
112            network: A dictionary representing the network to connect to.
113
114        """
115        ssid = network[WifiEnums.SSID_KEY]
116        wutils.start_wifi_connection_scan_and_ensure_network_found(ad, ssid)
117        wutils.wifi_connect(ad, network, num_of_tries=3)
118
119    def scan_and_connect_by_id(self, network, net_id):
120        """Scan for network and connect using network id.
121
122        Args:
123            net_id: Integer specifying the network id of the network.
124
125        """
126        ssid = network[WifiEnums.SSID_KEY]
127        wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
128            ssid)
129        wutils.wifi_connect_by_id(self.dut, net_id)
130
131    def run_ping(self, sec):
132        """Run ping for given number of seconds.
133
134        Args:
135            sec: Time in seconds to run teh ping traffic.
136
137        """
138        self.log.info("Running ping for %d seconds" % sec)
139        result = self.dut.adb.shell("ping -w %d %s" %(sec, PING_ADDR),
140            timeout=sec+1)
141        self.log.debug("Ping Result = %s" % result)
142        if "100% packet loss" in result:
143            raise signals.TestFailure("100% packet loss during ping")
144
145    def start_youtube_video(self, url=None, secs=60):
146        """Start a youtube video and check if it's playing.
147
148        Args:
149            url: The URL of the youtube video to play.
150            secs: Time to play video in seconds.
151
152        """
153        ad = self.dut
154        ad.log.info("Start a youtube video")
155        ad.ensure_screen_on()
156        video_played = False
157        for count in range(2):
158            ad.unlock_screen()
159            ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
160            if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
161                ad.log.info("Started a video in youtube.")
162                # Play video for given seconds.
163                time.sleep(secs)
164                video_played = True
165                break
166        if not video_played:
167            raise signals.TestFailure("Youtube video did not start. Current WiFi "
168                "state is %d" % self.dut.droid.wifiCheckState())
169
170    def add_networks(self, ad, networks):
171        """Add Wi-Fi networks to an Android device and verify the networks were
172        added correctly.
173
174        Args:
175            ad: the AndroidDevice object to add networks to.
176            networks: a list of dicts, each dict represents a Wi-Fi network.
177        """
178        for network in networks:
179            ret = ad.droid.wifiAddNetwork(network)
180            asserts.assert_true(ret != -1, "Failed to add network %s" %
181                                network)
182            ad.droid.wifiEnableNetwork(ret, 0)
183        configured_networks = ad.droid.wifiGetConfiguredNetworks()
184        self.log.debug("Configured networks: %s", configured_networks)
185
186    def connect_and_verify_connected_ssid(self, expected_con, is_pno=False):
187        """Start a scan to get the DUT connected to an AP and verify the DUT
188        is connected to the correct SSID.
189
190        Args:
191            expected_con: The expected info of the network to we expect the DUT
192                to roam to.
193        """
194        connection_info = self.dut.droid.wifiGetConnectionInfo()
195        self.log.info("Triggering network selection from %s to %s",
196                      connection_info[WifiEnums.SSID_KEY],
197                      expected_con[WifiEnums.SSID_KEY])
198        self.attenuators[0].set_atten(0)
199        if is_pno:
200            self.log.info("Wait %ss for PNO to trigger.", self.pno_interval)
201            time.sleep(self.pno_interval)
202        else:
203            # force start a single scan so we don't have to wait for the scheduled scan.
204            wutils.start_wifi_connection_scan_and_return_status(self.dut)
205            self.log.info("Wait 60s for network selection.")
206            time.sleep(60)
207        try:
208            self.log.info("Connected to %s network after network selection"
209                          % self.dut.droid.wifiGetConnectionInfo())
210            expected_ssid = expected_con[WifiEnums.SSID_KEY]
211            verify_con = {WifiEnums.SSID_KEY: expected_ssid}
212            wutils.verify_wifi_connection_info(self.dut, verify_con)
213            self.log.info("Connected to %s successfully after network selection",
214                          expected_ssid)
215        finally:
216            pass
217
218    def run_long_traffic(self, sec, args, q):
219        try:
220            # Start IPerf traffic
221            self.log.info("Running iperf client {}".format(args))
222            result, data = self.dut.run_iperf_client(self.iperf_server_address,
223                args, timeout=sec+1)
224            if not result:
225                self.log.debug("Error occurred in iPerf traffic.")
226                self.run_ping(sec)
227            q.put(True)
228        except:
229            q.put(False)
230
231    """Tests"""
232
233    @test_tracker_info(uuid="cd0016c6-58cf-4361-b551-821c0b8d2554")
234    def test_stress_toggle_wifi_state(self):
235        """Toggle WiFi state ON and OFF for N times."""
236        for count in range(self.stress_count):
237            """Test toggling wifi"""
238            try:
239                self.log.debug("Going from on to off.")
240                wutils.wifi_toggle_state(self.dut, False)
241                self.log.debug("Going from off to on.")
242                startTime = time.time()
243                wutils.wifi_toggle_state(self.dut, True)
244                startup_time = time.time() - startTime
245                self.log.debug("WiFi was enabled on the device in %s s." %
246                    startup_time)
247            except:
248                signals.TestFailure(details="", extras={"Iterations":"%d" %
249                    self.stress_count, "Pass":"%d" %count})
250        raise signals.TestPass(details="", extras={"Iterations":"%d" %
251            self.stress_count, "Pass":"%d" %(count+1)})
252
253    @test_tracker_info(uuid="4e591cec-9251-4d52-bc6e-6621507524dc")
254    def test_stress_toggle_wifi_state_bluetooth_on(self):
255        """Toggle WiFi state ON and OFF for N times when bluetooth ON."""
256        enable_bluetooth(self.dut.droid, self.dut.ed)
257        for count in range(self.stress_count):
258            """Test toggling wifi"""
259            try:
260                self.log.debug("Going from on to off.")
261                wutils.wifi_toggle_state(self.dut, False)
262                self.log.debug("Going from off to on.")
263                startTime = time.time()
264                wutils.wifi_toggle_state(self.dut, True)
265                startup_time = time.time() - startTime
266                self.log.debug("WiFi was enabled on the device in %s s." %
267                    startup_time)
268            except:
269                signals.TestFailure(details="", extras={"Iterations":"%d" %
270                    self.stress_count, "Pass":"%d" %count})
271        disable_bluetooth(self.dut.droid)
272        raise signals.TestPass(details="", extras={"Iterations":"%d" %
273            self.stress_count, "Pass":"%d" %(count+1)})
274
275    @test_tracker_info(uuid="49e3916a-9580-4bf7-a60d-a0f2545dcdde")
276    def test_stress_connect_traffic_disconnect_5g(self):
277        """Test to connect and disconnect from a network for N times.
278
279           Steps:
280               1. Scan and connect to a network.
281               2. Run IPerf to upload data for few seconds.
282               3. Disconnect.
283               4. Repeat 1-3.
284
285        """
286        for count in range(self.stress_count):
287            try:
288                net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
289                asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
290                self.scan_and_connect_by_id(self.wpa_5g, net_id)
291                # Start IPerf traffic from phone to server.
292                # Upload data for 10s.
293                args = "-p {} -t {}".format(self.iperf_server_port, 10)
294                self.log.info("Running iperf client {}".format(args))
295                result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
296                if not result:
297                    self.log.debug("Error occurred in iPerf traffic.")
298                    self.run_ping(10)
299                wutils.wifi_forget_network(self.dut,self.wpa_5g[WifiEnums.SSID_KEY])
300                time.sleep(WAIT_BEFORE_CONNECTION)
301            except:
302                raise signals.TestFailure("Network connect-disconnect failed."
303                    "Look at logs", extras={"Iterations":"%d" %
304                        self.stress_count, "Pass":"%d" %count})
305        raise signals.TestPass(details="", extras={"Iterations":"%d" %
306            self.stress_count, "Pass":"%d" %(count+1)})
307
308    @test_tracker_info(uuid="e9827dff-0755-43ec-8b50-1f9756958460")
309    def test_stress_connect_long_traffic_5g(self):
310        """Test to connect to network and hold connection for few hours.
311
312           Steps:
313               1. Scan and connect to a network.
314               2. Run IPerf to download data for few hours.
315               3. Run IPerf to upload data for few hours.
316               4. Verify no WiFi disconnects/data interruption.
317
318        """
319        self.scan_and_connect_by_ssid(self.dut, self.wpa_5g)
320        self.scan_and_connect_by_ssid(self.dut_client, self.wpa_5g)
321
322        q = queue.Queue()
323        sec = self.stress_hours * 60 * 60
324        start_time = time.time()
325
326        dl_args = "-p {} -t {} -R".format(self.iperf_server_port, sec)
327        dl = threading.Thread(target=self.run_long_traffic, args=(sec, dl_args, q))
328        dl.start()
329        dl.join()
330
331        total_time = time.time() - start_time
332        self.log.debug("WiFi state = %d" %self.dut.droid.wifiCheckState())
333        while(q.qsize() > 0):
334            if not q.get():
335                raise signals.TestFailure("Network long-connect failed.",
336                    extras={"Total Hours":"%d" %self.stress_hours,
337                    "Seconds Run":"%d" %total_time})
338        raise signals.TestPass(details="", extras={"Total Hours":"%d" %
339            self.stress_hours, "Seconds Run":"%d" %total_time})
340
341    @test_tracker_info(uuid="591d257d-9477-4a89-a220-5715c93a76a7")
342    def test_stress_youtube_5g(self):
343        """Test to connect to network and play various youtube videos.
344
345           Steps:
346               1. Scan and connect to a network.
347               2. Loop through and play a list of youtube videos.
348               3. Verify no WiFi disconnects/data interruption.
349
350        """
351        # List of Youtube 4K videos.
352        videos = ["https://www.youtube.com/watch?v=TKmGU77INaM",
353                  "https://www.youtube.com/watch?v=WNCl-69POro",
354                  "https://www.youtube.com/watch?v=dVkK36KOcqs",
355                  "https://www.youtube.com/watch?v=0wCC3aLXdOw",
356                  "https://www.youtube.com/watch?v=rN6nlNC9WQA",
357                  "https://www.youtube.com/watch?v=RK1K2bCg4J8"]
358        try:
359            self.scan_and_connect_by_ssid(self.dut, self.wpa_5g)
360            start_time = time.time()
361            for video in videos:
362                self.start_youtube_video(url=video, secs=10*60)
363        except:
364            total_time = time.time() - start_time
365            raise signals.TestFailure("The youtube stress test has failed."
366                "WiFi State = %d" %self.dut.droid.wifiCheckState(),
367                extras={"Total Hours":"1", "Seconds Run":"%d" %total_time})
368        total_time = time.time() - start_time
369        self.log.debug("WiFi state = %d" %self.dut.droid.wifiCheckState())
370        raise signals.TestPass(details="", extras={"Total Hours":"1",
371            "Seconds Run":"%d" %total_time})
372
373    @test_tracker_info(uuid="d367c83e-5b00-4028-9ed8-f7b875997d13")
374    def test_stress_wifi_failover(self):
375        """This test does aggressive failover to several networks in list.
376
377           Steps:
378               1. Add and enable few networks.
379               2. Let device auto-connect.
380               3. Remove the connected network.
381               4. Repeat 2-3.
382               5. Device should connect to a network until all networks are
383                  exhausted.
384
385        """
386        for count in range(int(self.stress_count/4)):
387            wutils.reset_wifi(self.dut)
388            ssids = list()
389            for network in self.networks:
390                ssids.append(network[WifiEnums.SSID_KEY])
391                ret = self.dut.droid.wifiAddNetwork(network)
392                asserts.assert_true(ret != -1, "Add network %r failed" % network)
393                self.dut.droid.wifiEnableNetwork(ret, 0)
394            self.dut.droid.wifiStartScan()
395            time.sleep(WAIT_FOR_AUTO_CONNECT)
396            cur_network = self.dut.droid.wifiGetConnectionInfo()
397            cur_ssid = cur_network[WifiEnums.SSID_KEY]
398            self.log.info("Cur_ssid = %s" % cur_ssid)
399            for i in range(0,len(self.networks)):
400                self.log.debug("Forget network %s" % cur_ssid)
401                wutils.wifi_forget_network(self.dut, cur_ssid)
402                time.sleep(WAIT_FOR_AUTO_CONNECT)
403                cur_network = self.dut.droid.wifiGetConnectionInfo()
404                cur_ssid = cur_network[WifiEnums.SSID_KEY]
405                self.log.info("Cur_ssid = %s" % cur_ssid)
406                if i == len(self.networks) - 1:
407                    break
408                if cur_ssid not in ssids:
409                    raise signals.TestFailure("Device did not failover to the "
410                        "expected network. SSID = %s" % cur_ssid)
411            network_config = self.dut.droid.wifiGetConfiguredNetworks()
412            self.log.info("Network Config = %s" % network_config)
413            if len(network_config):
414                raise signals.TestFailure("All the network configurations were not "
415                    "removed. Configured networks = %s" % network_config,
416                        extras={"Iterations":"%d" % self.stress_count,
417                            "Pass":"%d" %(count*4)})
418        raise signals.TestPass(details="", extras={"Iterations":"%d" %
419            self.stress_count, "Pass":"%d" %((count+1)*4)})
420
421    @test_tracker_info(uuid="2c19e8d1-ac16-4d7e-b309-795144e6b956")
422    def test_stress_softAP_startup_and_stop_5g(self):
423        """Test to bring up softAP and down for N times.
424
425        Steps:
426            1. Bring up softAP on 5G.
427            2. Check for softAP on teh client device.
428            3. Turn ON WiFi.
429            4. Verify softAP is turned down and WiFi is up.
430
431        """
432        ap_ssid = "softap_" + utils.rand_ascii_str(8)
433        ap_password = utils.rand_ascii_str(8)
434        self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
435        config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
436        config[wutils.WifiEnums.PWD_KEY] = ap_password
437        # Set country code explicitly to "US".
438        wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
439        wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US)
440        for count in range(self.stress_count):
441            initial_wifi_state = self.dut.droid.wifiCheckState()
442            wutils.start_wifi_tethering(self.dut,
443                ap_ssid,
444                ap_password,
445                WifiEnums.WIFI_CONFIG_APBAND_5G)
446            wutils.start_wifi_connection_scan_and_ensure_network_found(
447                self.dut_client, ap_ssid)
448            wutils.stop_wifi_tethering(self.dut)
449            asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
450                                 "SoftAp failed to shutdown!")
451            # Give some time for WiFi to come back to previous state.
452            time.sleep(2)
453            cur_wifi_state = self.dut.droid.wifiCheckState()
454            if initial_wifi_state != cur_wifi_state:
455               raise signals.TestFailure("Wifi state was %d before softAP and %d now!" %
456                    (initial_wifi_state, cur_wifi_state),
457                        extras={"Iterations":"%d" % self.stress_count,
458                            "Pass":"%d" %count})
459        raise signals.TestPass(details="", extras={"Iterations":"%d" %
460            self.stress_count, "Pass":"%d" %(count+1)})
461
462    @test_tracker_info(uuid="eb22e26b-95d1-4580-8c76-85dfe6a42a0f")
463    def test_stress_wifi_roaming(self):
464        AP1_network = self.reference_networks[0]["5g"]
465        AP2_network = self.reference_networks[1]["5g"]
466        wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
467        self.scan_and_connect_by_ssid(self.dut, AP1_network)
468        # Reduce iteration to half because each iteration does two roams.
469        for count in range(int(self.stress_count/2)):
470            self.log.info("Roaming iteration %d, from %s to %s", count,
471                           AP1_network, AP2_network)
472            try:
473                wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
474                    "AP1_off_AP2_on", AP2_network)
475                self.log.info("Roaming iteration %d, from %s to %s", count,
476                               AP2_network, AP1_network)
477                wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
478                    "AP1_on_AP2_off", AP1_network)
479            except:
480                raise signals.TestFailure("Roaming failed. Look at logs",
481                    extras={"Iterations":"%d" %self.stress_count, "Pass":"%d" %
482                        (count*2)})
483        raise signals.TestPass(details="", extras={"Iterations":"%d" %
484            self.stress_count, "Pass":"%d" %((count+1)*2)})
485
486    @test_tracker_info(uuid="e8ae8cd2-c315-4c08-9eb3-83db65b78a58")
487    def test_stress_network_selector_2G_connection(self):
488        """
489            1. Add one saved 2G network to DUT.
490            2. Move the DUT in range.
491            3. Verify the DUT is connected to the network.
492            4. Move the DUT out of range
493            5. Repeat step 2-4
494        """
495        for attenuator in self.attenuators:
496            attenuator.set_atten(95)
497        # add a saved network to DUT
498        networks = [self.reference_networks[0]['2g']]
499        self.add_networks(self.dut, networks)
500        for count in range(self.stress_count):
501            self.connect_and_verify_connected_ssid(self.reference_networks[0]['2g'])
502            # move the DUT out of range
503            self.attenuators[0].set_atten(95)
504            time.sleep(10)
505        wutils.set_attns(self.attenuators, "default")
506        raise signals.TestPass(details="", extras={"Iterations":"%d" %
507            self.stress_count, "Pass":"%d" %(count+1)})
508
509    @test_tracker_info(uuid="5d5d14cb-3cd1-4b3d-8c04-0d6f4b764b6b")
510    def test_stress_pno_connection_to_2g(self):
511        """Test PNO triggered autoconnect to a network for N times
512
513        Steps:
514        1. Save 2Ghz valid network configuration in the device.
515        2. Screen off DUT
516        3. Attenuate 5Ghz network and wait for a few seconds to trigger PNO.
517        4. Check the device connected to 2Ghz network automatically.
518        5. Repeat step 3-4
519        """
520        for attenuator in self.attenuators:
521            attenuator.set_atten(95)
522        # add a saved network to DUT
523        networks = [self.reference_networks[0]['2g']]
524        self.add_networks(self.dut, networks)
525        self.dut.droid.wakeLockRelease()
526        self.dut.droid.goToSleepNow()
527        for count in range(self.stress_count):
528            self.connect_and_verify_connected_ssid(self.reference_networks[0]['2g'], is_pno=True)
529            wutils.wifi_forget_network(
530                    self.dut, networks[0][WifiEnums.SSID_KEY])
531            # move the DUT out of range
532            self.attenuators[0].set_atten(95)
533            time.sleep(10)
534            self.add_networks(self.dut, networks)
535        wutils.set_attns(self.attenuators, "default")
536        raise signals.TestPass(details="", extras={"Iterations":"%d" %
537            self.stress_count, "Pass":"%d" %(count+1)})
538