Skip to content
Open
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
18 changes: 18 additions & 0 deletions src/integrationtest/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ class list_element_addition(config_substitution):
additional_object_class: str = ""
additional_object_id: str = ""

class PosixSignal(Enum):
SIGINT = 2
SIGKILL = 9
SIGUSR1 = 10
SIGUSR2 = 12
SIGTERM = 15
SIGCONT = 18
SIGSTOP = 19

@dataclass
class system_signal_config:
application_label: str
signal: PosixSignal
delay_s: int
application_name: str = "daq_application"

class ConnSvcControl(Enum):
INTEGRATIONTEST = "integrationtest"
Expand Down Expand Up @@ -73,6 +88,9 @@ class integtest_param_base_class:
# command-line arguments to be passed to run control
dunerc_cmd_args: list[str] = field(default_factory=list)

# Signals to send to applications during the test
system_signal_configs: list[system_signal_config] = field(default_factory=list)

@dataclass
class integtest_params_for_generated_dunedaq_config(integtest_param_base_class):
# *** Parameters that are needed for both generated and predefined configs,
Expand Down
78 changes: 78 additions & 0 deletions src/integrationtest/integrationtest_drunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,75 @@
import time
import random
import json
import signal
import threading


# keep track of the number of parametrizations (for various display uses)
total_paramtrization_combinations = 0
parametrization_counter = 0


def find_and_signal_process(application_name, label, wait_time=0, sig=signal.SIGTERM):
"""
Locate a process with a given application name and label, wait for some time,
and send a signal to that process if it still exists.
"""
try:
# Wait for the specified time
if wait_time > 0:
time.sleep(wait_time)

# Run ps aux to get process list
result = subprocess.run(
["ps", "aux"],
capture_output=True,
text=True,
check=True
)

# Parse the output to find matching process
pid = None
for line in result.stdout.splitlines():
# Split the line into parts
parts = line.split()

# ps aux format: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# COMMAND is the 11th column (index 10)
if len(parts) >= 11:
command = parts[10]

if command.endswith(application_name) or command == application_name:
if f"-n {label}" in line:
# Extract PID (second column, index 1)
try:
pid = int(parts[1])
break
except ValueError:
continue

if pid is None:
print(f">>integrationtest_drunc<< No processes found which match application_name ${application_name} and label ${label}")
print("", flush=True)
return None, False

# Check if process still exists and send signal
try:
print(f">>integrationtest_drunc<< Sending signal {sig} to process with PID {pid} (application_name: {application_name}, label: {label})")
print("", flush=True)
os.kill(pid, 0)
os.kill(pid, sig)
return pid, True
except OSError:
# Process no longer exists
return pid, False

except subprocess.CalledProcessError:
return None, False
except Exception:
return None, False


def parametrize_fixture_with_items(metafunc, fixture, itemsname):
"""Parametrize a fixture using the contents of variable `listname`
from module scope. We want to distinguish between the cases where
Expand Down Expand Up @@ -613,6 +675,22 @@ class RunResult:
cwd=run_dir
)

# Start threads for each system signal config
signal_threads = []
for signal_config in create_config_files.integtest_params.system_signal_configs:
thread = threading.Thread(
target=find_and_signal_process,
args=(
signal_config.application_name,
signal_config.application_label,
signal_config.delay_s,
signal_config.signal.value
),
daemon=True
)
thread.start()
signal_threads.append(thread)

# print out each line of captured output, subject to the verbosity level that the
# user has requested, as well as add it to the string that we pass back to the user
tmp_string = request.config.getoption("--dunerc-fullprint-watch-string")
Expand Down