From 60c280b4546196af94115aced7f64ad0299e9fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Mon, 14 Nov 2022 00:09:19 +0100 Subject: [PATCH] Some integration tests --- Software/.gitignore | 2 + Software/Integrationtests/DUMMY.CALKIT | 60 ++++++++++++++ Software/Integrationtests/Integrationtest.py | 22 +++++ Software/Integrationtests/tests/TestBase.py | 39 +++++++++ .../Integrationtests/tests/TestCalibration.py | 82 +++++++++++++++++++ .../Integrationtests/tests/TestConnect.py | 13 +++ Software/Integrationtests/tests/TestMode.py | 8 ++ .../Integrationtests/tests/TestVNASweep.py | 54 ++++++++++++ Software/Integrationtests/tests/__init__.py | 0 .../Integrationtests/tests/definitions.py | 1 + Software/Integrationtests/tests/libreVNA.py | 1 + 11 files changed, 282 insertions(+) create mode 100644 Software/Integrationtests/DUMMY.CALKIT create mode 100644 Software/Integrationtests/Integrationtest.py create mode 100644 Software/Integrationtests/tests/TestBase.py create mode 100644 Software/Integrationtests/tests/TestCalibration.py create mode 100644 Software/Integrationtests/tests/TestConnect.py create mode 100644 Software/Integrationtests/tests/TestMode.py create mode 100644 Software/Integrationtests/tests/TestVNASweep.py create mode 100644 Software/Integrationtests/tests/__init__.py create mode 100644 Software/Integrationtests/tests/definitions.py create mode 120000 Software/Integrationtests/tests/libreVNA.py diff --git a/Software/.gitignore b/Software/.gitignore index 2d14f1f..f7f8214 100644 --- a/Software/.gitignore +++ b/Software/.gitignore @@ -1,3 +1,5 @@ /.metadata/ RemoteSystemsTempFiles build-* +*.pyc + diff --git a/Software/Integrationtests/DUMMY.CALKIT b/Software/Integrationtests/DUMMY.CALKIT new file mode 100644 index 0000000..cbff17e --- /dev/null +++ b/Software/Integrationtests/DUMMY.CALKIT @@ -0,0 +1,60 @@ +{ + "Description": "Dummy ideal calibration kit", + "Manufacturer": "LibreVNA", + "Serialnumber": "1234", + "standards": [ + { + "params": { + "C0": 0.0, + "C1": 0.0, + "C2": 0.0, + "C3": 0.0, + "Z0": 50.0, + "delay": 0.0, + "id": 13726173464103258082, + "loss": 0.0, + "name": "Default" + }, + "type": "Open" + }, + { + "params": { + "L0": 0.0, + "L1": 0.0, + "L2": 0.0, + "L3": 0.0, + "Z0": 50.0, + "delay": 0.0, + "id": 13573063103112146609, + "loss": 0.0, + "name": "Default" + }, + "type": "Short" + }, + { + "params": { + "Cfirst": true, + "Cparallel": 0.0, + "Lseries": 0.0, + "Z0": 50.0, + "delay": 0.0, + "id": 11987083411312060381, + "loss": 0.0, + "name": "Default", + "resistance": 50.0 + }, + "type": "Load" + }, + { + "params": { + "Z0": 50.0, + "delay": 0.0, + "id": 2916288069679020801, + "loss": 0.0, + "name": "Default" + }, + "type": "Through" + } + ], + "version": "1.5.0-alpha.1-9b7f457aa" +} diff --git a/Software/Integrationtests/Integrationtest.py b/Software/Integrationtests/Integrationtest.py new file mode 100644 index 0000000..b7987f9 --- /dev/null +++ b/Software/Integrationtests/Integrationtest.py @@ -0,0 +1,22 @@ +import unittest + +testmodules = [ + 'tests.TestConnect', + 'tests.TestMode', + 'tests.TestVNASweep', + 'tests.TestCalibration', + ] + +suite = unittest.TestSuite() + +for t in testmodules: + try: + # If the module defines a suite() function, call it to get the suite. + mod = __import__(t, globals(), locals(), ['suite']) + suitefn = getattr(mod, 'suite') + suite.addTest(suitefn()) + except (ImportError, AttributeError): + # else, just load all the test cases from the module. + suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t)) + +unittest.TextTestRunner().run(suite) diff --git a/Software/Integrationtests/tests/TestBase.py b/Software/Integrationtests/tests/TestBase.py new file mode 100644 index 0000000..4e3a4e7 --- /dev/null +++ b/Software/Integrationtests/tests/TestBase.py @@ -0,0 +1,39 @@ +import unittest +from tests.libreVNA import libreVNA as libreVNA +import tests.definitions as defs +import subprocess +import time +import select +from signal import SIGINT + +class TestBase(unittest.TestCase): + def setUp(self): + self.gui = subprocess.Popen([defs.GUI_PATH, '-p', '19543'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + # wait for the SCPI server to become available + timeout = time.time() + 3; + poll_obj = select.poll() + poll_obj.register(self.gui.stdout, select.POLLIN) + while time.time() < timeout: + poll_result = poll_obj.poll(0) + if poll_result: + line = self.gui.stdout.readline().decode().strip() + if "Listening on port 19543" in line: + break + + time.sleep(0.2) + + self.vna = libreVNA('localhost', 19543) + self.vna.cmd(":DEV:CONN") + if self.vna.query(":DEV:CONN?") == "Not connected": + self.tearDown() + raise AssertionError("Not connected") + + def tearDown(self): + self.gui.send_signal(SIGINT) + try: + self.gui.wait(timeout = 0.1) + except subprocess.TimeoutExpired: + self.gui.kill() + + \ No newline at end of file diff --git a/Software/Integrationtests/tests/TestCalibration.py b/Software/Integrationtests/tests/TestCalibration.py new file mode 100644 index 0000000..9ab15af --- /dev/null +++ b/Software/Integrationtests/tests/TestCalibration.py @@ -0,0 +1,82 @@ +from tests.TestBase import TestBase +import time + +class TestCalibration(TestBase): + def cal_measure(self, number): + self.vna.cmd(":VNA:CAL:MEAS "+str(number)) + # wait for the measurement to finish + timeout = time.time() + 3 + while self.vna.query(":VNA:CAL:BUSY?") == "TRUE": + if time.time() > timeout: + raise AssertionError("Calibration measurement timed out") + time.sleep(0.1) + + def test_dummy_calibration(self): + # This test just iterates through the calibration steps. As no actual standards + # are applied to the ports, the calibration result is just random data + # Set up the sweep first + self.vna.cmd(":DEV:MODE VNA") + self.vna.cmd(":VNA:SWEEP FREQUENCY") + self.vna.cmd(":VNA:STIM:LVL -10") + self.vna.cmd(":VNA:ACQ:IFBW 50000") + self.vna.cmd(":VNA:ACQ:AVG 1") + self.vna.cmd(":VNA:ACQ:POINTS 501") + self.vna.cmd(":VNA:FREQuency:START 1000000") + self.vna.cmd(":VNA:FREQuency:STOP 6000000000") + + self.vna.cmd(":VNA:CAL:RESET") + + # No measurements yet, activating should fail + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_1"), "ERROR") + + # Load calibration kit + self.assertEqual(self.vna.query(":VNA:CAL:KIT:LOAD? DUMMY.CALKIT"), "TRUE") + + # Lets take the measurements for port 1 first + self.vna.cmd(":VNA:CAL:ADD OPEN") + self.vna.cmd(":VNA:CAL:ADD SHORT") + self.vna.cmd(":VNA:CAL:ADD LOAD") + self.vna.cmd(":VNA:CAL:PORT 0 1") + self.vna.cmd(":VNA:CAL:PORT 1 1") + self.vna.cmd(":VNA:CAL:PORT 2 1") + + self.cal_measure(0) + self.cal_measure(1) + self.cal_measure(2) + + # SOLT_1 should now be available + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_1"), "") + + # SOLT_2 and SOLT_12 should still be unavailable + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_2"), "ERROR") + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_12"), "ERROR") + + # Take measurements for SOLT_2 + self.vna.cmd(":VNA:CAL:ADD OPEN") + self.vna.cmd(":VNA:CAL:ADD SHORT") + self.vna.cmd(":VNA:CAL:ADD LOAD") + self.vna.cmd(":VNA:CAL:PORT 3 2") + self.vna.cmd(":VNA:CAL:PORT 4 2") + self.vna.cmd(":VNA:CAL:PORT 5 2") + + self.cal_measure(3) + self.cal_measure(4) + self.cal_measure(5) + + # SOLT_1 and SOLT_2 should now be available + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_1"), "") + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_2"), "") + + # SOLT_12 should still be unavailable + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_12"), "ERROR") + + # Take the final through measurement for SOLT_12 + self.vna.cmd(":VNA:CAL:ADD THROUGH") + self.vna.cmd(":VNA:CAL:PORT 6 1 2") + + self.cal_measure(6) + + # SOLT_1, SOLT_2 and SOLT_12 should now be available + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_1"), "") + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_2"), "") + self.assertEqual(self.vna.query(":VNA:CAL:ACT SOLT_12"), "") diff --git a/Software/Integrationtests/tests/TestConnect.py b/Software/Integrationtests/tests/TestConnect.py new file mode 100644 index 0000000..196bbdc --- /dev/null +++ b/Software/Integrationtests/tests/TestConnect.py @@ -0,0 +1,13 @@ +from tests.TestBase import TestBase + +class TestConnect(TestBase): + def test_connection(self): + self.vna.cmd(":DEV:DISC") + self.assertEqual(self.vna.query(":DEV:CONN?"), "Not connected") + self.vna.cmd(":DEV:CONN") + self.assertNotEqual(self.vna.query(":DEV:CONN?"), "Not connected") + + def test_list(self): + list = self.vna.query(":DEV:LIST?") + serials = list.split(',') + self.assertEqual(len(serials), 1) diff --git a/Software/Integrationtests/tests/TestMode.py b/Software/Integrationtests/tests/TestMode.py new file mode 100644 index 0000000..78fcf8a --- /dev/null +++ b/Software/Integrationtests/tests/TestMode.py @@ -0,0 +1,8 @@ +from tests.TestBase import TestBase + +class TestMode(TestBase): + def test_Mode(self): + modes = ["VNA", "GEN", "SA"] + for m in modes: + self.vna.cmd(":DEV:MODE "+m) + self.assertEqual(self.vna.query(":DEV:MODE?"), m) diff --git a/Software/Integrationtests/tests/TestVNASweep.py b/Software/Integrationtests/tests/TestVNASweep.py new file mode 100644 index 0000000..5a15458 --- /dev/null +++ b/Software/Integrationtests/tests/TestVNASweep.py @@ -0,0 +1,54 @@ +from tests.TestBase import TestBase +import time + +class TestVNASweep(TestBase): + def test_sweep_frequency(self): + self.vna.cmd(":DEV:MODE VNA") + self.vna.cmd(":VNA:SWEEP FREQUENCY") + self.vna.cmd(":VNA:STIM:LVL -10") + self.vna.cmd(":VNA:ACQ:IFBW 10000") + self.vna.cmd(":VNA:ACQ:AVG 1") + self.vna.cmd(":VNA:ACQ:POINTS 501") + self.vna.cmd(":VNA:FREQuency:START 1000000") + self.vna.cmd(":VNA:FREQuency:STOP 6000000000") + while self.vna.query(":VNA:ACQ:FIN?") == "FALSE": + time.sleep(0.1) + + S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11")) + self.assertEqual(S11[0][0], 1000000) + self.assertEqual(S11[-1][0], 6000000000) + + def test_sweep_zerospan(self): + self.vna.cmd(":DEV:MODE VNA") + self.vna.cmd(":VNA:SWEEP FREQUENCY") + self.vna.cmd(":VNA:STIM:LVL -10") + self.vna.cmd(":VNA:ACQ:IFBW 10000") + self.vna.cmd(":VNA:ACQ:AVG 1") + self.vna.cmd(":VNA:ACQ:POINTS 501") + self.vna.cmd(":VNA:FREQuency:START 500000000") + self.vna.cmd(":VNA:FREQuency:STOP 1500000000") + self.vna.cmd(":VNA:FREQuency:ZERO 1500000000") + while self.vna.query(":VNA:ACQ:FIN?") == "FALSE": + time.sleep(0.1) + + S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11")) + self.assertEqual(S11[0][0], 0.0) + # Sweep should take about 0.125 seconds + self.assertGreater(S11[-1][0], 0.1) + self.assertLess(S11[-1][0], 0.5) + + def test_sweep_power(self): + self.vna.cmd(":DEV:MODE VNA") + self.vna.cmd(":VNA:SWEEP POWER") + self.vna.cmd(":VNA:STIM:LVL -10") + self.vna.cmd(":VNA:ACQ:IFBW 10000") + self.vna.cmd(":VNA:ACQ:AVG 1") + self.vna.cmd(":VNA:ACQ:POINTS 501") + self.vna.cmd(":VNA:POWER:START -30") + self.vna.cmd(":VNA:POWER:STOP -10") + while self.vna.query(":VNA:ACQ:FIN?") == "FALSE": + time.sleep(0.1) + + S11 = self.vna.parse_trace_data(self.vna.query(":VNA:TRACE:DATA? S11")) + self.assertEqual(S11[0][0], -30) + self.assertEqual(S11[-1][0], -10) \ No newline at end of file diff --git a/Software/Integrationtests/tests/__init__.py b/Software/Integrationtests/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Software/Integrationtests/tests/definitions.py b/Software/Integrationtests/tests/definitions.py new file mode 100644 index 0000000..1684691 --- /dev/null +++ b/Software/Integrationtests/tests/definitions.py @@ -0,0 +1 @@ +GUI_PATH = "../PC_Application/LibreVNA-GUI/LibreVNA-GUI" \ No newline at end of file diff --git a/Software/Integrationtests/tests/libreVNA.py b/Software/Integrationtests/tests/libreVNA.py new file mode 120000 index 0000000..d17ab80 --- /dev/null +++ b/Software/Integrationtests/tests/libreVNA.py @@ -0,0 +1 @@ +../../../Documentation/UserManual/SCPI_Examples/libreVNA.py \ No newline at end of file