Skip to content
Open
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
26 changes: 9 additions & 17 deletions lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
# See LICENSE in the root of the repository for full licensing details.
"""Unit tests for the module :mod:`iris.fileformats._nc_load_rules.actions`."""

from pathlib import Path
import shutil
import tempfile
import warnings

import pytest

import iris.fileformats._nc_load_rules.engine
from iris.fileformats.cf import CFReader
import iris.fileformats.netcdf
from iris.fileformats.netcdf.loader import _load_cube
from iris.tests import _shared_utils
from iris.tests.stock.netcdf import ncgen_from_cdl
from iris.warnings import IrisLoadWarning

Expand All @@ -35,11 +35,8 @@
class Mixin__nc_load_actions:
"""Class to make testcases for rules or actions code, and check results.

Defines standard setUpClass/tearDownClass methods, to create a temporary
Defines standard setup method, to create a temporary
directory for intermediate files.
NOTE: owing to peculiarities of unittest, these must be explicitly called
from a setUpClass/tearDownClass within the 'final' inheritor, i.e. the
actual Test_XXX class which also inherits unittest.TestCase.

Testcases are manufactured by the '_make_testcase_cdl' method.
The 'run_testcase' method takes the '_make_testcase_cdl' kwargs and makes
Expand All @@ -55,15 +52,10 @@ class Mixin__nc_load_actions:
# "global" test setting : whether to output various debug info
debug_info = False

@classmethod
def setUpClass(cls):
@pytest.fixture(autouse=True, scope="class")
def setup_mixin(self, request, tmp_path_factory):
# Create a temp directory for temp files.
cls.temp_dirpath = Path(tempfile.mkdtemp())

@classmethod
def tearDownClass(cls):
# Destroy a temp directory for temp files.
shutil.rmtree(cls.temp_dirpath)
request.cls.temp_dirpath = tmp_path_factory.mktemp("temp")

def load_cube_from_cdl(self, cdl_string, cdl_path, nc_path, mocker=None):
"""Load the 'phenom' data variable in a CDL testcase, as a cube.
Expand Down Expand Up @@ -135,9 +127,9 @@ def run_testcase(self, warning_regex=None, **testcase_kwargs):
print("------\n")

if warning_regex is None:
context = self.assertNoWarningsRegexp()
context = _shared_utils.assert_no_warnings_regexp()
else:
context = self.assertWarnsRegex(IrisLoadWarning, warning_regex)
context = pytest.warns(IrisLoadWarning, match=warning_regex)
with context:
cube = self.load_cube_from_cdl(cdl_string, cdl_path, nc_path)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@

"""

import iris.coord_systems

import iris.tests as tests # isort: skip
import re

import pytest

import iris.coord_systems
import iris.coord_systems as ics
import iris.fileformats._nc_load_rules.helpers as hh
from iris.loading import LOAD_PROBLEMS
Expand Down Expand Up @@ -267,8 +266,8 @@ def check_result(

Various options control the expected things which are tested.
"""
self.assertEqual(cube.standard_name, "air_temperature")
self.assertEqual(cube.var_name, "phenom")
assert cube.standard_name == "air_temperature"
assert cube.var_name == "phenom"

