Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert unit/fileformats/cf to pytest #5873

Merged
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
14 changes: 6 additions & 8 deletions lib/iris/tests/unit/fileformats/cf/test_CFGroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Unit tests for the :class:`iris.fileformats.cf.CFGroup` class."""

from unittest.mock import MagicMock

import pytest

from iris.fileformats.cf import (
CFAuxiliaryCoordinateVariable,
CFCoordinateVariable,
CFDataVariable,
CFGroup,
)

# Import iris.tests first so that some things can be initialised before
# importing anything else.
import iris.tests as tests


class Tests(tests.IrisTest):
class Tests:
# TODO: unit tests for existing functionality pre 2021-03-11.
def setUp(self):
@pytest.fixture(autouse=True)
def _setup(self):
self.cf_group = CFGroup()

def test_non_data_names(self):
Expand All @@ -41,4 +39,4 @@ def test_non_data_names(self):

expected_names = [var.cf_name for var in (aux_var, coord_var, coord_var2)]
expected = set(expected_names)
self.assertEqual(expected, self.cf_group.non_data_variable_names)
assert self.cf_group.non_data_variable_names == expected
278 changes: 123 additions & 155 deletions lib/iris/tests/unit/fileformats/cf/test_CFReader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Unit tests for the `iris.fileformats.cf.CFReader` class."""

# Import iris.tests first so that some things can be initialised before
# importing anything else.
import iris.tests as tests # isort:skip

from unittest import mock
bjlittle marked this conversation as resolved.
Show resolved Hide resolved

import numpy as np
import pytest

from iris.fileformats.cf import CFReader

Expand Down Expand Up @@ -53,29 +49,31 @@ def netcdf_variable(
return ncvar


class Test_translate__global_attributes(tests.IrisTest):
def setUp(self):
class Test_translate__global_attributes:
@pytest.fixture(autouse=True)
def _setup(self, mocker):
ncvar = netcdf_variable("ncvar", "height", np.float64)
ncattrs = mock.Mock(return_value=["dimensions"])
getncattr = mock.Mock(return_value="something something_else")
self.dataset = mock.Mock(
dataset = mock.Mock(
bjlittle marked this conversation as resolved.
Show resolved Hide resolved
file_format="NetCDF4",
variables={"ncvar": ncvar},
ncattrs=ncattrs,
getncattr=getncattr,
)

def test_create_global_attributes(self):
with mock.patch(
mocker.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
):
global_attrs = CFReader("dummy").cf_group.global_attributes
self.assertEqual(global_attrs["dimensions"], "something something_else")
return_value=dataset,
)

def test_create_global_attributes(self, mocker):
global_attrs = CFReader("dummy").cf_group.global_attributes
assert global_attrs["dimensions"] == "something something_else"


class Test_translate__formula_terms(tests.IrisTest):
def setUp(self):
class Test_translate__formula_terms:
@pytest.fixture(autouse=True)
def _setup(self, mocker):
self.delta = netcdf_variable("delta", "height", np.float64, bounds="delta_bnds")
self.delta_bnds = netcdf_variable("delta_bnds", "height bnds", np.float64)
self.sigma = netcdf_variable("sigma", "height", np.float64, bounds="sigma_bnds")
Expand Down Expand Up @@ -124,53 +122,50 @@ def setUp(self):
file_format="NetCDF4", variables=self.variables, ncattrs=ncattrs
)
# Restrict the CFReader functionality to only performing translations.
build_patch = mock.patch("iris.fileformats.cf.CFReader._build_cf_groups")
reset_patch = mock.patch("iris.fileformats.cf.CFReader._reset")
build_patch.start()
reset_patch.start()
self.addCleanup(build_patch.stop)
self.addCleanup(reset_patch.stop)

def test_create_formula_terms(self):
with mock.patch(
mocker.patch("iris.fileformats.cf.CFReader._build_cf_groups")
mocker.patch("iris.fileformats.cf.CFReader._reset")
mocker.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
):
cf_group = CFReader("dummy").cf_group
self.assertEqual(len(cf_group), len(self.variables))
# Check there is a singular data variable.
group = cf_group.data_variables
self.assertEqual(len(group), 1)
self.assertEqual(list(group.keys()), ["temp"])
self.assertIs(group["temp"].cf_data, self.temp)
# Check there are three coordinates.
group = cf_group.coordinates
self.assertEqual(len(group), 3)
coordinates = ["height", "lat", "lon"]
self.assertEqual(set(group.keys()), set(coordinates))
for name in coordinates:
self.assertIs(group[name].cf_data, getattr(self, name))
# Check there are three auxiliary coordinates.
group = cf_group.auxiliary_coordinates
self.assertEqual(len(group), 3)
aux_coordinates = ["delta", "sigma", "orography"]
self.assertEqual(set(group.keys()), set(aux_coordinates))
for name in aux_coordinates:
self.assertIs(group[name].cf_data, getattr(self, name))
# Check all the auxiliary coordinates are formula terms.
formula_terms = cf_group.formula_terms
self.assertEqual(set(group.items()), set(formula_terms.items()))
# Check there are three bounds.
group = cf_group.bounds
self.assertEqual(len(group), 3)
bounds = ["height_bnds", "delta_bnds", "sigma_bnds"]
self.assertEqual(set(group.keys()), set(bounds))
for name in bounds:
self.assertEqual(group[name].cf_data, getattr(self, name))
)

