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 if 'rootcanal' in info['controller_configs']: 46 info['rootcanal_running'] = True 47 # Get root canal binary 48 rootcanal = os.path.join(get_gd_root(), "root-canal") 49 info['rootcanal'] = rootcanal 50 info['rootcanal_exist'] = os.path.isfile(rootcanal) 51 if not os.path.isfile(rootcanal): 52 return info 53 54 # Get root canal log 55 rootcanal_logpath = os.path.join(log_path_base, 'rootcanal_logs.txt') 56 info['rootcanal_logpath'] = rootcanal_logpath 57 # Make sure ports are available 58 rootcanal_config = info['controller_configs']['rootcanal'] 59 rootcanal_test_port = int(rootcanal_config.get("test_port", "6401")) 60 rootcanal_hci_port = int(rootcanal_config.get("hci_port", "6402")) 61 rootcanal_link_layer_port = int(rootcanal_config.get("link_layer_port", "6403")) 62 63 info['make_rootcanal_ports_available'] = make_ports_available((rootcanal_test_port, rootcanal_hci_port, 64 rootcanal_link_layer_port)) 65 if not make_ports_available((rootcanal_test_port, rootcanal_hci_port, rootcanal_link_layer_port)): 66 return info 67 68 # Start root canal process 69 rootcanal_cmd = [rootcanal, str(rootcanal_test_port), str(rootcanal_hci_port), str(rootcanal_link_layer_port)] 70 info['rootcanal_cmd'] = rootcanal_cmd 71 72 rootcanal_process = subprocess.Popen( 73 rootcanal_cmd, 74 cwd=get_gd_root(), 75 env=os.environ.copy(), 76 stdout=subprocess.PIPE, 77 stderr=subprocess.STDOUT, 78 universal_newlines=True) 79 80 info['rootcanal_process'] = rootcanal_process 81 if rootcanal_process: 82 info['is_rootcanal_process_started'] = True 83 else: 84 info['is_rootcanal_process_started'] = False 85 return info 86 info['is_subprocess_alive'] = is_subprocess_alive(rootcanal_process) 87 if not is_subprocess_alive(rootcanal_process): 88 info['is_subprocess_alive'] = False 89 return info 90 91 info['rootcanal_logger'] = AsyncSubprocessLogger( 92 rootcanal_process, [rootcanal_logpath], 93 log_to_stdout=verbose_mode, 94 tag="rootcanal", 95 color=TerminalColor.MAGENTA) 96 97 # Modify the device config to include the correct root-canal port 98 for gd_device_config in info['controller_configs'].get("GdDevice"): 99 gd_device_config["rootcanal_port"] = str(rootcanal_hci_port) 100 101 return info 102 103 104def teardown_class_core(rootcanal_running, rootcanal_process, rootcanal_logger, subprocess_wait_timeout_seconds): 105 if rootcanal_running: 106 stop_signal = signal.SIGINT 107 rootcanal_process.send_signal(stop_signal) 108 try: 109 return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds) 110 except subprocess.TimeoutExpired: 111 logging.error("Failed to interrupt root canal via SIGINT, sending SIGKILL") 112 stop_signal = signal.SIGKILL 113 rootcanal_process.kill() 114 try: 115 return_code = rootcanal_process.wait(timeout=subprocess_wait_timeout_seconds) 116 except subprocess.TimeoutExpired: 117 logging.error("Failed to kill root canal") 118 return_code = -65536 119 if return_code != 0 and return_code != -stop_signal: 120 logging.error("rootcanal stopped with code: %d" % return_code) 121 rootcanal_logger.stop() 122 123 124def setup_test_core(dut, cert, dut_module, cert_module): 125 dut.rootservice.StartStack( 126 facade_rootservice.StartStackRequest(module_under_test=facade_rootservice.BluetoothModule.Value(dut_module),)) 127 cert.rootservice.StartStack( 128 facade_rootservice.StartStackRequest(module_under_test=facade_rootservice.BluetoothModule.Value(cert_module),)) 129 130 dut.wait_channel_ready() 131 cert.wait_channel_ready() 132 133 134def teardown_test_core(cert, dut): 135 cert.rootservice.StopStack(facade_rootservice.StopStackRequest()) 136 dut.rootservice.StopStack(facade_rootservice.StopStackRequest()) 137 138 139def dump_crashes_core(dut, cert, rootcanal_running, rootcanal_process, rootcanal_logpath): 140 dut_crash, dut_log_tail = dut.get_crash_snippet_and_log_tail() 141 cert_crash, cert_log_tail = cert.get_crash_snippet_and_log_tail() 142 rootcanal_crash = None 143 rootcanal_log_tail = None 144 if rootcanal_running and not is_subprocess_alive(rootcanal_process): 145 rootcanal_crash, roocanal_log_tail = read_crash_snippet_and_log_tail(rootcanal_logpath) 146 147 crash_detail = "" 148 if dut_crash or cert_crash or rootcanal_crash: 149 if rootcanal_crash: 150 crash_detail += "rootcanal crashed:\n\n%s\n\n" % rootcanal_crash 151 if dut_crash: 152 crash_detail += "dut stack crashed:\n\n%s\n\n" % dut_crash 153 if cert_crash: 154 crash_detail += "cert stack crashed:\n\n%s\n\n" % cert_crash 155 else: 156 if rootcanal_log_tail: 157 crash_detail += "rootcanal log tail:\n\n%s\n\n" % rootcanal_log_tail 158 if dut_log_tail: 159 crash_detail += "dut log tail:\n\n%s\n\n" % dut_log_tail 160 if cert_log_tail: 161 crash_detail += "cert log tail:\n\n%s\n\n" % cert_log_tail 162 163 return crash_detail 164