1#!/usr/bin/env python3.4 2# 3# Copyright 2016 - 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 itertools 18import queue 19import time 20import traceback 21 22from acts import asserts 23from acts import utils 24from acts.test_decorators import test_tracker_info 25from acts_contrib.test_utils.wifi import wifi_constants 26from acts_contrib.test_utils.wifi import wifi_test_utils as wutils 27from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest 28 29SCANTIME = 10000 #framework support only 10s as minimum scan interval 30NUMBSSIDPERSCAN = 8 31EVENT_TAG = "WifiScannerScan" 32SCAN_TIME_PASSIVE = 47 # dwell time plus 2ms 33SCAN_TIME_ACTIVE = 32 # dwell time plus 2ms 34SHORT_TIMEOUT = 30 35NETWORK_ID_ERROR = "Network don't have ID" 36NETWORK_ERROR = "Device is not connected to reference network" 37INVALID_RESULT = "Test fail because scan result reported are not valid" 38EMPTY_RESULT = "Test fail because empty scan result reported" 39KEY_RET = "ResultElapsedRealtime" 40ATTENUATOR = 0 41 42class WifiScannerScanError(Exception): 43 pass 44 45 46class WifiScannerScanTest(WifiBaseTest): 47 def __init__(self, configs): 48 super().__init__(configs) 49 self.enable_packet_log = True 50 # TODO(angli): Remove this list. 51 # There are order dependencies among these tests so we'll have to leave 52 # it here for now. :( 53 self.tests = ( 54 "test_available_channels_band_1", 55 "test_available_channels_band_2", 56 "test_available_channels_band_3", 57 "test_available_channels_band_4", 58 "test_available_channels_band_6", 59 "test_available_channels_band_7", 60 "test_wifi_scanner_single_scan_channel_sanity", 61 "test_wifi_scanner_with_wifi_off", 62 "test_single_scan_report_each_scan_for_channels_with_enumerated_params", 63 "test_single_scan_report_each_scan_for_band_with_enumerated_params", 64 "test_single_scan_report_full_scan_for_channels_with_enumerated_params", 65 "test_single_scan_report_full_scan_for_band_with_enumerated_params", 66 "test_single_scan_while_pno", 67 "test_wifi_scanner_single_scan_in_isolated", 68 "test_wifi_scanner_with_invalid_numBssidsPerScan", 69 "test_wifi_scanner_dual_radio_low_latency", 70 "test_wifi_scanner_dual_radio_low_power", 71 "test_wifi_scanner_dual_radio_high_accuracy") 72 73 def setup_class(self): 74 super().setup_class() 75 self.dut = self.android_devices[0] 76 wutils.wifi_test_device_init(self.dut) 77 req_params = ("run_extended_test", "ping_addr", "dbs_supported_models", 78 "support_addition_channel") 79 opt_param = ["reference_networks"] 80 self.unpack_userparams( 81 req_param_names=req_params, opt_param_names=opt_param) 82 83 if "AccessPoint" in self.user_params: 84 self.legacy_configure_ap_and_start(ap_count=2, mirror_ap=False) 85 elif "OpenWrtAP" in self.user_params: 86 self.configure_openwrt_ap_and_start(open_network=True, 87 wpa_network=True, 88 ap_count=2) 89 90 self.leeway = 10 91 self.stime_channel = SCAN_TIME_PASSIVE 92 self.default_scan_setting = { 93 "band": wutils.WifiEnums.WIFI_BAND_BOTH, 94 "periodInMs": SCANTIME, 95 "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN 96 } 97 self.default_batch_scan_setting = { 98 "band": wutils.WifiEnums.WIFI_BAND_BOTH, 99 "periodInMs": SCANTIME, 100 "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL 101 } 102 self.log.debug("Run extended test: {}".format(self.run_extended_test)) 103 self.wifi_chs = wutils.WifiChannelUS(self.dut.model, self.support_addition_channel) 104 self.attenuators = wutils.group_attenuators(self.attenuators) 105 self.attenuators[0].set_atten(0) 106 self.attenuators[1].set_atten(0) 107 108 def teardown_test(self): 109 super().teardown_test() 110 self.log.debug("Shut down all wifi scanner activities.") 111 self.dut.droid.wifiScannerShutdown() 112 113 def teardown_class(self): 114 if "AccessPoint" in self.user_params: 115 del self.user_params["reference_networks"] 116 del self.user_params["open_network"] 117 118 """ Helper Functions Begin """ 119 120 def wifi_generate_scanner_scan_settings(self, extended, scan_type, 121 report_result): 122 """Generates all the combinations of different scan setting parameters. 123 124 Args: 125 extended: True for extended setting 126 scan_type: key for type of scan 127 report_result: event type of report scan results 128 129 Returns: 130 A list of dictionaries each representing a set of scan settings. 131 """ 132 base_scan_time = [SCANTIME * 2] 133 if scan_type == "band": 134 scan_types_setting = [wutils.WifiEnums.WIFI_BAND_BOTH] 135 else: 136 scan_types_setting = [self.wifi_chs.MIX_CHANNEL_SCAN] 137 num_of_bssid = [NUMBSSIDPERSCAN * 4] 138 max_scan_cache = [0] 139 if extended: 140 base_scan_time.append(SCANTIME) 141 if scan_type == "band": 142 scan_types_setting.extend( 143 [wutils.WifiEnums.WIFI_BAND_24_GHZ, 144 wutils.WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS, 145 wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS]) 146 else: 147 scan_types_setting.extend( 148 [self.wifi_chs.NONE_DFS_5G_FREQUENCIES, self.wifi_chs. 149 ALL_2G_FREQUENCIES, self.wifi_chs.DFS_5G_FREQUENCIES, 150 self.wifi_chs.ALL_5G_FREQUENCIES]) 151 num_of_bssid.append(NUMBSSIDPERSCAN * 3) 152 max_scan_cache.append(5) 153 # Generate all the combinations of report types and scan types 154 if report_result == wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT: 155 report_types = {"reportEvents": report_result} 156 setting_combinations = list(itertools.product(scan_types_setting, 157 base_scan_time)) 158 # Create scan setting strings based on the combinations 159 scan_settings = [] 160 for combo in setting_combinations: 161 s = dict(report_types) 162 s[scan_type] = combo[0] 163 s["periodInMs"] = combo[1] 164 scan_settings.append(s) 165 else: 166 report_types = {"reportEvents": report_result} 167 setting_combinations = list( 168 itertools.product(scan_types_setting, base_scan_time, 169 num_of_bssid, max_scan_cache)) 170 # Create scan setting strings based on the combinations 171 scan_settings = [] 172 for combo in setting_combinations: 173 s = dict(report_types) 174 s[scan_type] = combo[0] 175 s["periodInMs"] = combo[1] 176 s["numBssidsPerScan"] = combo[2] 177 s["maxScansToCache"] = combo[3] 178 scan_settings.append(s) 179 return scan_settings 180 181 def proces_and_valid_batch_scan_result(self, scan_resutls, scan_rt, 182 result_rt, scan_setting): 183 """This function process scan results and validate against settings used 184 while starting the scan. 185 186 There are two steps for the verification. First it checks that all the 187 wifi networks in results are of the correct frequencies set by scan setting 188 params. Then it checks that the delta between the batch of scan results less 189 than the time required for scanning channel set by scan setting params. 190 191 Args: 192 scan_results: scan results reported. 193 scan_rt: Elapsed real time on start scan. 194 result_rt: Elapsed ral time on results reported. 195 scan_setting: The params for the single scan. 196 197 Returns: 198 bssids: total number of bssids scan result have 199 validity: True if the all scan result are valid. 200 """ 201 bssids = 0 202 validity = True 203 scan_time_mic = 0 204 scan_channels = [] 205 scan_time, scan_channels = wutils.get_scan_time_and_channels( 206 self.wifi_chs, scan_setting, self.stime_channel) 207 scan_time_mic = scan_time * 1000 208 for i, batch in enumerate(scan_resutls, start=1): 209 asserts.assert_true( 210 batch["ScanResults"], 211 "At least one scan result is required to validate") 212 max_scan_interval = batch["ScanResults"][0][ 213 "timestamp"] + scan_time_mic 214 self.log.debug("max_scan_interval: %s", max_scan_interval) 215 for result in batch["ScanResults"]: 216 # Though the tests are run in shield box, there are leakes 217 # from outside environment. This would ignore any such SSIDs 218 ssid = result["SSID"] 219 if not ssid.startswith("2g_") or not ssid.startswith("5g_"): 220 continue 221 if (result["frequency"] not in scan_channels or 222 result["timestamp"] > max_scan_interval or 223 result["timestamp"] < scan_rt * 1000 or 224 result["timestamp"] > result_rt * 1000): 225 self.log.error("Result didn't match requirement: %s", 226 result) 227 validity = False 228 self.log.info("Number of scan result in batch %s: %s", i, 229 len(batch["ScanResults"])) 230 bssids += len(batch["ScanResults"]) 231 return bssids, validity 232 233 def pop_scan_result_events(self, event_name): 234 """Function to pop all the scan result events. 235 236 Args: 237 event_name: event name. 238 239 Returns: 240 results: list of scan result reported in events 241 """ 242 results = [] 243 try: 244 events = self.dut.ed.pop_all(event_name) 245 for event in events: 246 results.append(event["data"]["Results"]) 247 except queue.Empty as error: 248 self.log.debug("Number of Full scan results %s", len(results)) 249 return results 250 251 def wifi_scanner_single_scan(self, scan_setting): 252 """Common logic for an enumerated wifi scanner single scan test case. 253 254 1. Start WifiScanner single scan for scan_setting. 255 2. Wait for the scan result event, wait time depend on scan settings 256 parameter. 257 3. Verify that scan results match with scan settings parameters. 258 4. Also verify that only one scan result event trigger. 259 260 Args: 261 scan_setting: The params for the single scan. 262 """ 263 data = wutils.start_wifi_single_scan(self.dut, scan_setting) 264 idx = data["Index"] 265 scan_rt = data["ScanElapsedRealtime"] 266 self.log.info( 267 "Wifi single shot scan started index: %s at real time: %s", idx, 268 scan_rt) 269 results = [] 270 #generating event wait time from scan setting plus leeway 271 scan_time, scan_channels = wutils.get_scan_time_and_channels( 272 self.wifi_chs, scan_setting, self.stime_channel) 273 wait_time = int(scan_time / 1000) + self.leeway 274 validity = False 275 #track number of result received 276 result_received = 0 277 try: 278 for snumber in range(1, 3): 279 event_name = "{}{}onResults".format(EVENT_TAG, idx) 280 self.log.debug("Waiting for event: %s for time %s", event_name, 281 wait_time) 282 event = self.dut.ed.pop_event(event_name, wait_time) 283 self.log.debug("Event received: %s", event) 284 results = event["data"]["Results"] 285 result_received += 1 286 bssids, validity = self.proces_and_valid_batch_scan_result( 287 results, scan_rt, event["data"][KEY_RET], scan_setting) 288 asserts.assert_equal( 289 len(results), 1, 290 "Test fail because number of scan result %s" % 291 len(results)) 292 asserts.assert_true(bssids > 0, EMPTY_RESULT) 293 asserts.assert_true(validity, INVALID_RESULT) 294 self.log.info("Scan number Buckets: %s\nTotal BSSID: %s", 295 len(results), bssids) 296 except queue.Empty as error: 297 asserts.assert_true( 298 result_received >= 1, 299 "Event did not triggered for single shot {}".format(error)) 300 finally: 301 self.dut.droid.wifiScannerStopScan(idx) 302 #For single shot number of result received and length of result should be one 303 asserts.assert_true( 304 result_received == 1, 305 "Test fail because received result {}".format(result_received)) 306 307 def wifi_scanner_single_scan_full(self, scan_setting): 308 """Common logic for single scan test case for full scan result. 309 310 1. Start WifiScanner single scan with scan_setting for full scan result. 311 2. Wait for the scan result event, wait time depend on scan settings 312 parameter. 313 3. Pop all full scan result events occurred earlier. 314 4. Verify that full scan results match with normal scan results. 315 5. If the scan type is included in scan_setting, verify that the 316 radioChainInfos length. 317 318 Args: 319 scan_setting: The parameters for the single scan. 320 """ 321 self.dut.ed.clear_all_events() 322 data = wutils.start_wifi_single_scan(self.dut, scan_setting) 323 idx = data["Index"] 324 scan_rt = data["ScanElapsedRealtime"] 325 self.log.info("Wifi single shot scan started with index: %s", idx) 326 #generating event wait time from scan setting plus leeway 327 scan_time, scan_channels = wutils.get_scan_time_and_channels( 328 self.wifi_chs, scan_setting, self.stime_channel) 329 wait_time = int(scan_time / 1000) + self.leeway 330 results = [] 331 validity = False 332 try: 333 event_name = "%s%sonResults" % (EVENT_TAG, idx) 334 self.log.debug("Waiting for event: %s for time %s", event_name, 335 wait_time) 336 event = self.dut.ed.pop_event(event_name, wait_time) 337 self.log.info("Event received: %s", event) 338 bssids, validity = (self.proces_and_valid_batch_scan_result( 339 event["data"]["Results"], scan_rt, event["data"][KEY_RET], 340 scan_setting)) 341 asserts.assert_true(bssids > 0, EMPTY_RESULT) 342 asserts.assert_true(validity, INVALID_RESULT) 343 event_name = "{}{}onFullResult".format(EVENT_TAG, idx) 344 results = self.pop_scan_result_events(event_name) 345 asserts.assert_true( 346 len(results) >= bssids, 347 "Full single shot result don't match {}".format(len(results))) 348 if 'type' in scan_setting.keys(): 349 for item in results: 350 self.verify_radio_chain_length(scan_setting['type'], item) 351 except queue.Empty as error: 352 raise AssertionError( 353 "Event did not triggered for single shot {}".format(error)) 354 finally: 355 self.dut.droid.wifiScannerStopScan(idx) 356 357 def verify_radio_chain_length(self, scan_setting_type, scan_result): 358 llen = len(scan_result[0]["radioChainInfos"]) 359 if scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY \ 360 or scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_POWER: 361 asserts.assert_true(llen == 1, 362 "radioChainInfos len expected:{} " 363 "actual:{}".format(1, llen)) 364 else: 365 asserts.assert_true(llen == 2, 366 "radioChainInfos len expected:{} " 367 "actual:{}".format(2, llen)) 368 369 def wifi_scanner_batch_scan_full(self, scan_setting): 370 """Common logic for batch scan test case for full scan result. 371 372 1. Start WifiScanner batch scan with scan_setting for full scan result. 373 2. Wait for the scan result event, wait time depend on scan settings 374 parameter. 375 3. Pop all full scan result events occurred earlier. 376 4. Verify that full scan results match with scan results. 377 378 Args: 379 scan_setting: The params for the batch scan. 380 """ 381 self.dut.ed.clear_all_events() 382 data = wutils.start_wifi_background_scan(self.dut, scan_setting) 383 idx = data["Index"] 384 scan_rt = data["ScanElapsedRealtime"] 385 self.log.info("Wifi batch shot scan started with index: %s", idx) 386 #generating event wait time from scan setting plus leeway 387 scan_time, scan_channels = wutils.get_scan_time_and_channels( 388 self.wifi_chs, scan_setting, self.stime_channel) 389 # multiply scan period by two to account for scheduler changing period 390 scan_time += scan_setting[ 391 'periodInMs'] * 2 #add scan period delay for next cycle 392 wait_time = scan_time / 1000 + self.leeway 393 validity = False 394 try: 395 for snumber in range(1, 3): 396 results = [] 397 event_name = "%s%sonResults" % (EVENT_TAG, idx) 398 self.log.debug("Waiting for event: %s for time %s", event_name, 399 wait_time) 400 event = self.dut.ed.pop_event(event_name, wait_time) 401 self.log.debug("Event received: %s", event) 402 bssids, validity = self.proces_and_valid_batch_scan_result( 403 event["data"]["Results"], scan_rt, event["data"][KEY_RET], 404 scan_setting) 405 event_name = "%s%sonFullResult" % (EVENT_TAG, idx) 406 results = self.pop_scan_result_events(event_name) 407 asserts.assert_true( 408 len(results) >= bssids, 409 "Full single shot result don't match %s" % len(results)) 410 asserts.assert_true(bssids > 0, EMPTY_RESULT) 411 asserts.assert_true(validity, INVALID_RESULT) 412 except queue.Empty as error: 413 raise AssertionError("Event did not triggered for batch scan %s" % 414 error) 415 finally: 416 self.dut.droid.wifiScannerStopBackgroundScan(idx) 417 self.dut.ed.clear_all_events() 418 419 def wifi_scanner_batch_scan(self, scan_setting): 420 """Common logic for an enumerated wifi scanner batch scan test case. 421 422 1. Start WifiScanner batch scan for given scan_setting. 423 2. Wait for the scan result event, wait time depend on scan settings 424 parameter. 425 3. Verify that scan results match with scan settings parameters. 426 4. Also verify that multiple scan result events trigger. 427 428 Args: 429 scan_setting: The parameters for the batch scan. 430 """ 431 data = wutils.start_wifi_background_scan(self.dut, scan_setting) 432 idx = data["Index"] 433 scan_rt = data["ScanElapsedRealtime"] 434 self.log.info( 435 "Wifi background scan started with index: %s real time %s", idx, 436 scan_rt) 437 scan_time, scan_channels = wutils.get_scan_time_and_channels( 438 self.wifi_chs, scan_setting, self.stime_channel) 439 #generating event wait time from scan setting plus leeway 440 time_cache = 0 441 number_bucket = 1 #bucket for Report result on each scan 442 check_get_result = False 443 if scan_setting[ 444 'reportEvents'] == wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL: 445 check_get_result = True 446 if ('maxScansToCache' in scan_setting and 447 scan_setting['maxScansToCache'] != 0): 448 time_cache = (scan_setting['maxScansToCache'] * 449 scan_setting['periodInMs']) 450 number_bucket = scan_setting['maxScansToCache'] 451 else: 452 time_cache = 10 * scan_setting['periodInMs' 453 ] #10 as default max scan cache 454 number_bucket = 10 455 else: 456 time_cache = scan_setting[ 457 'periodInMs' 458 ] #need while waiting for seconds resutls 459 # multiply cache time by two to account for scheduler changing period 460 wait_time = (time_cache * 2 + scan_time) / 1000 + self.leeway 461 validity = False 462 try: 463 for snumber in range(1, 3): 464 event_name = "%s%sonResults" % (EVENT_TAG, idx) 465 self.log.info("Waiting for event: %s for time %s", event_name, 466 wait_time) 467 event = self.dut.ed.pop_event(event_name, wait_time) 468 self.log.debug("Event received: %s", event) 469 results = event["data"]["Results"] 470 bssids, validity = (self.proces_and_valid_batch_scan_result( 471 results, scan_rt, event["data"][KEY_RET], scan_setting)) 472 self.log.info("Scan number: %s\n Buckets: %s\n BSSID: %s", 473 snumber, len(results), bssids) 474 asserts.assert_equal( 475 len(results), number_bucket, 476 "Test fail because number_bucket %s" % len(results)) 477 asserts.assert_true(bssids >= 1, EMPTY_RESULT) 478 asserts.assert_true(validity, INVALID_RESULT) 479 if snumber % 2 == 1 and check_get_result: 480 self.log.info("Get Scan result using GetScanResult API") 481 time.sleep(wait_time / number_bucket) 482 if self.dut.droid.wifiScannerGetScanResults(): 483 event = self.dut.ed.pop_event(event_name, 1) 484 self.log.debug("Event onResults: %s", event) 485 results = event["data"]["Results"] 486 bssids, validity = self.proces_and_valid_batch_scan_result( 487 results, scan_rt, event["data"][KEY_RET], 488 scan_setting) 489 self.log.info("Got Scan result number: %s BSSID: %s", 490 snumber, bssids) 491 asserts.assert_true(bssids >= 1, EMPTY_RESULT) 492 asserts.assert_true(validity, INVALID_RESULT) 493 else: 494 self.log.error("Error while fetching the scan result") 495 except queue.Empty as error: 496 raise AssertionError("Event did not triggered for batch scan %s" % 497 error) 498 finally: 499 self.dut.droid.wifiScannerStopBackgroundScan(idx) 500 self.dut.ed.clear_all_events() 501 502 def start_wifi_scanner_single_scan_expect_failure(self, scan_setting): 503 """Common logic to test wif scanner single scan with invalid settings 504 or environment 505 506 1. Start WifiScanner batch scan for setting parameters. 507 2. Verify that scan is not started. 508 509 Args: 510 scan_setting: The params for the single scan. 511 """ 512 try: 513 idx = self.dut.droid.wifiScannerStartScan(scan_setting) 514 event = self.dut.ed.pop_event( 515 "{}{}onFailure".format(EVENT_TAG, idx), SHORT_TIMEOUT) 516 except queue.Empty as error: 517 raise AssertionError("Did not get expected onFailure {}".format( 518 error)) 519 520 def start_wifi_scanner_background_scan_expect_failure(self, scan_setting): 521 """Common logic to test wif scanner batch scan with invalid settings 522 or environment 523 524 1. Start WifiScanner batch scan for setting parameters. 525 2. Verify that scan is not started. 526 527 Args: 528 scan_setting: The params for the single scan. 529 """ 530 try: 531 idx = self.dut.droid.wifiScannerStartBackgroundScan(scan_setting) 532 event = self.dut.ed.pop_event( 533 "{}{}onFailure".format(EVENT_TAG, idx), SHORT_TIMEOUT) 534 except queue.Empty as error: 535 raise AssertionError("Did not get expected onFailure {}".format( 536 error)) 537 538 def check_get_available_channels_with_one_band(self, band): 539 """Common logic to check available channels for a band. 540 541 1. Get available channels for band. 542 2. Verify that channels match with supported channels for band. 543 544 Args: 545 band: wifi band.""" 546 547 r = self.dut.droid.wifiScannerGetAvailableChannels(band) 548 self.log.info("Band: %s" % band) 549 self.log.info("Available channels: %s" % r) 550 expected = self.wifi_chs.band_to_freq(band) 551 self.log.info("Expected channels: %s" % expected) 552 asserts.assert_equal(set(r), set(expected), "Band %s failed." % band) 553 554 def connect_to_reference_network(self): 555 """Connect to reference network and make sure that connection happen""" 556 self.dut.droid.wakeLockAcquireBright() 557 self.dut.droid.wakeUpNow() 558 try: 559 self.dut.droid.wifiConnectByConfig(self.reference_networks[0]["2g"]) 560 connect_result = self.dut.ed.pop_event( 561 wifi_constants.CONNECT_BY_CONFIG_SUCCESS, SHORT_TIMEOUT) 562 self.log.info(connect_result) 563 return wutils.track_connection(self.dut, 564 self.reference_networks[0]["2g"]["SSID"], 1) 565 except Exception as error: 566 self.log.exception(traceback.format_exc()) 567 self.log.error("Connection to network fail because %s", error) 568 return False 569 finally: 570 self.dut.droid.wifiLockRelease() 571 self.dut.droid.goToSleepNow() 572 573 """ Helper Functions End """ 574 """ Tests Begin """ 575 576 # Test channels 577 """ Test available channels for different bands. 578 579 1. Get available channels for different bands. 580 2. Verify that channels match with supported channels for respective band. 581 """ 582 @test_tracker_info(uuid="7cca8142-529f-4951-ab6f-cd03b359b3cc") 583 def test_available_channels_band_1(self): 584 self.check_get_available_channels_with_one_band(1) 585 586 @test_tracker_info(uuid="612afda1-0d74-4d2f-bc37-72ef2b98310a") 587 def test_available_channels_band_2(self): 588 self.check_get_available_channels_with_one_band(2) 589 590 @test_tracker_info(uuid="a9275bb9-afa7-4dd4-b2e0-60296ffd33bb") 591 def test_available_channels_band_3(self): 592 self.check_get_available_channels_with_one_band(3) 593 594 @test_tracker_info(uuid="5413632e-ce72-4ecc-bf9b-33ac9e4bf3fc") 595 def test_available_channels_band_4(self): 596 self.check_get_available_channels_with_one_band(4) 597 598 @test_tracker_info(uuid="a8f40b4f-d79d-4d2f-bed8-3b139a082f6c") 599 def test_available_channels_band_6(self): 600 self.check_get_available_channels_with_one_band(6) 601 602 @test_tracker_info(uuid="84cdfc25-8e64-42c7-b7f9-0a04e45d78b6") 603 def test_available_channels_band_7(self): 604 self.check_get_available_channels_with_one_band(7) 605 606 @test_tracker_info(uuid="95069244-b76c-4834-b3a6-96b0d8da98d8") 607 def test_single_scan_report_each_scan_for_channels_with_enumerated_params( 608 self): 609 """Test WiFi scanner single scan for channels with enumerated settings. 610 611 1. Start WifiScanner single scan for different channels with enumerated 612 scan settings. 613 2. Verify that scan results match with respective scan settings. 614 """ 615 scan_settings = self.wifi_generate_scanner_scan_settings( 616 self.run_extended_test, "channels", 617 wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN) 618 self.log.debug("Scan settings: %s\n%s", len(scan_settings), 619 scan_settings) 620 self.wifi_scanner_single_scan(scan_settings[0]) 621 622 @test_tracker_info(uuid="5595ebe5-6d91-4379-a606-be59967e5ec9") 623 def test_single_scan_report_each_scan_for_band_with_enumerated_params( 624 self): 625 """Test WiFi scanner single scan for bands with enumerated settings. 626 627 1. Start WifiScanner single scan for different bands with enumerated 628 scan settings. 629 2. Verify that scan results match with respective scan settings. 630 """ 631 scan_settings = self.wifi_generate_scanner_scan_settings( 632 self.run_extended_test, "band", 633 wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN) 634 self.log.debug("Scan settings:%s\n%s", len(scan_settings), 635 scan_settings) 636 self.wifi_scanner_single_scan(scan_settings[0]) 637 638 @test_tracker_info(uuid="44989f93-e63b-4c2e-a90a-a483477303bb") 639 def test_batch_scan_report_buffer_full_for_channels_with_enumerated_params( 640 self): 641 """Test WiFi scanner batch scan using channels with enumerated settings 642 to report buffer full scan results. 643 644 1. Start WifiScanner batch scan using different channels with enumerated 645 scan settings to report buffer full scan results. 646 2. Verify that scan results match with respective scan settings. 647 """ 648 scan_settings = self.wifi_generate_scanner_scan_settings( 649 self.run_extended_test, "channels", 650 wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL) 651 self.log.debug("Scan settings:%s\n%s", len(scan_settings), 652 scan_settings) 653 self.wifi_scanner_batch_scan(scan_settings[0]) 654 655 @test_tracker_info(uuid="63538df6-388a-4c16-964f-e9c19b750e07") 656 def test_batch_scan_report_buffer_full_for_band_with_enumerated_params( 657 self): 658 """Test WiFi scanner batch scan using band with enumerated settings 659 to report buffer full scan results. 660 661 1. Start WifiScanner batch scan using different bands with enumerated 662 scan settings to report buffer full scan results. 663 2. Verify that scan results match with respective scan settings. 664 """ 665 scan_settings = self.wifi_generate_scanner_scan_settings( 666 self.run_extended_test, "band", 667 wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL) 668 self.log.debug("Scan settings:{}\n{}".format( 669 len(scan_settings), scan_settings)) 670 self.wifi_scanner_batch_scan(scan_settings[0]) 671 672 @test_tracker_info(uuid="bd4e8c53-16c8-4ed6-b680-55c1ba688ad8") 673 def test_batch_scan_report_each_scan_for_channels_with_enumerated_params( 674 self): 675 """Test WiFi scanner batch scan using channels with enumerated settings 676 to report each scan results. 677 678 1. Start WifiScanner batch scan using different channels with enumerated 679 scan settings to report each scan results. 680 2. Verify that scan results match with respective scan settings. 681 """ 682 scan_settings = self.wifi_generate_scanner_scan_settings( 683 self.run_extended_test, "channels", 684 wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN) 685 self.log.debug("Scan settings:{}\n{}".format( 686 len(scan_settings), scan_settings)) 687 self.wifi_scanner_batch_scan(scan_settings[0]) 688 689 @test_tracker_info(uuid="d11e8c09-97d0-49c1-bf09-b9ec672c2fa6") 690 def test_batch_scan_report_each_scan_for_band_with_enumerated_params(self): 691 """Test WiFi scanner batch scan using band with enumerated settings 692 to report each scan results. 693 694 1. Start WifiScanner batch scan using different bands with enumerated 695 scan settings to report each scan results. 696 2. Verify that scan results match with respective scan settings. 697 """ 698 scan_settings = self.wifi_generate_scanner_scan_settings( 699 self.run_extended_test, "band", 700 wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN) 701 self.log.debug("Scan settings:{}\n{}".format( 702 len(scan_settings), scan_settings)) 703 self.wifi_scanner_batch_scan(scan_settings[0]) 704 705 @test_tracker_info(uuid="7f967b0e-82fe-403e-9d74-0dee7f09a21d") 706 def test_single_scan_report_full_scan_for_channels_with_enumerated_params( 707 self): 708 """Test WiFi scanner single scan using channels with enumerated settings 709 to report full scan results. 710 711 1. Start WifiScanner single scan using different channels with enumerated 712 scan settings to report full scan results. 713 2. Verify that scan results match with respective scan settings. 714 """ 715 scan_settings = self.wifi_generate_scanner_scan_settings( 716 self.run_extended_test, "channels", 717 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT) 718 self.log.debug("Full Scan settings:{}\n{}".format( 719 len(scan_settings), scan_settings)) 720 self.wifi_scanner_single_scan_full(scan_settings[0]) 721 722 @test_tracker_info(uuid="34d09f60-31bf-4952-8fb3-03fc93ec98fa") 723 def test_single_scan_report_full_scan_for_band_with_enumerated_params( 724 self): 725 """Test WiFi scanner single scan using band with enumerated settings 726 to report full scan results. 727 728 1. Start WifiScanner single scan using different bands with enumerated 729 scan settings to report full scan results. 730 2. Verify that scan results match with respective scan settings. 731 """ 732 scan_settings = self.wifi_generate_scanner_scan_settings( 733 self.run_extended_test, "band", 734 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT) 735 self.log.debug("Full Scan settings:{}\n{}".format( 736 len(scan_settings), scan_settings)) 737 self.wifi_scanner_single_scan_full(scan_settings[0]) 738 739 @test_tracker_info(uuid="0ddccf2e-b518-45a7-ae75-96924070b841") 740 def test_batch_scan_report_full_scan_for_channels_with_enumerated_params( 741 self): 742 """Test WiFi scanner batch scan using channels with enumerated settings 743 to report full scan results. 744 745 1. Start WifiScanner batch scan using different channels with enumerated 746 scan settings to report full scan results. 747 2. Verify that scan results match with respective scan settings. 748 """ 749 scan_settings = self.wifi_generate_scanner_scan_settings( 750 self.run_extended_test, "channels", 751 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT) 752 self.log.debug("Full Scan settings:{}\n{}".format( 753 len(scan_settings), scan_settings)) 754 self.wifi_scanner_batch_scan_full(scan_settings[0]) 755 756 @test_tracker_info(uuid="0685b667-8470-43a0-923d-dee71428f8ce") 757 def test_batch_scan_report_full_scan_for_band_with_enumerated_params(self): 758 """Test WiFi scanner batch scan using channels with enumerated settings 759 to report full scan results. 760 761 1. Start WifiScanner batch scan using different channels with enumerated 762 scan settings to report full scan results. 763 2. Verify that scan results match with respective scan settings. 764 """ 765 scan_settings = self.wifi_generate_scanner_scan_settings( 766 self.run_extended_test, "band", 767 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT) 768 self.log.debug("Full Scan settings:{}\n{}".format( 769 len(scan_settings), scan_settings)) 770 self.wifi_scanner_batch_scan_full(scan_settings[0]) 771 772 @test_tracker_info(uuid="e9a7cfb5-21c4-4c40-8169-8d88b65a1dee") 773 @WifiBaseTest.wifi_test_wrap 774 def test_single_scan_while_pno(self): 775 """Test wifi scanner single scan parallel to PNO connection. 776 777 1. Check device have a saved network. 778 2. Trigger PNO by attenuate the signal to move out of range. 779 3. Start WifiScanner single scan for both band with default scan settings. 780 4. Verify that scanner report single scan results. 781 5. Attenuate the signal to move in range. 782 6. Verify connection occurred through PNO. 783 """ 784 self.log.info("Check connection through PNO for reference network") 785 self.attenuators[ATTENUATOR].set_atten(0) 786 asserts.assert_true(self.connect_to_reference_network(), NETWORK_ERROR) 787 time.sleep(10) #wait for connection to be active 788 asserts.assert_true( 789 wutils.validate_connection(self.dut, self.ping_addr), 790 "Error, No internet connection for current network") 791 792 current_network = self.dut.droid.wifiGetConnectionInfo() 793 self.log.info("Current network: {}".format(current_network)) 794 asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR) 795 asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR) 796 self.log.info("Kicking PNO for reference network") 797 self.attenuators[ATTENUATOR].set_atten(90) 798 time.sleep(10) #wait for PNO to be kicked 799 self.log.info("Starting single scan while PNO") 800 self.wifi_scanner_single_scan(self.default_scan_setting) 801 self.attenuators[ATTENUATOR].set_atten(0) 802 self.log.info("Check connection through PNO for reference network") 803 time.sleep(60) #wait for connection through PNO 804 current_network = self.dut.droid.wifiGetConnectionInfo() 805 self.log.info("Current network: {}".format(current_network)) 806 asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR) 807 asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR) 808 time.sleep(10) #wait for IP to be assigned 809 asserts.assert_true( 810 wutils.validate_connection(self.dut, self.ping_addr), 811 "Error, No internet connection for current network") 812 wutils.wifi_forget_network(self.dut, 813 self.reference_networks[0]["2g"]["SSID"]) 814 815 @test_tracker_info(uuid="fc18d947-0b5a-42b4-98f3-dd1f2b52a7af") 816 def test_wifi_connection_and_pno_while_batch_scan(self): 817 """Test configuring a connection and PNO connection parallel to wifi 818 scanner batch scan. 819 820 1. Start WifiScanner batch scan with default batch scan settings. 821 2. Wait for scan result event for a time depend on scan settings. 822 3. Verify reported batch scan results. 823 4. Configure a connection to reference network. 824 5. Verify that connection to reference network occurred. 825 6. Wait for scan result event for a time depend on scan settings. 826 7. Verify reported batch scan results. 827 8. Trigger PNO by attenuate the signal to move out of range. 828 9. Wait for scan result event for a time depend on scan settings. 829 10. Verify reported batch scan results. 830 11. Attenuate the signal to move in range. 831 12. Verify connection occurred through PNO. 832 """ 833 self.attenuators[ATTENUATOR].set_atten(0) 834 data = wutils.start_wifi_background_scan( 835 self.dut, self.default_batch_scan_setting) 836 idx = data["Index"] 837 scan_rt = data["ScanElapsedRealtime"] 838 self.log.info( 839 "Wifi background scan started with index: {} rt {}".format( 840 idx, scan_rt)) 841 #generating event wait time from scan setting plus leeway 842 scan_time, scan_channels = wutils.get_scan_time_and_channels( 843 self.wifi_chs, self.default_batch_scan_setting, self.stime_channel) 844 #default number buckets 845 number_bucket = 10 846 time_cache = self.default_batch_scan_setting[ 847 'periodInMs'] * number_bucket #default cache 848 #add 2 seconds extra time for switch between the channel for connection scan 849 #multiply cache time by two to account for scheduler changing period 850 wait_time = (time_cache * 2 + scan_time) / 1000 + self.leeway + 2 851 result_flag = 0 852 try: 853 for snumber in range(1, 7): 854 event_name = "{}{}onResults".format(EVENT_TAG, idx) 855 self.log.info("Waiting for event: {}".format(event_name)) 856 event = self.dut.ed.pop_event(event_name, wait_time) 857 self.log.debug("Event onResults: {}".format(event)) 858 results = event["data"]["Results"] 859 bssids, validity = self.proces_and_valid_batch_scan_result( 860 results, scan_rt, event["data"][KEY_RET], 861 self.default_batch_scan_setting) 862 self.log.info( 863 "Scan number: {}\n Buckets: {}\n BSSID: {}".format( 864 snumber, len(results), bssids)) 865 asserts.assert_true(bssids >= 1, 866 "Not able to fetch scan result") 867 if snumber == 1: 868 self.log.info( 869 "Try to connect AP while waiting for event: {}".format( 870 event_name)) 871 asserts.assert_true(self.connect_to_reference_network(), 872 NETWORK_ERROR) 873 time.sleep(10) #wait for connection to be active 874 asserts.assert_true( 875 wutils.validate_connection(self.dut, self.ping_addr), 876 "Error, No internet connection for current network") 877 elif snumber == 3: 878 self.log.info("Kicking PNO for reference network") 879 self.attenuators[ATTENUATOR].set_atten(90) 880 elif snumber == 4: 881 self.log.info("Bring back device for PNO connection") 882 current_network = self.dut.droid.wifiGetConnectionInfo() 883 self.log.info("Current network: {}".format( 884 current_network)) 885 asserts.assert_true('network_id' in current_network, 886 NETWORK_ID_ERROR) 887 asserts.assert_true( 888 current_network['network_id'] == -1, 889 "Device is still connected to network {}".format( 890 current_network[wutils.WifiEnums.SSID_KEY])) 891 self.attenuators[ATTENUATOR].set_atten(0) 892 time.sleep( 893 10 894 ) #wait for connection to take place before waiting for scan result 895 elif snumber == 6: 896 self.log.info( 897 "Check connection through PNO for reference network") 898 current_network = self.dut.droid.wifiGetConnectionInfo() 899 self.log.info("Current network: {}".format( 900 current_network)) 901 asserts.assert_true('network_id' in current_network, 902 NETWORK_ID_ERROR) 903 asserts.assert_true(current_network['network_id'] >= 0, 904 NETWORK_ERROR) 905 time.sleep(10) #wait for connection to be active 906 asserts.assert_true( 907 wutils.validate_connection(self.dut, self.ping_addr), 908 "Error, No internet connection for current network") 909 wutils.wifi_forget_network(self.dut, 910 self.reference_networks[0]["2g"]["SSID"]) 911 except queue.Empty as error: 912 raise AssertionError( 913 "Event did not triggered for batch scan {}".format(error)) 914 finally: 915 self.dut.droid.wifiScannerStopBackgroundScan(idx) 916 self.dut.ed.clear_all_events() 917 918 @test_tracker_info(uuid="7c25ce32-0fae-4a68-a7cb-fdf6d4d03caf") 919 def test_wifi_scanner_single_scan_channel_sanity(self): 920 """Test WiFi scanner single scan for mix channel with default setting 921 parameters. 922 923 1. Start WifiScanner single scan for mix channels with default setting 924 parameters. 925 2. Verify that scan results match with respective scan settings. 926 """ 927 scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN, 928 "periodInMs": SCANTIME, 929 "reportEvents": 930 wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN} 931 self.wifi_scanner_single_scan(scan_setting) 932 933 @test_tracker_info(uuid="7c8da0c4-dec7-4d04-abd4-f8ea467a5c6d") 934 @WifiBaseTest.wifi_test_wrap 935 def test_wifi_scanner_dual_radio_low_latency(self): 936 """Test WiFi scanner single scan for mix channel with default setting 937 parameters. 938 939 1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_LATENCY. 940 2. Verify that scan results match with respective scan settings. 941 """ 942 if self.dut.model not in self.dbs_supported_models: 943 asserts.skip( 944 ("Device %s does not support dual radio scanning.") 945 % self.dut.model) 946 scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN, 947 "periodInMs": SCANTIME, 948 "reportEvents": 949 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT, 950 "type": wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY} 951 self.wifi_scanner_single_scan_full(scan_setting) 952 953 @test_tracker_info(uuid="58b49b01-851b-4e45-b218-9fd27c0be921") 954 @WifiBaseTest.wifi_test_wrap 955 def test_wifi_scanner_dual_radio_low_power(self): 956 """Test WiFi scanner single scan for mix channel with default setting 957 parameters. 958 959 1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_POWER. 960 2. Verify that scan results match with respective scan settings. 961 """ 962 if self.dut.model not in self.dbs_supported_models: 963 asserts.skip( 964 ("Device %s does not support dual radio scanning.") 965 % self.dut.model) 966 scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN, 967 "periodInMs": SCANTIME, 968 "reportEvents": 969 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT, 970 "type": wutils.WifiEnums.SCAN_TYPE_LOW_POWER} 971 self.wifi_scanner_single_scan_full(scan_setting) 972 973 @test_tracker_info(uuid="3e7288bc-45e4-497c-bf3a-977eec4e896e") 974 @WifiBaseTest.wifi_test_wrap 975 def test_wifi_scanner_dual_radio_high_accuracy(self): 976 """Test WiFi scanner single scan for mix channel with default setting 977 parameters. 978 979 1. Start WifiScanner single scan for type = SCAN_TYPE_HIGH_ACCURACY. 980 2. Verify that scan results match with respective scan settings. 981 """ 982 if self.dut.model not in self.dbs_supported_models: 983 asserts.skip( 984 ("Device %s does not support dual radio scanning.") 985 % self.dut.model) 986 scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN, 987 "periodInMs": SCANTIME, 988 "reportEvents": 989 wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT, 990 "type": wutils.WifiEnums.SCAN_TYPE_HIGH_ACCURACY} 991 self.wifi_scanner_single_scan_full(scan_setting) 992 993 @test_tracker_info(uuid="e9f3aaad-4af3-4c54-9829-65dc1d6d4987") 994 def test_wifi_scanner_batch_scan_channel_sanity(self): 995 """Test WiFi scanner batch scan for mix channel with default setting 996 parameters to report the result on buffer full. 997 998 1. Start WifiScanner batch scan for mix channels with default setting 999 parameters. 1000 2. Verify that scan results match with respective scan settings. 1001 """ 1002 scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN, 1003 "periodInMs": SCANTIME, 1004 "reportEvents": 1005 wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL} 1006 self.wifi_scanner_batch_scan(scan_setting) 1007 1008 @test_tracker_info(uuid="49ba245a-52e2-4c9b-90ad-a2fbc97e3d9f") 1009 def test_wifi_scanner_batch_scan_period_too_short(self): 1010 """Test WiFi scanner batch scan for band with too short period time. 1011 1012 1. Start WifiScanner batch scan for both band with interval period as 5s. 1013 2. Verify that scan is not started.""" 1014 scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS, 1015 "periodInMs": 5000, 1016 "reportEvents": 1017 wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL} 1018 self.start_wifi_scanner_background_scan_expect_failure(scan_setting) 1019 1020 @test_tracker_info(uuid="6fe45cd7-4fac-4ddd-a950-b9431e68f735") 1021 def test_wifi_scanner_single_scan_in_isolated(self): 1022 """Test WiFi scanner in isolated environment with default scan settings. 1023 1024 1. Created isolated environment by attenuating the single by 90db 1025 2. Start WifiScanner single scan for mix channels with default setting 1026 parameters. 1027 3. Verify that empty scan results reported. 1028 """ 1029 self.attenuators[0].set_atten(90) 1030 self.attenuators[1].set_atten(90) 1031 data = wutils.start_wifi_single_scan(self.dut, 1032 self.default_scan_setting) 1033 idx = data["Index"] 1034 scan_rt = data["ScanElapsedRealtime"] 1035 self.log.info("Wifi single shot scan started with index: {}".format( 1036 idx)) 1037 results = [] 1038 #generating event wait time from scan setting plus leeway 1039 scan_time, scan_channels = wutils.get_scan_time_and_channels( 1040 self.wifi_chs, self.default_scan_setting, self.stime_channel) 1041 wait_time = int(scan_time / 1000) + self.leeway 1042 try: 1043 event_name = "{}{}onResults".format(EVENT_TAG, idx) 1044 self.log.debug("Waiting for event: {} for time {}".format( 1045 event_name, wait_time)) 1046 event = self.dut.ed.pop_event(event_name, wait_time) 1047 self.log.debug("Event received: {}".format(event)) 1048 results = event["data"]["Results"] 1049 for batch in results: 1050 asserts.assert_false(batch["ScanResults"], 1051 "Test fail because report scan " 1052 "results reported are not empty") 1053 except queue.Empty as error: 1054 raise AssertionError( 1055 "Event did not triggered for in isolated environment {}".format( 1056 error)) 1057 finally: 1058 self.dut.ed.clear_all_events() 1059 self.attenuators[0].set_atten(0) 1060 self.attenuators[1].set_atten(0) 1061 1062 @test_tracker_info(uuid="46f817b9-97a3-455e-af2c-56f9aea64f7e") 1063 def test_wifi_scanner_with_wifi_off(self): 1064 """Test WiFi scanner single scan when wifi is off. 1065 1066 1. Toggle wifi state to off. 1067 2. Start WifiScanner single scan for both band with default scan settings. 1068 3. Verify that scan is not started. 1069 """ 1070 self.log.debug("Make sure wifi is off.") 1071 wutils.wifi_toggle_state(self.dut, False) 1072 self.start_wifi_scanner_single_scan_expect_failure( 1073 self.default_scan_setting) 1074 self.log.debug("Turning wifi back on.") 1075 wutils.wifi_toggle_state(self.dut, True) 1076 1077 @test_tracker_info(uuid="257ad734-c21f-49f4-b448-3986b70eba3d") 1078 def test_wifi_scanner_with_invalid_numBssidsPerScan(self): 1079 """Test WiFi scanner single scan with invalid number of bssids reported 1080 per scan. 1081 1082 1. Start WifiScanner single scan with invalid number of bssids reported 1083 per scan. 1084 2. Verify that scan results triggered for default supported number of 1085 bssids per scan. 1086 """ 1087 scan_setting = { 1088 "band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS, 1089 "periodInMs": SCANTIME, 1090 "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN, 1091 'numBssidsPerScan': 33 1092 } 1093 self.wifi_scanner_single_scan(scan_setting) 1094 1095 """ Tests End """ 1096