Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,11 @@ Version Number: <!--- Include version number of MeshiPhi the PR will be included
## Fixes # (issue)
<!--- If this PR adds functionality or resolves problems associated with an issue on GitHub, please include a link to the issue -->

# Testing
To ensure that the functionality of the MeshiPhi codebase remains consistent throughout the development cycle a testing strategy has been developed, which can be viewed in the document `test/testing_strategy.md`.
This includes a collection of test files which should be run according to which part of the codebase has been altered in a pull request. Please consult the testing strategy to determine which tests need to be run.

- [ ] My changes have not altered any of the files listed in the testing strategy

- [ ] My changes result in all required regression tests passing without the need to update test files.

> *list which files have been altered and include a pytest.txt file for each of
> the tests required to be run*
>
> The files which have been changed during this PR can be listed using the command

git diff --name-only 2.2.x

- [ ] My changes require one or more test files to be updated for all regression tests to pass.

> *include pytest.txt file showing which tests fail.*
> *include reasoning as to why your changes cause these tests to fail.*
>
> Should these changes be valid, relevant test files should be updated.
> *include pytest.txt file of test passing after test files have been updated.*

# Checklist

- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have updated the documentation of the codebase where required.
- [ ] My changes generate no new warnings.
- [ ] My PR has been made to the `2.2.x` branch (**DO NOT SUBMIT A PR TO MAIN**)


1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ Contents:
./sections/Dataloaders/overview
./sections/Mesh_Construction/Mesh_construction_overview
./sections/Plotting/mesh_plotting
./sections/testing_strategy
26 changes: 26 additions & 0 deletions docs/source/sections/testing_strategy.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. _testing_strategy:

Testing Strategy
=================

When updating any files within the MeshiPhi repository, tests must be run to ensure that the core functionality of the software remains unchanged.

To allow for validation of changes, a suite of regression tests have been provided in the folder ``tests/regression_tests/...``.

These tests attempt to rebuild existing test cases using the changed code and compares these rebuilt outputs to the reference test files.

To run tests:

`pytest`

To run tests in parallel (faster):

`pytest -n auto`

To avoid running slow tests:

`pytest -m "not slow"`

To run only slow tests:

`pytest -m slow`
20 changes: 11 additions & 9 deletions meshiphi/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from meshiphi.mesh_generation.environment_mesh import EnvironmentMesh
from meshiphi.test_automation.test_automater import TestAutomater

logger = logging.getLogger(__name__)


