blob: 0fa9076c51b4dffe2137ce06c9a91c62ce200a65 [file] [log] [blame]
#!/usr/bin/env python3
#
# 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.
import importlib
import logging
import os
import signal
import subprocess
from blueberry.tests.gd.cert.context import get_current_context
from blueberry.tests.gd.cert.tracelogger import TraceLogger
from cert.async_subprocess_logger import AsyncSubprocessLogger
from cert.os_utils import get_gd_root
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 mobly import asserts
from mobly import base_test
CONTROLLER_CONFIG_NAME = "GdDevice"
def setup_test_core(verbose_mode, log_path_base, controller_configs):
info = {}
info['controller_configs'] = controller_configs
# Start root-canal if needed
info['rootcanal_running'] = False
info['rootcanal_logpath'] = None
info['rootcanal_process'] = None
info['rootcanal_logger'] = None
if 'rootcanal' not in info['controller_configs']:
return
info['rootcanal_running'] = True
# Get root canal binary
rootcanal = os.path.join(get_gd_root(), "root-canal")
info['rootcanal'] = rootcanal
info['rootcanal_exist'] = os.path.isfile(rootcanal)
if not os.path.isfile(rootcanal):
return info
# Get root canal log
rootcanal_logpath = os.path.join(log_path_base, 'rootcanal_logs.txt')
info['rootcanal_logpath'] = rootcanal_logpath
# Make sure ports are available
rootcanal_config = info['controller_configs']['rootcanal']
rootcanal_test_port = int(rootcanal_config.get("test_port", "6401"))
rootcanal_hci_port = int(rootcanal_config.get("hci_port", "6402"))
rootcanal_link_layer_port = int(rootcanal_config.get("link_layer_port", "6403"))
info['make_rootcanal_ports_available'] = make_ports_available((rootcanal_test_port, rootcanal_hci_port,
rootcanal_link_layer_port))
if not make_ports_available((rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)):
return info
# Start root canal process
rootcanal_cmd = [rootcanal, str(rootcanal_test_port), str(rootcanal_hci_port), str(rootcanal_link_layer_port)]
info['rootcanal_cmd'] = rootcanal_cmd
rootcanal_process = subprocess.Popen(
rootcanal_cmd,
cwd=get_gd_root(),
env=os.environ.copy(),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True)
info['rootcanal_process'] = rootcanal_process
if rootcanal_process:
info['is_rootcanal_process_started'] = True
else:
info['is_rootcanal_process_started'] = False
return info
info['is_subprocess_alive'] = is_subprocess_alive(rootcanal_process)
if not is_subprocess_alive(rootcanal_process):
info['is_subprocess_alive'] = False
return info
info['rootcanal_logger'] = AsyncSubprocessLogger(
rootcanal_process, [rootcanal_logpath],
log_to_stdout=verbose_mode,
tag="rootcanal",
color=TerminalColor.MAGENTA)
# Modify the device config to include the correct root-canal port
for gd_device_config in info['controller_configs'].get("GdDevice"):
gd_device_config["rootcanal_port"] = str(rootcanal_hci_port)
return info
def teardown_class_core(rootcanal_running, rootcanal_process, rootcanal_logger, subprocess_wait_timeout_seconds):
if rootcanal_running:
stop_signal = signal.SIGINT
rootcanal_process.send_signal(stop_signal)
try:
return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds)
except subprocess.TimeoutExpired:
logging.error("Failed to interrupt root canal via SIGINT, sending SIGKILL")
stop_signal = signal.SIGKILL
rootcanal_process.kill()
try:
return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds)
except subprocess.TimeoutExpired:
logging.error("Failed to kill root canal")
return_code = -65536
if return_code != 0 and return_code != -stop_signal:
logging.error("rootcanal stopped with code: %d" % return_code)
rootcanal_logger.stop()
def dump_crashes_core(dut, cert, rootcanal_running, rootcanal_process, rootcanal_logpath):
dut_crash, dut_log_tail = dut.get_crash_snippet_and_log_tail()
cert_crash, cert_log_tail = cert.get_crash_snippet_and_log_tail()
rootcanal_crash = None
rootcanal_log_tail = None
if rootcanal_running and not is_subprocess_alive(rootcanal_process):
rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(rootcanal_logpath)
crash_detail = ""
if dut_crash or cert_crash or rootcanal_crash:
if rootcanal_crash:
crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash
if dut_crash:
crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash
if cert_crash:
crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash
else:
if rootcanal_log_tail:
crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail
if dut_log_tail:
crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail
if cert_log_tail:
crash_detail += "cert log tail:\n\n%s\n\n" % cert_log_tail
return crash_detail
class TopshimBaseTest(base_test.BaseTestClass):
def setup_class(self):
super().setup_test()
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_test_core(
verbose_mode=self.verbose_mode,
log_path_base=self.log_path_base,
controller_configs=self.controller_configs)
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']
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']
controllers = self.register_controller(importlib.import_module('rust.topshim.facade.topshim_device'))
self.cert_port = controllers[0].grpc_port
self.dut_port = controllers[1].grpc_port
def test_empty(self):
pass
def teardown_test(self):
return super().teardown_test()
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=1)