Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upmerge #17

Merged
merged 18 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion autopts/bot/iut_config/zephyr.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
},
"test_cases": [
'VOCS', 'VCS', 'AICS', 'IAS', 'PACS', 'ASCS', 'BAP', 'HAS', 'CSIS', 'MICP',
'MICS', 'VCP', 'MCP', 'CAP', 'BASS', 'GMCS', 'CCP'
'MICS', 'VCP', 'MCP', 'CAP', 'BASS', 'GMCS', 'CCP', 'HAP'
]
},

Expand Down
45 changes: 31 additions & 14 deletions autopts/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from cliparser import CliParser

log = logging.debug
log_lock = threading.RLock()

RUNNING_TEST_CASE = {}
TEST_CASE_DB = None
Expand All @@ -69,6 +70,11 @@
AUTO_PTS_LOCAL = "AUTO_PTS_LOCAL" in os.environ


def logger_log(self, *args, **kwargs):
with log_lock:
self.original_log(*args, **kwargs)


class PtsServerProxy(xmlrpc.client.ServerProxy):
"""Client to remote autoptsserver"""
def __init__(self, server_address, server_port):
Expand Down Expand Up @@ -405,6 +411,12 @@ def get_test_data_path(pts):
def init_logging(tag=""):
"""Initialize logging"""
root_logger = logging.getLogger('root')

# Monkey-patch this to make use of logger lock
if not hasattr(logging.Logger, 'original_log'):
logging.Logger.original_log = logging.Logger._log
logging.Logger._log = logger_log

if root_logger.handlers:
# Already inited
return
Expand Down Expand Up @@ -905,21 +917,26 @@ def cancel_sync_points(self):
def interrupt(self):
self.cancel_sync_points()

while not get_global_end():
try:
# Acquire the logger lock to prevent breaking the lock or other
# logger handles at interrupt.
if not logging._lock.acquire(True, timeout=1):
continue
# Acquire the lock to prevent breaking pre- or post-test-case steps,
# which crucial and do not fail in general.
while not self.interrupt_lock.acquire(True, timeout=1) and not get_global_end():
# Ctrl + C friendly loop
pass

try:
super().interrupt()
return
finally:
logging._lock.release()
except BaseException as e:
logging.exception(e)
traceback.print_exc()
# Acquire the logger lock to prevent breaking the lock or other
# logger handles at interrupt.
while not log_lock.acquire(True, timeout=1) and not get_global_end():
# Ctrl + C friendly loop
pass

try:
super().interrupt()
except BaseException as e:
logging.exception(e)
traceback.print_exc()
finally:
log_lock.release()
self.interrupt_lock.release()

def _run_test_case(self, pts, test_case, exceptions, finish_count):
"""Runs the test case specified by a TestCase instance."""
Expand Down
55 changes: 30 additions & 25 deletions autopts/ptscontrol.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,6 @@ class PTSLogger(win32com.server.connect.ConnectableServer):
_reg_progid_ = "autopts.PTSLogger"
_public_methods_ = ['Log'] + win32com.server.connect.ConnectableServer._public_methods_

def __getattribute__(self, name):
if super().__getattribute__('_end') and name in ['Log']:
raise AlreadyClosedException

return super().__getattribute__(name)

def __init__(self):
""""Constructor"""
super().__init__()
Expand All @@ -109,6 +103,7 @@ def __init__(self):
self._test_case_name = None
self._end = False
self._tc_status = ResultWithFlag()
self.in_call = False

def close(self):
self._end = True
Expand Down Expand Up @@ -145,6 +140,11 @@ def Log(self, log_type, logtype_string, log_time, log_message):
};
"""

if self._end:
raise AlreadyClosedException

self.in_call = True

logger = logging.getChild(self.__class__.__name__)

logger.info("%d %s %s %s", log_type, logtype_string, log_time, log_message)
Expand Down Expand Up @@ -178,6 +178,8 @@ def Log(self, log_type, logtype_string, log_time, log_message):
if not self._tc_status.is_set():
self._tc_status.set(None)
logging.exception(e)
finally:
self.in_call = False

def get_test_case_status(self, timeout):
return self._tc_status.get(timeout=timeout, predicate=lambda: not self._end)
Expand All @@ -190,19 +192,14 @@ class PTSSender(win32com.server.connect.ConnectableServer):
_reg_progid_ = "autopts.PTSSender"
_public_methods_ = ['OnImplicitSend'] + win32com.server.connect.ConnectableServer._public_methods_

def __getattribute__(self, name):
if super().__getattribute__('_end') and name in ['OnImplicitSend']:
raise AlreadyClosedException

return super().__getattribute__(name)

def __init__(self):
""""Constructor"""
super().__init__()

