1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import importlib 18import logging 19import os 20import signal 21import subprocess 22import traceback 23 24from functools import wraps 25from grpc import RpcError 26 27from cert.async_subprocess_logger import AsyncSubprocessLogger 28from cert.os_utils import get_gd_root 29from cert.os_utils import read_crash_snippet_and_log_tail 30from cert.os_utils import is_subprocess_alive 31from cert.os_utils import make_ports_available 32from cert.os_utils import TerminalColor 33from cert.gd_device import MOBLY_CONTROLLER_CONFIG_NAME as CONTROLLER_CONFIG_NAME 34from facade import rootservice_pb2 as facade_rootservice 35 36 37def setup_class_core(dut_module, cert_module, verbose_mode, log_path_base, controller_configs): 38 info = {} 39 info['dut_module'] = dut_module 40 info['cert_module'] = cert_module 41 info['controller_configs'] = controller_configs 42 43 # Start root-canal if needed 44 info['rootcanal_running'] = False 45 info['rootcanal_logpath'] = "" 46 info['rootcanal_process'] = None 47 info['rootcanal_logger'] = None 48 if 'rootcanal' in info['controller_configs']: 49 info['rootcanal_running'] = True 50 # Get root canal binary 51 rootcanal = os.path.join(get_gd_root(), "root-canal") 52 info['rootcanal'] = rootcanal 53 info['rootcanal_exist'] = os.path.isfile(rootcanal) 54 if not os.path.isfile(rootcanal): 55 return info 56 57 # Get root canal log 58 rootcanal_logpath = os.path.join(log_path_base, 'rootcanal_logs.txt') 59 info['rootcanal_logpath'] = rootcanal_logpath 60 # Make sure ports are available 61 rootcanal_config = info['controller_configs']['rootcanal'] 62 rootcanal_test_port = int(rootcanal_config.get("test_port", "6401")) 63 rootcanal_hci_port = int(rootcanal_config.get("hci_port", "6402")) 64 rootcanal_link_layer_port = int(rootcanal_config.get("link_layer_port", "6403")) 65 66 info['make_rootcanal_ports_available'] = make_ports_available((rootcanal_test_port, rootcanal_hci_port, 67 rootcanal_link_layer_port)) 68 if not make_ports_available((rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)): 69 return info 70 71 # Start root canal process 72 rootcanal_cmd = [rootcanal, str(rootcanal_test_port), str(rootcanal_hci_port), str(rootcanal_link_layer_port)] 73 info['rootcanal_cmd'] = rootcanal_cmd 74 75 rootcanal_process = subprocess.Popen( 76 rootcanal_cmd, 77 cwd=get_gd_root(), 78 env=os.environ.copy(), 79 stdout=subprocess.PIPE, 80 stderr=subprocess.STDOUT, 81 universal_newlines=True) 82 83 info['rootcanal_process'] = rootcanal_process 84 if rootcanal_process: 85 info['is_rootcanal_process_started'] = True 86 else: 87 info['is_rootcanal_process_started'] = False 88 return info 89 info['is_subprocess_alive'] = is_subprocess_alive(rootcanal_process) 90 if not is_subprocess_alive(rootcanal_process): 91 info['is_subprocess_alive'] = False 92 return info 93 94 info['rootcanal_logger'] = AsyncSubprocessLogger( 95 rootcanal_process, [rootcanal_logpath], 96 log_to_stdout=verbose_mode, 97 tag="rootcanal", 98 color=TerminalColor.MAGENTA) 99 100 # Modify the device config to include the correct root-canal port 101 for gd_device_config in info['controller_configs'].get("GdDevice"): 102 gd_device_config["rootcanal_port"] = str(rootcanal_hci_port) 103 104 return info 105 106 107def teardown_class_core(rootcanal_running, rootcanal_process, rootcanal_logger, subprocess_wait_timeout_seconds): 108 if rootcanal_running: 109 stop_signal = signal.SIGINT 110 rootcanal_process.send_signal(stop_signal) 111 try: 112 return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds) 113 except subprocess.TimeoutExpired: 114 logging.error("Failed to interrupt root canal via SIGINT, sending SIGKILL") 115 stop_signal = signal.SIGKILL 116 rootcanal_process.kill() 117 try: 118 return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds) 119 except subprocess.TimeoutExpired: 120 logging.error("Failed to kill root canal") 121 return_code = -65536 122 if return_code != 0 and return_code != -stop_signal: 123 logging.error("rootcanal stopped with code: %d" % return_code) 124 rootcanal_logger.stop() 125 126 127def setup_test_core(dut, cert, dut_module, cert_module): 128 dut.rootservice.StartStack( 129 facade_rootservice.StartStackRequest(module_under_test=facade_rootservice.BluetoothModule.Value(dut_module),)) 130 cert.rootservice.StartStack( 131 facade_rootservice.StartStackRequest(module_under_test=facade_rootservice.BluetoothModule.Value(cert_module),)) 132 133 dut.wait_channel_ready() 134 cert.wait_channel_ready() 135 136 137def teardown_test_core(cert, dut): 138 cert.rootservice.StopStack(facade_rootservice.StopStackRequest()) 139 dut.rootservice.StopStack(facade_rootservice.StopStackRequest()) 140 141 142def dump_crashes_core(dut, cert, rootcanal_running, rootcanal_process, rootcanal_logpath): 143 dut_crash, dut_log_tail = dut.get_crash_snippet_and_log_tail() 144 cert_crash, cert_log_tail = cert.get_crash_snippet_and_log_tail() 145 rootcanal_crash = None 146 rootcanal_log_tail = None 147 if rootcanal_running and not is_subprocess_alive(rootcanal_process): 148 rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(rootcanal_logpath) 149 150 crash_detail = "" 151 if dut_crash or cert_crash or rootcanal_crash: 152 if rootcanal_crash: 153 crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash 154 if dut_crash: 155 crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash 156 if cert_crash: 157 crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash 158 else: 159 if rootcanal_log_tail: 160 crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail 161 if dut_log_tail: 162 crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail 163 if cert_log_tail: 164 crash_detail += "cert log tail:\n\n%s\n\n" % cert_log_tail 165 166 return crash_detail 167