def test_create_formula_terms(self, mocker):
cf_group = CFReader("dummy").cf_group
assert len(cf_group) == len(self.variables)
# Check there is a singular data variable.
group = cf_group.data_variables
assert len(group) == 1
assert list(group.keys()) == ["temp"]
assert group["temp"].cf_data is self.temp
# Check there are three coordinates.
group = cf_group.coordinates
assert len(group) == 3
coordinates = ["height", "lat", "lon"]
assert set(group.keys()) == set(coordinates)
for name in coordinates:
assert group[name].cf_data is getattr(self, name)
# Check there are three auxiliary coordinates.
group = cf_group.auxiliary_coordinates
assert len(group) == 3
aux_coordinates = ["delta", "sigma", "orography"]
assert set(group.keys()) == set(aux_coordinates)
for name in aux_coordinates:
assert group[name].cf_data is getattr(self, name)
# Check all the auxiliary coordinates are formula terms.
formula_terms = cf_group.formula_terms
assert set(group.items()) == set(formula_terms.items())
# Check there are three bounds.
group = cf_group.bounds
assert len(group) == 3
bounds = ["height_bnds", "delta_bnds", "sigma_bnds"]
assert set(group.keys()) == set(bounds)
for name in bounds:
assert group[name].cf_data == getattr(self, name)

class Test_build_cf_groups__formula_terms(tests.IrisTest):
def setUp(self):

class Test_build_cf_groups__formula_terms:
@pytest.fixture(autouse=True)
def _setup(self, mocker):
self.delta = netcdf_variable("delta", "height", np.float64, bounds="delta_bnds")
self.delta_bnds = netcdf_variable("delta_bnds", "height bnds", np.float64)
self.sigma = netcdf_variable("sigma", "height", np.float64, bounds="sigma_bnds")
Expand Down Expand Up @@ -224,122 +219,95 @@ def setUp(self):
)
# Restrict the CFReader functionality to only performing translations
# and building first level cf-groups for variables.
patcher = mock.patch("iris.fileformats.cf.CFReader._reset")
patcher.start()
self.addCleanup(patcher.stop)

def test_associate_formula_terms_with_data_variable(self):
with mock.patch(
mocker.patch("iris.fileformats.cf.CFReader._reset")
mocker.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
):
cf_group = CFReader("dummy").cf_group
self.assertEqual(len(cf_group), len(self.variables))
# Check the cf-group associated with the data variable.
temp_cf_group = cf_group["temp"].cf_group
# Check the data variable is associated with eight variables.
self.assertEqual(len(temp_cf_group), 8)
# Check there are three coordinates.
group = temp_cf_group.coordinates
self.assertEqual(len(group), 3)
coordinates = ["height", "lat", "lon"]
self.assertEqual(set(group.keys()), set(coordinates))
for name in coordinates:
self.assertIs(group[name].cf_data, getattr(self, name))
# Check the height coordinate is bounded.
group = group["height"].cf_group
self.assertEqual(len(group.bounds), 1)
self.assertIn("height_bnds", group.bounds)
self.assertIs(group["height_bnds"].cf_data, self.height_bnds)
# Check there are five auxiliary coordinates.
group = temp_cf_group.auxiliary_coordinates
self.assertEqual(len(group), 5)
aux_coordinates = ["delta", "sigma", "orography", "x", "y"]
self.assertEqual(set(group.keys()), set(aux_coordinates))
for name in aux_coordinates:
self.assertIs(group[name].cf_data, getattr(self, name))
# Check all the auxiliary coordinates are formula terms.
formula_terms = cf_group.formula_terms
self.assertTrue(set(formula_terms.items()).issubset(list(group.items())))
# Check the terms by root.
for name, term in zip(aux_coordinates, ["a", "b", "orog"]):
self.assertEqual(
formula_terms[name].cf_terms_by_root, dict(height=term)
)
# Check the bounded auxiliary coordinates.
for name, name_bnds in zip(
["delta", "sigma"], ["delta_bnds", "sigma_bnds"]
):
aux_coord_group = group[name].cf_group
self.assertEqual(len(aux_coord_group.bounds), 1)
self.assertIn(name_bnds, aux_coord_group.bounds)
self.assertIs(
aux_coord_group[name_bnds].cf_data,
getattr(self, name_bnds),
)
)

