Merge "Fixed a couple Fuchsia Device excemptions and add get_info to attenuator"
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index 4b89de6..38db6a7 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -150,6 +150,7 @@
self.lan = self.interfaces.get_lan_interface()
self.__initial_ap()
self.scapy_install_path = None
+ self.setup_bridge = False
def __initial_ap(self):
"""Initial AP interfaces.
@@ -179,7 +180,10 @@
self.ssh.run(BRIDGE_DOWN)
self.ssh.run(BRIDGE_DEL)
- def start_ap(self, hostapd_config, additional_parameters=None):
+ def start_ap(self,
+ hostapd_config,
+ setup_bridge=False,
+ additional_parameters=None):
"""Starts as an ap using a set of configurations.
This will start an ap on this host. To start an ap the controller
@@ -190,11 +194,13 @@
Args:
hostapd_config: hostapd_config.HostapdConfig, The configurations
- to use when starting up the ap.
+ to use when starting up the ap.
+ setup_bridge: Whether to bridge the LAN interface WLAN interface.
+ Only one WLAN interface can be bridged with the LAN interface
+ and none of the guest networks can be bridged.
additional_parameters: A dictionary of parameters that can sent
- directly into the hostapd config file. This
- can be used for debugging and or adding one
- off parameters into the config.
+ directly into the hostapd config file. This can be used for
+ debugging and or adding one off parameters into the config.
Returns:
An identifier for each ssid being started. These identifiers can be
@@ -203,7 +209,6 @@
Raises:
Error: When the ap can't be brought up.
"""
-
if hostapd_config.frequency < 5000:
interface = self.wlan_2g
subnet = self._AP_2G_SUBNET
@@ -275,7 +280,12 @@
# the server will come up.
interface_ip = ipaddress.ip_interface(
'%s/%s' % (subnet.router, subnet.network.netmask))
- self._ip_cmd.set_ipv4_address(interface, interface_ip)
+ if setup_bridge is True:
+ bridge_interface_name = 'br_lan'
+ self.create_bridge(bridge_interface_name, [interface, self.lan])
+ self._ip_cmd.set_ipv4_address(bridge_interface_name, interface_ip)
+ else:
+ self._ip_cmd.set_ipv4_address(interface, interface_ip)
if hostapd_config.bss_lookup:
# This loop goes through each interface that was setup for
# hostapd and assigns the DHCP scopes that were defined but
@@ -442,6 +452,13 @@
del self._aps[identifier]
if configured_subnets:
self.start_dhcp(subnets=configured_subnets)
+ bridge_interfaces = self.interfaces.get_bridge_interface()
+ if bridge_interfaces:
+ for iface in bridge_interfaces:
+ BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
+ BRIDGE_DEL = 'brctl delbr {}'.format(iface)
+ self.ssh.run(BRIDGE_DOWN)
+ self.ssh.run(BRIDGE_DEL)
def stop_all_aps(self):
"""Stops all running aps on this device."""
diff --git a/acts/framework/acts/controllers/iperf_client.py b/acts/framework/acts/controllers/iperf_client.py
index 40c6993..046025f 100644
--- a/acts/framework/acts/controllers/iperf_client.py
+++ b/acts/framework/acts/controllers/iperf_client.py
@@ -23,6 +23,8 @@
from acts import utils
from acts.controllers.android_device import AndroidDevice
from acts.controllers.iperf_server import _AndroidDeviceBridge
+from acts.controllers.fuchsia_lib.utils_lib import create_ssh_connection
+from acts.controllers.fuchsia_lib.utils_lib import SshResults
from acts.controllers.utils_lib.ssh import connection
from acts.controllers.utils_lib.ssh import settings
from acts.event import event_bus
@@ -51,12 +53,24 @@
if type(c) is dict and 'AndroidDevice' in c:
results.append(IPerfClientOverAdb(c['AndroidDevice']))
elif type(c) is dict and 'ssh_config' in c:
- results.append(IPerfClientOverSsh(c['ssh_config']))
+ results.append(
+ IPerfClientOverSsh(c['ssh_config'],
+ use_paramiko=c.get('use_paramiko'),
+ test_interface=c.get('test_interface')))
else:
results.append(IPerfClient())
return results
+def get_info(iperf_clients):
+ """Placeholder for info about iperf clients
+
+ Returns:
+ None
+ """
+ return None
+
+
def destroy(_):
# No cleanup needed.
pass
@@ -98,7 +112,7 @@
return os.path.join(full_out_dir, out_file_name)
- def start(self, ip, iperf_args, tag, timeout=3600):
+ def start(self, ip, iperf_args, tag, iperf_binary=None, timeout=3600):
"""Starts iperf client, and waits for completion.
Args:
@@ -106,6 +120,8 @@
iperf_args: A string representing arguments to start iperf
client. Eg: iperf_args = "-t 10 -p 5001 -w 512k/-u -b 200M -J".
tag: A string to further identify iperf results file
+ iperf_binary: Location of iperf3 binary. If none, it is assumed the
+ the binary is in the path.
timeout: the maximum amount of time the iperf client can run.
Returns:
@@ -116,8 +132,7 @@
class IPerfClient(IPerfClientBase):
"""Class that handles iperf3 client operations."""
-
- def start(self, ip, iperf_args, tag, timeout=3600):
+ def start(self, ip, iperf_args, tag, iperf_binary=None, timeout=3600):
"""Starts iperf client, and waits for completion.
Args:
@@ -125,12 +140,20 @@
iperf_args: A string representing arguments to start iperf
client. Eg: iperf_args = "-t 10 -p 5001 -w 512k/-u -b 200M -J".
tag: tag to further identify iperf results file
+ iperf_binary: Location of iperf3 binary. If none, it is assumed the
+ the binary is in the path.
timeout: unused.
Returns:
full_out_path: iperf result path.
"""
- iperf_cmd = ['iperf3', '-c', ip] + iperf_args.split(' ')
+ if not iperf_binary:
+ logging.debug('No iperf3 binary specified. '
+ 'Assuming iperf3 is in the path.')
+ iperf_binary = 'iperf3'
+ else:
+ logging.debug('Using iperf3 binary located at %s' % iperf_binary)
+ iperf_cmd = [str(iperf_binary), '-c', ip] + iperf_args.split(' ')
full_out_path = self._get_full_file_path(tag)
with open(full_out_path, 'w') as out_file:
@@ -141,12 +164,21 @@
class IPerfClientOverSsh(IPerfClientBase):
"""Class that handles iperf3 client operations on remote machines."""
-
- def __init__(self, ssh_config):
+ def __init__(self, ssh_config, use_paramiko=False, test_interface=None):
self._ssh_settings = settings.from_config(ssh_config)
- self._ssh_session = connection.SshConnection(self._ssh_settings)
+ self._use_paramiko = use_paramiko
+ if str(self._use_paramiko) == 'True':
+ self._ssh_session = create_ssh_connection(
+ ip_address=ssh_config['host'],
+ ssh_username=ssh_config['user'],
+ ssh_config=ssh_config['ssh_config'])
+ else:
+ self._ssh_session = connection.SshConnection(self._ssh_settings)
- def start(self, ip, iperf_args, tag, timeout=3600):
+ self.hostname = self._ssh_settings.hostname
+ self.test_interface = test_interface
+
+ def start(self, ip, iperf_args, tag, iperf_binary=None, timeout=3600):
"""Starts iperf client, and waits for completion.
Args:
@@ -154,16 +186,34 @@
iperf_args: A string representing arguments to start iperf
client. Eg: iperf_args = "-t 10 -p 5001 -w 512k/-u -b 200M -J".
tag: tag to further identify iperf results file
+ iperf_binary: Location of iperf3 binary. If none, it is assumed the
+ the binary is in the path.
timeout: the maximum amount of time to allow the iperf client to run
Returns:
full_out_path: iperf result path.
"""
- iperf_cmd = 'iperf3 -c {} {}'.format(ip, iperf_args)
+ if not iperf_binary:
+ logging.debug('No iperf3 binary specified. '
+ 'Assuming iperf3 is in the path.')
+ iperf_binary = 'iperf3'
+ else:
+ logging.debug('Using iperf3 binary located at %s' % iperf_binary)
+ iperf_cmd = '{} -c {} {}'.format(iperf_binary, ip, iperf_args)
full_out_path = self._get_full_file_path(tag)
try:
- iperf_process = self._ssh_session.run(iperf_cmd, timeout=timeout)
+ if self._use_paramiko:
+ cmd_result_stdin, cmd_result_stdout, cmd_result_stderr = (
+ self._ssh_session.exec_command(iperf_cmd, timeout=timeout))
+ cmd_result_exit_status = (
+ cmd_result_stdout.channel.recv_exit_status())
+ iperf_process = SshResults(cmd_result_stdin, cmd_result_stdout,
+ cmd_result_stderr,
+ cmd_result_exit_status)
+ else:
+ iperf_process = self._ssh_session.run(iperf_cmd,
+ timeout=timeout)
iperf_output = iperf_process.stdout
with open(full_out_path, 'w') as out_file:
out_file.write(iperf_output)
@@ -175,7 +225,6 @@
class IPerfClientOverAdb(IPerfClientBase):
"""Class that handles iperf3 operations over ADB devices."""
-
def __init__(self, android_device_or_serial):
"""Creates a new IPerfClientOverAdb object.
@@ -195,7 +244,7 @@
return _AndroidDeviceBridge.android_devices()[
self._android_device_or_serial]
- def start(self, ip, iperf_args, tag, timeout=3600):
+ def start(self, ip, iperf_args, tag, iperf_binary=None, timeout=3600):
"""Starts iperf client, and waits for completion.
Args:
@@ -203,6 +252,8 @@
iperf_args: A string representing arguments to start iperf
client. Eg: iperf_args = "-t 10 -p 5001 -w 512k/-u -b 200M -J".
tag: tag to further identify iperf results file
+ iperf_binary: Location of iperf3 binary. If none, it is assumed the
+ the binary is in the path.
timeout: the maximum amount of time to allow the iperf client to run
Returns:
@@ -210,8 +261,19 @@
"""
iperf_output = ''
try:
- iperf_status, iperf_output = self._android_device.run_iperf_client(
- ip, iperf_args, timeout=timeout)
+ if not iperf_binary:
+ logging.debug('No iperf3 binary specified. '
+ 'Assuming iperf3 is in the path.')
+ iperf_binary = 'iperf3'
+ else:
+ logging.debug('Using iperf3 binary located at %s' %
+ iperf_binary)
+ iperf_cmd = '{} -c {} {}'.format(iperf_binary, ip, iperf_args)
+ out = self._android_device.adb.shell(str(iperf_cmd),
+ timeout=timeout)
+ clean_out = out.split('\n')
+ if "error" in clean_out[0].lower():
+ raise Exception('clean_out')
except job.TimeoutError:
logging.warning('TimeoutError: Iperf measurement timed out.')
diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py
index 039f143..3243e70 100755
--- a/acts/framework/acts/controllers/iperf_server.py
+++ b/acts/framework/acts/controllers/iperf_server.py
@@ -17,6 +17,7 @@
import json
import logging
import math
+import IPy
import os
import shlex
import subprocess
@@ -36,6 +37,10 @@
ACTS_CONTROLLER_CONFIG_NAME = 'IPerfServer'
ACTS_CONTROLLER_REFERENCE_NAME = 'iperf_servers'
+KILOBITS = 1024
+MEGABITS = KILOBITS * 1024
+GIGABITS = MEGABITS * 1024
+BITS_IN_BYTE = 8
def create(configs):
@@ -56,7 +61,10 @@
elif type(c) is dict and 'AndroidDevice' in c and 'port' in c:
results.append(IPerfServerOverAdb(c['AndroidDevice'], c['port']))
elif type(c) is dict and 'ssh_config' in c and 'port' in c:
- results.append(IPerfServerOverSsh(c['ssh_config'], c['port']))
+ results.append(
+ IPerfServerOverSsh(c['ssh_config'],
+ c['port'],
+ test_interface=c.get('test_interface')))
else:
raise ValueError(
'Config entry %s in %s is not a valid IPerfServer '
@@ -64,6 +72,15 @@
return results
+def get_info(iperf_servers):
+ """Placeholder for info about iperf servers
+
+ Returns:
+ None
+ """
+ return None
+
+
def destroy(iperf_server_list):
for iperf_server in iperf_server_list:
try:
@@ -73,7 +90,7 @@
class IPerfResult(object):
- def __init__(self, result_path):
+ def __init__(self, result_path, reporting_speed_units='Mbytes'):
"""Loads iperf result from file.
Loads iperf result from JSON formatted server log. File can be accessed
@@ -82,6 +99,7 @@
containing multiple iperf client runs.
"""
# if result_path isn't a path, treat it as JSON
+ self.reporting_speed_units = reporting_speed_units
if not os.path.exists(result_path):
self.result = json.loads(result_path)
else:
@@ -89,8 +107,8 @@
with open(result_path, 'r') as f:
iperf_output = f.readlines()
if '}\n' in iperf_output:
- iperf_output = iperf_output[:iperf_output.index('}\n')
- + 1]
+ iperf_output = iperf_output[:iperf_output.index('}\n'
+ ) + 1]
iperf_string = ''.join(iperf_output)
iperf_string = iperf_string.replace('nan', '0')
self.result = json.loads(iperf_string)
@@ -110,6 +128,33 @@
return ('end' in self.result) and ('sum_received' in self.result['end']
or 'sum' in self.result['end'])
+ def _get_reporting_speed(self, network_speed_in_bits_per_second):
+ """Sets the units for the network speed reporting based on how the
+ object was initiated. Defaults to Megabytes per second. Currently
+ supported, bits per second (bits), kilobits per second (kbits), megabits
+ per second (mbits), gigabits per second (gbits), bytes per second
+ (bytes), kilobits per second (kbytes), megabits per second (mbytes),
+ gigabytes per second (gbytes).
+
+ Args:
+ network_speed_in_bits_per_second: The network speed from iperf in
+ bits per second.
+
+ Returns:
+ The value of the throughput in the appropriate units.
+ """
+ speed_divisor = 1
+ print(self.reporting_speed_units)
+ if self.reporting_speed_units[1:].lower() == 'bytes':
+ speed_divisor = speed_divisor * BITS_IN_BYTE
+ if self.reporting_speed_units[0:1].lower() == 'k':
+ speed_divisor = speed_divisor * KILOBITS
+ if self.reporting_speed_units[0:1].lower() == 'm':
+ speed_divisor = speed_divisor * MEGABITS
+ if self.reporting_speed_units[0:1].lower() == 'g':
+ speed_divisor = speed_divisor * GIGABITS
+ return network_speed_in_bits_per_second / speed_divisor
+
def get_json(self):
"""Returns the raw json output from iPerf."""
return self.result
@@ -131,7 +176,7 @@
if not self._has_data() or 'sum' not in self.result['end']:
return None
bps = self.result['end']['sum']['bits_per_second']
- return bps / 8 / 1024 / 1024
+ return self._get_reporting_speed(bps)
@property
def avg_receive_rate(self):
@@ -143,7 +188,7 @@
if not self._has_data() or 'sum_received' not in self.result['end']:
return None
bps = self.result['end']['sum_received']['bits_per_second']
- return bps / 8 / 1024 / 1024
+ return self._get_reporting_speed(bps)
@property
def avg_send_rate(self):
@@ -155,7 +200,7 @@
if not self._has_data() or 'sum_sent' not in self.result['end']:
return None
bps = self.result['end']['sum_sent']['bits_per_second']
- return bps / 8 / 1024 / 1024
+ return self._get_reporting_speed(bps)
@property
def instantaneous_rates(self):
@@ -167,7 +212,7 @@
if not self._has_data():
return None
intervals = [
- interval['sum']['bits_per_second'] / 8 / 1024 / 1024
+ self._get_reporting_speed(interval['sum']['bits_per_second'])
for interval in self.result['intervals']
]
return intervals
@@ -199,10 +244,11 @@
"""
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]
+ sqd_deviations = ([(rate - avg_rate)**2
+ for rate in instantaneous_rates])
std_dev = math.sqrt(
math.fsum(sqd_deviations) / (len(sqd_deviations) - 1))
return std_dev
@@ -256,7 +302,7 @@
Note: If the directory for the file path does not exist, it will be
created.
- Args:
+ Args:s
tag: The tag passed in to the server run.
"""
out_dir = self.log_path
@@ -297,7 +343,6 @@
class IPerfServer(IPerfServerBase):
"""Class that handles iperf server commands on localhost."""
-
def __init__(self, port=5201):
super().__init__(port)
self._hinted_port = port
@@ -335,8 +380,9 @@
if self._last_opened_file:
self._last_opened_file.close()
self._last_opened_file = open(self._current_log_file, 'w')
- self._iperf_process = subprocess.Popen(
- command, stdout=self._last_opened_file, stderr=subprocess.DEVNULL)
+ self._iperf_process = subprocess.Popen(command,
+ stdout=self._last_opened_file,
+ stderr=subprocess.DEVNULL)
for attempts_left in reversed(range(3)):
try:
self._port = int(
@@ -374,15 +420,22 @@
class IPerfServerOverSsh(IPerfServerBase):
"""Class that handles iperf3 operations on remote machines."""
-
- def __init__(self, ssh_config, port):
+ def __init__(self, ssh_config, port, test_interface=None):
super().__init__(port)
ssh_settings = settings.from_config(ssh_config)
self._ssh_session = connection.SshConnection(ssh_settings)
- self._iperf_command = 'iperf3 -s -J -p {}'.format(self.port)
self._iperf_pid = None
self._current_tag = None
+ self.hostname = ssh_settings.hostname
+ try:
+ # A test interface can only be found if an ip address is specified.
+ # A fully qualified hostname will return None for the
+ # test_interface.
+ self.test_interface = self._get_test_interface_based_on_ip(
+ test_interface)
+ except Exception:
+ self.test_interface = None
@property
def port(self):
@@ -395,20 +448,69 @@
def _get_remote_log_path(self):
return 'iperf_server_port%s.log' % self.port
- def start(self, extra_args='', tag=''):
+ def _get_test_interface_based_on_ip(self, test_interface):
+ """Gets the test interface for a particular IP if the test interface
+ passed in test_interface is None
+
+ Args:
+ test_interface: Either a interface name, ie eth0, or None
+
+ Returns:
+ The name of the test interface.
+ """
+ if test_interface:
+ return test_interface
+ return utils.get_interface_based_on_ip(self._ssh_session,
+ self.hostname)
+
+ 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._ssh_session, interface)
+
+ def renew_test_interface_ip_address(self):
+ """Renews the test interface's IP address. Necessary for changing
+ DHCP scopes during a test.
+ """
+ utils.renew_linux_ip_address(self._ssh_session, self.test_interface)
+
+ def start(self, extra_args='', iperf_binary=None, tag=''):
"""Starts iperf server on specified machine and port.
Args:
extra_args: A string representing extra arguments to start iperf
server with.
+ iperf_binary: Location of iperf3 binary. If none, it is assumed the
+ the binary is in the path.
tag: Appended to log file name to identify logs from different
iperf runs.
"""
if self.started:
return
+ if not iperf_binary:
+ logging.debug('No iperf3 binary specified. '
+ 'Assuming iperf3 is in the path.')
+ iperf_binary = 'iperf3'
+ else:
+ logging.debug('Using iperf3 binary located at %s' % iperf_binary)
+ iperf_command = '{} -s -J -p {}'.format(iperf_binary, self.port)
+
cmd = '{cmd} {extra_flags} > {log_file}'.format(
- cmd=self._iperf_command,
+ cmd=iperf_command,
extra_flags=extra_args,
log_file=self._get_remote_log_path())
@@ -463,8 +565,10 @@
"""
if not _AndroidDeviceBridge._test_class:
return {}
- return {device.serial: device
- for device in _AndroidDeviceBridge._test_class.android_devices}
+ return {
+ device.serial: device
+ for device in _AndroidDeviceBridge._test_class.android_devices
+ }
event_bus.register_subscription(
@@ -474,7 +578,6 @@
class IPerfServerOverAdb(IPerfServerBase):
"""Class that handles iperf3 operations over ADB devices."""
-
def __init__(self, android_device_or_serial, port):
"""Creates a new IPerfServerOverAdb object.
@@ -488,7 +591,6 @@
super().__init__(port)
self._android_device_or_serial = android_device_or_serial
- self._iperf_command = 'iperf3 -s -J -p {}'.format(self.port)
self._iperf_process = None
self._current_tag = ''
@@ -511,21 +613,31 @@
def _get_device_log_path(self):
return '~/data/iperf_server_port%s.log' % self.port
- def start(self, extra_args='', tag=''):
+ def start(self, extra_args='', iperf_binary=None, tag=''):
"""Starts iperf server on an ADB device.
Args:
extra_args: A string representing extra arguments to start iperf
server with.
+ iperf_binary: Location of iperf3 binary. If none, it is assumed the
+ the binary is in the path.
tag: Appended to log file name to identify logs from different
iperf runs.
"""
if self._iperf_process is not None:
return
+ if not iperf_binary:
+ logging.debug('No iperf3 binary specified. '
+ 'Assuming iperf3 is in the path.')
+ iperf_binary = 'iperf3'
+ else:
+ logging.debug('Using iperf3 binary located at %s' % iperf_binary)
+ iperf_command = '{} -s -J -p {}'.format(iperf_binary, self.port)
+
self._iperf_process = self._android_device.adb.shell_nb(
'{cmd} {extra_flags} > {log_file}'.format(
- cmd=self._iperf_command,
+ cmd=iperf_command,
extra_flags=extra_args,
log_file=self._get_device_log_path()))
self._iperf_process_adb_pid = ''
diff --git a/acts/framework/acts/test_utils/abstract_devices/utils_lib/wlan_utils.py b/acts/framework/acts/test_utils/abstract_devices/utils_lib/wlan_utils.py
index 745d35f..d4af3c3 100644
--- a/acts/framework/acts/test_utils/abstract_devices/utils_lib/wlan_utils.py
+++ b/acts/framework/acts/test_utils/abstract_devices/utils_lib/wlan_utils.py
@@ -25,8 +25,8 @@
Args: Args match setup_ap_and_associate
"""
- asserts.assert_true(
- setup_ap_and_associate(*args, **kwargs), 'Failed to associate.')
+ asserts.assert_true(setup_ap_and_associate(*args, **kwargs),
+ 'Failed to associate.')
asserts.explicit_pass('Successfully associated.')
@@ -49,7 +49,8 @@
check_connectivity=False,
n_capabilities=None,
ac_capabilities=None,
- vht_bandwidth=None):
+ vht_bandwidth=None,
+ setup_bridge=False):
"""Sets up the AP and associates a client.
Args:
@@ -73,14 +74,13 @@
beacon_interval, dtim_period, frag_threshold, rts_threshold,
force_wmm, hidden, security, additional_ap_parameters, password,
check_connectivity, n_capabilities, ac_capabilities,
- vht_bandwidth)
+ vht_bandwidth, setup_bridge)
- return associate(
- client,
- ssid,
- password,
- check_connectivity=check_connectivity,
- hidden=hidden)
+ return associate(client,
+ ssid,
+ password,
+ check_connectivity=check_connectivity,
+ hidden=hidden)
def setup_ap(access_point,
@@ -101,7 +101,8 @@
check_connectivity=False,
n_capabilities=None,
ac_capabilities=None,
- vht_bandwidth=None):
+ vht_bandwidth=None,
+ setup_bridge=False):
"""Sets up the AP.
Args:
@@ -120,27 +121,27 @@
password: Password to connect to WLAN if necessary.
check_connectivity: Whether to check for internet connectivity.
"""
- ap = hostapd_ap_preset.create_ap_preset(
- profile_name=profile_name,
- iface_wlan_2g=access_point.wlan_2g,
- iface_wlan_5g=access_point.wlan_5g,
- channel=channel,
- ssid=ssid,
- mode=mode,
- short_preamble=preamble,
- beacon_interval=beacon_interval,
- dtim_period=dtim_period,
- frag_threshold=frag_threshold,
- rts_threshold=rts_threshold,
- force_wmm=force_wmm,
- hidden=hidden,
- bss_settings=[],
- security=security,
- n_capabilities=n_capabilities,
- ac_capabilities=ac_capabilities,
- vht_bandwidth=vht_bandwidth)
- access_point.start_ap(
- hostapd_config=ap, additional_parameters=additional_ap_parameters)
+ ap = hostapd_ap_preset.create_ap_preset(profile_name=profile_name,
+ iface_wlan_2g=access_point.wlan_2g,
+ iface_wlan_5g=access_point.wlan_5g,
+ channel=channel,
+ ssid=ssid,
+ mode=mode,
+ short_preamble=preamble,
+ beacon_interval=beacon_interval,
+ dtim_period=dtim_period,
+ frag_threshold=frag_threshold,
+ rts_threshold=rts_threshold,
+ force_wmm=force_wmm,
+ hidden=hidden,
+ bss_settings=[],
+ security=security,
+ n_capabilities=n_capabilities,
+ ac_capabilities=ac_capabilities,
+ vht_bandwidth=vht_bandwidth)
+ access_point.start_ap(hostapd_config=ap,
+ setup_bridge=setup_bridge,
+ additional_parameters=additional_ap_parameters)
def associate(client,
@@ -157,8 +158,10 @@
check_connectivity: Whether to check internet connectivity.
hidden: If the WLAN is hidden or not.
"""
- return client.associate(
- ssid, password, check_connectivity=check_connectivity, hidden=hidden)
+ return client.associate(ssid,
+ password,
+ check_connectivity=check_connectivity,
+ hidden=hidden)
def status(client):
diff --git a/acts/framework/acts/test_utils/coex/audio_capture.py b/acts/framework/acts/test_utils/coex/audio_capture.py
deleted file mode 100644
index bb29dcc..0000000
--- a/acts/framework/acts/test_utils/coex/audio_capture.py
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018 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 argparse
-import json
-import logging
-import os
-import pyaudio
-import wave
-
-RECORD_FILE_TEMPLATE = 'recorded_audio_%s.wav'
-
-
-class DeviceNotFound(Exception):
- """Raises exception if audio capture device is not found."""
-
-# TODO: (@sairamganesh) This class will be deprecated for
-# ../acts/test_utils/coex/audio_capture_device.py
-
-
-class AudioCapture:
-
- def __init__(self, test_params, path):
- """Creates object to pyaudio and defines audio parameters.
-
- Args:
- test_params: Audio parameters fetched from config.
- path: Result path.
- """
- self.audio = pyaudio.PyAudio()
- self.audio_format = pyaudio.paInt16
- self.audio_params = test_params
- self.channels = self.audio_params["channel"]
- self.chunk = self.audio_params["chunk"]
- self.sample_rate = self.audio_params["sample_rate"]
- self.file_counter = 0
- self.__input_device = None
- self.record_file_template = os.path.join(path, RECORD_FILE_TEMPLATE)
- if not self.audio_params["ssh_config"]:
- self.__input_device = self.__get_input_device()
-
- @property
- def name(self):
- try:
- return self.audio_params["ssh_config"]["host"]
- except KeyError:
- return self.__input_device["name"]
-
- def __get_input_device(self):
- """Checks for the audio capture device."""
- if self.__input_device is None:
- for i in range(self.audio.get_device_count()):
- device_info = self.audio.get_device_info_by_index(i)
- logging.info("Device Information {}".format(device_info))
- if self.audio_params['input_device'] in device_info['name']:
- self.__input_device = device_info
- break
- else:
- logging.error("Audio Capture device {} not found.".format(
- self.audio_params["input_device"]))
- raise DeviceNotFound("Audio Capture Input device not found")
- return self.__input_device
-
- def capture_and_store_audio(self, trim_beginning=0, trim_end=0):
- """Records the A2DP streaming.
-
- Args:
- trim_beginning: how many seconds to trim from the beginning
- trim_end: how many seconds to trim from the end
- """
- if self.audio_params['ssh_config']:
- self.__input_device = self.__get_input_device()
- stream = self.audio.open(
- format=self.audio_format,
- channels=self.channels,
- rate=self.sample_rate,
- input=True,
- frames_per_buffer=self.chunk,
- input_device_index=self.__input_device['index'])
- frames = []
- b_chunks = trim_beginning * (self.sample_rate // self.chunk)
- e_chunks = trim_end * (self.sample_rate // self.chunk)
- total_chunks = self.sample_rate // self.chunk * self.audio_params[
- 'record_duration']
- for i in range(total_chunks):
- try:
- data = stream.read(self.chunk, exception_on_overflow=False)
- except IOError as ex:
- logging.error("Cannot record audio :{}".format(ex))
- return False
- if b_chunks <= i < total_chunks - e_chunks:
- frames.append(data)
-
- stream.stop_stream()
- stream.close()
- status = self.write_record_file(frames)
- return status
-
- def last_fileno(self):
- return self.next_fileno() - 1
-
- def next_fileno(self):
- counter = 0
- while os.path.exists(self.record_file_template % counter):
- counter += 1
- return counter
-
- def write_record_file(self, frames):
- """Writes the recorded audio into the file.
-
- Args:
- frames: Recorded audio frames.
- """
- file_name = self.record_file_template % self.next_fileno()
- logging.info('writing to %s' % file_name)
- wf = wave.open(file_name, 'wb')
- wf.setnchannels(self.channels)
- wf.setsampwidth(self.audio.get_sample_size(self.audio_format))
- wf.setframerate(self.sample_rate)
- wf.writeframes(b''.join(frames))
- wf.close()
- return True
-
- def terminate_audio(self):
- """Terminates the pulse audio instance."""
- self.audio.terminate()
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '-p',
- '--path',
- type=str,
- help="Contains path where the recorded files to be stored")
- parser.add_argument(
- '-t',
- '--test_params',
- type=json.loads,
- help="Contains sample rate, channels,"
- " chunk and device index for recording.")
- args = parser.parse_args()
- audio = AudioCapture(args.test_params, args.path)
- audio.capture_and_store_audio(args.test_params['trim_beginning'],
- args.test_params['trim_end'])
- audio.terminate_audio()
diff --git a/acts/framework/acts/test_utils/coex/audio_capture_device.py b/acts/framework/acts/test_utils/coex/audio_capture_device.py
index 924bf4a..f99f6a8 100644
--- a/acts/framework/acts/test_utils/coex/audio_capture_device.py
+++ b/acts/framework/acts/test_utils/coex/audio_capture_device.py
@@ -57,6 +57,21 @@
def last_fileno(self):
return self.next_fileno - 1
+ @property
+ def get_last_record_duration_millis(self):
+ """Get duration of most recently recorded file.
+
+ Returns:
+ duration (float): duration of recorded file in milliseconds.
+ """
+ latest_file_path = self.wave_file % self.last_fileno
+ print (latest_file_path)
+ with wave.open(latest_file_path, 'r') as f:
+ frames = f.getnframes()
+ rate = f.getframerate()
+ duration = (frames / float(rate)) * 1000
+ return duration
+
def write_record_file(self, audio_params, frames):
"""Writes the recorded audio into the file.
diff --git a/acts/framework/acts/test_utils/coex/audio_test_utils.py b/acts/framework/acts/test_utils/coex/audio_test_utils.py
index b514712..02b99ca 100644
--- a/acts/framework/acts/test_utils/coex/audio_test_utils.py
+++ b/acts/framework/acts/test_utils/coex/audio_test_utils.py
@@ -16,16 +16,12 @@
import logging
import os
-import wave
+from acts.test_utils.coex.audio_capture_device import AudioCaptureBase
from acts.test_utils.coex.audio_capture_device import CaptureAudioOverAdb
from acts.test_utils.coex.audio_capture_device import CaptureAudioOverLocal
-from acts.controllers.utils_lib.ssh import connection
-from acts.controllers.utils_lib.ssh import settings
from acts.test_utils.audio_analysis_lib import audio_analysis
from acts.test_utils.audio_analysis_lib.check_quality import quality_analysis
-from acts.test_utils.coex.audio_capture import AudioCapture
-from acts.test_utils.coex.audio_capture import RECORD_FILE_TEMPLATE
ANOMALY_DETECTION_BLOCK_SIZE = audio_analysis.ANOMALY_DETECTION_BLOCK_SIZE
ANOMALY_GROUPING_TOLERANCE = audio_analysis.ANOMALY_GROUPING_TOLERANCE
@@ -67,58 +63,18 @@
class FileNotFound(Exception):
"""Raises Exception if file is not present"""
-# TODO @sairamganesh Rename this class to AudioCaptureResult and
-# remove duplicates which are in ../test_utils/coex/audio_capture_device.py.
+class AudioCaptureResult(AudioCaptureBase):
-class SshAudioCapture(AudioCapture):
-
- def __init__(self, test_params, path):
- super(SshAudioCapture, self).__init__(test_params, path)
- self.remote_path = path
- self.ssh_session = None
-
- def capture_audio(self, trim_beginning=0, trim_end=0):
- """Captures audio and store results.
+ def __init__(self, path):
+ """Initializes Audio Capture Result class.
Args:
- trim_beginning: To trim audio at the start in seconds.
- trim_end: To trim audio at the end in seconds.
-
- Returns:
- Returns exit status of ssh connection.
+ path: Path of audio capture result.
"""
- if not trim_beginning:
- trim_beginning = self.audio_params.get('trim_beginning', 0)
- if not trim_end:
- trim_end = self.audio_params.get('trim_end', 0)
- if self.audio_params["ssh_config"]:
- ssh_settings = settings.from_config(
- self.audio_params["ssh_config"])
- self.ssh_session = connection.SshConnection(ssh_settings)
- cur_path = os.path.dirname(os.path.realpath(__file__))
- local_path = os.path.join(cur_path, "audio_capture.py")
- self.ssh_session.send_file(local_path,
- self.audio_params["dest_path"])
- path = self.audio_params["dest_path"]
- test_params = str(self.audio_params).replace("\'", "\"")
- self.cmd = "python3 audio_capture.py -p '{}' -t '{}'".format(
- path, test_params)
- job_result = self.ssh_session.run(self.cmd)
- logging.debug("Job Result {}".format(job_result.stdout))
- self.ssh_session.pull_file(
- self.remote_path, os.path.join(
- self.audio_params["dest_path"], "*.wav"))
- return bool(not job_result.exit_status)
- else:
- return self.capture_and_store_audio(trim_beginning, trim_end)
-
- def terminate_and_store_audio_results(self):
- """Terminates audio and stores audio files."""
- if self.audio_params["ssh_config"]:
- self.ssh_session.run('rm *.wav', ignore_status=True)
- else:
- self.terminate_audio()
+ super().__init__()
+ self.path = path
+ self.analysis_path = os.path.join(self.log_path, ANALYSIS_FILE_TEMPLATE)
def THDN(self, win_size=None, step_size=None, q=1, freq=None):
"""Calculate THD+N value for most recently recorded file.
@@ -137,13 +93,12 @@
channel_results (list): THD+N value for each channel's signal.
List index corresponds to channel index.
"""
- latest_file_path = self.record_file_template % self.last_fileno()
if not (win_size and step_size):
- return audio_analysis.get_file_THDN(filename=latest_file_path,
+ return audio_analysis.get_file_THDN(filename=self.path,
q=q,
freq=freq)
else:
- return audio_analysis.get_file_max_THDN(filename=latest_file_path,
+ return audio_analysis.get_file_max_THDN(filename=self.path,
step_size=step_size,
window_size=win_size,
q=q,
@@ -172,28 +127,22 @@
channel_results (list): anomaly durations for each channel's signal.
List index corresponds to channel index.
"""
- latest_file_path = self.record_file_template % self.last_fileno()
return audio_analysis.get_file_anomaly_durations(
- filename=latest_file_path,
+ filename=self.path,
freq=freq,
block_size=block_size,
threshold=threshold,
tolerance=tolerance)
- def get_last_record_duration_millis(self):
- """Get duration of most recently recorded file.
+ @property
+ def analysis_fileno(self):
+ """Returns the file number to dump audio analysis results."""
+ counter = 0
+ while os.path.exists(self.analysis_path % counter):
+ counter += 1
+ return counter
- Returns:
- duration (float): duration of recorded file in milliseconds.
- """
- latest_file_path = self.record_file_template % self.last_fileno()
- with wave.open(latest_file_path, 'r') as f:
- frames = f.getnframes()
- rate = f.getframerate()
- duration = (frames / float(rate)) * 1000
- return duration
-
- def audio_quality_analysis(self, path):
+ def audio_quality_analysis(self, audio_params):
"""Measures audio quality based on the audio file given as input.
Args:
@@ -202,19 +151,16 @@
Returns:
analysis_path on success.
"""
- dest_file_path = os.path.join(path,
- RECORD_FILE_TEMPLATE % self.last_fileno())
- analysis_path = os.path.join(path,
- ANALYSIS_FILE_TEMPLATE % self.last_fileno())
- if not os.path.exists(dest_file_path):
+ analysis_path = self.analysis_path % self.analysis_fileno
+ if not os.path.exists(self.path):
raise FileNotFound("Recorded file not found")
try:
quality_analysis(
- filename=dest_file_path,
+ filename=self.path,
output_file=analysis_path,
bit_width=bits_per_sample,
- rate=self.audio_params["sample_rate"],
- channel=self.audio_params["channel"],
+ rate=audio_params["sample_rate"],
+ channel=audio_params["channel"],
spectral_only=False)
except Exception as err:
logging.exception("Failed to analyze raw audio: %s" % err)
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index 99c961a..b42ea21 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -19,6 +19,7 @@
import copy
import datetime
import functools
+import IPy
import json
import logging
import os
@@ -1381,3 +1382,97 @@
def ascii_string(uc_string):
"""Converts unicode string to ascii"""
return str(uc_string).encode('ASCII')
+
+
+def get_interface_ip_addresses(comm_channel, interface):
+ """Gets all of the ip addresses, ipv4 and ipv6, associated with a
+ particular interface name.
+
+ Args:
+ comm_channel: How to send commands to a device. Can be ssh, adb serial,
+ etc. Must have the run function implemented.
+ 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
+ """
+ ipv4_private_local_addresses = []
+ ipv4_public_addresses = []
+ ipv6_link_local_addresses = []
+ ipv6_private_local_addresses = []
+ ipv6_public_addresses = []
+ 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':
+ 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())
+ return {
+ 'ipv4_private': ipv4_private_local_addresses,
+ 'ipv4_public': ipv4_public_addresses,
+ 'ipv6_link_local': ipv6_link_local_addresses,
+ 'ipv6_private_local': ipv6_private_local_addresses,
+ 'ipv6_public': ipv6_public_addresses
+ }
+
+
+def get_interface_based_on_ip(comm_channel, desired_ip_address):
+ """Gets the interface for a particular IP
+
+ Args:
+ comm_channel: How to send commands to a device. Can be ssh, adb serial,
+ etc. Must have the run function implemented.
+ desired_ip_address: The IP address that is being looked for on a device.
+
+ Returns:
+ The name of the test interface.
+ """
+
+ desired_ip_address = desired_ip_address.split('%', 1)[0]
+ 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]
+ return None
+
+
+def renew_linux_ip_address(comm_channel, interface):
+ 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 f2d84c0..59210f9 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -40,6 +40,7 @@
'xlsxwriter',
'mobly',
'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/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py b/acts/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
index 00cb735..c5c1879 100644
--- a/acts/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
+++ b/acts/tests/google/coex/performance_tests/CoexBasicPerformanceTest.py
@@ -14,153 +14,60 @@
# License for the specific language governing permissions and limitations under
# the License.
+import itertools
+
+from acts.test_utils.bt.bt_test_utils import enable_bluetooth
from acts.test_utils.coex.CoexPerformanceBaseTest import CoexPerformanceBaseTest
from acts.test_utils.coex.coex_test_utils import perform_classic_discovery
class CoexBasicPerformanceTest(CoexPerformanceBaseTest):
- def setup_class(self):
- super().setup_class()
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ req_params = [
+ # A dict containing:
+ # protocol: A list containing TCP/UDP. Ex: protocol: ['tcp'].
+ # stream: A list containing ul/dl. Ex: stream: ['ul']
+ 'standalone_params'
+ ]
+ self.unpack_userparams(req_params)
+ self.tests = self.generated_test_cases(['bt_on', 'perform_discovery'])
- def run_iperf_and_perform_discovery(self):
- """Starts iperf client on host machine and bluetooth discovery
+ def perform_discovery(self):
+ """ Starts iperf client on host machine and bluetooth discovery
simultaneously.
Returns:
True if successful, False otherwise.
"""
tasks = [(perform_classic_discovery,
- (self.pri_ad, self.iperf["duration"], self.json_file,
- self.dev_list)), (self.run_iperf_and_get_result, ())]
- if not self.set_attenuation_and_run_iperf(tasks):
- return False
- return self.teardown_result()
+ (self.pri_ad, self.iperf['duration'], self.json_file,
+ self.dev_list)),
+ (self.run_iperf_and_get_result, ())]
+ return self.set_attenuation_and_run_iperf(tasks)
- def test_performance_with_bt_on_tcp_ul(self):
- """Check throughput when bluetooth on.
-
- This test is to start TCP-Uplink traffic between host machine and
- android device and check the throughput when bluetooth is on.
-
- Steps:
- 1. Start TCP-uplink traffic when bluetooth is on.
-
- Test Id: Bt_CoEx_kpi_005
- """
- self.set_attenuation_and_run_iperf()
- return self.teardown_result()
-
- def test_performance_with_bt_on_tcp_dl(self):
- """Check throughput when bluetooth on.
-
- This test is to start TCP-downlink traffic between host machine and
- android device and check the throughput when bluetooth is on.
-
- Steps:
- 1. Start TCP-downlink traffic when bluetooth is on.
-
- Test Id: Bt_CoEx_kpi_006
- """
- self.set_attenuation_and_run_iperf()
- return self.teardown_result()
-
- def test_performance_with_bt_on_udp_ul(self):
- """Check throughput when bluetooth on.
-
- This test is to start UDP-uplink traffic between host machine and
- android device and check the throughput when bluetooth is on.
-
- Steps:
- 1. Start UDP-uplink traffic when bluetooth is on.
-
- Test Id: Bt_CoEx_kpi_007
- """
- self.set_attenuation_and_run_iperf()
- return self.teardown_result()
-
- def test_performance_with_bt_on_udp_dl(self):
- """Check throughput when bluetooth on.
-
- This test is to start UDP-downlink traffic between host machine and
- android device and check the throughput when bluetooth is on.
-
- Steps:
- 1. Start UDP-downlink traffic when bluetooth is on.
-
- Test Id: Bt_CoEx_kpi_008
- """
- self.set_attenuation_and_run_iperf()
- return self.teardown_result()
-
- def test_performance_with_bluetooth_discovery_tcp_ul(self):
- """Check throughput when bluetooth discovery is ongoing.
-
- This test is to start TCP-uplink traffic between host machine and
- android device and bluetooth discovery and checks throughput.
-
- Steps:
- 1. Start TCP-uplink traffic and bluetooth discovery parallelly.
+ def bt_on(self):
+ """ Turns on bluetooth and runs iperf.
Returns:
- True if successful, False otherwise.
-
- Test Id: Bt_CoEx_kpi_009
+ True on success, False otherwise.
"""
- if not self.run_iperf_and_perform_discovery():
+ if not enable_bluetooth(self.pri_ad.droid, self.pri_ad.ed):
return False
- return True
+ return self.set_attenuation_and_run_iperf()
- def test_performance_with_bluetooth_discovery_tcp_dl(self):
- """Check throughput when bluetooth discovery is ongoing.
+ def generated_test_cases(self, test_types):
+ """ Auto generates tests for basic coex tests. """
+ test_cases = []
+ for protocol, stream, test_type in itertools.product(
+ self.standalone_params['protocol'],
+ self.standalone_params['stream'], test_types):
- This test is to start TCP-downlink traffic between host machine and
- android device and bluetooth discovery and checks throughput.
+ test_name = 'test_performance_with_{}_{}_{}'.format(
+ test_type, protocol, stream)
- Steps:
- 1. Start TCP-downlink traffic and bluetooth discovery parallelly.
-
- Returns:
- True if successful, False otherwise.
-
- Test Id: Bt_CoEx_kpi_010
- """
- if not self.run_iperf_and_perform_discovery():
- return False
- return True
-
- def test_performance_with_bluetooth_discovery_udp_ul(self):
- """Check throughput when bluetooth discovery is ongoing.
-
- This test is to start UDP-uplink traffic between host machine and
- android device and bluetooth discovery and checks throughput.
-
- Steps:
- 1. Start UDP-uplink traffic and bluetooth discovery parallelly.
-
- Returns:
- True if successful, False otherwise.
-
- Test Id: Bt_CoEx_kpi_011
- """
- if not self.run_iperf_and_perform_discovery():
- return False
- return True
-
- def test_performance_with_bluetooth_discovery_udp_dl(self):
- """Check throughput when bluetooth discovery is ongoing.
-
- This test is to start UDP-downlink traffic between host machine and
- android device and bluetooth discovery and checks throughput.
-
- Steps:
- 1. Start UDP-downlink traffic and bluetooth discovery parallelly.
-
- Returns:
- True if successful, False otherwise.
-
- Test Id: Bt_CoEx_kpi_012
- """
- if not self.run_iperf_and_perform_discovery():
- return False
- return True
+ test_function = getattr(self, test_type)
+ setattr(self, test_name, test_function)
+ test_cases.append(test_name)
+ return test_cases
diff --git a/acts/tests/google/net/DataCostTest.py b/acts/tests/google/net/DataCostTest.py
index 2b7bd50..617626f 100644
--- a/acts/tests/google/net/DataCostTest.py
+++ b/acts/tests/google/net/DataCostTest.py
@@ -65,6 +65,34 @@
""" Helper functions """
+ def _clear_netstats(self, ad):
+ """ Clear netstats stored on device
+
+ Args:
+ ad: Android device object
+ """
+ ad.log.info("Clear netstats record.")
+ ad.adb.shell("rm /data/system/netstats/*")
+ asserts.assert_equal("", ad.adb.shell("ls /data/system/netstats/"),
+ "Fail to clear netstats.")
+ ad.reboot()
+ time.sleep(10)
+ self._check_multipath_preference_from_dumpsys(ad)
+
+ def _check_multipath_preference_from_dumpsys(self, ad):
+ """ Check cell multipath_preference from dumpsys
+
+ Args:
+ ad: Android device object
+ """
+ out = ad.adb.shell("dumpsys connectivity | grep budget")
+ asserts.assert_true(out, "Fail to get status from dumpsys.")
+ ad.log.info("MultipathPolicyTracker: %s" % out)
+ asserts.assert_true(
+ "HANDOVER|RELIABILITY" in out,
+ "Cell multipath preference should be HANDOVER|RELIABILITY."
+ )
+
def _get_total_data_usage_for_device(self, ad, conn_type, sub_id):
""" Get total data usage in MB for device
@@ -138,6 +166,8 @@
"""
# set vars
ad = self.android_devices[0]
+ self._clear_netstats(ad)
+
sub_id = str(ad.droid.telephonyGetSubscriberId())
cell_network = ad.droid.connectivityGetActiveNetwork()
self.log.info("cell network %s" % cell_network)
@@ -182,6 +212,8 @@
"""
# set vars
ad = self.android_devices[1]
+ self._clear_netstats(ad)
+
cell_network = ad.droid.connectivityGetActiveNetwork()
self.log.info("cell network %s" % cell_network)
wutils.wifi_connect(ad, self.wifi_network)