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 various download stress scenarios.
18
19"""
20import os
21import threading
22import uuid
23
24from acts.base_test import BaseTestClass
25from acts import signals
26from acts.controllers.access_point import setup_ap
27from acts.controllers.ap_lib import hostapd_constants
28from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
29from acts_contrib.test_utils.fuchsia import utils
30from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
31from acts.utils import rand_ascii_str
32
33
34class DownloadStressTest(BaseTestClass):
35    # Default number of test iterations here.
36    # Override using parameter in config file.
37    # Eg: "download_stress_test_iterations": "10"
38    num_of_iterations = 3
39
40    # Timeout for download thread in seconds
41    download_timeout_s = 60 * 5
42
43    # Download urls
44    url_20MB = 'http://ipv4.download.thinkbroadband.com/20MB.zip'
45    url_40MB = 'http://ipv4.download.thinkbroadband.com/40MB.zip'
46    url_60MB = 'http://ipv4.download.thinkbroadband.com/60MB.zip'
47    url_512MB = 'http://ipv4.download.thinkbroadband.com/512MB.zip'
48
49    # Constants used in test_one_large_multiple_small_downloads
50    download_small_url = url_20MB
51    download_large_url = url_512MB
52    num_of_small_downloads = 5
53    download_threads_result = []
54
55    def setup_class(self):
56        super().setup_class()
57        self.ssid = rand_ascii_str(10)
58        self.fd = self.fuchsia_devices[0]
59        self.wlan_device = create_wlan_device(self.fd)
60        self.ap = self.access_points[0]
61        self.num_of_iterations = int(
62            self.user_params.get("download_stress_test_iterations",
63                                 self.num_of_iterations))
64
65        setup_ap(access_point=self.ap,
66                 profile_name='whirlwind',
67                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
68                 ssid=self.ssid)
69        self.wlan_device.associate(self.ssid)
70
71    def teardown_test(self):
72        self.download_threads_result.clear()
73        self.wlan_device.disconnect()
74        self.wlan_device.reset_wifi()
75        self.ap.stop_all_aps()
76
77    def test_download_small(self):
78        self.log.info("Downloading small file")
79        return self.download_file(self.url_20MB)
80
81    def test_download_large(self):
82        return self.download_file(self.url_512MB)
83
84    def test_continuous_download(self):
85        for x in range(0, self.num_of_iterations):
86            if not self.download_file(self.url_512MB):
87                return False
88        return True
89
90    def download_file(self, url):
91        self.log.info("Start downloading: %s" % url)
92        return utils.http_file_download_by_curl(
93            self.fd,
94            url,
95            additional_args='--max-time %d --silent' % self.download_timeout_s)
96
97    def download_thread(self, url):
98        download_status = self.download_file(url)
99        if download_status:
100            self.log.info("Success downloading: %s" % url)
101        else:
102            self.log.info("Failure downloading: %s" % url)
103
104        self.download_threads_result.append(download_status)
105        return download_status
106
107    def test_multi_downloads(self):
108        download_urls = [self.url_20MB, self.url_40MB, self.url_60MB]
109        download_threads = []
110
111        try:
112            # Start multiple downloads at the same time
113            for index, url in enumerate(download_urls):
114                self.log.info('Create and start thread %d.' % index)
115                t = threading.Thread(target=self.download_thread, args=(url, ))
116                download_threads.append(t)
117                t.start()
118
119            # Wait for all threads to complete or timeout
120            for t in download_threads:
121                t.join(self.download_timeout_s)
122
123        finally:
124            is_alive = False
125
126            for index, t in enumerate(download_threads):
127                if t.isAlive():
128                    t = None
129                    is_alive = True
130
131            if is_alive:
132                raise signals.TestFailure('Thread %d timedout' % index)
133
134        for index in range(0, len(self.download_threads_result)):
135            if not self.download_threads_result[index]:
136                self.log.info("Download failed for %d" % index)
137                raise signals.TestFailure('Thread %d failed to download' %
138                                          index)
139                return False
140
141        return True
142
143    def test_one_large_multiple_small_downloads(self):
144        for index in range(self.num_of_iterations):
145            download_threads = []
146            try:
147                large_thread = threading.Thread(
148                    target=self.download_thread,
149                    args=(self.download_large_url, ))
150                download_threads.append(large_thread)
151                large_thread.start()
152
153                for i in range(self.num_of_small_downloads):
154                    # Start small file download
155                    t = threading.Thread(target=self.download_thread,
156                                         args=(self.download_small_url, ))
157                    download_threads.append(t)
158                    t.start()
159                    # Wait for thread to exit before starting the next iteration
160                    t.join(self.download_timeout_s)
161
162                # Wait for the large file download thread to complete
163                large_thread.join(self.download_timeout_s)
164
165            finally:
166                is_alive = False
167
168                for index, t in enumerate(download_threads):
169                    if t.isAlive():
170                        t = None
171                        is_alive = True
172
173                if is_alive:
174                    raise signals.TestFailure('Thread %d timedout' % index)
175
176            for index in range(0, len(self.download_threads_result)):
177                if not self.download_threads_result[index]:
178                    self.log.info("Download failed for %d" % index)
179                    raise signals.TestFailure('Thread %d failed to download' %
180                                              index)
181                    return False
182
183            # Clear results before looping again
184            self.download_threads_result.clear()
185
186        return True
187