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
60 changes: 0 additions & 60 deletions lib/iris/tests/unit/fileformats/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,3 @@
# 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 :mod:`iris.fileformats` package."""

import iris.tests as tests # isort:skip


class TestField(tests.IrisTest):
def _test_for_coord(
self, field, convert, coord_predicate, expected_points, expected_bounds
):
(
factories,
references,
standard_name,
long_name,
units,
attributes,
cell_methods,
dim_coords_and_dims,
aux_coords_and_dims,
) = convert(field)

# Check for one and only one matching coordinate.
coords_and_dims = dim_coords_and_dims + aux_coords_and_dims
matching_coords = [
coord for coord, _ in coords_and_dims if coord_predicate(coord)
]
self.assertEqual(len(matching_coords), 1, str(matching_coords))
coord = matching_coords[0]

# Check points and bounds.
if expected_points is not None:
self.assertArrayEqual(coord.points, expected_points)

if expected_bounds is None:
self.assertIsNone(coord.bounds)
else:
self.assertArrayEqual(coord.bounds, expected_bounds)

def assertCoordsAndDimsListsMatch(
self, coords_and_dims_got, coords_and_dims_expected
):
"""Check that coords_and_dims lists are equivalent.

The arguments are lists of pairs of (coordinate, dimensions).
The elements are compared one-to-one, by coordinate name (so the order
of the lists is _not_ significant).
It also checks that the coordinate types (DimCoord/AuxCoord) match.

"""

def sorted_by_coordname(list):
return sorted(list, key=lambda item: item[0].name())

coords_and_dims_got = sorted_by_coordname(coords_and_dims_got)
coords_and_dims_expected = sorted_by_coordname(coords_and_dims_expected)
self.assertEqual(coords_and_dims_got, coords_and_dims_expected)
# Also check coordinate type equivalences (as Coord.__eq__ does not).
self.assertEqual(
[type(coord) for coord, dims in coords_and_dims_got],
[type(coord) for coord, dims in coords_and_dims_expected],
)
23 changes: 23 additions & 0 deletions lib/iris/tests/unit/fileformats/pp_load_rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,26 @@
# 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 :mod:`iris.fileformats.pp_load_rules` module."""


# a general utility function for PP field tests
def assert_coords_and_dims_lists_match(coords_and_dims_got, coords_and_dims_expected):
"""Check that coords_and_dims lists are equivalent.

The arguments are lists of pairs of (coordinate, dimensions).
The elements are compared one-to-one, by coordinate name (so the order
of the lists is _not_ significant).
It also checks that the coordinate types (DimCoord/AuxCoord) match.

"""

def sorted_by_coordname(list):
return sorted(list, key=lambda item: item[0].name())

coords_and_dims_got = sorted_by_coordname(coords_and_dims_got)
coords_and_dims_expected = sorted_by_coordname(coords_and_dims_expected)
assert coords_and_dims_got == coords_and_dims_expected
# Also check coordinate type equivalences (as Coord.__eq__ does not).
assert [type(coord) for coord, dims in coords_and_dims_got] == [
type(coord) for coord, dims in coords_and_dims_expected
]
109 changes: 50 additions & 59 deletions lib/iris/tests/unit/fileformats/pp_load_rules/test__all_other_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@
# 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.pp_load_rules._all_other_rules` function."""

# 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

from cf_units import CALENDAR_360_DAY, Unit
Expand All @@ -17,7 +12,7 @@
from iris.coords import AuxCoord, CellMethod, DimCoord
from iris.fileformats.pp import SplittableInt
from iris.fileformats.pp_load_rules import _all_other_rules
from iris.tests.unit.fileformats import TestField
from iris.tests.unit.fileformats.pp_load_rules import assert_coords_and_dims_lists_match

# iris.fileformats.pp_load_rules._all_other_rules() returns a tuple of
# of various metadata. This constant is the index into this
Expand All @@ -27,88 +22,88 @@
AUX_COORDS_INDEX = 7


