From df28922e2e992c6f877b5380fb672fdea21cf83f Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 10:31:51 +0000 Subject: [PATCH 01/15] Updated naming of model scripts --- .../abiotic/{model.py => abiotic_model.py} | 2 +- .../models/soil/{model.py => soil_model.py} | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) rename virtual_rainforest/models/abiotic/{model.py => abiotic_model.py} (98%) rename virtual_rainforest/models/soil/{model.py => soil_model.py} (85%) diff --git a/virtual_rainforest/models/abiotic/model.py b/virtual_rainforest/models/abiotic/abiotic_model.py similarity index 98% rename from virtual_rainforest/models/abiotic/model.py rename to virtual_rainforest/models/abiotic/abiotic_model.py index 1af532910..3332bf36d 100644 --- a/virtual_rainforest/models/abiotic/model.py +++ b/virtual_rainforest/models/abiotic/abiotic_model.py @@ -1,5 +1,5 @@ """The :mod:`abiotic.model` module creates a -:class:`~virtual_rainforest.abiotic.model.AbioticModel` class as a child of the +:class:`~virtual_rainforest.abiotic.abiotic_model.AbioticModel` class as a child of the :class:`~virtual_rainforest.core.model.BaseModel` class. """ # noqa: D205, D415 diff --git a/virtual_rainforest/models/soil/model.py b/virtual_rainforest/models/soil/soil_model.py similarity index 85% rename from virtual_rainforest/models/soil/model.py rename to virtual_rainforest/models/soil/soil_model.py index 089f7bc90..94e5d4f4f 100644 --- a/virtual_rainforest/models/soil/model.py +++ b/virtual_rainforest/models/soil/soil_model.py @@ -1,18 +1,19 @@ -"""The :mod:`~virtual_rainforest.soil.model` module creates a -:class:`~virtual_rainforest.soil.model.SoilModel` class as a child of the +"""The :mod:`~virtual_rainforest.models.soil.soil_model` module creates a +:class:`~virtual_rainforest.models.soil.soil_model.SoilModel` class as a child of the :class:`~virtual_rainforest.core.model.BaseModel` class. At present a lot of the abstract methods of the parent class (e.g. :func:`~virtual_rainforest.core.model.BaseModel.setup` and :func:`~virtual_rainforest.core.model.BaseModel.spinup`) are overwritten using placeholder functions that don't do anything. This will change as the :mod:`virtual_rainforest` model develops. The factory method -:func:`~virtual_rainforest.soil.model.SoilModel.from_config` exists in a more complete -state, and unpacks a small number of parameters from our currently pretty minimal -configuration dictionary. These parameters are then used to generate a class instance. -If errors crop here when converting the information from the config dictionary to the -required types (e.g. :class:`~numpy.timedelta64`) they are caught and then logged, and -at the end of the unpacking an error is thrown. This error should be caught and handled -by downstream functions so that all model configuration failures can be reported as one. +:func:`~virtual_rainforest.models.soil.soil_model.SoilModel.from_config` exists in a +more complete state, and unpacks a small number of parameters from our currently pretty +minimal configuration dictionary. These parameters are then used to generate a class +instance. If errors crop here when converting the information from the config dictionary +to the required types (e.g. :class:`~numpy.timedelta64`) they are caught and then +logged, and at the end of the unpacking an error is thrown. This error should be caught +and handled by downstream functions so that all model configuration failures can be +reported as one. """ # noqa: D205, D415 from __future__ import annotations From 877833411504520a2d37477dee804bb307e762ad Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 10:34:36 +0000 Subject: [PATCH 02/15] Updated docs to reflect changing in model.py naming scheme --- docs/source/development/defining_new_models.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/development/defining_new_models.md b/docs/source/development/defining_new_models.md index 573a19a37..525ba5c3f 100644 --- a/docs/source/development/defining_new_models.md +++ b/docs/source/development/defining_new_models.md @@ -19,10 +19,11 @@ You should first start by defining a new folder for your model (within mkdir virtual_rainforest/models/freshwater ``` -Within this folder a `python` script defining the model should be created. +Within this folder a `python` script defining the model should be created. This script +should be called "{MODEL_NAME}_model.py". ```bash -touch virtual_rainforest/models/freshwater/model.py +touch virtual_rainforest/models/freshwater/freshwater_model.py ``` This script must import a number of things to be able to set up a new `Model` class From 256a3059872fb93095348a6932e078cb7edb2adc Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 10:38:49 +0000 Subject: [PATCH 03/15] Updated import paths to stop tests breaking --- tests/test_main.py | 2 +- tests/test_models.py | 4 ++-- virtual_rainforest/__init__.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index aeb37a50b..79fe50989 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -19,7 +19,7 @@ select_models, vr_run, ) -from virtual_rainforest.models.soil.model import SoilModel +from virtual_rainforest.models.soil.soil_model import SoilModel from .conftest import log_check diff --git a/tests/test_models.py b/tests/test_models.py index 5d8aa1f57..ddfb3ecfb 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -11,8 +11,8 @@ from numpy import datetime64, timedelta64 from virtual_rainforest.core.model import BaseModel, InitialisationError -from virtual_rainforest.models.abiotic.model import AbioticModel -from virtual_rainforest.models.soil.model import SoilModel +from virtual_rainforest.models.abiotic.abiotic_model import AbioticModel +from virtual_rainforest.models.soil.soil_model import SoilModel from .conftest import log_check diff --git a/virtual_rainforest/__init__.py b/virtual_rainforest/__init__.py index f2d651361..526c139b3 100644 --- a/virtual_rainforest/__init__.py +++ b/virtual_rainforest/__init__.py @@ -3,11 +3,11 @@ # Import all module schema here to ensure that they are added to the registry from virtual_rainforest.core import schema # noqa from virtual_rainforest.models.abiotic import schema # noqa -from virtual_rainforest.models.abiotic.model import AbioticModel # noqa +from virtual_rainforest.models.abiotic.abiotic_model import AbioticModel # noqa from virtual_rainforest.models.plants import schema # noqa from virtual_rainforest.models.soil import schema # noqa # Import models here so that they also end up in the registry -from virtual_rainforest.models.soil.model import SoilModel # noqa +from virtual_rainforest.models.soil.soil_model import SoilModel # noqa __version__ = importlib.metadata.version("virtual_rainforest") From 5bb644a94f2646b52e17676d1998d0cd2be3137e Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 10:46:09 +0000 Subject: [PATCH 04/15] Moved core tests into seperate folder --- tests/{ => core}/test_axes.py | 0 tests/{ => core}/test_config.py | 0 tests/{ => core}/test_data.py | 0 tests/{ => core}/test_grid.py | 0 tests/{ => core}/test_readers.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/{ => core}/test_axes.py (100%) rename tests/{ => core}/test_config.py (100%) rename tests/{ => core}/test_data.py (100%) rename tests/{ => core}/test_grid.py (100%) rename tests/{ => core}/test_readers.py (100%) diff --git a/tests/test_axes.py b/tests/core/test_axes.py similarity index 100% rename from tests/test_axes.py rename to tests/core/test_axes.py diff --git a/tests/test_config.py b/tests/core/test_config.py similarity index 100% rename from tests/test_config.py rename to tests/core/test_config.py diff --git a/tests/test_data.py b/tests/core/test_data.py similarity index 100% rename from tests/test_data.py rename to tests/core/test_data.py diff --git a/tests/test_grid.py b/tests/core/test_grid.py similarity index 100% rename from tests/test_grid.py rename to tests/core/test_grid.py diff --git a/tests/test_readers.py b/tests/core/test_readers.py similarity index 100% rename from tests/test_readers.py rename to tests/core/test_readers.py From 9c8fe0d6e3b92af2a23f4dec1fe2b2f956ccce7e Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 11:18:57 +0000 Subject: [PATCH 05/15] Made models folder within tests --- tests/models/abiotic/test_abiotic_model.py | 149 ++++++++++++++++++ .../soil/test_carbon.py} | 0 tests/models/soil/test_soil_model.py | 113 +++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 tests/models/abiotic/test_abiotic_model.py rename tests/{test_soil_carbon.py => models/soil/test_carbon.py} (100%) create mode 100644 tests/models/soil/test_soil_model.py diff --git a/tests/models/abiotic/test_abiotic_model.py b/tests/models/abiotic/test_abiotic_model.py new file mode 100644 index 000000000..1d3497137 --- /dev/null +++ b/tests/models/abiotic/test_abiotic_model.py @@ -0,0 +1,149 @@ +"""Test module for abiotic_model.py (and associated functionality).""" + +from contextlib import nullcontext as does_not_raise +from logging import ERROR, INFO + +import pytest +from numpy import datetime64, timedelta64 + +from tests.conftest import log_check +from virtual_rainforest.core.model import InitialisationError +from virtual_rainforest.models.abiotic.abiotic_model import AbioticModel + + +@pytest.mark.parametrize( + "soil_layers,canopy_layers,raises,expected_log_entries", + [ + ( + 2, + 3, + does_not_raise(), + (), + ), + ( + -2, + 3, + pytest.raises(InitialisationError), + ( + ( + ERROR, + "There has to be at least one soil layer in the abiotic model!", + ), + ), + ), + ( + 2, + -3, + pytest.raises(InitialisationError), + ( + ( + ERROR, + "There has to be at least one canopy layer in the abiotic model!", + ), + ), + ), + ( + 2.5, + 3, + pytest.raises(InitialisationError), + ( + ( + ERROR, + "The number of soil layers must be an integer!", + ), + ), + ), + ( + 2, + 3.4, + pytest.raises(InitialisationError), + ( + ( + ERROR, + "The number of canopy layers must be an integer!", + ), + ), + ), + ], +) +def test_abiotic_model_initialization( + caplog, soil_layers, canopy_layers, raises, expected_log_entries +): + """Test `AbioticModel` initialization.""" + + with raises: + # Initialize model + model = AbioticModel( + timedelta64(1, "W"), datetime64("2022-11-01"), soil_layers, canopy_layers + ) + + # In cases where it passes then checks that the object has the right properties + assert set(["setup", "spinup", "update", "cleanup"]).issubset(dir(model)) + assert model.model_name == "abiotic" + assert ( + repr(model) + == f"AbioticModel(update_interval = 1 weeks, next_update = 2022-11-08, " + f"soil_layers = {int(soil_layers)}, " + f"canopy_layers = {int(canopy_layers)})" + ) + assert model.soil_layers == soil_layers + assert model.canopy_layers == canopy_layers + + # Final check that expected logging entries are produced + log_check(caplog, expected_log_entries) + + +@pytest.mark.parametrize( + "config,time_interval,raises,expected_log_entries", + [ + ( + {}, + None, + pytest.raises(KeyError), + (), # This error isn't handled so doesn't generate logging + ), + ( + { + "core": {"timing": {"start_time": "2020-01-01"}}, + "abiotic": { + "soil_layers": 2, + "canopy_layers": 3, + "model_time_step": "12 hours", + }, + }, + timedelta64(12, "h"), + does_not_raise(), + ( + ( + INFO, + "Information required to initialise the abiotic model successfully " + "extracted.", + ), + ), + ), + ], +) +def test_generate_abiotic_model( + caplog, config, time_interval, raises, expected_log_entries +): + """Test that the function to initialise the soil model behaves as expected.""" + + # Check whether model is initialised (or not) as expected + with raises: + model = AbioticModel.from_config(config) + assert model.soil_layers == config["abiotic"]["soil_layers"] + assert model.canopy_layers == config["abiotic"]["canopy_layers"] + assert model.update_interval == time_interval + assert ( + model.next_update + == datetime64(config["core"]["timing"]["start_time"]) + time_interval + ) + # Run the update step and check that next_update has incremented properly + model.update() + assert ( + model.next_update + == datetime64(config["core"]["timing"]["start_time"]) + 2 * time_interval + ) + + # Final check that expected logging entries are produced + log_check(caplog, expected_log_entries) diff --git a/tests/test_soil_carbon.py b/tests/models/soil/test_carbon.py similarity index 100% rename from tests/test_soil_carbon.py rename to tests/models/soil/test_carbon.py diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py new file mode 100644 index 000000000..09119a72a --- /dev/null +++ b/tests/models/soil/test_soil_model.py @@ -0,0 +1,113 @@ +"""Test module for soil_model.py (and associated functionality).""" + +from contextlib import nullcontext as does_not_raise +from logging import ERROR, INFO + +import pytest +from numpy import datetime64, timedelta64 + +from tests.conftest import log_check +from virtual_rainforest.core.model import InitialisationError +from virtual_rainforest.models.soil.soil_model import SoilModel + + +@pytest.mark.parametrize( + "no_layers,raises,expected_log_entries", + [ + ( + 2, + does_not_raise(), + (), + ), + ( + -2, + pytest.raises(InitialisationError), + ( + ( + ERROR, + "There has to be at least one soil layer in the soil model!", + ), + ), + ), + ( + 2.5, + pytest.raises(InitialisationError), + ( + ( + ERROR, + "The number of soil layers must be an integer!", + ), + ), + ), + ], +) +def test_soil_model_initialization(caplog, no_layers, raises, expected_log_entries): + """Test `SoilModel` initialization.""" + + with raises: + # Initialize model + model = SoilModel(timedelta64(1, "W"), datetime64("2022-11-01"), no_layers) + + # In cases where it passes then checks that the object has the right properties + assert set(["setup", "spinup", "update", "cleanup"]).issubset(dir(model)) + assert model.model_name == "soil" + assert str(model) == "A soil model instance" + assert ( + repr(model) + == f"SoilModel(update_interval = 1 weeks, next_update = 2022-11-08, " + f"no_layers = {int(no_layers)})" + ) + + # Final check that expected logging entries are produced + log_check(caplog, expected_log_entries) + + +@pytest.mark.parametrize( + "config,time_interval,raises,expected_log_entries", + [ + ( + {}, + None, + pytest.raises(KeyError), + (), # This error isn't handled so doesn't generate logging + ), + ( + { + "core": {"timing": {"start_time": "2020-01-01"}}, + "soil": {"no_layers": 2, "model_time_step": "12 hours"}, + }, + timedelta64(12, "h"), + does_not_raise(), + ( + ( + INFO, + "Information required to initialise the soil model successfully " + "extracted.", + ), + ), + ), + ], +) +def test_generate_soil_model( + caplog, config, time_interval, raises, expected_log_entries +): + """Test that the function to initialise the soil model behaves as expected.""" + + # Check whether model is initialised (or not) as expected + with raises: + model = SoilModel.from_config(config) + assert model.no_layers == config["soil"]["no_layers"] + assert model.update_interval == time_interval + assert ( + model.next_update + == datetime64(config["core"]["timing"]["start_time"]) + time_interval + ) + # Run the update step and check that next_update has incremented properly + model.update() + assert ( + model.next_update + == datetime64(config["core"]["timing"]["start_time"]) + 2 * time_interval + ) + + # Final check that expected logging entries are produced + log_check(caplog, expected_log_entries) From 8b5510dbb2deaf77d50085b23d18a978be9c2e49 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 11:23:03 +0000 Subject: [PATCH 06/15] Moved remaining model tests into tests/core --- tests/core/test_model.py | 95 +++++++++++ tests/test_models.py | 343 --------------------------------------- 2 files changed, 95 insertions(+), 343 deletions(-) create mode 100644 tests/core/test_model.py delete mode 100644 tests/test_models.py diff --git a/tests/core/test_model.py b/tests/core/test_model.py new file mode 100644 index 000000000..446ad79b0 --- /dev/null +++ b/tests/core/test_model.py @@ -0,0 +1,95 @@ +"""Test module for model.py (and associated functionality). + +This module tests the functionality of model.py, as well as other bits of code that +define models based on the class defined in model.py +""" + +from contextlib import nullcontext as does_not_raise +from logging import CRITICAL, WARNING + +import pytest +from numpy import datetime64, timedelta64 + +from tests.conftest import log_check +from virtual_rainforest.core.model import BaseModel + + +def test_base_model_initialization(caplog, mocker): + """Test `BaseModel` initialization.""" + + # Patch abstract methods so that BaseModel can be instantiated for testing + mocker.patch.object(BaseModel, "__abstractmethods__", new_callable=set) + + # Initialise model + model = BaseModel(timedelta64(1, "W"), datetime64("2022-11-01")) + + # In cases where it passes then checks that the object has the right properties + assert set(["setup", "spinup", "update", "cleanup"]).issubset(dir(model)) + assert str(model) == "A base model instance" + assert ( + repr(model) == "BaseModel(update_interval = 1 weeks, next_update = 2022-11-08)" + ) + + +@pytest.mark.parametrize( + "name,raises,expected_log_entries", + [ + ( + 27, + pytest.raises(TypeError), + ((CRITICAL, "Models should only be named using strings!"),), + ), + ( + "soil", + does_not_raise(), + ( + ( + WARNING, + "Model with name soil already exists and is being replaced", + ), + ), + ), + ( + "abiotic", + does_not_raise(), + ( + ( + WARNING, + "Model with name abiotic already exists and is being replaced", + ), + ), + ), + ( + "freshwater", + does_not_raise(), + (), + ), + ], +) +def test_register_model_errors(caplog, name, raises, expected_log_entries): + """Test that the function registering models generates correct errors/warnings.""" + + with raises: + + class NewSoilModel(BaseModel): + """Test class for use in testing __init_subclass__.""" + + model_name = name + """The model name for use in registering the model and logging.""" + + # Then check that the correct (warning) log messages are emitted + log_check(caplog, expected_log_entries) + + +def test_unnamed_model(caplog): + """Test that the registering a model without a name fails correctly.""" + + with pytest.raises(ValueError): + + class UnnamedModel(BaseModel): + """Model where a model_name hasn't been included.""" + + expected_log_entries = ((CRITICAL, "Models must have a model_name attribute!"),) + + # Then check that the correct (warning) log messages are emitted + log_check(caplog, expected_log_entries) diff --git a/tests/test_models.py b/tests/test_models.py deleted file mode 100644 index ddfb3ecfb..000000000 --- a/tests/test_models.py +++ /dev/null @@ -1,343 +0,0 @@ -"""Test module for model.py (and associated functionality). - -This module tests the functionality of model.py, as well as other bits of code that -define models based on the class defined in model.py -""" - -from contextlib import nullcontext as does_not_raise -from logging import CRITICAL, ERROR, INFO, WARNING - -import pytest -from numpy import datetime64, timedelta64 - -from virtual_rainforest.core.model import BaseModel, InitialisationError -from virtual_rainforest.models.abiotic.abiotic_model import AbioticModel -from virtual_rainforest.models.soil.soil_model import SoilModel - -from .conftest import log_check - - -def test_base_model_initialization(caplog, mocker): - """Test `BaseModel` initialization.""" - - # Patch abstract methods so that BaseModel can be instantiated for testing - mocker.patch.object(BaseModel, "__abstractmethods__", new_callable=set) - - # Initialise model - model = BaseModel(timedelta64(1, "W"), datetime64("2022-11-01")) - - # In cases where it passes then checks that the object has the right properties - assert set(["setup", "spinup", "update", "cleanup"]).issubset(dir(model)) - assert str(model) == "A base model instance" - assert ( - repr(model) == "BaseModel(update_interval = 1 weeks, next_update = 2022-11-08)" - ) - - -@pytest.mark.parametrize( - "no_layers,raises,expected_log_entries", - [ - ( - 2, - does_not_raise(), - (), - ), - ( - -2, - pytest.raises(InitialisationError), - ( - ( - ERROR, - "There has to be at least one soil layer in the soil model!", - ), - ), - ), - ( - 2.5, - pytest.raises(InitialisationError), - ( - ( - ERROR, - "The number of soil layers must be an integer!", - ), - ), - ), - ], -) -def test_soil_model_initialization(caplog, no_layers, raises, expected_log_entries): - """Test `SoilModel` initialization.""" - - with raises: - # Initialize model - model = SoilModel(timedelta64(1, "W"), datetime64("2022-11-01"), no_layers) - - # In cases where it passes then checks that the object has the right properties - assert set(["setup", "spinup", "update", "cleanup"]).issubset(dir(model)) - assert model.model_name == "soil" - assert str(model) == "A soil model instance" - assert ( - repr(model) - == f"SoilModel(update_interval = 1 weeks, next_update = 2022-11-08, " - f"no_layers = {int(no_layers)})" - ) - - # Final check that expected logging entries are produced - log_check(caplog, expected_log_entries) - - -@pytest.mark.parametrize( - "name,raises,expected_log_entries", - [ - ( - 27, - pytest.raises(TypeError), - ((CRITICAL, "Models should only be named using strings!"),), - ), - ( - "soil", - does_not_raise(), - ( - ( - WARNING, - "Model with name soil already exists and is being replaced", - ), - ), - ), - ( - "abiotic", - does_not_raise(), - ( - ( - WARNING, - "Model with name abiotic already exists and is being replaced", - ), - ), - ), - ( - "freshwater", - does_not_raise(), - (), - ), - ], -) -def test_register_model_errors(caplog, name, raises, expected_log_entries): - """Test that the function registering models generates correct errors/warnings.""" - - with raises: - - class NewSoilModel(BaseModel): - """Test class for use in testing __init_subclass__.""" - - model_name = name - """The model name for use in registering the model and logging.""" - - # Then check that the correct (warning) log messages are emitted - log_check(caplog, expected_log_entries) - - -def test_unnamed_model(caplog): - """Test that the registering a model without a name fails correctly.""" - - with pytest.raises(ValueError): - - class UnnamedModel(BaseModel): - """Model where a model_name hasn't been included.""" - - expected_log_entries = ((CRITICAL, "Models must have a model_name attribute!"),) - - # Then check that the correct (warning) log messages are emitted - log_check(caplog, expected_log_entries) - - -@pytest.mark.parametrize( - "config,time_interval,raises,expected_log_entries", - [ - ( - {}, - None, - pytest.raises(KeyError), - (), # This error isn't handled so doesn't generate logging - ), - ( - { - "core": {"timing": {"start_time": "2020-01-01"}}, - "soil": {"no_layers": 2, "model_time_step": "12 hours"}, - }, - timedelta64(12, "h"), - does_not_raise(), - ( - ( - INFO, - "Information required to initialise the soil model successfully " - "extracted.", - ), - ), - ), - ], -) -def test_generate_soil_model( - caplog, config, time_interval, raises, expected_log_entries -): - """Test that the function to initialise the soil model behaves as expected.""" - - # Check whether model is initialised (or not) as expected - with raises: - model = SoilModel.from_config(config) - assert model.no_layers == config["soil"]["no_layers"] - assert model.update_interval == time_interval - assert ( - model.next_update - == datetime64(config["core"]["timing"]["start_time"]) + time_interval - ) - # Run the update step and check that next_update has incremented properly - model.update() - assert ( - model.next_update - == datetime64(config["core"]["timing"]["start_time"]) + 2 * time_interval - ) - - # Final check that expected logging entries are produced - log_check(caplog, expected_log_entries) - - -# ------------------------------------- -# abiotic model tests -# ------------------------------------- - - -@pytest.mark.parametrize( - "soil_layers,canopy_layers,raises,expected_log_entries", - [ - ( - 2, - 3, - does_not_raise(), - (), - ), - ( - -2, - 3, - pytest.raises(InitialisationError), - ( - ( - ERROR, - "There has to be at least one soil layer in the abiotic model!", - ), - ), - ), - ( - 2, - -3, - pytest.raises(InitialisationError), - ( - ( - ERROR, - "There has to be at least one canopy layer in the abiotic model!", - ), - ), - ), - ( - 2.5, - 3, - pytest.raises(InitialisationError), - ( - ( - ERROR, - "The number of soil layers must be an integer!", - ), - ), - ), - ( - 2, - 3.4, - pytest.raises(InitialisationError), - ( - ( - ERROR, - "The number of canopy layers must be an integer!", - ), - ), - ), - ], -) -def test_abiotic_model_initialization( - caplog, soil_layers, canopy_layers, raises, expected_log_entries -): - """Test `AbioticModel` initialization.""" - - with raises: - # Initialize model - model = AbioticModel( - timedelta64(1, "W"), datetime64("2022-11-01"), soil_layers, canopy_layers - ) - - # In cases where it passes then checks that the object has the right properties - assert set(["setup", "spinup", "update", "cleanup"]).issubset(dir(model)) - assert model.model_name == "abiotic" - assert ( - repr(model) - == f"AbioticModel(update_interval = 1 weeks, next_update = 2022-11-08, " - f"soil_layers = {int(soil_layers)}, " - f"canopy_layers = {int(canopy_layers)})" - ) - assert model.soil_layers == soil_layers - assert model.canopy_layers == canopy_layers - - # Final check that expected logging entries are produced - log_check(caplog, expected_log_entries) - - -@pytest.mark.parametrize( - "config,time_interval,raises,expected_log_entries", - [ - ( - {}, - None, - pytest.raises(KeyError), - (), # This error isn't handled so doesn't generate logging - ), - ( - { - "core": {"timing": {"start_time": "2020-01-01"}}, - "abiotic": { - "soil_layers": 2, - "canopy_layers": 3, - "model_time_step": "12 hours", - }, - }, - timedelta64(12, "h"), - does_not_raise(), - ( - ( - INFO, - "Information required to initialise the abiotic model successfully " - "extracted.", - ), - ), - ), - ], -) -def test_generate_abiotic_model( - caplog, config, time_interval, raises, expected_log_entries -): - """Test that the function to initialise the soil model behaves as expected.""" - - # Check whether model is initialised (or not) as expected - with raises: - model = AbioticModel.from_config(config) - assert model.soil_layers == config["abiotic"]["soil_layers"] - assert model.canopy_layers == config["abiotic"]["canopy_layers"] - assert model.update_interval == time_interval - assert ( - model.next_update - == datetime64(config["core"]["timing"]["start_time"]) + time_interval - ) - # Run the update step and check that next_update has incremented properly - model.update() - assert ( - model.next_update - == datetime64(config["core"]["timing"]["start_time"]) + 2 * time_interval - ) - - # Final check that expected logging entries are produced - log_check(caplog, expected_log_entries) From 54c1fa1a3de938ffe2c2bfb7f006d53a6a48abe1 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 10 Feb 2023 11:50:13 +0000 Subject: [PATCH 07/15] Fixed minor problems with tests --- tests/core/test_config.py | 3 +-- tests/core/test_data.py | 3 +-- tests/core/test_readers.py | 2 +- tests/models/soil/test_carbon.py | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 11932805f..d0b10378f 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -13,10 +13,9 @@ import pytest import virtual_rainforest.core.config as config +from tests.conftest import log_check from virtual_rainforest.core.config import register_schema -from .conftest import log_check - @pytest.mark.parametrize( "d_a,d_b,overlap", diff --git a/tests/core/test_data.py b/tests/core/test_data.py index 360e6a315..8b16ea48a 100644 --- a/tests/core/test_data.py +++ b/tests/core/test_data.py @@ -7,10 +7,9 @@ import pytest from xarray import DataArray, Dataset +from tests.conftest import log_check from virtual_rainforest.core.config import ConfigurationError -from .conftest import log_check - @pytest.mark.parametrize( argnames=["use_grid", "exp_err", "expected_log"], diff --git a/tests/core/test_readers.py b/tests/core/test_readers.py index 718004b2d..0438dbb14 100644 --- a/tests/core/test_readers.py +++ b/tests/core/test_readers.py @@ -6,7 +6,7 @@ import pytest from xarray import DataArray -from .conftest import log_check +from tests.conftest import log_check @pytest.mark.parametrize( diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index f916dc76e..47bb849dd 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -9,11 +9,10 @@ import numpy as np import pytest +from tests.conftest import log_check from virtual_rainforest.core.model import InitialisationError from virtual_rainforest.models.soil.carbon import SoilCarbonPools -from .conftest import log_check - @pytest.mark.parametrize( "maom,lmwc,raises,expected_log_entries", From ac68dd1635911c06e13949d8bd65b9d5705c7d68 Mon Sep 17 00:00:00 2001 From: David Orme Date: Fri, 10 Feb 2023 13:06:08 +0000 Subject: [PATCH 08/15] Moved data for core testing, removing parallel fixture paths for data --- tests/{fixtures => core/data}/all_config.toml | 0 .../test_data => core/data}/cellid_coords.nc | Bin .../data}/cellid_coords_bad_cellid.nc | Bin .../data}/cellid_coords_too_few.nc | Bin .../data}/cellid_dim_too_few.nc | Bin .../data}/cellid_dim_too_many.nc | Bin .../test_data => core/data}/cellid_dims.nc | Bin .../data}/default_config.toml | 0 .../data}/example_netcdf_generator.py | 0 .../{data/test_data => core/data}/garbage.nc | 0 tests/{data/test_data => core/data}/test.toml | 0 .../test_data => core/data}/test_dupes.toml | 0 .../data}/this_data_format.not_handled | 0 .../test_data => core/data}/xy_coords.nc | Bin .../data}/xy_coords_shifted.nc | Bin .../data}/xy_coords_small.nc | Bin tests/{data/test_data => core/data}/xy_dim.nc | Bin .../test_data => core/data}/xy_dim_large.nc | Bin .../test_data => core/data}/xy_dim_small.nc | Bin tests/test_main/all_config.toml | 26 ++++++++++++++++++ tests/test_main/default_config.toml | 7 +++++ 21 files changed, 33 insertions(+) rename tests/{fixtures => core/data}/all_config.toml (100%) rename tests/{data/test_data => core/data}/cellid_coords.nc (100%) rename tests/{data/test_data => core/data}/cellid_coords_bad_cellid.nc (100%) rename tests/{data/test_data => core/data}/cellid_coords_too_few.nc (100%) rename tests/{data/test_data => core/data}/cellid_dim_too_few.nc (100%) rename tests/{data/test_data => core/data}/cellid_dim_too_many.nc (100%) rename tests/{data/test_data => core/data}/cellid_dims.nc (100%) rename tests/{fixtures => core/data}/default_config.toml (100%) rename tests/{data/test_data => core/data}/example_netcdf_generator.py (100%) rename tests/{data/test_data => core/data}/garbage.nc (100%) rename tests/{data/test_data => core/data}/test.toml (100%) rename tests/{data/test_data => core/data}/test_dupes.toml (100%) rename tests/{data/test_data => core/data}/this_data_format.not_handled (100%) rename tests/{data/test_data => core/data}/xy_coords.nc (100%) rename tests/{data/test_data => core/data}/xy_coords_shifted.nc (100%) rename tests/{data/test_data => core/data}/xy_coords_small.nc (100%) rename tests/{data/test_data => core/data}/xy_dim.nc (100%) rename tests/{data/test_data => core/data}/xy_dim_large.nc (100%) rename tests/{data/test_data => core/data}/xy_dim_small.nc (100%) create mode 100644 tests/test_main/all_config.toml create mode 100644 tests/test_main/default_config.toml diff --git a/tests/fixtures/all_config.toml b/tests/core/data/all_config.toml similarity index 100% rename from tests/fixtures/all_config.toml rename to tests/core/data/all_config.toml diff --git a/tests/data/test_data/cellid_coords.nc b/tests/core/data/cellid_coords.nc similarity index 100% rename from tests/data/test_data/cellid_coords.nc rename to tests/core/data/cellid_coords.nc diff --git a/tests/data/test_data/cellid_coords_bad_cellid.nc b/tests/core/data/cellid_coords_bad_cellid.nc similarity index 100% rename from tests/data/test_data/cellid_coords_bad_cellid.nc rename to tests/core/data/cellid_coords_bad_cellid.nc diff --git a/tests/data/test_data/cellid_coords_too_few.nc b/tests/core/data/cellid_coords_too_few.nc similarity index 100% rename from tests/data/test_data/cellid_coords_too_few.nc rename to tests/core/data/cellid_coords_too_few.nc diff --git a/tests/data/test_data/cellid_dim_too_few.nc b/tests/core/data/cellid_dim_too_few.nc similarity index 100% rename from tests/data/test_data/cellid_dim_too_few.nc rename to tests/core/data/cellid_dim_too_few.nc diff --git a/tests/data/test_data/cellid_dim_too_many.nc b/tests/core/data/cellid_dim_too_many.nc similarity index 100% rename from tests/data/test_data/cellid_dim_too_many.nc rename to tests/core/data/cellid_dim_too_many.nc diff --git a/tests/data/test_data/cellid_dims.nc b/tests/core/data/cellid_dims.nc similarity index 100% rename from tests/data/test_data/cellid_dims.nc rename to tests/core/data/cellid_dims.nc diff --git a/tests/fixtures/default_config.toml b/tests/core/data/default_config.toml similarity index 100% rename from tests/fixtures/default_config.toml rename to tests/core/data/default_config.toml diff --git a/tests/data/test_data/example_netcdf_generator.py b/tests/core/data/example_netcdf_generator.py similarity index 100% rename from tests/data/test_data/example_netcdf_generator.py rename to tests/core/data/example_netcdf_generator.py diff --git a/tests/data/test_data/garbage.nc b/tests/core/data/garbage.nc similarity index 100% rename from tests/data/test_data/garbage.nc rename to tests/core/data/garbage.nc diff --git a/tests/data/test_data/test.toml b/tests/core/data/test.toml similarity index 100% rename from tests/data/test_data/test.toml rename to tests/core/data/test.toml diff --git a/tests/data/test_data/test_dupes.toml b/tests/core/data/test_dupes.toml similarity index 100% rename from tests/data/test_data/test_dupes.toml rename to tests/core/data/test_dupes.toml diff --git a/tests/data/test_data/this_data_format.not_handled b/tests/core/data/this_data_format.not_handled similarity index 100% rename from tests/data/test_data/this_data_format.not_handled rename to tests/core/data/this_data_format.not_handled diff --git a/tests/data/test_data/xy_coords.nc b/tests/core/data/xy_coords.nc similarity index 100% rename from tests/data/test_data/xy_coords.nc rename to tests/core/data/xy_coords.nc diff --git a/tests/data/test_data/xy_coords_shifted.nc b/tests/core/data/xy_coords_shifted.nc similarity index 100% rename from tests/data/test_data/xy_coords_shifted.nc rename to tests/core/data/xy_coords_shifted.nc diff --git a/tests/data/test_data/xy_coords_small.nc b/tests/core/data/xy_coords_small.nc similarity index 100% rename from tests/data/test_data/xy_coords_small.nc rename to tests/core/data/xy_coords_small.nc diff --git a/tests/data/test_data/xy_dim.nc b/tests/core/data/xy_dim.nc similarity index 100% rename from tests/data/test_data/xy_dim.nc rename to tests/core/data/xy_dim.nc diff --git a/tests/data/test_data/xy_dim_large.nc b/tests/core/data/xy_dim_large.nc similarity index 100% rename from tests/data/test_data/xy_dim_large.nc rename to tests/core/data/xy_dim_large.nc diff --git a/tests/data/test_data/xy_dim_small.nc b/tests/core/data/xy_dim_small.nc similarity index 100% rename from tests/data/test_data/xy_dim_small.nc rename to tests/core/data/xy_dim_small.nc diff --git a/tests/test_main/all_config.toml b/tests/test_main/all_config.toml new file mode 100644 index 000000000..ce73d26b7 --- /dev/null +++ b/tests/test_main/all_config.toml @@ -0,0 +1,26 @@ +[core] +modules = ["plants", "soil"] + +[core.grid] +nx = 10 +ny = 10 + +[core.timing] +start_date = "2020-01-01" +update_interval = "10 minutes" +run_length = "50 years" + +[plants] +a_plant_integer = 12 + +[[plants.ftypes]] +pft_name = "shrub" +maxh = 1.0 + +[[plants.ftypes]] +pft_name = "broadleaf" +maxh = 50.0 + +[soil] +no_layers = 1 +model_time_step = "5 days" diff --git a/tests/test_main/default_config.toml b/tests/test_main/default_config.toml new file mode 100644 index 000000000..e211a3830 --- /dev/null +++ b/tests/test_main/default_config.toml @@ -0,0 +1,7 @@ +[[plants.ftypes]] +pft_name = "shrub" +maxh = 1.0 + +[[plants.ftypes]] +pft_name = "broadleaf" +maxh = 50.0 \ No newline at end of file From 1711a2b0be20c27e97b9fc509b9ed8b2fa95a3cb Mon Sep 17 00:00:00 2001 From: David Orme Date: Fri, 10 Feb 2023 13:12:27 +0000 Subject: [PATCH 09/15] removed test_data nesting in TOML config files --- tests/core/data/test.toml | 8 ++++---- tests/core/data/test_dupes.toml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/core/data/test.toml b/tests/core/data/test.toml index fd1edf68a..2d53dda27 100644 --- a/tests/core/data/test.toml +++ b/tests/core/data/test.toml @@ -1,12 +1,12 @@ [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "temp" [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "prec" [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "elev" [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "vapd" diff --git a/tests/core/data/test_dupes.toml b/tests/core/data/test_dupes.toml index 3d0a8420a..8552fe204 100644 --- a/tests/core/data/test_dupes.toml +++ b/tests/core/data/test_dupes.toml @@ -1,12 +1,12 @@ [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "temp" [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "prec" [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "elev" [[core.data.variable]] -file = "test_data/cellid_coords.nc" +file = "cellid_coords.nc" var_name = "elev" From 6013af1c21804577962bc4e75fcc9619d684fd36 Mon Sep 17 00:00:00 2001 From: David Orme Date: Fri, 10 Feb 2023 13:13:50 +0000 Subject: [PATCH 10/15] Updated reader and data tests to new structure --- tests/core/test_data.py | 34 +++++++++++++++++----------------- tests/core/test_readers.py | 10 +++++----- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/core/test_data.py b/tests/core/test_data.py index 8b16ea48a..464d20964 100644 --- a/tests/core/test_data.py +++ b/tests/core/test_data.py @@ -250,7 +250,7 @@ def test_Data_contains(fixture_data, var_name, expected): def test_Data_load_to_dataarray_naming(caplog, shared_datadir, name, exp_log): """Test the coding of the name handling and replacement.""" - # Setup a Data instance to match the example files generated in test_data/ + # Setup a Data instance to match the example files generated in tests/core/data from virtual_rainforest.core.data import Data from virtual_rainforest.core.grid import Grid @@ -271,7 +271,7 @@ def test_Data_load_to_dataarray_naming(caplog, shared_datadir, name, exp_log): caplog.clear() # Load the data from file - datafile = shared_datadir / "test_data/cellid_coords.nc" + datafile = shared_datadir / "cellid_coords.nc" data[name] = load_to_dataarray(file=datafile, var_name=name) @@ -322,7 +322,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["__any__"], - "test_data/cellid_dims.nc", + "cellid_dims.nc", does_not_raise(), None, ( @@ -334,7 +334,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["__any__"], - "test_data/cellid_dim_too_few.nc", + "cellid_dim_too_few.nc", pytest.raises(ValueError), "Grid defines 100 cells, data provides 60", ( @@ -347,7 +347,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["__any__"], - "test_data/cellid_dim_too_many.nc", + "cellid_dim_too_many.nc", pytest.raises(ValueError), "Grid defines 100 cells, data provides 200", ( @@ -360,7 +360,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["__any__"], - "test_data/cellid_coords.nc", + "cellid_coords.nc", does_not_raise(), None, ( @@ -372,7 +372,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["__any__"], - "test_data/cellid_coords_too_few.nc", + "cellid_coords_too_few.nc", pytest.raises(ValueError), "The data cell ids do not provide a one-to-one map onto grid " "cell ids.", ( @@ -389,7 +389,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["__any__"], - "test_data/cellid_coords_bad_cellid.nc", + "cellid_coords_bad_cellid.nc", pytest.raises(ValueError), "The data cell ids do not provide a one-to-one map onto grid " "cell ids.", ( @@ -406,7 +406,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["square"], - "test_data/xy_dim.nc", + "xy_dim.nc", does_not_raise(), None, ( @@ -418,7 +418,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["square"], - "test_data/xy_dim_small.nc", + "xy_dim_small.nc", pytest.raises(ValueError), "Data XY dimensions do not match square grid", ( @@ -431,7 +431,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["square"], - "test_data/xy_coords.nc", + "xy_coords.nc", does_not_raise(), None, ( @@ -443,7 +443,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["square"], - "test_data/xy_coords_small.nc", + "xy_coords_small.nc", pytest.raises(ValueError), "Mapped points do not cover all cells.", ( @@ -456,7 +456,7 @@ def fixture_load_data_grids(request): ), pytest.param( ["square"], - "test_data/xy_coords_shifted.nc", + "xy_coords_shifted.nc", pytest.raises(ValueError), "Mapped points fall outside grid.", ( @@ -492,7 +492,7 @@ def test_Data_load_to_dataarray_data_handling( data name and name replacement functionality """ - # Setup a Data instance to match the example files generated in test_data/ + # Setup a Data instance to match the example files generated in tests/core/data from virtual_rainforest.core.data import Data from virtual_rainforest.core.readers import load_to_dataarray @@ -526,7 +526,7 @@ def test_Data_load_to_dataarray_data_handling( argnames=["file", "exp_error", "exp_msg", "exp_log"], argvalues=[ pytest.param( - "test_data/test.toml", + "test.toml", does_not_raise(), None, ( @@ -543,7 +543,7 @@ def test_Data_load_to_dataarray_data_handling( id="valid config", ), pytest.param( - "test_data/test_dupes.toml", + "test_dupes.toml", pytest.raises(ConfigurationError), "Data configuration did not load cleanly", ( @@ -577,7 +577,7 @@ def test_Data_load_from_config( config loader part of the mechanism """ - # Setup a Data instance to match the example files generated in test_data/ + # Setup a Data instance to match the example files generated in tests/core/data from virtual_rainforest.core.config import load_in_config_files from virtual_rainforest.core.data import Data diff --git a/tests/core/test_readers.py b/tests/core/test_readers.py index 0438dbb14..d93cab01d 100644 --- a/tests/core/test_readers.py +++ b/tests/core/test_readers.py @@ -77,19 +77,19 @@ def test_func(): ((CRITICAL, "Data file not found"),), ), ( - "test_data/garbage.nc", + "garbage.nc", "irrelevant", pytest.raises(ValueError), ((CRITICAL, "Could not load data from"),), ), ( - "test_data/xy_dim.nc", + "xy_dim.nc", "missing", pytest.raises(KeyError), ((CRITICAL, "Variable missing not found in"),), ), ( - "test_data/xy_dim.nc", + "xy_dim.nc", "temp", does_not_raise(), (), @@ -127,7 +127,7 @@ def test_load_netcdf(shared_datadir, caplog, file, file_var, exp_err, expected_l id="unhandled file format", ), pytest.param( - "test_data/cellid_dims.nc", + "cellid_dims.nc", does_not_raise(), None, ((INFO, "Loading variable 'temp' from file:"),), @@ -151,7 +151,7 @@ def test_load_to_dataarray( and the test methods for individual readers should test failure modes. """ - # Setup a Data instance to match the example files generated in test_data/ + # Setup a Data instance to match the example files generated in tests/core/data from virtual_rainforest.core.readers import load_to_dataarray From fce07f97d742c592de67720ca59f3cfc418101d6 Mon Sep 17 00:00:00 2001 From: David Orme Date: Fri, 10 Feb 2023 13:36:08 +0000 Subject: [PATCH 11/15] Switched test_config::test_final_validation_log to use data dir; minor bug in vr.core.config.py --- tests/core/test_config.py | 31 ++++++++++--------------------- virtual_rainforest/core/config.py | 8 ++++++-- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/tests/core/test_config.py b/tests/core/test_config.py index d0b10378f..d17f0144e 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -251,41 +251,30 @@ def test_construct_combined_schema(caplog: pytest.LogCaptureFixture) -> None: "file_path,expected_log_entries", [ ( - "tests/fixtures/default_config.toml", # File entirely of defaults + "default_config.toml", # File entirely of defaults ( - ( - INFO, - "Configuration files successfully validated!", - ), - ( - INFO, - "Saving all configuration details to complete_config.toml", - ), + (INFO, "Configuration files successfully validated!"), + (INFO, "Saving all configuration details to"), ), ), ( - "tests/fixtures/all_config.toml", # File with no defaults + "all_config.toml", # File with no defaults ( - ( - INFO, - "Configuration files successfully validated!", - ), - ( - INFO, - "Saving all configuration details to complete_config.toml", - ), + (INFO, "Configuration files successfully validated!"), + (INFO, "Saving all configuration details to"), ), ), ], ) -def test_final_validation_log(caplog, file_path, expected_log_entries): +def test_final_validation_log(caplog, shared_datadir, file_path, expected_log_entries): """Checks that validation passes as expected and produces the correct output.""" - config.validate_config([file_path], Path("./complete_config.toml")) + outfile = shared_datadir / "complete_config.toml" + config.validate_config([shared_datadir / file_path], outfile) # Remove generated output file # As a bonus tests that output file was generated correctly + to the right location - Path("./complete_config.toml").unlink() + outfile.unlink() # Then check that the correct (critical error) log messages are emitted log_check(caplog, expected_log_entries) diff --git a/virtual_rainforest/core/config.py b/virtual_rainforest/core/config.py index e8a7f202d..9ac01cc15 100644 --- a/virtual_rainforest/core/config.py +++ b/virtual_rainforest/core/config.py @@ -235,8 +235,12 @@ def check_outfile(merge_file_path: Path) -> None: directory, or the final output file already exists. """ - # Extract parent folder name and output file name - parent_fold = merge_file_path.parent.relative_to(".") + # Extract parent folder name and output file name. If this is a relative path, it is + # expected to be relative to where the command is being run. + if not merge_file_path.is_absolute(): + parent_fold = merge_file_path.parent.relative_to(".") + else: + parent_fold = merge_file_path.parent out_file_name = merge_file_path.name # Throw critical error if the output folder doesn't exist From fa666f985daac35efe7d7feba990054f9ad7107d Mon Sep 17 00:00:00 2001 From: David Orme Date: Fri, 10 Feb 2023 13:56:40 +0000 Subject: [PATCH 12/15] Updated test_config::test_collect_files to use shared_datadir --- tests/core/test_config.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/core/test_config.py b/tests/core/test_config.py index d17f0144e..dfe8b7a5e 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -97,8 +97,7 @@ def test_check_outfile(caplog, mocker, out_path, expected_log_entries): ( ( CRITICAL, - "The following (user provided) config paths do not exist:\n" - "['Nonsense/file/location']", + "The following (user provided) config paths do not exist:", ), ), ), @@ -110,13 +109,13 @@ def test_check_outfile(caplog, mocker, out_path, expected_log_entries): ( CRITICAL, "The following (user provided) config folders do not contain any " - "toml files:\n['.']", + "toml files:", ), ), ), ( - ["tests/fixtures/", "tests/fixtures/all_config.toml"], - [Path("tests/fixtures/all_config.toml")], + ["", "all_config.toml"], + ["all_config.toml"], config.ConfigurationError, ( ( @@ -129,17 +128,23 @@ def test_check_outfile(caplog, mocker, out_path, expected_log_entries): ], ) def test_collect_files( - caplog, mocker, cfg_paths, contents, expected_exception, expected_log_entries + caplog, + mocker, + shared_datadir, + cfg_paths, + contents, + expected_exception, + expected_log_entries, ): """Checks errors for missing config files.""" - # Configure the mock to return a specific list of files + # Configure the mock to return a specific list of files when globbing a directory mock_get = mocker.patch("virtual_rainforest.core.config.Path.glob") - mock_get.return_value = contents + mock_get.return_value = [shared_datadir / fn for fn in contents] # Check that file collection fails as expected with pytest.raises(expected_exception): - config.collect_files(cfg_paths) + config.collect_files([shared_datadir / fn for fn in cfg_paths]) log_check(caplog, expected_log_entries) From 3181e9c4260f9cfb34e58fed9d2ccd693ba7e5ea Mon Sep 17 00:00:00 2001 From: David Orme Date: Fri, 10 Feb 2023 14:03:47 +0000 Subject: [PATCH 13/15] test_main.py updated; tests are mocking _all_ the file content, so actual files not needed --- tests/test_main.py | 6 +++--- tests/test_main/all_config.toml | 26 -------------------------- tests/test_main/default_config.toml | 7 ------- 3 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 tests/test_main/all_config.toml delete mode 100644 tests/test_main/default_config.toml diff --git a/tests/test_main.py b/tests/test_main.py index 79fe50989..d0cc720a4 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -172,7 +172,7 @@ def test_vr_run_miss_model(mocker, caplog): mock_conf.return_value = {"core": {"modules": ["topsoil"]}} with pytest.raises(InitialisationError): - vr_run("tests/fixtures/all_config.toml", Path("./delete_me.toml")) + vr_run("path/does/not/need/to/exist", Path("./delete_me.toml")) # If vr_run is successful (which it shouldn't be) clean up the file Path("./delete_me.toml").unlink() @@ -206,7 +206,7 @@ def test_vr_run_bad_model(mocker, caplog): } with pytest.raises(InitialisationError): - vr_run("tests/fixtures/all_config.toml", Path("./delete_me.toml")) + vr_run("path/does/not/need/to/exist", Path("./delete_me.toml")) # If vr_run is successful (which it shouldn't be) clean up the file Path("./delete_me.toml").unlink() @@ -350,7 +350,7 @@ def test_extract_timing_details(caplog, config, output, raises, expected_log_ent ), ], ) -def test_check_for_fast_models(caplog, mocker, update_interval, expected_log_entries): +def test_check_for_fast_models(caplog, update_interval, expected_log_entries): """Test that function to warn user about short module time steps works.""" # Create SoilModel instance and then populate the update_interval diff --git a/tests/test_main/all_config.toml b/tests/test_main/all_config.toml deleted file mode 100644 index ce73d26b7..000000000 --- a/tests/test_main/all_config.toml +++ /dev/null @@ -1,26 +0,0 @@ -[core] -modules = ["plants", "soil"] - -[core.grid] -nx = 10 -ny = 10 - -[core.timing] -start_date = "2020-01-01" -update_interval = "10 minutes" -run_length = "50 years" - -[plants] -a_plant_integer = 12 - -[[plants.ftypes]] -pft_name = "shrub" -maxh = 1.0 - -[[plants.ftypes]] -pft_name = "broadleaf" -maxh = 50.0 - -[soil] -no_layers = 1 -model_time_step = "5 days" diff --git a/tests/test_main/default_config.toml b/tests/test_main/default_config.toml deleted file mode 100644 index e211a3830..000000000 --- a/tests/test_main/default_config.toml +++ /dev/null @@ -1,7 +0,0 @@ -[[plants.ftypes]] -pft_name = "shrub" -maxh = 1.0 - -[[plants.ftypes]] -pft_name = "broadleaf" -maxh = 50.0 \ No newline at end of file From 084b28d13f311bf85f12944751b191ea046abb29 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Mon, 13 Feb 2023 08:50:22 +0000 Subject: [PATCH 14/15] Clarified description of test_model.py --- tests/core/test_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/test_model.py b/tests/core/test_model.py index 446ad79b0..4b70ff54d 100644 --- a/tests/core/test_model.py +++ b/tests/core/test_model.py @@ -1,7 +1,7 @@ """Test module for model.py (and associated functionality). -This module tests the functionality of model.py, as well as other bits of code that -define models based on the class defined in model.py +This module tests the functionality of model.py, which defines the basic model API that +specific models (e.g. `soil_model.py`) utilise. """ from contextlib import nullcontext as does_not_raise From 3f979e1a8c636c157dbd35d06c238036b6d0ab9b Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Mon, 13 Feb 2023 08:54:25 +0000 Subject: [PATCH 15/15] Removed outdated (and assocaited functionality) --- tests/core/test_model.py | 2 +- tests/models/abiotic/test_abiotic_model.py | 2 +- tests/models/soil/test_soil_model.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/core/test_model.py b/tests/core/test_model.py index 4b70ff54d..17424ab59 100644 --- a/tests/core/test_model.py +++ b/tests/core/test_model.py @@ -1,4 +1,4 @@ -"""Test module for model.py (and associated functionality). +"""Test module for model.py. This module tests the functionality of model.py, which defines the basic model API that specific models (e.g. `soil_model.py`) utilise. diff --git a/tests/models/abiotic/test_abiotic_model.py b/tests/models/abiotic/test_abiotic_model.py index 1d3497137..c94671eb4 100644 --- a/tests/models/abiotic/test_abiotic_model.py +++ b/tests/models/abiotic/test_abiotic_model.py @@ -1,4 +1,4 @@ -"""Test module for abiotic_model.py (and associated functionality).""" +"""Test module for abiotic_model.py.""" from contextlib import nullcontext as does_not_raise from logging import ERROR, INFO diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 09119a72a..d7c4caa9a 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -1,4 +1,4 @@ -"""Test module for soil_model.py (and associated functionality).""" +"""Test module for soil_model.py.""" from contextlib import nullcontext as does_not_raise from logging import ERROR, INFO