def test_associate_formula_terms_with_data_variable(self, mocker):
cf_group = CFReader("dummy").cf_group
assert len(cf_group) == len(self.variables)
# Check the cf-group associated with the data variable.
temp_cf_group = cf_group["temp"].cf_group
# Check the data variable is associated with eight variables.
assert len(temp_cf_group) == 8
# Check there are three coordinates.
group = temp_cf_group.coordinates
assert len(group) == 3
coordinates = ["height", "lat", "lon"]
assert set(group.keys()) == set(coordinates)
for name in coordinates:
assert group[name].cf_data is getattr(self, name)
# Check the height coordinate is bounded.
group = group["height"].cf_group
assert len(group.bounds) == 1
assert "height_bnds" in group.bounds
assert group["height_bnds"].cf_data is self.height_bnds
# Check there are five auxiliary coordinates.
group = temp_cf_group.auxiliary_coordinates
assert len(group) == 5
aux_coordinates = ["delta", "sigma", "orography", "x", "y"]
assert set(group.keys()) == set(aux_coordinates)
for name in aux_coordinates:
assert group[name].cf_data is getattr(self, name)
# Check all the auxiliary coordinates are formula terms.
formula_terms = cf_group.formula_terms
assert set(formula_terms.items()).issubset(list(group.items()))
# Check the terms by root.
for name, term in zip(aux_coordinates, ["a", "b", "orog"]):
assert formula_terms[name].cf_terms_by_root == dict(height=term)
# Check the bounded auxiliary coordinates.
for name, name_bnds in zip(["delta", "sigma"], ["delta_bnds", "sigma_bnds"]):
aux_coord_group = group[name].cf_group
assert len(aux_coord_group.bounds) == 1
assert name_bnds in aux_coord_group.bounds
assert aux_coord_group[name_bnds].cf_data is getattr(self, name_bnds)

def test_promote_reference(self):
with mock.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
):
cf_group = CFReader("dummy").cf_group
self.assertEqual(len(cf_group), len(self.variables))
# Check the number of data variables.
self.assertEqual(len(cf_group.data_variables), 1)
self.assertEqual(list(cf_group.data_variables.keys()), ["temp"])
# Check the number of promoted variables.
self.assertEqual(len(cf_group.promoted), 1)
self.assertEqual(list(cf_group.promoted.keys()), ["orography"])
# Check the promoted variable dependencies.
group = cf_group.promoted["orography"].cf_group.coordinates
self.assertEqual(len(group), 2)
coordinates = ("lat", "lon")
self.assertEqual(set(group.keys()), set(coordinates))
for name in coordinates:
self.assertIs(group[name].cf_data, getattr(self, name))
cf_group = CFReader("dummy").cf_group
assert len(cf_group) == len(self.variables)
# Check the number of data variables.
assert len(cf_group.data_variables) == 1
assert list(cf_group.data_variables.keys()) == ["temp"]
# Check the number of promoted variables.
assert len(cf_group.promoted) == 1
assert list(cf_group.promoted.keys()) == ["orography"]
# Check the promoted variable dependencies.
group = cf_group.promoted["orography"].cf_group.coordinates
assert len(group) == 2
coordinates = ("lat", "lon")
assert set(group.keys()) == set(coordinates)
for name in coordinates:
assert group[name].cf_data is getattr(self, name)

def test_formula_terms_ignore(self):
self.orography.dimensions = ["lat", "wibble"]
with mock.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
), mock.patch("warnings.warn") as warn:
with pytest.warns(match="Ignoring formula terms variable"):
cf_group = CFReader("dummy").cf_group
group = cf_group.promoted
self.assertEqual(list(group.keys()), ["orography"])
self.assertIs(group["orography"].cf_data, self.orography)
self.assertEqual(warn.call_count, 1)
assert list(group.keys()) == ["orography"]
assert group["orography"].cf_data is self.orography

def test_auxiliary_ignore(self):
self.x.dimensions = ["lat", "wibble"]
with mock.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
), mock.patch("warnings.warn") as warn:
with pytest.warns(match="Ignoring variable 'x'"):
cf_group = CFReader("dummy").cf_group
promoted = ["x", "orography"]
group = cf_group.promoted
self.assertEqual(set(group.keys()), set(promoted))
assert set(group.keys()) == set(promoted)
for name in promoted:
self.assertIs(group[name].cf_data, getattr(self, name))
self.assertEqual(warn.call_count, 1)
assert group[name].cf_data is getattr(self, name)

def test_promoted_auxiliary_ignore(self):
self.wibble = netcdf_variable("wibble", "lat wibble", np.float64)
self.variables["wibble"] = self.wibble
self.orography.coordinates = "wibble"
with mock.patch(
"iris.fileformats.netcdf._thread_safe_nc.DatasetWrapper",
return_value=self.dataset,
), mock.patch("warnings.warn") as warn:
with pytest.warns(match="Ignoring variable 'wibble'") as warns:
cf_group = CFReader("dummy").cf_group.promoted
promoted = ["wibble", "orography"]
self.assertEqual(set(cf_group.keys()), set(promoted))
assert set(cf_group.keys()) == set(promoted)
for name in promoted:
self.assertIs(cf_group[name].cf_data, getattr(self, name))
self.assertEqual(warn.call_count, 2)


if __name__ == "__main__":
tests.main()
assert cf_group[name].cf_data is getattr(self, name)
# we should have got 2 warnings
assert len(warns.list) == 2
Loading