blob: 3a186b87856516bcead322f0f3622520825298ee [file] [log] [blame]
"""Command-line test runner script for running Bluetooth tests.
This module allows users to initiate Bluetooth test targets and run them against
specified DUTs (devices-under-test) using a simple command line interface.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import base64
from absl import app
from absl import flags
from absl import logging
# Internal import
# Internal import
# Internal import
# Internal import
# Internal import
FLAGS = flags.FLAGS
flags.DEFINE_multi_string('bt_test', None, 'Bluetooth test to run.')
flags.DEFINE_multi_string('bt_dut', None,
'Bluetooth device to allocate for tests.')
# Valid config keys for the --bt_test command line flag.
BT_TEST_CONFIG_KEYS = {'target'}
# Valid config keys for the --bt_dut (device-under-test) command line flag.
BT_DUT_CONFIG_KEYS = {'hardware'}
TEST_FLAGS = ('--notest_loasd --test_output=streamed '
'--test_arg=--param_dut_config=%s')
class Error(Exception):
"""Base class for module exceptions."""
pass
class TestConfigError(Error):
"""Raised when --bt_test config flags are specified incorrectly."""
pass
class DutConfigError(Error):
"""Raised when --bt_dut config flags are specified incorrectly."""
pass
def validate_bt_test_flags(flag_value):
"""Validates the format of specified --bt_test flags.
Args:
flag_value: string, the config flag value for a given --bt_test flag.
Returns:
bool, True if --bt_test flags have been specified correctly.
"""
if not flag_value:
logging.error('No tests specified! Please specify at least one '
'test using the --bt_test flag.')
return False
for test in flag_value:
config_args = test.split(',')
for config_arg in config_args:
if config_arg.split('=')[0] not in BT_TEST_CONFIG_KEYS:
logging.error('--bt_test config key "%s" is invalid!',
config_arg.split('=')[0])
return False
return True
def validate_bt_dut_flags(flag_value):
"""Validates the format of specified --bt_dut flags.
Args:
flag_value: string, the config flag value for a given --bt_dut flag.
Returns:
bool, True if --bt_dut flags have been specified correctly.
"""
if not flag_value:
logging.error('No DUTs specified! Please specify at least one '
'DUT using the --bt_dut flag.')
return False
for dut in flag_value:
config_args = dut.split(',')
for config_arg in config_args:
if config_arg.split('=')[0] not in BT_DUT_CONFIG_KEYS:
logging.error('--bt_dut config key "%s" is invalid!',
config_arg.split('=')[0])
return False
return True
flags.register_validator(
'bt_test', validate_bt_test_flags,
('Invalid --bt_test configuration specified!'
' Valid configuration fields include: %s')
% BT_TEST_CONFIG_KEYS)
flags.register_validator(
'bt_dut', validate_bt_dut_flags,
('Invalid --bt_dut configuration specified!'
' Valid configuration fields include: %s')
% BT_DUT_CONFIG_KEYS)
def parse_flag_value(flag_value):
"""Parses a config flag value string into a dict.
Example input: 'target=//tests:bluetooth_pairing_test'
Example output: {'target': '//tests:bluetooth_pairing_test'}
Args:
flag_value: string, the config flag value for a given flag.
Returns:
dict, A dict object representation of a config flag value.
"""
config_dict = {}
config_args = flag_value.split(',')
for config_arg in config_args:
config_dict[config_arg.split('=')[0]] = config_arg.split('=')[1]
return config_dict
def get_device_type(gateway_stub, dut_config_dict):
"""Determines a device type based on a device query.
Args:
gateway_stub: An RPC2 stub object.
dut_config_dict: dict, A dict of device config args.
Returns:
string, The MobileHarness device type.
Raises:
DutConfigError: If --bt_dut flag(s) are incorrectly specified.
"""
device_query_filter = device_query_pb2.DeviceQueryFilter()
device_query_filter.type_regex.append('AndroidRealDevice')
for dut_config_key in dut_config_dict:
dimension_filter = device_query_filter.dimension_filter.add()
dimension_filter.name = dut_config_key
dimension_filter.value_regex = dut_config_dict[dut_config_key]
request = gateway_service_pb2.QueryDeviceRequest(
device_query_filter=device_query_filter)
response = gateway_stub.QueryDevice(request)
if response.device_query_result.device_info:
return 'AndroidRealDevice'
device_query_filter.ClearField('type_regex')
device_query_filter.type_regex.append('TestbedDevice')
request = gateway_service_pb2.QueryDeviceRequest(
device_query_filter=device_query_filter)
response = gateway_stub.QueryDevice(request)
if response.device_query_result.device_info:
return 'TestbedDevice'
raise DutConfigError('Invalid --bt_dut config specified: %s' %
dut_config_dict)
def generate_dut_configs(gateway_stub):
"""Generates a unicode string specifying the desired DUT configurations.
Args:
gateway_stub: An RPC2 stub object.
Returns:
string, Unicode string specifying DUT configurations.
Raises:
DutConfigError: If --bt_dut flag(s) are incorrectly specified.
"""
dut_list = job_config_pb2.JobConfig().DeviceList()
dut_config_dict_list = [parse_flag_value(value) for value in FLAGS.bt_dut]
for dut_config_dict in dut_config_dict_list:
dut_config_dict['pool'] = 'bluetooth-iop'
dut = job_config_pb2.JobConfig().SubDeviceSpec()
if 'hardware' not in dut_config_dict:
raise DutConfigError('Must specify hardware name for bt_dut: %s' %
dut_config_dict)
dut.type = get_device_type(gateway_stub, dut_config_dict)
for config_key in dut_config_dict:
dut.dimensions.content[config_key] = dut_config_dict[config_key]
dut_list.sub_device_spec.append(dut)
logging.info(base64.b64encode(dut_list.SerializeToString()).decode('utf-8'))
return base64.b64encode(dut_list.SerializeToString()).decode('utf-8')
def generate_blaze_targets(session_config, gateway_stub):
"""Generates and appends blaze test targets to a MobileHarness session.
Args:
session_config: The SessionConfig object to append blaze test targets to.
gateway_stub: An RPC2 stub object.
Raises:
TestConfigError: If --bt_test flag(s) are incorrectly specified.
"""
test_config_dict_list = [parse_flag_value(value) for value in FLAGS.bt_test]
for test_config_dict in test_config_dict_list:
target = setting_pb2.BlazeTarget()
if 'target' not in test_config_dict:
raise TestConfigError('Must specify a target for bt_test: %s' %
test_config_dict)
target.target_name = test_config_dict['target']
target.test_flags = TEST_FLAGS % generate_dut_configs(gateway_stub)
session_config.blaze_target.append(target)
def run_session():
"""Runs a configured test session.
Returns:
A RunSessionResponse object.
"""
session_config = setting_pb2.SessionConfig()
channel = rpcutil.GetNewChannel('blade:mobileharness-gateway')
gateway_stub = gateway_service_pb2.GatewayService.NewRPC2Stub(channel=channel)
generate_blaze_targets(session_config, gateway_stub)
request = gateway_service_pb2.RunSessionRequest()
request.session_config.CopyFrom(session_config)
response = gateway_stub.RunSession(request)
logging.info('Sponge link: %s', response.sponge)
logging.info('Session ID: %s', response.session_id)
return response
def main(argv):
logging.use_python_logging()
del argv
run_session()
if __name__ == '__main__':
flags.mark_flag_as_required('bt_test')
flags.mark_flag_as_required('bt_dut')
app.run(main)