From 2de64e791bd82053589ab4d6a58cfaa2390e9eac Mon Sep 17 00:00:00 2001 From: CamDavidsonPilon Date: Fri, 15 Sep 2023 19:03:24 -0400 Subject: [PATCH] test fixes --- CHANGELOG.md | 7 ++- .../actions/leader/experiment_profile.py | 42 ++++++++++++++- pioreactor/tests/test_led_control.py | 20 +++---- .../tests/test_temperature_approximation.py | 52 +++++++++---------- 4 files changed, 84 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f7e1e80..3e5b5ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ ### Upcoming - When installing plugins, any leader-only commands would not be run. This is fixed. - - Base automations now subclass from `pioreactor.automations.BaseAutomationJob`. Nothing the user needs to worry about. + - Base automations now subclass from `pioreactor.automations.BaseAutomationJob`. You may need to change custom automation imports from, for example, `from pioreactor.automations import DosingAutomationJobContrib` to `from pioreactor.automations.dosing.base import DosingAutomationJobContrib` - Fixed bug that ignored `.yml` files in the UI. + - Improvements to experiment profiles, both in the UI and in the backend. Executing now verifies common mistakes in experiment profiles before it runs. + - Fixed a bug that could cause controllers to have a disconnected automation. #422 + - SPI is on by default on all new image installs + - Plugin author information is presented on the `/plugins` page in the UI. + ### 23.8.29 - Pioreactor's IPv4 and hostname is now displayed under System in the UI. diff --git a/pioreactor/actions/leader/experiment_profile.py b/pioreactor/actions/leader/experiment_profile.py index b5d1cb7e..4d008cdc 100644 --- a/pioreactor/actions/leader/experiment_profile.py +++ b/pioreactor/actions/leader/experiment_profile.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import annotations +from collections import defaultdict from pathlib import Path from threading import Timer from typing import Callable @@ -112,7 +113,46 @@ def hours_to_seconds(hours: float) -> float: def load_and_verify_profile_file(profile_filename: str) -> struct.Profile: with open(profile_filename) as f: - return decode(f.read(), type=struct.Profile) + profile = decode(f.read(), type=struct.Profile) + + actions_per_job = defaultdict(list) + + for job in profile.common.keys(): + for action in profile.common[job]["actions"]: + actions_per_job[job].append(action) + + for unit in profile.pioreactors.values(): + for job in unit["jobs"].keys(): + for action in unit["jobs"][job]["actions"]: + actions_per_job[job].append(action) + + # TODO: things to check for: + # 1. Don't "stop" any *_automations + # 2. Don't change generic settings on *_controllers, (Ex: changing target temp on temp_controller is wrong) + + # 1. + def check_for_not_stopping_automations(act): + if act.type == "stop": + raise ValueError("Don't use 'stop' for automations. Use 'stop' for controllers.") + return True + + for automation_type in ["temperature_automation", "dosing_automation", "led_automation"]: + assert all( + check_for_not_stopping_automations(act) for act in actions_per_job[automation_type] + ) + + # 2. + def check_for_settings_change_on_controllers(act): + if act.type == "update" and "automation-name" not in act.options: + raise ValueError("Update automations, not controllers, with settings.") + return True + + for control_type in ["temperature_control", "dosing_control", "led_control"]: + assert all( + check_for_settings_change_on_controllers(act) for act in actions_per_job[control_type] + ) + + return profile def publish_labels_to_ui(labels_map: dict[str, str]) -> None: diff --git a/pioreactor/tests/test_led_control.py b/pioreactor/tests/test_led_control.py index 074565c3..2153e0cc 100644 --- a/pioreactor/tests/test_led_control.py +++ b/pioreactor/tests/test_led_control.py @@ -27,7 +27,9 @@ def pause(n=1) -> None: def test_silent() -> None: experiment = "test_silent" - with LEDController("silent", duration=60, unit=unit, experiment=experiment) as ld: + with LEDController( + automation_name="silent", duration=60, unit=unit, experiment=experiment + ) as ld: pause() pause() pause() @@ -54,7 +56,7 @@ def test_changing_automation_over_mqtt() -> None: experiment = "test_changing_automation_over_mqtt" original_duration = 60 with LEDController( - "silent", duration=original_duration, unit=unit, experiment=experiment + automation_name="silent", duration=original_duration, unit=unit, experiment=experiment ) as ld: assert ld.automation_name == "silent" assert ld.automation_job.duration == original_duration @@ -110,7 +112,7 @@ def test_light_dark_cycle_starts_on() -> None: experiment = "test_light_dark_cycle_starts_on" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=60, light_intensity=50, light_duration_minutes=60 * 16, @@ -130,7 +132,7 @@ def test_light_dark_cycle_turns_off_after_N_cycles() -> None: experiment = "test_light_dark_cycle_turns_off_after_N_cycles" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=0.01, light_intensity=50, light_duration_minutes=60 * 16, @@ -155,7 +157,7 @@ def test_dark_duration_hour_to_zero() -> None: experiment = "test_dark_duration_hour_to_zero" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=0.005, light_intensity=50, light_duration_minutes=60 * 16, @@ -185,7 +187,7 @@ def test_light_duration_hour_to_zero() -> None: experiment = "test_light_duration_hour_to_zero" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=0.01, light_intensity=50, light_duration_minutes=60 * 16, @@ -205,7 +207,7 @@ def test_add_dark_duration_minutes() -> None: experiment = "test_add_dark_duration_minutes * 60" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=0.01, light_intensity=50, light_duration_minutes=60 * 16, @@ -235,7 +237,7 @@ def test_remove_dark_duration_minutes() -> None: experiment = "test_remove_dark_duration_minutes * 60" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=0.005, light_intensity=50, light_duration_minutes=60 * 16, @@ -269,7 +271,7 @@ def test_fractional_hours() -> None: experiment = "test_fractional_hours" unit = get_unit_name() with LEDController( - "light_dark_cycle", + automation_name="light_dark_cycle", duration=0.005, light_intensity=50, light_duration_minutes=60 * 0.9, diff --git a/pioreactor/tests/test_temperature_approximation.py b/pioreactor/tests/test_temperature_approximation.py index 8e725acb..cd7e9b8b 100644 --- a/pioreactor/tests/test_temperature_approximation.py +++ b/pioreactor/tests/test_temperature_approximation.py @@ -34,7 +34,7 @@ def test_temperature_approximation_if_less_than_hardcoded_room_temp() -> None: ], } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 19.0 <= t.approximate_temperature(features) <= 20.0 @@ -46,7 +46,7 @@ def test_temperature_approximation_if_constant() -> None: features = {"previous_heater_dc": 17, "time_series_of_temp": 30 * [32.0]} with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: for temp in range(20, 45): features = { @@ -72,7 +72,7 @@ def test_temperature_approximation_even_if_very_tiny_heat_source() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert (32 * np.exp(-0.008 * 17)) < t.approximate_temperature(features) < 32 @@ -92,7 +92,7 @@ def test_temperature_approximation_even_if_very_large_heat_source() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert (24 * np.exp(-0.008 * 17)) < t.approximate_temperature(features) < 25 @@ -103,7 +103,7 @@ def test_temperature_approximation_if_dc_is_nil() -> None: unit = get_unit_name() with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert t.approximate_temperature(features) == 32.1875 @@ -150,7 +150,7 @@ def test_temperature_approximation1() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 33.389 <= t.approximate_temperature(features) <= 33.830 @@ -196,7 +196,7 @@ def test_temperature_approximation_heating_vial1() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 33.525 <= t.approximate_temperature(features) <= 34.00 @@ -242,7 +242,7 @@ def test_temperature_approximation_heating_vial2() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 33.695 <= t.approximate_temperature(features) <= 34.170 @@ -288,7 +288,7 @@ def test_temperature_approximation_heating_vial3() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 33.898 <= t.approximate_temperature(features) <= 34.339 @@ -334,7 +334,7 @@ def test_temperature_approximation_heating_vial4() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.068 <= t.approximate_temperature(features) <= 34.577 @@ -380,7 +380,7 @@ def test_temperature_approximation_heating_vial5() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.305 <= t.approximate_temperature(features) <= 34.814 @@ -426,7 +426,7 @@ def test_temperature_approximation6() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.475 <= t.approximate_temperature(features) <= 35.018 @@ -472,7 +472,7 @@ def test_temperature_approximation7() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.644 <= t.approximate_temperature(features) <= 35.153 @@ -518,7 +518,7 @@ def test_temperature_approximation8() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.746 <= t.approximate_temperature(features) <= 35.289 @@ -563,7 +563,7 @@ def test_temperature_approximation9() -> None: ], } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.848 <= t.approximate_temperature(features) <= 35.391 @@ -609,7 +609,7 @@ def test_temperature_approximation10() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.950 <= t.approximate_temperature(features) <= 35.493 @@ -655,7 +655,7 @@ def test_temperature_approximation20() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 34.950 <= t.approximate_temperature(features) <= 35.493 @@ -702,7 +702,7 @@ def test_temperature_approximation_cooling1() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 32.169 <= t.approximate_temperature(features) @@ -748,7 +748,7 @@ def test_temperature_approximation_cooling2() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 31.118 <= t.approximate_temperature(features) @@ -794,7 +794,7 @@ def test_temperature_approximation11() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 29.628 <= t.approximate_temperature(features) <= 30.136 @@ -840,7 +840,7 @@ def test_temperature_approximation12() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 28.476 <= t.approximate_temperature(features) <= 28.747 @@ -886,7 +886,7 @@ def test_temperature_approximation13() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 28.374 <= t.approximate_temperature(features) <= 28.645 @@ -932,7 +932,7 @@ def test_temperature_approximation14() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 28.374 <= t.approximate_temperature(features) <= 28.578 @@ -977,7 +977,7 @@ def test_temperature_approximation15() -> None: ], } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 28.815 <= t.approximate_temperature(features) <= 29.119 @@ -1024,7 +1024,7 @@ def test_temperature_approximation16() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 25.261 <= t.approximate_temperature(features) <= 25.430 @@ -1071,6 +1071,6 @@ def test_temperature_approximation17() -> None: } with temperature_control.TemperatureController( - "only_record_temperature", unit=unit, experiment=experiment + automation_name="only_record_temperature", unit=unit, experiment=experiment ) as t: assert 25.295 <= t.approximate_temperature(features) <= 25.430