From fb25a01db56103c014f29d27319c874e45f98fc8 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Thu, 11 May 2023 12:15:22 +0200 Subject: [PATCH] Automate virtual test (#714) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * using argparse for parsing command line arguments * added command line option to specify which servers to run --------- Co-authored-by: Erik Fröjdh --- python/slsdet/detector.py | 6 + slsDetectorSoftware/src/Module.cpp | 2 +- slsDetectorSoftware/tests/test-CmdProxy.cpp | 6 +- tests/CMakeLists.txt | 2 + tests/scripts/test_simulators.py | 200 ++++++++++++++++++++ 5 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 tests/scripts/test_simulators.py diff --git a/python/slsdet/detector.py b/python/slsdet/detector.py index 803faee77..4c9920916 100755 --- a/python/slsdet/detector.py +++ b/python/slsdet/detector.py @@ -1441,6 +1441,10 @@ class Detector(CppDetectorApi): @udp_srcip.setter def udp_srcip(self, ip): + if ip == "auto": + if self.type == detectorType.GOTTHARD: + raise NotImplementedError('Auto for udp_srcip cannot be used for GotthardI') + ip = socket.gethostbyname(self.hostname[0]) ip = ut.make_ip(ip) ut.set_using_dict(self.setSourceUDPIP, ip) @@ -1467,6 +1471,8 @@ class Detector(CppDetectorApi): @udp_srcip2.setter def udp_srcip2(self, ip): + if ip == "auto": + ip = socket.gethostbyname(self.hostname) ip = ut.make_ip(ip) ut.set_using_dict(self.setSourceUDPIP2, ip) diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 782aa624f..f7d437927 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -3222,7 +3222,7 @@ slsDetectorDefs::detectorType Module::getDetectorTypeFromShm(int det_id, bool verify) { if (!shm.exists()) { throw SharedMemoryError("Shared memory " + shm.getName() + - "does not exist.\n Corrupted Multi Shared " + " does not exist.\n Corrupted Multi Shared " "memory. Please free shared memory."); } diff --git a/slsDetectorSoftware/tests/test-CmdProxy.cpp b/slsDetectorSoftware/tests/test-CmdProxy.cpp index 1f03f9e15..3107df290 100644 --- a/slsDetectorSoftware/tests/test-CmdProxy.cpp +++ b/slsDetectorSoftware/tests/test-CmdProxy.cpp @@ -1207,10 +1207,10 @@ TEST_CASE("dbitphase", "[.cmd]") { } { std::ostringstream oss1, oss2; - proxy.Call("dbitphase", {"20", "deg"}, -1, PUT, oss1); - REQUIRE(oss1.str() == "dbitphase 20 deg\n"); + proxy.Call("dbitphase", {"23", "deg"}, -1, PUT, oss1); + REQUIRE(oss1.str() == "dbitphase 23 deg\n"); proxy.Call("dbitphase", {"deg"}, -1, GET, oss2); - REQUIRE(oss2.str() == "dbitphase 20 deg\n"); + REQUIRE(oss2.str() == "dbitphase 23 deg\n"); } for (int i = 0; i != det.size(); ++i) { det.setDBITPhase(prev_val[i], {i}); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b30a8aa26..f222cc599 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -58,3 +58,5 @@ set_target_properties(tests PROPERTIES include(CTest) include(Catch) catch_discover_tests(tests) + +configure_file(scripts/test_simulators.py ${CMAKE_BINARY_DIR}/bin/test_simulators.py COPYONLY) diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py new file mode 100644 index 000000000..24ee6f4dd --- /dev/null +++ b/tests/scripts/test_simulators.py @@ -0,0 +1,200 @@ +# SPDX-License-Identifier: LGPL-3.0-or-other +# Copyright (C) 2021 Contributors to the SLS Detector Package +''' +This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. +''' +import argparse +import os, sys, subprocess, time, colorama, signal, psutil + +from colorama import Fore +from slsdet import Detector, detectorType, detectorSettings +from slsdet.defines import DEFAULT_TCP_CNTRL_PORTNO, DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO +HALFMOD2_TCP_CNTRL_PORTNO=1955 +HALFMOD2_TCP_RX_PORTNO=1957 + +colorama.init(autoreset=True) + +class RuntimeException (Exception): + def __init__ (self, message): + super().__init__(Fore.RED + message) + +def Log(color, message): + print('\n' + color + message, flush=True) + +def checkIfProcessRunning(processName): + ''' + Check if there is any running process that contains the given name processName. + https://gist.github.com/Sanix-Darker/8cbed2ff6f8eb108ce2c8c51acd2aa5a + ''' + # Iterate over the all the running process + for proc in psutil.process_iter(): + try: + # Check if process name contains the given name string. + if processName.lower() in proc.name().lower(): + return True + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + return False; + +def killProcess(name): + if checkIfProcessRunning(name): + Log(Fore.GREEN, 'killing ' + name) + p = subprocess.run(['killall', name]) + if p.returncode != 0: + raise RuntimeException('error in killall ' + name) + +def cleanup(name, d): + ''' + kill both servers, receivers and clean shared memory + ''' + Log(Fore.GREEN, 'Cleaning up...') + killProcess(name + 'DetectorServer_virtual') + killProcess('slsReceiver') + killProcess('slsMultiReceiver') + d.freeSharedMemory() + +def startProcessInBackground(name): + try: + # in background and dont print output + p = subprocess.Popen(name.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + Log(Fore.GREEN, 'Starting up ' + name + ' ...') + except: + Log(Fore.RED, 'Could not start ' + name) + raise + +def startServer(name): + startProcessInBackground(name + 'DetectorServer_virtual') + # second half + if name == 'eiger': + startProcessInBackground(name + 'DetectorServer_virtual -p' + str(HALFMOD2_TCP_CNTRL_PORTNO)) + tStartup = 6 + Log(Fore.WHITE, 'Takes ' + str(tStartup) + ' seconds... Please be patient') + time.sleep(tStartup) + +def startReceiver(name): + startProcessInBackground('slsReceiver') + # second half + if name == 'eiger': + startProcessInBackground('slsReceiver -t' + str(HALFMOD2_TCP_RX_PORTNO)) + time.sleep(2) + +def loadConfig(name, rx_hostname, settingsdir): + try: + d = Detector() + if name == 'eiger': + d.hostname = 'localhost:' + str(DEFAULT_TCP_CNTRL_PORTNO) + '+localhost:' + str(HALFMOD2_TCP_CNTRL_PORTNO) + #d.udp_dstport = {2: 50003} + # will set up for every module + d.udp_dstport = DEFAULT_UDP_DST_PORTNO + d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 + d.rx_hostname = rx_hostname + ':' + str(DEFAULT_TCP_RX_PORTNO) + '+' + rx_hostname + ':' + str(HALFMOD2_TCP_RX_PORTNO) + d.udp_dstip = 'auto' + d.trimen = [4500, 5400, 6400] + d.settingspath = settingsdir + '/eiger/' + d.setThresholdEnergy(4500, detectorSettings.STANDARD) + else: + d.hostname = 'localhost' + d.rx_hostname = rx_hostname + d.udp_dstip = 'auto' + if d.type == detectorType.GOTTHARD: + d.udp_srcip = d.udp_dstip + else: + d.udp_srcip = 'auto' + if d.type == detectorType.JUNGFRAU or d.type == detectorType.MOENCH: + d.powerchip = 1 + except: + Log(Fore.RED, 'Could not load config for ' + name) + raise + +def startCmdTests(name, fp): + try: + p = subprocess.run(['tests', '--abort', '[.cmd]'], stdout=fp, stderr=fp) + if p.returncode != 0: + raise Exception + except: + Log(Fore.RED, 'Cmd tests failed for ' + name) + raise + +def startNormalTests(d, fp): + try: + Log(Fore.BLUE, '\nNormal tests') + p = subprocess.run(['tests', '--abort' ], stdout=fp, stderr=fp) + if p.returncode != 0: + raise Exception + d.freeSharedMemory() + except: + Log(Fore.RED, 'Normal tests failed') + raise + + +# parse cmd line for rx_hostname and settingspath using the argparse library +parser = argparse.ArgumentParser(description = 'automated tests with the virtual detector servers') +parser.add_argument('rx_hostname', help = 'hostname/ip of the current machine') +parser.add_argument('settingspath', help = 'Relative or absolut path to the settingspath') +parser.add_argument('-s', '--servers', help='Detector servers to run', nargs='*') +args = parser.parse_args() +if args.rx_hostname == 'localhost': + raise RuntimeException('Cannot use localhost for rx_hostname for the tests (fails for rx_arping for eg.)') + +if args.servers is None: + servers = [ + 'eiger', + 'jungfrau', + 'mythen3', + 'gotthard2', + 'gotthard', + 'ctb', + 'moench', + ] +else: + servers = args.servers + +Log(Fore.WHITE, 'rx_hostname: ' + args.rx_hostname + '\settingspath: \'' + args.settingspath + '\'') + + +# handle zombies (else killing slsReceivers will fail) +# dont care about child process success +signal.signal(signal.SIGCHLD, signal.SIG_IGN) + + +# redirect to file +original_stdout = sys.stdout +original_stderr = sys.stderr +fname = '/tmp/slsDetectorPackage_virtual_test.txt' +Log(Fore.BLUE, 'Tests -> ' + fname) +with open(fname, 'w') as fp: + sys.stdout = fp + sys.stderr = fp + + d = Detector() + # TODO: redirect Detector object print out also to file + startNormalTests(d, fp) + + for server in servers: + try: + # print to terminal for progress + sys.stdout = original_stdout + sys.stderr = original_stderr + Log(Fore.BLUE, server + ' tests') + sys.stdout = fp + sys.stderr = fp + + # cmd tests for det + Log(Fore.BLUE, 'Cmd Tests for ' + server) + cleanup(server, d) + startServer(server) + startReceiver(server) + loadConfig(server, args.rx_hostname, args.settingspath) + startCmdTests(server, fp) + cleanup(server, d) + except: + cleanup(server, d) + raise + + + Log(Fore.GREEN, 'Passed all tests for virtual detectors \n' + str(servers)) + +# redirect to terminal +sys.stdout = original_stdout +sys.stderr = original_stderr +Log(Fore.GREEN, 'Passed all tests for virtual detectors \n' + str(servers) + '\nYayyyy! :) ') \ No newline at end of file