apetest.plugin.controlcenter
module
Plugin that monitors the SoftFab Control Center's log while it is being tested.
This plugin is only useful as-is for people working on SoftFab, but it can serve as an example for implementing a custom plugin that checks the log of a web app under test.
Source code
# SPDX-License-Identifier: BSD-3-Clause
"""Plugin that monitors the SoftFab Control Center's log
while it is being tested.
This plugin is only useful as-is for people working on SoftFab, but it
can serve as an example for implementing a custom plugin that checks the
log of a web app under test.
"""
import os
from apetest.plugin import Plugin
def plugin_arguments(parser):
parser.add_argument(
'--cclog',
help='log file to monitor for Control Center database changes'
)
def plugin_create(args):
if args.cclog is not None:
yield DataChangeMonitor(args.cclog)
class DataChangeMonitor(Plugin):
"""Monitors the log file for reported database changes.
HTTP `GET` requests must be idempotent, so any database activity
resulting from them is suspect.
"""
def __init__(self, cclog):
"""Initialize a monitor for the log at file path `cclog`."""
self._log_file = cclog
self._log_fd = None
self._partial_line = b''
def __process_data(self, report):
if self._log_fd is None:
try:
self._log_fd = os.open(
self._log_file, os.O_RDONLY | os.O_NONBLOCK
)
except OSError as ex:
report.warning('Could not open log file for reading: %s', ex)
return
while True:
new_data = os.read(self._log_fd, 200)
if new_data:
buf = self._partial_line + new_data
lines = buf.split(b'\n')
for line in lines[ : -1]:
line = line.decode('ascii')
index = line.find('> datachange/')
if index >= 0:
marker_, db_name, change, record_id = \
line[index + 2 : ].split('/')
yield change, db_name, record_id
self._partial_line = lines[-1]
else:
break
def report_added(self, report):
for change, db_name, record_id in self.__process_data(report):
if (db_name, change) in (
# Shadow DB has automatic cleanup.
('shadow', 'remove'),
# These are singleton records that are created
# automatically.
# Note that only "add" is accepted, "update" is not.
('project', 'add'),
# Schedule start times will automatically update if the
# current time is past the stored start time.
('scheduled', 'update'),
# Reserved resource types are created automatically.
# The code below checks whether the type was indeed
# reserved.
('restypes', 'add')):
if db_name != 'restypes' or record_id.startswith('sf.'):
continue
report.warning(
'Unexpected %s in database "%s" on record "%s"',
change, db_name, record_id
)}
Functions
def plugin_arguments(parser)
-
Source code
def plugin_arguments(parser): parser.add_argument( '--cclog', help='log file to monitor for Control Center database changes' )}
def plugin_create(args)
-
Source code
def plugin_create(args): if args.cclog is not None: yield DataChangeMonitor(args.cclog)}
Classes
class DataChangeMonitor (ancestors: Plugin)
-
Monitors the log file for reported database changes.
HTTP
GET
requests must be idempotent, so any database activity resulting from them is suspect.Source code
class DataChangeMonitor(Plugin): """Monitors the log file for reported database changes. HTTP `GET` requests must be idempotent, so any database activity resulting from them is suspect. """ def __init__(self, cclog): """Initialize a monitor for the log at file path `cclog`.""" self._log_file = cclog self._log_fd = None self._partial_line = b'' def __process_data(self, report): if self._log_fd is None: try: self._log_fd = os.open( self._log_file, os.O_RDONLY | os.O_NONBLOCK ) except OSError as ex: report.warning('Could not open log file for reading: %s', ex) return while True: new_data = os.read(self._log_fd, 200) if new_data: buf = self._partial_line + new_data lines = buf.split(b'\n') for line in lines[ : -1]: line = line.decode('ascii') index = line.find('> datachange/') if index >= 0: marker_, db_name, change, record_id = \ line[index + 2 : ].split('/') yield change, db_name, record_id self._partial_line = lines[-1] else: break def report_added(self, report): for change, db_name, record_id in self.__process_data(report): if (db_name, change) in ( # Shadow DB has automatic cleanup. ('shadow', 'remove'), # These are singleton records that are created # automatically. # Note that only "add" is accepted, "update" is not. ('project', 'add'), # Schedule start times will automatically update if the # current time is past the stored start time. ('scheduled', 'update'), # Reserved resource types are created automatically. # The code below checks whether the type was indeed # reserved. ('restypes', 'add')): if db_name != 'restypes' or record_id.startswith('sf.'): continue report.warning( 'Unexpected %s in database "%s" on record "%s"', change, db_name, record_id )}
Methods
def __init__(self, cclog)
-
Initialize a monitor for the log at file path
cclog
.Source code
def __init__(self, cclog): """Initialize a monitor for the log at file path `cclog`.""" self._log_file = cclog self._log_fd = None self._partial_line = b''}
Inherited members