x_coords = cube.coords(dimensions=(1,))
y_coords = cube.coords(dimensions=(0,))
Expand All @@ -283,40 +282,40 @@ def check_result(
else:
expected_dim_coords += x_coords

self.assertEqual(set(expected_dim_coords), set(cube.coords(dim_coords=True)))
assert set(expected_dim_coords) == set(cube.coords(dim_coords=True))
if cube_no_xycoords:
self.assertEqual(expected_dim_coords, [])
assert expected_dim_coords == []
x_coord = None
y_coord = None
else:
self.assertEqual(len(x_coords), 1)
assert len(x_coords) == 1
(x_coord,) = x_coords
self.assertEqual(len(y_coords), 1)
assert len(y_coords) == 1
(y_coord,) = y_coords

self.assertEqual(set(expected_aux_coords), set(cube.coords(dim_coords=False)))
assert set(expected_aux_coords) == set(cube.coords(dim_coords=False))

if x_coord:
if xco_stdname is None:
# no check
pass
elif xco_stdname is True:
self.assertIsNotNone(x_coord.standard_name)
assert x_coord.standard_name is not None
elif xco_stdname is False:
self.assertIsNone(x_coord.standard_name)
assert x_coord.standard_name is None
else:
self.assertEqual(x_coord.standard_name, xco_stdname)
assert x_coord.standard_name == xco_stdname

if y_coord:
if yco_stdname is None:
# no check
pass
if yco_stdname is True:
self.assertIsNotNone(y_coord.standard_name)
assert y_coord.standard_name is not None
elif yco_stdname is False:
self.assertIsNone(y_coord.standard_name)
assert y_coord.standard_name is None
else:
self.assertEqual(y_coord.standard_name, yco_stdname)
assert y_coord.standard_name == yco_stdname

cube_cs = cube.coord_system()
if cube_no_xycoords:
Expand All @@ -326,36 +325,29 @@ def check_result(
yco_cs = y_coord.coord_system
xco_cs = x_coord.coord_system
if cube_no_cs:
self.assertIsNone(cube_cs)
self.assertIsNone(yco_cs)
self.assertIsNone(xco_cs)
assert cube_cs is None
assert yco_cs is None
assert xco_cs is None
else:
self.assertIsNotNone(cube_cs)
assert cube_cs is not None
if cube_cstype is not None:
self.assertIsInstance(cube_cs, cube_cstype)
assert isinstance(cube_cs, cube_cstype)
if xco_no_cs:
self.assertIsNone(xco_cs)
assert xco_cs is None
else:
self.assertEqual(xco_cs, cube_cs)
assert xco_cs == cube_cs
if yco_no_cs:
self.assertIsNone(yco_cs)
assert yco_cs is None
else:
self.assertEqual(yco_cs, cube_cs)
assert yco_cs == cube_cs

if load_problems_regex is not None:
load_problem = LOAD_PROBLEMS.problems[-1]
self.assertRegex(str(load_problem.stack_trace), load_problems_regex)
assert re.search(load_problems_regex, str(load_problem.stack_trace))


class Test__grid_mapping(Mixin__grid_mapping, tests.IrisTest):
class Test__grid_mapping(Mixin__grid_mapping):
# Various testcases for translation of grid-mappings
@classmethod
def setUpClass(cls):
super().setUpClass()

@classmethod
def tearDownClass(cls):
super().tearDownClass()

def test_basic_latlon(self):
# A basic reference example with a lat-long grid.
Expand Down Expand Up @@ -801,15 +793,8 @@ def test_extended_mapping_basic_latlon_missing_coords(self):
self.check_result(result, xco_no_cs=True)


class Test__aux_latlons(Mixin__grid_mapping, tests.IrisTest):
class Test__aux_latlons(Mixin__grid_mapping):
# Testcases for translating auxiliary latitude+longitude variables
@classmethod
def setUpClass(cls):
super().setUpClass()

@classmethod
def tearDownClass(cls):
super().tearDownClass()

def test_aux_lon(self):
# Change the name of xdim, and put xco on the coords list.
Expand Down Expand Up @@ -933,15 +918,7 @@ def test_extended_grid_mapping_aux_lat_and_lon(self):
self.check_result(result, xco_is_aux=True, yco_is_aux=True, cube_no_cs=False)


class Test__nondimcoords(Mixin__grid_mapping, tests.IrisTest):
@classmethod
def setUpClass(cls):
super().setUpClass()

@classmethod
def tearDownClass(cls):
super().tearDownClass()

class Test__nondimcoords(Mixin__grid_mapping):
def test_nondim_lats(self):
# Fix a coord's values so it cannot be a dim-coord.
#
Expand Down Expand Up @@ -1235,7 +1212,3 @@ def test_one_coord_system_simple(self, osgb_cs, latlon_cs, mocker, tmp_path):

# Loading multiple coord systems or using extended grid mapping implies ordered axes:
assert cube.extended_grid_mapping is False


if __name__ == "__main__":
tests.main()
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,11 @@

"""

import iris.tests as tests # isort: skip

import iris.fileformats._nc_load_rules.helpers as hh
from iris.tests.unit.fileformats.nc_load_rules.actions import Mixin__nc_load_actions


class Test__formulae_tests(Mixin__nc_load_actions, tests.IrisTest):
@classmethod
def setUpClass(cls):
super().setUpClass()

@classmethod
def tearDownClass(cls):
super().tearDownClass()

class Test__formulae_tests(Mixin__nc_load_actions):
def _make_testcase_cdl(
self, formula_root_name=None, term_names=None, extra_formula_type=None
):
Expand Down Expand Up @@ -111,7 +101,7 @@ def check_result(self, cube, factory_type="_auto", formula_terms="_auto"):
# replace with our 'default', which is hybrid-height.
# N.B. 'None' is different: it means expect *no* factory.
factory_type = "atmosphere_hybrid_height_coordinate"
self.assertEqual(cube._formula_type_name, factory_type)
assert cube._formula_type_name == factory_type

if formula_terms == "_auto":
# Set default terms-expected, according to the expected factory
Expand All @@ -130,12 +120,12 @@ def check_result(self, cube, factory_type="_auto", formula_terms="_auto"):

# N.B. the terms dictionary can be missing, if there were none
actual_terms = cube._formula_terms_byname or {}
self.assertEqual(sorted(formula_terms), sorted(actual_terms.keys()))
assert sorted(formula_terms) == sorted(actual_terms.keys())

# Check that there is an aux-coord of the expected name for each term
for var_name in actual_terms.values():
coords = cube.coords(var_name=var_name, dim_coords=False)
self.assertEqual(len(coords), 1)
assert len(coords) == 1

#
# Actual testcase routines
Expand Down Expand Up @@ -272,7 +262,3 @@ def test_ocean_s_coordinate_g2(self):
term_names = hh.CF_COORD_VERTICAL[hybrid_type]
result = self.run_testcase(formula_root_name=hybrid_type, term_names=term_names)
self.check_result(result, factory_type=hybrid_type, formula_terms=term_names)


if __name__ == "__main__":
tests.main()
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

"""

import re
from typing import Literal

import iris.tests as tests # isort: skip
import pytest

from iris.common import LimitedAttributeDict
from iris.coord_systems import GeogCS, RotatedGeogCS
Expand All @@ -27,8 +28,8 @@ class Mixin_latlon_dimcoords(Mixin__nc_load_actions):
# Set by inheritor classes, which are actual TestCases.
lat_1_or_lon_0: Literal[0, 1]

def setUp(self):
super().setUp()
@pytest.fixture(autouse=True)
def _setup(self):
# Generate some useful settings : just to generalise operation over
# both latitude and longitude.
islat = self.lat_1_or_lon_0
Expand Down Expand Up @@ -133,9 +134,9 @@ def check_result(
# affect the results here, in some cases.
coords = cube.coords()
# There should be one and only one coord.
self.assertEqual(1, len(coords))
assert 1 == len(coords)
# It should also be a dim-coord
self.assertEqual(1, len(cube.coords(dim_coords=True)))
assert 1 == len(cube.coords(dim_coords=True))
(coord,) = coords
if self.debug_info:
print()
Expand All @@ -146,24 +147,24 @@ def check_result(
getattr(coord, name)
for name in ("standard_name", "long_name", "units", "coord_system")
]
self.assertEqual(standard_name, coord_stdname, context_message)
self.assertEqual(long_name, coord_longname, context_message)
self.assertEqual(units, coord_units, context_message)
assert standard_name == coord_stdname, context_message
assert long_name == coord_longname, context_message
assert units == coord_units, context_message
assert crs in (None, "latlon", "rotated")
if crs is None:
self.assertEqual(None, coord_crs, context_message)
assert None is coord_crs, context_message
elif crs == "latlon":
self.assertIsInstance(coord_crs, GeogCS, context_message)
assert isinstance(coord_crs, GeogCS), context_message
elif crs == "rotated":
self.assertIsInstance(coord_crs, RotatedGeogCS, context_message)
assert isinstance(coord_crs, RotatedGeogCS), context_message

def check_load_problem(self, setup_kwargs, expected_msg):
# Check that the expected load problem is stored.
_ = self.run_testcase(**setup_kwargs)
load_problem = LOAD_PROBLEMS.problems[-1]
attributes = load_problem.loaded.attributes[LimitedAttributeDict.IRIS_RAW]
self.assertEqual(attributes["standard_name"], setup_kwargs["standard_name"])
self.assertRegex("".join(load_problem.stack_trace.format()), expected_msg)
assert attributes["standard_name"] == setup_kwargs["standard_name"]
assert re.search(expected_msg, "".join(load_problem.stack_trace.format()))

#
# Testcase routines
Expand Down Expand Up @@ -330,35 +331,9 @@ def test_fail_projected(self):
)


class Test__longitude_coords(Mixin_latlon_dimcoords, tests.IrisTest):
class Test__longitude_coords(Mixin_latlon_dimcoords):
lat_1_or_lon_0 = 0

@classmethod
def setUpClass(cls):
super().setUpClass()

@classmethod
def tearDownClass(cls):
super().tearDownClass()

def setUp(self):
super().setUp()


class Test__latitude_coords(Mixin_latlon_dimcoords, tests.IrisTest):
class Test__latitude_coords(Mixin_latlon_dimcoords):
lat_1_or_lon_0 = 1

@classmethod
def setUpClass(cls):
super().setUpClass()

@classmethod
def tearDownClass(cls):
super().tearDownClass()

def setUp(self):
super().setUp()


if __name__ == "__main__":
tests.main()
Loading
Loading