self._callback = None
self._end = False
self._response = ResultWithFlag()
self.in_call = False

def close(self):
self._end = True
Expand Down Expand Up @@ -235,6 +232,11 @@ def OnImplicitSend(self, project_name, wid, test_case, description, style):
[in] unsigned long style);
};
"""
if self._end:
raise AlreadyClosedException

self.in_call = True

logger = logging.getChild(self.__class__.__name__)

# Remove whitespaces from project and test case name
Expand Down Expand Up @@ -295,6 +297,8 @@ def wait_until():
logger.info("END OnImplicitSend")
logger.info("*" * 20)

self.in_call = False

return win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_BSTR,
[rsp, rsp_len, is_present])

Expand Down Expand Up @@ -566,20 +570,21 @@ def stop_pts(self):
self._ready = False

if self._pts_logger:
try:
log(f"Closing PTSLogger")
self._pts_logger.close()
self._pts_logger = None
except Exception as error:
logging.exception(repr(error))
log(f"Closing PTSLogger")
log(f"Closing PTSSender")
# No new calls to the PTS callbacks after closing
self._pts_logger.close()
self._pts_sender.close()

if self._pts_sender:
try:
log(f"Closing PTSSender")
self._pts_sender.close()
self._pts_sender = None
except Exception as error:
logging.exception(repr(error))
# Wait until the PTS callbacks are out of fn call to decrease
# a chance of breaking the log file handler lock
for i in range(10):
if not self._pts_logger.in_call and not self._pts_sender.in_call:
break
time.sleep(1)

self._pts_logger = None
self._pts_sender = None

if self._pts:
pid = self._get_process_pid()
Expand Down
2 changes: 2 additions & 0 deletions autopts/ptsprojects/stack/layers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@
from .vcp import *
from .vcs import *
from .vocs import *
from .cap import *
from .csip import *
# GENERATOR append 1
1 change: 1 addition & 0 deletions autopts/ptsprojects/stack/layers/bap.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def __init__(self):

def __init__(self):
self.peers = {}
self.ase_configs = []
self.broadcast_id = 0x1000000 # Invalid Broadcast ID
self.broadcast_code = ''
self.event_queues = {
Expand Down
47 changes: 47 additions & 0 deletions autopts/ptsprojects/stack/layers/cap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# auto-pts - The Bluetooth PTS Automation Framework
#
# Copyright (c) 2024, Codecoup.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
from autopts.ptsprojects.stack.common import wait_for_queue_event
from autopts.pybtp import defs


class CAP:
def __init__(self):
self.event_queues = {
defs.CAP_EV_DISCOVERY_COMPLETED: [],
defs.CAP_EV_UNICAST_START_COMPLETED: [],
defs.CAP_EV_UNICAST_STOP_COMPLETED: [],
}

def event_received(self, event_type, event_data_tuple):
self.event_queues[event_type].append(event_data_tuple)

def wait_discovery_completed_ev(self, addr_type, addr, timeout, remove=True):
return wait_for_queue_event(
self.event_queues[defs.CAP_EV_DISCOVERY_COMPLETED],
lambda _addr_type, _addr, *_:
(addr_type, addr) == (_addr_type, _addr),
timeout, remove)

def wait_unicast_start_completed_ev(self, cig_id, timeout, remove=True):
return wait_for_queue_event(
self.event_queues[defs.CAP_EV_UNICAST_START_COMPLETED],
lambda _cig_id: cig_id == _cig_id,
timeout, remove)

def wait_unicast_stop_completed_ev(self, cig_id, timeout, remove=True):
return wait_for_queue_event(
self.event_queues[defs.CAP_EV_UNICAST_STOP_COMPLETED],
lambda _cig_id, *_: cig_id == _cig_id,
timeout, remove)
26 changes: 26 additions & 0 deletions autopts/ptsprojects/stack/layers/csip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# auto-pts - The Bluetooth PTS Automation Framework
#
# Copyright (c) 2024, Codecoup.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#

from autopts.ptsprojects.stack.common import wait_for_queue_event
from autopts.pybtp import defs


class CSIP:
def __init__(self):
self.event_queues = {
}

def event_received(self, event_type, event_data):
self.event_queues[event_type].append(event_data)
14 changes: 7 additions & 7 deletions autopts/ptsprojects/stack/layers/gattcl.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,37 +50,37 @@ def set_event_to_await(self, event):
def wait_for_rsp_event(self, timeout=30):
return wait_for_event(timeout, self.event_to_await)

def is_mtu_exchanged(self, args):
def is_mtu_exchanged(self, *args):
return self.mtu_exchanged.data

def wait_for_mtu_exchange(self, timeout=30):
return wait_for_event(timeout, self.is_mtu_exchanged)

def is_prim_disc_complete(self, args):
def is_prim_disc_complete(self, *args):
return is_procedure_done(self.prim_svcs, self.prim_svcs_cnt)

def wait_for_prim_svcs(self, timeout=30):
return wait_for_event(timeout, self.is_prim_disc_complete)

def is_incl_disc_complete(self, args):
def is_incl_disc_complete(self, *args):
return is_procedure_done(self.incl_svcs, self.incl_svcs_cnt)

def wait_for_incl_svcs(self, timeout=30):
return wait_for_event(timeout, self.is_incl_disc_complete)

def is_chrcs_disc_complete(self, args):
def is_chrcs_disc_complete(self, *args):
return is_procedure_done(self.chrcs, self.chrcs_cnt)

def wait_for_chrcs(self, timeout=30):
return wait_for_event(timeout, self.is_chrcs_disc_complete)

def is_dscs_disc_complete(self, args):
def is_dscs_disc_complete(self, *args):
return is_procedure_done(self.dscs, self.dscs_cnt)

def wait_for_descs(self, timeout=30):
return wait_for_event(timeout, self.is_dscs_disc_complete)

def is_read_complete(self, args):
def is_read_complete(self, *args):
return self.verify_values != []

def wait_for_read(self, timeout=30):
Expand All @@ -95,7 +95,7 @@ def wait_for_notifications(self, timeout=30, expected_count=0):
return wait_for_event(timeout,
self.is_notification_rxed, expected_count)

def is_write_completed(self, args):
def is_write_completed(self, *args):
return self.write_status is not None

def wait_for_write_rsp(self, timeout=30):
Expand Down
16 changes: 16 additions & 0 deletions autopts/ptsprojects/stack/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"MCP": 1 << defs.BTP_SERVICE_ID_MCP,
"GMCS": 1 << defs.BTP_SERVICE_ID_GMCS,
"HAP": 1 << defs.BTP_SERVICE_ID_HAP,
"CAP": 1 << defs.BTP_SERVICE_ID_CAP,
"CSIP": 1 << defs.BTP_SERVICE_ID_CSIP,
# GENERATOR append 1
}

Expand Down Expand Up @@ -76,6 +78,8 @@ def __init__(self):
self.mcp = None
self.gmcs = None
self.hap = None
self.cap = None
self.csip = None
# GENERATOR append 2

def is_svc_supported(self, svc):
Expand Down Expand Up @@ -157,6 +161,12 @@ def vcp_init(self):
def hap_init(self):
self.hap = HAP()

def cap_init(self):
self.cap = CAP()

def csip_init(self):
self.csip = CSIP()

# GENERATOR append 3

def cleanup(self):
Expand Down Expand Up @@ -220,6 +230,12 @@ def cleanup(self):
if self.hap:
self.hap_init()

if self.cap:
self.cap_init()

if self.csip:
self.csip_init()

# GENERATOR append 4


Expand Down
Loading
Loading