From 9c47bf1b96179a0d05a9505b7c5766bc07e0b76b Mon Sep 17 00:00:00 2001 From: TShapinsky Date: Mon, 1 Jul 2024 15:40:47 -0600 Subject: [PATCH] Allow for running Alfalfa integration tests on non-local deployment --- tests/api/conftest.py | 8 +- tests/conftest.py | 12 ++ tests/integration/conftest.py | 14 +- tests/integration/test_broken_models.py | 3 +- tests/integration/test_refrig_case_osw.py | 77 ++++---- tests/integration/test_schedule_override.py | 6 +- .../integration/test_simple_thermostat_fmu.py | 169 +++++++++--------- tests/integration/test_single_zone_vav_fmu.py | 46 +++-- tests/integration/test_small_office_osw.py | 6 +- 9 files changed, 166 insertions(+), 175 deletions(-) diff --git a/tests/api/conftest.py b/tests/api/conftest.py index 80a11a02..88da9018 100644 --- a/tests/api/conftest.py +++ b/tests/api/conftest.py @@ -7,13 +7,13 @@ @pytest.fixture -def base_url(): - return 'http://localhost/api/v2' +def base_url(alfalfa_host: str): + return f'{alfalfa_host}/api/v2' @pytest.fixture -def alfalfa_client(): - return AlfalfaClient() +def alfalfa_client(alfalfa_host: str): + return AlfalfaClient(host=alfalfa_host) @pytest.fixture diff --git a/tests/conftest.py b/tests/conftest.py index a149a998..ac66b613 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -34,3 +34,15 @@ def mock_dispatcher(tmp_path: Path): work_dir.mkdir() dispatcher = MockDispatcher(work_dir) yield dispatcher + + +def pytest_addoption(parser): + parser.addoption("--host", action="store", default="http://localhost") + + +@pytest.fixture +def alfalfa_host(pytestconfig: pytest.Config): + alfalfa_host = pytestconfig.getoption("host", default="http://localhost") + if isinstance(alfalfa_host, str): + alfalfa_host.rstrip('/') + return alfalfa_host diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 9e85e4fd..79aad4f7 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,7 +1,5 @@ # Consider factoring this out of the test file import os -import shutil -import tempfile from pathlib import Path import pytest @@ -28,8 +26,8 @@ def pytest_generate_tests(metafunc): @pytest.fixture -def alfalfa(): - client = AlfalfaClient(host="http://localhost") +def alfalfa(alfalfa_host: str): + client = AlfalfaClient(host=alfalfa_host) yield client @@ -43,14 +41,6 @@ def ref_id(model_path: Path, alfalfa: AlfalfaClient): alfalfa.stop() -def create_zip(model_dir): - zip_file_fd, zip_file_path = tempfile.mkstemp(suffix='.zip') - zip_file_path = Path(zip_file_path) - shutil.make_archive(zip_file_path.parent / zip_file_path.stem, "zip", model_dir) - - return zip_file_path - - def prepare_model(model_path): model_path = Path(__file__).parents[0] / 'models' / model_path return str(model_path) diff --git a/tests/integration/test_broken_models.py b/tests/integration/test_broken_models.py index febae7b0..f43094bd 100644 --- a/tests/integration/test_broken_models.py +++ b/tests/integration/test_broken_models.py @@ -6,8 +6,7 @@ @pytest.mark.integration -def test_broken_models(broken_model_path): - alfalfa = AlfalfaClient(host='http://localhost') +def test_broken_models(broken_model_path, alfalfa: AlfalfaClient): with pytest.raises(AlfalfaException): run_id = alfalfa.submit(str(broken_model_path)) alfalfa.start( diff --git a/tests/integration/test_refrig_case_osw.py b/tests/integration/test_refrig_case_osw.py index 00890a77..ce53bfbb 100644 --- a/tests/integration/test_refrig_case_osw.py +++ b/tests/integration/test_refrig_case_osw.py @@ -1,5 +1,4 @@ import datetime -from unittest import TestCase import pytest from alfalfa_client.alfalfa_client import AlfalfaClient @@ -9,52 +8,50 @@ @pytest.mark.integration -class TestRefrigCaseOSW(TestCase): - - def test_invalid_start_conditions(self): - zip_file_path = prepare_model('refrig_case_osw') - alfalfa = AlfalfaClient(host='http://localhost') - model_id = alfalfa.submit(zip_file_path) - with pytest.raises(AlfalfaException): - alfalfa.start( - model_id, - external_clock=False, - start_datetime=datetime.datetime(2019, 1, 2, 0, 0, 0), - end_datetime=datetime.datetime(2019, 1, 1, 0, 0, 0), - timescale=5 - ) - - def test_basic_io(self): - zip_file_path = prepare_model('refrig_case_osw') - alfalfa = AlfalfaClient(host='http://localhost') - model_id = alfalfa.submit(zip_file_path) - - alfalfa.wait(model_id, "ready") +def test_invalid_start_conditions(alfalfa: AlfalfaClient): + zip_file_path = prepare_model('refrig_case_osw') + model_id = alfalfa.submit(zip_file_path) + with pytest.raises(AlfalfaException): alfalfa.start( model_id, - external_clock=True, - start_datetime=datetime.datetime(2019, 1, 2, 0, 2, 0), - end_datetime=datetime.datetime(2019, 1, 3, 0, 0, 0) + external_clock=False, + start_datetime=datetime.datetime(2019, 1, 2, 0, 0, 0), + end_datetime=datetime.datetime(2019, 1, 1, 0, 0, 0), + timescale=5 ) - alfalfa.wait(model_id, "running") - inputs = alfalfa.get_inputs(model_id) - assert "Test_Point_1" in inputs, "Test_Point_1 is in input points" - inputs = {} - inputs["Test_Point_1"] = 12 +@pytest.mark.integration +def test_basic_io(alfalfa: AlfalfaClient): + zip_file_path = prepare_model('refrig_case_osw') + model_id = alfalfa.submit(zip_file_path) + + alfalfa.wait(model_id, "ready") + alfalfa.start( + model_id, + external_clock=True, + start_datetime=datetime.datetime(2019, 1, 2, 0, 2, 0), + end_datetime=datetime.datetime(2019, 1, 3, 0, 0, 0) + ) + + alfalfa.wait(model_id, "running") + + inputs = alfalfa.get_inputs(model_id) + assert "Test_Point_1" in inputs, "Test_Point_1 is in input points" + inputs = {} + inputs["Test_Point_1"] = 12 - alfalfa.set_inputs(model_id, inputs) + alfalfa.set_inputs(model_id, inputs) - outputs = alfalfa.get_outputs(model_id) - assert "Test_Point_1" in outputs.keys(), "Echo point for Test_Point_1 is not in outputs" + outputs = alfalfa.get_outputs(model_id) + assert "Test_Point_1" in outputs.keys(), "Echo point for Test_Point_1 is not in outputs" - # -- Advance a single time step - alfalfa.advance([model_id]) + # -- Advance a single time step + alfalfa.advance([model_id]) - outputs = alfalfa.get_outputs(model_id) - assert outputs["Test_Point_1"] == pytest.approx(12), "Test_Point_1 value has not been processed by the model" + outputs = alfalfa.get_outputs(model_id) + assert outputs["Test_Point_1"] == pytest.approx(12), "Test_Point_1 value has not been processed by the model" - # Shut down - alfalfa.stop(model_id) - alfalfa.wait(model_id, "complete") + # Shut down + alfalfa.stop(model_id) + alfalfa.wait(model_id, "complete") diff --git a/tests/integration/test_schedule_override.py b/tests/integration/test_schedule_override.py index 66c6706f..269c9be4 100644 --- a/tests/integration/test_schedule_override.py +++ b/tests/integration/test_schedule_override.py @@ -7,8 +7,7 @@ @pytest.mark.integration -def test_schedule_point_generation(): - alfalfa = AlfalfaClient('http://localhost') +def test_schedule_point_generation(alfalfa: AlfalfaClient): site_id = alfalfa.submit(prepare_model('schedule_model')) inputs = alfalfa.get_inputs(site_id) @@ -27,8 +26,7 @@ def test_schedule_point_generation(): @pytest.mark.integration -def test_schedule_override(): - alfalfa = AlfalfaClient('http://localhost') +def test_schedule_override(alfalfa: AlfalfaClient): site_id = alfalfa.submit(prepare_model('schedule_model')) alfalfa.start( diff --git a/tests/integration/test_simple_thermostat_fmu.py b/tests/integration/test_simple_thermostat_fmu.py index e7600576..8c90a7b3 100644 --- a/tests/integration/test_simple_thermostat_fmu.py +++ b/tests/integration/test_simple_thermostat_fmu.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from unittest import TestCase import pytest from alfalfa_client.alfalfa_client import AlfalfaClient @@ -19,87 +18,89 @@ ################################################################################################## +@pytest.fixture +def simple_thermostat_run_id(alfalfa: AlfalfaClient): + fmu_path = prepare_model('simple_thermostat.fmu') + run_id = alfalfa.submit(fmu_path) + + alfalfa.wait(run_id, "ready") + + yield run_id + + alfalfa.stop(run_id) + alfalfa.wait(run_id, "complete") + + @pytest.mark.integration -class TestSimpleThermostat(TestCase): - - def setUp(self): - self.alfalfa = AlfalfaClient(host='http://localhost') - fmu_path = prepare_model('simple_thermostat.fmu') - self.model_id = self.alfalfa.submit(fmu_path) - - self.alfalfa.wait(self.model_id, "ready") - - self.current_datetime = datetime(2019, 1, 1) - - self.alfalfa.start( - self.model_id, - external_clock=True, - start_datetime=self.current_datetime, - end_datetime=datetime(2019, 1, 1, 0, 5), - timescale=5 - ) - self.alfalfa.wait(self.model_id, "running") - - def test_io_with_external_clock(self): - # Simulation is running, but time should still be at 0 - model_time = self.alfalfa.get_sim_time(self.model_id) - assert self.current_datetime == model_time - - # If outputs are requested before the simulation is advanced, - # there will be an error. - # See issue https://github.com/NREL/alfalfa/issues/119 - self.current_datetime += timedelta(minutes=1) - self.alfalfa.advance([self.model_id]) - model_time = self.alfalfa.get_sim_time(self.model_id) - assert self.current_datetime == model_time - - # Having not set any inputs the fmu will be at the initial state. - # The control signal output "rea" is at 0.0 - outputs = self.alfalfa.get_outputs(self.model_id) - rea = outputs.get("rea") - assert rea == pytest.approx(0.0) - - # Attempt to override the measured temp (ie zone temperature), - # and the setpoint, such that zone temperature is over setpoint. - self.alfalfa.set_inputs(self.model_id, {"oveWriMeasuredTemp_u": 303.15, "oveWriSetPoint_u": 294.15}) - - # Advance time, outputs will not be updated until advance happens. - # Should this limitation be considered a bug? - # Note that boptest advance and set input apis are combined, - # so that there is no method to set inputs without advancing - self.current_datetime += timedelta(minutes=1) - self.alfalfa.advance([self.model_id]) - model_time = self.alfalfa.get_sim_time(self.model_id) - assert self.current_datetime == model_time - - # When temperature is over setpoint controller returns 0.0 - outputs = self.alfalfa.get_outputs(self.model_id) - rea = outputs.get("rea") - assert rea == pytest.approx(0.0) - - # Now override the measured (zone) temperature such that it is below setpoint - self.alfalfa.set_inputs(self.model_id, {"oveWriMeasuredTemp_u": 283.15, "oveWriSetPoint_u": 294.15}) - - self.current_datetime += timedelta(minutes=1) - self.alfalfa.advance([self.model_id]) - model_time = self.alfalfa.get_sim_time(self.model_id) - assert self.current_datetime == model_time - - # When temperature is below setpoint controller returns 1.0 - outputs = self.alfalfa.get_outputs(self.model_id) - rea = outputs.get("rea") - assert rea == pytest.approx(1.0) - - # Test the control signal override - self.alfalfa.set_inputs(self.model_id, {"oveWriActuatorSignal_u": 0.0}) - self.current_datetime += timedelta(minutes=1) - self.alfalfa.advance([self.model_id]) - model_time = self.alfalfa.get_sim_time(self.model_id) - assert self.current_datetime == model_time - outputs = self.alfalfa.get_outputs(self.model_id) - rea = outputs.get("rea") - assert rea == pytest.approx(0.0) - - def tearDown(self): - self.alfalfa.stop(self.model_id) - self.alfalfa.wait(self.model_id, "complete") +def test_io_with_external_clock(alfalfa: AlfalfaClient, simple_thermostat_run_id): + run_id = simple_thermostat_run_id + + current_datetime = datetime(2019, 1, 1) + + alfalfa.start( + run_id, + external_clock=True, + start_datetime=current_datetime, + end_datetime=datetime(2019, 1, 1, 0, 5), + timescale=5 + ) + alfalfa.wait(run_id, "running") + + # Simulation is running, but time should still be at 0 + model_time = alfalfa.get_sim_time(run_id) + assert current_datetime == model_time + + # If outputs are requested before the simulation is advanced, + # there will be an error. + # See issue https://github.com/NREL/alfalfa/issues/119 + current_datetime += timedelta(minutes=1) + alfalfa.advance([run_id]) + model_time = alfalfa.get_sim_time(run_id) + assert current_datetime == model_time + + # Having not set any inputs the fmu will be at the initial state. + # The control signal output "rea" is at 0.0 + outputs = alfalfa.get_outputs(run_id) + rea = outputs.get("rea") + assert rea == pytest.approx(0.0) + + # Attempt to override the measured temp (ie zone temperature), + # and the setpoint, such that zone temperature is over setpoint. + alfalfa.set_inputs(run_id, {"oveWriMeasuredTemp_u": 303.15, "oveWriSetPoint_u": 294.15}) + + # Advance time, outputs will not be updated until advance happens. + # Should this limitation be considered a bug? + # Note that boptest advance and set input apis are combined, + # so that there is no method to set inputs without advancing + current_datetime += timedelta(minutes=1) + alfalfa.advance([run_id]) + model_time = alfalfa.get_sim_time(run_id) + assert current_datetime == model_time + + # When temperature is over setpoint controller returns 0.0 + outputs = alfalfa.get_outputs(run_id) + rea = outputs.get("rea") + assert rea == pytest.approx(0.0) + + # Now override the measured (zone) temperature such that it is below setpoint + alfalfa.set_inputs(run_id, {"oveWriMeasuredTemp_u": 283.15, "oveWriSetPoint_u": 294.15}) + + current_datetime += timedelta(minutes=1) + alfalfa.advance([run_id]) + model_time = alfalfa.get_sim_time(run_id) + assert current_datetime == model_time + + # When temperature is below setpoint controller returns 1.0 + outputs = alfalfa.get_outputs(run_id) + rea = outputs.get("rea") + assert rea == pytest.approx(1.0) + + # Test the control signal override + alfalfa.set_inputs(run_id, {"oveWriActuatorSignal_u": 0.0}) + current_datetime += timedelta(minutes=1) + alfalfa.advance([run_id]) + model_time = alfalfa.get_sim_time(run_id) + assert current_datetime == model_time + outputs = alfalfa.get_outputs(run_id) + rea = outputs.get("rea") + assert rea == pytest.approx(0.0) diff --git a/tests/integration/test_single_zone_vav_fmu.py b/tests/integration/test_single_zone_vav_fmu.py index 308e1063..e3dad6ca 100644 --- a/tests/integration/test_single_zone_vav_fmu.py +++ b/tests/integration/test_single_zone_vav_fmu.py @@ -1,6 +1,5 @@ from datetime import datetime from time import sleep -from unittest import TestCase import pytest from alfalfa_client.alfalfa_client import AlfalfaClient @@ -9,27 +8,24 @@ @pytest.mark.integration -class TestSingleZoneVAVFMU(TestCase): - - def test_simple_internal_clock(self): - alfalfa = AlfalfaClient(host='http://localhost') - fmu_path = prepare_model('single_zone_vav.fmu') - model_id = alfalfa.submit(fmu_path) - - alfalfa.wait(model_id, "ready") - - end_datetime = datetime(2019, 1, 1, 0, 5) - alfalfa.start( - model_id, - external_clock=False, - start_datetime=datetime(2019, 1, 1), - end_datetime=end_datetime, - timescale=5 - ) - - alfalfa.wait(model_id, "running") - # wait for model to advance for 1 minute at timescale 5 - sleep(60) - alfalfa.wait(model_id, "complete") - model_time = alfalfa.get_sim_time(model_id) - assert end_datetime == model_time +def test_simple_internal_clock(alfalfa: AlfalfaClient): + fmu_path = prepare_model('single_zone_vav.fmu') + model_id = alfalfa.submit(fmu_path) + + alfalfa.wait(model_id, "ready") + + end_datetime = datetime(2019, 1, 1, 0, 5) + alfalfa.start( + model_id, + external_clock=False, + start_datetime=datetime(2019, 1, 1), + end_datetime=end_datetime, + timescale=5 + ) + + alfalfa.wait(model_id, "running") + # wait for model to advance for 1 minute at timescale 5 + sleep(60) + alfalfa.wait(model_id, "complete") + model_time = alfalfa.get_sim_time(model_id) + assert end_datetime == model_time diff --git a/tests/integration/test_small_office_osw.py b/tests/integration/test_small_office_osw.py index 3b456bd5..aa2c932e 100644 --- a/tests/integration/test_small_office_osw.py +++ b/tests/integration/test_small_office_osw.py @@ -7,9 +7,8 @@ @pytest.mark.integration -def test_python_environment(): +def test_python_environment(alfalfa: AlfalfaClient): zip_file_path = prepare_model('small_office') - alfalfa = AlfalfaClient(host='http://localhost') model_id = alfalfa.submit(zip_file_path) alfalfa.wait(model_id, "ready") @@ -31,9 +30,8 @@ def test_python_environment(): @pytest.mark.integration -def test_io_enable_disable(): +def test_io_enable_disable(alfalfa: AlfalfaClient): zip_file_path = prepare_model('small_office') - alfalfa = AlfalfaClient(host='http://localhost') site_id = alfalfa.submit(zip_file_path) alfalfa.wait(site_id, "ready")