From edb4ee24785b47ec4ea565ed9aae25ebe7f89cdd Mon Sep 17 00:00:00 2001 From: Jizheng Chu Date: Wed, 23 Jun 2021 13:40:12 -0700 Subject: Port GD Cert tests to Blueberry/Mobly Bug: 188852781 Test: Build artifact and run GD Cert tests in venv with Mobly installed, see detail steps in go/port-gd-cert Change-Id: I5e9e69ed281b92e980a32546d107dc922a0add33 --- blueberry/tests/gd/cert/cert_self_test.py | 3 +- blueberry/tests/gd/cert/context.py | 164 +++++++ blueberry/tests/gd/cert/gd_base_test.py | 159 +++++++ blueberry/tests/gd/cert/gd_device.py | 491 +++++++++++++++++++++ blueberry/tests/gd/cert/metadata.py | 79 ++++ blueberry/tests/gd/cert/pts_base_test.py | 29 ++ blueberry/tests/gd/cert/test_decorators.py | 267 +++++++++++ blueberry/tests/gd/cert/tracelogger.py | 63 +++ blueberry/tests/gd/hal/simple_hal_test.py | 39 ++ blueberry/tests/gd/hci/acl_manager_test.py | 50 +++ blueberry/tests/gd/hci/controller_test.py | 35 ++ blueberry/tests/gd/hci/direct_hci_test.py | 37 ++ blueberry/tests/gd/hci/le_acl_manager_test.py | 37 ++ .../tests/gd/hci/le_advertising_manager_test.py | 37 ++ blueberry/tests/gd/hci/le_scanning_manager_test.py | 29 ++ .../tests/gd/hci/le_scanning_with_security_test.py | 29 ++ blueberry/tests/gd/host_config.yaml | 58 +-- blueberry/tests/gd/iso/le_iso_test.py | 36 ++ .../gd/l2cap/classic/l2cap_performance_test.py | 36 ++ blueberry/tests/gd/l2cap/classic/l2cap_test.py | 36 ++ blueberry/tests/gd/l2cap/le/dual_l2cap_test.py | 36 ++ blueberry/tests/gd/l2cap/le/le_l2cap_test.py | 36 ++ blueberry/tests/gd/neighbor/neighbor_test.py | 37 ++ blueberry/tests/gd/security/le_security_test.py | 36 ++ blueberry/tests/gd/security/security_test.py | 36 ++ blueberry/tests/gd/shim/shim_test.py | 32 ++ blueberry/tests/gd/shim/stack_test.py | 29 ++ 27 files changed, 1926 insertions(+), 30 deletions(-) create mode 100644 blueberry/tests/gd/cert/context.py create mode 100644 blueberry/tests/gd/cert/gd_base_test.py create mode 100644 blueberry/tests/gd/cert/gd_device.py create mode 100644 blueberry/tests/gd/cert/metadata.py create mode 100644 blueberry/tests/gd/cert/pts_base_test.py create mode 100644 blueberry/tests/gd/cert/test_decorators.py create mode 100644 blueberry/tests/gd/cert/tracelogger.py create mode 100644 blueberry/tests/gd/hal/simple_hal_test.py create mode 100644 blueberry/tests/gd/hci/acl_manager_test.py create mode 100644 blueberry/tests/gd/hci/controller_test.py create mode 100644 blueberry/tests/gd/hci/direct_hci_test.py create mode 100644 blueberry/tests/gd/hci/le_acl_manager_test.py create mode 100644 blueberry/tests/gd/hci/le_advertising_manager_test.py create mode 100644 blueberry/tests/gd/hci/le_scanning_manager_test.py create mode 100644 blueberry/tests/gd/hci/le_scanning_with_security_test.py create mode 100644 blueberry/tests/gd/iso/le_iso_test.py create mode 100644 blueberry/tests/gd/l2cap/classic/l2cap_performance_test.py create mode 100644 blueberry/tests/gd/l2cap/classic/l2cap_test.py create mode 100644 blueberry/tests/gd/l2cap/le/dual_l2cap_test.py create mode 100644 blueberry/tests/gd/l2cap/le/le_l2cap_test.py create mode 100644 blueberry/tests/gd/neighbor/neighbor_test.py create mode 100644 blueberry/tests/gd/security/le_security_test.py create mode 100644 blueberry/tests/gd/security/security_test.py create mode 100644 blueberry/tests/gd/shim/shim_test.py create mode 100644 blueberry/tests/gd/shim/stack_test.py (limited to 'blueberry') diff --git a/blueberry/tests/gd/cert/cert_self_test.py b/blueberry/tests/gd/cert/cert_self_test.py index edfe10bcf..a014a79a5 100644 --- a/blueberry/tests/gd/cert/cert_self_test.py +++ b/blueberry/tests/gd/cert/cert_self_test.py @@ -14,12 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from blueberry.tests.gd.cert.metadata import metadata + from mobly import asserts from mobly import signals from mobly import test_runner from mobly import base_test -from cert.metadata import metadata from cert.cert_self_test_lib import * diff --git a/blueberry/tests/gd/cert/context.py b/blueberry/tests/gd/cert/context.py new file mode 100644 index 000000000..bad984205 --- /dev/null +++ b/blueberry/tests/gd/cert/context.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# +# Copyright 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 enum +import logging +import os + + +class ContextLevel(enum.IntEnum): + ROOT = 0 + TESTCLASS = 1 + TESTCASE = 2 + + +def get_current_context(depth=None): + """Get the current test context at the specified depth. + Pulls the most recently created context, with a level at or below the given + depth, from the _contexts stack. + + Args: + depth: The desired context level. For example, the TESTCLASS level would + yield the current test class context, even if the test is currently + within a test case. + + Returns: An instance of TestContext. + """ + if depth is None: + return _contexts[-1] + return _contexts[min(depth, len(_contexts) - 1)] + + +class TestContext(object): + """An object representing the current context in which a test is executing. + + The context encodes the current state of the test runner with respect to a + particular scenario in which code is being executed. For example, if some + code is being executed as part of a test case, then the context should + encode information about that test case such as its name or enclosing + class. + + The subcontext specifies a relative path in which certain outputs, + e.g. logcat, should be kept for the given context. + + The full output path is given by + //. + + Attributes: + _base_output_paths: a dictionary mapping a logger's name to its base + output path + _subcontexts: a dictionary mapping a logger's name to its + subcontext-level output directory + """ + + _base_output_paths = {} + _subcontexts = {} + + def get_base_output_path(self, log_name=None): + """Gets the base output path for this logger. + + The base output path is interpreted as the reporting root for the + entire test runner. + + If a path has been added with add_base_output_path, it is returned. + Otherwise, a default is determined by _get_default_base_output_path(). + + Args: + log_name: The name of the logger. + + Returns: + The output path. + """ + if log_name in self._base_output_paths: + return self._base_output_paths[log_name] + return self._get_default_base_output_path() + + def get_subcontext(self, log_name=None): + """Gets the subcontext for this logger. + + The subcontext is interpreted as the directory, relative to the + context-level path, where all outputs of the given logger are stored. + + If a path has been added with add_subcontext, it is returned. + Otherwise, the empty string is returned. + + Args: + log_name: The name of the logger. + + Returns: + The output path. + """ + return self._subcontexts.get(log_name, '') + + def get_full_output_path(self, log_name=None): + """Gets the full output path for this context. + + The full path represents the absolute path to the output directory, + as given by // + + Args: + log_name: The name of the logger. Used to specify the base output + path and the subcontext. + + Returns: + The output path. + """ + + path = os.path.join( + self.get_base_output_path(log_name), self._get_default_context_dir(), self.get_subcontext(log_name)) + os.makedirs(path, exist_ok=True) + return path + + def _get_default_base_output_path(self): + """Gets the default base output path. + + This will attempt to use logging path set up in the global + logger. + + Returns: + The logging path. + + Raises: + EnvironmentError: If logger has not been initialized. + """ + try: + return logging.log_path + except AttributeError as e: + raise EnvironmentError('The Mobly logger has not been set up and' + ' "base_output_path" has not been set.') from e + + def _get_default_context_dir(self): + """Gets the default output directory for this context.""" + raise NotImplementedError() + + +class RootContext(TestContext): + """A TestContext that represents a test run.""" + + @property + def identifier(self): + return 'root' + + def _get_default_context_dir(self): + """Gets the default output directory for this context. + + Logs at the root level context are placed directly in the base level + directory, so no context-level path exists.""" + return '' + + +# stack for keeping track of the current test context +_contexts = [RootContext()] diff --git a/blueberry/tests/gd/cert/gd_base_test.py b/blueberry/tests/gd/cert/gd_base_test.py new file mode 100644 index 000000000..c8674b31f --- /dev/null +++ b/blueberry/tests/gd/cert/gd_base_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 importlib +import logging +import traceback + +from functools import wraps +from grpc import RpcError + +from cert.gd_base_test_lib import setup_class_core +from cert.gd_base_test_lib import teardown_class_core +from cert.gd_base_test_lib import setup_test_core +from cert.gd_base_test_lib import teardown_test_core +from cert.gd_base_test_lib import dump_crashes_core + +from blueberry.tests.gd.cert.context import get_current_context +from blueberry.tests.gd.cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME as CONTROLLER_CONFIG_NAME +from blueberry.tests.gd.cert.tracelogger import TraceLogger + +from mobly import asserts, signals +from mobly import base_test + + +class GdBaseTestClass(base_test.BaseTestClass): + + SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10 + + def setup_class(self, dut_module, cert_module): + self.log = TraceLogger(logging.getLogger()) + self.log_path_base = get_current_context().get_full_output_path() + self.verbose_mode = bool(self.user_params.get('verbose_mode', False)) + for config in self.controller_configs[CONTROLLER_CONFIG_NAME]: + config['verbose_mode'] = self.verbose_mode + + self.info = setup_class_core( + dut_module=dut_module, + cert_module=cert_module, + verbose_mode=self.verbose_mode, + log_path_base=self.log_path_base, + controller_configs=self.controller_configs) + self.dut_module = self.info['dut_module'] + self.cert_module = self.info['cert_module'] + self.rootcanal_running = self.info['rootcanal_running'] + self.rootcanal_logpath = self.info['rootcanal_logpath'] + self.rootcanal_process = self.info['rootcanal_process'] + self.rootcanal_logger = self.info['rootcanal_logger'] + + if 'rootcanal' in self.controller_configs: + asserts.assert_true(self.info['rootcanal_exist'], + "Root canal does not exist at %s" % self.info['rootcanal']) + asserts.assert_true(self.info['make_rootcanal_ports_available'], + "Failed to make root canal ports available") + + self.log.debug("Running %s" % " ".join(self.info['rootcanal_cmd'])) + asserts.assert_true( + self.info['is_rootcanal_process_started'], + msg="Cannot start root-canal at " + str(self.info['rootcanal'])) + asserts.assert_true(self.info['is_subprocess_alive'], msg="root-canal stopped immediately after running") + + self.controller_configs = self.info['controller_configs'] + + # Parse and construct GD device objects + self.register_controller(importlib.import_module('blueberry.tests.gd.cert.gd_device'), builtin=True) + self.dut = self.gd_device[1] + self.cert = self.gd_device[0] + + def teardown_class(self): + teardown_class_core( + rootcanal_running=self.rootcanal_running, + rootcanal_process=self.rootcanal_process, + rootcanal_logger=self.rootcanal_logger, + subprocess_wait_timeout_seconds=self.SUBPROCESS_WAIT_TIMEOUT_SECONDS) + + def setup_test(self): + setup_test_core(dut=self.dut, cert=self.cert, dut_module=self.dut_module, cert_module=self.cert_module) + + def teardown_test(self): + teardown_test_core(cert=self.cert, dut=self.dut) + + @staticmethod + def get_module_reference_name(a_module): + """Returns the module's module's submodule name as reference name. + + Args: + a_module: Any module. Ideally, a controller module. + Returns: + A string corresponding to the module's name. + """ + return a_module.__name__.split('.')[-1] + + def register_controller(self, controller_module, required=True, builtin=False): + """Registers an controller module for a test class. Invokes Mobly's + implementation of register_controller. + """ + module_ref_name = self.get_module_reference_name(controller_module) + module_config_name = controller_module.MOBLY_CONTROLLER_CONFIG_NAME + + # Get controller objects from Mobly's register_controller + controllers = self._controller_manager.register_controller(controller_module, required=required) + if not controllers: + return None + + # Log controller information + # Implementation of "get_info" is optional for a controller module. + if hasattr(controller_module, "get_info"): + controller_info = controller_module.get_info(controllers) + self.log.info("Controller %s: %s", module_config_name, controller_info) + + if builtin: + setattr(self, module_ref_name, controllers) + return controllers + + def __getattribute__(self, name): + attr = super().__getattribute__(name) + if not callable(attr) or not GdBaseTestClass.__is_entry_function(name): + return attr + + @wraps(attr) + def __wrapped(*args, **kwargs): + try: + return attr(*args, **kwargs) + except RpcError as e: + exception_info = "".join(traceback.format_exception(e.__class__, e, e.__traceback__)) + raise signals.TestFailure( + "RpcError during test\n\nRpcError:\n\n%s\n%s" % (exception_info, self.__dump_crashes())) + + return __wrapped + + __ENTRY_METHODS = {"setup_class", "teardown_class", "setup_test", "teardown_test"} + + @staticmethod + def __is_entry_function(name): + return name.startswith("test_") or name in GdBaseTestClass.__ENTRY_METHODS + + def __dump_crashes(self): + """ + return: formatted stack traces if found, or last few lines of log + """ + crash_detail = dump_crashes_core( + dut=self.dut, + cert=self.cert, + rootcanal_running=self.rootcanal_running, + rootcanal_process=self.rootcanal_process, + rootcanal_logpath=self.rootcanal_logpath) + return crash_detail diff --git a/blueberry/tests/gd/cert/gd_device.py b/blueberry/tests/gd/cert/gd_device.py new file mode 100644 index 000000000..185229040 --- /dev/null +++ b/blueberry/tests/gd/cert/gd_device.py @@ -0,0 +1,491 @@ +#!/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 abc import ABC +from datetime import datetime +import inspect +import logging +import os +import pathlib +import shutil +import signal +import socket +import subprocess +import time +from typing import List + +import grpc + +from cert.async_subprocess_logger import AsyncSubprocessLogger +from cert.gd_device_lib import create_core +from cert.gd_device_lib import destroy_core +from cert.gd_device_lib import get_info +from cert.gd_device_lib import replace_vars +from cert.gd_device_lib import GdDeviceBaseCore +from cert.gd_device_lib import GdHostOnlyDeviceCore +from cert.gd_device_lib import MOBLY_CONTROLLER_CONFIG_NAME +from cert.logging_client_interceptor import LoggingClientInterceptor +from cert.os_utils import get_gd_root +from cert.os_utils import read_crash_snippet_and_log_tail +from cert.os_utils import is_subprocess_alive +from cert.os_utils import make_ports_available +from cert.os_utils import TerminalColor + +from blueberry.tests.gd.cert.context import get_current_context + +from mobly import asserts +from mobly import utils +from mobly.controllers.android_device_lib.adb import AdbProxy +from mobly.controllers.android_device_lib.adb import AdbError + + +def create(configs): + create_core(configs) + return get_instances_with_configs(configs) + + +def destroy(devices): + destroy_core(devices) + + +def get_instances_with_configs(configs): + print(configs) + devices = [] + for config in configs: + resolved_cmd = [] + for arg in config["cmd"]: + logging.debug(arg) + resolved_cmd.append(replace_vars(arg, config)) + verbose_mode = bool(config.get('verbose_mode', False)) + if config.get("serial_number"): + device = GdAndroidDevice(config["grpc_port"], config["grpc_root_server_port"], config["signal_port"], + resolved_cmd, config["label"], MOBLY_CONTROLLER_CONFIG_NAME, config["name"], + config["serial_number"], verbose_mode) + else: + device = GdHostOnlyDevice(config["grpc_port"], config["grpc_root_server_port"], config["signal_port"], + resolved_cmd, config["label"], MOBLY_CONTROLLER_CONFIG_NAME, config["name"], + verbose_mode) + device.setup() + devices.append(device) + return devices + + +class GdDeviceBase(GdDeviceBaseCore): + """ + Base GD device class that covers common traits which assumes that the + device must be driven by a driver-like backing process that takes following + command line arguments: + --grpc-port: main entry port for facade services + --root-server-port: management port for starting and stopping services + --btsnoop: path to btsnoop HCI log + --signal-port: signaling port to indicate that backing process is started + --rootcanal-port: root-canal HCI port, optional + """ + + def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, + type_identifier: str, name: str, verbose_mode: bool): + """Verify arguments and log path, initialize Base GD device, common traits + for both device based and host only GD cert tests + :param grpc_port: main gRPC service port + :param grpc_root_server_port: gRPC root server port + :param signal_port: signaling port for backing process start up + :param cmd: list of arguments to run in backing process + :param label: device label used in logs + :param type_identifier: device type identifier used in logs + :param name: name of device used in logs + """ + # Must be at the first line of __init__ method + values = locals() + arguments = [values[arg] for arg in inspect.getfullargspec(GdDeviceBase.__init__).args if arg != "verbose_mode"] + asserts.assert_true(all(arguments), "All arguments to GdDeviceBase must not be None nor empty") + asserts.assert_true(all(cmd), "cmd list should not have None nor empty component") + + self.log_path_base = get_current_context().get_full_output_path() + self.test_runner_base_path = \ + get_current_context().get_base_output_path() + + GdDeviceBaseCore.__init__(self, grpc_port, grpc_root_server_port, signal_port, cmd, label, type_identifier, + name, verbose_mode, self.log_path_base, self.test_runner_base_path) + + def setup(self): + """Inherited setup method from base class to set up this device for test, + ensure signal port is available and backing process is started and alive, + must run before using this device + - After calling this, teardown() must be called when test finishes + - Should be executed after children classes' setup() methods + :return: + """ + GdDeviceBaseCore.setup(self) + # Ensure signal port is available + # signal port is the only port that always listen on the host machine + asserts.assert_true(self.signal_port_available, "[%s] Failed to make signal port available" % self.label) + + # Ensure backing process is started and alive + asserts.assert_true(self.backing_process, msg="Cannot start backing_process at " + " ".join(self.cmd)) + asserts.assert_true( + self.is_backing_process_alive, + msg="backing_process stopped immediately after running " + " ".join(self.cmd)) + + def get_crash_snippet_and_log_tail(self): + GdDeviceBaseCore.get_crash_snippet_and_log_tail(self) + + def teardown(self): + """Inherited teardown method from base class to tear down this device + and clean up any resources. + - Must be called after setup() + - Should be executed before children classes' teardown() + :return: + """ + GdDeviceBaseCore.teardown(self) + + def wait_channel_ready(self): + try: + GdDeviceBaseCore.wait_channel_ready(self) + except grpc.FutureTimeoutError: + asserts.fail("[%s] wait channel ready timeout" % self.label) + + +class GdHostOnlyDevice(GdDeviceBase): + """ + Host only device where the backing process is running on the host machine + """ + + def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, + type_identifier: str, name: str, verbose_mode: bool): + super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd, label, MOBLY_CONTROLLER_CONFIG_NAME, name, + verbose_mode) + # Enable LLVM code coverage output for host only tests + self.backing_process_profraw_path = pathlib.Path(self.log_path_base).joinpath( + "%s_%s_backing_coverage.profraw" % (self.type_identifier, self.label)) + self.environment["LLVM_PROFILE_FILE"] = str(self.backing_process_profraw_path) + llvm_binutils = pathlib.Path(get_gd_root()).joinpath("llvm_binutils").joinpath("bin") + llvm_symbolizer = llvm_binutils.joinpath("llvm-symbolizer") + if llvm_symbolizer.is_file(): + self.environment["ASAN_SYMBOLIZER_PATH"] = llvm_symbolizer + else: + logging.warning("[%s] Cannot find LLVM symbolizer at %s" % (self.label, str(llvm_symbolizer))) + + def teardown(self): + super().teardown() + self.generate_coverage_report() + + def generate_coverage_report(self): + GdHostOnlyDeviceCore.generate_coverage_report(self, self.backing_process_profraw_path, self.label, + self.test_runner_base_path, self.type_identifier, self.cmd) + + def setup(self): + # Ensure ports are available + # Only check on host only test, for Android devices, these ports will + # be opened on Android device and host machine ports will be occupied + # by sshd or adb forwarding + asserts.assert_true( + make_ports_available((self.grpc_port, self.grpc_root_server_port)), + "[%s] Failed to make backing process ports available" % self.label) + super().setup() + + +class GdAndroidDevice(GdDeviceBase): + """Real Android device where the backing process is running on it + """ + + WAIT_FOR_DEVICE_TIMEOUT_SECONDS = 180 + + def __init__(self, grpc_port: str, grpc_root_server_port: str, signal_port: str, cmd: List[str], label: str, + type_identifier: str, name: str, serial_number: str, verbose_mode: bool): + super().__init__(grpc_port, grpc_root_server_port, signal_port, cmd, label, type_identifier, name, verbose_mode) + asserts.assert_true(serial_number, "serial_number must not be None nor empty") + self.serial_number = serial_number + self.adb = AdbProxy(serial_number) + + def setup(self): + logging.info("Setting up device %s %s" % (self.label, self.serial_number)) + asserts.assert_true(self.adb.ensure_root(), "device %s cannot run as root", self.serial_number) + + # Try freeing ports and ignore results + self.cleanup_port_forwarding() + self.sync_device_time() + + # Set up port forwarding or reverse or die + self.tcp_forward_or_die(self.grpc_port, self.grpc_port) + self.tcp_forward_or_die(self.grpc_root_server_port, self.grpc_root_server_port) + self.tcp_reverse_or_die(self.signal_port, self.signal_port) + + # Push test binaries + self.ensure_verity_disabled() + self.push_or_die(os.path.join(get_gd_root(), "target", "bluetooth_stack_with_facade"), "system/bin") + self.push_or_die( + os.path.join(get_gd_root(), "target", "android.system.suspend.control-V1-ndk.so"), "system/lib64") + self.push_or_die(os.path.join(get_gd_root(), "target", "libbluetooth_gd.so"), "system/lib64") + self.push_or_die(os.path.join(get_gd_root(), "target", "libgrpc++_unsecure.so"), "system/lib64") + self.push_or_die(os.path.join(get_gd_root(), "target", "libgrpc++.so"), "system/lib64") + self.push_or_die(os.path.join(get_gd_root(), "target", "libgrpc_wrap.so"), "system/lib64") + self.push_or_die(os.path.join(get_gd_root(), "target", "libstatslog.so"), "system/lib64") + + try: + self.adb.shell("rm /data/misc/bluetooth/logs/btsnoop_hci.log") + except AdbError as error: + logging.error("Error during setup: " + str(error)) + + try: + self.adb.shell("rm /data/misc/bluetooth/logs/btsnooz_hci.log") + except AdbError as error: + logging.error("Error during setup: " + str(error)) + + try: + self.adb.shell("rm /data/misc/bluedroid/bt_config.conf") + except AdbError as error: + logging.error("Error during setup: " + str(error)) + + try: + self.adb.shell("rm /data/misc/bluedroid/bt_config.bak") + except AdbError as error: + logging.error("Error during setup: " + str(error)) + + self.ensure_no_output(self.adb.shell("svc bluetooth disable")) + + # Start logcat logging + self.logcat_output_path = os.path.join( + self.log_path_base, '%s_%s_%s_logcat_logs.txt' % (self.type_identifier, self.label, self.serial_number)) + self.logcat_cmd = ["adb", "-s", self.serial_number, "logcat", "-T", "1", "-v", "year", "-v", "uid"] + logging.debug("Running %s", " ".join(self.logcat_cmd)) + self.logcat_process = subprocess.Popen( + self.logcat_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + asserts.assert_true(self.logcat_process, msg="Cannot start logcat_process at " + " ".join(self.logcat_cmd)) + asserts.assert_true( + is_subprocess_alive(self.logcat_process), + msg="logcat_process stopped immediately after running " + " ".join(self.logcat_cmd)) + self.logcat_logger = AsyncSubprocessLogger( + self.logcat_process, [self.logcat_output_path], + log_to_stdout=self.verbose_mode, + tag="%s_%s" % (self.label, self.serial_number), + color=self.terminal_color) + + # Done run parent setup + logging.info("Done preparation for %s, starting backing process" % self.serial_number) + super().setup() + + def teardown(self): + super().teardown() + stop_signal = signal.SIGINT + self.logcat_process.send_signal(stop_signal) + try: + return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) + except subprocess.TimeoutExpired: + logging.error("[%s_%s] Failed to interrupt logcat process via SIGINT, sending SIGKILL" % + (self.label, self.serial_number)) + stop_signal = signal.SIGKILL + self.logcat_process.kill() + try: + return_code = self.logcat_process.wait(timeout=self.WAIT_CHANNEL_READY_TIMEOUT_SECONDS) + except subprocess.TimeoutExpired: + logging.error("Failed to kill logcat_process %s %s" % (self.label, self.serial_number)) + return_code = -65536 + if return_code not in [-stop_signal, 0]: + logging.error("logcat_process %s_%s stopped with code: %d" % (self.label, self.serial_number, return_code)) + self.logcat_logger.stop() + self.cleanup_port_forwarding() + self.adb.pull("/data/misc/bluetooth/logs/btsnoop_hci.log %s" % os.path.join(self.log_path_base, + "%s_btsnoop_hci.log" % self.label)) + self.adb.pull("/data/misc/bluedroid/bt_config.conf %s" % os.path.join(self.log_path_base, + "%s_bt_config.conf" % self.label)) + self.adb.pull( + "/data/misc/bluedroid/bt_config.bak %s" % os.path.join(self.log_path_base, "%s_bt_config.bak" % self.label)) + + def cleanup_port_forwarding(self): + try: + self.adb.remove_tcp_forward(self.grpc_port) + except AdbError as error: + logging.error("Error during port forwarding cleanup: " + str(error)) + + try: + self.adb.remove_tcp_forward(self.grpc_root_server_port) + except AdbError as error: + logging.error("Error during port forwarding cleanup: " + str(error)) + + try: + self.adb.reverse("--remove tcp:%d" % self.signal_port) + except AdbError as error: + logging.error("Error during port forwarding cleanup: " + str(error)) + + @staticmethod + def ensure_no_output(result): + """ + Ensure a command has not output + """ + asserts.assert_true( + result is None or len(result) == 0, msg="command returned something when it shouldn't: %s" % result) + + def sync_device_time(self): + self.adb.shell("settings put global auto_time 0") + self.adb.shell("settings put global auto_time_zone 0") + device_tz = self.adb.shell("date +%z") + asserts.assert_true(device_tz, "date +%z must return device timezone, " + "but returned {} instead".format(device_tz)) + host_tz = time.strftime("%z") + if device_tz != host_tz: + target_timezone = utils.get_timezone_olson_id() + logging.debug("Device timezone %s does not match host timezone %s, " + "syncing them by setting timezone to %s" % (device_tz, host_tz, target_timezone)) + self.adb.shell("setprop persist.sys.timezone %s" % target_timezone) + self.reboot() + device_tz = self.adb.shell("date +%z") + asserts.assert_equal( + host_tz, device_tz, "Device timezone %s still does not match host " + "timezone %s after reset" % (device_tz, host_tz)) + self.adb.shell("date %s" % time.strftime("%m%d%H%M%Y.%S")) + datetime_format = "%Y-%m-%dT%H:%M:%S%z" + try: + device_time = datetime.strptime(self.adb.shell("date +'%s'" % datetime_format), datetime_format) + except ValueError: + asserts.fail("Failed to get time after sync") + return + # Include ADB delay that might be longer in SSH environment + max_delta_seconds = 3 + host_time = datetime.now(tz=device_time.tzinfo) + asserts.assert_almost_equal( + (device_time - host_time).total_seconds(), + 0, + msg="Device time %s and host time %s off by >%dms after sync" % + (device_time.isoformat(), host_time.isoformat(), int(max_delta_seconds * 1000)), + delta=max_delta_seconds) + + def push_or_die(self, src_file_path, dst_file_path, push_timeout=300): + """Pushes a file to the Android device + + Args: + src_file_path: The path to the file to install. + dst_file_path: The destination of the file. + push_timeout: How long to wait for the push to finish in seconds + """ + out = self.adb.push('%s %s' % (src_file_path, dst_file_path), timeout=push_timeout) + if 'error' in out: + asserts.fail('Unable to push file %s to %s due to %s' % (src_file_path, dst_file_path, out)) + + def tcp_forward_or_die(self, host_port, device_port, num_retry=1): + """ + Forward a TCP port from host to device or fail + :param host_port: host port, int, 0 for adb to assign one + :param device_port: device port, int + :param num_retry: number of times to reboot and retry this before dying + :return: host port int + """ + error_or_port = self.adb.tcp_forward(host_port, device_port) + if not error_or_port: + logging.debug("host port %d was already forwarded" % host_port) + return host_port + if not isinstance(error_or_port, int): + if num_retry > 0: + # If requested, reboot an retry + num_retry -= 1 + logging.warning( + "[%s] Failed to TCP forward host port %d to " + "device port %d, num_retries left is %d" % (self.label, host_port, device_port, num_retry)) + self.reboot() + return self.tcp_forward_or_die(host_port, device_port, num_retry=num_retry) + asserts.fail( + 'Unable to forward host port %d to device port %d, error %s' % (host_port, device_port, error_or_port)) + return error_or_port + + def tcp_reverse_or_die(self, device_port, host_port, num_retry=1): + """ + Forward a TCP port from device to host or fail + :param device_port: device port, int, 0 for adb to assign one + :param host_port: host port, int + :param num_retry: number of times to reboot and retry this before dying + :return: device port int + """ + error_or_port = self.adb.reverse("tcp:%d tcp:%d" % (device_port, host_port)) + if not error_or_port: + logging.debug("device port %d was already reversed" % device_port) + return device_port + try: + error_or_port = int(error_or_port) + except ValueError: + if num_retry > 0: + # If requested, reboot an retry + num_retry -= 1 + logging.warning( + "[%s] Failed to TCP reverse device port %d to " + "host port %d, num_retries left is %d" % (self.label, device_port, host_port, num_retry)) + self.reboot() + return self.tcp_reverse_or_die(device_port, host_port, num_retry=num_retry) + asserts.fail( + 'Unable to reverse device port %d to host port %d, error %s' % (device_port, host_port, error_or_port)) + return error_or_port + + def ensure_verity_disabled(self): + """Ensures that verity is enabled. + + If verity is not enabled, this call will reboot the phone. Note that + this only works on debuggable builds. + """ + logging.debug("Disabling verity and remount for %s", self.serial_number) + # The below properties will only exist if verity has been enabled. + system_verity = self.adb.getprop('partition.system.verified') + vendor_verity = self.adb.getprop('partition.vendor.verified') + if system_verity or vendor_verity: + self.adb.disable_verity() + self.reboot() + self.adb.remount() + self.adb.wait_for_device(timeout=self.WAIT_FOR_DEVICE_TIMEOUT_SECONDS) + + def reboot(self, timeout_minutes=15.0): + """Reboots the device. + + Reboot the device, wait for device to complete booting. + """ + logging.debug("Rebooting %s", self.serial_number) + self.adb.reboot() + + timeout_start = time.time() + timeout = timeout_minutes * 60 + # Android sometimes return early after `adb reboot` is called. This + # means subsequent calls may make it to the device before the reboot + # goes through, return false positives for getprops such as + # sys.boot_completed. + while time.time() < timeout_start + timeout: + try: + self.adb.get_state() + time.sleep(.1) + except AdbError: + # get_state will raise an error if the device is not found. We + # want the device to be missing to prove the device has kicked + # off the reboot. + break + minutes_left = timeout_minutes - (time.time() - timeout_start) / 60.0 + self.wait_for_boot_completion(timeout_minutes=minutes_left) + asserts.assert_true(self.adb.ensure_root(), "device %s cannot run as root after reboot", self.serial_number) + + def wait_for_boot_completion(self, timeout_minutes=15.0): + """ + Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. + :param timeout_minutes: number of minutes to wait + """ + timeout_start = time.time() + timeout = timeout_minutes * 60 + + self.adb.wait_for_device(timeout=self.WAIT_FOR_DEVICE_TIMEOUT_SECONDS) + while time.time() < timeout_start + timeout: + try: + completed = self.adb.getprop("sys.boot_completed") + if completed == '1': + return + except AdbError: + # adb shell calls may fail during certain period of booting + # process, which is normal. Ignoring these errors. + pass + time.sleep(5) + asserts.fail(msg='Device %s booting process timed out.' % self.serial_number) diff --git a/blueberry/tests/gd/cert/metadata.py b/blueberry/tests/gd/cert/metadata.py new file mode 100644 index 000000000..aa1bc9a8b --- /dev/null +++ b/blueberry/tests/gd/cert/metadata.py @@ -0,0 +1,79 @@ +#!/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 functools +import inspect + +from mobly import asserts + +from blueberry.tests.gd.cert.test_decorators import test_info + + +def _fail_decorator(msg): + + def fail_decorator(func): + + @functools.wraps(func) + def fail(*args, **kwargs): + asserts.fail(msg) + + return fail + + return fail_decorator + + +def metadata(_do_not_use=None, pts_test_id=None, pts_test_name=None): + """ + Record a piece of test metadata in the Extra section of the test Record in + the test summary file. The metadata will come with a timestamp, but there + is no guarantee on the order of when the metadata will be written + + Note: + - Metadata is recorded per test case as key-value pairs. + - Metadata is only guaranteed to be written when the test result is PASS, + FAIL or SKIPPED. When there are test infrastructural errors, metadata + might not be written successfully + :param _do_not_use: a positional argument with default value. This argument + is to ensure that @metadata(key=value) is used in a + functional form instead of @metadata or @metadata(a) + :param pts_test_id: A fully qualified PTS test ID such as + L2CAP/COS/IEX/BV-01-C + :param pts_test_name: A human readable test name such as + "Request Connection" for the above example + :return: decorated test case function object + """ + if _do_not_use is not None: + + def fail(*args, **kwargs): + asserts.fail("@metadata must be used in functional form such " "as @metadta(key=value)") + + return fail + + # Create a dictionary of optional parameters + values = locals() + args = {arg: values[arg] for arg in inspect.getfullargspec(metadata).args} + del args["_do_not_use"] + + # Check if at least one optional parameter is valid + if not any(args.values()): + return _fail_decorator("at least one optional argument should be valid") + + # Validate pts_test_id and pts_test_name + if any((pts_test_id, pts_test_name)) and \ + not all((pts_test_id, pts_test_name)): + return _fail_decorator("pts_test_id and pts_test_name must both " "be valid if one of them is valid") + + return test_info(**args) diff --git a/blueberry/tests/gd/cert/pts_base_test.py b/blueberry/tests/gd/cert/pts_base_test.py new file mode 100644 index 000000000..0e8b99a71 --- /dev/null +++ b/blueberry/tests/gd/cert/pts_base_test.py @@ -0,0 +1,29 @@ +#!/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 mobly import base_test + +import importlib + + +class PTSBaseTestClass(base_test.BaseTestClass): + + def __init__(self, configs): + BaseTestClass.__init__(self, configs) + + gd_devices = self.controller_configs.get("GdDevice") + + self.register_controller(importlib.import_module('cert.gd_device'), builtin=True) diff --git a/blueberry/tests/gd/cert/test_decorators.py b/blueberry/tests/gd/cert/test_decorators.py new file mode 100644 index 000000000..453062e26 --- /dev/null +++ b/blueberry/tests/gd/cert/test_decorators.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +# +# 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. + +from mobly import signals + + +def test_info(predicate=None, **keyed_info): + """Adds info about test. + + Extra info to include about the test. This info will be available in the + test output. Note that if a key is given multiple times it will be added + as a list of all values. If multiples of these are stacked their results + will be merged. + + Example: + # This test will have a variable my_var + @test_info(my_var='THIS IS MY TEST') + def my_test(self): + return False + + Args: + predicate: A func to call that if false will skip adding this test + info. Function signature is bool(test_obj, args, kwargs) + **keyed_info: The key, value info to include in the extras for this + test. + """ + + def test_info_decorator(func): + return _TestInfoDecoratorFunc(func, predicate, keyed_info) + + return test_info_decorator + + +def __select_last(test_signals, _): + return test_signals[-1] + + +def repeated_test(num_passes, acceptable_failures=0, result_selector=__select_last): + """A decorator that runs a test case multiple times. + + This decorator can be used to run a test multiple times and aggregate the + data into a single test result. By setting `result_selector`, the user can + access the returned result of each run, allowing them to average results, + return the median, or gather and return standard deviation values. + + This decorator should be used on test cases, and should not be used on + static or class methods. The test case must take in an additional argument, + `attempt_number`, which returns the current attempt number, starting from + 1. + + Note that any TestSignal intended to abort or skip the test will take + abort or skip immediately. + + Args: + num_passes: The number of times the test needs to pass to report the + test case as passing. + acceptable_failures: The number of failures accepted. If the failures + exceeds this number, the test will stop repeating. The maximum + number of runs is `num_passes + acceptable_failures`. If the test + does fail, result_selector will still be called. + result_selector: A lambda that takes in the list of TestSignals and + returns the test signal to report the test case as. Note that the + list also contains any uncaught exceptions from the test execution. + """ + + def decorator(func): + if not func.__name__.startswith('test_'): + raise ValueError('Tests must start with "test_".') + + def test_wrapper(self): + num_failures = 0 + num_seen_passes = 0 + test_signals_received = [] + for i in range(num_passes + acceptable_failures): + try: + func(self, i + 1) + except (signals.TestFailure, signals.TestError, AssertionError) as signal: + test_signals_received.append(signal) + num_failures += 1 + except signals.TestPass as signal: + test_signals_received.append(signal) + num_seen_passes += 1 + except (signals.TestSignal, KeyboardInterrupt): + raise + except Exception as signal: + test_signals_received.append(signal) + num_failures += 1 + else: + num_seen_passes += 1 + test_signals_received.append( + signals.TestPass('Test iteration %s of %s passed without details.' % (i, func.__name__))) + + if num_failures > acceptable_failures: + break + elif num_seen_passes == num_passes: + break + else: + self.teardown_test() + self.setup_test() + + raise result_selector(test_signals_received, self) + + return test_wrapper + + return decorator + + +def test_tracker_info(uuid, extra_environment_info=None, predicate=None): + """Decorator for adding test tracker info to tests results. + + Will add test tracker info inside of Extras/test_tracker_info. + + Example: + # This test will be linked to test tracker uuid abcd + @test_tracker_info(uuid='abcd') + def my_test(self): + return False + + Args: + uuid: The uuid of the test case in test tracker. + extra_environment_info: Extra info about the test tracker environment. + predicate: A func that if false when called will ignore this info. + """ + return test_info(test_tracker_uuid=uuid, test_tracker_environment_info=extra_environment_info, predicate=predicate) + + +class _TestInfoDecoratorFunc(object): + """Object that acts as a function decorator test info.""" + + def __init__(self, func, predicate, keyed_info): + self.func = func + self.predicate = predicate + self.keyed_info = keyed_info + self.__name__ = func.__name__ + self.__doc__ = func.__doc__ + self.__module__ = func.__module__ + + def __get__(self, instance, owner): + """Called by Python to create a binding for an instance closure. + + When called by Python this object will create a special binding for + that instance. That binding will know how to interact with this + specific decorator. + """ + return _TestInfoBinding(self, instance) + + def __call__(self, *args, **kwargs): + """ + When called runs the underlying func and then attaches test info + to a signal. + """ + cause = None + try: + result = self.func(*args, **kwargs) + + if result or result is None: + new_signal = signals.TestPass('') + else: + new_signal = signals.TestFailure('') + except signals.TestSignal as signal: + new_signal = signal + except Exception as ex: + cause = ex + new_signal = signals.TestError(cause) + + if new_signal.extras is None: + new_signal.extras = {} + if not isinstance(new_signal.extras, dict): + raise ValueError('test_info can only append to signal data that has a dict as the extra value.') + + gathered_extras = self._gather_local_info(None, *args, **kwargs) + for k, v in gathered_extras.items(): + if k not in new_signal.extras: + new_signal.extras[k] = v + else: + if not isinstance(new_signal.extras[k], list): + new_signal.extras[k] = [new_signal.extras[k]] + + new_signal.extras[k].insert(0, v) + + raise new_signal from cause + + def gather(self, *args, **kwargs): + """ + Gathers the info from this decorator without invoking the underlying + function. This will also gather all child info if the underlying func + has that ability. + + Returns: A dictionary of info. + """ + if hasattr(self.func, 'gather'): + extras = self.func.gather(*args, **kwargs) + else: + extras = {} + + self._gather_local_info(extras, *args, **kwargs) + + return extras + + def _gather_local_info(self, gather_into, *args, **kwargs): + """Gathers info from this decorator and ignores children. + + Args: + gather_into: Gathers into a dictionary that already exists. + + Returns: The dictionary with gathered info in it. + """ + if gather_into is None: + extras = {} + else: + extras = gather_into + if not self.predicate or self.predicate(args, kwargs): + for k, v in self.keyed_info.items(): + if v and k not in extras: + extras[k] = v + elif v and k in extras: + if not isinstance(extras[k], list): + extras[k] = [extras[k]] + extras[k].insert(0, v) + + return extras + + +class _TestInfoBinding(object): + """ + When Python creates an instance of an object it creates a binding object + for each closure that contains what the instance variable should be when + called. This object is a similar binding for _TestInfoDecoratorFunc. + When Python tries to create a binding of a _TestInfoDecoratorFunc it + will return one of these objects to hold the instance for that closure. + """ + + def __init__(self, target, instance): + """ + Args: + target: The target for creating a binding to. + instance: The instance to bind the target with. + """ + self.target = target + self.instance = instance + self.__name__ = target.__name__ + + def __call__(self, *args, **kwargs): + """ + When this object is called it will call the target with the bound + instance. + """ + return self.target(self.instance, *args, **kwargs) + + def gather(self, *args, **kwargs): + """ + Will gather the target with the bound instance. + """ + return self.target.gather(self.instance, *args, **kwargs) diff --git a/blueberry/tests/gd/cert/tracelogger.py b/blueberry/tests/gd/cert/tracelogger.py new file mode 100644 index 000000000..a4e5a2203 --- /dev/null +++ b/blueberry/tests/gd/cert/tracelogger.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright 2016 - 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 inspect +import os + + +class TraceLogger(object): + + def __init__(self, logger): + self._logger = logger + + @staticmethod + def _get_trace_info(level=1, offset=2): + # We want the stack frame above this and above the error/warning/info + inspect_stack = inspect.stack() + trace_info = '' + for i in range(level): + try: + stack_frames = inspect_stack[offset + i] + info = inspect.getframeinfo(stack_frames[0]) + trace_info = '%s[%s:%s:%s]' % (trace_info, os.path.basename(info.filename), info.function, info.lineno) + except IndexError: + break + return trace_info + + def _log_with(self, logging_lambda, trace_level, msg, *args, **kwargs): + trace_info = TraceLogger._get_trace_info(level=trace_level, offset=3) + logging_lambda('%s %s' % (msg, trace_info), *args, **kwargs) + + def exception(self, msg, *args, **kwargs): + self._log_with(self._logger.exception, 5, msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + self._log_with(self._logger.debug, 3, msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + self._log_with(self._logger.error, 3, msg, *args, **kwargs) + + def warn(self, msg, *args, **kwargs): + self._log_with(self._logger.warn, 3, msg, *args, **kwargs) + + def warning(self, msg, *args, **kwargs): + self._log_with(self._logger.warning, 3, msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + self._log_with(self._logger.info, 1, msg, *args, **kwargs) + + def __getattr__(self, name): + return getattr(self._logger, name) diff --git a/blueberry/tests/gd/hal/simple_hal_test.py b/blueberry/tests/gd/hal/simple_hal_test.py new file mode 100644 index 000000000..f7bc1cb41 --- /dev/null +++ b/blueberry/tests/gd/hal/simple_hal_test.py @@ -0,0 +1,39 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hal.cert.simple_hal_test_lib import SimpleHalTestBase +from mobly import test_runner + +_GRPC_TIMEOUT = 10 + + +class SimpleHalTest(gd_base_test.GdBaseTestClass, SimpleHalTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HAL', cert_module='HAL') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + SimpleHalTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + SimpleHalTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/acl_manager_test.py b/blueberry/tests/gd/hci/acl_manager_test.py new file mode 100644 index 000000000..fd921c33b --- /dev/null +++ b/blueberry/tests/gd/hci/acl_manager_test.py @@ -0,0 +1,50 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.acl_manager_test_lib import AclManagerTestBase +from mobly import test_runner + + +class AclManagerTest(gd_base_test.GdBaseTestClass, AclManagerTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') + + # todo: move into GdBaseTestClass, based on modules inited + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + AclManagerTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + AclManagerTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + def test_dut_connects(self): + AclManagerTestBase.test_dut_connects(self) + + def test_cert_connects(self): + AclManagerTestBase.test_cert_connects(self) + + def test_cert_connects_disconnects(self): + AclManagerTestBase.test_cert_connects_disconnects(self) + + def test_recombination_l2cap_packet(self): + AclManagerTestBase.test_recombination_l2cap_packet(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/controller_test.py b/blueberry/tests/gd/hci/controller_test.py new file mode 100644 index 000000000..d225d2579 --- /dev/null +++ b/blueberry/tests/gd/hci/controller_test.py @@ -0,0 +1,35 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.controller_test_lib import ControllerTestBase +from mobly import test_runner + + +class ControllerTest(gd_base_test.GdBaseTestClass, ControllerTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES') + + def test_get_addresses(self): + ControllerTestBase.test_get_addresses(self, self.dut, self.cert) + + def test_write_local_name(self): + ControllerTestBase.test_write_local_name(self, self.dut, self.cert) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/direct_hci_test.py b/blueberry/tests/gd/hci/direct_hci_test.py new file mode 100644 index 000000000..e02a83a53 --- /dev/null +++ b/blueberry/tests/gd/hci/direct_hci_test.py @@ -0,0 +1,37 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.direct_hci_test_lib import DirectHciTestBase +from mobly import test_runner + + +class DirectHciTest(gd_base_test.GdBaseTestClass, DirectHciTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI', cert_module='HAL') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + DirectHciTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + DirectHciTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/le_acl_manager_test.py b/blueberry/tests/gd/hci/le_acl_manager_test.py new file mode 100644 index 000000000..e65a972f7 --- /dev/null +++ b/blueberry/tests/gd/hci/le_acl_manager_test.py @@ -0,0 +1,37 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.le_acl_manager_test_lib import LeAclManagerTestBase +from mobly import test_runner + + +class LeAclManagerTest(gd_base_test.GdBaseTestClass, LeAclManagerTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + LeAclManagerTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + LeAclManagerTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/le_advertising_manager_test.py b/blueberry/tests/gd/hci/le_advertising_manager_test.py new file mode 100644 index 000000000..814645df5 --- /dev/null +++ b/blueberry/tests/gd/hci/le_advertising_manager_test.py @@ -0,0 +1,37 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.le_advertising_manager_test_lib import LeAdvertisingManagerTestBase +from mobly import test_runner + + +class LeAdvertisingManagerTest(gd_base_test.GdBaseTestClass, LeAdvertisingManagerTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + LeAdvertisingManagerTestBase.setup_test(self, self.cert) + + def teardown_test(self): + LeAdvertisingManagerTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/le_scanning_manager_test.py b/blueberry/tests/gd/hci/le_scanning_manager_test.py new file mode 100644 index 000000000..08227471e --- /dev/null +++ b/blueberry/tests/gd/hci/le_scanning_manager_test.py @@ -0,0 +1,29 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.le_scanning_manager_test_lib import LeScanningManagerTestBase +from mobly import test_runner + + +class LeScanningManagerTest(gd_base_test.GdBaseTestClass, LeScanningManagerTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES') + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/hci/le_scanning_with_security_test.py b/blueberry/tests/gd/hci/le_scanning_with_security_test.py new file mode 100644 index 000000000..207b2dadf --- /dev/null +++ b/blueberry/tests/gd/hci/le_scanning_with_security_test.py @@ -0,0 +1,29 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from hci.cert.le_scanning_with_security_test_lib import LeScanningWithSecurityTestBase +from mobly import test_runner + + +class LeScanningWithSecurityTest(gd_base_test.GdBaseTestClass, LeScanningWithSecurityTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='HCI_INTERFACES') + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/host_config.yaml b/blueberry/tests/gd/host_config.yaml index 77498377d..15155db8d 100644 --- a/blueberry/tests/gd/host_config.yaml +++ b/blueberry/tests/gd/host_config.yaml @@ -1,32 +1,32 @@ _description: Bluetooth cert testing TestBeds: - - _description: Host only cert testbed - Name: HostOnlyCert - rootcanal: - test_port: '6401' - hci_port: '6402' - link_layer_port: '6403' - GdDevice: - - grpc_port: '8998' - grpc_root_server_port: '8996' - signal_port: '8994' - label: cert - Name: Cert Device - cmd: - - "$GD_ROOT/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" - - grpc_port: '8999' - grpc_root_server_port: '8997' - signal_port: '8995' - label: dut - Name: DUT Device - cmd: - - "$GD_ROOT/bluetooth_stack_with_facade" - - "--grpc-port=$(grpc_port)" - - "--root-server-port=$(grpc_root_server_port)" - - "--rootcanal-port=$(rootcanal_port)" - - "--signal-port=$(signal_port)" + - Name: HostOnlyCert + Controllers: + rootcanal: + test_port: '6401' + hci_port: '6402' + link_layer_port: '6403' + GdDevice: + - grpc_port: '8998' + grpc_root_server_port: '8996' + signal_port: '8994' + label: cert + name: Cert Device + cmd: + - "$GD_ROOT/bluetooth_stack_with_facade" + - "--grpc-port=$(grpc_port)" + - "--root-server-port=$(grpc_root_server_port)" + - "--rootcanal-port=$(rootcanal_port)" + - "--signal-port=$(signal_port)" + - grpc_port: '8999' + grpc_root_server_port: '8997' + signal_port: '8995' + label: dut + name: DUT Device + cmd: + - "$GD_ROOT/bluetooth_stack_with_facade" + - "--grpc-port=$(grpc_port)" + - "--root-server-port=$(grpc_root_server_port)" + - "--rootcanal-port=$(rootcanal_port)" + - "--signal-port=$(signal_port)" logpath: "/tmp/logs" diff --git a/blueberry/tests/gd/iso/le_iso_test.py b/blueberry/tests/gd/iso/le_iso_test.py new file mode 100644 index 000000000..e0b509012 --- /dev/null +++ b/blueberry/tests/gd/iso/le_iso_test.py @@ -0,0 +1,36 @@ +# +# Copyright 2021 - 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 blueberry.tests.gd.cert import gd_base_test +from iso.cert.le_iso_test_lib import LeIsoTestBase +from mobly import test_runner + + +class LeIsoTest(gd_base_test.GdBaseTestClass, LeIsoTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + LeIsoTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + LeIsoTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/l2cap/classic/l2cap_performance_test.py b/blueberry/tests/gd/l2cap/classic/l2cap_performance_test.py new file mode 100644 index 000000000..9a0010c2c --- /dev/null +++ b/blueberry/tests/gd/l2cap/classic/l2cap_performance_test.py @@ -0,0 +1,36 @@ +# +# 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 blueberry.tests.gd.cert import gd_base_test +from l2cap.classic.cert.l2cap_performance_test_lib import L2capPerformanceTestBase +from mobly import test_runner + + +class L2capPerformanceTest(gd_base_test.GdBaseTestClass, L2capPerformanceTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + L2capPerformanceTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + L2capPerformanceTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/l2cap/classic/l2cap_test.py b/blueberry/tests/gd/l2cap/classic/l2cap_test.py new file mode 100644 index 000000000..71dcc5b2b --- /dev/null +++ b/blueberry/tests/gd/l2cap/classic/l2cap_test.py @@ -0,0 +1,36 @@ +# +# 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 blueberry.tests.gd.cert import gd_base_test +from l2cap.classic.cert.l2cap_test_lib import L2capTestBase +from mobly import test_runner + + +class L2capTest(gd_base_test.GdBaseTestClass, L2capTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + L2capTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + L2capTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/l2cap/le/dual_l2cap_test.py b/blueberry/tests/gd/l2cap/le/dual_l2cap_test.py new file mode 100644 index 000000000..814da6b2d --- /dev/null +++ b/blueberry/tests/gd/l2cap/le/dual_l2cap_test.py @@ -0,0 +1,36 @@ +# +# 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 blueberry.tests.gd.cert import gd_base_test +from l2cap.le.cert.dual_l2cap_test_lib import DualL2capTestBase +from mobly import test_runner + + +class DualL2capTest(gd_base_test.GdBaseTestClass, DualL2capTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + DualL2capTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + DualL2capTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/l2cap/le/le_l2cap_test.py b/blueberry/tests/gd/l2cap/le/le_l2cap_test.py new file mode 100644 index 000000000..ed3f38350 --- /dev/null +++ b/blueberry/tests/gd/l2cap/le/le_l2cap_test.py @@ -0,0 +1,36 @@ +# +# 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 blueberry.tests.gd.cert import gd_base_test +from l2cap.le.cert.le_l2cap_test_lib import LeL2capTestBase +from mobly import test_runner + + +class LeL2capTest(gd_base_test.GdBaseTestClass, LeL2capTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + LeL2capTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + LeL2capTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/neighbor/neighbor_test.py b/blueberry/tests/gd/neighbor/neighbor_test.py new file mode 100644 index 000000000..ceb9ea2f2 --- /dev/null +++ b/blueberry/tests/gd/neighbor/neighbor_test.py @@ -0,0 +1,37 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from neighbor.cert.neighbor_test_lib import NeighborTestBase +from mobly import test_runner + + +class NeighborTest(gd_base_test.GdBaseTestClass, NeighborTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + NeighborTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + NeighborTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/security/le_security_test.py b/blueberry/tests/gd/security/le_security_test.py new file mode 100644 index 000000000..1ba1ca558 --- /dev/null +++ b/blueberry/tests/gd/security/le_security_test.py @@ -0,0 +1,36 @@ +# +# 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 blueberry.tests.gd.cert import gd_base_test +from security.cert.le_security_test_lib import LeSecurityTestBase +from mobly import test_runner + + +class LeSecurityTest(gd_base_test.GdBaseTestClass, LeSecurityTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='SECURITY') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + LeSecurityTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + LeSecurityTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/security/security_test.py b/blueberry/tests/gd/security/security_test.py new file mode 100644 index 000000000..d6bb324ee --- /dev/null +++ b/blueberry/tests/gd/security/security_test.py @@ -0,0 +1,36 @@ +# +# 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 blueberry.tests.gd.cert import gd_base_test +from security.cert.security_test_lib import SecurityTestBase +from mobly import test_runner + + +class SecurityTest(gd_base_test.GdBaseTestClass, SecurityTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='L2CAP') + + def setup_test(self): + gd_base_test.GdBaseTestClass.setup_test(self) + SecurityTestBase.setup_test(self, self.dut, self.cert) + + def teardown_test(self): + SecurityTestBase.teardown_test(self) + gd_base_test.GdBaseTestClass.teardown_test(self) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/shim/shim_test.py b/blueberry/tests/gd/shim/shim_test.py new file mode 100644 index 000000000..65f9564d8 --- /dev/null +++ b/blueberry/tests/gd/shim/shim_test.py @@ -0,0 +1,32 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from shim.cert.shim_test_lib import ShimTestBase +from mobly import test_runner + + +class ShimTest(gd_base_test.GdBaseTestClass, ShimTestBase): + + def setup_class(self): + gd_base_test.GdBaseTestClass.setup_class(self, dut_module='SHIM', cert_module='SHIM') + + def test_dumpsys(self): + ShimTestBase.test_dumpsys(self, self.dut, self.cert) + + +if __name__ == '__main__': + test_runner.main() diff --git a/blueberry/tests/gd/shim/stack_test.py b/blueberry/tests/gd/shim/stack_test.py new file mode 100644 index 000000000..8a1373aa7 --- /dev/null +++ b/blueberry/tests/gd/shim/stack_test.py @@ -0,0 +1,29 @@ +#!/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 blueberry.tests.gd.cert import gd_base_test +from shim.cert.stack_test_lib import StackTestBase +from mobly import test_runner + + +class StackTest(gd_base_test.GdBaseTestClass, StackTestBase): + + def setup_class(self): + super().setup_class(dut_module='SHIM', cert_module='SHIM') + + +if __name__ == '__main__': + test_runner.main() -- cgit v1.2.3