Merge "ACTS: Fix regex in get_current_user" am: ffaeca04f7 am: c2583f8ed3 am: 93c85c9dc9
Change-Id: I722b11a3ba4d457e7e40e7cc5695a1be37bad7e2
diff --git a/acts/framework/acts/controllers/buds_lib/apollo_lib.py b/acts/framework/acts/controllers/buds_lib/apollo_lib.py
index 9b971ff..7b0f80f 100644
--- a/acts/framework/acts/controllers/buds_lib/apollo_lib.py
+++ b/acts/framework/acts/controllers/buds_lib/apollo_lib.py
@@ -46,7 +46,7 @@
import time
import serial
-from acts import tracelogger
+from acts.controllers.buds_lib import tako_trace_logger
from acts.controllers.buds_lib import logserial
from acts.controllers.buds_lib.b29_lib import B29Device
from acts.controllers.buds_lib.dev_utils import apollo_log_decoder
@@ -55,7 +55,7 @@
from logging import Logger
from retry import retry
-logging = tracelogger.TakoTraceLogger(Logger('apollo'))
+logging = tako_trace_logger.TakoTraceLogger(Logger('apollo'))
BAUD_RATE = 115200
BYTE_SIZE = 8
diff --git a/acts/framework/acts/controllers/buds_lib/b29_lib.py b/acts/framework/acts/controllers/buds_lib/b29_lib.py
index bb4ea7d..df6a163 100644
--- a/acts/framework/acts/controllers/buds_lib/b29_lib.py
+++ b/acts/framework/acts/controllers/buds_lib/b29_lib.py
@@ -28,12 +28,12 @@
import os
import re
import time
-
-from acts import tracelogger
-from acts import utils
from logging import Logger
-logging = tracelogger.TakoTraceLogger(Logger(__file__))
+from acts import utils
+from acts.controllers.buds_lib import tako_trace_logger
+
+logging = tako_trace_logger.TakoTraceLogger(Logger(__file__))
DEVICE_REGEX = (
r'_(?P<device_serial>[A-Z0-9]+)-(?P<interface>\w+)\s->\s'
r'(\.\./){2}(?P<port>\w+)'
diff --git a/acts/framework/acts/controllers/buds_lib/logserial.py b/acts/framework/acts/controllers/buds_lib/logserial.py
index 58bf154..6b18e3c 100644
--- a/acts/framework/acts/controllers/buds_lib/logserial.py
+++ b/acts/framework/acts/controllers/buds_lib/logserial.py
@@ -21,15 +21,15 @@
import sys
import time
import uuid
+from logging import Logger
from threading import Thread
import serial
from serial.tools import list_ports
-from acts import tracelogger
-from logging import Logger
+from acts.controllers.buds_lib import tako_trace_logger
-logging = tracelogger.TakoTraceLogger(Logger(__file__))
+logging = tako_trace_logger.TakoTraceLogger(Logger(__file__))
RETRIES = 0
diff --git a/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py b/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py
new file mode 100644
index 0000000..ff31840
--- /dev/null
+++ b/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+
+from acts import tracelogger
+
+
+class TakoTraceLogger(tracelogger.TraceLogger):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.d = self.debug
+ self.e = self.error
+ self.i = self.info
+ self.t = self.step
+ self.w = self.warning
+
+ def _logger_level(self, level_name):
+ level = logging.getLevelName(level_name)
+ return lambda *args, **kwargs: self._logger.log(level, *args, **kwargs)
+
+ def step(self, msg, *args, **kwargs):
+ """Delegate a step call to the underlying logger."""
+ self._log_with(self._logger_level('STEP'), 1, msg, *args, **kwargs)
+
+ def device(self, msg, *args, **kwargs):
+ """Delegate a device call to the underlying logger."""
+ self._log_with(self._logger_level('DEVICE'), 1, msg, *args, **kwargs)
+
+ def suite(self, msg, *args, **kwargs):
+ """Delegate a device call to the underlying logger."""
+ self._log_with(self._logger_level('SUITE'), 1, msg, *args, **kwargs)
+
+ def case(self, msg, *args, **kwargs):
+ """Delegate a case call to the underlying logger."""
+ self._log_with(self._logger_level('CASE'), 1, msg, *args, **kwargs)
+
+ def flush_log(self):
+ """This function exists for compatibility with Tako's logserial module.
+
+ Note that flushing the log is handled automatically by python's logging
+ module.
+ """
+ pass
diff --git a/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py b/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py
index 42f8c46..e94fc52 100644
--- a/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py
+++ b/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py
@@ -19,8 +19,8 @@
import datetime
import time
-from acts import tracelogger
from acts import utils
+from acts.controllers.buds_lib import tako_trace_logger
class AudioUtilsError(Exception):
@@ -36,7 +36,7 @@
"""
def __init__(self):
- self.logger = tracelogger.TakoTraceLogger()
+ self.logger = tako_trace_logger.TakoTraceLogger()
def play_audio_into_device(self, audio_file_path, audio_player, dut):
"""Open mic on DUT, play audio into DUT, close mic on DUT.
diff --git a/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py b/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py
index 7b6cbc4..8f4b37a 100644
--- a/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py
+++ b/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py
@@ -22,7 +22,7 @@
import inspect
import time
-from acts import tracelogger
+from acts.controllers.buds_lib import tako_trace_logger
from acts.libs.utils.timer import TimeRecorder
# All methods start with "_" are considered hidden.
@@ -37,7 +37,7 @@
func_name = self._convert_default_action_name(method.__name__)
if not func_name:
func_name = method.__name__
- self.logger.step('%s...' % func_name)
+ self.log_step('%s...' % func_name)
self.timer.start_timer(func_name, True)
result = method(self, *args, **kw)
# TODO: Method run time collected can be used for automatic KPI checks
@@ -135,9 +135,11 @@
def __init__(self, logger=None):
if logger is None:
- self.logger = tracelogger.TakoTraceLogger()
+ self.logger = tako_trace_logger.TakoTraceLogger()
+ self.log_step = self.logger.step
else:
self.logger = logger
+ self.log_step = self.logger.info
self.timer = TimeRecorder()
self._fill_default_action_map()
@@ -172,15 +174,16 @@
exceptions
"""
num_acts = len(self._action_map)
- self.logger.i('I can do %d action%s:' %
+
+ self.logger.info('I can do %d action%s:' %
(num_acts, 's' if num_acts != 1 else ''))
for act in self._action_map.keys():
- self.logger.i(' - %s' % act)
+ self.logger.info(' - %s' % act)
return True
@timed_action
def sleep(self, seconds):
- self.logger.i('%s seconds' % seconds)
+ self.logger.info('%s seconds' % seconds)
time.sleep(seconds)
diff --git a/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py b/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py
index 2582402..f0ac041 100644
--- a/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py
+++ b/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py
@@ -31,12 +31,13 @@
"""
import queue
import time
-
-from acts import tracelogger
-from acts.utils import wait_until
-from acts.utils import TimeoutError
from logging import Logger
+from acts import asserts
+from acts.controllers.buds_lib import tako_trace_logger
+from acts.utils import TimeoutError
+from acts.utils import wait_until
+
# Add connection profile for future devices in this dictionary
WEARABLE_BT_PROTOCOLS = {
'rio': {
@@ -69,7 +70,7 @@
def __init__(self):
self.default_timeout = 60
- self.logger = tracelogger.TakoTraceLogger(Logger(__file__))
+ self.logger = tako_trace_logger.TakoTraceLogger(Logger(__file__))
def bt_pair_and_connect(self, pri_device, sec_device):
"""Pair and connect a pri_device to a sec_device.
@@ -198,8 +199,9 @@
return True, 0
self.logger.debug('Unpairing from %s' % target_address)
start_time = end_time = time.time()
- assert (True is pri_device.droid.bluetoothUnbond(target_address),
- 'Failed to request device unpairing.')
+ asserts.assert_true(
+ pri_device.droid.bluetoothUnbond(target_address),
+ 'Failed to request device unpairing.')
# Check that devices have unpaired successfully.
self.logger.debug('Verifying devices are unpaired')
@@ -290,4 +292,3 @@
if expected[key] != actual[key]:
return False
return True
-
diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py
index e51f815..923bcb2 100755
--- a/acts/framework/acts/controllers/iperf_server.py
+++ b/acts/framework/acts/controllers/iperf_server.py
@@ -17,7 +17,6 @@
import json
import logging
import math
-import IPy
import os
import shlex
import subprocess
@@ -243,8 +242,8 @@
"""
if not self._has_data():
return None
- instantaneous_rates = self.instantaneous_rates[
- iperf_ignored_interval:-1]
+ instantaneous_rates = self.instantaneous_rates[iperf_ignored_interval:
+ -1]
avg_rate = math.fsum(instantaneous_rates) / len(instantaneous_rates)
sqd_deviations = ([(rate - avg_rate)**2
for rate in instantaneous_rates])
@@ -295,25 +294,6 @@
"""
raise NotImplementedError('stop() must be specified.')
- def get_interface_ip_addresses(self, interface):
- """Gets all of the ip addresses, ipv4 and ipv6, associated with a
- particular interface name.
-
- Args:
- interface: The interface name on the device, ie eth0
-
- Returns:
- A list of dictionaries of the the various IP addresses:
- ipv4_private_local_addresses: Any 192.168, 172.16, or 10
- addresses
- ipv4_public_addresses: Any IPv4 public addresses
- ipv6_link_local_addresses: Any fe80:: addresses
- ipv6_private_local_addresses: Any fd00:: addresses
- ipv6_public_addresses: Any publicly routable addresses
- """
- raise NotImplementedError('get_interface_ip_addresses'
- ' must be specified.')
-
def _get_full_file_path(self, tag=None):
"""Returns the full file path for the IPerfServer log file.
@@ -432,24 +412,6 @@
return self._current_log_file
- def get_interface_ip_addresses(self, interface):
- """Gets all of the ip addresses, ipv4 and ipv6, associated with a
- particular interface name.
-
- Args:
- interface: The interface name on the device, ie eth0
-
- Returns:
- A list of dictionaries of the the various IP addresses:
- ipv4_private_local_addresses: Any 192.168, 172.16, or 10
- addresses
- ipv4_public_addresses: Any IPv4 public addresses
- ipv6_link_local_addresses: Any fe80:: addresses
- ipv6_private_local_addresses: Any fd00:: addresses
- ipv6_public_addresses: Any publicly routable addresses
- """
- return utils.get_interface_ip_addresses(job, interface)
-
def __del__(self):
self.stop()
@@ -506,15 +468,15 @@
Args:
interface: The interface name on the device, ie eth0
- Returns:
- A list of dictionaries of the the various IP addresses:
- ipv4_private_local_addresses: Any 192.168, 172.16, or 10
- addresses
- ipv4_public_addresses: Any IPv4 public addresses
- ipv6_link_local_addresses: Any fe80:: addresses
- ipv6_private_local_addresses: Any fd00:: addresses
- ipv6_public_addresses: Any publicly routable addresses
- """
+ Returns:
+ A list of dictionaries of the the various IP addresses:
+ ipv4_private_local_addresses: Any 192.168, 172.16, or 10
+ addresses
+ ipv4_public_addresses: Any IPv4 public addresses
+ ipv6_link_local_addresses: Any fe80:: addresses
+ ipv6_private_local_addresses: Any fd00:: addresses
+ ipv6_public_addresses: Any publicly routable addresses
+ """
return utils.get_interface_ip_addresses(self._ssh_session, interface)
def renew_test_interface_ip_address(self):
@@ -716,22 +678,3 @@
self._iperf_process = None
return log_file
-
- def get_interface_ip_addresses(self, interface):
- """Gets all of the ip addresses, ipv4 and ipv6, associated with a
- particular interface name.
-
- Args:
- interface: The interface name on the device, ie eth0
-
- Returns:
- A list of dictionaries of the the various IP addresses:
- ipv4_private_local_addresses: Any 192.168, 172.16, or 10
- addresses
- ipv4_public_addresses: Any IPv4 public addresses
- ipv6_link_local_addresses: Any fe80:: addresses
- ipv6_private_local_addresses: Any fd00:: addresses
- ipv6_public_addresses: Any publicly routable addresses
- """
- return utils.get_interface_ip_addresses(self._android_device_or_serial,
- interface)
diff --git a/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py b/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py
index a55fc8a..6a7e4af 100644
--- a/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py
@@ -152,6 +152,8 @@
'seconds.' % time_limit_seconds)
self._set_usb_passthrough_mode(expected_state)
time.sleep(1)
+ self._log.info('Monsoon usbPassthroughMode is now "%s"',
+ state)
if expected_state in [PassthroughStates.ON]:
self._on_reconnect()
@@ -261,10 +263,10 @@
TimeoutError upon failure to reconnect over USB.
"""
self._log.info('Reconnecting dut.')
- # Wait for one second to ensure that the relay is ready, then
+ # Wait for two seconds to ensure that the device is ready, then
# attempt to reconnect. If reconnect times out, reset the passthrough
# state and try again.
- time.sleep(1)
+ time.sleep(2)
try:
self.on_reconnect()
except TimeoutError as err:
diff --git a/acts/framework/acts/controllers/spectracom_lib/__init__.py b/acts/framework/acts/controllers/spectracom_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/spectracom_lib/__init__.py
diff --git a/acts/framework/acts/controllers/spectracom_lib/gsg6.py b/acts/framework/acts/controllers/spectracom_lib/gsg6.py
new file mode 100644
index 0000000..6b96456
--- /dev/null
+++ b/acts/framework/acts/controllers/spectracom_lib/gsg6.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Python module for Spectracom/Orolia GSG-6 GNSS simulator."""
+
+from acts.controllers import abstract_inst
+
+
+class GSG6Error(abstract_inst.SocketInstrumentError):
+ """GSG-6 Instrument Error Class."""
+
+
+class GSG6(abstract_inst.SocketInstrument):
+ """GSG-6 Class, inherted from abstract_inst SocketInstrument."""
+
+ def __init__(self, ip_addr, ip_port):
+ """Init method for GSG-6.
+
+ Args:
+ ip_addr: IP Address.
+ Type, str.
+ ip_port: TCPIP Port.
+ Type, str.
+ """
+ super(GSG6, self).__init__(ip_addr, ip_port)
+
+ self.idn = ''
+
+ def connect(self):
+ """Init and Connect to GSG-6."""
+ self._connect_socket()
+
+ self.get_idn()
+
+ infmsg = 'Connected to GSG-6, with ID: {}'.format(self.idn)
+ self._logger.debug(infmsg)
+
+ def close(self):
+ """Close GSG-6."""
+ self._close_socket()
+
+ self._logger.debug('Closed connection to GSG-6')
+
+ def get_idn(self):
+ """Get the Idenification of GSG-6.
+
+ Returns:
+ GSG-6 Identifier
+ """
+ self.idn = self._query('*IDN?')
+
+ return self.idn
+
+ def start_scenario(self, scenario=''):
+ """Start to run scenario.
+
+ Args:
+ scenario: Scenario to run.
+ Type, str.
+ Default, '', which will run current selected one.
+ """
+ if scenario:
+ cmd = 'SOUR:SCEN:LOAD ' + scenario
+ self._send(cmd)
+
+ self._send('SOUR:SCEN:CONT START')
+
+ if scenario:
+ infmsg = 'Started running scenario {}'.format(scenario)
+ else:
+ infmsg = 'Started running current scenario'
+
+ self._logger.debug(infmsg)
+
+ def stop_scenario(self):
+ """Stop the running scenario."""
+
+ self._send('SOUR:SCEN:CONT STOP')
+
+ self._logger.debug('Stopped running scenario')
+
+ def preset(self):
+ """Preset GSG-6 to default status."""
+ self._send('*RST')
+
+ self._logger.debug('Reset GSG-6')
+
+ def set_power(self, power_level):
+ """set GSG-6 transmit power on all bands.
+
+ Args:
+ power_level: transmit power level
+ Type, float.
+ Decimal, unit [dBm]
+
+ Raises:
+ GSG6Error: raise when power level is not in [-160, -65] range.
+ """
+ if not -160 <= power_level <= -65:
+ errmsg = ('"power_level" must be within [-160, -65], '
+ 'current input is {}').format(str(power_level))
+ raise GSG6Error(error=errmsg, command='set_power')
+
+ self._send(':SOUR:POW ' + str(round(power_level, 1)))
+
+ infmsg = 'Set GSG-6 transmit power to "{}"'.format(
+ round(power_level, 1))
+ self._logger.debug(infmsg)
diff --git a/acts/framework/acts/controllers/spirent_lib/__init__.py b/acts/framework/acts/controllers/spirent_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/controllers/spirent_lib/__init__.py
diff --git a/acts/framework/acts/controllers/spirent_lib/gss6450.py b/acts/framework/acts/controllers/spirent_lib/gss6450.py
new file mode 100644
index 0000000..aa84575
--- /dev/null
+++ b/acts/framework/acts/controllers/spirent_lib/gss6450.py
@@ -0,0 +1,381 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Python module for Spirent GSS6450 GNSS RPS."""
+
+import datetime
+import numbers
+from acts.controllers import abstract_inst
+
+
+class GSS6450Error(abstract_inst.SocketInstrumentError):
+ """GSS6450 Instrument Error Class."""
+
+
+class GSS6450(abstract_inst.RequestInstrument):
+ """GSS6450 Class, inherted from abstract_inst RequestInstrument."""
+
+ def __init__(self, ip_addr):
+ """Init method for GSS6450.
+
+ Args:
+ ip_addr: IP Address.
+ Type, str.
+ """
+ super(GSS6450, self).__init__(ip_addr)
+
+ self.idn = 'Spirent-GSS6450'
+
+ def _put(self, cmd):
+ """Send put command via GSS6450 HTTP Request and get response.
+
+ Args:
+ cmd: parameters listed in SHM_PUT.
+ Type, Str.
+
+ Returns:
+ resp: Response from the _query method.
+ Type, Str.
+ """
+ put_cmd = 'shm_put.shtml?' + cmd
+ resp = self._query(put_cmd)
+
+ return resp
+
+ def _get(self, cmd):
+ """Send get command via GSS6450 HTTP Request and get response.
+
+ Args:
+ cmd: parameters listed in SHM_GET.
+ Type, Str.
+
+ Returns:
+ resp: Response from the _query method.
+ Type, Str.
+ """
+ get_cmd = 'shm_get.shtml?' + cmd
+ resp = self._query(get_cmd)
+
+ return resp
+
+ def get_scenario_filename(self):
+ """Get the scenario filename of GSS6450.
+
+ Returns:
+ filename: RPS Scenario file name.
+ Type, Str.
+ """
+ resp_raw = self._get('-f')
+ filename = resp_raw.split(':')[-1].strip(' ')
+ self._logger.debug('Got scenario file name: "%s".', filename)
+
+ return filename
+
+ def get_scenario_description(self):
+ """Get the scenario description of GSS6450.
+
+ Returns:
+ description: RPS Scenario description.
+ Type, Str.
+ """
+ resp_raw = self._get('-d')
+ description = resp_raw.split('-d')[-1].strip(' ')
+
+ if description:
+ self._logger.debug('Got scenario description: "%s".', description)
+ else:
+ self._logger.warning('Got scenario description with empty string.')
+
+ return description
+
+ def get_scenario_location(self):
+ """Get the scenario location of GSS6450.
+
+ Returns:
+ location: RPS Scenario location.
+ Type, Str.
+ """
+ resp_raw = self._get('-i')
+ location = resp_raw.split('-i')[-1].strip(' ')
+
+ if location:
+ self._logger.debug('Got scenario location: "%s".', location)
+ else:
+ self._logger.warning('Got scenario location with empty string.')
+
+ return location
+
+ def get_operation_mode(self):
+ """Get the operation mode of GSS6450.
+
+ Returns:
+ mode: RPS Operation Mode.
+ Type, Str.
+ Option, STOPPED/PLAYING/RECORDING
+ """
+ resp_raw = self._get('-m')
+ mode = resp_raw.split('-m')[-1].strip(' ')
+ self._logger.debug('Got operation mode: "%s".', mode)
+
+ return mode
+
+ def get_battery_level(self):
+ """Get the battery level of GSS6450.
+
+ Returns:
+ batterylevel: RPS Battery Level.
+ Type, float.
+ """
+ resp_raw = self._get('-l')
+ batterylevel = float(resp_raw.split('-l')[-1].strip(' '))
+ self._logger.debug('Got battery level: %s%%.', batterylevel)
+
+ return batterylevel
+
+ def get_rfport_voltage(self):
+ """Get the RF port voltage of GSS6450.
+
+ Returns:
+ voltageout: RPS RF port voltage.
+ Type, str
+ """
+ resp_raw = self._get('-v')
+ voltageout = resp_raw.split('-v')[-1].strip(' ')
+ self._logger.debug('Got RF port voltage: "%s".', voltageout)
+
+ return voltageout
+
+ def get_storage_media(self):
+ """Get the storage media of GSS6450.
+
+ Returns:
+ media: RPS storage.
+ Type, str
+
+ Raises:
+ GSS6450Error: raise when request response is not support.
+ """
+ resp_raw = self._get('-M')
+ resp_num = resp_raw.split('-M')[-1].strip(' ')
+
+ if resp_num == '1':
+ media = '1-INTERNAL'
+ elif resp_num == '2':
+ media = '2-REMOVABLE'
+ else:
+ errmsg = ('"{}" is not recognized as GSS6450 valid storage media'
+ ' type'.format(resp_num))
+ raise GSS6450Error(error=errmsg, command='get_storage_media')
+
+ self._logger.debug('Got current storage media: %s.', media)
+
+ return media
+
+ def get_attenuation(self):
+ """Get the attenuation of GSS6450.
+
+ Returns:
+ attenuation: RPS attenuation level, in dB.
+ Type, list of float.
+ """
+ resp_raw = self._get('-a')
+ resp_str = resp_raw.split('-a')[-1].strip(' ')
+ self._logger.debug('Got attenuation: %s dB.', resp_str)
+ attenuation = [float(itm) for itm in resp_str.split(',')]
+
+ return attenuation
+
+ def get_elapsed_time(self):
+ """Get the running scenario elapsed time of GSS6450.
+
+ Returns:
+ etime: RPS elapsed time.
+ Type, datetime.timedelta.
+ """
+ resp_raw = self._get('-e')
+ resp_str = resp_raw.split('-e')[-1].strip(' ')
+ self._logger.debug('Got senario elapsed time: "%s".', resp_str)
+ etime_tmp = datetime.datetime.strptime(resp_str, '%H:%M:%S')
+ etime = datetime.timedelta(hours=etime_tmp.hour,
+ minutes=etime_tmp.minute,
+ seconds=etime_tmp.second)
+
+ return etime
+
+ def get_playback_offset(self):
+ """Get the running scenario playback offset of GSS6450.
+
+ Returns:
+ offset: RPS playback offset.
+ Type, datetime.timedelta.
+ """
+ resp_raw = self._get('-o')
+ offset_tmp = float(resp_raw.split('-o')[-1].strip(' '))
+ self._logger.debug('Got senario playback offset: %s sec.', offset_tmp)
+ offset = datetime.timedelta(seconds=offset_tmp)
+
+ return offset
+
+ def play_scenario(self, scenario=''):
+ """Start to play scenario in GSS6450.
+
+ Args:
+ scenario: Scenario to play.
+ Type, str.
+ Default, '', which will run current selected one.
+ """
+ if scenario:
+ cmd = '-f{},-wP'.format(scenario)
+ else:
+ cmd = '-wP'
+
+ _ = self._put(cmd)
+
+ if scenario:
+ infmsg = 'Started playing scenario: "{}".'.format(scenario)
+ else:
+ infmsg = 'Started playing current scenario.'
+
+ self._logger.debug(infmsg)
+
+ def record_scenario(self, scenario=''):
+ """Start to record scenario in GSS6450.
+
+ Args:
+ scenario: Scenario to record.
+ Type, str.
+ Default, '', which will run current selected one.
+ """
+ if scenario:
+ cmd = '-f{},-wR'.format(scenario)
+ else:
+ cmd = '-wR'
+
+ _ = self._put(cmd)
+
+ if scenario:
+ infmsg = 'Started recording scenario: "{}".'.format(scenario)
+ else:
+ infmsg = 'Started recording scenario.'
+
+ self._logger.debug(infmsg)
+
+ def stop_scenario(self):
+ """Start to stop playing/recording scenario in GSS6450."""
+ _ = self._put('-wS')
+
+ self._logger.debug('Stopped playing/recording scanrio.')
+
+ def set_rfport_voltage(self, voltageout):
+ """Set the RF port voltage of GSS6450.
+
+ Args:
+ voltageout: RPS RF port voltage.
+ Type, str
+
+ Raises:
+ GSS6450Error: raise when voltageout input is not valid.
+ """
+ if voltageout == 'OFF':
+ voltage_cmd = '0'
+ elif voltageout == '3.3V':
+ voltage_cmd = '3'
+ elif voltageout == '5V':
+ voltage_cmd = '5'
+ else:
+ errmsg = ('"{}" is not recognized as GSS6450 valid RF port voltage'
+ ' type'.format(voltageout))
+ raise GSS6450Error(error=errmsg, command='set_rfport_voltage')
+
+ _ = self._put('-v{},-wV'.format(voltage_cmd))
+ self._logger.debug('Set RF port voltage: "%s".', voltageout)
+
+ def set_attenuation(self, attenuation):
+ """Set the attenuation of GSS6450.
+
+ Args:
+ attenuation: RPS attenuation level, in dB.
+ Type, numerical.
+
+ Raises:
+ GSS6450Error: raise when attenuation is not in range.
+ """
+ if not 0 <= attenuation <= 31:
+ errmsg = ('"attenuation" must be within [0, 31], '
+ 'current input is {}').format(str(attenuation))
+ raise GSS6450Error(error=errmsg, command='set_attenuation')
+
+ attenuation_raw = round(attenuation)
+
+ if attenuation_raw != attenuation:
+ warningmsg = ('"attenuation" must be integer, current input '
+ 'will be rounded to {}'.format(attenuation_raw))
+ self._logger.warning(warningmsg)
+
+ _ = self._put('-a{},-wA'.format(attenuation_raw))
+
+ self._logger.debug('Set attenuation: %s dB.', attenuation_raw)
+
+ def set_playback_offset(self, offset):
+ """Set the playback offset of GSS6450.
+
+ Args:
+ offset: RPS playback offset.
+ Type, datetime.timedelta, or numerical.
+
+ Raises:
+ GSS6450Error: raise when offset is not numeric or timedelta.
+ """
+ if isinstance(offset, datetime.timedelta):
+ offset_raw = offset.total_seconds()
+ elif isinstance(offset, numbers.Number):
+ offset_raw = offset
+ else:
+ raise GSS6450Error(error=('"offset" must be numerical value or '
+ 'datetime.timedelta'),
+ command='set_playback_offset')
+
+ _ = self._put('-o{}'.format(offset_raw))
+
+ self._logger.debug('Set playback offset: %s sec.', offset_raw)
+
+ def set_storage_media(self, media):
+ """Set the storage media of GSS6450.
+
+ Args:
+ media: RPS storage Media, Internal or External.
+ Type, str. Option, 'internal', 'removable'
+
+ Raises:
+ GSS6450Error: raise when media option is not support.
+ """
+ if media == 'internal':
+ raw_media = '1'
+ elif media == 'removable':
+ raw_media = '2'
+ else:
+ raise GSS6450Error(
+ error=('"media" input must be in ["internal", "removable"]. '
+ ' Current input is {}'.format(media)),
+ command='set_storage_media')
+
+ _ = self._put('-M{}-wM'.format(raw_media))
+
+ resp_raw = self.get_storage_media()
+ if raw_media != resp_raw[0]:
+ raise GSS6450Error(
+ error=('Setting media "{}" is not the same as queried media '
+ '"{}".'.format(media, resp_raw)),
+ command='set_storage_media')
diff --git a/acts/framework/acts/test_utils/instrumentation/device/apps/dismiss_dialogs.py b/acts/framework/acts/test_utils/instrumentation/device/apps/dismiss_dialogs.py
new file mode 100644
index 0000000..909326a
--- /dev/null
+++ b/acts/framework/acts/test_utils/instrumentation/device/apps/dismiss_dialogs.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+from acts.test_utils.instrumentation.device.apps.app_installer import \
+ AppInstaller
+from acts.test_utils.instrumentation.device.command.instrumentation_command_builder \
+ import InstrumentationCommandBuilder
+
+DISMISS_DIALOGS_RUNNER = '.DismissDialogsInstrumentation'
+SCREENSHOTS_DIR = 'dialog-dismissal'
+DISMISS_DIALOGS_TIMEOUT = 300
+
+
+class DialogDismissalUtil(object):
+ """Utility for dismissing app dialogs."""
+ def __init__(self, dut, util_apk):
+ self._dut = dut
+ self._dismiss_dialogs_apk = AppInstaller(dut, util_apk)
+ self._dismiss_dialogs_apk.install('-g')
+
+ def dismiss_dialogs(self, apps, screenshots=True, quit_on_error=True):
+ """Dismiss dialogs for the given apps.
+
+ Args:
+ apps: List of apps to dismiss dialogs
+ screenshots: Boolean to enable screenshots upon dialog dismissal
+ quit_on_error: Boolean to indicate if tool should quit on failure
+ """
+ if not apps:
+ return
+ if not isinstance(apps, list):
+ apps = [apps]
+ self._dut.log.info('Dismissing app dialogs for %s' % apps)
+ cmd_builder = InstrumentationCommandBuilder()
+ cmd_builder.set_manifest_package(self._dismiss_dialogs_apk.pkg_name)
+ cmd_builder.set_runner(DISMISS_DIALOGS_RUNNER)
+ cmd_builder.add_flag('-w')
+ cmd_builder.add_key_value_param('apps', ','.join(apps))
+ cmd_builder.add_key_value_param('screenshots', screenshots)
+ cmd_builder.add_key_value_param('quitOnError', quit_on_error)
+ self._dut.adb.shell(cmd_builder.build(),
+ timeout=DISMISS_DIALOGS_TIMEOUT)
+
+ # Pull screenshots if screenshots=True
+ if screenshots:
+ self._dut.pull_files(
+ os.path.join(self._dut.external_storage_path, SCREENSHOTS_DIR),
+ self._dut.device_log_path
+ )
+
+ def close(self):
+ """Clean up util by uninstalling the dialog dismissal APK."""
+ self._dismiss_dialogs_apk.uninstall()
diff --git a/acts/framework/acts/test_utils/instrumentation/device/apps/hotword_model_extractor.py b/acts/framework/acts/test_utils/instrumentation/device/apps/hotword_model_extractor.py
new file mode 100644
index 0000000..57386b9
--- /dev/null
+++ b/acts/framework/acts/test_utils/instrumentation/device/apps/hotword_model_extractor.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import tempfile
+import zipfile
+
+from acts.test_utils.instrumentation.device.apps.app_installer \
+ import AppInstaller
+
+DEFAULT_MODEL_NAME = 'en_us.mmap'
+MODEL_DIR = 'res/raw'
+
+
+class HotwordModelExtractor(object):
+ """
+ Extracts a voice model data file from the Hotword APK and pushes it
+ onto the device.
+ """
+ def __init__(self, dut):
+ self._dut = dut
+
+ def extract_to_dut(self, hotword_pkg, model_name=DEFAULT_MODEL_NAME):
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ extracted_model = self._extract(hotword_pkg, model_name, tmp_dir)
+ if not extracted_model:
+ return
+ device_dir = self._dut.adb.shell('echo $EXTERNAL_STORAGE')
+ self._dut.adb.push(
+ extracted_model, os.path.join(device_dir, model_name))
+
+ def _extract(self, hotword_pkg, model_name, dest):
+ """Extracts the model file from the given Hotword APK.
+
+ Args:
+ hotword_pkg: Package name of the Hotword APK
+ model_name: File name of the model file.
+ dest: Destination directory
+
+ Returns: Full path to the extracted model file.
+ """
+ self._dut.log.info('Extracting voice model from Hotword APK.')
+ hotword_apk = AppInstaller.pull_from_device(
+ self._dut, hotword_pkg, dest)
+ if not hotword_apk:
+ self._dut.log.warning('Cannot extract Hotword voice model: '
+ 'Hotword APK not installed.')
+ return None
+
+ model_rel_path = os.path.join(MODEL_DIR, model_name)
+ with zipfile.ZipFile(hotword_apk.apk_path) as hotword_zip:
+ try:
+ return hotword_zip.extract(model_rel_path, dest)
+ except KeyError:
+ self._dut.log.warning(
+ 'Cannot extract Hotword voice model: Model file %s not '
+ 'found.' % model_rel_path)
+ return None
diff --git a/acts/framework/acts/test_utils/instrumentation/device/command/adb_command_types.py b/acts/framework/acts/test_utils/instrumentation/device/command/adb_command_types.py
index 5c55734..072e1ae 100644
--- a/acts/framework/acts/test_utils/instrumentation/device/command/adb_command_types.py
+++ b/acts/framework/acts/test_utils/instrumentation/device/command/adb_command_types.py
@@ -14,6 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from acts.test_utils.instrumentation.device.command.intent_builder import \
+ IntentBuilder
+
class DeviceState(object):
"""Class for adb commands for setting device properties to a value."""
@@ -82,6 +85,31 @@
on_val, off_val)
+class DeviceGServices(DeviceState):
+ """Class for overriding a GServices value."""
+
+ OVERRIDE_GSERVICES_INTENT = ('com.google.gservices.intent.action.'
+ 'GSERVICES_OVERRIDE')
+
+ def __init__(self, setting, on_val='true', off_val='false'):
+ """Create a DeviceGServices.
+
+ Args:
+ setting: Name of the GServices setting
+ on_val: Value used for the 'on' state
+ off_val: Value used for the 'off' state
+ """
+ super().__init__(None, on_val, off_val)
+ self._intent_builder = IntentBuilder('am broadcast')
+ self._intent_builder.set_action(self.OVERRIDE_GSERVICES_INTENT)
+ self._setting = setting
+
+ def set_value(self, value):
+ """Returns the adb command with the given value."""
+ self._intent_builder.add_key_value_param(self._setting, value)
+ return self._intent_builder.build()
+
+
class DeviceBinaryCommandSeries(object):
"""Class for toggling multiple settings at once."""
diff --git a/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/common.py b/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/common.py
index 0658419..bf853dc 100644
--- a/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/common.py
+++ b/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/common.py
@@ -72,8 +72,7 @@
# Screen
-screen_adaptive_brightness = DeviceSetting(
- SYSTEM, 'screen_brightness_mode')
+screen_adaptive_brightness = DeviceSetting(SYSTEM, 'screen_brightness_mode')
screen_brightness = DeviceSetting(SYSTEM, 'screen_brightness')
@@ -83,6 +82,8 @@
doze_mode = DeviceSetting(SECURE, 'doze_enabled')
+doze_always_on = DeviceSetting(SECURE, 'doze_always_on')
+
wake_gesture = DeviceSetting(SECURE, 'wake_gesture_enabled')
screensaver = DeviceSetting(SECURE, 'screensaver_enabled')
@@ -124,6 +125,16 @@
disable_doze = 'dumpsys deviceidle disable'
+# Sensors
+
+disable_sensors = 'dumpsys sensorservice restrict blah'
+
+MOISTURE_DETECTION_SETTING_FILE = '/sys/class/power_supply/usb/moisture_detection_enabled'
+disable_moisture_detection = 'echo 0 > %s' % MOISTURE_DETECTION_SETTING_FILE
+
+## Ambient EQ: https://support.google.com/googlenest/answer/9137130?hl=en
+ambient_eq = DeviceSetting(SECURE, 'display_white_balance_enabled')
+
# Miscellaneous
test_harness = DeviceBinaryCommandSeries(
diff --git a/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/goog.py b/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/goog.py
new file mode 100644
index 0000000..48f6bf1
--- /dev/null
+++ b/acts/framework/acts/test_utils/instrumentation/device/command/adb_commands/goog.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.test_utils.instrumentation.device.command.adb_command_types \
+ import DeviceBinaryCommandSeries
+from acts.test_utils.instrumentation.device.command.adb_command_types \
+ import DeviceGServices
+from acts.test_utils.instrumentation.device.command.adb_command_types \
+ import DeviceState
+
+"""Google-internal device settings for power testing."""
+
+# TODO: add descriptions to each setting
+
+# Location
+
+location_collection = DeviceGServices(
+ 'location:collection_enabled', on_val='1', off_val='0')
+
+location_opt_in = DeviceBinaryCommandSeries(
+ [
+ DeviceState('content insert --uri content://com.google.settings/'
+ 'partner --bind name:s:use_location_for_services '
+ '--bind value:s:%s'),
+ DeviceState('content insert --uri content://com.google.settings/'
+ 'partner --bind name:s:network_location_opt_in '
+ '--bind value:s:%s')
+ ]
+)
+
+# Cast
+
+cast_broadcast = DeviceGServices('gms:cast:mdns_device_scanner:is_enabled')
+
+
+# Apps
+
+disable_playstore = 'pm disable-user com.android.vending'
+
+
+# Volta
+
+disable_volta = 'pm disable-user com.google.android.volta'
+
+
+# CHRE
+
+disable_chre = 'setprop ctl.stop vendor.chre'
+
+
+# MusicIQ
+
+disable_musiciq = 'pm disable-user com.google.intelligence.sense'
+
+
+# Hotword
+
+disable_hotword = (
+ 'am start -a com.android.intent.action.MANAGE_VOICE_KEYPHRASES '
+ '--ei com.android.intent.extra.VOICE_KEYPHRASE_ACTION 2 '
+ '--es com.android.intent.extra.VOICE_KEYPHRASE_HINT_TEXT "demo" '
+ '--es com.android.intent.extra.VOICE_KEYPHRASE_LOCALE "en-US" '
+ 'com.android.hotwordenrollment.okgoogle/'
+ 'com.android.hotwordenrollment.okgoogle.EnrollmentActivity')
diff --git a/acts/framework/acts/test_utils/instrumentation/power/__init__.py b/acts/framework/acts/test_utils/instrumentation/power/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/instrumentation/power/__init__.py
diff --git a/acts/framework/acts/test_utils/instrumentation/power/instrumentation_power_test.py b/acts/framework/acts/test_utils/instrumentation/power/instrumentation_power_test.py
new file mode 100644
index 0000000..03b65f0
--- /dev/null
+++ b/acts/framework/acts/test_utils/instrumentation/power/instrumentation_power_test.py
@@ -0,0 +1,475 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import shutil
+import tempfile
+import time
+
+import tzlocal
+from acts.controllers.android_device import SL4A_APK_NAME
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.instrumentation import instrumentation_proto_parser \
+ as proto_parser
+from acts.test_utils.instrumentation.device.apps.app_installer import \
+ AppInstaller
+from acts.test_utils.instrumentation.device.command.adb_command_types import \
+ DeviceGServices
+from acts.test_utils.instrumentation.device.command.adb_command_types import \
+ DeviceSetprop
+from acts.test_utils.instrumentation.device.command.adb_command_types import \
+ DeviceSetting
+from acts.test_utils.instrumentation.device.command.adb_commands import common
+from acts.test_utils.instrumentation.device.command.adb_commands import goog
+from acts.test_utils.instrumentation.device.command.instrumentation_command_builder \
+ import DEFAULT_NOHUP_LOG
+from acts.test_utils.instrumentation.device.command.instrumentation_command_builder \
+ import InstrumentationTestCommandBuilder
+from acts.test_utils.instrumentation.instrumentation_base_test \
+ import InstrumentationBaseTest
+from acts.test_utils.instrumentation.instrumentation_base_test \
+ import InstrumentationTestError
+from acts.test_utils.instrumentation.instrumentation_proto_parser import \
+ DEFAULT_INST_LOG_DIR
+from acts.test_utils.instrumentation.power.power_metrics import Measurement
+from acts.test_utils.instrumentation.power.power_metrics import PowerMetrics
+from acts.test_utils.instrumentation.device.apps.permissions import PermissionsUtil
+
+from acts import asserts
+from acts import context
+
+ACCEPTANCE_THRESHOLD = 'acceptance_threshold'
+AUTOTESTER_LOG = 'autotester.log'
+DEFAULT_PUSH_FILE_TIMEOUT = 180
+DISCONNECT_USB_FILE = 'disconnectusb.log'
+POLLING_INTERVAL = 0.5
+
+
+class InstrumentationPowerTest(InstrumentationBaseTest):
+ """Instrumentation test for measuring and validating power metrics.
+
+ Params:
+ metric_logger: Blackbox metric logger used to store test metrics.
+ _instr_cmd_builder: Builder for the instrumentation command
+ """
+
+ def __init__(self, configs):
+ super().__init__(configs)
+
+ self.metric_logger = BlackboxMappedMetricLogger.for_test_case()
+ self._test_apk = None
+ self._sl4a_apk = None
+ self._instr_cmd_builder = None
+ self._power_metrics = None
+
+ def setup_class(self):
+ super().setup_class()
+ self.monsoon = self.monsoons[0]
+ self._setup_monsoon()
+ self._instr_cmd_builder = self.power_instrumentation_command_builder()
+
+ def _prepare_device(self):
+ """Prepares the device for power testing."""
+ super()._prepare_device()
+ self._cleanup_test_files()
+ self._permissions_util = PermissionsUtil(
+ self.ad_dut,
+ self._instrumentation_config.get_file('permissions_apk'))
+ self._permissions_util.grant_all()
+ self.install_test_apk()
+
+ def _cleanup_device(self):
+ """Clean up device after power testing."""
+ if self._test_apk:
+ self._test_apk.uninstall()
+ self._permissions_util.close()
+ self._cleanup_test_files()
+
+ def base_device_configuration(self):
+ """Run the base setup commands for power testing."""
+ self.log.info('Running base device setup commands.')
+
+ self.ad_dut.adb.ensure_root()
+ self.adb_run(common.dismiss_keyguard)
+ self.ad_dut.ensure_screen_on()
+
+ # Test harness flag
+ self.adb_run(common.test_harness.toggle(True))
+
+ # Calling
+ self.adb_run(common.disable_dialing.toggle(True))
+
+ # Screen
+ self.adb_run(common.screen_always_on.toggle(True))
+ self.adb_run(common.screen_adaptive_brightness.toggle(False))
+
+ brightness_level = None
+ if 'brightness_level' in self._instrumentation_config:
+ brightness_level = self._instrumentation_config['brightness_level']
+
+ if brightness_level is None:
+ raise ValueError('no brightness level defined (or left as None) '
+ 'and it is needed.')
+
+ self.adb_run(common.screen_brightness.set_value(brightness_level))
+ self.adb_run(common.screen_timeout_ms.set_value(1800000))
+ self.adb_run(common.notification_led.toggle(False))
+ self.adb_run(common.screensaver.toggle(False))
+ self.adb_run(common.wake_gesture.toggle(False))
+ self.adb_run(common.doze_mode.toggle(False))
+ self.adb_run(common.doze_always_on.toggle(False))
+
+ # Sensors
+ self.adb_run(common.auto_rotate.toggle(False))
+ self.adb_run(common.disable_sensors)
+ self.adb_run(common.ambient_eq.toggle(False))
+
+ if self.file_exists(common.MOISTURE_DETECTION_SETTING_FILE):
+ self.adb_run(common.disable_moisture_detection)
+
+ # Time
+ self.adb_run(common.auto_time.toggle(False))
+ self.adb_run(common.auto_timezone.toggle(False))
+ self.adb_run(common.timezone.set_value(str(tzlocal.get_localzone())))
+
+ # Location
+ self.adb_run(common.location_gps.toggle(False))
+ self.adb_run(common.location_network.toggle(False))
+
+ # Power
+ self.adb_run(common.battery_saver_mode.toggle(False))
+ self.adb_run(common.battery_saver_trigger.set_value(0))
+ self.adb_run(common.enable_full_batterystats_history)
+ self.adb_run(common.disable_doze)
+
+ # Camera
+ self.adb_run(DeviceSetprop(
+ 'camera.optbar.hdr', 'true', 'false').toggle(True))
+
+ # Gestures
+ gestures = {
+ 'doze_pulse_on_pick_up': False,
+ 'doze_pulse_on_double_tap': False,
+ 'camera_double_tap_power_gesture_disabled': True,
+ 'camera_double_twist_to_flip_enabled': False,
+ 'assist_gesture_enabled': False,
+ 'assist_gesture_silence_alerts_enabled': False,
+ 'assist_gesture_wake_enabled': False,
+ 'system_navigation_keys_enabled': False,
+ 'camera_lift_trigger_enabled': False,
+ 'doze_always_on': False,
+ 'aware_enabled': False,
+ 'doze_wake_screen_gesture': False,
+ 'skip_gesture': False,
+ 'silence_gesture': False
+ }
+ self.adb_run(
+ [DeviceSetting(common.SECURE, k).toggle(v)
+ for k, v in gestures.items()])
+
+ # GServices
+ self.adb_run(goog.location_collection.toggle(False))
+ self.adb_run(goog.cast_broadcast.toggle(False))
+ self.adb_run(DeviceGServices(
+ 'location:compact_log_enabled').toggle(True))
+ self.adb_run(DeviceGServices('gms:magictether:enable').toggle(False))
+ self.adb_run(DeviceGServices('ocr.cc_ocr_enabled').toggle(False))
+ self.adb_run(DeviceGServices(
+ 'gms:phenotype:phenotype_flag:debug_bypass_phenotype').toggle(True))
+ self.adb_run(DeviceGServices(
+ 'gms_icing_extension_download_enabled').toggle(False))
+
+ # Comms
+ self.adb_run(common.wifi.toggle(False))
+ self.adb_run(common.bluetooth.toggle(False))
+ self.adb_run(common.airplane_mode.toggle(True))
+
+ # Misc. Google features
+ self.adb_run(goog.disable_playstore)
+ self.adb_run(goog.disable_volta)
+ self.adb_run(goog.disable_chre)
+ self.adb_run(goog.disable_musiciq)
+ self.adb_run(goog.disable_hotword)
+
+ # Enable clock dump info
+ self.adb_run('echo 1 > /d/clk/debug_suspend')
+
+ def _setup_monsoon(self):
+ """Set up the Monsoon controller for this testclass/testcase."""
+ self.log.info('Setting up Monsoon %s' % self.monsoon.serial)
+ monsoon_config = self._get_merged_config('Monsoon')
+ self._monsoon_voltage = monsoon_config.get_numeric('voltage', 4.2)
+ self.monsoon.set_voltage_safe(self._monsoon_voltage)
+ if 'max_current' in monsoon_config:
+ self.monsoon.set_max_current(
+ monsoon_config.get_numeric('max_current'))
+
+ self.monsoon.usb('on')
+ self.monsoon.set_on_disconnect(self._on_disconnect)
+ self.monsoon.set_on_reconnect(self._on_reconnect)
+
+ self._disconnect_usb_timeout = monsoon_config.get_numeric(
+ 'usb_disconnection_timeout', 240)
+
+ self._measurement_args = dict(
+ duration=monsoon_config.get_numeric('duration'),
+ hz=monsoon_config.get_numeric('frequency'),
+ measure_after_seconds=monsoon_config.get_numeric('delay')
+ )
+
+ def _on_disconnect(self):
+ """Callback invoked by device disconnection from the Monsoon."""
+ self.ad_dut.log.info('Disconnecting device.')
+ self.ad_dut.stop_services()
+ # Uninstall SL4A
+ self._sl4a_apk = AppInstaller.pull_from_device(
+ self.ad_dut, SL4A_APK_NAME, tempfile.mkdtemp(prefix='sl4a'))
+ self._sl4a_apk.uninstall()
+ time.sleep(1)
+
+ def _on_reconnect(self):
+ """Callback invoked by device reconnection to the Monsoon"""
+ # Reinstall SL4A
+ if not self.ad_dut.is_sl4a_installed() and self._sl4a_apk:
+ self._sl4a_apk.install()
+ shutil.rmtree(os.path.dirname(self._sl4a_apk.apk_path))
+ self._sl4a_apk = None
+ self.ad_dut.start_services()
+ # Release wake lock to put device into sleep.
+ self.ad_dut.droid.goToSleepNow()
+ self.ad_dut.log.info('Device reconnected.')
+
+ def install_test_apk(self):
+ """Installs test apk on the device."""
+ test_apk_file = self._instrumentation_config.get_file('test_apk')
+ self._test_apk = AppInstaller(self.ad_dut, test_apk_file)
+ self._test_apk.install('-g')
+ if not self._test_apk.is_installed():
+ raise InstrumentationTestError('Failed to install test APK.')
+
+ def _cleanup_test_files(self):
+ """Remove test-generated files from the device."""
+ self.ad_dut.log.info('Cleaning up test generated files.')
+ for file_name in [DISCONNECT_USB_FILE, DEFAULT_INST_LOG_DIR,
+ DEFAULT_NOHUP_LOG, AUTOTESTER_LOG]:
+ path = os.path.join(self.ad_dut.external_storage_path, file_name)
+ self.adb_run('rm -rf %s' % path)
+
+ def trigger_scan_on_external_storage(self):
+ cmd = 'am broadcast -a android.intent.action.MEDIA_MOUNTED '
+ cmd = cmd + '-d file://%s ' % self.ad_dut.external_storage_path
+ cmd = cmd + '--receiver-include-background'
+ return self.adb_run(cmd)
+
+ def file_exists(self, file_path):
+ cmd = '(test -f %s && echo yes) || echo no' % file_path
+ result = self.adb_run(cmd)
+ if result[cmd] == 'yes':
+ return True
+ elif result[cmd] == 'no':
+ return False
+ raise ValueError('Couldn\'t determine if %s exists. '
+ 'Expected yes/no, got %s' % (file_path, result[cmd]))
+
+ def push_to_external_storage(self, file_path, dest=None,
+ timeout=DEFAULT_PUSH_FILE_TIMEOUT):
+ """Pushes a file to {$EXTERNAL_STORAGE} and returns its final location.
+
+ Args:
+ file_path: The file to be pushed.
+ dest: Where within {$EXTERNAL_STORAGE} it should be pushed.
+ timeout: Float number of seconds to wait for the file to be pushed.
+
+ Returns: The absolute path where the file was pushed.
+ """
+ if dest is None:
+ dest = os.path.basename(file_path)
+
+ dest_path = os.path.join(self.ad_dut.external_storage_path, dest)
+ self.log.info('clearing %s before pushing %s' % (dest_path, file_path))
+ self.ad_dut.adb.shell('rm -rf %s', dest_path)
+ self.log.info('pushing file %s to %s' % (file_path, dest_path))
+ self.ad_dut.adb.push(file_path, dest_path, timeout=timeout)
+ return dest_path
+
+ # Test runtime utils
+
+ def power_instrumentation_command_builder(self):
+ """Return the default command builder for power tests"""
+ builder = InstrumentationTestCommandBuilder.default()
+ builder.set_manifest_package(self._test_apk.pkg_name)
+ builder.set_nohup()
+ return builder
+
+ def _wait_for_disconnect_signal(self):
+ """Poll the device for a disconnect USB signal file. This will indicate
+ to the Monsoon that the device is ready to be disconnected.
+ """
+ self.log.info('Waiting for USB disconnect signal')
+ disconnect_file = os.path.join(
+ self.ad_dut.external_storage_path, DISCONNECT_USB_FILE)
+ start_time = time.time()
+ while time.time() < start_time + self._disconnect_usb_timeout:
+ if self.ad_dut.adb.shell('ls %s' % disconnect_file):
+ return
+ time.sleep(POLLING_INTERVAL)
+ raise InstrumentationTestError('Timeout while waiting for USB '
+ 'disconnect signal.')
+
+ def measure_power(self):
+ """Measures power consumption with the Monsoon. See monsoon_lib API for
+ details.
+ """
+ if not hasattr(self, '_measurement_args'):
+ raise InstrumentationTestError('Missing Monsoon measurement args.')
+
+ # Start measurement after receiving disconnect signal
+ self._wait_for_disconnect_signal()
+ power_data_path = os.path.join(
+ context.get_current_context().get_full_output_path(), 'power_data')
+ self.log.info('Starting Monsoon measurement.')
+ self.monsoon.usb('auto')
+ measure_start_time = time.time()
+ result = self.monsoon.measure_power(
+ **self._measurement_args, output_path=power_data_path)
+ self.monsoon.usb('on')
+ self.log.info('Monsoon measurement complete.')
+
+ # Gather relevant metrics from measurements
+ session = self.dump_instrumentation_result_proto()
+ self._power_metrics = PowerMetrics(self._monsoon_voltage,
+ start_time=measure_start_time)
+ self._power_metrics.generate_test_metrics(
+ PowerMetrics.import_raw_data(power_data_path),
+ proto_parser.get_test_timestamps(session))
+ self._log_metrics()
+ return result
+
+ def run_and_measure(self, instr_class, instr_method=None, req_params=None,
+ extra_params=None):
+ """Convenience method for setting up the instrumentation test command,
+ running it on the device, and starting the Monsoon measurement.
+
+ Args:
+ instr_class: Fully qualified name of the instrumentation test class
+ instr_method: Name of the instrumentation test method
+ req_params: List of required parameter names
+ extra_params: List of ad-hoc parameters to be passed defined as
+ tuples of size 2.
+
+ Returns: summary of Monsoon measurement
+ """
+ if instr_method:
+ self._instr_cmd_builder.add_test_method(instr_class, instr_method)
+ else:
+ self._instr_cmd_builder.add_test_class(instr_class)
+ params = {}
+ instr_call_config = self._get_merged_config('instrumentation_call')
+ # Add required parameters
+ for param_name in req_params or []:
+ params[param_name] = instr_call_config.get(
+ param_name, verify_fn=lambda x: x is not None,
+ failure_msg='%s is a required parameter.' % param_name)
+ # Add all other parameters
+ params.update(instr_call_config)
+ for name, value in params.items():
+ self._instr_cmd_builder.add_key_value_param(name, value)
+
+ if extra_params:
+ for name, value in extra_params:
+ self._instr_cmd_builder.add_key_value_param(name, value)
+
+ instr_cmd = self._instr_cmd_builder.build()
+ self.log.info('Running instrumentation call: %s' % instr_cmd)
+ self.adb_run_async(instr_cmd)
+ return self.measure_power()
+
+ def _log_metrics(self):
+ """Record the collected metrics with the metric logger."""
+ for metric_name in PowerMetrics.ALL_METRICS:
+ for instr_test_name in self._power_metrics.test_metrics:
+ metric_value = getattr(
+ self._power_metrics.test_metrics[instr_test_name],
+ metric_name).value
+ self.metric_logger.add_metric(
+ '%s__%s' % (metric_name, instr_test_name), metric_value)
+
+ def validate_power_results(self, *instr_test_names):
+ """Compare power measurements with target values and set the test result
+ accordingly.
+
+ Args:
+ instr_test_names: Name(s) of the instrumentation test method.
+ If none specified, defaults to all test methods run.
+
+ Raises:
+ signals.TestFailure if one or more metrics do not satisfy threshold
+ """
+ summaries = {}
+ failure = False
+ all_thresholds = self._get_merged_config(ACCEPTANCE_THRESHOLD)
+
+ if not instr_test_names:
+ instr_test_names = all_thresholds.keys()
+
+ for instr_test_name in instr_test_names:
+ try:
+ test_metrics = self._power_metrics.test_metrics[instr_test_name]
+ except KeyError:
+ raise InstrumentationTestError(
+ 'Unable to find test method %s in instrumentation output. '
+ 'Check instrumentation call results in '
+ 'instrumentation_proto.txt.'
+ % instr_test_name)
+
+ summaries[instr_test_name] = {}
+ test_thresholds = all_thresholds.get_config(instr_test_name)
+ for metric_name, metric in test_thresholds.items():
+ try:
+ actual_result = getattr(test_metrics, metric_name)
+ except AttributeError:
+ continue
+
+ if 'unit_type' not in metric or 'unit' not in metric:
+ continue
+ unit_type = metric['unit_type']
+ unit = metric['unit']
+
+ lower_value = metric.get_numeric('lower_limit', float('-inf'))
+ upper_value = metric.get_numeric('upper_limit', float('inf'))
+ if 'expected_value' in metric and 'percent_deviation' in metric:
+ expected_value = metric.get_numeric('expected_value')
+ percent_deviation = metric.get_numeric('percent_deviation')
+ lower_value = expected_value * (1 - percent_deviation / 100)
+ upper_value = expected_value * (1 + percent_deviation / 100)
+
+ lower_bound = Measurement(lower_value, unit_type, unit)
+ upper_bound = Measurement(upper_value, unit_type, unit)
+ summary_entry = {
+ 'expected': '[%s, %s]' % (lower_bound, upper_bound),
+ 'actual': str(actual_result.to_unit(unit))
+ }
+ summaries[instr_test_name][metric_name] = summary_entry
+ if not lower_bound <= actual_result <= upper_bound:
+ failure = True
+ self.log.info('Summary of measurements: %s' % summaries)
+ asserts.assert_false(
+ failure,
+ msg='One or more measurements do not meet the specified criteria',
+ extras=summaries)
+ asserts.explicit_pass(
+ msg='All measurements meet the criteria',
+ extras=summaries)
diff --git a/acts/framework/acts/test_utils/instrumentation/power/power_metrics.py b/acts/framework/acts/test_utils/instrumentation/power/power_metrics.py
new file mode 100644
index 0000000..b41ae61
--- /dev/null
+++ b/acts/framework/acts/test_utils/instrumentation/power/power_metrics.py
@@ -0,0 +1,298 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import math
+
+from acts.test_utils.instrumentation import instrumentation_proto_parser \
+ as parser
+from acts.test_utils.instrumentation.instrumentation_base_test \
+ import InstrumentationTestError
+
+# Unit type constants
+CURRENT = 'current'
+POWER = 'power'
+TIME = 'time'
+
+# Unit constants
+MILLIAMP = 'mA'
+AMP = 'A'
+AMPERE = AMP
+MILLIWATT = 'mW'
+WATT = 'W'
+MILLISECOND = 'ms'
+SECOND = 's'
+MINUTE = 'm'
+HOUR = 'h'
+
+CONVERSION_TABLES = {
+ CURRENT: {
+ MILLIAMP: 0.001,
+ AMP: 1
+ },
+ POWER: {
+ MILLIWATT: 0.001,
+ WATT: 1
+ },
+ TIME: {
+ MILLISECOND: 0.001,
+ SECOND: 1,
+ MINUTE: 60,
+ HOUR: 3600
+ }
+}
+
+
+class Measurement(object):
+ """Base class for describing power measurement values. Each object contains
+ an value and a unit. Enables some basic arithmetic operations with other
+ measurements of the same unit type.
+
+ Attributes:
+ _value: Numeric value of the measurement
+ _unit_type: Unit type of the measurement (e.g. current, power)
+ _unit: Unit of the measurement (e.g. W, mA)
+ """
+
+ def __init__(self, value, unit_type, unit):
+ if unit_type not in CONVERSION_TABLES:
+ raise TypeError('%s is not a valid unit type' % unit_type)
+ self._value = value
+ self._unit_type = unit_type
+ self._unit = unit
+
+ # Convenience constructor methods
+ @staticmethod
+ def amps(amps):
+ """Create a new current measurement, in amps."""
+ return Measurement(amps, CURRENT, AMP)
+
+ @staticmethod
+ def watts(watts):
+ """Create a new power measurement, in watts."""
+ return Measurement(watts, POWER, WATT)
+
+ @staticmethod
+ def seconds(seconds):
+ """Create a new time measurement, in seconds."""
+ return Measurement(seconds, TIME, SECOND)
+
+ # Comparison methods
+
+ def __eq__(self, other):
+ return self.value == other.to_unit(self._unit).value
+
+ def __lt__(self, other):
+ return self.value < other.to_unit(self._unit).value
+
+ def __le__(self, other):
+ return self == other or self < other
+
+ # Addition and subtraction with other measurements
+
+ def __add__(self, other):
+ """Adds measurements of compatible unit types. The result will be in the
+ same units as self.
+ """
+ return Measurement(self.value + other.to_unit(self._unit).value,
+ self._unit_type, self._unit)
+
+ def __sub__(self, other):
+ """Subtracts measurements of compatible unit types. The result will be
+ in the same units as self.
+ """
+ return Measurement(self.value - other.to_unit(self._unit).value,
+ self._unit_type, self._unit)
+
+ # String representation
+
+ def __str__(self):
+ return '%g%s' % (self._value, self._unit)
+
+ def __repr__(self):
+ return str(self)
+
+ @property
+ def unit(self):
+ return self._unit
+
+ @property
+ def value(self):
+ return self._value
+
+ def to_unit(self, new_unit):
+ """Create an equivalent measurement under a different unit.
+ e.g. 0.5W -> 500mW
+
+ Args:
+ new_unit: Target unit. Must be compatible with current unit.
+
+ Returns: A new measurement with the converted value and unit.
+ """
+ try:
+ new_value = self._value * (
+ CONVERSION_TABLES[self._unit_type][self._unit] /
+ CONVERSION_TABLES[self._unit_type][new_unit])
+ except KeyError:
+ raise TypeError('Incompatible units: %s, %s' %
+ (self._unit, new_unit))
+ return Measurement(new_value, self._unit_type, new_unit)
+
+
+class PowerMetrics(object):
+ """Class for processing raw power metrics generated by Monsoon measurements.
+ Provides useful metrics such as average current, max current, and average
+ power. Can generate individual test metrics.
+
+ See section "Numeric metrics" below for available metrics.
+ """
+
+ def __init__(self, voltage, start_time=0):
+ """Create a PowerMetrics.
+
+ Args:
+ voltage: Voltage of the measurement
+ start_time: Start time of the measurement. Used for generating
+ test-specific metrics.
+ """
+ self._voltage = voltage
+ self._start_time = start_time
+ self._num_samples = 0
+ self._sum_currents = 0
+ self._sum_squares = 0
+ self._max_current = None
+ self._min_current = None
+ self.test_metrics = {}
+
+ @staticmethod
+ def import_raw_data(path):
+ """Create a generator from a Monsoon data file.
+
+ Args:
+ path: path to raw data file
+
+ Returns: generator that yields (timestamp, sample) per line
+ """
+ with open(path, 'r') as f:
+ for line in f:
+ time, sample = line.split()
+ yield float(time[:-1]), float(sample)
+
+ def update_metrics(self, sample):
+ """Update the running metrics with the current sample.
+
+ Args:
+ sample: A current sample in Amps.
+ """
+ self._num_samples += 1
+ self._sum_currents += sample
+ self._sum_squares += sample ** 2
+ if self._max_current is None or sample > self._max_current:
+ self._max_current = sample
+ if self._min_current is None or sample < self._min_current:
+ self._min_current = sample
+
+ def generate_test_metrics(self, raw_data, test_timestamps=None):
+ """Split the data into individual test metrics, based on the timestamps
+ given as a dict.
+
+ Args:
+ raw_data: raw data as list or generator of (timestamp, sample)
+ test_timestamps: dict following the output format of
+ instrumentation_proto_parser.get_test_timestamps()
+ """
+
+ # Initialize metrics for each test
+ if test_timestamps is None:
+ test_timestamps = {}
+ test_starts = {}
+ test_ends = {}
+ for test_name, times in test_timestamps.items():
+ self.test_metrics[test_name] = PowerMetrics(
+ self._voltage, self._start_time)
+ try:
+ test_starts[test_name] = Measurement(
+ times[parser.START_TIMESTAMP], TIME, MILLISECOND) \
+ .to_unit(SECOND).value - self._start_time
+ except KeyError:
+ raise InstrumentationTestError(
+ 'Missing start timestamp for test scenario "%s". Refer to '
+ 'instrumentation_proto.txt for details.' % test_name)
+ try:
+ test_ends[test_name] = Measurement(
+ times[parser.END_TIMESTAMP], TIME, MILLISECOND) \
+ .to_unit(SECOND).value - self._start_time
+ except KeyError:
+ raise InstrumentationTestError(
+ 'Missing end timestamp for test scenario "%s". Test '
+ 'scenario may have terminated with errors. Refer to '
+ 'instrumentation_proto.txt for details.' % test_name)
+
+ # Assign data to tests based on timestamps
+ for timestamp, sample in raw_data:
+ self.update_metrics(sample)
+ for test_name in test_timestamps:
+ if test_starts[test_name] <= timestamp <= test_ends[test_name]:
+ self.test_metrics[test_name].update_metrics(sample)
+
+ # Numeric metrics
+
+ ALL_METRICS = ('avg_current', 'max_current', 'min_current', 'stdev_current',
+ 'avg_power')
+
+ @property
+ def avg_current(self):
+ """Average current, in milliamps."""
+ if not self._num_samples:
+ return Measurement.amps(0).to_unit(MILLIAMP)
+ return (Measurement.amps(self._sum_currents / self._num_samples)
+ .to_unit(MILLIAMP))
+
+ @property
+ def max_current(self):
+ """Max current, in milliamps."""
+ return Measurement.amps(self._max_current or 0).to_unit(MILLIAMP)
+
+ @property
+ def min_current(self):
+ """Min current, in milliamps."""
+ return Measurement.amps(self._min_current or 0).to_unit(MILLIAMP)
+
+ @property
+ def stdev_current(self):
+ """Standard deviation of current values, in milliamps."""
+ if self._num_samples < 2:
+ return Measurement.amps(0).to_unit(MILLIAMP)
+ stdev = math.sqrt(
+ (self._sum_squares - (
+ self._num_samples * self.avg_current.to_unit(AMP).value ** 2))
+ / (self._num_samples - 1))
+ return Measurement.amps(stdev).to_unit(MILLIAMP)
+
+ def current_to_power(self, current):
+ """Converts a current value to a power value."""
+ return (Measurement.watts(current.to_unit(AMP).value * self._voltage))
+
+ @property
+ def avg_power(self):
+ """Average power, in milliwatts."""
+ return self.current_to_power(self.avg_current).to_unit(MILLIWATT)
+
+ @property
+ def summary(self):
+ """A summary of test metrics"""
+ return {'average_current': str(self.avg_current),
+ 'max_current': str(self.max_current),
+ 'average_power': str(self.avg_power)}
diff --git a/acts/framework/acts/test_utils/net/connectivity_const.py b/acts/framework/acts/test_utils/net/connectivity_const.py
index 37b8c80..c3d8d4a 100644
--- a/acts/framework/acts/test_utils/net/connectivity_const.py
+++ b/acts/framework/acts/test_utils/net/connectivity_const.py
@@ -66,6 +66,8 @@
# Private DNS constants
DNS_GOOGLE = "dns.google"
+DNS_QUAD9 = "dns.quad9.net"
+DNS_CLOUDFLARE = "1dot1dot1dot1.cloudflare-dns.com"
PRIVATE_DNS_MODE_OFF = "off"
PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"
PRIVATE_DNS_MODE_STRICT = "hostname"
diff --git a/acts/framework/acts/test_utils/net/connectivity_test_utils.py b/acts/framework/acts/test_utils/net/connectivity_test_utils.py
index 4b7668c..153630f 100644
--- a/acts/framework/acts/test_utils/net/connectivity_test_utils.py
+++ b/acts/framework/acts/test_utils/net/connectivity_test_utils.py
@@ -15,54 +15,95 @@
from acts import asserts
from acts.test_utils.net import connectivity_const as cconst
+from queue import Empty
-def start_natt_keepalive(ad, src_ip, src_port, dst_ip, interval = 10):
- """ Start NAT-T keep alive on dut """
+def _listen_for_keepalive_event(ad, key, msg, ka_event):
+ """Listen for keepalive event and return status
- ad.log.info("Starting NATT Keepalive")
- status = None
-
- key = ad.droid.connectivityStartNattKeepalive(
- interval, src_ip, src_port, dst_ip)
-
- ad.droid.connectivityNattKeepaliveStartListeningForEvent(key, "Started")
+ Args:
+ ad: DUT object
+ key: keepalive key
+ msg: Error message
+ event: Keepalive event type
+ """
+ ad.droid.socketKeepaliveStartListeningForEvent(key, ka_event)
try:
- event = ad.ed.pop_event("PacketKeepaliveCallback")
- status = event["data"]["packetKeepaliveEvent"]
+ event = ad.ed.pop_event("SocketKeepaliveCallback")
+ status = event["data"]["socketKeepaliveEvent"] == ka_event
except Empty:
- msg = "Failed to receive confirmation of starting natt keepalive"
asserts.fail(msg)
finally:
- ad.droid.connectivityNattKeepaliveStopListeningForEvent(
- key, "Started")
-
- if status != "Started":
- ad.log.error("Received keepalive status: %s" % status)
- ad.droid.connectivityRemovePacketKeepaliveReceiverKey(key)
- return None
- return key
-
-def stop_natt_keepalive(ad, key):
- """ Stop NAT-T keep alive on dut """
-
- ad.log.info("Stopping NATT keepalive")
- status = False
- ad.droid.connectivityStopNattKeepalive(key)
-
- ad.droid.connectivityNattKeepaliveStartListeningForEvent(key, "Stopped")
- try:
- event = ad.ed.pop_event("PacketKeepaliveCallback")
- status = event["data"]["packetKeepaliveEvent"] == "Stopped"
- except Empty:
- msg = "Failed to receive confirmation of stopping natt keepalive"
- asserts.fail(msg)
- finally:
- ad.droid.connectivityNattKeepaliveStopListeningForEvent(
- key, "Stopped")
-
- ad.droid.connectivityRemovePacketKeepaliveReceiverKey(key)
+ ad.droid.socketKeepaliveStopListeningForEvent(key, ka_event)
+ if ka_event != "Started":
+ ad.droid.removeSocketKeepaliveReceiverKey(key)
+ if status:
+ ad.log.info("'%s' keepalive event successful" % ka_event)
return status
+def start_natt_socket_keepalive(ad, udp_encap, src_ip, dst_ip, interval = 10):
+ """Start NATT SocketKeepalive on DUT
+
+ Args:
+ ad: DUT object
+ udp_encap: udp_encap socket key
+ src_ip: IP addr of the client
+ dst_ip: IP addr of the keepalive server
+ interval: keepalive time interval
+ """
+ ad.log.info("Starting Natt Socket Keepalive")
+ key = ad.droid.startNattSocketKeepalive(udp_encap, src_ip, dst_ip, interval)
+ msg = "Failed to receive confirmation of starting natt socket keeaplive"
+ status = _listen_for_keepalive_event(ad, key, msg, "Started")
+ return key if status else None
+
+def start_tcp_socket_keepalive(ad, socket, time_interval = 10):
+ """Start TCP socket keepalive on DUT
+
+ Args:
+ ad: DUT object
+ socket: TCP socket key
+ time_interval: Keepalive time interval
+ """
+ ad.log.info("Starting TCP Socket Keepalive")
+ key = ad.droid.startTcpSocketKeepalive(socket, time_interval)
+ msg = "Failed to receive confirmation of starting tcp socket keeaplive"
+ status = _listen_for_keepalive_event(ad, key, msg, "Started")
+ return key if status else None
+
+def socket_keepalive_error(ad, key):
+ """Verify Error callback
+
+ Args:
+ ad: DUT object
+ key: Keepalive key
+ """
+ ad.log.info("Verify Error callback on keepalive: %s" % key)
+ msg = "Failed to receive confirmation of Error callback"
+ return _listen_for_keepalive_event(ad, key, msg, "Error")
+
+def socket_keepalive_data_received(ad, key):
+ """Verify OnDataReceived callback
+
+ Args:
+ ad: DUT object
+ key: Keepalive key
+ """
+ ad.log.info("Verify OnDataReceived callback on keepalive: %s" % key)
+ msg = "Failed to receive confirmation of OnDataReceived callback"
+ return _listen_for_keepalive_event(ad, key, msg, "OnDataReceived")
+
+def stop_socket_keepalive(ad, key):
+ """Stop SocketKeepalive on DUT
+
+ Args:
+ ad: DUT object
+ key: Keepalive key
+ """
+ ad.log.info("Stopping Socket keepalive: %s" % key)
+ ad.droid.stopSocketKeepalive(key)
+ msg = "Failed to receive confirmation of stopping socket keepalive"
+ return _listen_for_keepalive_event(ad, key, msg, "Stopped")
+
def set_private_dns(ad, dns_mode, hostname=None):
""" Set private DNS mode on dut """
if dns_mode == cconst.PRIVATE_DNS_MODE_OFF:
diff --git a/acts/framework/acts/test_utils/net/net_test_utils.py b/acts/framework/acts/test_utils/net/net_test_utils.py
index 6e62ad7..a0d3361 100644
--- a/acts/framework/acts/test_utils/net/net_test_utils.py
+++ b/acts/framework/acts/test_utils/net/net_test_utils.py
@@ -14,9 +14,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
+import os
from acts.controllers import adb
from acts import asserts
+from acts import signals
from acts import utils
from acts.controllers.adb import AdbError
from acts.logger import epoch_to_log_line_timestamp
@@ -44,6 +46,9 @@
USB_TETHERING_MODE = "svc usb setFunctions rndis"
DEVICE_IP_ADDRESS = "ip address"
+GCE_SSH = "gcloud compute ssh "
+GCE_SCP = "gcloud compute scp "
+
def verify_lte_data_and_tethering_supported(ad):
"""Verify if LTE data is enabled and tethering supported"""
@@ -290,6 +295,85 @@
file_name = "tcpdump_%s_%s.pcap" % (ad.serial, test_name)
return "%s/%s" % (log_path, file_name)
+def start_tcpdump_gce_server(ad, test_name, dest_port, gce):
+ """ Start tcpdump on gce server
+
+ Args:
+ ad: android device object
+ test_name: test case name
+ dest_port: port to collect tcpdump
+ gce: dictionary of gce instance
+
+ Returns:
+ process id and pcap file path from gce server
+ """
+ ad.log.info("Starting tcpdump on gce server")
+
+ # pcap file name
+ fname = "/tmp/%s_%s_%s_%s" % \
+ (test_name, ad.model, ad.serial,
+ time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime(time.time())))
+
+ # start tcpdump
+ tcpdump_cmd = "sudo bash -c \'tcpdump -i %s -w %s.pcap port %s > \
+ %s.txt 2>&1 & echo $!\'" % (gce["interface"], fname, dest_port, fname)
+ gcloud_ssh_cmd = "%s --project=%s --zone=%s %s@%s --command " % \
+ (GCE_SSH, gce["project"], gce["zone"], gce["username"], gce["hostname"])
+ gce_ssh_cmd = '%s "%s"' % (gcloud_ssh_cmd, tcpdump_cmd)
+ utils.exe_cmd(gce_ssh_cmd)
+
+ # get process id
+ ps_cmd = '%s "ps aux | grep tcpdump | grep %s"' % (gcloud_ssh_cmd, fname)
+ tcpdump_pid = utils.exe_cmd(ps_cmd).decode("utf-8", "ignore").split()
+ if not tcpdump_pid:
+ raise signals.TestFailure("Failed to start tcpdump on gce server")
+ return tcpdump_pid[1], fname
+
+def stop_tcpdump_gce_server(ad, tcpdump_pid, fname, gce):
+ """ Stop and pull tcpdump file from gce server
+
+ Args:
+ ad: android device object
+ tcpdump_pid: process id for tcpdump file
+ fname: tcpdump file path
+ gce: dictionary of gce instance
+
+ Returns:
+ pcap file from gce server
+ """
+ ad.log.info("Stop and pull pcap file from gce server")
+
+ # stop tcpdump
+ tcpdump_cmd = "sudo kill %s" % tcpdump_pid
+ gcloud_ssh_cmd = "%s --project=%s --zone=%s %s@%s --command " % \
+ (GCE_SSH, gce["project"], gce["zone"], gce["username"], gce["hostname"])
+ gce_ssh_cmd = '%s "%s"' % (gcloud_ssh_cmd, tcpdump_cmd)
+ utils.exe_cmd(gce_ssh_cmd)
+
+ # verify tcpdump is stopped
+ ps_cmd = '%s "ps aux | grep tcpdump"' % gcloud_ssh_cmd
+ res = utils.exe_cmd(ps_cmd).decode("utf-8", "ignore")
+ if tcpdump_pid in res.split():
+ raise signals.TestFailure("Failed to stop tcpdump on gce server")
+ if not fname:
+ return None
+
+ # pull pcap file
+ gcloud_scp_cmd = "%s --project=%s --zone=%s %s@%s:" % \
+ (GCE_SCP, gce["project"], gce["zone"], gce["username"], gce["hostname"])
+ pull_file = '%s%s.pcap %s/' % (gcloud_scp_cmd, fname, ad.device_log_path)
+ utils.exe_cmd(pull_file)
+ if not os.path.exists(
+ "%s/%s.pcap" % (ad.device_log_path, fname.split('/')[-1])):
+ raise signals.TestFailure("Failed to pull tcpdump from gce server")
+
+ # delete pcaps
+ utils.exe_cmd('%s "sudo rm %s.*"' % (gcloud_ssh_cmd, fname))
+
+ # return pcap file
+ pcap_file = "%s/%s.pcap" % (ad.device_log_path, fname.split('/')[-1])
+ return pcap_file
+
def is_ipaddress_ipv6(ip_address):
"""Verify if the given string is a valid IPv6 address.
diff --git a/acts/framework/acts/test_utils/net/socket_test_utils.py b/acts/framework/acts/test_utils/net/socket_test_utils.py
index a8a05fc..42e4537 100644
--- a/acts/framework/acts/test_utils/net/socket_test_utils.py
+++ b/acts/framework/acts/test_utils/net/socket_test_utils.py
@@ -258,6 +258,18 @@
status = ad.droid.closeTcpServerSocket(socket)
asserts.assert_true(status, "Failed to socket")
+def shutdown_socket(ad, socket):
+ """ Shutdown socket
+
+ Args:
+ 1. ad - androidandroid device object
+ 2. socket - socket key
+ """
+ fd = ad.droid.getFileDescriptorOfSocket(socket)
+ asserts.assert_true(fd, "Failed to get FileDescriptor key")
+ status = ad.droid.shutdownFileDescriptor(fd)
+ asserts.assert_true(status, "Failed to shutdown socket")
+
def send_recv_data_sockets(client, server, client_sock, server_sock):
""" Send data over TCP socket from client to server.
Verify that server received the data
diff --git a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
index 82d655f..122709f 100644
--- a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
@@ -316,11 +316,6 @@
ent_network_pwd=False,
radius_conf_pwd=None,
ap_count=1):
- asserts.assert_true(
- len(self.user_params["AccessPoint"]) == 2,
- "Exactly two access points must be specified. \
- Each access point has 2 radios, one each for 2.4GHZ \
- and 5GHz. A test can choose to use one or both APs.")
config_count = 1
count = 0
diff --git a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
index 2623f9b..e297799 100644
--- a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
@@ -468,6 +468,28 @@
return True
+def run_ping6(dut, target_ip, duration=60):
+ """Run ping test and return the latency result
+
+ Args:
+ dut: the dut which run the ping cmd
+ target_ip: target IP Address for ping
+ duration: the duration time of the ping
+
+ return: dict contains "min/avg/max/mdev" result
+ """
+ cmd = "ping6 -w %d %s" % (duration, target_ip)
+ ping_result = dut.adb.shell(cmd, timeout=duration + 1)
+ res = re.match(".*mdev = (\S+) .*", ping_result, re.S)
+ asserts.assert_true(res, "Cannot reach the IP address %s", target_ip)
+ title = ["min", "avg", "max", "mdev"]
+ result = res.group(1).split("/")
+ latency_result = {}
+ for i in range(len(title)):
+ latency_result[title[i]] = result[i]
+ return latency_result
+
+
#########################################################
# Aware primitives
#########################################################
diff --git a/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py b/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py
index 7aca3c6..448647e 100644
--- a/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py
@@ -24,6 +24,8 @@
from acts.test_utils.wifi import wifi_test_utils as wutils
from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
+WAIT_TIME = 60
+
class WifiP2pBaseTest(BaseTestClass):
def __init__(self, controllers):
if not hasattr(self, 'android_devices'):
@@ -105,5 +107,7 @@
def get_p2p_mac_address(self, dut):
"""Gets the current MAC address being used for Wi-Fi Direct."""
+ dut.reboot()
+ time.sleep(WAIT_TIME)
out = dut.adb.shell("ifconfig p2p0")
return re.match(".* HWaddr (\S+).*", out, re.S).group(1)
diff --git a/acts/framework/acts/test_utils/wifi/wifi_constants.py b/acts/framework/acts/test_utils/wifi/wifi_constants.py
index 21f13d2..49f9725 100644
--- a/acts/framework/acts/test_utils/wifi/wifi_constants.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_constants.py
@@ -41,6 +41,9 @@
# WifiManagerSoftApCallback-[callbackId]-OnNumClientsChanged
SOFTAP_NUMBER_CLIENTS_CHANGED = "-OnNumClientsChanged"
SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY = "NumClients"
+SOFTAP_INFO_CHANGED = "-OnInfoChanged"
+SOFTAP_INFO_FREQUENCY_CALLBACK_KEY = "frequency"
+SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY = "bandwidth"
SOFTAP_STATE_CHANGE_CALLBACK_KEY = "State"
WIFI_AP_DISABLING_STATE = 10
WIFI_AP_DISABLED_STATE = 11
diff --git a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
index 8f356ed..cb3153a 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -49,16 +49,16 @@
DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
-roaming_attn = {
+ROAMING_ATTN = {
"AP1_on_AP2_off": [
0,
0,
- 95,
- 95
+ 60,
+ 60
],
"AP1_off_AP2_on": [
- 95,
- 95,
+ 60,
+ 60,
0,
0
],
@@ -638,6 +638,7 @@
ad.ed.clear_all_events()
# Setting wifi state.
ad.droid.wifiToggleState(new_state)
+ time.sleep(2)
fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state,
ad.serial)
try:
@@ -1025,7 +1026,7 @@
num_of_tries=num_of_tries)
-def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=1):
+def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=3):
"""Toggle wifi state and then wait for Android device to reconnect to
the provided wifi network.
@@ -1948,12 +1949,13 @@
return [attn0, attn1]
-def set_attns(attenuator, attn_val_name):
+def set_attns(attenuator, attn_val_name, roaming_attn=ROAMING_ATTN):
"""Sets attenuation values on attenuators used in this test.
Args:
attenuator: The attenuator object.
attn_val_name: Name of the attenuation value pair to use.
+ roaming_attn: Dictionary specifying the attenuation params.
"""
logging.info("Set attenuation values to %s", roaming_attn[attn_val_name])
try:
@@ -1966,7 +1968,11 @@
attn_val_name)
raise
-def set_attns_steps(attenuators, atten_val_name, steps=10, wait_time=12):
+def set_attns_steps(attenuators,
+ atten_val_name,
+ roaming_attn=ROAMING_ATTN,
+ steps=10,
+ wait_time=12):
"""Set attenuation values on attenuators used in this test. It will change
the attenuation values linearly from current value to target value step by
step.
@@ -1975,6 +1981,7 @@
attenuators: The list of attenuator objects that you want to change
their attenuation value.
atten_val_name: Name of the attenuation value pair to use.
+ roaming_attn: Dictionary specifying the attenuation params.
steps: Number of attenuator changes to reach the target value.
wait_time: Sleep time for each change of attenuator.
"""
@@ -1990,7 +1997,11 @@
time.sleep(wait_time)
-def trigger_roaming_and_validate(dut, attenuator, attn_val_name, expected_con):
+def trigger_roaming_and_validate(dut,
+ attenuator,
+ attn_val_name,
+ expected_con,
+ roaming_attn=ROAMING_ATTN):
"""Sets attenuators to trigger roaming and validate the DUT connected
to the BSSID expected.
@@ -1998,14 +2009,13 @@
attenuator: The attenuator object.
attn_val_name: Name of the attenuation value pair to use.
expected_con: The network information of the expected network.
+ roaming_attn: Dictionary specifying the attenaution params.
"""
expected_con = {
WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
WifiEnums.BSSID_KEY: expected_con["bssid"],
}
- set_attns(attenuator, attn_val_name)
- logging.info("Wait %ss for roaming to finish.", ROAMING_TIMEOUT)
- time.sleep(ROAMING_TIMEOUT)
+ set_attns_steps(attenuator, attn_val_name, roaming_attn)
verify_wifi_connection_info(dut, expected_con)
expected_bssid = expected_con[WifiEnums.BSSID_KEY]
@@ -2034,6 +2044,13 @@
Returns: dict, the softAP config.
"""
+ # Register before start the test.
+ callbackId = ad.dut.droid.registerSoftApCallback()
+ # Check softap info value is default
+ frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True)
+ asserts.assert_true(frequency == 0, "Softap frequency is not reset")
+ asserts.assert_true(bandwdith == 0, "Softap bandwdith is not reset")
+
config = create_softap_config()
start_wifi_tethering(ad.dut,
config[WifiEnums.SSID_KEY],
@@ -2042,6 +2059,15 @@
"SoftAp is not reported as running")
start_wifi_connection_scan_and_ensure_network_found(ad.dut_client,
config[WifiEnums.SSID_KEY])
+
+ # Check softap info can get from callback succeed and assert value should be
+ # valid.
+ frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True)
+ asserts.assert_true(frequency > 0, "Softap frequency is not valid")
+ asserts.assert_true(bandwdith > 0, "Softap bandwdith is not valid")
+ # Unregister callback
+ ad.dut.droid.unregisterSoftApCallback(callbackId)
+
return config
def wait_for_expected_number_of_softap_clients(ad, callbackId,
@@ -2092,6 +2118,41 @@
return None
return num_of_clients
+def get_current_softap_info(ad, callbackId, least_one):
+ """pop up all of softap info changed event from queue.
+ Args:
+ callbackId: Id of the callback associated with registering.
+ least_one: Wait for the info callback event before pop all.
+ Returns:
+ Returns last updated information of softap.
+ """
+ eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str(
+ callbackId) + wifi_constants.SOFTAP_INFO_CHANGED
+ ad.log.info("softap info dump from eventStr %s",
+ eventStr)
+ frequency = 0
+ bandwidth = 0
+ if (least_one):
+ event = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)
+ frequency = event['data'][wifi_constants.
+ SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
+ bandwidth = event['data'][wifi_constants.
+ SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
+ ad.log.info("softap info updated, frequency is %s, bandwidth is %s",
+ frequency, bandwidth)
+
+ events = ad.ed.pop_all(eventStr)
+ for event in events:
+ frequency = event['data'][wifi_constants.
+ SOFTAP_INFO_FREQUENCY_CALLBACK_KEY]
+ bandwidth = event['data'][wifi_constants.
+ SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY]
+ ad.log.info("softap info, frequency is %s, bandwidth is %s",
+ frequency, bandwidth)
+ return frequency, bandwidth
+
+
+
def get_ssrdumps(ad, test_name=""):
"""Pulls dumps in the ssrdump dir
Args:
@@ -2151,30 +2212,33 @@
if test_status:
shutil.rmtree(os.path.dirname(fname))
-def verify_mac_not_found_in_pcap(mac, packets):
+def verify_mac_not_found_in_pcap(ad, mac, packets):
"""Verify that a mac address is not found in the captured packets.
Args:
+ ad: android device object
mac: string representation of the mac address
packets: packets obtained by rdpcap(pcap_fname)
"""
for pkt in packets:
logging.debug("Packet Summary = %s", pkt.summary())
if mac in pkt.summary():
- asserts.fail("Caught Factory MAC: %s in packet sniffer."
- "Packet = %s" % (mac, pkt.show()))
+ asserts.fail("Device %s caught Factory MAC: %s in packet sniffer."
+ "Packet = %s" % (ad.serial, mac, pkt.show()))
-def verify_mac_is_found_in_pcap(mac, packets):
+def verify_mac_is_found_in_pcap(ad, mac, packets):
"""Verify that a mac address is found in the captured packets.
Args:
+ ad: android device object
mac: string representation of the mac address
packets: packets obtained by rdpcap(pcap_fname)
"""
for pkt in packets:
if mac in pkt.summary():
return
- asserts.fail("Did not find MAC = %s in packet sniffer." % mac)
+ asserts.fail("Did not find MAC = %s in packet sniffer."
+ "for device %s" % (mac, ad.serial))
def start_cnss_diags(ads):
for ad in ads:
diff --git a/acts/framework/acts/tracelogger.py b/acts/framework/acts/tracelogger.py
index 9652fd0..ed9ee12 100644
--- a/acts/framework/acts/tracelogger.py
+++ b/acts/framework/acts/tracelogger.py
@@ -14,11 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import datetime
import inspect
-import logging
import os
-import xml.etree.cElementTree as et
class TraceLogger(object):
@@ -65,41 +62,3 @@
def __getattr__(self, name):
return getattr(self._logger, name)
-
-
-class TakoTraceLogger(TraceLogger):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.d = self.debug
- self.e = self.error
- self.i = self.info
- self.t = self.step
- self.w = self.warning
-
- def _logger_level(self, level_name):
- level = logging.getLevelName(level_name)
- return lambda *args, **kwargs: self._logger.log(level, *args, **kwargs)
-
- def step(self, msg, *args, **kwargs):
- """Delegate a step call to the underlying logger."""
- self._log_with(self._logger_level('STEP'), 1, msg, *args, **kwargs)
-
- def device(self, msg, *args, **kwargs):
- """Delegate a device call to the underlying logger."""
- self._log_with(self._logger_level('DEVICE'), 1, msg, *args, **kwargs)
-
- def suite(self, msg, *args, **kwargs):
- """Delegate a device call to the underlying logger."""
- self._log_with(self._logger_level('SUITE'), 1, msg, *args, **kwargs)
-
- def case(self, msg, *args, **kwargs):
- """Delegate a case call to the underlying logger."""
- self._log_with(self._logger_level('CASE'), 1, msg, *args, **kwargs)
-
- def flush_log(self):
- """This function exists for compatibility with Tako's logserial module.
-
- Note that flushing the log is handled automatically by python's logging
- module.
- """
- pass
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index 091d640..209e028 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -19,7 +19,7 @@
import copy
import datetime
import functools
-import IPy
+import ipaddress
import json
import logging
import os
@@ -37,9 +37,6 @@
from acts import signals
from acts.controllers import adb
-from acts.controllers.android_device import AndroidDevice
-from acts.controllers.fuchsia_device import FuchsiaDevice
-from acts.controllers.utils_lib.ssh.connection import SshConnection
from acts.libs.proc import job
# File name length is limited to 255 chars on some OS, so we need to make sure
@@ -962,10 +959,7 @@
default www.google.com
timeout: timeout for icmp pings to complete.
"""
- if is_valid_ipv6_address(dest_ip):
- ping_cmd = "ping6 -W 1"
- else:
- ping_cmd = "ping -W 1"
+ ping_cmd = "ping -W 1"
if count:
ping_cmd += " -c %d" % count
if dest_ip:
@@ -1401,7 +1395,7 @@
Returns:
A list of dictionaries of the the various IP addresses:
- ipv4_private_local_addresses: Any 192.168, 172.16, 10, or 169.254
+ ipv4_private_local_addresses: Any 192.168, 172.16, or 10
addresses
ipv4_public_addresses: Any IPv4 public addresses
ipv6_link_local_addresses: Any fe80:: addresses
@@ -1413,68 +1407,26 @@
ipv6_link_local_addresses = []
ipv6_private_local_addresses = []
ipv6_public_addresses = []
- is_local = comm_channel == job
- if type(comm_channel) is AndroidDevice:
- all_interfaces_and_addresses = comm_channel.adb.shell(
- 'ip -o addr | awk \'!/^[0-9]*: ?lo|link\/ether/ {gsub("/", " "); '
- 'print $2" "$4}\'')
- ifconfig_output = comm_channel.adb.shell('ifconfig %s' % interface)
- elif (type(comm_channel) is SshConnection or is_local):
- all_interfaces_and_addresses = comm_channel.run(
- 'ip -o addr | awk \'!/^[0-9]*: ?lo|link\/ether/ {gsub("/", " "); '
- 'print $2" "$4}\'').stdout
- ifconfig_output = comm_channel.run('ifconfig %s' % interface).stdout
- elif type(comm_channel) is FuchsiaDevice:
- all_interfaces_and_addresses = []
- comm_channel.netstack_lib.init()
- interfaces = comm_channel.netstack_lib.netstackListInterfaces()
- if interfaces.get('error') is not None:
- raise ActsUtilsError('Failed with {}'.format(
- interfaces.get('error')))
- for item in interfaces.get('result'):
- for ipv4_address in item['ipv4_addresses']:
- ipv4_address = '.'.join(map(str, ipv4_address))
- all_interfaces_and_addresses.append(
- '%s %s' % (item['name'], ipv4_address))
- for ipv6_address in item['ipv6_addresses']:
- converted_ipv6_address = []
- for octet in ipv6_address:
- converted_ipv6_address.append(format(octet, 'x').zfill(2))
- ipv6_address = ''.join(converted_ipv6_address)
- ipv6_address = (':'.join(
- ipv6_address[i:i + 4]
- for i in range(0, len(ipv6_address), 4)))
- all_interfaces_and_addresses.append(
- '%s %s' % (item['name'], str(IPy.IP(ipv6_address))))
- all_interfaces_and_addresses = '\n'.join(all_interfaces_and_addresses)
- ifconfig_output = all_interfaces_and_addresses
- else:
- raise ValueError('Unsupported method to send command to device.')
-
+ all_interfaces_and_addresses = comm_channel.run(
+ 'ip -o addr | awk \'!/^[0-9]*: ?lo|link\/ether/ {gsub("/", " "); '
+ 'print $2" "$4}\'').stdout
+ ifconfig_output = comm_channel.run('ifconfig %s' % interface).stdout
for interface_line in all_interfaces_and_addresses.split('\n'):
if interface != interface_line.split()[0]:
continue
- on_device_ip = IPy.IP(interface_line.split()[1])
- if on_device_ip.version() is 4:
- if on_device_ip.iptype() == 'PRIVATE':
- if str(on_device_ip) in ifconfig_output:
- ipv4_private_local_addresses.append(
- on_device_ip.strNormal())
- elif (on_device_ip.iptype() == 'PUBLIC'
- or on_device_ip.iptype() == 'CARRIER_GRADE_NAT'):
- if str(on_device_ip) in ifconfig_output:
- ipv4_public_addresses.append(on_device_ip.strNormal())
- elif on_device_ip.version() is 6:
- if on_device_ip.iptype() == 'LINKLOCAL':
- if str(on_device_ip) in ifconfig_output:
- ipv6_link_local_addresses.append(on_device_ip.strNormal())
- elif on_device_ip.iptype() == 'ULA':
- if str(on_device_ip) in ifconfig_output:
- ipv6_private_local_addresses.append(
- on_device_ip.strNormal())
- elif 'ALLOCATED' in on_device_ip.iptype():
- if str(on_device_ip) in ifconfig_output:
- ipv6_public_addresses.append(on_device_ip.strNormal())
+ on_device_ip = ipaddress.ip_address(interface_line.split()[1])
+ if on_device_ip.version() == 4:
+ if on_device_ip.is_private():
+ ipv4_private_local_addresses.append(str(on_device_ip))
+ elif on_device_ip.is_global():
+ ipv4_public_addresses.append(str(on_device_ip))
+ elif on_device_ip.version() == 6:
+ if on_device_ip.is_link_local():
+ ipv6_link_local_addresses.append(str(on_device_ip))
+ elif on_device_ip.is_private():
+ ipv6_private_local_addresses.append(str(on_device_ip))
+ elif on_device_ip.is_global():
+ ipv6_public_addresses.append(str(on_device_ip))
return {
'ipv4_private': ipv4_private_local_addresses,
'ipv4_public': ipv4_public_addresses,
@@ -1500,6 +1452,14 @@
all_ips_and_interfaces = comm_channel.run(
'(ip -o -4 addr show; ip -o -6 addr show) | '
'awk \'{print $2" "$4}\'').stdout
+ #ipv4_addresses = comm_channel.run(
+ # 'ip -o -4 addr show| awk \'{print $2": "$4}\'').stdout
+ #ipv6_addresses = comm_channel._ssh_session.run(
+ # 'ip -o -6 addr show| awk \'{print $2": "$4}\'').stdout
+ #if desired_ip_address in ipv4_addresses:
+ # ip_addresses_to_search = ipv4_addresses
+ #elif desired_ip_address in ipv6_addresses:
+ # ip_addresses_to_search = ipv6_addresses
for ip_address_and_interface in all_ips_and_interfaces.split('\n'):
if desired_ip_address in ip_address_and_interface:
return ip_address_and_interface.split()[1][:-1]
@@ -1509,18 +1469,5 @@
def renew_linux_ip_address(comm_channel, interface):
comm_channel.run('sudo ifconfig %s down' % interface)
comm_channel.run('sudo ifconfig %s up' % interface)
- comm_channel.run('sudo killall dhcpcd 2>/dev/null; echo""')
- is_dhcpcd_dead = False
- dhcpcd_counter = 0
- dhcpcd_checker_max_times = 3
- while not is_dhcpcd_dead:
- if dhcpcd_counter == dhcpcd_checker_max_times:
- raise TimeoutError('Unable to stop dhcpcd')
- time.sleep(1)
- if 'dhcpcd' in comm_channel.run('ps axu').stdout:
- dhcpcd_counter += 1
- else:
- is_dhcpcd_dead = True
- comm_channel.run('sudo dhcpcd -q -b')
comm_channel.run('sudo dhclient -r %s' % interface)
comm_channel.run('sudo dhclient %s' % interface)
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index e08824c..6eb41e1 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -32,6 +32,7 @@
'numpy',
'pyserial',
'pyyaml>=5.1',
+ 'tzlocal',
'shellescape>=3.4.1',
'protobuf',
'retry',
@@ -41,7 +42,6 @@
'xlsxwriter',
'mobly>=1.10.0',
'grpcio',
- 'IPy',
'Monsoon',
# paramiko-ng is needed vs paramiko as currently paramiko does not support
# ed25519 ssh keys, which is what Fuchsia uses.
diff --git a/acts/framework/tests/acts_utils_test.py b/acts/framework/tests/acts_utils_test.py
index 686b4b1..2ede709 100755
--- a/acts/framework/tests/acts_utils_test.py
+++ b/acts/framework/tests/acts_utils_test.py
@@ -23,156 +23,13 @@
from acts import utils
from acts import signals
from acts.controllers.adb import AdbError
-from acts.controllers.android_device import AndroidDevice
-from acts.controllers.fuchsia_device import FuchsiaDevice
-from acts.controllers.utils_lib.ssh.connection import SshConnection
-from acts.libs.proc import job
PROVISIONED_STATE_GOOD = 1
-MOCK_IP_ADDRESSES = """eno1 100.127.110.79
-eno1 2401:fa00:480:7a00:8d4f:85ff:cc5c:787e
-eno1 2401:fa00:480:7a00:459:b993:fcbf:1419
-eno1 fe80::c66d:3c75:2cec:1d72
-enx00e04c000d06 192.168.42.220
-enx00e04c000d06 fe80::2c68:f1b7:eaaa:52e7"""
-
-MOCK_IFCONFIG_OUTPUT = """eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
- inet 100.127.110.79 netmask 255.255.255.0 broadcast 100.127.110.255
- inet6 2401:fa00:480:7a00:8d4f:85ff:cc5c:787e prefixlen 64 scopeid 0x0<global>
- inet6 fe80::c66d:3c75:2cec:1d72 prefixlen 64 scopeid 0x20<link>
- inet6 2401:fa00:480:7a00:459:b993:fcbf:1419 prefixlen 64 scopeid 0x0<global>
- ether 54:b2:03:13:36:05 txqueuelen 1000 (Ethernet)
- RX packets 32943262 bytes 13324306863 (13.3 GB)
- RX errors 669 dropped 0 overruns 0 frame 669
- TX packets 4778580 bytes 3012041798 (3.0 GB)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
- device interrupt 16 memory 0xdf200000-df220000
-
-enx00e04c000d06: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
- inet 192.168.42.220 netmask 255.255.255.0 broadcast 192.168.42.255
- inet6 fe80::2c68:f1b7:eaaa:52e7 prefixlen 64 scopeid 0x20<link>
- ether 00:e0:4c:00:0d:06 txqueuelen 1000 (Ethernet)
- RX packets 10212416 bytes 3204008175 (3.2 GB)
- RX errors 0 dropped 0 overruns 0 frame 0
- TX packets 9868425 bytes 5641667955 (5.6 GB)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
-
-lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
- inet 127.0.0.1 netmask 255.0.0.0
- inet6 ::1 prefixlen 128 scopeid 0x10<host>
- loop txqueuelen 1000 (Local Loopback)
- RX packets 42779835 bytes 6144028882 (6.1 GB)
- RX errors 0 dropped 0 overruns 0 frame 0
- TX packets 42779835 bytes 6144028882 (6.1 GB)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
-
-"""
-
-FUCHSIA_INTERFACES = {
- 'id':
- '1',
- 'result': [{
- 'features':
- 4,
- 'filepath':
- '[none]',
- 'id':
- 1,
- 'ipv4_addresses': [[127, 0, 0, 1]],
- 'ipv6_addresses': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]],
- 'is_administrative_status_enabled':
- True,
- 'is_physical_status_up':
- True,
- 'mac': [0, 0, 0, 0, 0, 0],
- 'mtu':
- 65536,
- 'name':
- 'lo',
- 'topopath':
- 'loopback'
- }, {
- 'features':
- 0,
- 'filepath':
- '/dev/class/ethernet/000',
- 'id':
- 2,
- 'ipv4_addresses': [[100, 127, 110, 79]],
- 'ipv6_addresses':
- [[254, 128, 0, 0, 0, 0, 0, 0, 198, 109, 60, 117, 44, 236, 29, 114],
- [36, 1, 250, 0, 4, 128, 122, 0, 141, 79, 133, 255, 204, 92, 120, 126],
- [36, 1, 250, 0, 4, 128, 122, 0, 4, 89, 185, 147, 252, 191, 20, 25]],
- 'is_administrative_status_enabled':
- True,
- 'is_physical_status_up':
- True,
- 'mac': [0, 224, 76, 5, 76, 229],
- 'mtu':
- 1514,
- 'name':
- 'eno1',
- 'topopath':
- '@/dev/xhci/xhci/usb-bus/001/001/ifc-000/usb-cdc-ecm/ethernet'
- }, {
- 'features':
- 1,
- 'filepath':
- '/dev/class/ethernet/001',
- 'id':
- 3,
- 'ipv4_addresses': [],
- 'ipv6_addresses':
- [[254, 128, 0, 0, 0, 0, 0, 0, 96, 255, 93, 96, 52, 253, 253, 243],
- [254, 128, 0, 0, 0, 0, 0, 0, 70, 7, 11, 255, 254, 118, 126, 192]],
- 'is_administrative_status_enabled':
- False,
- 'is_physical_status_up':
- False,
- 'mac': [68, 7, 11, 118, 126, 192],
- 'mtu':
- 1500,
- 'name':
- 'wlanxc0',
- 'topopath':
- '@/dev/wifi/wlanphy/wlanif-client/wlan-ethernet/ethernet'
- }],
- 'error':
- None
-}
-
-CORRECT_FULL_IP_LIST = {
- 'ipv4_private': [],
- 'ipv4_public': ['100.127.110.79'],
- 'ipv6_link_local': ['fe80:0:0:0:c66d:3c75:2cec:1d72'],
- 'ipv6_private_local': [],
- 'ipv6_public': [
- '2401:fa00:480:7a00:8d4f:85ff:cc5c:787e',
- '2401:fa00:480:7a00:459:b993:fcbf:1419'
- ]
-}
-
-CORRECT_EMPTY_IP_LIST = {
- 'ipv4_private': [],
- 'ipv4_public': [],
- 'ipv6_link_local': [],
- 'ipv6_private_local': [],
- 'ipv6_public': []
-}
-
-FUCHSIA_INIT_SERVER = ('acts.controllers.fuchsia_device.FuchsiaDevice.'
- 'init_server_connection')
-FUCHSIA_NETSTACK_LIST_INTERFACES = (
- 'acts.controllers.'
- 'fuchsia_lib.netstack.netstack_lib.'
- 'FuchsiaNetstackLib.netstackListInterfaces')
-FUCHSIA_INIT_NETSTACK = ('acts.controllers.fuchsia_lib.netstack.'
- 'netstack_lib.FuchsiaNetstackLib.init')
-
class ByPassSetupWizardTests(unittest.TestCase):
"""This test class for unit testing acts.utils.bypass_setup_wizard."""
+
def test_start_standing_subproc(self):
with self.assertRaisesRegex(utils.ActsUtilsError,
'Process .* has terminated'):
@@ -269,32 +126,33 @@
class BypassSetupWizardReturn:
# No complications. Bypass works the first time without issues.
- NO_COMPLICATIONS = (
- 'Starting: Intent { cmp=com.google.android.setupwizard/'
- '.SetupWizardExitActivity }')
+ NO_COMPLICATIONS = ('Starting: Intent { cmp=com.google.android.setupwizard/'
+ '.SetupWizardExitActivity }')
# Fail with doesn't need to be skipped/was skipped already.
ALREADY_BYPASSED = AdbError('', 'ADB_CMD_OUTPUT:0', 'Error type 3\n'
- 'Error: Activity class', 1)
+ 'Error: Activity class',
+ 1)
# Fail with different error.
UNRECOGNIZED_ERR = AdbError('', 'ADB_CMD_OUTPUT:0', 'Error type 4\n'
- 'Error: Activity class', 0)
+ 'Error: Activity class',
+ 0)
# Fail, get root access, then no complications arise.
- ROOT_ADB_NO_COMP = AdbError(
- '', 'ADB_CMD_OUTPUT:255', 'Security exception: Permission Denial: '
- 'starting Intent { flg=0x10000000 '
- 'cmp=com.google.android.setupwizard/'
- '.SetupWizardExitActivity } from null '
- '(pid=5045, uid=2000) not exported from uid '
- '10000', 0)
+ ROOT_ADB_NO_COMP = AdbError('', 'ADB_CMD_OUTPUT:255',
+ 'Security exception: Permission Denial: '
+ 'starting Intent { flg=0x10000000 '
+ 'cmp=com.google.android.setupwizard/'
+ '.SetupWizardExitActivity } from null '
+ '(pid=5045, uid=2000) not exported from uid '
+ '10000', 0)
# Even with root access, the bypass setup wizard doesn't need to be skipped.
- ROOT_ADB_SKIPPED = AdbError(
- '', 'ADB_CMD_OUTPUT:255', 'Security exception: Permission Denial: '
- 'starting Intent { flg=0x10000000 '
- 'cmp=com.google.android.setupwizard/'
- '.SetupWizardExitActivity } from null '
- '(pid=5045, uid=2000) not exported from '
- 'uid 10000', 0)
+ ROOT_ADB_SKIPPED = AdbError('', 'ADB_CMD_OUTPUT:255',
+ 'Security exception: Permission Denial: '
+ 'starting Intent { flg=0x10000000 '
+ 'cmp=com.google.android.setupwizard/'
+ '.SetupWizardExitActivity } from null '
+ '(pid=5045, uid=2000) not exported from '
+ 'uid 10000', 0)
# Even with root access, the bypass setup wizard fails
ROOT_ADB_FAILS = AdbError(
'', 'ADB_CMD_OUTPUT:255',
@@ -306,6 +164,7 @@
class ConcurrentActionsTest(unittest.TestCase):
"""Tests acts.utils.run_concurrent_actions and related functions."""
+
@staticmethod
def function_returns_passed_in_arg(arg):
return arg
@@ -314,8 +173,7 @@
def function_raises_passed_in_exception_type(exception_type):
raise exception_type
- def test_run_concurrent_actions_no_raise_returns_proper_return_values(
- self):
+ def test_run_concurrent_actions_no_raise_returns_proper_return_values(self):
"""Tests run_concurrent_actions_no_raise returns in the correct order.
Each function passed into run_concurrent_actions_no_raise returns the
@@ -324,7 +182,8 @@
ret_values = utils.run_concurrent_actions_no_raise(
lambda: self.function_returns_passed_in_arg('ARG1'),
lambda: self.function_returns_passed_in_arg('ARG2'),
- lambda: self.function_returns_passed_in_arg('ARG3'))
+ lambda: self.function_returns_passed_in_arg('ARG3')
+ )
self.assertEqual(len(ret_values), 3)
self.assertEqual(ret_values[0], 'ARG1')
@@ -340,7 +199,8 @@
"""
ret_values = utils.run_concurrent_actions_no_raise(
lambda: self.function_raises_passed_in_exception_type(IndexError),
- lambda: self.function_raises_passed_in_exception_type(KeyError))
+ lambda: self.function_raises_passed_in_exception_type(KeyError)
+ )
self.assertEqual(len(ret_values), 2)
self.assertEqual(ret_values[0].__class__, IndexError)
@@ -356,7 +216,8 @@
ret_values = utils.run_concurrent_actions(
lambda: self.function_returns_passed_in_arg('ARG1'),
lambda: self.function_returns_passed_in_arg('ARG2'),
- lambda: self.function_returns_passed_in_arg('ARG3'))
+ lambda: self.function_returns_passed_in_arg('ARG3')
+ )
self.assertEqual(len(ret_values), 3)
self.assertEqual(ret_values[0], 'ARG1')
@@ -367,43 +228,42 @@
"""Tests run_concurrent_actions raises exceptions from given actions."""
with self.assertRaises(KeyError):
utils.run_concurrent_actions(
- lambda: self.function_returns_passed_in_arg('ARG1'), lambda:
- self.function_raises_passed_in_exception_type(KeyError))
+ lambda: self.function_returns_passed_in_arg('ARG1'),
+ lambda: self.function_raises_passed_in_exception_type(KeyError)
+ )
def test_test_concurrent_actions_raises_non_test_failure(self):
"""Tests test_concurrent_actions raises the given exception."""
with self.assertRaises(KeyError):
utils.test_concurrent_actions(
- lambda: self.function_raises_passed_in_exception_type(KeyError
- ),
- failure_exceptions=signals.TestFailure)
+ lambda: self.function_raises_passed_in_exception_type(KeyError),
+ failure_exceptions=signals.TestFailure
+ )
def test_test_concurrent_actions_raises_test_failure(self):
"""Tests test_concurrent_actions raises the given exception."""
with self.assertRaises(signals.TestFailure):
utils.test_concurrent_actions(
- lambda: self.function_raises_passed_in_exception_type(KeyError
- ),
- failure_exceptions=KeyError)
+ lambda: self.function_raises_passed_in_exception_type(KeyError),
+ failure_exceptions=KeyError
+ )
class SuppressLogOutputTest(unittest.TestCase):
"""Tests SuppressLogOutput"""
+
def test_suppress_log_output(self):
"""Tests that the SuppressLogOutput context manager removes handlers
of the specified levels upon entry and re-adds handlers upon exit.
"""
- handlers = [
- logging.NullHandler(level=lvl)
- for lvl in (logging.DEBUG, logging.INFO, logging.ERROR)
- ]
+ handlers = [logging.NullHandler(level=lvl) for lvl in
+ (logging.DEBUG, logging.INFO, logging.ERROR)]
log = logging.getLogger('test_log')
for handler in handlers:
log.addHandler(handler)
with utils.SuppressLogOutput(log, [logging.INFO, logging.ERROR]):
self.assertTrue(
- any(handler.level == logging.DEBUG
- for handler in log.handlers))
+ any(handler.level == logging.DEBUG for handler in log.handlers))
self.assertFalse(
any(handler.level in (logging.INFO, logging.ERROR)
for handler in log.handlers))
@@ -411,6 +271,7 @@
class IpAddressUtilTest(unittest.TestCase):
+
def test_positive_ipv4_normal_address(self):
ip_address = "192.168.1.123"
self.assertTrue(utils.is_valid_ipv4_address(ip_address))
@@ -455,96 +316,6 @@
ip_address = 'fdsafdsafdsafdsf'
self.assertFalse(utils.is_valid_ipv6_address(ip_address))
- @mock.patch('acts.libs.proc.job.run')
- def test_local_get_interface_ip_addresses_full(self, job_mock):
- job_mock.side_effect = [
- job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
- encoding='utf-8'),
- job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
- encoding='utf-8')
- ]
- self.assertTrue(
- utils.get_interface_ip_addresses(job, 'eno1') ==
- CORRECT_FULL_IP_LIST)
-
- @mock.patch('acts.libs.proc.job.run')
- def test_local_get_interface_ip_addresses_empty(self, job_mock):
- job_mock.side_effect = [
- job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
- encoding='utf-8'),
- job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
- encoding='utf-8')
- ]
- self.assertTrue(
- utils.get_interface_ip_addresses(job, 'wlan1') ==
- CORRECT_EMPTY_IP_LIST)
-
- @mock.patch('acts.controllers.utils_lib.ssh.connection.SshConnection.run')
- def test_ssh_get_interface_ip_addresses_full(self, ssh_mock):
- ssh_mock.side_effect = [
- job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
- encoding='utf-8'),
- job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
- encoding='utf-8')
- ]
- self.assertTrue(
- utils.get_interface_ip_addresses(SshConnection('mock_settings'),
- 'eno1') == CORRECT_FULL_IP_LIST)
-
- @mock.patch('acts.controllers.utils_lib.ssh.connection.SshConnection.run')
- def test_ssh_get_interface_ip_addresses_empty(self, ssh_mock):
- ssh_mock.side_effect = [
- job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
- encoding='utf-8'),
- job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
- encoding='utf-8')
- ]
- self.assertTrue(
- utils.get_interface_ip_addresses(SshConnection('mock_settings'),
- 'wlan1') == CORRECT_EMPTY_IP_LIST)
-
- @mock.patch('acts.controllers.adb.AdbProxy.shell')
- def test_android_get_interface_ip_addresses_full(self, adb_mock):
- adb_mock.side_effect = [MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT]
- self.assertTrue(
- utils.get_interface_ip_addresses(AndroidDevice(), 'eno1') ==
- CORRECT_FULL_IP_LIST)
-
- @mock.patch('acts.controllers.adb.AdbProxy.shell')
- def test_android_get_interface_ip_addresses_empty(self, adb_mock):
- adb_mock.side_effect = [MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT]
- self.assertTrue(
- utils.get_interface_ip_addresses(AndroidDevice(), 'wlan1') ==
- CORRECT_EMPTY_IP_LIST)
-
- @mock.patch(FUCHSIA_INIT_SERVER)
- @mock.patch(FUCHSIA_NETSTACK_LIST_INTERFACES)
- @mock.patch(FUCHSIA_INIT_NETSTACK)
- def test_fuchsia_get_interface_ip_addresses_full(self, init_mock,
- list_interfaces_mock,
- fuchsia_device_mock):
- init_mock.return_value = None
- list_interfaces_mock.return_value = FUCHSIA_INTERFACES
- fuchsia_device_mock.return_value = None
- self.assertTrue(
- utils.get_interface_ip_addresses(
- FuchsiaDevice({'ip': '192.168.1.1'}), 'eno1') ==
- CORRECT_FULL_IP_LIST)
-
- @mock.patch(FUCHSIA_INIT_SERVER)
- @mock.patch(FUCHSIA_NETSTACK_LIST_INTERFACES)
- @mock.patch(FUCHSIA_INIT_NETSTACK)
- def test_fuchsia_get_interface_ip_addresses_empty(self, init_mock,
- list_interfaces_mock,
- fuchsia_device_mock):
- init_mock.return_value = None
- list_interfaces_mock.return_value = FUCHSIA_INTERFACES
- fuchsia_device_mock.return_value = None
- self.assertTrue(
- utils.get_interface_ip_addresses(
- FuchsiaDevice({'ip': '192.168.1.1'}), 'wlan1') ==
- CORRECT_EMPTY_IP_LIST)
-
if __name__ == '__main__':
unittest.main()
diff --git a/acts/framework/tests/test_utils/instrumentation/data/sample_monsoon_data b/acts/framework/tests/test_utils/instrumentation/data/sample_monsoon_data
new file mode 100644
index 0000000..2a70273
--- /dev/null
+++ b/acts/framework/tests/test_utils/instrumentation/data/sample_monsoon_data
@@ -0,0 +1,10 @@
+0s 3.67
+1s 3.69
+2s 0.95
+3s 3.06
+4s 2.17
+5s 1.62
+6s 3.95
+7s 2.47
+8s 1.11
+9s 0.47
diff --git a/acts/framework/tests/test_utils/instrumentation/device/apps/dismiss_dialogs_test.py b/acts/framework/tests/test_utils/instrumentation/device/apps/dismiss_dialogs_test.py
new file mode 100644
index 0000000..3b36274
--- /dev/null
+++ b/acts/framework/tests/test_utils/instrumentation/device/apps/dismiss_dialogs_test.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+import mock
+from acts.test_utils.instrumentation.device.apps.dismiss_dialogs import \
+ DialogDismissalUtil
+
+
+class MockDialogDismissalUtil(DialogDismissalUtil):
+ """Mock DialogDismissalUtil for unit testing"""
+ def __init__(self):
+ self._dut = mock.MagicMock()
+ self._dismiss_dialogs_apk = mock.MagicMock()
+ self._dismiss_dialogs_apk.pkg_name = 'dismiss.dialogs'
+
+
+class DialogDismissalUtilTest(unittest.TestCase):
+ def setUp(self):
+ self._dismiss_dialogs_util = MockDialogDismissalUtil()
+
+ def test_dismiss_dialog_zero_apps(self):
+ """Test that no command is run if the apps arg is empty."""
+ apps = []
+ self._dismiss_dialogs_util.dismiss_dialogs(apps)
+ self._dismiss_dialogs_util._dut.adb.shell.assert_not_called()
+
+ def test_dismiss_dialog_single_app(self):
+ """
+ Test that the correct command is run when a single app is specified.
+ """
+ apps = ['sample.app.one']
+ self._dismiss_dialogs_util.dismiss_dialogs(apps)
+ expected_cmd = (
+ 'am instrument -w -f -e apps sample.app.one '
+ 'dismiss.dialogs/.DismissDialogsInstrumentation '
+ '-e screenshots true -e quitOnError true'
+ )
+ self._dismiss_dialogs_util._dut.adb.shell.assert_called_with(
+ expected_cmd)
+
+ def test_dismiss_dialog_multiple_apps(self):
+ """
+ Test that the correct command is run when multiple apps are specified.
+ """
+ apps = ['sample.app.one', 'sample.app.two']
+ self._dismiss_dialogs_util.dismiss_dialogs(apps)
+ expected_cmd = (
+ 'am instrument -w -f -e apps sample.app.one,sample.app.two '
+ 'dismiss.dialogs/.DismissDialogsInstrumentation '
+ '-e screenshots true -e quitOnError true'
+ )
+ self._dismiss_dialogs_util._dut.adb.shell.assert_called_with(
+ expected_cmd)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts/framework/tests/test_utils/instrumentation/device/apps/hotword_model_extractor_test.py b/acts/framework/tests/test_utils/instrumentation/device/apps/hotword_model_extractor_test.py
new file mode 100644
index 0000000..053da31
--- /dev/null
+++ b/acts/framework/tests/test_utils/instrumentation/device/apps/hotword_model_extractor_test.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import unittest
+
+import mock
+from acts.test_utils.instrumentation.device.apps.hotword_model_extractor import \
+ HotwordModelExtractor
+from acts.test_utils.instrumentation.device.apps.hotword_model_extractor import \
+ MODEL_DIR
+
+GOOD_PACKAGE = 'good_package'
+GOOD_MODEL = 'good_model'
+BAD_PACKAGE = 'bad_package'
+BAD_MODEL = 'bad_model'
+
+
+def mock_pull_from_device(_, hotword_pkg, __):
+ """Mocks the AppInstaller.pull_from_device method."""
+ return mock.MagicMock() if hotword_pkg == GOOD_PACKAGE else None
+
+
+class MockZipFile(object):
+ """Class for mocking zipfile.ZipFile"""
+ def extract(self, path, _):
+ if path == os.path.join(MODEL_DIR, GOOD_MODEL):
+ return path
+ raise KeyError
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *_):
+ pass
+
+
+@mock.patch('acts.test_utils.instrumentation.device.apps.app_installer.'
+ 'AppInstaller.pull_from_device', side_effect=mock_pull_from_device)
+@mock.patch('zipfile.ZipFile', return_value=MockZipFile())
+class HotwordModelExtractorTest(unittest.TestCase):
+ """Unit tests for HotwordModelExtractor."""
+ def setUp(self):
+ self.extractor = HotwordModelExtractor(mock.MagicMock())
+
+ def test_package_not_installed(self, *_):
+ result = self.extractor._extract(BAD_PACKAGE, GOOD_MODEL, '')
+ self.assertIsNone(result)
+
+ def test_voice_model_not_found(self, *_):
+ result = self.extractor._extract(GOOD_PACKAGE, BAD_MODEL, '')
+ self.assertIsNone(result)
+
+ def test_extract_model(self, *_):
+ result = self.extractor._extract(GOOD_PACKAGE, GOOD_MODEL, '')
+ self.assertEqual(result, 'res/raw/good_model')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts/framework/tests/test_utils/instrumentation/device/command/adb_command_types_test.py b/acts/framework/tests/test_utils/instrumentation/device/command/adb_command_types_test.py
index efe9bf0..127575b 100755
--- a/acts/framework/tests/test_utils/instrumentation/device/command/adb_command_types_test.py
+++ b/acts/framework/tests/test_utils/instrumentation/device/command/adb_command_types_test.py
@@ -19,6 +19,8 @@
from acts.test_utils.instrumentation.device.command.adb_command_types import \
DeviceBinaryCommandSeries
from acts.test_utils.instrumentation.device.command.adb_command_types import \
+ DeviceGServices
+from acts.test_utils.instrumentation.device.command.adb_command_types import \
DeviceSetprop
from acts.test_utils.instrumentation.device.command.adb_command_types import \
DeviceSetting
@@ -108,6 +110,19 @@
device_binary_setting.toggle(False),
'settings put system some_other_setting off')
+ def test_device_gservices(self):
+ """Tests that DeviceGServices returns the correct ADB command with
+ set_value.
+ """
+ setting = 'some_gservice'
+ val = 22
+ device_gservices = DeviceGServices(setting)
+ self.assertEqual(
+ device_gservices.set_value(val),
+ 'am broadcast -a '
+ 'com.google.gservices.intent.action.GSERVICES_OVERRIDE '
+ '--ei some_gservice 22')
+
def test_device_binary_command_series(self):
"""Tests that DeviceBinaryCommandSuite returns the correct ADB
commands.
diff --git a/acts/framework/tests/test_utils/instrumentation/power/__init__.py b/acts/framework/tests/test_utils/instrumentation/power/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/tests/test_utils/instrumentation/power/__init__.py
diff --git a/acts/framework/tests/test_utils/instrumentation/power/instrumentation_power_test_test.py b/acts/framework/tests/test_utils/instrumentation/power/instrumentation_power_test_test.py
new file mode 100644
index 0000000..3a617e2
--- /dev/null
+++ b/acts/framework/tests/test_utils/instrumentation/power/instrumentation_power_test_test.py
@@ -0,0 +1,280 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+import mock
+from acts.test_utils.instrumentation.config_wrapper import ConfigWrapper
+from acts.test_utils.instrumentation.power.instrumentation_power_test \
+ import ACCEPTANCE_THRESHOLD
+from acts.test_utils.instrumentation.power.instrumentation_power_test \
+ import InstrumentationPowerTest
+from acts.test_utils.instrumentation.power.power_metrics import PowerMetrics
+
+from acts import signals
+
+
+class MockInstrumentationPowerTest(InstrumentationPowerTest):
+ """Mock test class to initialize required attributes."""
+
+ # avg: 2.214, stdev: 1.358, max: 4.78, min: 0.61
+ SAMPLE_DATA = [1.64, 2.98, 1.72, 3.45, 1.31, 4.78, 3.43, 0.61, 1.19, 1.03]
+
+ def __init__(self):
+ self.log = mock.Mock()
+ self.metric_logger = mock.Mock()
+ self.current_test_name = 'test_case'
+ self._power_metrics = PowerMetrics(4.2)
+ self._power_metrics.test_metrics = {
+ 'instrTest1': PowerMetrics(4.2),
+ 'instrTest2': PowerMetrics(4.2)
+ }
+ self._power_metrics.test_metrics['instrTest1'].generate_test_metrics(
+ list(zip(range(10), self.SAMPLE_DATA))
+ )
+ self._power_metrics.test_metrics['instrTest2'].generate_test_metrics(
+ list(zip(range(10), self.SAMPLE_DATA))
+ )
+ self._instrumentation_config = ConfigWrapper()
+ self._class_config = ConfigWrapper(
+ {
+ self.current_test_name: {
+ ACCEPTANCE_THRESHOLD: {}
+ }
+ }
+ )
+
+ def set_criteria(self, criteria):
+ """Set the acceptance criteria for metrics validation."""
+ test_config = self._class_config[self.current_test_name]
+ test_config[ACCEPTANCE_THRESHOLD] = ConfigWrapper(criteria)
+
+
+class InstrumentationPowerTestTest(unittest.TestCase):
+ """Unit tests for InstrumentationPowerTest."""
+ def setUp(self):
+ self.instrumentation_power_test = MockInstrumentationPowerTest()
+
+ def test_validate_power_results_lower_and_upper_limit_accept(self):
+ """Test that validate_power_results accept passing measurements
+ given a lower and upper limit.
+ """
+ criteria_accept = {
+ 'instrTest1': {
+ 'avg_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'lower_limit': 1.5,
+ 'upper_limit': 2.5
+ },
+ 'max_current': {
+ 'unit_type': 'current',
+ 'unit': 'mA',
+ 'upper_limit': 5000
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_accept)
+ with self.assertRaises(signals.TestPass):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_lower_and_upper_limit_reject(self):
+ """Test that validate_power_results reject failing measurements
+ given a lower and upper limit.
+ """
+ criteria_reject = {
+ 'instrTest1': {
+ 'avg_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'lower_limit': 1.5,
+ 'upper_limit': 2
+ },
+ 'max_current': {
+ 'unit_type': 'current',
+ 'unit': 'mA',
+ 'upper_limit': 4000
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_reject)
+ with self.assertRaises(signals.TestFailure):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_expected_value_and_deviation_accept(self):
+ """Test that validate_power_results accept passing measurements
+ given an expected value and percent deviation.
+ """
+ criteria_accept = {
+ 'instrTest1': {
+ 'stdev_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'expected_value': 1.5,
+ 'percent_deviation': 20
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_accept)
+ with self.assertRaises(signals.TestPass):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_expected_value_and_deviation_reject(self):
+ """Test that validate_power_results reject failing measurements
+ given an expected value and percent deviation.
+ """
+ criteria_reject = {
+ 'instrTest1': {
+ 'min_current': {
+ 'unit_type': 'current',
+ 'unit': 'mA',
+ 'expected_value': 500,
+ 'percent_deviation': 10
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_reject)
+ with self.assertRaises(signals.TestFailure):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_no_such_test(self):
+ """Test that validate_power_results skip validation if there are no
+ criteria matching the specified instrumentation test name.
+ """
+ criteria_wrong_test = {
+ 'instrTest2': {
+ 'min_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'expected_value': 2,
+ 'percent_deviation': 20
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_wrong_test)
+ with self.assertRaises(signals.TestPass):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_no_such_metric(self):
+ """Test that validate_power_results skip validation if the specified
+ metric is invalid.
+ """
+ criteria_invalid_metric = {
+ 'instrTest1': {
+ 'no_such_metric': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'lower_limit': 5,
+ 'upper_limit': 7
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_invalid_metric)
+ with self.assertRaises(signals.TestPass):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_criteria_missing_params(self):
+ """Test that validate_power_results skip validation if the specified
+ metric has missing parameters.
+ """
+ criteria_missing_params = {
+ 'instrTest1': {
+ 'avg_current': {
+ 'unit': 'A',
+ 'lower_limit': 1,
+ 'upper_limit': 2
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_missing_params)
+ with self.assertRaises(signals.TestPass):
+ self.instrumentation_power_test.validate_power_results('instrTest1')
+
+ def test_validate_power_results_pass_if_all_tests_accept(self):
+ """Test that validate_power_results succeeds if it accepts the results
+ of all instrumentation tests.
+ """
+ criteria_multi_test_accept = {
+ 'instrTest1': {
+ 'avg_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'lower_limit': 2
+ },
+ 'stdev_current': {
+ 'unit_type': 'current',
+ 'unit': 'mA',
+ 'expected_value': 1250,
+ 'percent_deviation': 30
+ }
+ },
+ 'instrTest2': {
+ 'max_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'upper_limit': 5
+ },
+ 'avg_power': {
+ 'unit_type': 'power',
+ 'unit': 'W',
+ 'upper_limit': 10
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_multi_test_accept)
+ with self.assertRaises(signals.TestPass):
+ self.instrumentation_power_test.validate_power_results(
+ 'instrTest1', 'instrTest2')
+
+ def test_validate_power_results_fail_if_at_least_one_test_rejects(self):
+ """Test that validate_power_results fails if it rejects the results
+ of at least one instrumentation test.
+ """
+ criteria_multi_test_reject = {
+ 'instrTest1': {
+ 'avg_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'lower_limit': 2
+ },
+ 'stdev_current': {
+ 'unit_type': 'current',
+ 'unit': 'mA',
+ 'expected_value': 1250,
+ 'percent_deviation': 30
+ }
+ },
+ 'instrTest2': {
+ 'max_current': {
+ 'unit_type': 'current',
+ 'unit': 'A',
+ 'upper_limit': 5
+ },
+ 'avg_power': {
+ 'unit_type': 'power',
+ 'unit': 'W',
+ 'upper_limit': 8
+ }
+ }
+ }
+ self.instrumentation_power_test.set_criteria(criteria_multi_test_reject)
+ with self.assertRaises(signals.TestFailure):
+ self.instrumentation_power_test.validate_power_results(
+ 'instrTest1', 'instrTest2')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts/framework/tests/test_utils/instrumentation/power/power_metrics_test.py b/acts/framework/tests/test_utils/instrumentation/power/power_metrics_test.py
new file mode 100644
index 0000000..38441d1
--- /dev/null
+++ b/acts/framework/tests/test_utils/instrumentation/power/power_metrics_test.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import statistics
+import unittest
+
+from acts.test_utils.instrumentation import instrumentation_proto_parser \
+ as parser
+from acts.test_utils.instrumentation.power.power_metrics import CURRENT
+from acts.test_utils.instrumentation.power.power_metrics import HOUR
+from acts.test_utils.instrumentation.power.power_metrics import MILLIAMP
+from acts.test_utils.instrumentation.power.power_metrics import MINUTE
+from acts.test_utils.instrumentation.power.power_metrics import Measurement
+from acts.test_utils.instrumentation.power.power_metrics import PowerMetrics
+from acts.test_utils.instrumentation.power.power_metrics import TIME
+from acts.test_utils.instrumentation.power.power_metrics import WATT
+
+FAKE_UNIT_TYPE = 'fake_unit'
+FAKE_UNIT = 'F'
+
+
+class MeasurementTest(unittest.TestCase):
+ """Unit tests for the Measurement class."""
+
+ def test_init_with_valid_unit_type(self):
+ """Test that a Measurement is properly initialized given a valid unit
+ type.
+ """
+ measurement = Measurement(2, CURRENT, MILLIAMP)
+ self.assertEqual(measurement.value, 2)
+ self.assertEqual(measurement._unit, MILLIAMP)
+
+ def test_init_with_invalid_unit_type(self):
+ """Test that __init__ raises an error if given an invalid unit type."""
+ with self.assertRaisesRegex(TypeError, 'valid unit type'):
+ measurement = Measurement(2, FAKE_UNIT_TYPE, FAKE_UNIT)
+
+ def test_unit_conversion(self):
+ """Test that to_unit correctly converts value and unit."""
+ ratio = 1000
+ current_amps = Measurement.amps(15)
+ current_milliamps = current_amps.to_unit(MILLIAMP)
+ self.assertEqual(current_milliamps.value / current_amps.value, ratio)
+
+ def test_unit_conversion_with_wrong_type(self):
+ """Test that to_unit raises and error if incompatible unit type is
+ specified.
+ """
+ current_amps = Measurement.amps(3.4)
+ with self.assertRaisesRegex(TypeError, 'Incompatible units'):
+ power_watts = current_amps.to_unit(WATT)
+
+ def test_comparison_operators(self):
+ """Test that the comparison operators work as intended."""
+ # time_a == time_b < time_c
+ time_a = Measurement.seconds(120)
+ time_b = Measurement(2, TIME, MINUTE)
+ time_c = Measurement(0.1, TIME, HOUR)
+
+ self.assertEqual(time_a, time_b)
+ self.assertEqual(time_b, time_a)
+ self.assertLessEqual(time_a, time_b)
+ self.assertGreaterEqual(time_a, time_b)
+
+ self.assertNotEqual(time_a, time_c)
+ self.assertNotEqual(time_c, time_a)
+ self.assertLess(time_a, time_c)
+ self.assertLessEqual(time_a, time_c)
+ self.assertGreater(time_c, time_a)
+ self.assertGreaterEqual(time_c, time_a)
+
+ def test_arithmetic_operators(self):
+ """Test that the addition and subtraction operators work as intended"""
+ time_a = Measurement(3, TIME, HOUR)
+ time_b = Measurement(90, TIME, MINUTE)
+
+ sum_ = time_a + time_b
+ self.assertEqual(sum_.value, 4.5)
+ self.assertEqual(sum_._unit, HOUR)
+
+ sum_reversed = time_b + time_a
+ self.assertEqual(sum_reversed.value, 270)
+ self.assertEqual(sum_reversed._unit, MINUTE)
+
+ diff = time_a - time_b
+ self.assertEqual(diff.value, 1.5)
+ self.assertEqual(diff._unit, HOUR)
+
+ diff_reversed = time_b - time_a
+ self.assertEqual(diff_reversed.value, -90)
+ self.assertEqual(diff_reversed._unit, MINUTE)
+
+
+class PowerMetricsTest(unittest.TestCase):
+ """Unit tests for the PowerMetrics class."""
+
+ SAMPLES = [0.13, 0.95, 0.32, 4.84, 2.48, 4.11, 4.85, 4.88, 4.22, 2.2]
+ RAW_DATA = list(zip(range(10), SAMPLES))
+ VOLTAGE = 4.2
+ START_TIME = 5
+
+ def setUp(self):
+ self.power_metrics = PowerMetrics(self.VOLTAGE, self.START_TIME)
+
+ def test_import_raw_data(self):
+ """Test that power metrics can be loaded from file. Simply ensure that
+ the number of samples is correct."""
+
+ imported_data = PowerMetrics.import_raw_data(
+ os.path.join(os.path.dirname(__file__),
+ '../data/sample_monsoon_data')
+ )
+ self.power_metrics.generate_test_metrics(imported_data)
+ self.assertEqual(self.power_metrics._num_samples, 10)
+
+ def test_split_by_test_with_timestamps(self):
+ """Test that given test timestamps, a power metric is generated from
+ a subset of samples corresponding to the test."""
+ sample_test = 'sample_test'
+ test_start = 8500
+ test_end = 13500
+ test_timestamps = {sample_test: {parser.START_TIMESTAMP: test_start,
+ parser.END_TIMESTAMP: test_end}}
+ self.power_metrics.generate_test_metrics(self.RAW_DATA, test_timestamps)
+ test_metrics = self.power_metrics.test_metrics[sample_test]
+ self.assertEqual(test_metrics._num_samples, 5)
+
+ def test_numeric_metrics(self):
+ """Test that the numeric metrics have correct values."""
+ self.power_metrics.generate_test_metrics(self.RAW_DATA)
+ self.assertAlmostEqual(self.power_metrics.avg_current.value,
+ statistics.mean(self.SAMPLES) * 1000)
+ self.assertAlmostEqual(self.power_metrics.max_current.value,
+ max(self.SAMPLES) * 1000)
+ self.assertAlmostEqual(self.power_metrics.min_current.value,
+ min(self.SAMPLES) * 1000)
+ self.assertAlmostEqual(self.power_metrics.stdev_current.value,
+ statistics.stdev(self.SAMPLES) * 1000)
+ self.assertAlmostEqual(
+ self.power_metrics.avg_power.value,
+ self.power_metrics.avg_current.value * self.VOLTAGE)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
index 2af4c05..c38fc93 100644
--- a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
@@ -43,22 +43,21 @@
class ConcurrentBleAdvertisementDiscoveryTest(BluetoothBaseTest):
default_timeout = 10
+ droid_list = []
max_advertisements = -1
advertise_callback_list = []
def setup_class(self):
super().setup_class()
- self.droid_list = get_advanced_droid_list(self.android_devices)
self.scn_ad = self.android_devices[0]
self.adv_ad = self.android_devices[1]
+ self.droid_list = get_advanced_droid_list(self.android_devices)
self.max_advertisements = self.droid_list[1]['max_advertisements']
def setup_test(self):
- return reset_bluetooth(self.android_devices)
-
- def setup_test(self):
- super(BluetoothBaseTest, self).setup_test()
+ super().setup_test()
self.log.info("Setting up advertisements")
+ reset_bluetooth(self.android_devices)
try:
self.advertise_callback_list = setup_n_advertisements(
self.adv_ad, self.max_advertisements)
diff --git a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
index 94ee0f7..09c6cd3 100644
--- a/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
+++ b/acts/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
@@ -43,17 +43,18 @@
class ConcurrentBleAdvertisingTest(BluetoothBaseTest):
default_timeout = 10
+ droid_list = []
max_advertisements = -1
def setup_class(self):
super().setup_class()
- self.droid_list = get_advanced_droid_list(self.android_devices)
self.scn_ad = self.android_devices[0]
self.adv_ad = self.android_devices[1]
+ self.droid_list = get_advanced_droid_list(self.android_devices)
self.max_advertisements = self.droid_list[1]['max_advertisements']
def setup_test(self):
- super(BluetoothBaseTest, self).setup_test()
+ super().setup_test()
return reset_bluetooth(self.android_devices)
def _verify_n_advertisements(self, num_advertisements):
diff --git a/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py b/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py
index 97ace9b..166b848 100644
--- a/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCocTest.py
@@ -49,14 +49,14 @@
def setup_class(self):
super().setup_class()
self.client_ad = self.android_devices[0]
- # The client which is scanning will need location to be enabled in order to
- # start scan and get scan results.
- utils.set_location_service(self.client_ad, True)
self.server_ad = self.android_devices[1]
# Note that some tests required a third device.
if len(self.android_devices) > 2:
self.server2_ad = self.android_devices[2]
+ # The client which is scanning will need location to be enabled in order to
+ # start scan and get scan results.
+ utils.set_location_service(self.client_ad, True)
return setup_multiple_devices_for_bt_test(self.android_devices)
def teardown_test(self):
diff --git a/acts/tests/google/ble/system_tests/BleStressTest.py b/acts/tests/google/ble/system_tests/BleStressTest.py
index f97907f..afbb67a 100644
--- a/acts/tests/google/ble/system_tests/BleStressTest.py
+++ b/acts/tests/google/ble/system_tests/BleStressTest.py
@@ -37,6 +37,7 @@
class BleStressTest(BluetoothBaseTest):
default_timeout = 10
PAIRING_TIMEOUT = 20
+ droid_list = []
def setup_class(self):
super().setup_class()
@@ -45,7 +46,7 @@
self.adv_ad = self.android_devices[1]
def teardown_test(self):
- super(BluetoothBaseTest, self).teardown_test()
+ super().teardown_test()
self.log_stats()
def bleadvertise_verify_onsuccess_handler(self, event):
diff --git a/acts/tests/google/experimental/BluetoothLatencyTest.py b/acts/tests/google/experimental/BluetoothLatencyTest.py
index 811a41c..ea6ed4a 100644
--- a/acts/tests/google/experimental/BluetoothLatencyTest.py
+++ b/acts/tests/google/experimental/BluetoothLatencyTest.py
@@ -21,6 +21,7 @@
from acts import asserts
from acts.base_test import BaseTestClass
from acts.signals import TestPass
+from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
@@ -112,6 +113,7 @@
return (end_time - start_time) * 1000
@BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='7748295d-204e-4ad0-adf5-7591380b940a')
def test_bluetooth_latency(self):
"""Tests the latency for a data transfer over RFCOMM"""
diff --git a/acts/tests/google/experimental/BluetoothPairAndConnectTest.py b/acts/tests/google/experimental/BluetoothPairAndConnectTest.py
index e54e4e7..2402fb5 100644
--- a/acts/tests/google/experimental/BluetoothPairAndConnectTest.py
+++ b/acts/tests/google/experimental/BluetoothPairAndConnectTest.py
@@ -27,8 +27,11 @@
from acts.controllers.buds_lib.test_actions.apollo_acts import ApolloTestActions
from acts.signals import TestFailure
from acts.signals import TestPass
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_test_utils import factory_reset_bluetooth
from acts.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
from acts.utils import set_location_service
@@ -74,10 +77,10 @@
self.bt_logger = BluetoothMetricLogger.for_test_case()
def setup_test(self):
+ setup_multiple_devices_for_bt_test(self.android_devices)
# Make sure Bluetooth is on
enable_bluetooth(self.phone.droid, self.phone.ed)
set_location_service(self.phone, True)
- factory_reset_bluetooth([self.phone])
self.apollo_act.factory_reset()
self.log.info('===== START BLUETOOTH PAIR AND CONNECT TEST =====')
@@ -116,6 +119,8 @@
return pair_time, connection_time
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='c914fd08-350d-465a-96cf-970d40e71060')
def test_bluetooth_connect(self):
# Store metrics
metrics = {}
diff --git a/acts/tests/google/experimental/BluetoothReconnectTest.py b/acts/tests/google/experimental/BluetoothReconnectTest.py
index a03ec7b..5339e51 100644
--- a/acts/tests/google/experimental/BluetoothReconnectTest.py
+++ b/acts/tests/google/experimental/BluetoothReconnectTest.py
@@ -22,9 +22,12 @@
from acts import asserts
from acts.base_test import BaseTestClass
from acts.controllers.buds_lib.test_actions.apollo_acts import ApolloTestActions
-from acts.signals import TestPass, TestFailure
+from acts.signals import TestFailure
+from acts.signals import TestPass
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_test_utils import enable_bluetooth
-from acts.test_utils.bt.bt_test_utils import factory_reset_bluetooth
+from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger
from acts.utils import set_location_service
@@ -67,10 +70,10 @@
self.bt_logger = BluetoothMetricLogger.for_test_case()
def setup_test(self):
+ setup_multiple_devices_for_bt_test(self.android_devices)
# Make sure Bluetooth is on
enable_bluetooth(self.phone.droid, self.phone.ed)
set_location_service(self.phone, True)
- factory_reset_bluetooth([self.phone])
self.apollo_act.factory_reset()
# Initial pairing and connection of devices
@@ -119,6 +122,8 @@
end_time = time.perf_counter()
return (end_time - start_time) * 1000
+ @BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='da921903-92d0-471d-ae01-456058cc1297')
def test_bluetooth_reconnect(self):
"""Reconnects Bluetooth between a phone and Apollo device a specified
number of times and reports connection time statistics."""
diff --git a/acts/tests/google/experimental/BluetoothThroughputTest.py b/acts/tests/google/experimental/BluetoothThroughputTest.py
index 3403ded..1f53172 100644
--- a/acts/tests/google/experimental/BluetoothThroughputTest.py
+++ b/acts/tests/google/experimental/BluetoothThroughputTest.py
@@ -18,6 +18,7 @@
from acts import asserts
from acts.base_test import BaseTestClass
from acts.signals import TestPass
+from acts.test_decorators import test_tracker_info
from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
@@ -109,6 +110,7 @@
return throughput
@BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='23afba5b-5801-42c2-8d7a-41510e91a605')
def test_bluetooth_throughput_large_buffer(self):
"""Tests the throughput over a series of data transfers with large
buffer size.
@@ -143,6 +145,7 @@
extras=proto)
@BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='5472fe33-891e-4fa1-ba84-78fc6f6a2327')
def test_bluetooth_throughput_medium_buffer(self):
"""Tests the throughput over a series of data transfers with medium
buffer size.
@@ -177,6 +180,7 @@
extras=proto)
@BluetoothBaseTest.bt_test_wrap
+ @test_tracker_info(uuid='97589280-cefa-4ae4-b3fd-94ec9c1f4104')
def test_bluetooth_throughput_small_buffer(self):
"""Tests the throughput over a series of data transfers with small
buffer size.
diff --git a/acts/tests/google/instrumentation/power/camera/CapturePhotosTest.py b/acts/tests/google/instrumentation/power/camera/CapturePhotosTest.py
new file mode 100644
index 0000000..e6f2626
--- /dev/null
+++ b/acts/tests/google/instrumentation/power/camera/CapturePhotosTest.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.test_utils.instrumentation.power import instrumentation_power_test
+from acts.test_utils.instrumentation.device.apps.dismiss_dialogs import \
+ DialogDismissalUtil
+
+
+class ImageCaptureTest(instrumentation_power_test.InstrumentationPowerTest):
+ """
+ Test class for running instrumentation test CameraTests#testImageCapture.
+ """
+
+ def _prepare_device(self):
+ super()._prepare_device()
+ self.mode_airplane()
+ self.base_device_configuration()
+ self._dialog_util = DialogDismissalUtil(
+ self.ad_dut,
+ self._instrumentation_config.get_file('dismiss_dialogs_apk')
+ )
+ self._dialog_util.dismiss_dialogs('GoogleCamera')
+
+ def _cleanup_device(self):
+ self._dialog_util.close()
+ super()._cleanup_device()
+
+ def test_capture_photos(self):
+ """Measures power during photo capture."""
+ self.run_and_measure(
+ 'com.google.android.platform.powertests.CameraTests',
+ 'testImageCapture', req_params=['hdr_mode'])
+ self.validate_power_results()
diff --git a/acts/tests/google/instrumentation/power/idle/DisplayAlwaysOnTest.py b/acts/tests/google/instrumentation/power/idle/DisplayAlwaysOnTest.py
new file mode 100644
index 0000000..b891b0c
--- /dev/null
+++ b/acts/tests/google/instrumentation/power/idle/DisplayAlwaysOnTest.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.test_utils.instrumentation.power import instrumentation_power_test
+from acts.test_utils.instrumentation.device.command.adb_commands import common
+
+
+class DisplayAlwaysOnTest(instrumentation_power_test.InstrumentationPowerTest):
+ """Test class for running instrumentation test DisplayAlwaysOn."""
+
+ def _prepare_device(self):
+ super()._prepare_device()
+ self.base_device_configuration()
+ self.adb_run(common.doze_mode.toggle(True))
+ self.adb_run(common.doze_always_on.toggle(True))
+ self.adb_run(common.disable_sensors)
+
+ def test_display_always_on(self):
+ """Measures power when the device is rock bottom state plus display
+ always on (known as doze mode)."""
+
+ self.run_and_measure(
+ 'com.google.android.platform.powertests.IdleTestCase',
+ 'testIdleScreenOff')
+
+ self.validate_power_results()
diff --git a/acts/tests/google/instrumentation/power/idle/PartialWakeLockTest.py b/acts/tests/google/instrumentation/power/idle/PartialWakeLockTest.py
new file mode 100644
index 0000000..973c0f9
--- /dev/null
+++ b/acts/tests/google/instrumentation/power/idle/PartialWakeLockTest.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.test_utils.instrumentation.power import instrumentation_power_test
+
+
+class PartialWakeLockTest(instrumentation_power_test.InstrumentationPowerTest):
+ """Test class for running instrumentation test PartialWakeLock."""
+
+ def _prepare_device(self):
+ super()._prepare_device()
+ self.base_device_configuration()
+
+ def test_partial_wake_lock(self):
+ """Measures power when the device is idle with a partial wake lock."""
+ self.run_and_measure(
+ 'com.google.android.platform.powertests.IdleTestCase',
+ 'testPartialWakelock')
+ self.validate_power_results()
diff --git a/acts/tests/google/instrumentation/power/idle/RockBottomTest.py b/acts/tests/google/instrumentation/power/idle/RockBottomTest.py
new file mode 100644
index 0000000..43dd9f1
--- /dev/null
+++ b/acts/tests/google/instrumentation/power/idle/RockBottomTest.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.test_utils.instrumentation.power import instrumentation_power_test
+
+
+class RockBottomTest(instrumentation_power_test.InstrumentationPowerTest):
+ """Test class for running instrumentation test RockBottom."""
+
+ def _prepare_device(self):
+ super()._prepare_device()
+ self.base_device_configuration()
+
+ def test_rock_bottom(self):
+ """Measures power when the device is in a rock bottom state."""
+ self.run_and_measure(
+ 'com.google.android.platform.powertests.IdleTestCase',
+ 'testIdleScreenOff')
+ self.validate_power_results()
diff --git a/acts/tests/google/instrumentation/power/media/VideoPlaybackTest.py b/acts/tests/google/instrumentation/power/media/VideoPlaybackTest.py
new file mode 100644
index 0000000..f4364f2
--- /dev/null
+++ b/acts/tests/google/instrumentation/power/media/VideoPlaybackTest.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.test_utils.instrumentation.power import instrumentation_power_test
+
+BIG_FILE_PUSH_TIMEOUT = 600
+
+
+class VideoPlaybackTest(
+ instrumentation_power_test.InstrumentationPowerTest):
+ """Test class for running instrumentation tests
+ VideoPlaybackHighBitRateTest."""
+
+ def _prepare_device(self):
+ super()._prepare_device()
+ self.base_device_configuration()
+
+ def test_playback_high_bit_rate(self):
+ """Measures power when the device is in a rock bottom state."""
+ video_location = self.push_to_external_storage(
+ self._instrumentation_config.get_file('high_bit_rate_video'),
+ timeout=BIG_FILE_PUSH_TIMEOUT)
+ self.trigger_scan_on_external_storage()
+
+ self.run_and_measure(
+ 'com.google.android.platform.powertests.PhotosTests',
+ 'testVideoPlaybackThroughIntent',
+ extra_params=[('video_file_path', video_location)])
+
+ self.validate_power_results()
diff --git a/acts/tests/google/wifi/WifiAutoUpdateTest.py b/acts/tests/google/wifi/WifiAutoUpdateTest.py
index 33369a2..400ffe8 100755
--- a/acts/tests/google/wifi/WifiAutoUpdateTest.py
+++ b/acts/tests/google/wifi/WifiAutoUpdateTest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+# !/usr/bin/env python3.4
#
# Copyright 2017 - The Android Open Source Project
#
@@ -14,22 +14,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import itertools
-import pprint
-import queue
-import time
-
-import acts.base_test
-import acts.signals as signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
-import acts.utils
-
+import re
from acts import asserts
+from acts.controllers.android_device import SL4A_APK_NAME
from acts.libs.ota import ota_updater
+import acts.signals as signals
from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+import acts.test_utils.wifi.wifi_test_utils as wutils
from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+import acts.utils as utils
WifiEnums = wutils.WifiEnums
+SSID = WifiEnums.SSID_KEY
+PWD = WifiEnums.PWD_KEY
+NETID = WifiEnums.NETID_KEY
# Default timeout used for reboot, toggle WiFi and Airplane mode,
# for the system to settle down after the operation.
DEFAULT_TIMEOUT = 10
@@ -51,50 +50,52 @@
self.tests = (
"test_check_wifi_state_after_au",
"test_verify_networks_after_au",
+ "test_configstore_after_au",
+ "test_mac_randomization_after_au",
+ "test_wifi_hotspot_5g_psk_after_au",
"test_all_networks_connectable_after_au",
- "test_connection_to_new_networks",
+ "test_connect_to_network_suggestion_after_au",
"test_check_wifi_toggling_after_au",
+ "test_connection_to_new_networks",
"test_reset_wifi_after_au")
def setup_class(self):
super(WifiAutoUpdateTest, self).setup_class()
ota_updater.initialize(self.user_params, self.android_devices)
self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
wutils.wifi_test_device_init(self.dut)
- req_params = []
- opt_param = [
- "open_network", "reference_networks", "iperf_server_address"
- ]
- self.unpack_userparams(
- req_param_names=req_params, opt_param_names=opt_param)
-
- if "AccessPoint" in self.user_params:
- self.legacy_configure_ap_and_start()
-
- asserts.assert_true(
- len(self.reference_networks) > 0,
- "Need at least two reference network with psk.")
- asserts.assert_true(
- len(self.open_network) > 0,
- "Need at least two open network with psk.")
wutils.wifi_toggle_state(self.dut, True)
+ # configure APs
+ self.legacy_configure_ap_and_start(wpa_network=True)
+ self.wpapsk_2g = self.reference_networks[0]["2g"]
+ self.wpapsk_5g = self.reference_networks[0]["5g"]
+ self.open_2g = self.open_network[0]["2g"]
+ self.open_5g = self.open_network[0]["5g"]
+
+ # saved & connected networks, network suggestions
+ # and new networks
+ self.saved_networks = [self.open_2g]
+ self.network_suggestions = [self.wpapsk_5g]
+ self.connected_networks = [self.wpapsk_2g, self.open_5g]
+ self.new_networks = [self.reference_networks[1]["2g"],
+ self.open_network[1]["5g"]]
+
+ # add pre ota upgrade configuration
self.wifi_config_list = []
-
- # Disabling WiFi setup before OTA for debugging.
- # Setup WiFi and add few open and wpa networks before OTA.
- # self.add_network_and_enable(self.open_network[0]['2g'])
- # self.add_network_and_enable(self.reference_networks[0]['5g'])
-
- # Add few dummy networks to the list.
- # self.add_and_enable_dummy_networks()
+ self.pre_default_mac = {}
+ self.pre_random_mac = {}
+ self.pst_default_mac = {}
+ self.pst_random_mac = {}
+ self.add_pre_update_configuration()
# Run OTA below, if ota fails then abort all tests.
try:
ota_updater.update(self.dut)
- except Exception as err:
+ except Exception as e:
raise signals.TestAbortClass(
- "Failed up apply OTA update. Aborting tests")
+ "Failed up apply OTA update. Aborting tests: %s" % e)
def setup_test(self):
self.dut.droid.wakeLockAcquireBright()
@@ -113,45 +114,111 @@
del self.user_params["reference_networks"]
del self.user_params["open_network"]
- """Helper Functions"""
+ ### Helper Methods
+
+ def add_pre_update_configuration(self):
+ self.add_network_suggestions(self.network_suggestions)
+ self.add_network_and_enable(self.saved_networks[0])
+ self.add_wifi_hotspot()
+ self.connect_to_multiple_networks(self.connected_networks)
+
+ def add_wifi_hotspot(self):
+ self.wifi_hotspot = {"SSID": "hotspot_%s" % utils.rand_ascii_str(6),
+ "password": "pass_%s" % utils.rand_ascii_str(6)}
+ wutils.save_wifi_soft_ap_config(self.dut,
+ self.wifi_hotspot,
+ WIFI_CONFIG_APBAND_5G)
+
+ def verify_wifi_hotspot(self):
+ """Verify wifi tethering."""
+ wutils.start_wifi_tethering_saved_config(self.dut)
+ wutils.connect_to_wifi_network(self.dut_client,
+ self.wifi_hotspot,
+ check_connectivity=False)
+ wutils.stop_wifi_tethering(self.dut)
+
+ def connect_to_multiple_networks(self, networks):
+ """Connect to a list of wifi networks.
+
+ Args:
+ networks : list of wifi networks.
+ """
+ self.log.info("Connect to multiple wifi networks")
+ for network in networks:
+ ssid = network[SSID]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, ssid)
+ wutils.wifi_connect(self.dut, network, num_of_tries=6)
+ self.wifi_config_list.append(network)
+ self.pre_default_mac[network[SSID]] = self.get_sta_mac_address()
+ self.pre_random_mac[network[SSID]] = \
+ self.dut.droid.wifigetRandomizedMacAddress(network)
+
+ def get_sta_mac_address(self):
+ """Gets the current MAC address being used for client mode."""
+ out = self.dut.adb.shell("ifconfig wlan0")
+ res = re.match(".* HWaddr (\S+).*", out, re.S)
+ return res.group(1)
+
+ def add_network_suggestions(self, network_suggestions):
+ """Add wifi network suggestions to DUT.
+
+ Args:
+ network_suggestions : suggestions to add.
+ """
+ self.dut.log.info("Adding network suggestions")
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions(network_suggestions),
+ "Failed to add suggestions")
+
+ # Enable suggestions by the app.
+ self.dut.log.debug("Enabling suggestions from test")
+ self.dut.adb.shell(
+ "cmd wifi network-suggestions-set-user-approved %s yes" % \
+ SL4A_APK_NAME)
+
+ def remove_suggestions_and_ensure_no_connection(self,
+ network_suggestions,
+ expected_ssid):
+ """Remove network suggestions.
+
+ Args:
+ network_suggestions : suggestions to remove.
+ expected_ssid : SSID to verify that DUT is not connected.
+ """
+ # remove network suggestion and verify disconnect
+ self.dut.log.info("Removing network suggestions")
+ asserts.assert_true(
+ self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions),
+ "Failed to remove suggestions")
+
+ wutils.wait_for_disconnect(self.dut)
+ self.dut.ed.clear_all_events()
+
+ # Now ensure that we didn't connect back.
+ asserts.assert_false(
+ wutils.wait_for_connect(self.dut,
+ expected_ssid,
+ assert_on_fail=False),
+ "Device should not connect back")
def add_network_and_enable(self, network):
"""Add a network and enable it.
Args:
network : Network details for the network to be added.
-
"""
+ self.log.info("Add a wifi network and enable it")
ret = self.dut.droid.wifiAddNetwork(network)
asserts.assert_true(ret != -1, "Add network %r failed" % network)
- self.wifi_config_list.append({
- WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY],
- WifiEnums.NETID_KEY: ret})
+ self.wifi_config_list.append({SSID: network[SSID], NETID: ret})
self.dut.droid.wifiEnableNetwork(ret, 0)
- def add_and_enable_dummy_networks(self, num_networks=5):
- """Add some dummy networks to the device and enable them.
-
- Args:
- num_networks: Number of networks to add.
- """
- ssid_name_base = "dummy_network_"
- for i in range(0, num_networks):
- network = {}
- network[WifiEnums.SSID_KEY] = ssid_name_base + str(i)
- network[WifiEnums.PWD_KEY] = "dummynet_password"
- self.add_network_and_enable(network)
-
def check_networks_after_autoupdate(self, networks):
- """Verify that all previously configured networks are presistent after
- reboot.
+ """Verify that all previously configured networks are persistent.
Args:
networks: List of network dicts.
-
- Return:
- None. Raises TestFailure.
-
"""
network_info = self.dut.droid.wifiGetConfiguredNetworks()
if len(network_info) != len(networks):
@@ -160,21 +227,35 @@
"don't match. \nBefore reboot = %s \n After reboot = %s" %
(networks, network_info))
raise signals.TestFailure(msg)
- current_count = 0
+
# For each network, check if it exists in configured list after Auto-
# update.
for network in networks:
- exists = wutils.match_networks({
- WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]
- }, network_info)
- if not len(exists):
+ exists = wutils.match_networks({SSID: network[SSID]}, network_info)
+ if not exists:
raise signals.TestFailure("%s network is not present in the"
" configured list after Auto-update" %
- network[WifiEnums.SSID_KEY])
+ network[SSID])
# Get the new network id for each network after reboot.
- network[WifiEnums.NETID_KEY] = exists[0]['networkId']
+ network[NETID] = exists[0]["networkId"]
- """Tests"""
+ def get_enabled_network(self, network1, network2):
+ """Check network status and return currently unconnected network.
+
+ Args:
+ network1: dict representing a network.
+ network2: dict representing a network.
+
+ Returns:
+ Network dict of the unconnected network.
+ """
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ enabled = network1
+ if wifi_info[SSID] == network1[SSID]:
+ enabled = network2
+ return enabled
+
+ ### Tests
@test_tracker_info(uuid="9ff1f01e-e5ff-408b-9a95-29e87a2df2d8")
def test_check_wifi_state_after_au(self):
@@ -192,6 +273,78 @@
"""
self.check_networks_after_autoupdate(self.wifi_config_list)
+ @test_tracker_info(uuid="799e83c2-305d-4510-921e-dac3c0dbb6c5")
+ def test_configstore_after_au(self):
+ """Verify DUT automatically connects to wifi networks after ota.
+
+ Steps:
+ 1. Connect to two wifi networks pre ota.
+ 2. Verify DUT automatically connects to 1 after ota.
+ 3. Re-connect to the other wifi network.
+ """
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ self.pst_default_mac[wifi_info[SSID]] = self.get_sta_mac_address()
+ self.pst_random_mac[wifi_info[SSID]] = \
+ self.dut.droid.wifigetRandomizedMacAddress(wifi_info)
+ reconnect_to = self.get_enabled_network(self.wifi_config_list[1],
+ self.wifi_config_list[2])
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, reconnect_to[SSID])
+ wutils.wifi_connect_by_id(self.dut, reconnect_to[NETID])
+ connect_data = self.dut.droid.wifiGetConnectionInfo()
+ connect_ssid = connect_data[SSID]
+ self.log.info("Expected SSID = %s" % reconnect_to[SSID])
+ self.log.info("Connected SSID = %s" % connect_ssid)
+ if connect_ssid != reconnect_to[SSID]:
+ raise signals.TestFailure(
+ "Device failed to reconnect to the correct"
+ " network after reboot.")
+ self.pst_default_mac[wifi_info[SSID]] = self.get_sta_mac_address()
+ self.pst_random_mac[wifi_info[SSID]] = \
+ self.dut.droid.wifigetRandomizedMacAddress(wifi_info)
+
+ for network in self.connected_networks:
+ wutils.wifi_forget_network(self.dut, network[SSID])
+
+ @test_tracker_info(uuid="e26d0ed9-9457-4a95-a962-4d43b0032bac")
+ def test_mac_randomization_after_au(self):
+ """Verify randomized MAC addrs are persistent after ota.
+
+ Steps:
+ 1. Reconnect to the wifi networks configured pre ota.
+ 2. Get the randomized MAC addrs.
+ """
+ for ssid, mac in self.pst_random_mac.items():
+ asserts.assert_true(
+ self.pre_random_mac[ssid] == mac,
+ "MAC addr of %s is %s after ota. Expected %s" %
+ (ssid, mac, self.pre_random_mac[ssid]))
+
+ @test_tracker_info(uuid="f68a65e6-97b7-4746-bad8-4c206551d87e")
+ def test_wifi_hotspot_5g_psk_after_au(self):
+ """Verify hotspot after ota upgrade.
+
+ Steps:
+ 1. Start wifi hotspot on the saved config.
+ 2. Verify DUT client connects to it.
+ """
+ self.verify_wifi_hotspot()
+
+ @test_tracker_info(uuid="21f91372-88a6-44b9-a4e8-d4664823dffb")
+ def test_connect_to_network_suggestion_after_au(self):
+ """Verify connection to network suggestion after ota.
+
+ Steps:
+ 1. DUT has network suggestion added before OTA.
+ 2. Wait for the device to connect to it.
+ 3. Remove the suggestions and ensure the device does not
+ connect back.
+ """
+ wutils.start_wifi_connection_scan_and_return_status(self.dut)
+ wutils.wait_for_connect(self.dut, self.network_suggestions[0][SSID])
+ self.remove_suggestions_and_ensure_no_connection(
+ self.network_suggestions, self.network_suggestions[0][SSID])
+
@test_tracker_info(uuid="b8e47a4f-62fe-4a0e-b999-27ae1ebf4d19")
def test_connection_to_new_networks(self):
"""Check if we can connect to new networks after Auto-update.
@@ -201,14 +354,11 @@
2. Connect to an open network.
3. Forget ntworks added in 1 & 2.
TODO: (@bmahadev) Add WEP network once it's ready.
-
"""
- wutils.connect_to_wifi_network(self.dut, self.open_network[0]['5g'])
- wutils.connect_to_wifi_network(self.dut, self.reference_networks[0]['2g'])
- wutils.wifi_forget_network(self.dut,
- self.reference_networks[0]['2g'][WifiEnums.SSID_KEY])
- wutils.wifi_forget_network(self.dut,
- self.open_network[0]['5g'][WifiEnums.SSID_KEY])
+ for network in self.new_networks:
+ wutils.connect_to_wifi_network(self.dut, network)
+ for network in self.new_networks:
+ wutils.wifi_forget_network(self.dut, network[SSID])
@test_tracker_info(uuid="1d8309e4-d5a2-4f48-ba3b-895a58c9bf3a")
def test_all_networks_connectable_after_au(self):
@@ -218,15 +368,14 @@
1. Connect to previously added PSK network using network id.
2. Connect to previously added open network using network id.
TODO: (@bmahadev) Add WEP network once it's ready.
-
"""
- for network in self.wifi_config_list:
- if 'dummy' not in network[WifiEnums.SSID_KEY]:
- if not wutils.connect_to_wifi_network_with_id(self.dut,
- network[WifiEnums.NETID_KEY],
- network[WifiEnums.SSID_KEY]):
- raise signals.TestFailure("Failed to connect to %s after \
- Auto-update" % network[WifiEnums.SSID_KEY])
+ network = self.wifi_config_list[0]
+ if not wutils.connect_to_wifi_network_with_id(self.dut,
+ network[NETID],
+ network[SSID]):
+ raise signals.TestFailure("Failed to connect to %s after OTA" %
+ network[SSID])
+ wutils.wifi_forget_network(self.dut, network[SSID])
@test_tracker_info(uuid="05671859-38b1-4dbf-930c-18048971d075")
def test_check_wifi_toggling_after_au(self):
diff --git a/acts/tests/google/wifi/WifiDppTest.py b/acts/tests/google/wifi/WifiDppTest.py
index 42591b0..77471a8 100644
--- a/acts/tests/google/wifi/WifiDppTest.py
+++ b/acts/tests/google/wifi/WifiDppTest.py
@@ -49,19 +49,33 @@
DPP_TEST_MESSAGE_TYPE = "Type"
DPP_TEST_MESSAGE_STATUS = "Status"
DPP_TEST_MESSAGE_NETWORK_ID = "NetworkId"
+ DPP_TEST_MESSAGE_FAILURE_SSID = "onFailureSsid"
+ DPP_TEST_MESSAGE_FAILURE_CHANNEL_LIST = "onFailureChannelList"
+ DPP_TEST_MESSAGE_FAILURE_BAND_LIST = "onFailureBandList"
DPP_TEST_NETWORK_ROLE_STA = "sta"
DPP_TEST_NETWORK_ROLE_AP = "ap"
+ DPP_TEST_PARAM_SSID = "SSID"
+ DPP_TEST_PARAM_PASSWORD = "Password"
+
WPA_SUPPLICANT_SECURITY_SAE = "sae"
WPA_SUPPLICANT_SECURITY_PSK = "psk"
+ DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0
+ DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1
+ DPP_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2
+ DPP_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3
+
+ DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0
+ DPP_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1
+
def setup_class(self):
""" Sets up the required dependencies from the config file and configures the device for
WifiService API tests.
Returns:
- True is successfully configured the requirements for testig.
+ True is successfully configured the requirements for testing.
"""
# Device 0 is under test. Device 1 performs the responder role
@@ -75,6 +89,41 @@
utils.require_sl4a((self.dut,))
utils.sync_device_time(self.dut)
+ req_params = ["dpp_r1_test_only"]
+ opt_param = ["wifi_psk_network", "wifi_sae_network"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ self.dut.log.info(
+ "Parsed configs: %s %s" % (self.wifi_psk_network, self.wifi_sae_network))
+
+ # Set up the networks. This is optional. In case these networks are not initialized,
+ # the script will create random ones. However, a real AP is required to pass DPP R2 test.
+ # Most efficient setup would be to use an AP in WPA2/WPA3 transition mode.
+ if self.DPP_TEST_PARAM_SSID in self.wifi_psk_network:
+ self.psk_network_ssid = self.wifi_psk_network[self.DPP_TEST_PARAM_SSID]
+ else:
+ self.psk_network_ssid = None
+
+ if self.DPP_TEST_PARAM_PASSWORD in self.wifi_psk_network:
+ self.psk_network_password = self.wifi_psk_network[self.DPP_TEST_PARAM_PASSWORD]
+ else:
+ self.psk_network_ssid = None
+
+ if self.DPP_TEST_PARAM_SSID in self.wifi_sae_network:
+ self.sae_network_ssid = self.wifi_sae_network[self.DPP_TEST_PARAM_SSID]
+ else:
+ self.sae_network_ssid = None
+
+ if self.DPP_TEST_PARAM_PASSWORD in self.wifi_sae_network:
+ self.sae_network_password = self.wifi_sae_network[self.DPP_TEST_PARAM_PASSWORD]
+ else:
+ self.sae_network_ssid = None
+
+ if self.dpp_r1_test_only == "False":
+ if not self.wifi_psk_network or not self.wifi_sae_network:
+ asserts.fail("Must specify wifi_psk_network and wifi_sae_network for DPP R2 tests")
+
# Enable verbose logging on the dut
self.dut.droid.wifiEnableVerboseLogging(1)
asserts.assert_true(self.dut.droid.wifiGetVerboseLoggingLevel() == 1,
@@ -87,18 +136,42 @@
self.dut.take_bug_report(test_name, begin_time)
self.dut.cat_adb_log(test_name, begin_time)
- def create_and_save_wifi_network_config(self, security):
+ def create_and_save_wifi_network_config(self, security, random_network=False,
+ r2_auth_error=False):
""" Create a config with random SSID and password.
Args:
security: Security type: PSK or SAE
+ random_network: A boolean that indicates if to create a random network
+ r2_auth_error: A boolean that indicates if to create a network with a bad password
Returns:
A tuple with the config and networkId for the newly created and
saved network.
"""
- config_ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8)
- config_password = utils.rand_ascii_str(8)
+ if security == self.DPP_TEST_SECURITY_PSK:
+ if self.psk_network_ssid is None or self.psk_network_password is None or \
+ random_network is True:
+ config_ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8)
+ config_password = utils.rand_ascii_str(8)
+ else:
+ config_ssid = self.psk_network_ssid
+ if r2_auth_error:
+ config_password = utils.rand_ascii_str(8)
+ else:
+ config_password = self.psk_network_password
+ else:
+ if self.sae_network_ssid is None or self.sae_network_password is None or \
+ random_network is True:
+ config_ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8)
+ config_password = utils.rand_ascii_str(8)
+ else:
+ config_ssid = self.sae_network_ssid
+ if r2_auth_error:
+ config_password = utils.rand_ascii_str(8)
+ else:
+ config_password = self.sae_network_password
+
self.dut.log.info(
"creating config: %s %s %s" % (config_ssid, config_password, security))
config = {
@@ -231,9 +304,9 @@
cmd = "wpa_cli DPP_BOOTSTRAP_REMOVE %s" % uri_id
result = device.adb.shell(cmd)
- if "FAIL" in result:
- asserts.fail("del_uri: Failed to delete URI. Command used: %s" % cmd)
- device.log.info("Deleted URI, id = %s" % uri_id)
+ # If URI was already flushed, ignore a failure here
+ if "FAIL" not in result:
+ device.log.info("Deleted URI, id = %s" % uri_id)
def start_responder_configurator(self,
device,
@@ -269,15 +342,23 @@
self.log.warning("SAE not supported on device! reverting to PSK")
security = self.DPP_TEST_SECURITY_PSK_PASSPHRASE
+ ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8)
+ password = utils.rand_ascii_str(8)
+
if security == self.DPP_TEST_SECURITY_SAE:
conf += self.WPA_SUPPLICANT_SECURITY_SAE
+ if not self.sae_network_ssid is None:
+ ssid = self.sae_network_ssid
+ password = self.sae_network_password
elif security == self.DPP_TEST_SECURITY_PSK_PASSPHRASE:
conf += self.WPA_SUPPLICANT_SECURITY_PSK
+ if not self.psk_network_ssid is None:
+ ssid = self.psk_network_ssid
+ password = self.psk_network_password
else:
conf += self.WPA_SUPPLICANT_SECURITY_PSK
use_psk = True
- ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8)
self.log.debug("SSID = %s" % ssid)
ssid_encoded = binascii.hexlify(ssid.encode()).decode()
@@ -291,7 +372,6 @@
psk_encoded = psk
self.log.debug("PSK = %s" % psk)
else:
- password = utils.rand_ascii_str(8)
if not invalid_config:
password_encoded = binascii.b2a_hex(password.encode()).decode()
else:
@@ -356,9 +436,11 @@
if "FAIL" in result:
asserts.fail("start_responder_enrollee: Failure. Command used: %s" % cmd)
+ device.adb.shell("wpa_cli set dpp_config_processing 2")
+
device.log.info("Started responder in enrollee mode")
- def stop_responder(self, device):
+ def stop_responder(self, device, flush=False):
"""Stop responder on helper device
Args:
@@ -368,7 +450,9 @@
if "FAIL" in result:
asserts.fail("stop_responder: Failed to stop responder")
device.adb.shell("wpa_cli set dpp_configurator_params")
-
+ device.adb.shell("wpa_cli set dpp_config_processing 0")
+ if flush:
+ device.adb.shell("wpa_cli flush")
device.log.info("Stopped responder")
def start_dpp_as_initiator_configurator(self,
@@ -379,7 +463,9 @@
net_role=DPP_TEST_NETWORK_ROLE_STA,
cause_timeout=False,
fail_authentication=False,
- invalid_uri=False):
+ invalid_uri=False,
+ r2_no_ap=False,
+ r2_auth_error=False):
""" Test Easy Connect (DPP) as initiator configurator.
1. Enable wifi, if needed
@@ -406,13 +492,16 @@
fail_authentication: Fail authentication by corrupting the
responder's key
invalid_uri: Use garbage string instead of a URI
+ r2_no_ap: Indicates if to test DPP R2 no AP failure event
+ r2_auth_error: Indicates if to test DPP R2 authentication failure
"""
if not self.dut.droid.wifiIsEasyConnectSupported():
self.log.warning("Easy Connect is not supported on device!")
return
wutils.wifi_toggle_state(self.dut, True)
- test_network_id = self.create_and_save_wifi_network_config(security)
+ test_network_id = self.create_and_save_wifi_network_config(security, random_network=r2_no_ap,
+ r2_auth_error=r2_auth_error)
if use_mac:
mac = autils.get_mac_addr(self.helper_dev, "wlan0")
@@ -454,34 +543,57 @@
== self.DPP_TEST_EVENT_ENROLLEE_SUCCESS:
asserts.fail("DPP failure, unexpected result!")
break
- if dut_event[self.DPP_TEST_EVENT_DATA][
- self
- .DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS:
- if cause_timeout or fail_authentication or invalid_uri:
+ if dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_TYPE] \
+ == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS:
+ if cause_timeout or fail_authentication or invalid_uri or r2_no_ap or r2_auth_error:
asserts.fail(
"Unexpected DPP success, status code: %s" %
dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
else:
val = dut_event[self.DPP_TEST_EVENT_DATA][
self.DPP_TEST_MESSAGE_STATUS]
- if val == 0:
+ if val == self.DPP_EVENT_SUCCESS_CONFIGURATION_SENT:
self.dut.log.info("DPP Configuration sent success")
+ if val == self.DPP_EVENT_SUCCESS_CONFIGURATION_APPLIED:
+ self.dut.log.info("DPP Configuration applied by enrollee")
break
- if dut_event[self.DPP_TEST_EVENT_DATA][
- self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_PROGRESS:
+ if dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_TYPE] \
+ == self.DPP_TEST_EVENT_PROGRESS:
self.dut.log.info("DPP progress event")
val = dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]
- if val == 0:
+ if val == self.DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS:
self.dut.log.info("DPP Authentication success")
- elif val == 1:
+ elif val == self.DPP_EVENT_PROGRESS_RESPONSE_PENDING:
self.dut.log.info("DPP Response pending")
+ elif val == self.DPP_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE:
+ self.dut.log.info("DPP Configuration sent, waiting response")
+ elif val == self.DPP_EVENT_PROGRESS_CONFIGURATION_ACCEPTED:
+ self.dut.log.info("Configuration accepted")
continue
if dut_event[self.DPP_TEST_EVENT_DATA][
self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_FAILURE:
- if cause_timeout or fail_authentication or invalid_uri:
+ if cause_timeout or fail_authentication or invalid_uri or r2_no_ap or r2_auth_error:
self.dut.log.info(
"Error %s occurred, as expected" %
dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
+ if r2_no_ap or r2_auth_error:
+ if not dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_SSID]:
+ asserts.fail("Expected SSID value in DPP R2 onFailure event")
+ self.dut.log.info(
+ "Enrollee searched for SSID %s" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_SSID])
+ if r2_no_ap:
+ if not dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_CHANNEL_LIST]:
+ asserts.fail("Expected Channel list value in DPP R2 onFailure event")
+ self.dut.log.info(
+ "Enrollee scanned the following channels: %s" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_CHANNEL_LIST])
+ if r2_no_ap or r2_auth_error:
+ if not dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_BAND_LIST]:
+ asserts.fail("Expected Band Support list value in DPP R2 onFailure event")
+ self.dut.log.info(
+ "Enrollee supports the following bands: %s" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_BAND_LIST])
else:
asserts.fail(
"DPP failure, status code: %s" %
@@ -492,7 +604,7 @@
self.dut.ed.clear_all_events()
# Stop responder
- self.stop_responder(self.helper_dev)
+ self.stop_responder(self.helper_dev, flush=True)
if not invalid_uri:
# Delete URI
@@ -607,7 +719,7 @@
self.dut.ed.clear_all_events()
# Stop responder
- self.stop_responder(self.helper_dev)
+ self.stop_responder(self.helper_dev, flush=True)
# Delete URI
self.del_uri(self.helper_dev, uri_id)
@@ -792,4 +904,16 @@
use_mac=True,
cause_timeout=True)
- """ Tests End """
+ def test_dpp_as_initiator_configurator_fail_r2_no_ap(self):
+ asserts.skip_if(self.dpp_r1_test_only == "True",
+ "DPP R1 test, skipping this test for DPP R2 only")
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, use_mac=True, r2_no_ap=True)
+
+ def test_dpp_as_initiator_configurator_fail_r2_auth_error(self):
+ asserts.skip_if(self.dpp_r1_test_only == "True",
+ "DPP R1 test, skipping this test for DPP R2 only")
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, use_mac=True, r2_auth_error=True)
+
+""" Tests End """
diff --git a/acts/tests/google/wifi/WifiIOTTest.py b/acts/tests/google/wifi/WifiIOTTest.py
deleted file mode 100644
index 1daf346..0000000
--- a/acts/tests/google/wifi/WifiIOTTest.py
+++ /dev/null
@@ -1,376 +0,0 @@
-#!/usr/bin/env python3.4
-#
-# Copyright 2017 - The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import itertools
-import pprint
-import time
-
-import acts.signals
-import acts.test_utils.wifi.wifi_test_utils as wutils
-
-from acts import asserts
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-
-WifiEnums = wutils.WifiEnums
-
-
-class WifiIOTTest(WifiBaseTest):
- """ Tests for wifi IOT
-
- Test Bed Requirement:
- * One Android device
- * Wi-Fi IOT networks visible to the device
- """
-
- def setup_class(self):
- super().setup_class()
-
- self.dut = self.android_devices[0]
- wutils.wifi_test_device_init(self.dut)
-
- req_params = [ "iot_networks", ]
- opt_params = [ "open_network", "iperf_server_address" ]
- self.unpack_userparams(req_param_names=req_params,
- opt_param_names=opt_params)
-
- asserts.assert_true(
- len(self.iot_networks) > 0,
- "Need at least one iot network with psk.")
-
- if getattr(self, 'open_network', False):
- self.iot_networks.append(self.open_network)
-
- wutils.wifi_toggle_state(self.dut, True)
- if "iperf_server_address" in self.user_params:
- self.iperf_server = self.iperf_servers[0]
- self.iperf_server.start()
-
- # create hashmap for testcase name and SSIDs
- self.iot_test_prefix = "test_iot_connection_to_"
- self.ssid_map = {}
- for network in self.iot_networks:
- SSID = network['SSID'].replace('-','_')
- self.ssid_map[SSID] = network
-
- def setup_test(self):
- self.dut.droid.wakeLockAcquireBright()
- self.dut.droid.wakeUpNow()
-
- def teardown_test(self):
- self.dut.droid.wakeLockRelease()
- self.dut.droid.goToSleepNow()
- wutils.reset_wifi(self.dut)
-
- def teardown_class(self):
- if "iperf_server_address" in self.user_params:
- self.iperf_server.stop()
-
- def on_fail(self, test_name, begin_time):
- self.dut.take_bug_report(test_name, begin_time)
- self.dut.cat_adb_log(test_name, begin_time)
-
- """Helper Functions"""
-
- def connect_to_wifi_network(self, network):
- """Connection logic for open and psk wifi networks.
-
- Args:
- params: Dictionary with network info.
- """
- SSID = network[WifiEnums.SSID_KEY]
- self.dut.ed.clear_all_events()
- wutils.start_wifi_connection_scan(self.dut)
- scan_results = self.dut.droid.wifiGetScanResults()
- wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
- wutils.wifi_connect(self.dut, network, num_of_tries=3)
-
- def run_iperf_client(self, network):
- """Run iperf traffic after connection.
-
- Args:
- params: Dictionary with network info.
- """
- if "iperf_server_address" in self.user_params:
- wait_time = 5
- SSID = network[WifiEnums.SSID_KEY]
- self.log.info("Starting iperf traffic through {}".format(SSID))
- time.sleep(wait_time)
- port_arg = "-p {}".format(self.iperf_server.port)
- success, data = self.dut.run_iperf_client(self.iperf_server_address,
- port_arg)
- self.log.debug(pprint.pformat(data))
- asserts.assert_true(success, "Error occurred in iPerf traffic.")
-
- def connect_to_wifi_network_and_run_iperf(self, network):
- """Connection logic for open and psk wifi networks.
-
- Logic steps are
- 1. Connect to the network.
- 2. Run iperf traffic.
-
- Args:
- params: A dictionary with network info.
- """
- self.connect_to_wifi_network(network)
- self.run_iperf_client(network)
-
- """Tests"""
-
- @test_tracker_info(uuid="a57cc861-b6c2-47e4-9db6-7a3ab32c6e20")
- def test_iot_connection_to_ubiquity_ap1_2g(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="2065c2f7-2b89-4da7-a15d-e5dc17b88d52")
- def test_iot_connection_to_ubiquity_ap1_5g(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="6870e35b-f7a7-45bf-b021-fea049ae53de")
- def test_iot_connection_to_AirportExpress_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="95f4b405-79d7-4873-a152-4384acc88f41")
- def test_iot_connection_to_AirportExpress_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="02a8cc75-6781-4153-8d90-bed7568a1e78")
- def test_iot_connection_to_AirportExtreme_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="83a42c97-1358-4ba7-bdb2-238fdb1c945e")
- def test_iot_connection_to_AirportExtreme_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="d56cc46a-f772-4c96-b84e-4e05c82f5f9d")
- def test_iot_connection_to_AirportExtremeOld_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="4b57277d-ea96-4379-bd71-8b4f03253ec8")
- def test_iot_connection_to_AirportExtremeOld_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="2503d9ed-35df-4be0-b838-590324cecaee")
- def iot_connection_to_Dlink_AC1200_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="0a44e148-a4bf-43f4-88eb-e4c1ffa850ce")
- def iot_connection_to_Dlink_AC1200_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="6bd77417-089f-4fb1-b4c2-2cd673c64bcb")
- def test_iot_connection_to_Dlink_AC3200_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="9fbff6e7-36c8-4342-9c29-bce6a8ef04ec")
- def test_iot_connection_to_Dlink_AC3200_5G_1(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="bfccdaa9-8e01-488c-9768-8c71ab5ec157")
- def test_iot_connection_to_Dlink_AC3200_5G_2(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="0e4978de-0435-4856-ae5a-c39cc64e375b")
- def test_iot_connection_to_Dlink_N750_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="cdb82797-9981-4ba6-8958-025f59c60e83")
- def test_iot_connection_to_Dlink_N750_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="0bf8f129-eb96-4b1e-94bd-8dd93e8731e3")
- def iot_connection_to_Linksys_E800_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="f231216d-6ab6-46b7-a0a5-1ac15935e412")
- def test_iot_connection_to_Linksys_AC1900_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="5acd4bec-b210-4b4c-8b2c-60f3f67798a9")
- def test_iot_connection_to_Linksys_AC1900_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="f4fd9877-b13f-47b0-9523-1ce363200c2f")
- def iot_connection_to_Linksys_AC2400_2g(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="438d679a-4f6c-476d-9eba-63b6f1f2bef4")
- def iot_connection_to_Linksys_AC2400_5g(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="b9bc00d8-46c5-4c5e-bd58-93ab1ca8d53b")
- def iot_connection_to_NETGEAR_AC1900_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="fb4c7d80-4c12-4b08-a40a-2745e2bd167b")
- def iot_connection_to_NETGEAR_AC1900_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="054d2ffc-97fd-4613-bf47-acedd0fa4701")
- def test_iot_connection_to_NETGEAR_AC3200_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="d15a789a-def5-4c6a-b59e-1a75f73cc6a9")
- def test_iot_connection_to_NETGEAR_AC3200_5G_1(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="1de6369e-97da-479f-b17c-9144bb814f51")
- def test_iot_connection_to_NETGEAR_AC3200_5G_2(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="008ec18e-fd48-4359-8a0d-223c921a1faa")
- def iot_connection_to_NETGEAR_N300_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="c61eeaf0-af02-46bf-bcec-871e2f9dee71")
- def iot_connection_to_WNDR4500v2_AES_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="dcad3474-4022-48bc-8529-07321611b616")
- def iot_connection_to_WNDR4500v2_WEP_SHARED128_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="3573a880-4542-4dea-9909-aa2f9865a059")
- def iot_connection_to_ARCHER_WEP_OPEN_64_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="9c15c52e-945a-4b9b-bf0e-5bd6293dad1c")
- def iot_connection_to_ARCHER_WEP_OPEN_128_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="e5517b82-c225-449d-83ac-055a561a764f")
- def test_iot_connection_to_TP_LINK_AC1700_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="9531d3cc-129d-4501-a5e3-d7502120cd8b")
- def test_iot_connection_to_TP_LINK_AC1700_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="eab810d4-8e07-49c9-86c1-cb8d1a0285d0")
- def iot_connection_to_TP_LINK_N300_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="05d4cb25-a58d-46b4-a5ff-6e3fe28f2b16")
- def iot_connection_to_fritz_7490_5g(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="8333e5e6-72fd-4957-bab0-fa45ce1d765a")
- def iot_connection_to_NETGEAR_R8500_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="c88053fb-730f-4447-a802-1fb9721f69df")
- def iot_connection_to_NETGEAR_R8500_5G1(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="f5d1e44b-396b-4976-bb0c-160bdce89a59")
- def iot_connection_to_NETGEAR_R8500_5G2(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="7c12f943-d9e2-45b1-aa84-fcb43efbbb04")
- def test_iot_connection_to_TP_LINK_5504_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="52be6b76-5e43-4289-83e1-4cd0d995d39b")
- def test_iot_connection_to_TP_LINK_5504_5G_1(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="0b43d1da-e207-443d-b16c-c4ee3e924036")
- def test_iot_connection_to_TP_LINK_5504_5G_2(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="4adcef5c-589a-4398-a28c-28a56d762f72")
- def test_iot_connection_to_TP_LINK_C2600_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="3955a443-505b-4015-9daa-f52abbad8377")
- def test_iot_connection_to_TP_LINK_C2600_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="3e9115dd-adb6-40a4-9831-dca8f1f32abe")
- def test_iot_connection_to_Linksys06832_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="5dca028a-7384-444f-b231-973054afe215")
- def test_iot_connection_to_Linksys06832_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="e639f6db-ad8e-4b4f-91f3-10acdf93142a")
- def test_iot_connection_to_AmpedAthena_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="3dd90d80-952f-4f17-a48a-fe42e7d6e1ff")
- def test_iot_connection_to_AmpedAthena_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="b9babe3a-ecba-4c5c-bc6b-0ba48c744e66")
- def test_iot_connection_to_ASUS_AC3100_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="f8f06f92-821d-4e80-8f1e-efb6c6adc12a")
- def test_iot_connection_to_ASUS_AC3100_5G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
-
- @test_tracker_info(uuid="f4d227df-1151-469a-b01c-f4b1c1f7a84b")
- def iot_connection_to_NETGEAR_WGR614_2G(self):
- ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
- self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
diff --git a/acts/tests/google/wifi/WifiMacRandomizationTest.py b/acts/tests/google/wifi/WifiMacRandomizationTest.py
index 4485220..9461cbb 100644
--- a/acts/tests/google/wifi/WifiMacRandomizationTest.py
+++ b/acts/tests/google/wifi/WifiMacRandomizationTest.py
@@ -66,7 +66,7 @@
self.dut_client = self.android_devices[1]
wutils.wifi_test_device_init(self.dut)
wutils.wifi_test_device_init(self.dut_client)
- req_params = ["dbs_supported_models"]
+ req_params = ["dbs_supported_models", "roaming_attn"]
opt_param = [
"open_network", "reference_networks", "wep_networks"
]
@@ -79,8 +79,7 @@
self.configure_packet_capture()
if "AccessPoint" in self.user_params:
- if "AccessPoint" in self.user_params:
- self.legacy_configure_ap_and_start(wep_network=True, ap_count=2)
+ self.legacy_configure_ap_and_start(wep_network=True, ap_count=2)
asserts.assert_true(
len(self.reference_networks) > 0,
@@ -241,8 +240,9 @@
for pkt in packets:
self.log.debug("Packet Summary = %s" % pkt.summary())
if mac in pkt.summary():
- raise signals.TestFailure("Caught Factory MAC in packet sniffer."
- "Packet = %s" % pkt.show())
+ raise signals.TestFailure("Caught Factory MAC in packet sniffer"
+ "Packet = %s Device = %s"
+ % (pkt.show(), self.dut))
def verify_mac_is_found_in_pcap(self, mac, packets):
for pkt in packets:
@@ -250,7 +250,7 @@
if mac in pkt.summary():
return
raise signals.TestFailure("Did not find MAC = %s in packet sniffer."
- % mac)
+ "for device %s" % (mac, self.dut))
def get_sta_mac_address(self):
"""Gets the current MAC address being used for client mode."""
@@ -489,18 +489,18 @@
"""
AP1_network = self.reference_networks[0]["5g"]
AP2_network = self.reference_networks[1]["5g"]
- wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
+ wutils.set_attns(self.attenuators, "AP1_on_AP2_off", self.roaming_attn)
mac_before_roam = self.connect_to_network_and_verify_mac_randomization(
AP1_network)
wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
- "AP1_off_AP2_on", AP2_network)
+ "AP1_off_AP2_on", AP2_network, self.roaming_attn)
mac_after_roam = self.get_randomized_mac(AP2_network)
if mac_after_roam != mac_before_roam:
raise signals.TestFailure("Randomized MAC address changed after "
"roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after "
"roam = %s" %(mac_before_roam, mac_after_roam))
wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
- "AP1_on_AP2_off", AP1_network)
+ "AP1_on_AP2_off", AP1_network, self.roaming_attn)
mac_after_roam = self.get_randomized_mac(AP1_network)
if mac_after_roam != mac_before_roam:
raise signals.TestFailure("Randomized MAC address changed after "
diff --git a/acts/tests/google/wifi/WifiManagerTest.py b/acts/tests/google/wifi/WifiManagerTest.py
index a4ef011..ce57f64 100644
--- a/acts/tests/google/wifi/WifiManagerTest.py
+++ b/acts/tests/google/wifi/WifiManagerTest.py
@@ -97,6 +97,8 @@
def on_fail(self, test_name, begin_time):
self.dut.take_bug_report(test_name, begin_time)
self.dut.cat_adb_log(test_name, begin_time)
+ if self.dut_client:
+ self.dut_client.take_bug_report(test_name, begin_time)
def teardown_class(self):
if "AccessPoint" in self.user_params:
@@ -614,32 +616,6 @@
nw[WifiEnums.BSSID_KEY] != ssid,
"Found forgotten network %s in configured networks." % ssid)
- @test_tracker_info(uuid="b306d65c-6df3-4eb5-a178-6278bdc76c3e")
- def test_reconnect_to_connected_network(self):
- """Connect to a network and immediately issue reconnect.
-
- Steps:
- 1. Connect to a 2GHz network.
- 2. Reconnect to the network using its network id.
- 3. Connect to a 5GHz network.
- 4. Reconnect to the network using its network id.
-
- """
- connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
- reconnect_2g = self.connect_to_wifi_network_with_id(
- connect_2g_data[WifiEnums.NETID_KEY],
- connect_2g_data[WifiEnums.SSID_KEY])
- if not reconnect_2g:
- raise signals.TestFailure("Device did not connect to the correct"
- " 2GHz network.")
- connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
- reconnect_5g = self.connect_to_wifi_network_with_id(
- connect_5g_data[WifiEnums.NETID_KEY],
- connect_5g_data[WifiEnums.SSID_KEY])
- if not reconnect_5g:
- raise signals.TestFailure("Device did not connect to the correct"
- " 5GHz network.")
-
@test_tracker_info(uuid="3cff17f6-b684-4a95-a438-8272c2ad441d")
def test_reconnect_to_previously_connected(self):
"""Connect to multiple networks and reconnect to the previous network.
@@ -976,3 +952,42 @@
"wifi state changed after reboot")
disable_bluetooth(self.dut.droid)
+
+ @test_tracker_info(uuid="d0e14a2d-a28f-4551-8988-1e15d9d8bb1a")
+ def test_scan_result_api(self):
+ """Register scan result callback, start scan and wait for event"""
+ self.dut.ed.clear_all_events()
+ self.dut.droid.wifiStartScanWithListener()
+ try:
+ events = self.dut.ed.pop_events(
+ "WifiManagerScanResultsCallbackOnSuccess", 60)
+ except queue.Empty:
+ asserts.fail(
+ "Wi-Fi scan results did not become available within 60s.")
+
+ @test_tracker_info(uuid="03cfbc86-7fcc-48d8-ab0f-1f6f3523e596")
+ def test_enable_disable_auto_join_saved_network(self):
+ """
+ Add a saved network, simulate user change the auto join to false, ensure the device doesn't
+ auto connect to this network
+
+ Steps:
+ 1. Create a saved network.
+ 2. Add this saved network, and ensure we connect to this network
+ 3. Simulate user change the auto join to false.
+ 4. Toggle the Wifi off and on
+ 4. Ensure device doesn't connect to his network
+ """
+ network = self.open_network_5g
+ wutils.connect_to_wifi_network(self.dut, network)
+ info = self.dut.droid.wifiGetConnectionInfo()
+ network_id = info[WifiEnums.NETID_KEY]
+ self.dut.log.info("Disable auto join on network")
+ self.dut.droid.wifiEnableAutojoin(network_id, False)
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.wifi_toggle_state(self.dut, True)
+ asserts.assert_false(
+ wutils.wait_for_connect(self.dut, network[WifiEnums.SSID_KEY],
+ assert_on_fail=False), "Device should not connect.")
+ self.dut.droid.wifiEnableAutojoin(network_id, True)
+ wutils.wait_for_connect(self.dut, network[WifiEnums.SSID_KEY], assert_on_fail=False)
diff --git a/acts/tests/google/wifi/WifiNetworkRequestTest.py b/acts/tests/google/wifi/WifiNetworkRequestTest.py
index b966de2..28d5d16 100644
--- a/acts/tests/google/wifi/WifiNetworkRequestTest.py
+++ b/acts/tests/google/wifi/WifiNetworkRequestTest.py
@@ -405,39 +405,17 @@
network_specifier)
time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC)
self.dut.droid.wifiRegisterNetworkRequestMatchCallback()
- # Wait for the request to timeout. In the meantime, platform will scan
- # and return matching networks. Ensure the matching networks list is
- # empty.
- start_time = time.time()
- has_request_timedout = False
+ # Wait for the request to timeout.
+ timeout_secs = \
+ NETWORK_REQUEST_TIMEOUT_MS / 1000 + NETWORK_REQUEST_INSTANT_FAILURE_TIMEOUT_SEC
try:
- while not has_request_timedout and time.time() - start_time <= \
- NETWORK_REQUEST_TIMEOUT_MS / 1000:
- # Pop all network request related events.
- network_request_events = \
- self.dut.ed.pop_events("WifiManagerNetwork.*", 30)
- asserts.assert_true(network_request_events, "invalid events")
- for network_request_event in network_request_events:
- # Handle the network match callbacks.
- if network_request_event["name"] == \
- wifi_constants.WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH:
- matched_scan_results = network_request_event["data"]
- self.dut.log.debug(
- "Network request on match results %s",
- matched_scan_results)
- asserts.assert_false(matched_scan_results,
- "Empty network matches expected")
- # Handle the network request unavailable timeout.
- if network_request_event["name"] == \
- wifi_constants.WIFI_NETWORK_CB_ON_UNAVAILABLE:
- self.dut.log.info("Network request timed out")
- has_request_timedout = True
+ on_unavailable_event = self.dut.ed.pop_event(
+ wifi_constants.WIFI_NETWORK_CB_ON_UNAVAILABLE, timeout_secs)
+ asserts.assert_true(on_unavailable_event, "Network request did not timeout")
except queue.Empty:
asserts.fail("No events returned")
finally:
self.dut.droid.wifiStopTrackingStateChange()
- asserts.assert_true(has_request_timedout,
- "Network request did not timeout")
@test_tracker_info(uuid="760c3768-697d-442b-8d61-cfe02f10ceff")
def test_connect_failure_user_rejected(self):
diff --git a/acts/tests/google/wifi/WifiNetworkSelectorTest.py b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
index 5af4ad9..41f723c 100644
--- a/acts/tests/google/wifi/WifiNetworkSelectorTest.py
+++ b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -35,9 +35,13 @@
AP_1_5G_ATTENUATOR = 1
AP_2_2G_ATTENUATOR = 2
AP_2_5G_ATTENUATOR = 3
-ATTENUATOR_INITIAL_SETTING = 60
# WifiNetworkSelector imposes a 10 seconds gap between two selections
NETWORK_SELECTION_TIME_GAP = 12
+LVL1_ATTN = 15
+LVL2_ATTN = 30
+MIN_ATTN = 0
+MAX_ATTN = 95
+ATTN_SLEEP = 12
class WifiNetworkSelectorTest(WifiBaseTest):
@@ -50,44 +54,31 @@
self.dut = self.android_devices[0]
wutils.wifi_test_device_init(self.dut)
- req_params = []
- opt_param = ["open_network", "reference_networks"]
- self.unpack_userparams(
- req_param_names=req_params, opt_param_names=opt_param)
-
- if hasattr(self, 'access_points'):
- self.legacy_configure_ap_and_start(ap_count=2)
-
- if hasattr(self, 'packet_capture'):
- self.configure_packet_capture()
+ self.legacy_configure_ap_and_start(ap_count=2, mirror_ap=False)
+ self.configure_packet_capture()
def setup_test(self):
- #reset and clear all saved networks on the DUT
- wutils.reset_wifi(self.dut)
- #move the APs out of range
- for attenuator in self.attenuators:
- attenuator.set_atten(ATTENUATOR_INITIAL_SETTING)
- #turn on the screen
self.dut.droid.wakeLockAcquireBright()
self.dut.droid.wakeUpNow()
self.dut.ed.clear_all_events()
-
- if hasattr(self, 'packet_capture'):
- self.pcap_procs = wutils.start_pcap(
- self.packet_capture, 'dual', self.test_name)
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+ for a in self.attenuators:
+ a.set_atten(MAX_ATTN)
+ time.sleep(ATTN_SLEEP)
def teardown_test(self):
- #turn off the screen
+ for a in self.attenuators:
+ a.set_atten(MIN_ATTN)
+ wutils.reset_wifi(self.dut)
self.dut.droid.wakeLockRelease()
self.dut.droid.goToSleepNow()
def on_pass(self, test_name, begin_time):
- if hasattr(self, 'packet_capture'):
- wutils.stop_pcap(self.packet_capture, self.pcap_procs, True)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, True)
def on_fail(self, test_name, begin_time):
- if hasattr(self, 'packet_capture'):
- wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
self.dut.take_bug_report(test_name, begin_time)
self.dut.cat_adb_log(test_name, begin_time)
@@ -108,13 +99,14 @@
"""
for network in networks:
ret = ad.droid.wifiAddNetwork(network)
- asserts.assert_true(ret != -1, "Failed to add network %s" %
- network)
+ asserts.assert_true(ret != -1,
+ "Failed to add network %s" % network)
ad.droid.wifiEnableNetwork(ret, 0)
- configured_networks = ad.droid.wifiGetConfiguredNetworks()
- logging.debug("Configured networks: %s", configured_networks)
- def connect_and_verify_connected_bssid(self, expected_bssid):
+ configured_networks = ad.droid.wifiGetConfiguredNetworks()
+ self.log.info("Configured networks: %s", configured_networks)
+
+ def connect_and_verify_connected_bssid(self, network):
"""Start a scan to get the DUT connected to an AP and verify the DUT
is connected to the correct BSSID.
@@ -124,22 +116,19 @@
Returns:
True if connection to given network happen, else return False.
"""
- #wait for the attenuator to stablize
- time.sleep(10)
- #force start a single scan so we don't have to wait for the
- #WCM scheduled scan.
- wutils.start_wifi_connection_scan(self.dut)
- #wait for connection
+ expected_ssid = network['SSID']
+ expected_bssid = network['bssid']
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, expected_ssid)
time.sleep(20)
- #verify connection
actual_network = self.dut.droid.wifiGetConnectionInfo()
- logging.info("Actual network: %s", actual_network)
- try:
- asserts.assert_equal(expected_bssid,
- actual_network[WifiEnums.BSSID_KEY])
- except:
- msg = "Device did not connect to any network."
- raise signals.TestFailure(msg)
+ self.log.info("Actual network: %s", actual_network)
+ asserts.assert_true(
+ actual_network and WifiEnums.BSSID_KEY in actual_network and \
+ expected_bssid == actual_network[WifiEnums.BSSID_KEY],
+ "Expected BSSID: %s, Actual BSSID: %s" %
+ (expected_bssid, actual_network[WifiEnums.BSSID_KEY]))
+ self.log.info("DUT connected to valid network: %s" % expected_bssid)
""" Tests Begin """
@@ -150,14 +139,17 @@
2. Move the DUT in range.
3. Verify the DUT is connected to the network.
"""
- #add a saved network to DUT
+ # add a saved network to DUT
networks = [self.reference_networks[AP_1]['5g']]
self.add_networks(self.dut, networks)
- #move the DUT in range
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
+
+ # move the DUT in range
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected to AP_1 5g network
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
@test_tracker_info(uuid="3ea818f2-10d7-4aad-bfab-7d8fb25aae78")
def test_network_selector_basic_connection_prefer_5g(self):
@@ -166,18 +158,19 @@
2. Move the DUT in range.
3. Verify the DUT is connected to the 5G BSSID.
"""
- #add a saved network with both 2G and 5G BSSIDs to DUT
- # TODO: bmahadev Change this to a single SSID once we migrate tests to
- # use dynamic AP.
+ # add a saved network with both 2G and 5G BSSIDs to DUT
networks = [self.reference_networks[AP_1]['2g'],
self.reference_networks[AP_1]['5g']]
self.add_networks(self.dut, networks)
- #move the DUT in range
- self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
+
+ # Move DUT in range
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(MIN_ATTN)
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected to 5G network
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
@test_tracker_info(uuid="bebb29ca-4486-4cde-b390-c5f8f2e1580c")
def test_network_selector_prefer_stronger_rssi(self):
@@ -187,18 +180,19 @@
2. Move the DUT in range.
3. Verify the DUT is connected to the SSID with stronger RSSI.
"""
- #add a 2G and a 5G saved network to DUT
- networks = [
- self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
- '2g']
- ]
+ # add a 2G and a 5G saved network to DUT
+ networks = [self.reference_networks[AP_1]['2g'],
+ self.reference_networks[AP_2]['2g']]
self.add_networks(self.dut, networks)
- #move the DUT in range
- self.attenuators[AP_1_2G_ATTENUATOR].set_atten(20)
- self.attenuators[AP_2_2G_ATTENUATOR].set_atten(40)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '2g']['bssid'])
+
+ # move the DUT in range
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(LVL1_ATTN)
+ self.attenuators[AP_2_2G_ATTENUATOR].set_atten(LVL2_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected AP_1
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['2g'])
@test_tracker_info(uuid="f9f72dc5-034f-4fe2-a27d-df1b6cae76cd")
def test_network_selector_prefer_secure_over_open_network(self):
@@ -208,17 +202,18 @@
2. Move the DUT in range.
3. Verify the DUT is connected to the secure network that uses WPA2.
"""
- #add a open network and a secure saved network to DUT
- networks = [
- self.open_network[AP_1]['5g'], self.reference_networks[AP_1]['5g']
- ]
+ # add a open network and a secure saved network to DUT
+ networks = [self.open_network[AP_1]['5g'],
+ self.reference_networks[AP_1]['5g']]
self.add_networks(self.dut, networks)
- #move the DUT in range
- #TODO: control open network attenuator
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
+
+ # Move DUT in range
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT connects to secure network
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
@test_tracker_info(uuid="ab2c527c-0f9c-4f09-a13f-e3f461b7da52")
def test_network_selector_blacklist_by_connection_failure(self):
@@ -228,26 +223,30 @@
2. Move the DUT in range.
3. Verify the DUT is connected to network Y.
"""
- #add two saved networks to DUT, and one of them is configured with incorrect password
+ # add two saved networks to DUT, and one of them is configured with
+ # incorrect password
wrong_passwd_network = self.reference_networks[AP_1]['5g'].copy()
wrong_passwd_network['password'] += 'haha'
networks = [wrong_passwd_network, self.reference_networks[AP_2]['5g']]
self.add_networks(self.dut, networks)
- #make both AP_1 5G and AP_2 5G in range, and AP_1 5G has stronger RSSI than AP_2 5G
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
- self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10)
- #start 3 scans to get AP_1 5G blacklisted because of the incorrect password
- count = 0
- while count < 3:
- wutils.start_wifi_connection_scan(self.dut)
+
+ # make AP_1 5G has stronger RSSI than AP_2 5G
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN)
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(LVL1_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # start 3 scans to get AP_1 5G blacklisted because of the incorrect
+ # password
+ for _ in range(3):
+ wutils.start_wifi_connection_scan_and_return_status(self.dut)
time.sleep(NETWORK_SELECTION_TIME_GAP)
- count += 1
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
- '5g']['bssid'])
+
+ # verify DUT is connect AP_2 5G
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_2]['5g'])
@test_tracker_info(uuid="71d88fcf-c7b8-4fd2-a7cb-84ac4a130ecf")
- def test_network_selector_2g_to_5g_prefer_same_SSID(self):
+ def network_selector_2g_to_5g_prefer_same_SSID(self):
"""
1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G
BSSIDs.
@@ -277,7 +276,7 @@
'5g']['bssid'])
@test_tracker_info(uuid="c1243cf4-d96e-427e-869e-3d640bee3f28")
- def test_network_selector_2g_to_5g_different_ssid(self):
+ def network_selector_2g_to_5g_different_ssid(self):
"""
1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G
BSSIDs.
@@ -287,14 +286,13 @@
2G RSSI.
4. Verify the DUT switches to SSID_B's 5G.
"""
- #add two saved networks to DUT
- networks = [
- self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
- '2g']
- ]
+ # add two saved networks to DUT
+ networks = [self.reference_networks[AP_1]['2g'],
+ self.reference_networks[AP_2]['2g']]
self.add_networks(self.dut, networks)
- #make both AP_1 2G and AP_2 5G in range, and AP_1 2G
- #has much stronger RSSI than AP_2 5G
+
+ # make both AP_1 2G and AP_2 5G in range, and AP_1 2G
+ # has much stronger RSSI than AP_2 5G
self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
#verify
@@ -310,7 +308,7 @@
'5g']['bssid'])
@test_tracker_info(uuid="10da95df-83ed-4447-89f8-735b08dbe2eb")
- def test_network_selector_5g_to_2g_same_ssid(self):
+ def network_selector_5g_to_2g_same_ssid(self):
"""
1. Add one SSID that has both 2G and 5G to the DUT.
2. Attenuate down the 2G RSSI.
@@ -346,26 +344,30 @@
3. Change attenuation so that Y's RSSI goes above X's.
4. Verify the DUT stays on X.
"""
- #add two saved networks to DUT
- networks = [
- self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][
- '5g']
- ]
+ # add two saved networks to DUT
+ networks = [self.reference_networks[AP_1]['5g'],
+ self.reference_networks[AP_2]['5g']]
self.add_networks(self.dut, networks)
- #make both AP_1 5G and AP_2 5G in range, and AP_1 5G
- #has stronger RSSI than AP_2 5G
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10)
- self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
- #bump up AP_2 5G RSSI over AP_1 5G RSSI
- self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
- #ensure the time gap between two network selections
+
+ # make both AP_1 5G and AP_2 5G in range, and AP_1 5G
+ # has stronger RSSI than AP_2 5G
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(LVL1_ATTN)
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(LVL2_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected to AP_1
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
+
+ # bump up AP_2 5G RSSI over AP_1 5G RSSI
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(MIN_ATTN)
+
+ # ensure the time gap between two network selections
time.sleep(NETWORK_SELECTION_TIME_GAP)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
+
+ # verify DUT is still connected to AP_1
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
@test_tracker_info(uuid="5470010f-8b62-4b1c-8b83-1f91422eced0")
def test_network_selector_stay_on_user_selected_network(self):
@@ -375,23 +377,23 @@
3. Start a scan and network selection.
4. Verify DUT stays on SSID_A.
"""
- #make AP_1 5G in range with a low RSSI
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10)
- #connect to AP_1 via user selection
- wutils.wifi_connect(self.dut, self.reference_networks[AP_1]['5g'])
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
- #make AP_2 5G in range with a strong RSSI
- self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
- #add AP_2 as a saved network to DUT
+ # set max attenuation on AP_2 and make AP_1 5G in range with low RSSI
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(MIN_ATTN)
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(LVL1_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # connect to AP_1 via user selection and add, save AP_2
+ wutils.connect_to_wifi_network(
+ self.dut, self.reference_networks[AP_1]['5g'])
networks = [self.reference_networks[AP_2]['5g']]
self.add_networks(self.dut, networks)
- #ensure the time gap between two network selections
+
+ # ensure the time gap between two network selections
time.sleep(NETWORK_SELECTION_TIME_GAP)
- #verify we are still connected to AP_1 5G
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
+
+ # verify we are still connected to AP_1 5G
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
@test_tracker_info(uuid="f08d8f73-8c94-42af-bba9-4c49bbf16420")
def test_network_selector_reselect_after_forget_network(self):
@@ -402,22 +404,25 @@
3. Forget X.
5. Verify the DUT reselect and connect to Y.
"""
- #add two saved networks to DUT
- networks = [
- self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][
- '5g']
- ]
+ # add two networks to DUT
+ networks = [self.reference_networks[AP_1]['5g'],
+ self.reference_networks[AP_2]['5g']]
self.add_networks(self.dut, networks)
- #make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger
- #RSSI than AP_2 5G
- self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
- self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10)
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
- '5g']['bssid'])
- #forget AP_1
- wutils.wifi_forget_network(self.dut,
- self.reference_networks[AP_1]['5g']['SSID'])
- #verify
- self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
- '5g']['bssid'])
+
+ # make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger
+ # RSSI than AP_2 5G
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN)
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(LVL1_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT connected to AP1
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_1]['5g'])
+
+ # forget AP_1
+ wutils.wifi_forget_network(
+ self.dut, self.reference_networks[AP_1]['5g']['SSID'])
+
+ # verify DUT connected to AP2
+ self.connect_and_verify_connected_bssid(
+ self.reference_networks[AP_2]['5g'])
diff --git a/acts/tests/google/wifi/WifiNetworkSuggestionTest.py b/acts/tests/google/wifi/WifiNetworkSuggestionTest.py
index 6d6da35..350364b 100644
--- a/acts/tests/google/wifi/WifiNetworkSuggestionTest.py
+++ b/acts/tests/google/wifi/WifiNetworkSuggestionTest.py
@@ -36,10 +36,12 @@
EapPhase2 = WifiEnums.EapPhase2
# Enterprise Config Macros
Ent = WifiEnums.Enterprise
+ATT = 2
# Default timeout used for reboot, toggle WiFi and Airplane mode,
# for the system to settle down after the operation.
DEFAULT_TIMEOUT = 10
+PASSPOINT_TIMEOUT = 30
class WifiNetworkSuggestionTest(WifiBaseTest):
@@ -59,7 +61,7 @@
req_params = []
opt_param = [
"open_network", "reference_networks", "radius_conf_2g", "radius_conf_5g", "ca_cert",
- "eap_identity", "eap_password", "hidden_networks"
+ "eap_identity", "eap_password", "hidden_networks", "passpoint_networks"
]
self.unpack_userparams(
req_param_names=req_params, opt_param_names=opt_param)
@@ -70,21 +72,20 @@
radius_conf_2g=self.radius_conf_2g,
radius_conf_5g=self.radius_conf_5g,)
- asserts.assert_true(
- len(self.reference_networks) > 0,
- "Need at least one reference network with psk.")
- if hasattr(self, "reference_networks"):
- self.wpa_psk_2g = self.reference_networks[0]["2g"]
- self.wpa_psk_5g = self.reference_networks[0]["5g"]
- if hasattr(self, "open_network"):
+ if hasattr(self, "reference_networks") and \
+ isinstance(self.reference_networks, list):
+ self.wpa_psk_2g = self.reference_networks[0]["2g"]
+ self.wpa_psk_5g = self.reference_networks[0]["5g"]
+ if hasattr(self, "open_network") and isinstance(self.open_network,list):
self.open_2g = self.open_network[0]["2g"]
self.open_5g = self.open_network[0]["5g"]
- if hasattr(self, "ent_networks"):
+ if hasattr(self, "ent_networks") and isinstance(self.ent_networks,list):
self.ent_network_2g = self.ent_networks[0]["2g"]
self.ent_network_5g = self.ent_networks[0]["5g"]
self.config_aka = {
Ent.EAP: int(EAP.AKA),
WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY],
+ "carrierId": str(self.dut.droid.telephonyGetSimCarrierId()),
}
self.config_ttls = {
Ent.EAP: int(EAP.TTLS),
@@ -94,8 +95,9 @@
Ent.PHASE2: int(EapPhase2.MSCHAPV2),
WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY],
}
- if hasattr(self, "hidden_networks"):
- self.hidden_network = self.hidden_networks[0]
+ if hasattr(self, "hidden_networks") and \
+ isinstance(self.hidden_networks, list):
+ self.hidden_network = self.hidden_networks[0]
self.dut.droid.wifiRemoveNetworkSuggestions([])
def setup_test(self):
@@ -137,6 +139,24 @@
+ " " + SL4A_APK_NAME)
return True if (is_approved_str == "yes") else False
+ def set_carrier_approved(self, carrier_id, approved):
+ self.dut.log.debug(("Setting IMSI protection exemption for carrier: " + carrier_id
+ + "approved" if approved else "not approved"))
+ self.dut.adb.shell("cmd wifi imsi-protection-exemption-set-user-approved-for-carrier"
+ + " " + carrier_id
+ + " " + ("yes" if approved else "no"))
+
+ def is_carrier_approved(self, carrier_id):
+ is_approved_str = self.dut.adb.shell(
+ "cmd wifi imsi-protection-exemption-has-user-approved-for-carrier"
+ + " " + carrier_id)
+ return True if (is_approved_str == "yes") else False
+
+ def clear_carrier_approved(self, carrier_id):
+ self.dut.adb.shell(
+ "cmd wifi imsi-protection-exemption-clear-user-approved-for-carrier"
+ + " " + carrier_id)
+
def clear_deleted_ephemeral_networks(self):
self.dut.log.debug("Clearing deleted ephemeral networks")
self.dut.adb.shell(
@@ -148,18 +168,21 @@
if expect_post_connection_broadcast is not None:
self.dut.droid.wifiStartTrackingNetworkSuggestionStateChange()
- self.dut.log.info("Adding network suggestions");
+ self.dut.log.info("Adding network suggestions")
asserts.assert_true(
self.dut.droid.wifiAddNetworkSuggestions(network_suggestions),
"Failed to add suggestions")
# Enable suggestions by the app.
- self.dut.log.debug("Enabling suggestions from test");
+ self.dut.log.debug("Enabling suggestions from test")
self.set_approved(True)
wutils.start_wifi_connection_scan_and_return_status(self.dut)
+ # if suggestion is passpoint wait longer for connection.
+ if "profile" in network_suggestions:
+ time.sleep(PASSPOINT_TIMEOUT)
wutils.wait_for_connect(self.dut, expected_ssid)
if expect_post_connection_broadcast is None:
- return;
+ return
# Check if we expected to get the broadcast.
try:
@@ -180,15 +203,11 @@
def remove_suggestions_disconnect_and_ensure_no_connection_back(self,
network_suggestions,
expected_ssid):
+ # Remove suggestion trigger disconnect and wait for the disconnect.
self.dut.log.info("Removing network suggestions")
asserts.assert_true(
self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions),
"Failed to remove suggestions")
- # Ensure we did not disconnect
- wutils.ensure_no_disconnect(self.dut)
-
- # Trigger a disconnect and wait for the disconnect.
- self.dut.droid.wifiDisconnect()
wutils.wait_for_disconnect(self.dut)
self.dut.ed.clear_all_events()
@@ -219,6 +238,11 @@
self.remove_suggestions_disconnect_and_ensure_no_connection_back(
network_suggestions, wifi_network[WifiEnums.SSID_KEY])
+ # Reboot with empty suggestion, verify user approval is kept.
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ asserts.assert_true(self.is_approved(), "User approval should be kept")
+
@test_tracker_info(uuid="bda8ed20-4382-4380-831a-64cf77eca108")
def test_connect_to_wpa_psk_2g(self):
""" Adds a network suggestion and ensure that the device connected.
@@ -247,8 +271,11 @@
1. Send 2 network suggestions to the device (with different priorities).
2. Wait for the device to connect to the network with the highest
priority.
- 3. Re-add the suggestions with the priorities reversed.
- 4. Again wait for the device to connect to the network with the highest
+ 3. In-place modify network suggestions with priorities reversed
+ 4. Restart wifi, wait for the device to connect to the network with the highest
+ priority.
+ 5. Re-add the suggestions with the priorities reversed again.
+ 6. Again wait for the device to connect to the network with the highest
priority.
"""
network_suggestion_2g = self.wpa_psk_2g
@@ -262,16 +289,33 @@
self.wpa_psk_2g[WifiEnums.SSID_KEY],
None)
+ # In-place modify Reverse the priority, should be no disconnect
+ network_suggestion_2g[WifiEnums.PRIORITY] = 2
+ network_suggestion_5g[WifiEnums.PRIORITY] = 5
+ self.dut.log.info("Modifying network suggestions");
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions([network_suggestion_2g,
+ network_suggestion_5g]),
+ "Failed to add suggestions")
+ wutils.ensure_no_disconnect(self.dut)
+
+ # Disable and re-enable wifi, should connect to higher priority
+ wutils.wifi_toggle_state(self.dut, False)
+ time.sleep(DEFAULT_TIMEOUT)
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.start_wifi_connection_scan_and_return_status(self.dut)
+ wutils.wait_for_connect(self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY])
+
self.remove_suggestions_disconnect_and_ensure_no_connection_back(
- [], self.wpa_psk_2g[WifiEnums.SSID_KEY])
+ [], self.wpa_psk_5g[WifiEnums.SSID_KEY])
# Reverse the priority.
# Add suggestions & wait for the connection event.
- network_suggestion_2g[WifiEnums.PRIORITY] = 2
- network_suggestion_5g[WifiEnums.PRIORITY] = 5
+ network_suggestion_2g[WifiEnums.PRIORITY] = 5
+ network_suggestion_5g[WifiEnums.PRIORITY] = 2
self.add_suggestions_and_ensure_connection(
[network_suggestion_2g, network_suggestion_5g],
- self.wpa_psk_5g[WifiEnums.SSID_KEY],
+ self.wpa_psk_2g[WifiEnums.SSID_KEY],
None)
@test_tracker_info(uuid="b1d27eea-23c8-4c4f-b944-ef118e4cc35f")
@@ -308,6 +352,7 @@
4. Reboot the device.
5. Wait for the device to connect to back to it.
6. Remove the suggestions and ensure the device does not connect back.
+ 7. Reboot the device again, ensure user approval is kept
"""
self._test_connect_to_wifi_network_reboot_config_store(
[self.wpa_psk_5g], self.wpa_psk_5g)
@@ -325,9 +370,14 @@
4. Reboot the device.
5. Wait for the device to connect to the wifi network.
6. Remove suggestions and ensure device doesn't connect back to it.
+ 7. Reboot the device again, ensure user approval is kept
"""
+ if "carrierId" in self.config_aka:
+ self.set_carrier_approved(self.config_aka["carrierId"], True)
self._test_connect_to_wifi_network_reboot_config_store(
[self.config_aka], self.ent_network_2g)
+ if "carrierId" in self.config_aka:
+ self.clear_carrier_approved(self.config_aka["carrierId"])
@test_tracker_info(uuid="98b2d40a-acb4-4a2f-aba1-b069e2a1d09d")
def test_connect_to_wpa_ent_config_ttls_pap_reboot_config_store(self):
@@ -342,6 +392,7 @@
4. Reboot the device.
5. Wait for the device to connect to the wifi network.
6. Remove suggestions and ensure device doesn't connect back to it.
+ 7. Reboot the device again, ensure user approval is kept
"""
config = dict(self.config_ttls)
config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
@@ -456,3 +507,222 @@
[network_suggestion], network_suggestion[WifiEnums.SSID_KEY], False)
self.remove_suggestions_disconnect_and_ensure_no_connection_back(
[network_suggestion], network_suggestion[WifiEnums.SSID_KEY])
+
+ @test_tracker_info(uuid="806dff14-7543-482b-bd0a-598de59374b3")
+ def test_connect_to_passpoint_network_with_post_connection_broadcast(self):
+ """ Adds a passpoint network suggestion and ensure that the device connected.
+
+ Steps:
+ 1. Send a network suggestion to the device.
+ 2. Wait for the device to connect to it.
+ 3. Ensure that we did receive the post connection broadcast
+ (isAppInteractionRequired = true).
+ 4. Remove the suggestions and ensure the device does not connect back.
+ """
+ asserts.skip_if(not hasattr(self, "passpoint_networks"),
+ "No passpoint networks, skip this test")
+ passpoint_config = self.passpoint_networks[ATT]
+ passpoint_config[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True
+ if "carrierId" in passpoint_config:
+ self.set_carrier_approved(passpoint_config["carrierId"], True)
+ self.add_suggestions_and_ensure_connection([passpoint_config],
+ passpoint_config[WifiEnums.SSID_KEY], True)
+ self.remove_suggestions_disconnect_and_ensure_no_connection_back(
+ [passpoint_config], passpoint_config[WifiEnums.SSID_KEY])
+ if "carrierId" in passpoint_config:
+ self.clear_carrier_approved(passpoint_config["carrierId"])
+
+ @test_tracker_info(uuid="159b8b8c-fb00-4d4e-a29f-606881dcbf44")
+ def test_connect_to_passpoint_network_reboot_config_store(self):
+ """
+ Adds a passpoint network suggestion and ensure that the device connects to it
+ after reboot.
+
+ Steps:
+ 1. Send a network suggestion to the device.
+ 2. Wait for the device to connect to it.
+ 3. Ensure that we did not receive the post connection broadcast
+ (isAppInteractionRequired = False).
+ 4. Reboot the device.
+ 5. Wait for the device to connect to back to it.
+ 6. Remove the suggestions and ensure the device does not connect back.
+ 7. Reboot the device again, ensure user approval is kept
+ """
+ asserts.skip_if(not hasattr(self, "passpoint_networks"),
+ "No passpoint networks, skip this test")
+ passpoint_config = self.passpoint_networks[ATT]
+ if "carrierId" in passpoint_config:
+ self.set_carrier_approved(passpoint_config["carrierId"], True)
+ self._test_connect_to_wifi_network_reboot_config_store([passpoint_config],
+ passpoint_config)
+ if "carrierId" in passpoint_config:
+ self.clear_carrier_approved(passpoint_config["carrierId"])
+
+ @test_tracker_info(uuid="34f3d28a-bedf-43fe-a12d-2cfadf6bc6eb")
+ def test_fail_to_connect_to_passpoint_network_when_not_approved(self):
+ """
+ Adds a passpoint network suggestion and ensure that the device does not
+ connect to it until we approve the app.
+
+ Steps:
+ 1. Send a network suggestion to the device with the app not approved.
+ 2. Ensure the network is present in scan results, but we don't connect
+ to it.
+ 3. Now approve the app.
+ 4. Wait for the device to connect to it.
+ """
+ asserts.skip_if(not hasattr(self, "passpoint_networks"),
+ "No passpoint networks, skip this test")
+ passpoint_config = self.passpoint_networks[ATT]
+ if "carrierId" in passpoint_config:
+ self.set_carrier_approved(passpoint_config["carrierId"], True)
+ self.dut.log.info("Adding network suggestions")
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions([passpoint_config]),
+ "Failed to add suggestions")
+
+ # Disable suggestions by the app.
+ self.set_approved(False)
+
+ # Ensure the app is not approved.
+ asserts.assert_false(
+ self.is_approved(),
+ "Suggestions should be disabled")
+
+ # Start a new scan to trigger auto-join.
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, passpoint_config[WifiEnums.SSID_KEY])
+
+ # Ensure we don't connect to the network.
+ asserts.assert_false(
+ wutils.wait_for_connect(
+ self.dut, passpoint_config[WifiEnums.SSID_KEY], assert_on_fail=False),
+ "Should not connect to network suggestions from unapproved app")
+
+ self.dut.log.info("Enabling suggestions from test");
+ # Now Enable suggestions by the app & ensure we connect to the network.
+ self.set_approved(True)
+
+ # Ensure the app is approved.
+ asserts.assert_true(
+ self.is_approved(),
+ "Suggestions should be enabled")
+
+ # Start a new scan to trigger auto-join.
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, passpoint_config[WifiEnums.SSID_KEY])
+ time.sleep(PASSPOINT_TIMEOUT)
+ wutils.wait_for_connect(self.dut, passpoint_config[WifiEnums.SSID_KEY])
+ if "carrierId" in passpoint_config:
+ self.clear_carrier_approved(passpoint_config["carrierId"])
+
+ @test_tracker_info(uuid="cf624cda-4d25-42f1-80eb-6c717fb08338")
+ def test_fail_to_connect_to_passpoint_network_when_imsi_protection_exemption_not_approved(self):
+ """
+ Adds a passpoint network suggestion using SIM credential without IMSI privacy protection.
+ Before user approves the exemption, ensure that the device does noconnect to it until we
+ approve the carrier exemption.
+
+ Steps:
+ 1. Send a network suggestion to the device with IMSI protection exemption not approved.
+ 2. Ensure the network is present in scan results, but we don't connect
+ to it.
+ 3. Now approve the carrier.
+ 4. Wait for the device to connect to it.
+ """
+ asserts.skip_if(not hasattr(self, "passpoint_networks"),
+ "No passpoint networks, skip this test")
+ passpoint_config = self.passpoint_networks[ATT]
+ asserts.skip_if("carrierId" not in passpoint_config,
+ "Not a SIM based passpoint network, skip this test")
+
+ self.dut.log.info("Adding network suggestions")
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions([passpoint_config]),
+ "Failed to add suggestions")
+
+ # Ensure the carrier imsi protection exemption is not approved.
+ asserts.assert_false(
+ self.is_carrier_approved(passpoint_config["carrierId"]),
+ "Carrier should not be approved")
+
+ # Start a new scan to trigger auto-join.
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, passpoint_config[WifiEnums.SSID_KEY])
+
+ # Ensure we don't connect to the network.
+ asserts.assert_false(
+ wutils.wait_for_connect(
+ self.dut, passpoint_config[WifiEnums.SSID_KEY], assert_on_fail=False),
+ "Should not connect to network suggestions from unapproved app")
+
+ self.dut.log.info("Enabling suggestions from test")
+ # Now approve IMSI protection exemption by carrier & ensure we connect to the network.
+ self.set_carrier_approved(passpoint_config["carrierId"], True)
+
+ # Ensure the carrier is approved.
+ asserts.assert_true(
+ self.is_carrier_approved(passpoint_config["carrierId"]),
+ "Carrier should be approved")
+
+ # Start a new scan to trigger auto-join.
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, passpoint_config[WifiEnums.SSID_KEY])
+ time.sleep(PASSPOINT_TIMEOUT)
+ wutils.wait_for_connect(self.dut, passpoint_config[WifiEnums.SSID_KEY])
+ self.clear_carrier_approved(passpoint_config["carrierId"])
+
+ @test_tracker_info(uuid="e35f99c8-78a4-4b96-9258-f9834b6ddd33")
+ def test_initial_auto_join_on_network_suggestion(self):
+ """
+ Add a network suggestion with enableAutojoin bit set to false, ensure the device doesn't
+ auto connect to this network
+
+ Steps:
+ 1. Create a network suggestion.
+ 2. Set EnableAutojoin to false.
+ 3. Add this suggestion
+ 4. Ensure device doesn't connect to his network
+ """
+ network_suggestion = self.wpa_psk_5g
+ # Set suggestion auto join initial to false.
+ network_suggestion["enableAutojoin"] = False
+ self.dut.log.info("Adding network suggestions")
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions([network_suggestion]),
+ "Failed to add suggestions")
+ # Enable suggestions by the app.
+ self.dut.log.debug("Enabling suggestions from test")
+ self.set_approved(True)
+ wutils.start_wifi_connection_scan_and_return_status(self.dut)
+ asserts.assert_false(
+ wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY],
+ assert_on_fail=False), "Device should not connect.")
+
+ @test_tracker_info(uuid="ff4e451f-a380-4ff5-a5c2-dd9b1633d5e5")
+ def test_user_override_auto_join_on_network_suggestion(self):
+ """
+ Add a network suggestion, user change the auto join to false, ensure the device doesn't
+ auto connect to this network
+
+ Steps:
+ 1. Create a network suggestion.
+ 2. Add this suggestion, and ensure we connect to this network
+ 3. Simulate user change the auto join to false.
+ 4. Toggle the Wifi off and on
+ 4. Ensure device doesn't connect to his network
+ """
+ network_suggestion = self.wpa_psk_5g
+ self.add_suggestions_and_ensure_connection([network_suggestion],
+ network_suggestion[WifiEnums.SSID_KEY], False)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ self.dut.log.info(wifi_info)
+ network_id = wifi_info[WifiEnums.NETID_KEY]
+ # Simulate user disable auto join through Settings.
+ self.dut.log.info("Disable auto join on suggestion")
+ self.dut.droid.wifiEnableAutojoin(network_id, False)
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.wifi_toggle_state(self.dut, True)
+ asserts.assert_false(
+ wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY],
+ assert_on_fail=False), "Device should not connect.")
diff --git a/acts/tests/google/wifi/WifiPasspointTest.py b/acts/tests/google/wifi/WifiPasspointTest.py
index b867faa..afadb79 100755
--- a/acts/tests/google/wifi/WifiPasspointTest.py
+++ b/acts/tests/google/wifi/WifiPasspointTest.py
@@ -119,20 +119,18 @@
"""
ad = self.dut
ad.ed.clear_all_events()
- wutils.start_wifi_connection_scan(ad)
- scan_results = ad.droid.wifiGetScanResults()
- # Wait for scan to complete.
- time.sleep(5)
- ssid = passpoint_network
- wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, scan_results)
- # Passpoint network takes longer time to connect than normal networks.
- # Every try comes with a timeout of 30s. Setting total timeout to 120s.
- wutils.wifi_passpoint_connect(self.dut, passpoint_network, num_of_tries=4)
+ try:
+ wutils.start_wifi_connection_scan_and_return_status(ad)
+ wutils.wait_for_connect(ad)
+ except:
+ pass
# Re-verify we are connected to the correct network.
network_info = self.dut.droid.wifiGetConnectionInfo()
- if network_info[WifiEnums.SSID_KEY] != passpoint_network:
- raise signals.TestFailure("Device did not connect to the passpoint"
- " network.")
+ self.log.info("Network Info: %s" % network_info)
+ if not network_info or not network_info[WifiEnums.SSID_KEY] or \
+ network_info[WifiEnums.SSID_KEY] not in passpoint_network:
+ raise signals.TestFailure(
+ "Device did not connect to passpoint network.")
def get_configured_passpoint_and_delete(self):
@@ -395,3 +393,26 @@
@test_tracker_info(uuid="f43ea759-673f-4567-aa11-da3bc2cabf08")
def test_start_subscription_provisioning_and_toggle_wifi(self):
self.start_subscription_provisioning(TOGGLE)
+
+ @test_tracker_info(uuid="ad6d5eb8-a3c5-4ce0-9e10-d0f201cd0f40")
+ def test_user_override_auto_join_on_passpoint_network(self):
+ """Add a Passpoint network, simulate user change the auto join to false, ensure the device
+ doesn't auto connect to this passponit network
+
+ Steps:
+ 1. Install a Passpoint Profile.
+ 2. Verify the device connects to the required Passpoint SSID.
+ 3. Disable auto join Passpoint configuration using its FQDN.
+ 4. disable and enable Wifi toggle, ensure we don't connect back
+ """
+ passpoint_config = self.passpoint_networks[BOINGO]
+ self.install_passpoint_profile(passpoint_config)
+ ssid = passpoint_config[WifiEnums.SSID_KEY]
+ self.check_passpoint_connection(ssid)
+ self.dut.log.info("Disable auto join on passpoint")
+ self.dut.droid.wifiEnableAutojoinPasspoint(passpoint_config['fqdn'], False)
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.wifi_toggle_state(self.dut, True)
+ asserts.assert_false(
+ wutils.wait_for_connect(self.dut, ssid, assert_on_fail=False),
+ "Device should not connect.")
diff --git a/acts/tests/google/wifi/WifiRoamingTest.py b/acts/tests/google/wifi/WifiRoamingTest.py
index 4a64ec1..d7bec90 100644
--- a/acts/tests/google/wifi/WifiRoamingTest.py
+++ b/acts/tests/google/wifi/WifiRoamingTest.py
@@ -16,6 +16,8 @@
import pprint
import random
import time
+from acts import context
+from scapy.all import *
from acts import asserts
from acts import base_test
@@ -25,6 +27,11 @@
from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
WifiEnums = wutils.WifiEnums
+DEF_ATTN = 60
+MAX_ATTN = 95
+ROAM_DBM = -75
+WAIT_AFTER_ATTN = 12
+ATTN_STEP = 5
class WifiRoamingTest(WifiBaseTest):
@@ -54,7 +61,8 @@
asserts.assert_true(
len(self.open_network) > 1,
"Need at least two open networks for roaming")
- wutils.wifi_toggle_state(self.dut, True)
+
+ self.configure_packet_capture()
def teardown_class(self):
self.dut.ed.clear_all_events()
@@ -71,6 +79,8 @@
self.dut.droid.wakeLockRelease()
self.dut.droid.goToSleepNow()
wutils.reset_wifi(self.dut)
+ for a in self.attenuators:
+ a.set_atten(0)
def on_fail(self, test_name, begin_time):
self.dut.cat_adb_log(test_name, begin_time)
@@ -91,10 +101,87 @@
5. Validate connection information and ping.
"""
wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
- wutils.wifi_connect(self.dut, AP1_network)
+ wutils.connect_to_wifi_network(self.dut, AP1_network)
self.log.info("Roaming from %s to %s", AP1_network, AP2_network)
- wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
- "AP1_off_AP2_on", AP2_network)
+ self.trigger_roaming_and_verify_attenuation(AP1_network)
+ self.validate_roaming(AP2_network)
+
+ def get_rssi(self, pcap_file, expected_bssid):
+ """Get signal strength of the wifi network attenuated.
+
+ Args:
+ pcap_file: PCAP file path.
+ expected_bssid: BSSID of the wifi network attenuated.
+ """
+ packets = []
+ try:
+ packets = rdpcap(pcap_file)
+ except Scapy_Exception:
+ self.log.error("Failed to read pcap file")
+ if not packets:
+ return 0
+
+ dbm = -100
+ for pkt in packets:
+ if pkt and hasattr(pkt, 'type') and pkt.type == 0 and \
+ pkt.subtype == 8 and hasattr(pkt, 'info'):
+ bssid = pkt.addr3
+ if expected_bssid == bssid:
+ dbm = int(pkt.dBm_AntSignal)
+ self.log.info("RSSI: %s" % dbm)
+ return dbm
+
+ def trigger_roaming_and_verify_attenuation(self, network):
+ """Trigger roaming and verify signal strength is below roaming limit.
+
+ Args:
+ network: Wifi network that is being attenuated.
+ """
+ wutils.set_attns_steps(self.attenuators, "AP1_off_AP2_on")
+ band = '5G' if network['SSID'].startswith('5g_') else '2G'
+ attn = DEF_ATTN + ATTN_STEP
+ while attn <= MAX_ATTN:
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+ time.sleep(WAIT_AFTER_ATTN/3)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ pcap_file = os.path.join(
+ context.get_current_context().get_full_output_path(),
+ 'PacketCapture',
+ '%s_%s.pcap' % (self.test_name, band))
+
+ rssi = self.get_rssi(pcap_file, network["bssid"])
+ if rssi == 0:
+ self.log.error("Failed to verify signal strength")
+ break
+ if self.get_rssi(pcap_file, network["bssid"]) < ROAM_DBM:
+ break
+
+ self.attenuators[0].set_atten(attn)
+ self.attenuators[1].set_atten(attn)
+ time.sleep(WAIT_AFTER_ATTN) # allow some time for attenuation
+ attn += 5
+
+ def validate_roaming(self, expected_con):
+ """Validate roaming.
+
+ Args:
+ expected_con: Expected wifi network after roaming.
+ """
+ expected_con = {
+ WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY],
+ WifiEnums.BSSID_KEY: expected_con["bssid"],
+ }
+ curr_con = self.dut.droid.wifiGetConnectionInfo()
+ for key in expected_con:
+ if expected_con[key] != curr_con[key]:
+ asserts.fail("Expected '%s' to be %s, actual is %s." %
+ (key, expected_con[key], curr_con[key]))
+ self.log.info("Roamed to %s successfully",
+ expected_con[WifiEnums.BSSID_KEY])
+ if not wutils.validate_connection(self.dut):
+ raise signals.TestFailure("Fail to connect to internet on %s" %
+ expected_con[WifiEnums.BSSID_KEY])
""" Tests Begin.
diff --git a/acts/tests/google/wifi/WifiScannerMultiScanTest.py b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
index b0fc246..f8804bb 100755
--- a/acts/tests/google/wifi/WifiScannerMultiScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
@@ -408,6 +408,7 @@
""" Tests Begin """
@test_tracker_info(uuid="d490b146-5fc3-4fc3-9958-78ba0ad63211")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_two_scans_at_same_interval(self):
"""Perform two WifiScanner background scans, one at 2.4GHz and the other
at 5GHz, the same interval and number of BSSIDs per scan.
@@ -437,6 +438,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="0ec9a554-f942-41a9-8096-6b0b400f60b0")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_two_scans_at_different_interval(self):
"""Perform two WifiScanner background scans, one at 2.4GHz and the other
at 5GHz, different interval and number of BSSIDs per scan.
@@ -466,6 +468,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="0d616591-0d32-4be6-8fd4-e4a5e9ccdce0")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scans_24GHz_and_both(self):
"""Perform two WifiScanner background scans, one at 2.4GHz and
the other at both 2.4GHz and 5GHz
@@ -495,6 +498,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="ddcf959e-512a-4e86-b3d3-18cebd0b22a0")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scans_5GHz_and_both(self):
"""Perform two WifiScanner scans, one at 5GHz and the other at both
2.4GHz and 5GHz
@@ -524,6 +528,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="060469f1-fc6b-4255-ab6e-b1d5b54db53d")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scans_24GHz_5GHz_and_DFS(self):
"""Perform three WifiScanner scans, one at 5GHz, one at 2.4GHz and the
other at just 5GHz DFS channels
@@ -557,6 +562,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="14104e98-27a0-43d5-9525-b36b65ac3957")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scans_batch_and_24GHz(self):
"""Perform two WifiScanner background scans, one in batch mode for both
bands and the other in periodic mode at 2.4GHz
@@ -588,6 +594,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="cd6064b5-840b-4334-8cd4-8320a6cda52f")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scans_batch_and_5GHz(self):
"""Perform two WifiScanner background scans, one in batch mode for both
bands and the other in periodic mode at 5GHz
@@ -619,6 +626,7 @@
self.scan_and_validate_results(scan_settings)
@test_tracker_info(uuid="9f48cb0c-de87-4cd2-9e50-857579d44079")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scans_24GHz_5GHz_full_result(self):
"""Perform two WifiScanner background scans, one at 2.4GHz and
the other at 5GHz. Report full scan results.
diff --git a/acts/tests/google/wifi/WifiScannerScanTest.py b/acts/tests/google/wifi/WifiScannerScanTest.py
index eaef028..05945dc 100755
--- a/acts/tests/google/wifi/WifiScannerScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerScanTest.py
@@ -216,6 +216,11 @@
"timestamp"] + scan_time_mic
self.log.debug("max_scan_interval: %s", max_scan_interval)
for result in batch["ScanResults"]:
+ # Though the tests are run in shield box, there are leakes
+ # from outside environment. This would ignore any such SSIDs
+ ssid = result["SSID"]
+ if not ssid.startswith("2g_") or not ssid.startswith("5g_"):
+ continue
if (result["frequency"] not in scan_channels or
result["timestamp"] > max_scan_interval or
result["timestamp"] < scan_rt * 1000 or
@@ -767,6 +772,7 @@
self.wifi_scanner_batch_scan_full(scan_settings[0])
@test_tracker_info(uuid="e9a7cfb5-21c4-4c40-8169-8d88b65a1dee")
+ @WifiBaseTest.wifi_test_wrap
def test_single_scan_while_pno(self):
"""Test wifi scanner single scan parallel to PNO connection.
@@ -927,6 +933,7 @@
self.wifi_scanner_single_scan(scan_setting)
@test_tracker_info(uuid="7c8da0c4-dec7-4d04-abd4-f8ea467a5c6d")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scanner_dual_radio_low_latency(self):
"""Test WiFi scanner single scan for mix channel with default setting
parameters.
@@ -946,6 +953,7 @@
self.wifi_scanner_single_scan_full(scan_setting)
@test_tracker_info(uuid="58b49b01-851b-4e45-b218-9fd27c0be921")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scanner_dual_radio_low_power(self):
"""Test WiFi scanner single scan for mix channel with default setting
parameters.
@@ -965,6 +973,7 @@
self.wifi_scanner_single_scan_full(scan_setting)
@test_tracker_info(uuid="3e7288bc-45e4-497c-bf3a-977eec4e896e")
+ @WifiBaseTest.wifi_test_wrap
def test_wifi_scanner_dual_radio_high_accuracy(self):
"""Test WiFi scanner single scan for mix channel with default setting
parameters.
diff --git a/acts/tests/google/wifi/WifiSoftApAcsTest.py b/acts/tests/google/wifi/WifiSoftApAcsTest.py
index 3c08310..c130098 100644
--- a/acts/tests/google/wifi/WifiSoftApAcsTest.py
+++ b/acts/tests/google/wifi/WifiSoftApAcsTest.py
@@ -69,8 +69,17 @@
"iperf_server_port"]
self.unpack_userparams(
req_param_names=req_params, opt_param_names=opt_param)
+ self.chan_map = {v: k for k, v in hostapd_constants.CHANNEL_MAP.items()}
+ self.pcap_procs = None
def setup_test(self):
+ if hasattr(self, 'packet_capture'):
+ chan = self.test_name.split('_')[-1]
+ if chan.isnumeric():
+ band = '2G' if self.chan_map[int(chan)] < 5000 else '5G'
+ self.packet_capture[0].configure_monitor_mode(band, int(chan))
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture[0], band, self.test_name)
self.dut.droid.wakeLockAcquireBright()
self.dut.droid.wakeUpNow()
@@ -80,6 +89,9 @@
wutils.stop_wifi_tethering(self.dut)
wutils.reset_wifi(self.dut)
wutils.reset_wifi(self.dut_client)
+ if hasattr(self, 'packet_capture') and self.pcap_procs:
+ wutils.stop_pcap(self.packet_capture[0], self.pcap_procs, False)
+ self.pcap_procs = None
try:
if "AccessPoint" in self.user_params:
del self.user_params["reference_networks"]
@@ -155,12 +167,13 @@
"""
if "AccessPoint" in self.user_params:
if not channel_2g:
- self.legacy_configure_ap_and_start(channel_5g=channel_5g)
- elif not channel_5g:
- self.legacy_configure_ap_and_start(channel_2g=channel_2g)
- else:
- self.legacy_configure_ap_and_start(channel_2g=channel_2g,
- channel_5g=chanel_5g)
+ channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G
+ if not channel_5g:
+ channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G
+ self.legacy_configure_ap_and_start(wpa_network=True,
+ wep_network=True,
+ channel_2g=channel_2g,
+ channel_5g=channel_5g)
def start_traffic_and_softap(self, network, softap_band):
"""Start iPerf traffic on client dut, during softAP bring-up on dut.
diff --git a/acts/tests/google/wifi/WifiSoftApTest.py b/acts/tests/google/wifi/WifiSoftApTest.py
index ec1922d..de14eb3 100644
--- a/acts/tests/google/wifi/WifiSoftApTest.py
+++ b/acts/tests/google/wifi/WifiSoftApTest.py
@@ -90,9 +90,8 @@
del self.user_params["open_network"]
def setup_test(self):
- # Set country code explicitly to "US".
- wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
- wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US)
+ for ad in self.android_devices:
+ wutils.wifi_toggle_state(ad, True)
def teardown_test(self):
self.dut.log.debug("Toggling Airplane mode OFF.")
@@ -400,6 +399,7 @@
asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
"SoftAp is not reported as running")
# local hotspot may not have internet connectivity
+ self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
wutils.wifi_connect(self.dut_client, config, check_connectivity=False)
wutils.stop_wifi_tethering(self.dut)
wutils.wait_for_disconnect(self.dut_client)
@@ -572,7 +572,7 @@
def test_softap_auto_shut_off(self):
"""Test for softap auto shut off
- 1. Turn of hotspot
+ 1. Turn off hotspot
2. Register softap callback
3. Let client connect to the hotspot
4. Start wait [wifi_constants.DEFAULT_SOFTAP_TIMEOUT_S] seconds
diff --git a/acts/tests/google/wifi/WifiStaApConcurrencyTest.py b/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
index e16e253..c5d7424 100644
--- a/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
+++ b/acts/tests/google/wifi/WifiStaApConcurrencyTest.py
@@ -14,29 +14,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import itertools
import pprint
-import queue
import time
-import acts.base_test
+from acts import asserts
+from acts import base_test
+from acts.controllers.ap_lib import hostapd_constants
import acts.signals as signals
+from acts.test_decorators import test_tracker_info
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
import acts.utils as utils
-from acts import asserts
-from acts.test_decorators import test_tracker_info
-from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
-
WifiEnums = wutils.WifiEnums
-
+WLAN = "wlan0"
# Channels to configure the AP for various test scenarios.
WIFI_NETWORK_AP_CHANNEL_2G = 1
WIFI_NETWORK_AP_CHANNEL_5G = 36
WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
+
class WifiStaApConcurrencyTest(WifiBaseTest):
"""Tests for STA + AP concurrency scenarios.
@@ -50,53 +49,35 @@
self.dut = self.android_devices[0]
self.dut_client = self.android_devices[1]
- wutils.wifi_test_device_init(self.dut)
- wutils.wifi_test_device_init(self.dut_client)
+
# Do a simple version of init - mainly just sync the time and enable
# verbose logging. This test will fail if the DUT has a sim and cell
# data is disabled. We would also like to test with phones in less
# constrained states (or add variations where we specifically
# constrain).
- utils.require_sl4a((self.dut, self.dut_client))
- utils.sync_device_time(self.dut)
- utils.sync_device_time(self.dut_client)
- # Set country code explicitly to "US".
- wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
- wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US)
- # Enable verbose logging on the duts
- self.dut.droid.wifiEnableVerboseLogging(1)
- asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
- "Failed to enable WiFi verbose logging on the softap dut.")
- self.dut_client.droid.wifiEnableVerboseLogging(1)
- asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
- "Failed to enable WiFi verbose logging on the client dut.")
+ utils.require_sl4a(self.android_devices)
- req_params = ["AccessPoint", "dbs_supported_models"]
- opt_param = ["iperf_server_address", "iperf_server_port"]
- self.unpack_userparams(
- req_param_names=req_params, opt_param_names=opt_param)
+ for ad in self.android_devices:
+ wutils.wifi_test_device_init(ad)
+ utils.sync_device_time(ad)
+ # Set country code explicitly to "US".
+ wutils.set_wifi_country_code(ad, WifiEnums.CountryCode.US)
+ # Enable verbose logging on the duts.
+ ad.droid.wifiEnableVerboseLogging(1)
+ req_params = ["dbs_supported_models",
+ "iperf_server_address",
+ "iperf_server_port"]
+ self.unpack_userparams(req_param_names=req_params,)
asserts.abort_class_if(
self.dut.model not in self.dbs_supported_models,
"Device %s does not support dual interfaces." % self.dut.model)
- # Set the client wifi state to on before the test begins.
- wutils.wifi_toggle_state(self.dut_client, True)
-
- if len(self.android_devices) > 2:
- wutils.wifi_test_device_init(self.android_devices[2])
- utils.sync_device_time(self.android_devices[2])
- wutils.set_wifi_country_code(self.android_devices[2], wutils.WifiEnums.CountryCode.US)
- self.android_devices[2].droid.wifiEnableVerboseLogging(1)
- asserts.assert_equal(self.android_devices[2].droid.wifiGetVerboseLoggingLevel(), 1,
- "Failed to enable WiFi verbose logging on the client dut.")
-
def setup_test(self):
for ad in self.android_devices:
ad.droid.wakeLockAcquireBright()
ad.droid.wakeUpNow()
self.turn_location_off_and_scan_toggle_off()
- wutils.wifi_toggle_state(self.dut, False)
def teardown_test(self):
# Prevent the stop wifi tethering failure to block ap close
@@ -108,6 +89,8 @@
ad.droid.wakeLockRelease()
ad.droid.goToSleepNow()
wutils.reset_wifi(ad)
+ self.turn_location_on_and_scan_toggle_on()
+ wutils.wifi_toggle_state(self.dut, True)
self.access_points[0].close()
del self.user_params["reference_networks"]
del self.user_params["open_network"]
@@ -117,7 +100,8 @@
ad.take_bug_report(test_name, begin_time)
ad.cat_adb_log(test_name, begin_time)
- """Helper Functions"""
+ ### Helper Functions ###
+
def configure_ap(self, channel_2g=None, channel_5g=None):
"""Configure and bring up AP on required channel.
@@ -127,30 +111,27 @@
"""
if not channel_2g:
- self.legacy_configure_ap_and_start(channel_5g=channel_5g)
- elif not channel_5g:
- self.legacy_configure_ap_and_start(channel_2g=channel_2g)
- else:
- self.legacy_configure_ap_and_start(channel_2g=channel_2g,
- channel_5g=channel_5g)
- self.wpapsk_2g = self.reference_networks[0]["2g"]
- self.wpapsk_5g = self.reference_networks[0]["5g"]
+ channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G
+ if not channel_5g:
+ channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G
+ self.legacy_configure_ap_and_start(channel_2g=channel_2g,
+ channel_5g=channel_5g)
+ self.open_2g = self.open_network[0]["2g"]
+ self.open_5g = self.open_network[0]["5g"]
def turn_location_on_and_scan_toggle_on(self):
- """ Turns on wifi location scans.
- """
- acts.utils.set_location_service(self.dut, True)
+ """Turns on wifi location scans."""
+ utils.set_location_service(self.dut, True)
self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
msg = "Failed to turn on location service's scan."
asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
def turn_location_off_and_scan_toggle_off(self):
- """ Turns off wifi location scans.
- """
- acts.utils.set_location_service(self.dut, False)
+ """Turns off wifi location scans."""
+ utils.set_location_service(self.dut, False)
self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
msg = "Failed to turn off location service's scan."
- asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+ asserts.assert_false(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
def run_iperf_client(self, params):
"""Run iperf traffic after connection.
@@ -161,8 +142,8 @@
if "iperf_server_address" in self.user_params:
wait_time = 5
network, ad = params
- SSID = network[WifiEnums.SSID_KEY]
- self.log.info("Starting iperf traffic through {}".format(SSID))
+ ssid = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(ssid))
time.sleep(wait_time)
port_arg = "-p {}".format(self.iperf_server_port)
success, data = ad.run_iperf_client(self.iperf_server_address,
@@ -170,42 +151,6 @@
self.log.debug(pprint.pformat(data))
asserts.assert_true(success, "Error occurred in iPerf traffic.")
- def connect_to_wifi_network_and_verify(self, params):
- """Connection logic for open and psk wifi networks.
-
- Args:
- params: A tuple of network info and AndroidDevice object.
- """
- network, ad = params
- SSID = network[WifiEnums.SSID_KEY]
- wutils.reset_wifi(ad)
- wutils.start_wifi_connection_scan_and_ensure_network_found(
- ad, SSID)
- wutils.wifi_connect(ad, network, num_of_tries=3)
- if len(self.android_devices) > 2:
- wutils.reset_wifi(self.android_devices[2])
- wutils.start_wifi_connection_scan_and_ensure_network_found(
- self.android_devices[2], SSID)
- wutils.wifi_connect(self.android_devices[2], network)
-
- def confirm_softap_can_be_connected(self, network, check_connectivity=True):
- """Confirm the ap started by wifi tethering is seen in scan results.
-
- Args:
- network: config of the ap we are looking for.
- """
- SSID = network[WifiEnums.SSID_KEY]
- wutils.reset_wifi(self.dut_client)
- wutils.start_wifi_connection_scan_and_ensure_network_found(
- self.dut_client, SSID)
- wutils.wifi_connect(self.dut_client, network, check_connectivity=check_connectivity)
- if len(self.android_devices) > 2:
- wutils.reset_wifi(self.android_devices[2])
- wutils.start_wifi_connection_scan_and_ensure_network_found(
- self.android_devices[2], SSID)
- wutils.wifi_connect(
- self.android_devices[2], network, check_connectivity=check_connectivity)
-
def create_softap_config(self):
"""Create a softap config with ssid and password."""
ap_ssid = "softap_" + utils.rand_ascii_str(8)
@@ -216,84 +161,95 @@
return config
def start_softap_and_verify(self, band, check_connectivity=True):
- """Test startup of softap
+ """Test startup of softap.
1. Bring up AP mode.
2. Verify SoftAP active using the client device.
+
+ Args:
+ band: wifi band to start soft ap on
+ check_connectivity: If set, verify internet connectivity
+
+ Returns:
+ Softap config
"""
config = self.create_softap_config()
wutils.start_wifi_tethering(self.dut,
- config[wutils.WifiEnums.SSID_KEY],
- config[wutils.WifiEnums.PWD_KEY], band)
- self.confirm_softap_can_be_connected(config, check_connectivity)
+ config[WifiEnums.SSID_KEY],
+ config[WifiEnums.PWD_KEY],
+ band)
+ for ad in self.android_devices[1:]:
+ wutils.connect_to_wifi_network(
+ ad, config, check_connectivity=check_connectivity)
return config
def connect_to_wifi_network_and_start_softap(self, nw_params, softap_band):
"""Test concurrent wifi connection and softap.
+
This helper method first makes a wifi connection and then starts SoftAp.
-
- Args:
- nw_params: Params for network STA connection.
- softap_band: Band for the AP.
-
1. Bring up wifi.
2. Establish connection to a network.
3. Bring up softap and verify AP can be connected by a client device.
4. Run iperf on the wifi/softap connection to the network.
- """
- wutils.wifi_toggle_state(self.dut, True)
- self.connect_to_wifi_network_and_verify((nw_params, self.dut))
- softap_config = self.start_softap_and_verify(softap_band)
- self.run_iperf_client((nw_params, self.dut))
- self.run_iperf_client((softap_config, self.dut_client))
- if len(self.android_devices) > 2:
- self.log.info("Testbed has extra android devices, do more validation")
- self.verify_traffic_between_softap_clients(
- self.dut_client, self.android_devices[2])
- # Verify that both softap & wifi is enabled concurrently.
- self.verify_wifi_and_softap_enabled()
-
- def start_softap_and_connect_to_wifi_network(self, nw_params, softap_band):
- """Test concurrent wifi connection and softap.
- This helper method first starts SoftAp and then makes a wifi connection.
Args:
nw_params: Params for network STA connection.
softap_band: Band for the AP.
+ """
+ wutils.connect_to_wifi_network(self.dut, nw_params)
+ softap_config = self.start_softap_and_verify(softap_band)
+ self.run_iperf_client((nw_params, self.dut))
+ self.run_iperf_client((softap_config, self.dut_client))
+ if len(self.android_devices) > 2:
+ self.log.info("Testbed has extra devices, do more validation")
+ self.verify_traffic_between_dut_clients(
+ self.dut_client, self.android_devices[2])
+
+ asserts.assert_true(self.dut.droid.wifiCheckState(),
+ "Wifi is not reported as running")
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+
+ def start_softap_and_connect_to_wifi_network(self, nw_params, softap_band):
+ """Test concurrent wifi connection and softap.
+
+ This helper method first starts SoftAp and then makes a wifi connection.
1. Bring up softap and verify AP can be connected by a client device.
2. Bring up wifi.
3. Establish connection to a network.
4. Run iperf on the wifi/softap connection to the network.
+ 5. Verify wifi state and softap state.
+
+ Args:
+ nw_params: Params for network STA connection.
+ softap_band: Band for the AP.
"""
- softap_config = self.start_softap_and_verify(softap_band, check_connectivity=False)
- wutils.wifi_toggle_state(self.dut, True)
- self.connect_to_wifi_network_and_verify((nw_params, self.dut))
+ softap_config = self.start_softap_and_verify(softap_band, False)
+ wutils.connect_to_wifi_network(self.dut, nw_params)
self.run_iperf_client((nw_params, self.dut))
self.run_iperf_client((softap_config, self.dut_client))
- if len(self.android_devices) > 2:
- self.log.info("Testbed has extra android devices, do more validation")
- self.verify_traffic_between_ap_clients(
- self.dut, self.android_devices[2])
- # Verify that both softap & wifi is enabled concurrently.
- self.verify_wifi_and_softap_enabled()
- def verify_wifi_and_softap_enabled(self):
- """Helper to verify both wifi and softap is enabled
- """
+ if len(self.android_devices) > 2:
+ self.log.info("Testbed has extra devices, do more validation")
+ self.verify_traffic_between_dut_clients(
+ self.dut, self.android_devices[2])
+
asserts.assert_true(self.dut.droid.wifiCheckState(),
"Wifi is not reported as running")
asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
- "SoftAp is not reported as running")
+ "SoftAp is not reported as running")
- def verify_traffic_between_softap_clients(self, ad1, ad2, num_of_tries=2):
+ def verify_traffic_between_dut_clients(self, ad1, ad2, num_of_tries=2):
"""Test the clients that connect to DUT's softap can ping each other.
Args:
+ ad1: DUT 1
+ ad2: DUT 2
num_of_tries: the retry times of ping test.
"""
- ad1_ip = ad1.droid.connectivityGetIPv4Addresses('wlan0')[0]
- ad2_ip = ad2.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ ad1_ip = ad1.droid.connectivityGetIPv4Addresses(WLAN)[0]
+ ad2_ip = ad2.droid.connectivityGetIPv4Addresses(WLAN)[0]
# Ping each other
for _ in range(num_of_tries):
if utils.adb_shell_ping(ad1, count=10, dest_ip=ad2_ip, timeout=20):
@@ -306,148 +262,112 @@
else:
asserts.fail("%s ping %s failed" % (ad2.serial, ad1_ip))
- def verify_traffic_between_ap_clients(
- self, ad1, ad2, num_of_tries=2):
- """Test the clients that connect to access point can ping each other.
+ ### Tests ###
- Args:
- num_of_tries: the retry times of ping test.
- """
- ad1_ip = ad1.droid.connectivityGetIPv4Addresses('wlan0')[0]
- ad2_ip = ad2.droid.connectivityGetIPv4Addresses('wlan0')[0]
- # Ping each other
- for _ in range(num_of_tries):
- if utils.adb_shell_ping(ad1, count=10, dest_ip=ad2_ip, timeout=20):
- break
- else:
- asserts.fail("%s ping %s failed" % (ad1.serial, ad2_ip))
- for _ in range(num_of_tries):
- if utils.adb_shell_ping(ad2, count=10, dest_ip=ad1_ip, timeout=20):
- break
- else:
- asserts.fail("%s ping %s failed" % (ad2.serial, ad1_ip))
-
- """Tests"""
@test_tracker_info(uuid="c396e7ac-cf22-4736-a623-aa6d3c50193a")
def test_wifi_connection_2G_softap_2G(self):
- """Tests connection to 2G network followed by bringing up SoftAp on 2G.
- """
+ """Test connection to 2G network followed by SoftAp on 2G."""
self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_2g, WIFI_CONFIG_APBAND_2G)
+ self.open_2g, WIFI_CONFIG_APBAND_2G)
@test_tracker_info(uuid="1cd6120d-3db4-4624-9bae-55c976533a48")
def test_wifi_connection_5G_softap_5G(self):
- """Tests connection to 5G network followed by bringing up SoftAp on 5G.
- """
+ """Test connection to 5G network followed by SoftAp on 5G."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
@test_tracker_info(uuid="5f980007-3490-413e-b94e-7700ffab8534")
def test_wifi_connection_5G_DFS_softap_5G(self):
- """Tests connection to 5G DFS network followed by bringing up SoftAp on 5G.
- """
+ """Test connection to 5G DFS network followed by SoftAp on 5G."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
@test_tracker_info(uuid="d05d5d44-c738-4372-9f01-ce2a640a2f25")
def test_wifi_connection_5G_softap_2G(self):
- """Tests connection to 5G network followed by bringing up SoftAp on 2G.
- """
+ """Test connection to 5G network followed by SoftAp on 2G."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
@test_tracker_info(uuid="909ac713-1ad3-4dad-9be3-ad60f00ed25e")
def test_wifi_connection_5G_DFS_softap_2G(self):
- """Tests connection to 5G DFS network followed by bringing up SoftAp on 2G.
- """
+ """Test connection to 5G DFS network followed by SoftAp on 2G."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
@test_tracker_info(uuid="e8de724a-25d3-4801-94cc-22e9e0ecc8d1")
def test_wifi_connection_2G_softap_5G(self):
- """Tests connection to 2G network followed by bringing up SoftAp on 5G.
- """
+ """Test connection to 2G network followed by SoftAp on 5G."""
self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+ self.open_2g, WIFI_CONFIG_APBAND_5G)
@test_tracker_info(uuid="647f4e17-5c7a-4249-98af-f791d163a39f")
def test_wifi_connection_5G_softap_2G_with_location_scan_on(self):
- """Tests connection to 5G network followed by bringing up SoftAp on 2G
- with location scans turned on.
- """
+ """Test connection to 5G network, SoftAp on 2G with location scan on."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
self.turn_location_on_and_scan_toggle_on()
self.connect_to_wifi_network_and_start_softap(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
# Now toggle wifi off & ensure we can still scan.
wutils.wifi_toggle_state(self.dut, False)
wutils.start_wifi_connection_scan_and_ensure_network_found(
- self.dut, self.wpapsk_5g[WifiEnums.SSID_KEY])
+ self.dut, self.open_5g[WifiEnums.SSID_KEY])
@test_tracker_info(uuid="4aa56c11-e5bc-480b-bd61-4b4ee577a5da")
def test_softap_2G_wifi_connection_2G(self):
- """Tests bringing up SoftAp on 2G followed by connection to 2G network.
- """
+ """Test SoftAp on 2G followed by connection to 2G network."""
self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_2g, WIFI_CONFIG_APBAND_2G)
+ self.open_2g, WIFI_CONFIG_APBAND_2G)
@test_tracker_info(uuid="5f954957-ad20-4de1-b20c-6c97d0463bdd")
def test_softap_5G_wifi_connection_5G(self):
- """Tests bringing up SoftAp on 5G followed by connection to 5G network.
- """
+ """Test SoftAp on 5G followed by connection to 5G network."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
@test_tracker_info(uuid="1306aafc-a07e-4654-ba78-674f90cf748e")
def test_softap_5G_wifi_connection_5G_DFS(self):
- """Tests bringing up SoftAp on 5G followed by connection to 5G DFS network.
- """
+ """Test SoftAp on 5G followed by connection to 5G DFS network."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_5G)
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
@test_tracker_info(uuid="5e28e8b5-3faa-4cff-a782-13a796d7f572")
def test_softap_5G_wifi_connection_2G(self):
- """Tests bringing up SoftAp on 5G followed by connection to 2G network.
- """
+ """Test SoftAp on 5G followed by connection to 2G network."""
self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+ self.open_2g, WIFI_CONFIG_APBAND_5G)
@test_tracker_info(uuid="a2c62bc6-9ccd-4bc4-8a23-9a1b5d0b4b5c")
def test_softap_2G_wifi_connection_5G(self):
- """Tests bringing up SoftAp on 2G followed by connection to 5G network.
- """
+ """Test SoftAp on 2G followed by connection to 5G network."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
@test_tracker_info(uuid="75400685-a9d9-4091-8af3-97bd539c246a")
def test_softap_2G_wifi_connection_5G_DFS(self):
- """Tests bringing up SoftAp on 2G followed by connection to 5G DFS network.
- """
+ """Test SoftAp on 2G followed by connection to 5G DFS network."""
self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_5g, WIFI_CONFIG_APBAND_2G)
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
@test_tracker_info(uuid="aa23a3fc-31a1-4d5c-8cf5-2eb9fdf9e7ce")
def test_softap_5G_wifi_connection_2G_with_location_scan_on(self):
- """Tests bringing up SoftAp on 5G followed by connection to 2G network
- with location scans turned on.
- """
+ """Test SoftAp on 5G, connection to 2G network with location scan on."""
self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
self.turn_location_on_and_scan_toggle_on()
self.start_softap_and_connect_to_wifi_network(
- self.wpapsk_2g, WIFI_CONFIG_APBAND_5G)
+ self.open_2g, WIFI_CONFIG_APBAND_5G)
# Now toggle wifi off & ensure we can still scan.
wutils.wifi_toggle_state(self.dut, False)
wutils.start_wifi_connection_scan_and_ensure_network_found(
- self.dut, self.wpapsk_2g[WifiEnums.SSID_KEY])
+ self.dut, self.open_2g[WifiEnums.SSID_KEY])
diff --git a/acts/tests/google/wifi/WifiWakeTest.py b/acts/tests/google/wifi/WifiWakeTest.py
index 13c6b2d..580af86 100644
--- a/acts/tests/google/wifi/WifiWakeTest.py
+++ b/acts/tests/google/wifi/WifiWakeTest.py
@@ -192,7 +192,7 @@
self.dut.droid.wifiCheckState(),
"Expect Wifi Wake to enable Wifi, but Wifi is disabled.")
- @test_tracker_info(uuid="")
+ @test_tracker_info(uuid="3cecd1c5-54bc-44a2-86f7-ad84625bf094")
def test_reconnect_wifi_network_suggestion(self):
"""Tests that Wifi Wake re-enables Wifi for app provided suggestion."""
self.dut.log.info("Adding network suggestions");
diff --git a/acts/tests/google/wifi/aware/functional/DataPathTest.py b/acts/tests/google/wifi/aware/functional/DataPathTest.py
index 3f68376..cf939bf 100644
--- a/acts/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts/tests/google/wifi/aware/functional/DataPathTest.py
@@ -2159,8 +2159,8 @@
The NDPs are all OPEN (no encryption).
"""
- asserts.assert_true(
- len(self.android_devices) >= 3,
+ asserts.skip_if(
+ len(self.android_devices) < 3,
'A minimum of 3 devices is needed to run the test, have %d' % len(
self.android_devices))
diff --git a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
index 5f01c80..d867443 100644
--- a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -1068,3 +1068,192 @@
s_config=autils.create_discovery_config(
sub_service_name, aconsts.SUBSCRIBE_TYPE_PASSIVE),
device_startup_offset=self.device_startup_offset)
+
+ ##########################################################
+
+ def exchange_messages(self, p_dut, p_disc_id, s_dut, s_disc_id, peer_id_on_sub, session_name):
+ """
+ Exchange message between Publisher and Subscriber on target discovery session
+
+ Args:
+ p_dut: Publisher device
+ p_disc_id: Publish discovery session id
+ s_dut: Subscriber device
+ s_disc_id: Subscribe discovery session id
+ peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber
+ session_name: dictionary of discovery session name base on role("pub" or "sub")
+ {role: {disc_id: name}}
+ """
+ msg_template = "Hello {} from {} !"
+
+ # Message send from Subscriber to Publisher
+ s_to_p_msg = msg_template.format(session_name["pub"][p_disc_id],
+ session_name["sub"][s_disc_id])
+ s_dut.droid.wifiAwareSendMessage(s_disc_id,
+ peer_id_on_sub,
+ self.get_next_msg_id(),
+ s_to_p_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_SENT, s_disc_id))
+ event = autils.wait_for_event(p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ p_disc_id))
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], s_to_p_msg,
+ "Message on service %s from Subscriber to Publisher "
+ "not received correctly" % session_name["pub"][p_disc_id])
+ peer_id_on_pub = event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # Message send from Publisher to Subscriber
+ p_to_s_msg = msg_template.format(session_name["sub"][s_disc_id],
+ session_name["pub"][p_disc_id])
+ p_dut.droid.wifiAwareSendMessage(p_disc_id,
+ peer_id_on_pub,
+ self.get_next_msg_id(), p_to_s_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(
+ p_dut, autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_SENT, p_disc_id))
+ event = autils.wait_for_event(s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ s_disc_id))
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], p_to_s_msg,
+ "Message on service %s from Publisher to Subscriber"
+ "not received correctly" % session_name["sub"][s_disc_id])
+
+ def run_multiple_concurrent_services_same_name_diff_ssi(self, type_x, type_y):
+ """Validate same service name with multiple service specific info on publisher
+ and subscriber can see all service
+
+ - p_dut running Publish X and Y
+ - s_dut running subscribe A and B
+ - subscribe A find X and Y
+ - subscribe B find X and Y
+
+ Message exchanges:
+ - A to X and X to A
+ - B to X and X to B
+ - A to Y and Y to A
+ - B to Y and Y to B
+
+ Note: test requires that publisher device support 2 publish sessions concurrently,
+ and subscriber device support 2 subscribe sessions concurrently.
+ The test will be skipped if the devices are not capable.
+
+ Args:
+ type_x, type_y: A list of [ptype, stype] of the publish and subscribe
+ types for services X and Y respectively.
+ """
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ asserts.skip_if(
+ p_dut.aware_capabilities[aconsts.CAP_MAX_PUBLISHES] < 2
+ or s_dut.aware_capabilities[aconsts.CAP_MAX_SUBSCRIBES] < 2,
+ "Devices do not support 2 publish sessions or 2 subscribe sessions")
+
+ SERVICE_NAME = "ServiceName"
+ X_SERVICE_SSI = "ServiceSpecificInfoXXX"
+ Y_SERVICE_SSI = "ServiceSpecificInfoYYY"
+ use_id = True
+
+ # attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False, None, use_id)
+ autils.wait_for_event(p_dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, p_id))
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False, None, use_id)
+ autils.wait_for_event(s_dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, s_id))
+
+ # Publisher: start publishing both X & Y services and wait for confirmations
+ p_disc_id_x = p_dut.droid.wifiAwarePublish(
+ p_id, autils.create_discovery_config(SERVICE_NAME, type_x[0], X_SERVICE_SSI), use_id)
+ event = autils.wait_for_event(p_dut,
+ autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id_x))
+
+ p_disc_id_y = p_dut.droid.wifiAwarePublish(
+ p_id, autils.create_discovery_config(SERVICE_NAME, type_x[0], Y_SERVICE_SSI), use_id)
+ event = autils.wait_for_event(p_dut,
+ autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id_y))
+
+ # Subscriber: start subscribe session A
+ s_disc_id_a = s_dut.droid.wifiAwareSubscribe(
+ s_id, autils.create_discovery_config(SERVICE_NAME, type_x[1]), use_id)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, s_disc_id_a))
+
+ # Subscriber: start subscribe session B
+ s_disc_id_b = s_dut.droid.wifiAwareSubscribe(
+ p_id, autils.create_discovery_config(SERVICE_NAME, type_y[1]), use_id)
+ autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, s_disc_id_b))
+
+ session_name = {"pub": {p_disc_id_x: "X", p_disc_id_y: "Y"},
+ "sub": {s_disc_id_a: "A", s_disc_id_b: "B"}}
+
+ # Subscriber: subscribe session A & B wait for service discovery
+ # Number of results on each session should be exactly 2
+ results_a = {}
+ for i in range(2):
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_a))
+ results_a[
+ bytes(event["data"][
+ aconsts.SESSION_CB_KEY_SERVICE_SPECIFIC_INFO]).decode('utf-8')] = event
+ autils.fail_on_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_a))
+
+ results_b = {}
+ for i in range(2):
+ event = autils.wait_for_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_b))
+ results_b[
+ bytes(event["data"][
+ aconsts.SESSION_CB_KEY_SERVICE_SPECIFIC_INFO]).decode('utf-8')] = event
+ autils.fail_on_event(s_dut, autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_b))
+
+ s_a_peer_id_for_p_x = results_a[X_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+ s_a_peer_id_for_p_y = results_a[Y_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+ s_b_peer_id_for_p_x = results_b[X_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+ s_b_peer_id_for_p_y = results_b[Y_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # Message exchange between Publisher and Subscribe
+ self.exchange_messages(p_dut, p_disc_id_x,
+ s_dut, s_disc_id_a, s_a_peer_id_for_p_x, session_name)
+
+ self.exchange_messages(p_dut, p_disc_id_x,
+ s_dut, s_disc_id_b, s_b_peer_id_for_p_x, session_name)
+
+ self.exchange_messages(p_dut, p_disc_id_y,
+ s_dut, s_disc_id_a, s_a_peer_id_for_p_y, session_name)
+
+ self.exchange_messages(p_dut, p_disc_id_y,
+ s_dut, s_disc_id_b, s_b_peer_id_for_p_y, session_name)
+
+ # Check no more messages
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ ##########################################################
+
+ def test_multiple_concurrent_services_diff_ssi_unsolicited_passive(self):
+ """Multi service test on same service name but different Service Specific Info
+ - Unsolicited publish
+ - Passive subscribe
+ """
+ self.run_multiple_concurrent_services_same_name_diff_ssi(
+ type_x=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE],
+ type_y=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE])
+
+ def test_multiple_concurrent_services_diff_ssi_solicited_active(self):
+ """Multi service test on same service name but different Service Specific Info
+ - Solicited publish
+ - Active subscribe
+ """
+ self.run_multiple_concurrent_services_same_name_diff_ssi(
+ type_x=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE],
+ type_y=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE])
diff --git a/acts/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py b/acts/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
index 074ca48..3898832 100644
--- a/acts/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
+++ b/acts/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
@@ -70,12 +70,13 @@
pcaps = pcap_5g + pcap_2g
# Verify factory MAC is not leaked in both 2G and 5G pcaps
- for mac in factory_mac_addresses:
- wutils.verify_mac_not_found_in_pcap(mac, pcaps)
+ ads = [self.android_devices[0], self.android_devices[1]]
+ for i, mac in enumerate(factory_mac_addresses):
+ wutils.verify_mac_not_found_in_pcap(ads[i], mac, pcaps)
# Verify random MACs are being used and in pcaps
- for mac in mac_addresses:
- wutils.verify_mac_is_found_in_pcap(mac, pcaps)
+ for i, mac in enumerate(mac_addresses):
+ wutils.verify_mac_is_found_in_pcap(ads[i], mac, pcaps)
def transfer_mac_format(self, mac):
"""add ':' to mac String, and transfer to lower case
diff --git a/acts/tests/google/wifi/aware/functional/NonConcurrencyTest.py b/acts/tests/google/wifi/aware/functional/NonConcurrencyTest.py
index f0286f2..4f40509 100644
--- a/acts/tests/google/wifi/aware/functional/NonConcurrencyTest.py
+++ b/acts/tests/google/wifi/aware/functional/NonConcurrencyTest.py
@@ -40,6 +40,7 @@
AwareBaseTest.teardown_test(self)
for ad in self.android_devices:
ad.droid.wifiP2pClose()
+ ad.droid.connectivityStopTethering(0)
def run_aware_then_incompat_service(self, is_p2p):
"""Run test to validate that a running Aware session terminates when an
@@ -81,6 +82,15 @@
# expect an announcement about Aware non-availability
autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+ # Wifi state and location mode changes should not make Aware available
+ wutils.wifi_toggle_state(dut, False)
+ utils.set_location_service(dut, False)
+ wutils.wifi_toggle_state(dut, True)
+ utils.set_location_service(dut, True)
+ autils.fail_on_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+ asserts.assert_false(dut.droid.wifiIsAwareAvailable(),
+ "Aware is available (it shouldn't be)")
+
# try starting anyway (expect failure)
dut.droid.wifiAwareAttach()
autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
@@ -133,6 +143,7 @@
p_id = dut.droid.wifiAwareAttach()
autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(dut, ap_ssid)
wutils.wifi_connect(dut, config, check_connectivity=False)
autils.wait_for_event(dut, wconsts.WIFI_STATE_CHANGED)
diff --git a/acts/tests/google/wifi/aware/performance/ThroughputTest.py b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
index a90734d..2dab276 100644
--- a/acts/tests/google/wifi/aware/performance/ThroughputTest.py
+++ b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
@@ -406,3 +406,35 @@
[self.PASSPHRASE, self.PASSPHRASE2], results=results)
asserts.explicit_pass(
"test_iperf_max_ndi_aware_only_passphrases passes", extras=results)
+
+ def run_test_traffic_latency_single_ndp_ib_aware_only_open(self):
+ """Measure IPv6 traffic latency performance(ping) on NDP between 2 devices.
+ Security config is open.
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "subscriber"
+ ndp_info = autils.create_ib_ndp(p_dut,
+ s_dut,
+ autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED),
+ autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ self.device_startup_offset)
+ p_req_key = ndp_info[0]
+ s_req_key = ndp_info[1]
+ p_aware_if = ndp_info[2]
+ s_aware_if = ndp_info[3]
+ p_ipv6 = ndp_info[4]
+ s_ipv6 = ndp_info[5]
+ self.log.info("Interface names: P=%s, S=%s", p_aware_if, s_aware_if)
+ self.log.info("Interface addresses (IPv6): P=%s, S=%s", p_ipv6, s_ipv6)
+ self.log.info("Start ping %s from %s", s_ipv6, p_ipv6)
+ latency_result = autils.run_ping6(p_dut, s_ipv6)
+ self.log.info("The latency results are %s", latency_result)
+
+ def test_traffic_latency_single_ndp_ib_aware_only_open(self):
+ """Test IPv6 traffic latency performance on NDP with security config is open.
+ """
+ self.run_test_traffic_latency_single_ndp_ib_aware_only_open()
diff --git a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py b/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
index fbe95d4..153a81f 100644
--- a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
@@ -30,10 +30,18 @@
class MessagesStressTest(AwareBaseTest):
"""Set of stress tests for Wi-Fi Aware L2 (layer 2) message exchanges."""
+ # Number of the message queue depth per Uid from framework
+ MESSAGE_QUEUE_DEPTH_PER_UID = 50
# Number of iterations in the stress test (number of messages)
+ # Should be larger than MESSAGE_QUEUE_DEPTH_PER_UID
NUM_ITERATIONS = 100
+ # Number of message to send per round to avoid exceed message queue depth limit
+ # Should be less than or equal to 1/2 of MESSAGE_QUEUE_DEPTH_PER_UID
+ NUM_PER_ROUND = 20
+ NUM_ROUNDS = 5
+
# Maximum permitted percentage of messages which fail to be transmitted
# correctly
MAX_TX_FAILURE_PERCENTAGE = 2
@@ -172,39 +180,166 @@
if data[KEY_TX_OK_COUNT] > 0:
results["tx_count_success"] = results["tx_count_success"] + 1
if data[KEY_TX_OK_COUNT] > 1:
- results["tx_count_duplicate_success"] = (
- results["tx_count_duplicate_success"] + 1)
+ results["tx_count_duplicate_success"] += 1
if data[KEY_TX_FAIL_COUNT] > 0:
- results["tx_count_fail"] = results["tx_count_fail"] + 1
+ results["tx_count_fail"] += 1
if data[KEY_TX_FAIL_COUNT] > 1:
- results[
- "tx_count_duplicate_fail"] = results["tx_count_duplicate_fail"] + 1
+ results["tx_count_duplicate_fail"] += 1
if (data[KEY_TX_OK_COUNT] == 0 and data[KEY_TX_FAIL_COUNT] == 0
and data[KEY_ID] != -1):
- results["tx_count_neither"] = results["tx_count_neither"] + 1
+ results["tx_count_neither"] += 1
if data[KEY_TX_OK_COUNT] > 0 and data[KEY_RX_COUNT] == 0:
- results["tx_count_tx_ok_but_no_rx"] = (
- results["tx_count_tx_ok_but_no_rx"] + 1)
+ results["tx_count_tx_ok_but_no_rx"] += 1
if data[KEY_RX_COUNT] > 0:
- results["rx_count"] = results["rx_count"] + 1
+ results["rx_count"] += 1
if data[KEY_RX_COUNT] > 1:
- results[
- "rx_count_duplicate"] = results["rx_count_duplicate"] + 1
+ results["rx_count_duplicate"] += 1
if data[KEY_RX_COUNT] > 0 and data[KEY_TX_OK_COUNT] == 0:
- results["rx_count_no_ok_tx_indication"] = (
- results["rx_count_no_ok_tx_indication"] + 1)
+ results["rx_count_no_ok_tx_indication"] += 1
if data[KEY_RX_COUNT] > 0 and data[KEY_TX_FAIL_COUNT] > 0:
- results["rx_count_fail_tx_indication"] = (
- results["rx_count_fail_tx_indication"] + 1)
+ results["rx_count_fail_tx_indication"] += 1
if data[KEY_RX_COUNT] > 0 and data[KEY_ID] == -1:
- results[
- "rx_count_no_tx_message"] = results["rx_count_no_tx_message"] + 1
+ results["rx_count_no_tx_message"] += 1
#######################################################################
@test_tracker_info(uuid="e88c060f-4ca7-41c1-935a-d3d62878ec0b")
- def test_stress_message(self):
- """Stress test for bi-directional message transmission and reception."""
+ def test_stress_message_no_throttling(self):
+ """Stress test for bi-directional message transmission and reception no throttling"""
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ # Start up a discovery session
+ discovery_data = autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED),
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ device_startup_offset=self.device_startup_offset,
+ msg_id=self.get_next_msg_id())
+ p_id = discovery_data[0]
+ s_id = discovery_data[1]
+ p_disc_id = discovery_data[2]
+ s_disc_id = discovery_data[3]
+ peer_id_on_sub = discovery_data[4]
+ peer_id_on_pub = discovery_data[5]
+
+ # Store information on Tx & Rx messages
+ messages_by_msg = {} # keyed by message text
+ # {text -> {id, tx_ok_count, tx_fail_count, rx_count}}
+ messages_by_id = {} # keyed by message ID {id -> text}
+ iterations = 0
+ p_tx_ok_count_total = 0
+ p_tx_fail_count_total = 0
+ p_tx_unknown_id_total = 0
+ s_tx_ok_count_total = 0
+ s_tx_fail_count_total = 0
+ s_tx_unknown_id_total = 0
+
+ # First round will fill up the message queue
+ num_of_messages_this_round = self.MESSAGE_QUEUE_DEPTH_PER_UID
+
+ # send messages (one in each direction) in rounds to avoid exceed the queue limit
+ for j in range(self.NUM_ROUNDS):
+ for k in range(num_of_messages_this_round):
+ msg_p2s = "Message Publisher -> Subscriber #%d" % iterations
+ next_msg_id = self.get_next_msg_id()
+ self.init_info(msg_p2s, next_msg_id, messages_by_msg,
+ messages_by_id)
+ p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub,
+ next_msg_id, msg_p2s, 0)
+
+ msg_s2p = "Message Subscriber -> Publisher #%d" % iterations
+ next_msg_id = self.get_next_msg_id()
+ self.init_info(msg_s2p, next_msg_id, messages_by_msg,
+ messages_by_id)
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
+ next_msg_id, msg_s2p, 0)
+ iterations += 1
+
+ # wait for message tx confirmation
+ (p_tx_ok_count, p_tx_fail_count, p_tx_unknown_id) = self.wait_for_tx_events(
+ p_dut, self.NUM_PER_ROUND, messages_by_msg, messages_by_id)
+ p_tx_ok_count_total += p_tx_ok_count
+ p_tx_fail_count_total += p_tx_fail_count
+ p_tx_unknown_id_total += p_tx_unknown_id
+ (s_tx_ok_count, s_tx_fail_count, s_tx_unknown_id) = self.wait_for_tx_events(
+ s_dut, self.NUM_PER_ROUND, messages_by_msg, messages_by_id)
+ s_tx_ok_count_total += s_tx_ok_count
+ s_tx_fail_count_total += s_tx_fail_count
+ s_tx_unknown_id_total += s_tx_unknown_id
+
+ num_of_messages_this_round = self.NUM_PER_ROUND
+
+ # wait for the rest message tx confirmation
+ p_tx_total = p_tx_ok_count_total + p_tx_fail_count_total + p_tx_unknown_id_total
+ s_tx_total = s_tx_ok_count_total + s_tx_fail_count_total + s_tx_unknown_id_total
+ (p_tx_ok_count, p_tx_fail_count, p_tx_unknown_id) = self.wait_for_tx_events(
+ p_dut, iterations - p_tx_total, messages_by_msg, messages_by_id)
+ (s_tx_ok_count, s_tx_fail_count, s_tx_unknown_id) = self.wait_for_tx_events(
+ s_dut, iterations - s_tx_total, messages_by_msg, messages_by_id)
+ p_tx_ok_count_total += p_tx_ok_count
+ p_tx_fail_count_total += p_tx_fail_count
+ p_tx_unknown_id_total += p_tx_unknown_id
+ s_tx_ok_count_total += s_tx_ok_count
+ s_tx_fail_count_total += s_tx_fail_count
+ s_tx_unknown_id_total += s_tx_unknown_id
+ self.log.info(
+ "Transmission done: pub=%d, sub=%d transmitted successfully",
+ p_tx_ok_count_total, s_tx_ok_count_total)
+
+ # wait for message rx confirmation (giving it the total number of messages
+ # transmitted rather than just those transmitted correctly since sometimes
+ # the Tx doesn't get that information correctly. I.e. a message the Tx
+ # thought was not transmitted correctly is actually received - missing ACK?
+ # bug?)
+ self.wait_for_rx_events(p_dut, iterations, messages_by_msg)
+ self.wait_for_rx_events(s_dut, iterations, messages_by_msg)
+
+ # analyze results
+ results = {}
+ results["tx_count"] = 2 * iterations
+ results["tx_unknown_ids"] = p_tx_unknown_id_total + s_tx_unknown_id_total
+ self.analyze_results(results, messages_by_msg)
+
+ # clear errors
+ asserts.assert_equal(results["tx_unknown_ids"], 0,
+ "Message ID corruption", results)
+ asserts.assert_equal(results["tx_count_neither"], 0,
+ "Tx message with no success or fail indication",
+ results)
+ asserts.assert_equal(results["tx_count_duplicate_fail"], 0,
+ "Duplicate Tx fail messages", results)
+ asserts.assert_equal(results["tx_count_duplicate_success"], 0,
+ "Duplicate Tx success messages", results)
+ asserts.assert_equal(results["rx_count_no_tx_message"], 0,
+ "Rx message which wasn't sent - message corruption?", results)
+ asserts.assert_equal(results["tx_count_tx_ok_but_no_rx"], 0,
+ "Tx got ACK but Rx didn't get message", results)
+
+ # possibly ok - but flag since most frequently a bug
+ asserts.assert_equal(results["rx_count_no_ok_tx_indication"], 0,
+ "Message received but Tx didn't get ACK", results)
+ asserts.assert_equal(results["rx_count_fail_tx_indication"], 0,
+ "Message received but Tx didn't get ACK", results)
+
+ # permissible failures based on thresholds
+ asserts.assert_true(
+ results["tx_count_fail"] <=
+ (self.MAX_TX_FAILURE_PERCENTAGE * iterations * 2 / 100),
+ "Number of Tx failures exceeds threshold", extras=results)
+ asserts.assert_true(
+ results["rx_count_duplicate"] <=
+ (self.MAX_DUPLICATE_RX_PERCENTAGE * iterations * 2 / 100),
+ "Number of duplicate Rx exceeds threshold", extras=results)
+
+ asserts.explicit_pass("test_stress_message_no_throttling done", extras=results)
+
+ @test_tracker_info(uuid="546b0c6f-3071-4330-8e23-842ecbd07018")
+ def test_stress_message_throttling(self):
+ """Stress test for bi-directional message transmission and reception with throttling"""
p_dut = self.android_devices[0]
s_dut = self.android_devices[1]
@@ -295,14 +430,16 @@
# permissible failures based on thresholds
asserts.assert_true(
- results["tx_count_fail"] <=
- (self.MAX_TX_FAILURE_PERCENTAGE * self.NUM_ITERATIONS / 100),
- "Number of Tx failures exceeds threshold",
- extras=results)
- asserts.assert_true(
results["rx_count_duplicate"] <=
- (self.MAX_DUPLICATE_RX_PERCENTAGE * self.NUM_ITERATIONS / 100),
- "Number of duplicate Rx exceeds threshold",
- extras=results)
+ (self.MAX_DUPLICATE_RX_PERCENTAGE * results["tx_count_success"] / 100),
+ "Number of duplicate Rx exceeds threshold", extras=results)
- asserts.explicit_pass("test_stress_message done", extras=results)
+ # check working status message queue limit per UID
+ asserts.assert_true(
+ results["tx_count_success"] >= self.MESSAGE_QUEUE_DEPTH_PER_UID * 2,
+ "Number of messages did not reach uid message queue limit", extras=results)
+ asserts.assert_true(
+ results["tx_count_success"] < self.NUM_ITERATIONS * 2,
+ "Seems uid message queue limit is not working, Tx all message", extras=results)
+
+ asserts.explicit_pass("test_stress_message_throttling done", extras=results)
diff --git a/acts/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py b/acts/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
index f9d2b23..2a1cb92 100644
--- a/acts/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
+++ b/acts/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
@@ -17,6 +17,7 @@
import acts.test_utils.wifi.wifi_test_utils as wutils
import acts.utils
import time
+import re
from acts import asserts
from acts import utils
@@ -56,7 +57,6 @@
self.packet_capture, '2g', self.test_name)
def teardown_test(self):
- self.verify_mac_no_leakage()
super(WifiP2pSnifferTest, self).teardown_test()
def configure_packet_capture(self):
@@ -74,8 +74,8 @@
pcap_fname = '%s_%s.pcap' % (self.pcap_procs[BAND_2G][1],
BAND_2G.upper())
packets = rdpcap(pcap_fname)
- wutils.verify_mac_not_found_in_pcap(self.dut1_mac, packets)
- wutils.verify_mac_not_found_in_pcap(self.dut2_mac, packets)
+ wutils.verify_mac_not_found_in_pcap(self.dut1, self.dut1_mac, packets)
+ wutils.verify_mac_not_found_in_pcap(self.dut2, self.dut2_mac, packets)
"""Test Cases"""
@test_tracker_info(uuid=" d04e62dc-e1ef-4cea-86e6-39f0dd08fb6b")
@@ -88,6 +88,7 @@
self.log.info("Device discovery")
wp2putils.find_p2p_device(self.dut1, self.dut2)
wp2putils.find_p2p_device(self.dut2, self.dut1)
+ self.verify_mac_no_leakage()
@test_tracker_info(uuid="6a02be84-912d-4b5b-8dfa-fd80d2554c55")
def test_p2p_connect_via_pbc_and_ping_and_reconnect_sniffer(self):
@@ -136,3 +137,6 @@
wp2putils.p2p_disconnect(gc_dut)
wp2putils.check_disconnect(go_dut)
time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ # teardown
+ self.verify_mac_no_leakage()