@setup_logging
def get_args(
Expand Down Expand Up @@ -99,7 +101,7 @@ def rebuild_mesh_cli():

default_output = "rebuild_mesh.output.json"
args = get_args(default_output, mesh_arg=True, config_arg=False)
logging.info("{} {}".format(inspect.stack()[0][3][:-4], version))
logger.info("{} {}".format(inspect.stack()[0][3][:-4], version))

mesh_json = json.load(args.mesh)
config = mesh_json["config"]["mesh_info"]
Expand All @@ -108,7 +110,7 @@ def rebuild_mesh_cli():
rebuilt_mesh = MeshBuilder(config).build_environmental_mesh()
rebuilt_mesh_json = rebuilt_mesh.to_json()

logging.info("Saving mesh to {}".format(args.output))
logger.info("Saving mesh to {}".format(args.output))

json.dump(rebuilt_mesh_json, open(args.output, "w"), indent=4)

Expand All @@ -121,14 +123,14 @@ def create_mesh_cli():

default_output = "create_mesh.output.json"
args = get_args(default_output)
logging.info("{} {}".format(inspect.stack()[0][3][:-4], version))
logger.info("{} {}".format(inspect.stack()[0][3][:-4], version))

config = json.load(args.config)

# Discrete Meshing
cg = MeshBuilder(config).build_environmental_mesh()

logging.info("Saving mesh to {}".format(args.output))
logger.info("Saving mesh to {}".format(args.output))
info = cg.to_json()
json.dump(info, open(args.output, "w"), indent=4)

Expand All @@ -153,12 +155,12 @@ def export_mesh_cli():
elif args.format.upper() == "PNG":
args = get_args("mesh.png", config_arg=False, mesh_arg=True, format_arg=True)

logging.info("{} {}".format(inspect.stack()[0][3][:-4], version))
logger.info("{} {}".format(inspect.stack()[0][3][:-4], version))

mesh = json.load(args.mesh)
env_mesh = EnvironmentMesh.load_from_json(mesh)

logging.info(f"exporting mesh to {args.output} in format {args.format}")
logger.info(f"exporting mesh to {args.output} in format {args.format}")

env_mesh.save(args.output, args.format, args.format_conf)

Expand All @@ -174,14 +176,14 @@ def merge_mesh_cli():

default_output = "merged_mesh.output.json"
args = get_args(default_output, config_arg=False, mesh_arg=True, merge_arg=True)
logging.info("{} {}".format(inspect.stack()[0][3][:-4], version))
logger.info("{} {}".format(inspect.stack()[0][3][:-4], version))

with open(args.mesh.name, "r") as f:
mesh1 = json.load(args.mesh)
env_mesh1 = EnvironmentMesh.load_from_json(mesh1)

if args.directory:
logging.debug(
logger.debug(
"Merging multiple meshes from directory {} with input mesh".format(
args.merge
)
Expand All @@ -206,7 +208,7 @@ def merge_mesh_cli():

merged_mesh_json = env_mesh1.to_json()

logging.info("Saving merged mesh to {}".format(args.output))
logger.info("Saving merged mesh to {}".format(args.output))
json.dump(merged_mesh_json, open(args.output, "w"), indent=4)


Expand Down
30 changes: 16 additions & 14 deletions meshiphi/dataloaders/lut/abstract_lut.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from shapely.strtree import STRtree
from shapely.ops import unary_union

logger = logging.getLogger(__name__)


class LutDataLoader(DataLoaderInterface):
"""
Expand Down Expand Up @@ -40,47 +42,47 @@ def __init__(self, bounds, params):
"""
# Translates parameters from config input to desired inputs
params = self.add_default_params(params)
logging.info(f"Initialising {params['dataloader_name']} dataloader")
logger.info(f"Initialising {params['dataloader_name']} dataloader")
# Creates a class attribute for all keys in params
for key, val in params.items():
setattr(self, key, val)

# Read in and manipulate data to standard form
self.data = self.import_data(bounds)
if "files" in params:
logging.info("\tFiles read:")
logger.info("\tFiles read:")
for file in self.files:
logging.info(f"\t\t{file}")
logger.info(f"\t\t{file}")

# Get data name from column name if not set in params
if self.data_name is None:
logging.debug("\tSetting self.data_name from column name")
logger.debug("\tSetting self.data_name from column name")
self.data_name = self.get_data_col_name()
# or if set in params, set col name to data name
else:
logging.debug(f"\tSetting data column name to {self.data_name}")
logger.debug(f"\tSetting data column name to {self.data_name}")
self.data = self.set_data_col_name(self.data_name)

# Verify that all geometries are acceptable inputs
self.data = self.verify_data()

# Calculate fraction of boundary that data covers
data_coverage = self.calculate_coverage(bounds)
logging.info(
logger.info(
"\tMercator data range (roughly) covers "
+ f"{np.round(data_coverage * 100, 0).astype(int)}% "
+ "of initial boundary"
)
# If there's 0 datapoints in the initial boundary, raise ValueError
if data_coverage == 0:
logging.error("\tDataloader has no data in initial region!")
logger.error("\tDataloader has no data in initial region!")
raise ValueError(
f"Dataloader {params['dataloader_name']}"
+ " contains no data within initial region!"
)
else:
# Cut dataset down to initial boundary
logging.info(
logger.info(
"\tTrimming data to initial boundary: {min} to {max}".format(
min=(bounds.get_lat_min(), bounds.get_long_min()),
max=(bounds.get_lat_max(), bounds.get_long_max()),
Expand Down Expand Up @@ -251,7 +253,7 @@ def get_value(self, bounds, agg_type=None, skipna=False, data=None):
ValueError: aggregation type not in list of available methods
"""
polygons = self.trim_datapoints(bounds, data=data)
logging.debug(
logger.debug(
f"\t{len(polygons)} polygons found for attribute "
+ f"'{self.data_name}' within bounds '{bounds}'"
)
Expand Down Expand Up @@ -360,13 +362,13 @@ def reproject(self):
"""
Reprojection not supported by LookUpTable Dataloader
"""
logging.warning("Reprojection not supported by LookUpTable Dataloader")
logger.warning("Reprojection not supported by LookUpTable Dataloader")

def downsample(self):
"""
Downsampling not supported by LookUpTable Dataloader
"""
logging.warning("Downsampling not supported by LookUpTable Dataloader")
logger.warning("Downsampling not supported by LookUpTable Dataloader")

def get_data_col_name(self):
"""
Expand All @@ -383,7 +385,7 @@ def get_data_col_name(self):
name
"""

logging.debug(f"\tRetrieving data name from {type(self.data)}")
logger.debug(f"\tRetrieving data name from {type(self.data)}")

unique_cols = list(set(self.data.columns) - set(["time", "geometry"]))

Expand All @@ -407,8 +409,8 @@ def set_data_col_name(self, new_name):

old_name = self.get_data_col_name()
if old_name != new_name:
logging.info(f"\tChanging data name from {old_name} to {new_name}")
logger.info(f"\tChanging data name from {old_name} to {new_name}")
return self.data.rename({old_name: new_name})
else:
logging.info(f"\tData is already labelled '{new_name}'")
logger.info(f"\tData is already labelled '{new_name}'")
return self.data
Loading