class TestCellMethods(tests.IrisTest):
def test_time_mean(self):
class TestCellMethods:
def test_time_mean(self, mocker):
# lbproc = 128 -> mean
# lbtim.ib = 2 -> simple t1 to t2 interval.
field = mock.MagicMock(lbproc=128, lbtim=mock.Mock(ia=0, ib=2, ic=3))
field = mocker.MagicMock(lbproc=128, lbtim=mocker.Mock(ia=0, ib=2, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("mean", "time")]
self.assertEqual(res, expected)
assert res == expected

def test_hourly_mean(self):
def test_hourly_mean(self, mocker):
# lbtim.ia = 1 -> hourly
field = mock.MagicMock(lbproc=128, lbtim=mock.Mock(ia=1, ib=2, ic=3))
field = mocker.MagicMock(lbproc=128, lbtim=mocker.Mock(ia=1, ib=2, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("mean", "time", "1 hour")]
self.assertEqual(res, expected)
assert res == expected

def test_daily_mean(self):
def test_daily_mean(self, mocker):
# lbtim.ia = 24 -> daily
field = mock.MagicMock(lbproc=128, lbtim=mock.Mock(ia=24, ib=2, ic=3))
field = mocker.MagicMock(lbproc=128, lbtim=mocker.Mock(ia=24, ib=2, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("mean", "time", "24 hour")]
self.assertEqual(res, expected)
assert res == expected

def test_custom_max(self):
field = mock.MagicMock(lbproc=8192, lbtim=mock.Mock(ia=47, ib=2, ic=3))
def test_custom_max(self, mocker):
field = mocker.MagicMock(lbproc=8192, lbtim=mocker.Mock(ia=47, ib=2, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("maximum", "time", "47 hour")]
self.assertEqual(res, expected)
assert res == expected

def test_daily_min(self):
def test_daily_min(self, mocker):
# lbproc = 4096 -> min
field = mock.MagicMock(lbproc=4096, lbtim=mock.Mock(ia=24, ib=2, ic=3))
field = mocker.MagicMock(lbproc=4096, lbtim=mocker.Mock(ia=24, ib=2, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("minimum", "time", "24 hour")]
self.assertEqual(res, expected)
assert res == expected

def test_time_mean_over_multiple_years(self):
def test_time_mean_over_multiple_years(self, mocker):
# lbtim.ib = 3 -> interval within a year, over multiple years.
field = mock.MagicMock(lbproc=128, lbtim=mock.Mock(ia=0, ib=3, ic=3))
field = mocker.MagicMock(lbproc=128, lbtim=mocker.Mock(ia=0, ib=3, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [
CellMethod("mean within years", "time"),
CellMethod("mean over years", "time"),
]
self.assertEqual(res, expected)
assert res == expected

def test_hourly_mean_over_multiple_years(self):
field = mock.MagicMock(lbproc=128, lbtim=mock.Mock(ia=1, ib=3, ic=3))
def test_hourly_mean_over_multiple_years(self, mocker):
field = mocker.MagicMock(lbproc=128, lbtim=mocker.Mock(ia=1, ib=3, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [
CellMethod("mean within years", "time", "1 hour"),
CellMethod("mean over years", "time"),
]
self.assertEqual(res, expected)
assert res == expected

def test_climatology_max(self):
field = mock.MagicMock(lbproc=8192, lbtim=mock.Mock(ia=24, ib=3, ic=3))
def test_climatology_max(self, mocker):
field = mocker.MagicMock(lbproc=8192, lbtim=mocker.Mock(ia=24, ib=3, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("maximum", "time")]
self.assertEqual(res, expected)
assert res == expected

def test_climatology_min(self):
field = mock.MagicMock(lbproc=4096, lbtim=mock.Mock(ia=24, ib=3, ic=3))
def test_climatology_min(self, mocker):
field = mocker.MagicMock(lbproc=4096, lbtim=mocker.Mock(ia=24, ib=3, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("minimum", "time")]
self.assertEqual(res, expected)
assert res == expected

def test_other_lbtim_ib(self):
def test_other_lbtim_ib(self, mocker):
# lbtim.ib = 5 -> non-specific aggregation
field = mock.MagicMock(lbproc=4096, lbtim=mock.Mock(ia=24, ib=5, ic=3))
field = mocker.MagicMock(lbproc=4096, lbtim=mocker.Mock(ia=24, ib=5, ic=3))
res = _all_other_rules(field)[CELL_METHODS_INDEX]
expected = [CellMethod("minimum", "time")]
self.assertEqual(res, expected)
assert res == expected

def test_multiple_unordered_lbprocs(self):
field = mock.MagicMock(
def test_multiple_unordered_lbprocs(self, mocker):
field = mocker.MagicMock(
lbproc=192,
bzx=0,
bdx=1,
lbnpt=3,
lbrow=3,
lbtim=mock.Mock(ia=24, ib=5, ic=3),
lbtim=mocker.Mock(ia=24, ib=5, ic=3),
lbcode=SplittableInt(1),
x_bounds=None,
_x_coord_name=lambda: "longitude",
Expand All @@ -119,16 +114,16 @@ def test_multiple_unordered_lbprocs(self):
CellMethod("mean", "time"),
CellMethod("mean", "longitude"),
]
self.assertEqual(res, expected)
assert res == expected

def test_multiple_unordered_rotated_lbprocs(self):
field = mock.MagicMock(
def test_multiple_unordered_rotated_lbprocs(self, mocker):
field = mocker.MagicMock(
lbproc=192,
bzx=0,
bdx=1,
lbnpt=3,
lbrow=3,
lbtim=mock.Mock(ia=24, ib=5, ic=3),
lbtim=mocker.Mock(ia=24, ib=5, ic=3),
lbcode=SplittableInt(101),
x_bounds=None,
_x_coord_name=lambda: "grid_longitude",
Expand All @@ -139,23 +134,23 @@ def test_multiple_unordered_rotated_lbprocs(self):
CellMethod("mean", "time"),
CellMethod("mean", "grid_longitude"),
]
self.assertEqual(res, expected)
assert res == expected


class TestCrossSectionalTime(TestField):
def test_lbcode3x23(self):
class TestCrossSectionalTime:
def test_lbcode3x23(self, mocker):
time_bounds = np.array(
[[0.875, 1.125], [1.125, 1.375], [1.375, 1.625], [1.625, 1.875]]
)
field = mock.MagicMock(
field = mocker.MagicMock(
lbproc=0,
bzx=0,
bdx=0,
lbnpt=3,
lbrow=4,
t1=nc_datetime(2000, 1, 2, hour=0, minute=0, second=0),
t2=nc_datetime(2000, 1, 3, hour=0, minute=0, second=0),
lbtim=mock.Mock(ia=1, ib=2, ic=2),
lbtim=mocker.Mock(ia=1, ib=2, ic=2),
lbcode=SplittableInt(31323, {"iy": slice(0, 2), "ix": slice(2, 4)}),
x_bounds=None,
y_bounds=time_bounds,
Expand Down Expand Up @@ -201,10 +196,10 @@ def test_lbcode3x23(self):
0,
)
]
self.assertCoordsAndDimsListsMatch(res, expected)
assert_coords_and_dims_lists_match(res, expected)


class TestLBTIMx2x_ZeroYears(TestField):
class TestLBTIMx2x_ZeroYears:
_spec = [
"lbtim",
"lbcode",
Expand Down Expand Up @@ -272,29 +267,25 @@ def test_month_coord(self):
None,
),
]
self.assertCoordsAndDimsListsMatch(res, expected)
assert_coords_and_dims_lists_match(res, expected)

def test_diff_month(self):
field = self._make_field(lbmon=3, lbmond=4)
field.mock_add_spec(self._spec)
res = _all_other_rules(field)[AUX_COORDS_INDEX]

self.assertCoordsAndDimsListsMatch(res, [])
assert_coords_and_dims_lists_match(res, [])

def test_nonzero_year(self):
field = self._make_field(lbyr=1)
field.mock_add_spec(self._spec)
res = _all_other_rules(field)[AUX_COORDS_INDEX]

self.assertCoordsAndDimsListsMatch(res, [])
assert_coords_and_dims_lists_match(res, [])

def test_nonzero_yeard(self):
field = self._make_field(lbyrd=1)
field.mock_add_spec(self._spec)
res = _all_other_rules(field)[AUX_COORDS_INDEX]

self.assertCoordsAndDimsListsMatch(res, [])


if __name__ == "__main__":
tests.main()
assert_coords_and_dims_lists_match(res, [])
Loading