diff --git a/tests/utils/fixtures/legacy_unknown_keys.json b/tests/utils/fixtures/legacy_unknown_keys.json new file mode 100644 index 0000000..86f32c5 --- /dev/null +++ b/tests/utils/fixtures/legacy_unknown_keys.json @@ -0,0 +1,78 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Smith", + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + }, + "Kalman": { + "var_P": 1.0, + "var_Q": 0.01, + "var_R": 0.1 + }, + "Model": { + "kn": 0.0, + "gain": 0.8 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + }, + "Plant": { + "kn": 0.0, + "gain": 0.8 + } +} diff --git a/tests/utils/fixtures/missing_inner_hold_branch.json b/tests/utils/fixtures/missing_inner_hold_branch.json new file mode 100644 index 0000000..ffed149 --- /dev/null +++ b/tests/utils/fixtures/missing_inner_hold_branch.json @@ -0,0 +1,58 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Smith", + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/missing_outer_kp.json b/tests/utils/fixtures/missing_outer_kp.json new file mode 100644 index 0000000..0fbe063 --- /dev/null +++ b/tests/utils/fixtures/missing_outer_kp.json @@ -0,0 +1,64 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Smith", + "beta": 0.05, + "Outer": { + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/missing_pid_type.json b/tests/utils/fixtures/missing_pid_type.json new file mode 100644 index 0000000..57c4c8f --- /dev/null +++ b/tests/utils/fixtures/missing_pid_type.json @@ -0,0 +1,64 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/missing_tempctrl_section.json b/tests/utils/fixtures/missing_tempctrl_section.json new file mode 100644 index 0000000..68cb7d8 --- /dev/null +++ b/tests/utils/fixtures/missing_tempctrl_section.json @@ -0,0 +1,25 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/valid_normal.json b/tests/utils/fixtures/valid_normal.json new file mode 100644 index 0000000..dd11637 --- /dev/null +++ b/tests/utils/fixtures/valid_normal.json @@ -0,0 +1,65 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Normal", + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/valid_real.json b/tests/utils/fixtures/valid_real.json new file mode 100644 index 0000000..f615ec5 --- /dev/null +++ b/tests/utils/fixtures/valid_real.json @@ -0,0 +1,66 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Smith", + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "hendi", + "port": "/dev/ttyUSB0", + "speed": "115200" + }, + "Stirrer": { + "type": "pololu1376", + "port": "/dev/ttyACM0", + "speed": "115200" + }, + "TempSensor": { + "type": "max31865", + "temp_offset": -0.15 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/valid_sim.json b/tests/utils/fixtures/valid_sim.json new file mode 100644 index 0000000..37341fc --- /dev/null +++ b/tests/utils/fixtures/valid_sim.json @@ -0,0 +1,65 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Smith", + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/wrong_type_kp_string.json b/tests/utils/fixtures/wrong_type_kp_string.json new file mode 100644 index 0000000..9d45527 --- /dev/null +++ b/tests/utils/fixtures/wrong_type_kp_string.json @@ -0,0 +1,65 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": { + "pid_type": "Smith", + "beta": 0.05, + "Outer": { + "kp": 0.4, + "ki": 0.0, + "kd": 0.0, + "kt": 0.0, + "y_hold_min": -0.1 + }, + "Inner": { + "Heat": { + "kp": "0.08", + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + }, + "Hold": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5, + "yi_max": 0.3 + }, + "Cool": { + "kp": 0.08, + "ki": 0.02, + "kd": 0.0, + "kt": 1.5 + } + }, + "Thresholds": { + "HoldHeat": 1.0, + "HoldCool": 1.0, + "HeatHold": 1.0, + "HeatCool": 1.0, + "CoolHold": 1.0, + "CoolHeat": 1.0 + } + }, + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/fixtures/wrong_type_tempctrl_not_dict.json b/tests/utils/fixtures/wrong_type_tempctrl_not_dict.json new file mode 100644 index 0000000..a267602 --- /dev/null +++ b/tests/utils/fixtures/wrong_type_tempctrl_not_dict.json @@ -0,0 +1,26 @@ +{ + "ambient_temperature": 20, + "log_interval": 300, + "TempCtrl": "Smith", + "Heater": { + "type": "sim" + }, + "Stirrer": { + "type": "sim" + }, + "TempSensor": { + "type": "sim", + "temp_offset": 0.0, + "sigma": 0.05, + "stirrer_sigma": 0.0, + "stirrer_tau": 20.0 + }, + "Pot": { + "mass": 5.96, + "material": "Edelstahl 18/10", + "L": 0.2, + "Td": 17, + "water_mass": 22, + "volumen": 30 + } +} diff --git a/tests/utils/test_config_fixtures.py b/tests/utils/test_config_fixtures.py new file mode 100644 index 0000000..73cad2d --- /dev/null +++ b/tests/utils/test_config_fixtures.py @@ -0,0 +1,47 @@ +import json +import os +import unittest + +from utils.config_validate import ConfigError, validate_temp_ctrl + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixtures') + +# filename -> None if the fixture should pass validation, otherwise a regex +# matched against the raised ConfigError's message. +CASES = { + 'valid_sim.json': None, + 'valid_real.json': None, + 'valid_normal.json': None, + 'legacy_unknown_keys.json': None, + 'missing_outer_kp.json': r'TempCtrl\.Outer\.kp', + 'missing_inner_hold_branch.json': r'TempCtrl\.Inner\.Hold', + 'missing_pid_type.json': r'TempCtrl\.pid_type', + 'missing_tempctrl_section.json': r'missing required config key: TempCtrl$', + 'wrong_type_kp_string.json': r'TempCtrl\.Inner\.Heat\.kp', + 'wrong_type_tempctrl_not_dict.json': r'TempCtrl must be dict', +} + + +def _load(name): + with open(os.path.join(FIXTURES_DIR, name)) as f: + return json.load(f) + + +class TestConfigFixtures(unittest.TestCase): + def test_every_fixture_file_is_covered(self): + on_disk = {f for f in os.listdir(FIXTURES_DIR) if f.endswith('.json')} + self.assertEqual(on_disk, set(CASES), "fixtures/ and CASES have drifted apart") + + def test_fixtures_match_expected_outcome(self): + for name, expected_error in CASES.items(): + with self.subTest(fixture=name): + config = _load(name) + if expected_error is None: + validate_temp_ctrl(config) # must not raise + else: + with self.assertRaisesRegex(ConfigError, expected_error): + validate_temp_ctrl(config) + + +if __name__ == '__main__': + unittest.main()