diff --git a/lib/iris/tests/_shared_utils.py b/lib/iris/tests/_shared_utils.py index 592a642ec7..f7a4cb3420 100644 --- a/lib/iris/tests/_shared_utils.py +++ b/lib/iris/tests/_shared_utils.py @@ -311,7 +311,7 @@ def assert_CML_approx_data( fname[-1] = fname[-1][:-4] fname[-1] += ".data.%d.json" % i assert_data_almost_equal(cube.data, fname, **kwargs) - assert_CML(cubes, reference_filename, checksum=False) + assert_CML(request, cubes, reference_filename, checksum=False) def assert_CDL( @@ -459,8 +459,8 @@ def assert_data_almost_equal(data, reference_filename, **kwargs): kwargs.setdefault("err_msg", "Reference file %s" % reference_path) with open(reference_path, "r") as reference_file: stats = json.load(reference_file) - assert stats.get("shape", []), list(data.shape) - assert stats.get("masked", False), ma.is_masked(data) + assert stats.get("shape", []) == list(data.shape) + assert stats.get("masked", False) == ma.is_masked(data) nstats = np.array( ( stats.get("mean", 0.0), diff --git a/lib/iris/tests/unit/cube/test_Cube.py b/lib/iris/tests/unit/cube/test_Cube.py index ec94e346b2..71ef401651 100644 --- a/lib/iris/tests/unit/cube/test_Cube.py +++ b/lib/iris/tests/unit/cube/test_Cube.py @@ -35,43 +35,44 @@ CoordinateNotFoundError, UnitConversionError, ) +from iris.tests import _shared_utils import iris.tests.stock as stock from iris.tests.stock.mesh import sample_mesh, sample_mesh_cube, sample_meshcoord from iris.warnings import IrisUserWarning, IrisVagueMetadataWarning -class Test___init___data(tests.IrisTest): +class Test___init___data: def test_ndarray(self): # np.ndarray should be allowed through data = np.arange(12).reshape(3, 4) cube = Cube(data) - self.assertEqual(type(cube.data), np.ndarray) - self.assertArrayEqual(cube.data, data) + assert type(cube.data) is np.ndarray + _shared_utils.assert_array_equal(cube.data, data) def test_masked(self): # ma.MaskedArray should be allowed through data = ma.masked_greater(np.arange(12).reshape(3, 4), 1) cube = Cube(data) - self.assertEqual(type(cube.data), ma.MaskedArray) - self.assertMaskedArrayEqual(cube.data, data) + assert type(cube.data) is ma.MaskedArray + _shared_utils.assert_masked_array_equal(cube.data, data) def test_masked_no_mask(self): # ma.MaskedArray should be allowed through even if it has no mask data = ma.masked_array(np.arange(12).reshape(3, 4), False) cube = Cube(data) - self.assertEqual(type(cube.data), ma.MaskedArray) - self.assertMaskedArrayEqual(cube.data, data) + assert type(cube.data) is ma.MaskedArray + _shared_utils.assert_masked_array_equal(cube.data, data) def test_matrix(self): # Subclasses of np.ndarray should be coerced back to np.ndarray. # (Except for np.ma.MaskedArray.) data = np.matrix([[1, 2, 3], [4, 5, 6]]) cube = Cube(data) - self.assertEqual(type(cube.data), np.ndarray) - self.assertArrayEqual(cube.data, data) + assert type(cube.data) is np.ndarray + _shared_utils.assert_array_equal(cube.data, data) -class Test_data_dtype_fillvalue(tests.IrisTest): +class Test_data_dtype_fillvalue: def _sample_data(self, dtype=("f4"), masked=False, fill_value=None, lazy=False): data = np.arange(6).reshape((2, 3)) dtype = np.dtype(dtype) @@ -94,74 +95,74 @@ def _sample_cube(self, dtype=("f4"), masked=False, fill_value=None, lazy=False): def test_realdata_change(self): # Check re-assigning real data. cube = self._sample_cube() - self.assertEqual(cube.dtype, np.float32) + assert cube.dtype == np.float32 new_dtype = np.dtype("i4") new_data = self._sample_data(dtype=new_dtype) cube.data = new_data - self.assertIs(cube.core_data(), new_data) - self.assertEqual(cube.dtype, new_dtype) + assert cube.core_data() is new_data + assert cube.dtype == new_dtype def test_realmaskdata_change(self): # Check re-assigning real masked data. cube = self._sample_cube(masked=True, fill_value=1234) - self.assertEqual(cube.dtype, np.float32) + assert cube.dtype == np.float32 new_dtype = np.dtype("i4") new_fill_value = 4321 new_data = self._sample_data( masked=True, fill_value=new_fill_value, dtype=new_dtype ) cube.data = new_data - self.assertIs(cube.core_data(), new_data) - self.assertEqual(cube.dtype, new_dtype) - self.assertEqual(cube.data.fill_value, new_fill_value) + assert cube.core_data() is new_data + assert cube.dtype == new_dtype + assert cube.data.fill_value == new_fill_value def test_lazydata_change(self): # Check re-assigning lazy data. cube = self._sample_cube(lazy=True) - self.assertEqual(cube.core_data().dtype, np.float32) + assert cube.core_data().dtype == np.float32 new_dtype = np.dtype("f8") new_data = self._sample_data(new_dtype, lazy=True) cube.data = new_data - self.assertIs(cube.core_data(), new_data) - self.assertEqual(cube.dtype, new_dtype) + assert cube.core_data() is new_data + assert cube.dtype == new_dtype def test_lazymaskdata_change(self): # Check re-assigning lazy masked data. cube = self._sample_cube(masked=True, fill_value=1234, lazy=True) - self.assertEqual(cube.core_data().dtype, np.float32) + assert cube.core_data().dtype == np.float32 new_dtype = np.dtype("f8") new_fill_value = 4321 new_data = self._sample_data( dtype=new_dtype, masked=True, fill_value=new_fill_value, lazy=True ) cube.data = new_data - self.assertIs(cube.core_data(), new_data) - self.assertEqual(cube.dtype, new_dtype) - self.assertEqual(cube.data.fill_value, new_fill_value) + assert cube.core_data() is new_data + assert cube.dtype == new_dtype + assert cube.data.fill_value == new_fill_value def test_lazydata_realise(self): # Check touching lazy data. cube = self._sample_cube(lazy=True) data = cube.data - self.assertIs(cube.core_data(), data) - self.assertEqual(cube.dtype, np.float32) + assert cube.core_data() is data + assert cube.dtype == np.float32 def test_lazymaskdata_realise(self): # Check touching masked lazy data. fill_value = 27.3 cube = self._sample_cube(masked=True, fill_value=fill_value, lazy=True) data = cube.data - self.assertIs(cube.core_data(), data) - self.assertEqual(cube.dtype, np.float32) - self.assertEqual(data.fill_value, np.float32(fill_value)) + assert cube.core_data() is data + assert cube.dtype == np.float32 + assert data.fill_value == np.float32(fill_value) def test_realmaskedconstantint_realise(self): masked_data = ma.masked_array([666], mask=True) masked_constant = masked_data[0] cube = Cube(masked_constant) data = cube.data - self.assertTrue(ma.isMaskedArray(data)) - self.assertNotIsInstance(data, ma.core.MaskedConstant) + assert ma.isMaskedArray(data) + assert not isinstance(data, ma.core.MaskedConstant) def test_lazymaskedconstantint_realise(self): dtype = np.dtype("i2") @@ -170,8 +171,8 @@ def test_lazymaskedconstantint_realise(self): masked_constant_lazy = as_lazy_data(masked_constant) cube = Cube(masked_constant_lazy) data = cube.data - self.assertTrue(ma.isMaskedArray(data)) - self.assertNotIsInstance(data, ma.core.MaskedConstant) + assert ma.isMaskedArray(data) + assert not isinstance(data, ma.core.MaskedConstant) def test_lazydata___getitem__dtype(self): fill_value = 1234 @@ -185,24 +186,24 @@ def test_lazydata___getitem__dtype(self): lazy_masked_array = as_lazy_data(masked_array) cube = Cube(lazy_masked_array) subcube = cube[3:] - self.assertEqual(subcube.dtype, dtype) - self.assertEqual(subcube.data.fill_value, fill_value) + assert subcube.dtype == dtype + assert subcube.data.fill_value == fill_value -class Test_extract(tests.IrisTest): +class Test_extract: def test_scalar_cube_exists(self): # Ensure that extract is able to extract a scalar cube. constraint = iris.Constraint(name="a1") cube = Cube(1, long_name="a1") res = cube.extract(constraint) - self.assertIs(res, cube) + assert res is cube def test_scalar_cube_noexists(self): # Ensure that extract does not return a non-matching scalar cube. constraint = iris.Constraint(name="a2") cube = Cube(1, long_name="a1") res = cube.extract(constraint) - self.assertIs(res, None) + assert res is None def test_scalar_cube_coord_match(self): # Ensure that extract is able to extract a scalar cube according to @@ -212,7 +213,7 @@ def test_scalar_cube_coord_match(self): coord = iris.coords.AuxCoord(0, long_name="scalar_coord") cube.add_aux_coord(coord, None) res = cube.extract(constraint) - self.assertIs(res, cube) + assert res is cube def test_scalar_cube_coord_nomatch(self): # Ensure that extract is not extracting a scalar cube with scalar @@ -222,30 +223,30 @@ def test_scalar_cube_coord_nomatch(self): coord = iris.coords.AuxCoord(0, long_name="scalar_coord") cube.add_aux_coord(coord, None) res = cube.extract(constraint) - self.assertIs(res, None) + assert res is None def test_1d_cube_exists(self): # Ensure that extract is able to extract from a 1d cube. constraint = iris.Constraint(name="a1") cube = Cube([1], long_name="a1") res = cube.extract(constraint) - self.assertIs(res, cube) + assert res is cube def test_1d_cube_noexists(self): # Ensure that extract does not return a non-matching 1d cube. constraint = iris.Constraint(name="a2") cube = Cube([1], long_name="a1") res = cube.extract(constraint) - self.assertIs(res, None) + assert res is None -class Test_xml(tests.IrisTest): - def test_checksum_ignores_masked_values(self): +class Test_xml: + def test_checksum_ignores_masked_values(self, request): # Mask out an single element. data = ma.arange(12).reshape(3, 4) data[1, 2] = ma.masked cube = Cube(data) - self.assertCML(cube) + _shared_utils.assert_CML(request, cube) # If we change the underlying value before masking it, the # checksum should be unaffected. @@ -253,21 +254,21 @@ def test_checksum_ignores_masked_values(self): data[1, 2] = 42 data[1, 2] = ma.masked cube = Cube(data) - self.assertCML(cube) + _shared_utils.assert_CML(request, cube) def test_byteorder_default(self): cube = Cube(np.arange(3)) - self.assertIn("byteorder", cube.xml()) + assert "byteorder" in cube.xml() def test_byteorder_false(self): cube = Cube(np.arange(3)) - self.assertNotIn("byteorder", cube.xml(byteorder=False)) + assert "byteorder" not in cube.xml(byteorder=False) def test_byteorder_true(self): cube = Cube(np.arange(3)) - self.assertIn("byteorder", cube.xml(byteorder=True)) + assert "byteorder" in cube.xml(byteorder=True) - def test_cell_measures(self): + def test_cell_measures(self, request): cube = stock.simple_3d_w_multidim_coords() cm_a = iris.coords.CellMeasure( np.zeros(cube.shape[-2:]), measure="area", units="1" @@ -280,19 +281,20 @@ def test_cell_measures(self): units="m3", ) cube.add_cell_measure(cm_v, (0, 1, 2)) - self.assertCML(cube) + _shared_utils.assert_CML(request, cube) - def test_ancils(self): + def test_ancils(self, request): cube = stock.simple_2d_w_multidim_coords() av = iris.coords.AncillaryVariable( np.zeros(cube.shape), long_name="xy", var_name="vxy", units="1" ) cube.add_ancillary_variable(av, (0, 1)) - self.assertCML(cube) + _shared_utils.assert_CML(request, cube) -class Test_collapsed__lazy(tests.IrisTest): - def setUp(self): +class Test_collapsed__lazy: + @pytest.fixture(autouse=True) + def _setup(self): self.data = np.arange(6.0).reshape((2, 3)) self.lazydata = as_lazy_data(self.data) cube = Cube(self.lazydata) @@ -304,50 +306,51 @@ def setUp(self): def test_dim0_lazy(self): cube_collapsed = self.cube.collapsed("y", MEAN) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, [1.5, 2.5, 3.5]) - self.assertFalse(cube_collapsed.has_lazy_data()) + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal(cube_collapsed.data, [1.5, 2.5, 3.5]) + assert not cube_collapsed.has_lazy_data() def test_dim0_lazy_weights_none(self): cube_collapsed = self.cube.collapsed("y", MEAN, weights=None) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, [1.5, 2.5, 3.5]) - self.assertFalse(cube_collapsed.has_lazy_data()) + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal(cube_collapsed.data, [1.5, 2.5, 3.5]) + assert not cube_collapsed.has_lazy_data() def test_dim1_lazy(self): cube_collapsed = self.cube.collapsed("x", MEAN) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, [1.0, 4.0]) - self.assertFalse(cube_collapsed.has_lazy_data()) + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal(cube_collapsed.data, [1.0, 4.0]) + assert not cube_collapsed.has_lazy_data() def test_dim1_lazy_weights_none(self): cube_collapsed = self.cube.collapsed("x", MEAN, weights=None) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, [1.0, 4.0]) - self.assertFalse(cube_collapsed.has_lazy_data()) + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal(cube_collapsed.data, [1.0, 4.0]) + assert not cube_collapsed.has_lazy_data() def test_multidims(self): # Check that MEAN works with multiple dims. cube_collapsed = self.cube.collapsed(("x", "y"), MEAN) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAllClose(cube_collapsed.data, 2.5) + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_all_close(cube_collapsed.data, 2.5) def test_multidims_weights_none(self): # Check that MEAN works with multiple dims. cube_collapsed = self.cube.collapsed(("x", "y"), MEAN, weights=None) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAllClose(cube_collapsed.data, 2.5) + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_all_close(cube_collapsed.data, 2.5) def test_non_lazy_aggregator(self): # An aggregator which doesn't have a lazy function should still work. dummy_agg = Aggregator("custom_op", lambda x, axis=None: np.mean(x, axis=axis)) result = self.cube.collapsed("x", dummy_agg) - self.assertFalse(result.has_lazy_data()) - self.assertArrayEqual(result.data, np.mean(self.data, axis=1)) + assert not result.has_lazy_data() + _shared_utils.assert_array_equal(result.data, np.mean(self.data, axis=1)) -class Test_collapsed__multidim_weighted_with_arr(tests.IrisTest): - def setUp(self): +class Test_collapsed__multidim_weighted_with_arr: + @pytest.fixture(autouse=True) + def _multidim_arr_setup(self): self.data = np.arange(6.0).reshape((2, 3)) self.lazydata = as_lazy_data(self.data) # Test cubes with (same-valued) real and lazy data @@ -376,86 +379,102 @@ def test_weighted_fullweights_real_y(self): cube_collapsed = self.cube_real.collapsed( "y", MEAN, weights=self.full_weights_y ) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_y) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_y + ) + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_fullweights_lazy_y(self): # Full-shape weights, lazy data : Check lazy result, same values as real calc. cube_collapsed = self.cube_lazy.collapsed( "y", MEAN, weights=self.full_weights_y ) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_y) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_y + ) + assert cube_collapsed.units == "kg m-2 s-1" def test_weighted_1dweights_real_y(self): # 1-D weights, real data : Check same results as full-shape. cube_collapsed = self.cube_real.collapsed("y", MEAN, weights=self.y_weights) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_y) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_y + ) + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_1dweights_lazy_y(self): # 1-D weights, lazy data : Check lazy result, same values as real calc. cube_collapsed = self.cube_lazy.collapsed("y", MEAN, weights=self.y_weights) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_y) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_y + ) + assert cube_collapsed.units == "kg m-2 s-1" def test_weighted_fullweights_real_x(self): # Full weights, real data, ** collapse X ** : as for 'y' case above cube_collapsed = self.cube_real.collapsed( "x", MEAN, weights=self.full_weights_x ) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_x) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_x + ) + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_fullweights_lazy_x(self): # Full weights, lazy data, ** collapse X ** : as for 'y' case above cube_collapsed = self.cube_lazy.collapsed( "x", MEAN, weights=self.full_weights_x ) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_x) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_x + ) + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_1dweights_real_x(self): # 1-D weights, real data, ** collapse X ** : as for 'y' case above cube_collapsed = self.cube_real.collapsed("x", MEAN, weights=self.x_weights) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_x) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_x + ) + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_1dweights_lazy_x(self): # 1-D weights, lazy data, ** collapse X ** : as for 'y' case above cube_collapsed = self.cube_lazy.collapsed("x", MEAN, weights=self.x_weights) - self.assertTrue(cube_collapsed.has_lazy_data()) - self.assertArrayAlmostEqual(cube_collapsed.data, self.expected_result_x) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + assert cube_collapsed.has_lazy_data() + _shared_utils.assert_array_almost_equal( + cube_collapsed.data, self.expected_result_x + ) + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_sum_fullweights_adapt_units_real_y(self): # Check that units are adapted correctly (kg m-2 s-1 * 1 = kg m-2 s-1) cube_collapsed = self.cube_real.collapsed("y", SUM, weights=self.full_weights_y) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_sum_fullweights_adapt_units_lazy_y(self): # Check that units are adapted correctly (kg m-2 s-1 * 1 = kg m-2 s-1) cube_collapsed = self.cube_lazy.collapsed("y", SUM, weights=self.full_weights_y) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_sum_1dweights_adapt_units_real_y(self): # Check that units are adapted correctly (kg m-2 s-1 * 1 = kg m-2 s-1) # Note: the same test with lazy data fails: # https://github.com/SciTools/iris/issues/5083 cube_collapsed = self.cube_real.collapsed("y", SUM, weights=self.y_weights) - self.assertEqual(cube_collapsed.units, "kg m-2 s-1") - self.assertEqual(cube_collapsed.units.origin, "kg m-2 s-1") + assert cube_collapsed.units == "kg m-2 s-1" + assert cube_collapsed.units.origin == "kg m-2 s-1" def test_weighted_sum_with_unknown_units_real_y(self): # Check that units are adapted correctly ('unknown' * '1' = 'unknown') @@ -467,7 +486,7 @@ def test_weighted_sum_with_unknown_units_real_y(self): SUM, weights=self.full_weights_y, ) - self.assertEqual(cube_collapsed.units, "unknown") + assert cube_collapsed.units == "unknown" def test_weighted_sum_with_unknown_units_lazy_y(self): # Check that units are adapted correctly ('unknown' * '1' = 'unknown') @@ -479,7 +498,7 @@ def test_weighted_sum_with_unknown_units_lazy_y(self): SUM, weights=self.full_weights_y, ) - self.assertEqual(cube_collapsed.units, "unknown") + assert cube_collapsed.units == "unknown" # Simply redo the tests of Test_collapsed__multidim_weighted_with_arr with @@ -489,9 +508,8 @@ def test_weighted_sum_with_unknown_units_lazy_y(self): class Test_collapsed__multidim_weighted_with_cube( Test_collapsed__multidim_weighted_with_arr ): - def setUp(self): - super().setUp() - + @pytest.fixture(autouse=True) + def _multidim_cube_setup(self, _multidim_arr_setup): self.y_weights_original = self.y_weights self.full_weights_y_original = self.full_weights_y self.x_weights_original = self.x_weights @@ -507,27 +525,26 @@ def setUp(self): def test_weighted_sum_fullweights_adapt_units_real_y(self): # Check that units are adapted correctly (kg m-2 s-1 * m2 = kg s-1) cube_collapsed = self.cube_real.collapsed("y", SUM, weights=self.full_weights_y) - self.assertEqual(cube_collapsed.units, "kg s-1") + assert cube_collapsed.units == "kg s-1" def test_weighted_sum_fullweights_adapt_units_lazy_y(self): # Check that units are adapted correctly (kg m-2 s-1 * m2 = kg s-1) cube_collapsed = self.cube_lazy.collapsed("y", SUM, weights=self.full_weights_y) - self.assertEqual(cube_collapsed.units, "kg s-1") + assert cube_collapsed.units == "kg s-1" def test_weighted_sum_1dweights_adapt_units_real_y(self): # Check that units are adapted correctly (kg m-2 s-1 * m2 = kg s-1) # Note: the same test with lazy data fails: # https://github.com/SciTools/iris/issues/5083 cube_collapsed = self.cube_real.collapsed("y", SUM, weights=self.y_weights) - self.assertEqual(cube_collapsed.units, "kg s-1") + assert cube_collapsed.units == "kg s-1" class Test_collapsed__multidim_weighted_with_str( Test_collapsed__multidim_weighted_with_cube ): - def setUp(self): - super().setUp() - + @pytest.fixture(autouse=True) + def _multidim_str_setup(self, _multidim_cube_setup): self.full_weights_y = "full_y" self.full_weights_x = "full_x" self.y_weights = "y" @@ -561,17 +578,17 @@ def setUp(self): class Test_collapsed__multidim_weighted_with_dim_metadata( Test_collapsed__multidim_weighted_with_str ): - def setUp(self): - super().setUp() - + @pytest.fixture(autouse=True) + def _setup(self, _multidim_str_setup): self.full_weights_y = self.dim_metadata_full_y self.full_weights_x = self.dim_metadata_full_x self.y_weights = self.dim_metadata_1d_y self.x_weights = self.dim_metadata_1d_x -class Test_collapsed__cellmeasure_ancils(tests.IrisTest): - def setUp(self): +class Test_collapsed__cellmeasure_ancils: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(np.arange(6.0).reshape((2, 3))) for i_dim, name in enumerate(("y", "x")): npts = cube.shape[i_dim] @@ -585,19 +602,19 @@ def setUp(self): def test_ancillary_variables_and_cell_measures_kept(self): cube_collapsed = self.cube.collapsed("x", MEAN) - self.assertEqual( - cube_collapsed.ancillary_variables(), [self.ancillary_variable] - ) - self.assertEqual(cube_collapsed.cell_measures(), [self.cell_measure]) + assert cube_collapsed.ancillary_variables() == [self.ancillary_variable] + + assert cube_collapsed.cell_measures() == [self.cell_measure] def test_ancillary_variables_and_cell_measures_removed(self): cube_collapsed = self.cube.collapsed("y", MEAN) - self.assertEqual(cube_collapsed.ancillary_variables(), []) - self.assertEqual(cube_collapsed.cell_measures(), []) + assert cube_collapsed.ancillary_variables() == [] + assert cube_collapsed.cell_measures() == [] -class Test_collapsed__warning(tests.IrisTest): - def setUp(self): +class Test_collapsed__warning: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = Cube([[1, 2], [1, 2]]) lat = DimCoord([1, 2], standard_name="latitude") lon = DimCoord([1, 2], standard_name="longitude") @@ -624,79 +641,80 @@ def _assert_warn_collapse_without_weight(self, coords, warn): # Ensure that warning is raised. msg = "Collapsing spatial coordinate {!r} without weighting" for coord in coords: - self.assertIn( - mock.call(msg.format(coord), category=IrisUserWarning), - warn.call_args_list, + assert ( + mock.call(msg.format(coord), category=IrisUserWarning) + in warn.call_args_list ) def _assert_nowarn_collapse_without_weight(self, coords, warn): # Ensure that warning is not raised. msg = "Collapsing spatial coordinate {!r} without weighting" for coord in coords: - self.assertNotIn(mock.call(msg.format(coord)), warn.call_args_list) + assert not mock.call(msg.format(coord)) in warn.call_args_list - def test_lat_lon_noweighted_aggregator(self): + def test_lat_lon_noweighted_aggregator(self, mocker): # Collapse latitude coordinate with unweighted aggregator. aggregator = mock.Mock(spec=Aggregator, lazy_func=None) aggregator.cell_method = None coords = ["latitude", "longitude"] - with mock.patch("warnings.warn") as warn: - self.cube.collapsed(coords, aggregator, somekeyword="bla") + warn = mocker.patch("warnings.warn") + self.cube.collapsed(coords, aggregator, somekeyword="bla") self._assert_nowarn_collapse_without_weight(coords, warn) - def test_lat_lon_weighted_aggregator(self): + def test_lat_lon_weighted_aggregator(self, mocker): # Collapse latitude coordinate with weighted aggregator without # providing weights. aggregator = self._aggregator(False) coords = ["latitude", "longitude"] - with mock.patch("warnings.warn") as warn: - self.cube.collapsed(coords, aggregator) + warn = mocker.patch("warnings.warn") + self.cube.collapsed(coords, aggregator) coords = [coord for coord in coords if "latitude" in coord] self._assert_warn_collapse_without_weight(coords, warn) - def test_lat_lon_weighted_aggregator_with_weights(self): + def test_lat_lon_weighted_aggregator_with_weights(self, mocker): # Collapse latitude coordinate with a weighted aggregators and # providing suitable weights. weights = np.array([[0.1, 0.5], [0.3, 0.2]]) aggregator = self._aggregator(True) coords = ["latitude", "longitude"] - with mock.patch("warnings.warn") as warn: - self.cube.collapsed(coords, aggregator, weights=weights) + warn = mocker.patch("warnings.warn") + self.cube.collapsed(coords, aggregator, weights=weights) self._assert_nowarn_collapse_without_weight(coords, warn) - def test_lat_lon_weighted_aggregator_alt(self): + def test_lat_lon_weighted_aggregator_alt(self, mocker): # Collapse grid_latitude coordinate with weighted aggregator without # providing weights. Tests coordinate matching logic. aggregator = self._aggregator(False) coords = ["grid_latitude", "grid_longitude"] - with mock.patch("warnings.warn") as warn: - self.cube.collapsed(coords, aggregator) + warn = mocker.patch("warnings.warn") + self.cube.collapsed(coords, aggregator) coords = [coord for coord in coords if "latitude" in coord] self._assert_warn_collapse_without_weight(coords, warn) - def test_no_lat_weighted_aggregator_mixed(self): + def test_no_lat_weighted_aggregator_mixed(self, mocker): # Collapse grid_latitude and an unmatched coordinate (not lat/lon) # with weighted aggregator without providing weights. # Tests coordinate matching logic. aggregator = self._aggregator(False) coords = ["wibble"] - with mock.patch("warnings.warn") as warn: - self.cube.collapsed(coords, aggregator) + warn = mocker.patch("warnings.warn") + self.cube.collapsed(coords, aggregator) self._assert_nowarn_collapse_without_weight(coords, warn) -class Test_collapsed_coord_with_3_bounds(tests.IrisTest): - def setUp(self): +class Test_collapsed_coord_with_3_bounds: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = Cube([1, 2]) bounds = [[0.0, 1.0, 2.0], [2.0, 3.0, 4.0]] @@ -716,59 +734,59 @@ def _assert_warn_cannot_check_contiguity(self, warn): f"bounds. Metadata may not be fully descriptive for " f"'{coord}'. Ignoring bounds." ) - self.assertIn( - mock.call(msg, category=IrisVagueMetadataWarning), - warn.call_args_list, + assert ( + mock.call(msg, category=IrisVagueMetadataWarning) in warn.call_args_list ) def _assert_cube_as_expected(self, cube): """Ensure that cube data and coordinates are as expected.""" - self.assertArrayEqual(cube.data, np.array(3)) + _shared_utils.assert_array_equal(cube.data, np.array(3)) lat = cube.coord("latitude") - self.assertArrayAlmostEqual(lat.points, np.array([1.5])) - self.assertArrayAlmostEqual(lat.bounds, np.array([[1.0, 2.0]])) + _shared_utils.assert_array_almost_equal(lat.points, np.array([1.5])) + _shared_utils.assert_array_almost_equal(lat.bounds, np.array([[1.0, 2.0]])) lon = cube.coord("longitude") - self.assertArrayAlmostEqual(lon.points, np.array([1.5])) - self.assertArrayAlmostEqual(lon.bounds, np.array([[1.0, 2.0]])) + _shared_utils.assert_array_almost_equal(lon.points, np.array([1.5])) + _shared_utils.assert_array_almost_equal(lon.bounds, np.array([[1.0, 2.0]])) - def test_collapsed_lat_with_3_bounds(self): + def test_collapsed_lat_with_3_bounds(self, mocker): """Collapse latitude with 3 bounds.""" - with mock.patch("warnings.warn") as warn: - collapsed_cube = self.cube.collapsed("latitude", SUM) + warn = mocker.patch("warnings.warn") + collapsed_cube = self.cube.collapsed("latitude", SUM) self._assert_warn_cannot_check_contiguity(warn) self._assert_cube_as_expected(collapsed_cube) - def test_collapsed_lon_with_3_bounds(self): + def test_collapsed_lon_with_3_bounds(self, mocker): """Collapse longitude with 3 bounds.""" - with mock.patch("warnings.warn") as warn: - collapsed_cube = self.cube.collapsed("longitude", SUM) + warn = mocker.patch("warnings.warn") + collapsed_cube = self.cube.collapsed("longitude", SUM) self._assert_warn_cannot_check_contiguity(warn) self._assert_cube_as_expected(collapsed_cube) - def test_collapsed_lat_lon_with_3_bounds(self): + def test_collapsed_lat_lon_with_3_bounds(self, mocker): """Collapse latitude and longitude with 3 bounds.""" - with mock.patch("warnings.warn") as warn: - collapsed_cube = self.cube.collapsed(["latitude", "longitude"], SUM) + warn = mocker.patch("warnings.warn") + collapsed_cube = self.cube.collapsed(["latitude", "longitude"], SUM) self._assert_warn_cannot_check_contiguity(warn) self._assert_cube_as_expected(collapsed_cube) -class Test_summary(tests.IrisTest): - def setUp(self): +class Test_summary: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = Cube(0) def test_cell_datetime_objects(self): self.cube.add_aux_coord(AuxCoord(42, units="hours since epoch")) summary = self.cube.summary() - self.assertIn("1970-01-02 18:00:00", summary) + assert "1970-01-02 18:00:00" in summary def test_scalar_str_coord(self): str_value = "foo" self.cube.add_aux_coord(AuxCoord(str_value)) summary = self.cube.summary() - self.assertIn(str_value, summary) + assert str_value in summary def test_ancillary_variable(self): cube = Cube(np.arange(6).reshape(2, 3)) @@ -779,7 +797,7 @@ def test_ancillary_variable(self): " Ancillary variables:\n" " status_flag x -" ) - self.assertEqual(expected_summary, cube.summary()) + assert expected_summary == cube.summary() def test_similar_coords(self): coord1 = AuxCoord(42, long_name="foo", attributes=dict(bar=np.array([2, 5]))) @@ -787,7 +805,7 @@ def test_similar_coords(self): coord2.attributes = dict(bar="baz") for coord in [coord1, coord2]: self.cube.add_aux_coord(coord) - self.assertIn("baz", self.cube.summary()) + assert "baz" in self.cube.summary() def test_long_components(self): # Check that components with long names 'stretch' the printout correctly. @@ -821,7 +839,7 @@ def test_long_components(self): # For lines with any columns : check that columns are where expected for col_ind in colon_inds: # Chop out chars before+after each expected column. - self.assertEqual(line[col_ind - 1 : col_ind + 2], " x ") + assert line[col_ind - 1 : col_ind + 2] == " x " # Finally also: compare old with new, but replacing new name and ignoring spacing differences def collapse_space(string): @@ -830,37 +848,38 @@ def collapse_space(string): string = string.replace(" ", " ") return string - self.assertEqual( - collapse_space(new_summary).replace(long_name, old_name), - collapse_space(original_summary), - ) + assert collapse_space(new_summary).replace( + long_name, old_name + ) == collapse_space(original_summary) -class Test_is_compatible(tests.IrisTest): - def setUp(self): +class Test_is_compatible: + @pytest.fixture(autouse=True) + def _setup(self): self.test_cube = Cube([1.0]) self.other_cube = self.test_cube.copy() def test_noncommon_array_attrs_compatible(self): # Non-common array attributes should be ok. self.test_cube.attributes["array_test"] = np.array([1.0, 2, 3]) - self.assertTrue(self.test_cube.is_compatible(self.other_cube)) + assert self.test_cube.is_compatible(self.other_cube) def test_matching_array_attrs_compatible(self): # Matching array attributes should be ok. self.test_cube.attributes["array_test"] = np.array([1.0, 2, 3]) self.other_cube.attributes["array_test"] = np.array([1.0, 2, 3]) - self.assertTrue(self.test_cube.is_compatible(self.other_cube)) + assert self.test_cube.is_compatible(self.other_cube) def test_different_array_attrs_incompatible(self): # Differing array attributes should make the cubes incompatible. self.test_cube.attributes["array_test"] = np.array([1.0, 2, 3]) self.other_cube.attributes["array_test"] = np.array([1.0, 2, 777.7]) - self.assertFalse(self.test_cube.is_compatible(self.other_cube)) + assert not self.test_cube.is_compatible(self.other_cube) -class Test_rolling_window(tests.IrisTest): - def setUp(self): +class Test_rolling_window: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = Cube(np.arange(6), units="kg") self.multi_dim_cube = Cube(np.arange(36).reshape(6, 6)) val_coord = DimCoord([0, 1, 2, 3, 4, 5], long_name="val", units="s") @@ -902,8 +921,8 @@ def test_string_coord(self): ), long_name="month", ) - self.assertEqual(res_cube.coord("val"), val_coord) - self.assertEqual(res_cube.coord("month"), month_coord) + assert res_cube.coord("val") == val_coord + assert res_cube.coord("month") == month_coord def test_kwargs(self): # Rolling window with missing data not tolerated @@ -917,51 +936,52 @@ def test_kwargs(self): mask=[True, False, False, True, True], dtype=np.float64, ) - self.assertMaskedArrayEqual(expected_result, res_cube.data) + _shared_utils.assert_masked_array_equal(expected_result, res_cube.data) def test_ancillary_variables_and_cell_measures_kept(self): res_cube = self.multi_dim_cube.rolling_window("val", self.mock_agg, 3) - self.assertEqual(res_cube.ancillary_variables(), [self.ancillary_variable]) - self.assertEqual(res_cube.cell_measures(), [self.cell_measure]) + assert res_cube.ancillary_variables() == [self.ancillary_variable] + assert res_cube.cell_measures() == [self.cell_measure] def test_ancillary_variables_and_cell_measures_removed(self): res_cube = self.multi_dim_cube.rolling_window("extra", self.mock_agg, 3) - self.assertEqual(res_cube.ancillary_variables(), []) - self.assertEqual(res_cube.cell_measures(), []) + assert res_cube.ancillary_variables() == [] + assert res_cube.cell_measures() == [] def test_weights_arr(self): weights = np.array([0, 0, 1, 0, 2]) res_cube = self.cube.rolling_window("val", SUM, 5, weights=weights) - np.testing.assert_array_equal(res_cube.data, [10, 13]) - self.assertEqual(res_cube.units, "kg") + _shared_utils.assert_array_equal(res_cube.data, [10, 13]) + assert res_cube.units == "kg" def test_weights_cube(self): weights = Cube([0, 0, 1, 0, 2], units="m2") res_cube = self.cube.rolling_window("val", SUM, 5, weights=weights) - np.testing.assert_array_equal(res_cube.data, [10, 13]) - self.assertEqual(res_cube.units, "kg m2") + _shared_utils.assert_array_equal(res_cube.data, [10, 13]) + assert res_cube.units == "kg m2" def test_weights_str(self): weights = "val" res_cube = self.cube.rolling_window("val", SUM, 6, weights=weights) - np.testing.assert_array_equal(res_cube.data, [55]) - self.assertEqual(res_cube.units, "kg s") + _shared_utils.assert_array_equal(res_cube.data, [55]) + assert res_cube.units == "kg s" def test_weights_dim_coord(self): weights = self.cube.coord("val") res_cube = self.cube.rolling_window("val", SUM, 6, weights=weights) - np.testing.assert_array_equal(res_cube.data, [55]) - self.assertEqual(res_cube.units, "kg s") + _shared_utils.assert_array_equal(res_cube.data, [55]) + assert res_cube.units == "kg s" -class Test_slices_dim_order(tests.IrisTest): +class Test_slices_dim_order: """Test the capability of iris.cube.Cube.slices(). Test the capability of iris.cube.Cube.slices(), including its ability to correctly re-order the dimensions. """ - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): """Setup a 4D iris cube, each dimension is length 1. The dimensions are; dim1: time @@ -1026,16 +1046,17 @@ def check_order(self, dim1, dim2, dim3, dim_to_remove): sliced_cube = next(self.cube.slices([dim1, dim2, dim3])) sliced_cube.remove_coord(dim_to_remove) expected_cube = self.expected_cube_setup(dim1, dim2, dim3) - self.assertEqual(sliced_cube, expected_cube) + assert sliced_cube == expected_cube def test_all_permutations(self): for perm in permutations(["time", "height", "latitude", "longitude"]): self.check_order(*perm) -@tests.skip_data -class Test_slices_over(tests.IrisTest): - def setUp(self): +@_shared_utils.skip_data +class Test_slices_over: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = stock.realistic_4d() # Define expected iterators for 1D and 2D test cases. self.exp_iter_1d = range(len(self.cube.coord("model_level_number").points)) @@ -1048,30 +1069,30 @@ def test_1d_slice_coord_given(self): res = self.cube.slices_over(self.cube.coord("model_level_number")) for i, res_cube in zip(self.exp_iter_1d, res): expected = self.cube[:, i] - self.assertEqual(res_cube, expected) + assert res_cube == expected def test_1d_slice_nonexistent_coord_given(self): - with self.assertRaises(CoordinateNotFoundError): + with pytest.raises(CoordinateNotFoundError): _ = self.cube.slices_over(self.cube.coord("wibble")) def test_1d_slice_coord_name_given(self): res = self.cube.slices_over("model_level_number") for i, res_cube in zip(self.exp_iter_1d, res): expected = self.cube[:, i] - self.assertEqual(res_cube, expected) + assert res_cube == expected def test_1d_slice_nonexistent_coord_name_given(self): - with self.assertRaises(CoordinateNotFoundError): + with pytest.raises(CoordinateNotFoundError): _ = self.cube.slices_over("wibble") def test_1d_slice_dimension_given(self): res = self.cube.slices_over(1) for i, res_cube in zip(self.exp_iter_1d, res): expected = self.cube[:, i] - self.assertEqual(res_cube, expected) + assert res_cube == expected def test_1d_slice_nonexistent_dimension_given(self): - with self.assertRaisesRegex(ValueError, "iterator over a dimension"): + with pytest.raises(ValueError, match="iterator over a dimension"): _ = self.cube.slices_over(self.cube.ndim + 1) def test_2d_slice_coord_given(self): @@ -1085,10 +1106,10 @@ def test_2d_slice_coord_given(self): # Replace the dimensions not iterated over with spanning slices. indices[2] = indices[3] = slice(None) expected = self.cube[tuple(indices)] - self.assertEqual(next(res), expected) + assert next(res) == expected def test_2d_slice_nonexistent_coord_given(self): - with self.assertRaises(CoordinateNotFoundError): + with pytest.raises(CoordinateNotFoundError): _ = self.cube.slices_over( [self.cube.coord("time"), self.cube.coord("wibble")] ) @@ -1102,10 +1123,10 @@ def test_2d_slice_coord_name_given(self): # Replace the dimensions not iterated over with spanning slices. indices[2] = indices[3] = slice(None) expected = self.cube[tuple(indices)] - self.assertEqual(next(res), expected) + assert next(res) == expected def test_2d_slice_nonexistent_coord_name_given(self): - with self.assertRaises(CoordinateNotFoundError): + with pytest.raises(CoordinateNotFoundError): _ = self.cube.slices_over(["time", "wibble"]) def test_2d_slice_dimension_given(self): @@ -1117,7 +1138,7 @@ def test_2d_slice_dimension_given(self): # Replace the dimensions not iterated over with spanning slices. indices[2] = indices[3] = slice(None) expected = self.cube[tuple(indices)] - self.assertEqual(next(res), expected) + assert next(res) == expected def test_2d_slice_reversed_dimension_given(self): # Confirm that reversing the order of the dimensions returns the same @@ -1128,10 +1149,10 @@ def test_2d_slice_reversed_dimension_given(self): # Replace the dimensions not iterated over with spanning slices. indices[2] = indices[3] = slice(None) expected = self.cube[tuple(indices)] - self.assertEqual(next(res), expected) + assert next(res) == expected def test_2d_slice_nonexistent_dimension_given(self): - with self.assertRaisesRegex(ValueError, "iterator over a dimension"): + with pytest.raises(ValueError, match="iterator over a dimension"): _ = self.cube.slices_over([0, self.cube.ndim + 1]) def test_multidim_slice_coord_given(self): @@ -1145,24 +1166,24 @@ def test_multidim_slice_coord_given(self): # Replace the dimensions not iterated over with spanning slices. indices[0] = indices[1] = slice(None) expected = self.cube[tuple(indices)] - self.assertEqual(next(res), expected) + assert next(res) == expected def test_duplicate_coordinate_given(self): res = self.cube.slices_over([1, 1]) for i, res_cube in zip(self.exp_iter_1d, res): expected = self.cube[:, i] - self.assertEqual(res_cube, expected) + assert res_cube == expected def test_non_orthogonal_coordinates_given(self): res = self.cube.slices_over(["model_level_number", "sigma"]) for i, res_cube in zip(self.exp_iter_1d, res): expected = self.cube[:, i] - self.assertEqual(res_cube, expected) + assert res_cube == expected def test_nodimension(self): # Slicing over no dimension should return the whole cube. res = self.cube.slices_over([]) - self.assertEqual(next(res), self.cube) + assert next(res) == self.cube def create_cube(lon_min, lon_max, bounds=False): @@ -1213,178 +1234,200 @@ def create_cube(lon_min, lon_max, bounds=False): # Ensure all the other coordinates and factories are correctly preserved. -class Test_intersection__Metadata(tests.IrisTest): - def test_metadata(self): +class Test_intersection__Metadata: + def test_metadata(self, request): cube = create_cube(0, 360) result = cube.intersection(longitude=(170, 190)) - self.assertCMLApproxData(result) + _shared_utils.assert_CML_approx_data(request, result) - def test_metadata_wrapped(self): + def test_metadata_wrapped(self, request): cube = create_cube(-180, 180) result = cube.intersection(longitude=(170, 190)) - self.assertCMLApproxData(result) + _shared_utils.assert_CML_approx_data(request, result) # Explicitly check the handling of `circular` on the result. -class Test_intersection__Circular(tests.IrisTest): +class Test_intersection__Circular: def test_regional(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(170, 190)) - self.assertFalse(result.coord("longitude").circular) + assert not result.coord("longitude").circular def test_regional_wrapped(self): cube = create_cube(-180, 180) result = cube.intersection(longitude=(170, 190)) - self.assertFalse(result.coord("longitude").circular) + assert not result.coord("longitude").circular def test_global(self): cube = create_cube(-180, 180) result = cube.intersection(longitude=(-180, 180)) - self.assertTrue(result.coord("longitude").circular) + assert result.coord("longitude").circular def test_global_wrapped(self): cube = create_cube(-180, 180) result = cube.intersection(longitude=(10, 370)) - self.assertTrue(result.coord("longitude").circular) + assert result.coord("longitude").circular # Check the various error conditions. -class Test_intersection__Invalid(tests.IrisTest): +class Test_intersection__Invalid: def test_reversed_min_max(self): cube = create_cube(0, 360) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): cube.intersection(longitude=(30, 10)) def test_dest_too_large(self): cube = create_cube(0, 360) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): cube.intersection(longitude=(30, 500)) def test_src_too_large(self): cube = create_cube(0, 400) - with self.assertRaises(ValueError): + with pytest.raises(ValueError): cube.intersection(longitude=(10, 30)) def test_missing_coord(self): cube = create_cube(0, 360) - with self.assertRaises(iris.exceptions.CoordinateNotFoundError): + with pytest.raises(iris.exceptions.CoordinateNotFoundError): cube.intersection(parrots=(10, 30)) def test_multi_dim_coord(self): cube = create_cube(0, 360) - with self.assertRaises(iris.exceptions.CoordinateMultiDimError): + with pytest.raises(iris.exceptions.CoordinateMultiDimError): cube.intersection(surface_altitude=(10, 30)) def test_null_region(self): # 10 <= v < 10 cube = create_cube(0, 360) - with self.assertRaises(IndexError): + with pytest.raises(IndexError): cube.intersection(longitude=(10, 10, False, False)) -class Test_intersection__Lazy(tests.IrisTest): +class Test_intersection__Lazy: def test_real_data(self): cube = create_cube(0, 360) cube.data result = cube.intersection(longitude=(170, 190)) - self.assertFalse(result.has_lazy_data()) - self.assertArrayEqual(result.coord("longitude").points, np.arange(170, 191)) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 190) + assert not result.has_lazy_data() + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(170, 191) + ) + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 190 def test_real_data_wrapped(self): cube = create_cube(-180, 180) cube.data result = cube.intersection(longitude=(170, 190)) - self.assertFalse(result.has_lazy_data()) - self.assertArrayEqual(result.coord("longitude").points, np.arange(170, 191)) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + assert not result.has_lazy_data() + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(170, 191) + ) + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_lazy_data(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(170, 190)) - self.assertTrue(result.has_lazy_data()) - self.assertArrayEqual(result.coord("longitude").points, np.arange(170, 191)) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 190) + assert result.has_lazy_data() + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(170, 191) + ) + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 190 def test_lazy_data_wrapped(self): cube = create_cube(-180, 180) result = cube.intersection(longitude=(170, 190)) - self.assertTrue(result.has_lazy_data()) - self.assertArrayEqual(result.coord("longitude").points, np.arange(170, 191)) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + assert result.has_lazy_data() + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(170, 191) + ) + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 -class Test_intersection_Points(tests.IrisTest): +class Test_intersection_Points: def test_ignore_bounds(self): cube = create_cube(0, 30, bounds=True) result = cube.intersection(longitude=(9.5, 12.5), ignore_bounds=True) - self.assertArrayEqual(result.coord("longitude").points, np.arange(10, 13)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [9.5, 10.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [11.5, 12.5]) + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(10, 13) + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [9.5, 10.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [11.5, 12.5] + ) # Check what happens with a regional, points-only circular intersection # coordinate. -class Test_intersection__RegionalSrcModulus(tests.IrisTest): +class Test_intersection__RegionalSrcModulus: def test_request_subset(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(45, 50)) - self.assertArrayEqual(result.coord("longitude").points, np.arange(45, 51)) - self.assertArrayEqual(result.data[0, 0], np.arange(5, 11)) + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(45, 51) + ) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(5, 11)) def test_request_left(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(35, 45)) - self.assertArrayEqual(result.coord("longitude").points, np.arange(40, 46)) - self.assertArrayEqual(result.data[0, 0], np.arange(0, 6)) + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(40, 46) + ) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(0, 6)) def test_request_right(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(55, 65)) - self.assertArrayEqual(result.coord("longitude").points, np.arange(55, 60)) - self.assertArrayEqual(result.data[0, 0], np.arange(15, 20)) + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(55, 60) + ) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(15, 20)) def test_request_superset(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(35, 65)) - self.assertArrayEqual(result.coord("longitude").points, np.arange(40, 60)) - self.assertArrayEqual(result.data[0, 0], np.arange(0, 20)) + _shared_utils.assert_array_equal( + result.coord("longitude").points, np.arange(40, 60) + ) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(0, 20)) def test_request_subset_modulus(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(45 + 360, 50 + 360)) - self.assertArrayEqual( + _shared_utils.assert_array_equal( result.coord("longitude").points, np.arange(45 + 360, 51 + 360) ) - self.assertArrayEqual(result.data[0, 0], np.arange(5, 11)) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(5, 11)) def test_request_left_modulus(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(35 + 360, 45 + 360)) - self.assertArrayEqual( + _shared_utils.assert_array_equal( result.coord("longitude").points, np.arange(40 + 360, 46 + 360) ) - self.assertArrayEqual(result.data[0, 0], np.arange(0, 6)) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(0, 6)) def test_request_right_modulus(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(55 + 360, 65 + 360)) - self.assertArrayEqual( + _shared_utils.assert_array_equal( result.coord("longitude").points, np.arange(55 + 360, 60 + 360) ) - self.assertArrayEqual(result.data[0, 0], np.arange(15, 20)) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(15, 20)) def test_request_superset_modulus(self): cube = create_cube(40, 60) result = cube.intersection(longitude=(35 + 360, 65 + 360)) - self.assertArrayEqual( + _shared_utils.assert_array_equal( result.coord("longitude").points, np.arange(40 + 360, 60 + 360) ) - self.assertArrayEqual(result.data[0, 0], np.arange(0, 20)) + _shared_utils.assert_array_equal(result.data[0, 0], np.arange(0, 20)) def test_tolerance_f4(self): cube = create_cube(0, 5) @@ -1392,10 +1435,10 @@ def test_tolerance_f4(self): [0.0, 3.74999905, 7.49999809, 11.24999714, 14.99999619], dtype="f4" ) result = cube.intersection(longitude=(0, 5)) - self.assertArrayAlmostEqual( + _shared_utils.assert_array_almost_equal( result.coord("longitude").points, np.array([0.0, 3.74999905]) ) - self.assertArrayEqual(result.data[0, 0], np.array([0, 1])) + _shared_utils.assert_array_equal(result.data[0, 0], np.array([0, 1])) def test_tolerance_f8(self): cube = create_cube(0, 5) @@ -1403,15 +1446,15 @@ def test_tolerance_f8(self): [0.0, 3.74999905, 7.49999809, 11.24999714, 14.99999619], dtype="f8" ) result = cube.intersection(longitude=(0, 5)) - self.assertArrayAlmostEqual( + _shared_utils.assert_array_almost_equal( result.coord("longitude").points, np.array([0.0, 3.74999905]) ) - self.assertArrayEqual(result.data[0, 0], np.array([0, 1])) + _shared_utils.assert_array_equal(result.data[0, 0], np.array([0, 1])) # Check what happens with a global, points-only circular intersection # coordinate. -class Test_intersection__GlobalSrcModulus(tests.IrisTest): +class Test_intersection__GlobalSrcModulus: def test_global_wrapped_extreme_increasing_base_period(self): # Ensure that we can correctly handle points defined at (base + period) cube = create_cube(-180.0, 180.0) @@ -1419,7 +1462,7 @@ def test_global_wrapped_extreme_increasing_base_period(self): # Redefine longitude so that points at (base + period) lons.points = np.linspace(-180.0, 180, lons.points.size) result = cube.intersection(longitude=(lons.points.min(), lons.points.max())) - self.assertArrayEqual(result.data, cube.data) + _shared_utils.assert_array_equal(result.data, cube.data) def test_global_wrapped_extreme_decreasing_base_period(self): # Ensure that we can correctly handle points defined at (base + period) @@ -1428,41 +1471,41 @@ def test_global_wrapped_extreme_decreasing_base_period(self): # Redefine longitude so that points at (base + period) lons.points = np.linspace(180.0, -180.0, lons.points.size) result = cube.intersection(longitude=(lons.points.min(), lons.points.max())) - self.assertArrayEqual(result.data, cube.data) + _shared_utils.assert_array_equal(result.data, cube.data) def test_global(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(0, 360)) - self.assertEqual(result.coord("longitude").points[0], 0) - self.assertEqual(result.coord("longitude").points[-1], 359) - self.assertEqual(result.data[0, 0, 0], 0) - self.assertEqual(result.data[0, 0, -1], 359) + assert result.coord("longitude").points[0] == 0 + assert result.coord("longitude").points[-1] == 359 + assert result.data[0, 0, 0] == 0 + assert result.data[0, 0, -1] == 359 def test_global_wrapped(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(-180, 180)) - self.assertEqual(result.coord("longitude").points[0], -180) - self.assertEqual(result.coord("longitude").points[-1], 179) - self.assertEqual(result.data[0, 0, 0], 180) - self.assertEqual(result.data[0, 0, -1], 179) + assert result.coord("longitude").points[0] == -180 + assert result.coord("longitude").points[-1] == 179 + assert result.data[0, 0, 0] == 180 + assert result.data[0, 0, -1] == 179 def test_aux_coord(self): cube = create_cube(0, 360) cube.replace_coord(iris.coords.AuxCoord.from_coord(cube.coord("longitude"))) result = cube.intersection(longitude=(0, 360)) - self.assertEqual(result.coord("longitude").points[0], 0) - self.assertEqual(result.coord("longitude").points[-1], 359) - self.assertEqual(result.data[0, 0, 0], 0) - self.assertEqual(result.data[0, 0, -1], 359) + assert result.coord("longitude").points[0] == 0 + assert result.coord("longitude").points[-1] == 359 + assert result.data[0, 0, 0] == 0 + assert result.data[0, 0, -1] == 359 def test_aux_coord_wrapped(self): cube = create_cube(0, 360) cube.replace_coord(iris.coords.AuxCoord.from_coord(cube.coord("longitude"))) result = cube.intersection(longitude=(-180, 180)) - self.assertEqual(result.coord("longitude").points[0], 0) - self.assertEqual(result.coord("longitude").points[-1], -1) - self.assertEqual(result.data[0, 0, 0], 0) - self.assertEqual(result.data[0, 0, -1], 359) + assert result.coord("longitude").points[0] == 0 + assert result.coord("longitude").points[-1] == -1 + assert result.data[0, 0, 0] == 0 + assert result.data[0, 0, -1] == 359 def test_aux_coord_non_contiguous_wrapped(self): cube = create_cube(0, 360) @@ -1470,103 +1513,103 @@ def test_aux_coord_non_contiguous_wrapped(self): coord.points = (coord.points * 1.5) % 360 cube.replace_coord(coord) result = cube.intersection(longitude=(-90, 90)) - self.assertEqual(result.coord("longitude").points[0], 0) - self.assertEqual(result.coord("longitude").points[-1], 90) - self.assertEqual(result.data[0, 0, 0], 0) - self.assertEqual(result.data[0, 0, -1], 300) + assert result.coord("longitude").points[0] == 0 + assert result.coord("longitude").points[-1] == 90 + assert result.data[0, 0, 0] == 0 + assert result.data[0, 0, -1] == 300 def test_decrementing(self): cube = create_cube(360, 0) result = cube.intersection(longitude=(40, 60)) - self.assertEqual(result.coord("longitude").points[0], 60) - self.assertEqual(result.coord("longitude").points[-1], 40) - self.assertEqual(result.data[0, 0, 0], 300) - self.assertEqual(result.data[0, 0, -1], 320) + assert result.coord("longitude").points[0] == 60 + assert result.coord("longitude").points[-1] == 40 + assert result.data[0, 0, 0] == 300 + assert result.data[0, 0, -1] == 320 def test_decrementing_wrapped(self): cube = create_cube(360, 0) result = cube.intersection(longitude=(-10, 10)) - self.assertEqual(result.coord("longitude").points[0], 10) - self.assertEqual(result.coord("longitude").points[-1], -10) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + assert result.coord("longitude").points[0] == 10 + assert result.coord("longitude").points[-1] == -10 + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_no_wrap_after_modulus(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(170 + 360, 190 + 360)) - self.assertEqual(result.coord("longitude").points[0], 170 + 360) - self.assertEqual(result.coord("longitude").points[-1], 190 + 360) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 190) + assert result.coord("longitude").points[0] == 170 + 360 + assert result.coord("longitude").points[-1] == 190 + 360 + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 190 def test_wrap_after_modulus(self): cube = create_cube(-180, 180) result = cube.intersection(longitude=(170 + 360, 190 + 360)) - self.assertEqual(result.coord("longitude").points[0], 170 + 360) - self.assertEqual(result.coord("longitude").points[-1], 190 + 360) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + assert result.coord("longitude").points[0] == 170 + 360 + assert result.coord("longitude").points[-1] == 190 + 360 + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_select_by_coord(self): cube = create_cube(0, 360) coord = iris.coords.DimCoord(0, "longitude", units="degrees") result = cube.intersection(iris.coords.CoordExtent(coord, 10, 30)) - self.assertEqual(result.coord("longitude").points[0], 10) - self.assertEqual(result.coord("longitude").points[-1], 30) - self.assertEqual(result.data[0, 0, 0], 10) - self.assertEqual(result.data[0, 0, -1], 30) + assert result.coord("longitude").points[0] == 10 + assert result.coord("longitude").points[-1] == 30 + assert result.data[0, 0, 0] == 10 + assert result.data[0, 0, -1] == 30 def test_inclusive_exclusive(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(170, 190, True, False)) - self.assertEqual(result.coord("longitude").points[0], 170) - self.assertEqual(result.coord("longitude").points[-1], 189) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 189) + assert result.coord("longitude").points[0] == 170 + assert result.coord("longitude").points[-1] == 189 + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 189 def test_exclusive_inclusive(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(170, 190, False)) - self.assertEqual(result.coord("longitude").points[0], 171) - self.assertEqual(result.coord("longitude").points[-1], 190) - self.assertEqual(result.data[0, 0, 0], 171) - self.assertEqual(result.data[0, 0, -1], 190) + assert result.coord("longitude").points[0] == 171 + assert result.coord("longitude").points[-1] == 190 + assert result.data[0, 0, 0] == 171 + assert result.data[0, 0, -1] == 190 def test_exclusive_exclusive(self): cube = create_cube(0, 360) result = cube.intersection(longitude=(170, 190, False, False)) - self.assertEqual(result.coord("longitude").points[0], 171) - self.assertEqual(result.coord("longitude").points[-1], 189) - self.assertEqual(result.data[0, 0, 0], 171) - self.assertEqual(result.data[0, 0, -1], 189) + assert result.coord("longitude").points[0] == 171 + assert result.coord("longitude").points[-1] == 189 + assert result.data[0, 0, 0] == 171 + assert result.data[0, 0, -1] == 189 def test_single_point(self): # 10 <= v <= 10 cube = create_cube(0, 360) result = cube.intersection(longitude=(10, 10)) - self.assertEqual(result.coord("longitude").points[0], 10) - self.assertEqual(result.coord("longitude").points[-1], 10) - self.assertEqual(result.data[0, 0, 0], 10) - self.assertEqual(result.data[0, 0, -1], 10) + assert result.coord("longitude").points[0] == 10 + assert result.coord("longitude").points[-1] == 10 + assert result.data[0, 0, 0] == 10 + assert result.data[0, 0, -1] == 10 def test_two_points(self): # -1.5 <= v <= 0.5 cube = create_cube(0, 360) result = cube.intersection(longitude=(-1.5, 0.5)) - self.assertEqual(result.coord("longitude").points[0], -1) - self.assertEqual(result.coord("longitude").points[-1], 0) - self.assertEqual(result.data[0, 0, 0], 359) - self.assertEqual(result.data[0, 0, -1], 0) + assert result.coord("longitude").points[0] == -1 + assert result.coord("longitude").points[-1] == 0 + assert result.data[0, 0, 0] == 359 + assert result.data[0, 0, -1] == 0 def test_wrap_radians(self): cube = create_cube(0, 360) cube.coord("longitude").convert_units("radians") result = cube.intersection(longitude=(-1, 0.5)) - self.assertArrayAllClose( + _shared_utils.assert_array_all_close( result.coord("longitude").points, np.arange(-57, 29) * np.pi / 180 ) - self.assertEqual(result.data[0, 0, 0], 303) - self.assertEqual(result.data[0, 0, -1], 28) + assert result.data[0, 0, 0] == 303 + assert result.data[0, 0, -1] == 28 def test_tolerance_bug(self): # Floating point changes introduced by wrapping mean @@ -1575,7 +1618,7 @@ def test_tolerance_bug(self): cube = create_cube(0, 400) cube.coord("longitude").points = np.linspace(-179.55, 179.55, 400) result = cube.intersection(longitude=(125, 145)) - self.assertArrayAlmostEqual( + _shared_utils.assert_array_almost_equal( result.coord("longitude").points, cube.coord("longitude").points[339:361], ) @@ -1589,138 +1632,194 @@ def test_tolerance_bug_wrapped(self): cube.coord("longitude").points[389:] - 360.0, cube.coord("longitude").points[:11], ) - self.assertArrayAlmostEqual(result.coord("longitude").points, expected) + _shared_utils.assert_array_almost_equal( + result.coord("longitude").points, expected + ) # Check what happens with a global, points-and-bounds circular # intersection coordinate. -class Test_intersection__ModulusBounds(tests.IrisTest): +class Test_intersection__ModulusBounds: def test_global_wrapped_extreme_increasing_base_period(self): # Ensure that we can correctly handle bounds defined at (base + period) cube = create_cube(-180.0, 180.0, bounds=True) lons = cube.coord("longitude") result = cube.intersection(longitude=(lons.bounds.min(), lons.bounds.max())) - self.assertArrayEqual(result.data, cube.data) + _shared_utils.assert_array_equal(result.data, cube.data) def test_global_wrapped_extreme_decreasing_base_period(self): # Ensure that we can correctly handle bounds defined at (base + period) cube = create_cube(180.0, -180.0, bounds=True) lons = cube.coord("longitude") result = cube.intersection(longitude=(lons.bounds.min(), lons.bounds.max())) - self.assertArrayEqual(result.data, cube.data) + _shared_utils.assert_array_equal(result.data, cube.data) def test_misaligned_points_inside(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(169.75, 190.25)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [169.5, 170.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [189.5, 190.5]) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 190) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [169.5, 170.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [189.5, 190.5] + ) + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 190 def test_misaligned_points_outside(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(170.25, 189.75)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [169.5, 170.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [189.5, 190.5]) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 190) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [169.5, 170.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [189.5, 190.5] + ) + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 190 def test_misaligned_bounds(self): cube = create_cube(-180, 180, bounds=True) result = cube.intersection(longitude=(0, 360)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [-0.5, 0.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [358.5, 359.5]) - self.assertEqual(result.data[0, 0, 0], 180) - self.assertEqual(result.data[0, 0, -1], 179) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [-0.5, 0.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [358.5, 359.5] + ) + assert result.data[0, 0, 0] == 180 + assert result.data[0, 0, -1] == 179 def test_misaligned_bounds_decreasing(self): cube = create_cube(180, -180, bounds=True) result = cube.intersection(longitude=(0, 360)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [359.5, 358.5]) - self.assertArrayEqual(result.coord("longitude").points[-1], 0) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [0.5, -0.5]) - self.assertEqual(result.data[0, 0, 0], 181) - self.assertEqual(result.data[0, 0, -1], 180) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [359.5, 358.5] + ) + _shared_utils.assert_array_equal(result.coord("longitude").points[-1], 0) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [0.5, -0.5] + ) + assert result.data[0, 0, 0] == 181 + assert result.data[0, 0, -1] == 180 def test_aligned_inclusive(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(170.5, 189.5)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [169.5, 170.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [189.5, 190.5]) - self.assertEqual(result.data[0, 0, 0], 170) - self.assertEqual(result.data[0, 0, -1], 190) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [169.5, 170.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [189.5, 190.5] + ) + assert result.data[0, 0, 0] == 170 + assert result.data[0, 0, -1] == 190 def test_aligned_exclusive(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(170.5, 189.5, False, False)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [170.5, 171.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [188.5, 189.5]) - self.assertEqual(result.data[0, 0, 0], 171) - self.assertEqual(result.data[0, 0, -1], 189) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [170.5, 171.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [188.5, 189.5] + ) + assert result.data[0, 0, 0] == 171 + assert result.data[0, 0, -1] == 189 def test_aligned_bounds_at_modulus(self): cube = create_cube(-179.5, 180.5, bounds=True) result = cube.intersection(longitude=(0, 360)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [0, 1]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [359, 360]) - self.assertEqual(result.data[0, 0, 0], 180) - self.assertEqual(result.data[0, 0, -1], 179) + _shared_utils.assert_array_equal(result.coord("longitude").bounds[0], [0, 1]) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [359, 360] + ) + assert result.data[0, 0, 0] == 180 + assert result.data[0, 0, -1] == 179 def test_negative_aligned_bounds_at_modulus(self): cube = create_cube(0.5, 360.5, bounds=True) result = cube.intersection(longitude=(-180, 180)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [-180, -179]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [179, 180]) - self.assertEqual(result.data[0, 0, 0], 180) - self.assertEqual(result.data[0, 0, -1], 179) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [-180, -179] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [179, 180] + ) + assert result.data[0, 0, 0] == 180 + assert result.data[0, 0, -1] == 179 def test_negative_misaligned_points_inside(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(-10.25, 10.25)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [-10.5, -9.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [9.5, 10.5]) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [-10.5, -9.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [9.5, 10.5] + ) + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_negative_misaligned_points_outside(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(-9.75, 9.75)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [-10.5, -9.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [9.5, 10.5]) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [-10.5, -9.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [9.5, 10.5] + ) + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_negative_aligned_inclusive(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(-10.5, 10.5)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [-11.5, -10.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [10.5, 11.5]) - self.assertEqual(result.data[0, 0, 0], 349) - self.assertEqual(result.data[0, 0, -1], 11) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [-11.5, -10.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [10.5, 11.5] + ) + assert result.data[0, 0, 0] == 349 + assert result.data[0, 0, -1] == 11 def test_negative_aligned_exclusive(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(-10.5, 10.5, False, False)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [-10.5, -9.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [9.5, 10.5]) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [-10.5, -9.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [9.5, 10.5] + ) + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_decrementing(self): cube = create_cube(360, 0, bounds=True) result = cube.intersection(longitude=(40, 60)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [60.5, 59.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [40.5, 39.5]) - self.assertEqual(result.data[0, 0, 0], 300) - self.assertEqual(result.data[0, 0, -1], 320) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [60.5, 59.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [40.5, 39.5] + ) + assert result.data[0, 0, 0] == 300 + assert result.data[0, 0, -1] == 320 def test_decrementing_wrapped(self): cube = create_cube(360, 0, bounds=True) result = cube.intersection(longitude=(-10, 10)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [10.5, 9.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [-9.5, -10.5]) - self.assertEqual(result.data[0, 0, 0], 350) - self.assertEqual(result.data[0, 0, -1], 10) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [10.5, 9.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [-9.5, -10.5] + ) + assert result.data[0, 0, 0] == 350 + assert result.data[0, 0, -1] == 10 def test_numerical_tolerance(self): # test the tolerance on the coordinate value is not causing a @@ -1728,13 +1827,13 @@ def test_numerical_tolerance(self): cube = create_cube(28.5, 68.5, bounds=True) result = cube.intersection(longitude=(27.74, 68.61)) result_lons = result.coord("longitude") - self.assertAlmostEqual(result_lons.points[0], 28.5) - self.assertAlmostEqual(result_lons.points[-1], 67.5) + _shared_utils.assert_array_almost_equal(result_lons.points[0], 28.5) + _shared_utils.assert_array_almost_equal(result_lons.points[-1], 67.5) dtype = result_lons.dtype - np.testing.assert_array_almost_equal( + _shared_utils.assert_array_almost_equal( result_lons.bounds[0], np.array([28.0, 29.0], dtype=dtype) ) - np.testing.assert_array_almost_equal( + _shared_utils.assert_array_almost_equal( result_lons.bounds[-1], np.array([67.0, 68.0], dtype=dtype) ) @@ -1747,13 +1846,13 @@ def test_numerical_tolerance_wrapped(self): lons.bounds = lons.bounds / 10 result = cube.intersection(longitude=(-60, 60)) result_lons = result.coord("longitude") - self.assertAlmostEqual(result_lons.points[0], -60.05) - self.assertAlmostEqual(result_lons.points[-1], 60.05) + _shared_utils.assert_array_almost_equal(result_lons.points[0], -60.05) + _shared_utils.assert_array_almost_equal(result_lons.points[-1], 60.05) dtype = result_lons.dtype - np.testing.assert_array_almost_equal( + _shared_utils.assert_array_almost_equal( result_lons.bounds[0], np.array([-60.1, -60.0], dtype=dtype) ) - np.testing.assert_array_almost_equal( + _shared_utils.assert_array_almost_equal( result_lons.bounds[-1], np.array([60.0, 60.1], dtype=dtype) ) @@ -1762,55 +1861,79 @@ def test_ignore_bounds_wrapped(self): cube = create_cube(0, 360, bounds=True) result = cube.intersection(longitude=(10.25, 370.25), ignore_bounds=True) # Expect points 11..370 not bounds [9.5, 10.5] .. [368.5, 369.5] - self.assertArrayEqual(result.coord("longitude").bounds[0], [10.5, 11.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [369.5, 370.5]) - self.assertEqual(result.data[0, 0, 0], 11) - self.assertEqual(result.data[0, 0, -1], 10) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [10.5, 11.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [369.5, 370.5] + ) + assert result.data[0, 0, 0] == 11 + assert result.data[0, 0, -1] == 10 def test_within_cell(self): # Test cell is included when it entirely contains the requested range cube = create_cube(0, 10, bounds=True) result = cube.intersection(longitude=(0.7, 0.8)) - self.assertArrayEqual(result.coord("longitude").bounds[0], [0.5, 1.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [0.5, 1.5]) - self.assertEqual(result.data[0, 0, 0], 1) - self.assertEqual(result.data[0, 0, -1], 1) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [0.5, 1.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [0.5, 1.5] + ) + assert result.data[0, 0, 0] == 1 + assert result.data[0, 0, -1] == 1 def test_threshold_half(self): cube = create_cube(0, 10, bounds=True) result = cube.intersection(longitude=(1, 6.999), threshold=0.5) - self.assertArrayEqual(result.coord("longitude").bounds[0], [0.5, 1.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [5.5, 6.5]) - self.assertEqual(result.data[0, 0, 0], 1) - self.assertEqual(result.data[0, 0, -1], 6) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [0.5, 1.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [5.5, 6.5] + ) + assert result.data[0, 0, 0] == 1 + assert result.data[0, 0, -1] == 6 def test_threshold_full(self): cube = create_cube(0, 10, bounds=True) result = cube.intersection(longitude=(0.5, 7.499), threshold=1) - self.assertArrayEqual(result.coord("longitude").bounds[0], [0.5, 1.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [5.5, 6.5]) - self.assertEqual(result.data[0, 0, 0], 1) - self.assertEqual(result.data[0, 0, -1], 6) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [0.5, 1.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [5.5, 6.5] + ) + assert result.data[0, 0, 0] == 1 + assert result.data[0, 0, -1] == 6 def test_threshold_wrapped(self): # Test that a cell is wrapped to `maximum` if required to exceed # the threshold cube = create_cube(-180, 180, bounds=True) result = cube.intersection(longitude=(0.4, 360.4), threshold=0.2) - self.assertArrayEqual(result.coord("longitude").bounds[0], [0.5, 1.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [359.5, 360.5]) - self.assertEqual(result.data[0, 0, 0], 181) - self.assertEqual(result.data[0, 0, -1], 180) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [0.5, 1.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [359.5, 360.5] + ) + assert result.data[0, 0, 0] == 181 + assert result.data[0, 0, -1] == 180 def test_threshold_wrapped_gap(self): # Test that a cell is wrapped to `maximum` if required to exceed # the threshold (even with a gap in the range) cube = create_cube(-180, 180, bounds=True) result = cube.intersection(longitude=(0.4, 360.35), threshold=0.2) - self.assertArrayEqual(result.coord("longitude").bounds[0], [0.5, 1.5]) - self.assertArrayEqual(result.coord("longitude").bounds[-1], [359.5, 360.5]) - self.assertEqual(result.data[0, 0, 0], 181) - self.assertEqual(result.data[0, 0, -1], 180) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[0], [0.5, 1.5] + ) + _shared_utils.assert_array_equal( + result.coord("longitude").bounds[-1], [359.5, 360.5] + ) + assert result.data[0, 0, 0] == 181 + assert result.data[0, 0, -1] == 180 def unrolled_cube(): @@ -1828,29 +1951,34 @@ def unrolled_cube(): # Check what happens with a "unrolled" scatter-point data with a circular # intersection coordinate. -class Test_intersection__ScatterModulus(tests.IrisTest): +class Test_intersection__ScatterModulus: def test_subset(self): cube = unrolled_cube() result = cube.intersection(longitude=(5, 8)) - self.assertArrayEqual(result.coord("longitude").points, [5, 8, 5]) - self.assertArrayEqual(result.data, [0, 2, 3]) + _shared_utils.assert_array_equal(result.coord("longitude").points, [5, 8, 5]) + _shared_utils.assert_array_equal(result.data, [0, 2, 3]) def test_subset_wrapped(self): cube = unrolled_cube() result = cube.intersection(longitude=(5 + 360, 8 + 360)) - self.assertArrayEqual(result.coord("longitude").points, [365, 368, 365]) - self.assertArrayEqual(result.data, [0, 2, 3]) + _shared_utils.assert_array_equal( + result.coord("longitude").points, [365, 368, 365] + ) + _shared_utils.assert_array_equal(result.data, [0, 2, 3]) def test_superset(self): cube = unrolled_cube() result = cube.intersection(longitude=(0, 15)) - self.assertArrayEqual(result.coord("longitude").points, [5, 10, 8, 5, 3]) - self.assertArrayEqual(result.data, np.arange(5)) + _shared_utils.assert_array_equal( + result.coord("longitude").points, [5, 10, 8, 5, 3] + ) + _shared_utils.assert_array_equal(result.data, np.arange(5)) # Test the API of the cube interpolation method. -class Test_interpolate(tests.IrisTest): - def setUp(self): +class Test_interpolate: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = stock.simple_2d() self.scheme = mock.Mock(name="interpolation scheme") @@ -1866,10 +1994,10 @@ def test_api(self): self.interpolator.assert_called_once_with( (0.5, 0.6), collapse_scalar=self.collapse_coord ) - self.assertIs(result, mock.sentinel.RESULT) + assert result is mock.sentinel.RESULT -class Test_regrid(tests.IrisTest): +class Test_regrid: def test(self): # Test that Cube.regrid() just defers to the regridder of the # given scheme. @@ -1891,21 +2019,21 @@ def regridder(self, src, target): cube = Cube(0) scheme = FakeScheme() result = cube.regrid(mock.sentinel.TARGET, scheme) - self.assertEqual(result, (scheme, cube, mock.sentinel.TARGET, cube)) + assert result == (scheme, cube, mock.sentinel.TARGET, cube) -class Test_copy(tests.IrisTest): +class Test_copy: def _check_copy(self, cube, cube_copy): - self.assertIsNot(cube_copy, cube) - self.assertEqual(cube_copy, cube) - self.assertIsNot(cube_copy.core_data(), cube.core_data()) + assert cube_copy is not cube + assert cube_copy == cube + assert cube_copy.core_data() is not cube.core_data() if ma.isMaskedArray(cube.data): - self.assertMaskedArrayEqual(cube_copy.data, cube.data) + _shared_utils.assert_masked_array_equal(cube_copy.data, cube.data) if cube.data.mask is not ma.nomask: # "No mask" is a constant : all other cases must be distinct. - self.assertIsNot(cube_copy.core_data().mask, cube.core_data().mask) + assert cube_copy.core_data().mask is not cube.core_data().mask else: - self.assertArrayEqual(cube_copy.data, cube.data) + _shared_utils.assert_array_equal(cube_copy.data, cube.data) def test(self): cube = stock.simple_3d() @@ -1972,7 +2100,7 @@ def _add_test_meshcube(self, nomesh=False, n_z=2, **meshcoord_kwargs): self.cube = cube -class Test_coords__mesh_coords(tests.IrisTest): +class Test_coords__mesh_coords: """Checking *only* the new "mesh_coords" keyword of the coord/coords methods. This is *not* attached to the existing tests for this area, as they are @@ -1980,7 +2108,8 @@ class Test_coords__mesh_coords(tests.IrisTest): """ - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): # Create a standard test cube with a variety of types of coord. _add_test_meshcube(self) @@ -1997,7 +2126,7 @@ def sortkey(item): items_a = sorted(items_a, key=sortkey) items_b = sorted(items_b, key=sortkey) - self.assertEqual(items_a, items_b) + assert items_a == items_b def test_coords__all__meshcoords_default(self): # coords() includes mesh-coords along with the others. @@ -2020,12 +2149,12 @@ def test_coords__all__meshcoords_omitted(self): def test_coords__axis__meshcoords(self): # Coord (singular) with axis + mesh_coords=True result = self.cube.coord(axis="x", mesh_coords=True) - self.assertIs(result, self.meshco_x) + assert result is self.meshco_x def test_coords__dimcoords__meshcoords(self): # dim_coords and mesh_coords should be mutually exclusive. result = self.cube.coords(dim_coords=True, mesh_coords=True) - self.assertEqual(result, []) + assert result == [] def test_coords__nodimcoords__meshcoords(self): # When mesh_coords=True, dim_coords=False should have no effect. @@ -2034,24 +2163,26 @@ def test_coords__nodimcoords__meshcoords(self): self._assert_lists_equal(expected, result) -class Test_mesh(tests.IrisTest): - def setUp(self): +class Test_mesh: + @pytest.fixture(autouse=True) + def _setup(self): # Create a standard test cube with a variety of types of coord. _add_test_meshcube(self) def test_mesh(self): result = self.cube.mesh - self.assertIs(result, self.mesh) + assert result is self.mesh def test_no_mesh(self): # Replace standard setUp cube with a no-mesh version. _add_test_meshcube(self, nomesh=True) result = self.cube.mesh - self.assertIsNone(result) + assert result is None -class Test_location(tests.IrisTest): - def setUp(self): +class Test_location: + @pytest.fixture(autouse=True) + def _setup(self): # Create a standard test cube with a variety of types of coord. _add_test_meshcube(self) @@ -2059,23 +2190,24 @@ def test_no_mesh(self): # Replace standard setUp cube with a no-mesh version. _add_test_meshcube(self, nomesh=True) result = self.cube.location - self.assertIsNone(result) + assert result is None def test_mesh(self): cube = self.cube result = cube.location - self.assertEqual(result, self.meshco_x.location) + assert result == self.meshco_x.location def test_alternate_location(self): # Replace standard setUp cube with an edge-based version. _add_test_meshcube(self, location="edge") cube = self.cube result = cube.location - self.assertEqual(result, "edge") + assert result == "edge" -class Test_mesh_dim(tests.IrisTest): - def setUp(self): +class Test_mesh_dim: + @pytest.fixture(autouse=True) + def _setup(self): # Create a standard test cube with a variety of types of coord. _add_test_meshcube(self) @@ -2083,12 +2215,12 @@ def test_no_mesh(self): # Replace standard setUp cube with a no-mesh version. _add_test_meshcube(self, nomesh=True) result = self.cube.mesh_dim() - self.assertIsNone(result) + assert result is None def test_mesh(self): cube = self.cube result = cube.mesh_dim() - self.assertEqual(result, 1) + assert result == 1 def test_alternate(self): # Replace standard setUp cube with an edge-based version. @@ -2097,16 +2229,17 @@ def test_alternate(self): # Transpose the cube : the mesh dim is then 0 cube.transpose() result = cube.mesh_dim() - self.assertEqual(result, 0) + assert result == 0 -class Test__init__mesh(tests.IrisTest): +class Test__init__mesh: """Test that creation with mesh-coords functions, and prevents a cube having incompatible mesh-coords. """ - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): # Create a standard test mesh and other useful components. mesh = sample_mesh() meshco = sample_meshcoord(mesh=mesh) @@ -2126,7 +2259,7 @@ def test_mesh(self): dim_coords_and_dims=[(dimco_z, 0), (dimco_mesh, 1)], aux_coords_and_dims=[(meshco, 1)], ) - self.assertEqual(cube.mesh, meshco.mesh) + assert cube.mesh == meshco.mesh def test_fail_dim_meshcoord(self): # As "test_mesh", but attempt to use the meshcoord as a dim-coord. @@ -2134,7 +2267,7 @@ def test_fail_dim_meshcoord(self): nz, n_faces = self.nz, self.n_faces dimco_z = DimCoord(np.arange(nz), long_name="z") meshco = self.meshco - with self.assertRaisesRegex(ValueError, "may not be an AuxCoord"): + with pytest.raises(ValueError, match="may not be an AuxCoord"): Cube( np.zeros((nz, n_faces)), dim_coords_and_dims=[(dimco_z, 0), (meshco, 1)], @@ -2148,7 +2281,7 @@ def test_multi_meshcoords(self): np.zeros(n_faces), aux_coords_and_dims=[(meshco_x, 0), (meshco_y, 0)], ) - self.assertEqual(cube.mesh, meshco_x.mesh) + assert cube.mesh == meshco_x.mesh def test_multi_meshcoords_same_axis(self): # *Not* an error, as long as the coords are distinguishable. @@ -2163,7 +2296,7 @@ def test_multi_meshcoords_same_axis(self): np.zeros(n_faces), aux_coords_and_dims=[(meshco_1, 0), (meshco_2, 0)], ) - self.assertEqual(cube.mesh, meshco_1.mesh) + assert cube.mesh == meshco_1.mesh def test_fail_meshcoords_different_locations(self): # Same as successful 'multi_mesh', but different locations. @@ -2172,10 +2305,10 @@ def test_fail_meshcoords_different_locations(self): meshco_1 = sample_meshcoord(axis="x", mesh=mesh, location="face") meshco_2 = sample_meshcoord(axis="y", mesh=mesh, location="edge") # They should still have the same *shape* (or would fail anyway) - self.assertEqual(meshco_1.shape, meshco_2.shape) + assert meshco_1.shape == meshco_2.shape n_faces = meshco_1.shape[0] msg = "does not match existing cube location" - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): Cube( np.zeros(n_faces), aux_coords_and_dims=[(meshco_1, 0), (meshco_2, 0)], @@ -2195,7 +2328,7 @@ def test_fail_meshcoords_different_meshes(self): meshco_y = sample_meshcoord(axis="y") # Own (different) mesh meshco_y.mesh.long_name = "new_name" n_faces = meshco_x.shape[0] - with self.assertRaisesRegex(ValueError, "Mesh.* does not match"): + with pytest.raises(ValueError, match="Mesh.* does not match"): Cube( np.zeros(n_faces), aux_coords_and_dims=[(meshco_x, 0), (meshco_y, 0)], @@ -2209,20 +2342,21 @@ def test_fail_meshcoords_different_dims(self): meshco_x = sample_meshcoord(mesh=mesh, axis="x") meshco_y = sample_meshcoord(mesh=mesh, axis="y") msg = "does not match existing cube mesh dimension" - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): Cube( np.zeros((n_z, n_faces)), aux_coords_and_dims=[(meshco_x, 1), (meshco_y, 0)], ) -class Test__add_aux_coord__mesh(tests.IrisTest): +class Test__add_aux_coord__mesh: """Test that "Cube.add_aux_coord" functions with a mesh-coord, and prevents a cube having incompatible mesh-coords. """ - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): _add_test_meshcube(self) # Remove the existing "meshco_y", so we can add similar ones without # needing to distinguish from the existing. @@ -2233,7 +2367,7 @@ def test_add_compatible(self): meshco_y = self.meshco_y # Add the y-meshco back into the cube. cube.add_aux_coord(meshco_y, 1) - self.assertIn(meshco_y, cube.coords(mesh_coords=True)) + assert meshco_y in cube.coords(mesh_coords=True) def test_add_multiple(self): # Show that we can add extra mesh coords. @@ -2245,7 +2379,7 @@ def test_add_multiple(self): new_meshco_y = meshco_y.copy() new_meshco_y.rename("alternative") cube.add_aux_coord(new_meshco_y, 1) - self.assertEqual(len(cube.coords(mesh_coords=True)), 3) + assert len(cube.coords(mesh_coords=True)) == 3 def test_add_equal_mesh(self): # Make a duplicate y-meshco, and rename so it can add into the cube. @@ -2253,7 +2387,7 @@ def test_add_equal_mesh(self): # Create 'meshco_y' duplicate, but a new mesh meshco_y = sample_meshcoord(axis="y") cube.add_aux_coord(meshco_y, 1) - self.assertIn(meshco_y, cube.coords(mesh_coords=True)) + assert meshco_y in cube.coords(mesh_coords=True) def test_fail_different_mesh(self): # Make a duplicate y-meshco, and rename so it can add into the cube. @@ -2262,7 +2396,7 @@ def test_fail_different_mesh(self): meshco_y = sample_meshcoord(axis="y") meshco_y.mesh.long_name = "new_name" msg = "does not match existing cube mesh" - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): cube.add_aux_coord(meshco_y, 1) def test_fail_different_location(self): @@ -2275,7 +2409,7 @@ def test_fail_different_location(self): # Create a new meshco_y, same mesh but based on edges. meshco_y = sample_meshcoord(axis="y", mesh=self.mesh, location="edge") msg = "does not match existing cube location" - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): cube.add_aux_coord(meshco_y, 1) def test_fail_different_dimension(self): @@ -2288,11 +2422,11 @@ def test_fail_different_dimension(self): # Attempt to re-attach the 'y' meshcoord, to a different cube dimension. msg = "does not match existing cube mesh dimension" - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): cube.add_aux_coord(meshco_y, 0) -class Test__add_dim_coord__mesh(tests.IrisTest): +class Test__add_dim_coord__mesh: """Test that "Cube.add_dim_coord" cannot work with a mesh-coord.""" def test(self): @@ -2300,11 +2434,11 @@ def test(self): mesh = sample_mesh(n_faces=2) meshco = sample_meshcoord(mesh=mesh) cube = Cube([0, 1]) - with self.assertRaisesRegex(ValueError, "may not be an AuxCoord"): + with pytest.raises(ValueError, match="may not be an AuxCoord"): cube.add_dim_coord(meshco, 0) -class Test__eq__mesh(tests.IrisTest): +class Test__eq__mesh: """Check that cubes with meshes support == as expected. Note: there is no special code for this in iris.cube.Cube : it is @@ -2312,21 +2446,22 @@ class Test__eq__mesh(tests.IrisTest): """ - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): # Create a 'standard' test cube. _add_test_meshcube(self) def test_copied_cube_match(self): cube = self.cube cube2 = cube.copy() - self.assertEqual(cube, cube2) + assert cube == cube2 def test_equal_mesh_match(self): cube1 = self.cube # re-create an identical cube, using the same mesh. _add_test_meshcube(self) cube2 = self.cube - self.assertEqual(cube1, cube2) + assert cube1 == cube2 def test_new_mesh_different(self): cube1 = self.cube @@ -2334,11 +2469,12 @@ def test_new_mesh_different(self): _add_test_meshcube(self) self.cube.mesh.long_name = "new_name" cube2 = self.cube - self.assertNotEqual(cube1, cube2) + assert cube1 != cube2 -class Test_dtype(tests.IrisTest): - def setUp(self): +class Test_dtype: + @pytest.fixture(autouse=True) + def _setup(self): self.dtypes = ( np.dtype("int"), np.dtype("uint"), @@ -2350,56 +2486,56 @@ def test_real_data(self): for dtype in self.dtypes: data = np.array([0, 1], dtype=dtype) cube = Cube(data) - self.assertEqual(cube.dtype, dtype) + assert cube.dtype == dtype def test_real_data_masked__mask_unset(self): for dtype in self.dtypes: data = ma.array([0, 1], dtype=dtype) cube = Cube(data) - self.assertEqual(cube.dtype, dtype) + assert cube.dtype == dtype def test_real_data_masked__mask_set(self): for dtype in self.dtypes: data = ma.array([0, 1], dtype=dtype) data[0] = ma.masked cube = Cube(data) - self.assertEqual(cube.dtype, dtype) + assert cube.dtype == dtype def test_lazy_data(self): for dtype in self.dtypes: data = np.array([0, 1], dtype=dtype) cube = Cube(as_lazy_data(data)) - self.assertEqual(cube.dtype, dtype) + assert cube.dtype == dtype # Check that accessing the dtype does not trigger loading # of the data. - self.assertTrue(cube.has_lazy_data()) + assert cube.has_lazy_data() def test_lazy_data_masked__mask_unset(self): for dtype in self.dtypes: data = ma.array([0, 1], dtype=dtype) cube = Cube(as_lazy_data(data)) - self.assertEqual(cube.dtype, dtype) + assert cube.dtype == dtype # Check that accessing the dtype does not trigger loading # of the data. - self.assertTrue(cube.has_lazy_data()) + assert cube.has_lazy_data() def test_lazy_data_masked__mask_set(self): for dtype in self.dtypes: data = ma.array([0, 1], dtype=dtype) data[0] = ma.masked cube = Cube(as_lazy_data(data)) - self.assertEqual(cube.dtype, dtype) + assert cube.dtype == dtype # Check that accessing the dtype does not trigger loading # of the data. - self.assertTrue(cube.has_lazy_data()) + assert cube.has_lazy_data() -class TestSubset(tests.IrisTest): +class TestSubset: def test_scalar_coordinate(self): cube = Cube(0, long_name="apricot", units="1") cube.add_aux_coord(DimCoord([0], long_name="banana", units="1")) result = cube.subset(cube.coord("banana")) - self.assertEqual(cube, result) + assert cube == result def test_dimensional_coordinate(self): cube = Cube(np.zeros((4)), long_name="tinned_peach", units="1") @@ -2408,52 +2544,52 @@ def test_dimensional_coordinate(self): 0, ) result = cube.subset(cube.coord("sixteen_ton_weight")) - self.assertEqual(cube, result) + assert cube == result def test_missing_coordinate(self): cube = Cube(0, long_name="raspberry", units="1") cube.add_aux_coord(DimCoord([0], long_name="loganberry", units="1")) bad_coord = DimCoord([0], long_name="tiger", units="1") - self.assertRaises(CoordinateNotFoundError, cube.subset, bad_coord) + pytest.raises(CoordinateNotFoundError, cube.subset, bad_coord) def test_different_coordinate(self): cube = Cube(0, long_name="raspberry", units="1") cube.add_aux_coord(DimCoord([0], long_name="loganberry", units="1")) different_coord = DimCoord([2], long_name="loganberry", units="1") result = cube.subset(different_coord) - self.assertEqual(result, None) + assert result is None def test_different_coordinate_vector(self): cube = Cube([0, 1], long_name="raspberry", units="1") cube.add_dim_coord(DimCoord([0, 1], long_name="loganberry", units="1"), 0) different_coord = DimCoord([2], long_name="loganberry", units="1") result = cube.subset(different_coord) - self.assertEqual(result, None) + assert result is None def test_not_coordinate(self): cube = Cube(0, long_name="peach", units="1") cube.add_aux_coord(DimCoord([0], long_name="crocodile", units="1")) - self.assertRaises(ValueError, cube.subset, "Pointed Stick") + pytest.raises(ValueError, cube.subset, "Pointed Stick") -class Test_add_metadata(tests.IrisTest): +class Test_add_metadata: def test_add_dim_coord(self): cube = Cube(np.arange(3)) x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") cube.add_dim_coord(x_coord, 0) - self.assertEqual(cube.coord("x"), x_coord) + assert cube.coord("x") == x_coord def test_add_aux_coord(self): cube = Cube(np.arange(6).reshape(2, 3)) x_coord = AuxCoord(points=np.arange(6).reshape(2, 3), long_name="x") cube.add_aux_coord(x_coord, [0, 1]) - self.assertEqual(cube.coord("x"), x_coord) + assert cube.coord("x") == x_coord def test_add_cell_measure(self): cube = Cube(np.arange(6).reshape(2, 3)) a_cell_measure = CellMeasure(np.arange(6).reshape(2, 3), long_name="area") cube.add_cell_measure(a_cell_measure, [0, 1]) - self.assertEqual(cube.cell_measure("area"), a_cell_measure) + assert cube.cell_measure("area") == a_cell_measure def test_add_ancillary_variable(self): cube = Cube(np.arange(6).reshape(2, 3)) @@ -2461,9 +2597,7 @@ def test_add_ancillary_variable(self): data=np.arange(6).reshape(2, 3), long_name="detection quality" ) cube.add_ancillary_variable(ancillary_variable, [0, 1]) - self.assertEqual( - cube.ancillary_variable("detection quality"), ancillary_variable - ) + assert cube.ancillary_variable("detection quality") == ancillary_variable def test_add_valid_aux_factory(self): cube = Cube(np.arange(8).reshape(2, 2, 2)) @@ -2474,7 +2608,7 @@ def test_add_valid_aux_factory(self): cube.add_aux_coord(sigma, 0) cube.add_aux_coord(orog, (1, 2)) factory = HybridHeightFactory(delta=delta, sigma=sigma, orography=orog) - self.assertIsNone(cube.add_aux_factory(factory)) + assert cube.add_aux_factory(factory) is None def test_error_for_add_invalid_aux_factory(self): cube = Cube(np.arange(8).reshape(2, 2, 2), long_name="bar") @@ -2486,12 +2620,13 @@ def test_error_for_add_invalid_aux_factory(self): # Note orography is not added to the cube here factory = HybridHeightFactory(delta=delta, sigma=sigma, orography=orog) expected_error = "foo coordinate for factory is not present on cube bar" - with self.assertRaisesRegex(ValueError, expected_error): + with pytest.raises(ValueError, match=expected_error): cube.add_aux_factory(factory) -class Test_remove_metadata(tests.IrisTest): - def setUp(self): +class Test_remove_metadata: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(np.arange(6).reshape(2, 3)) x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") cube.add_dim_coord(x_coord, 1) @@ -2511,45 +2646,42 @@ def setUp(self): def test_remove_dim_coord(self): self.cube.remove_coord(self.cube.coord("x")) - self.assertEqual(self.cube.coords("x"), []) + assert self.cube.coords("x") == [] def test_remove_aux_coord(self): self.cube.remove_coord(self.cube.coord("z")) - self.assertEqual(self.cube.coords("z"), []) + assert self.cube.coords("z") == [] def test_remove_cell_measure(self): self.cube.remove_cell_measure(self.cube.cell_measure("area")) - self.assertEqual( - self.cube._cell_measures_and_dims, [(self.b_cell_measure, (0, 1))] - ) + assert self.cube._cell_measures_and_dims == [(self.b_cell_measure, (0, 1))] def test_remove_cell_measure_by_name(self): self.cube.remove_cell_measure("area") - self.assertEqual( - self.cube._cell_measures_and_dims, [(self.b_cell_measure, (0, 1))] - ) + assert self.cube._cell_measures_and_dims == [(self.b_cell_measure, (0, 1))] def test_fail_remove_cell_measure_by_name(self): - with self.assertRaises(CellMeasureNotFoundError): + with pytest.raises(CellMeasureNotFoundError): self.cube.remove_cell_measure("notarea") def test_remove_ancilliary_variable(self): self.cube.remove_ancillary_variable( self.cube.ancillary_variable("Quality of Detection") ) - self.assertEqual(self.cube._ancillary_variables_and_dims, []) + assert self.cube._ancillary_variables_and_dims == [] def test_remove_ancilliary_variable_by_name(self): self.cube.remove_ancillary_variable("Quality of Detection") - self.assertEqual(self.cube._ancillary_variables_and_dims, []) + assert self.cube._ancillary_variables_and_dims == [] def test_fail_remove_ancilliary_variable_by_name(self): - with self.assertRaises(AncillaryVariableNotFoundError): + with pytest.raises(AncillaryVariableNotFoundError): self.cube.remove_ancillary_variable("notname") -class TestCoords(tests.IrisTest): - def setUp(self): +class TestCoords: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(np.arange(6).reshape(2, 3)) x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") cube.add_dim_coord(x_coord, 1) @@ -2563,19 +2695,20 @@ def test_bad_coord(self): "Expected to find exactly 1 coordinate matching the given " "'x' coordinate's metadata, but found none." ) - with self.assertRaisesRegex(CoordinateNotFoundError, re): + with pytest.raises(CoordinateNotFoundError, match=re): _ = self.cube.coord(bad_coord) -class Test_coord_division_units(tests.IrisTest): +class Test_coord_division_units: def test(self): aux = AuxCoord(1, long_name="length", units="metres") cube = Cube(1, units="seconds") - self.assertEqual((aux / cube).units, "m.s-1") + assert (aux / cube).units == "m.s-1" -class Test__getitem_CellMeasure(tests.IrisTest): - def setUp(self): +class Test__getitem_CellMeasure: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(np.arange(6).reshape(2, 3)) x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") cube.add_dim_coord(x_coord, 1) @@ -2589,17 +2722,18 @@ def setUp(self): def test_cell_measure_2d(self): result = self.cube[0:2, 0:2] - self.assertEqual(len(result.cell_measures()), 1) - self.assertEqual(result.shape, result.cell_measures()[0].data.shape) + assert len(result.cell_measures()) == 1 + assert result.shape == result.cell_measures()[0].data.shape def test_cell_measure_1d(self): result = self.cube[0, 0:2] - self.assertEqual(len(result.cell_measures()), 1) - self.assertEqual(result.shape, result.cell_measures()[0].data.shape) + assert len(result.cell_measures()) == 1 + assert result.shape == result.cell_measures()[0].data.shape -class Test__getitem_AncillaryVariables(tests.IrisTest): - def setUp(self): +class Test__getitem_AncillaryVariables: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(np.arange(6).reshape(2, 3)) x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") cube.add_dim_coord(x_coord, 1) @@ -2615,17 +2749,18 @@ def setUp(self): def test_ancillary_variables_2d(self): result = self.cube[0:2, 0:2] - self.assertEqual(len(result.ancillary_variables()), 1) - self.assertEqual(result.shape, result.ancillary_variables()[0].data.shape) + assert len(result.ancillary_variables()) == 1 + assert result.shape == result.ancillary_variables()[0].data.shape def test_ancillary_variables_1d(self): result = self.cube[0, 0:2] - self.assertEqual(len(result.ancillary_variables()), 1) - self.assertEqual(result.shape, result.ancillary_variables()[0].data.shape) + assert len(result.ancillary_variables()) == 1 + assert result.shape == result.ancillary_variables()[0].data.shape -class TestAncillaryVariables(tests.IrisTest): - def setUp(self): +class TestAncillaryVariables: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(10 * np.arange(6).reshape(2, 3)) self.ancill_var = AncillaryVariable( np.arange(6).reshape(2, 3), @@ -2637,49 +2772,50 @@ def setUp(self): def test_get_ancillary_variable(self): ancill_var = self.cube.ancillary_variable("number_of_observations") - self.assertEqual(ancill_var, self.ancill_var) + assert ancill_var == self.ancill_var def test_get_ancillary_variables(self): ancill_vars = self.cube.ancillary_variables("number_of_observations") - self.assertEqual(len(ancill_vars), 1) - self.assertEqual(ancill_vars[0], self.ancill_var) + assert len(ancill_vars) == 1 + assert ancill_vars[0] == self.ancill_var def test_get_ancillary_variable_obj(self): ancill_vars = self.cube.ancillary_variables(self.ancill_var) - self.assertEqual(len(ancill_vars), 1) - self.assertEqual(ancill_vars[0], self.ancill_var) + assert len(ancill_vars) == 1 + assert ancill_vars[0] == self.ancill_var def test_fail_get_ancillary_variables(self): - with self.assertRaises(AncillaryVariableNotFoundError): + with pytest.raises(AncillaryVariableNotFoundError): self.cube.ancillary_variable("other_ancill_var") def test_fail_get_ancillary_variables_obj(self): ancillary_variable = self.ancill_var.copy() ancillary_variable.long_name = "Number of observations at site" - with self.assertRaises(AncillaryVariableNotFoundError): + with pytest.raises(AncillaryVariableNotFoundError): self.cube.ancillary_variable(ancillary_variable) def test_ancillary_variable_dims(self): ancill_var_dims = self.cube.ancillary_variable_dims(self.ancill_var) - self.assertEqual(ancill_var_dims, (0, 1)) + assert ancill_var_dims == (0, 1) def test_fail_ancill_variable_dims(self): ancillary_variable = self.ancill_var.copy() ancillary_variable.long_name = "Number of observations at site" - with self.assertRaises(AncillaryVariableNotFoundError): + with pytest.raises(AncillaryVariableNotFoundError): self.cube.ancillary_variable_dims(ancillary_variable) def test_ancillary_variable_dims_by_name(self): ancill_var_dims = self.cube.ancillary_variable_dims("number_of_observations") - self.assertEqual(ancill_var_dims, (0, 1)) + assert ancill_var_dims == (0, 1) def test_fail_ancillary_variable_dims_by_name(self): - with self.assertRaises(AncillaryVariableNotFoundError): + with pytest.raises(AncillaryVariableNotFoundError): self.cube.ancillary_variable_dims("notname") -class TestCellMeasures(tests.IrisTest): - def setUp(self): +class TestCellMeasures: + @pytest.fixture(autouse=True) + def _setup(self): cube = Cube(np.arange(6).reshape(2, 3)) x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") cube.add_dim_coord(x_coord, 1) @@ -2693,49 +2829,50 @@ def setUp(self): def test_get_cell_measure(self): cm = self.cube.cell_measure("area") - self.assertEqual(cm, self.a_cell_measure) + assert cm == self.a_cell_measure def test_get_cell_measures(self): cms = self.cube.cell_measures() - self.assertEqual(len(cms), 1) - self.assertEqual(cms[0], self.a_cell_measure) + assert len(cms) == 1 + assert cms[0] == self.a_cell_measure def test_get_cell_measures_obj(self): cms = self.cube.cell_measures(self.a_cell_measure) - self.assertEqual(len(cms), 1) - self.assertEqual(cms[0], self.a_cell_measure) + assert len(cms) == 1 + assert cms[0] == self.a_cell_measure def test_fail_get_cell_measure(self): - with self.assertRaises(CellMeasureNotFoundError): + with pytest.raises(CellMeasureNotFoundError): _ = self.cube.cell_measure("notarea") def test_fail_get_cell_measures_obj(self): a_cell_measure = self.a_cell_measure.copy() a_cell_measure.units = "km2" - with self.assertRaises(CellMeasureNotFoundError): + with pytest.raises(CellMeasureNotFoundError): _ = self.cube.cell_measure(a_cell_measure) def test_cell_measure_dims(self): cm_dims = self.cube.cell_measure_dims(self.a_cell_measure) - self.assertEqual(cm_dims, (0, 1)) + assert cm_dims == (0, 1) def test_fail_cell_measure_dims(self): a_cell_measure = self.a_cell_measure.copy() a_cell_measure.units = "km2" - with self.assertRaises(CellMeasureNotFoundError): + with pytest.raises(CellMeasureNotFoundError): _ = self.cube.cell_measure_dims(a_cell_measure) def test_cell_measure_dims_by_name(self): cm_dims = self.cube.cell_measure_dims("area") - self.assertEqual(cm_dims, (0, 1)) + assert cm_dims == (0, 1) def test_fail_cell_measure_dims_by_name(self): - with self.assertRaises(CellMeasureNotFoundError): + with pytest.raises(CellMeasureNotFoundError): self.cube.cell_measure_dims("notname") -class Test_transpose(tests.IrisTest): - def setUp(self): +class Test_transpose: + @pytest.fixture(autouse=True) + def _setup(self): self.data = np.arange(24).reshape(3, 2, 4) self.cube = Cube(self.data) self.lazy_cube = Cube(as_lazy_data(self.data)) @@ -2743,28 +2880,28 @@ def setUp(self): def test_lazy_data(self): cube = self.lazy_cube cube.transpose() - self.assertTrue(cube.has_lazy_data()) - self.assertArrayEqual(self.data.T, cube.data) + assert cube.has_lazy_data() + _shared_utils.assert_array_equal(self.data.T, cube.data) def test_real_data(self): self.cube.transpose() - self.assertFalse(self.cube.has_lazy_data()) - self.assertIs(self.data.base, self.cube.data.base) - self.assertArrayEqual(self.data.T, self.cube.data) + assert not self.cube.has_lazy_data() + assert self.data.base is self.cube.data.base + _shared_utils.assert_array_equal(self.data.T, self.cube.data) def test_real_data__new_order(self): new_order = [2, 0, 1] self.cube.transpose(new_order) - self.assertFalse(self.cube.has_lazy_data()) - self.assertIs(self.data.base, self.cube.data.base) - self.assertArrayEqual(self.data.transpose(new_order), self.cube.data) + assert not self.cube.has_lazy_data() + assert self.data.base is self.cube.data.base + _shared_utils.assert_array_equal(self.data.transpose(new_order), self.cube.data) def test_lazy_data__new_order(self): new_order = [2, 0, 1] cube = self.lazy_cube cube.transpose(new_order) - self.assertTrue(cube.has_lazy_data()) - self.assertArrayEqual(self.data.transpose(new_order), cube.data) + assert cube.has_lazy_data() + _shared_utils.assert_array_equal(self.data.transpose(new_order), cube.data) def test_lazy_data__transpose_order_ndarray(self): # Check that a transpose order supplied as an array does not trip up @@ -2772,31 +2909,31 @@ def test_lazy_data__transpose_order_ndarray(self): new_order = np.array([2, 0, 1]) cube = self.lazy_cube cube.transpose(new_order) - self.assertTrue(cube.has_lazy_data()) - self.assertArrayEqual(self.data.transpose(new_order), cube.data) + assert cube.has_lazy_data() + _shared_utils.assert_array_equal(self.data.transpose(new_order), cube.data) def test_bad_transpose_order(self): exp_emsg = "Incorrect number of dimensions" - with self.assertRaisesRegex(ValueError, exp_emsg): + with pytest.raises(ValueError, match=exp_emsg): self.cube.transpose([1]) def test_dim_coords(self): x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x") self.cube.add_dim_coord(x_coord, 0) self.cube.transpose() - self.assertEqual(self.cube._dim_coords_and_dims, [(x_coord, 2)]) + assert self.cube._dim_coords_and_dims == [(x_coord, 2)] def test_aux_coords(self): x_coord = AuxCoord(points=np.array([[2, 3], [8, 4], [7, 9]]), long_name="x") self.cube.add_aux_coord(x_coord, (0, 1)) self.cube.transpose() - self.assertEqual(self.cube._aux_coords_and_dims, [(x_coord, (2, 1))]) + assert self.cube._aux_coords_and_dims == [(x_coord, (2, 1))] def test_cell_measures(self): area_cm = CellMeasure(np.arange(12).reshape(3, 4), long_name="area of cells") self.cube.add_cell_measure(area_cm, (0, 2)) self.cube.transpose() - self.assertEqual(self.cube._cell_measures_and_dims, [(area_cm, (2, 0))]) + assert self.cube._cell_measures_and_dims == [(area_cm, (2, 0))] def test_ancillary_variables(self): ancill_var = AncillaryVariable( @@ -2804,19 +2941,17 @@ def test_ancillary_variables(self): ) self.cube.add_ancillary_variable(ancill_var, (1, 2)) self.cube.transpose() - self.assertEqual( - self.cube._ancillary_variables_and_dims, [(ancill_var, (1, 0))] - ) + assert self.cube._ancillary_variables_and_dims == [(ancill_var, (1, 0))] -class Test_convert_units(tests.IrisTest): +class Test_convert_units: def test_convert_unknown_units(self): cube = iris.cube.Cube(1) emsg = ( "Cannot convert from unknown units. " 'The "cube.units" attribute may be set directly.' ) - with self.assertRaisesRegex(UnitConversionError, emsg): + with pytest.raises(UnitConversionError, match=emsg): cube.convert_units("mm day-1") def test_preserves_lazy(self): @@ -2825,8 +2960,8 @@ def test_preserves_lazy(self): cube = iris.cube.Cube(lazy_data, units="m") real_data_ft = Unit("m").convert(real_data, "ft") cube.convert_units("ft") - self.assertTrue(cube.has_lazy_data()) - self.assertArrayAllClose(cube.data, real_data_ft) + assert cube.has_lazy_data() + _shared_utils.assert_array_all_close(cube.data, real_data_ft) def test_unit_multiply(self): _client = Client() @@ -2837,22 +2972,22 @@ def test_unit_multiply(self): _client.close() -class Test__eq__data(tests.IrisTest): +class Test__eq__data: """Partial cube equality testing, for data type only.""" def test_cube_identical_to_itself(self): cube = Cube([1.0]) - self.assertTrue(cube == cube) + assert cube == cube def test_data_float_eq(self): cube1 = Cube([1.0]) cube2 = Cube([1.0]) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 def test_data_float_nan_eq(self): cube1 = Cube([np.nan, 1.0]) cube2 = Cube([np.nan, 1.0]) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 def test_data_float_eqtol(self): val1 = np.array(1.0, dtype=np.float32) @@ -2861,45 +2996,45 @@ def test_data_float_eqtol(self): val2 = np.array(1.0 + 1.0e-6, dtype=np.float32) cube1 = Cube([val1]) cube2 = Cube([val2]) - self.assertNotEqual(val1, val2) - self.assertTrue(cube1 == cube2) + assert val1 != val2 + assert cube1 == cube2 def test_data_float_not_eq(self): val1 = 1.0 val2 = 1.0 + 1.0e-4 cube1 = Cube([1.0, val1]) cube2 = Cube([1.0, val2]) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 def test_data_int_eq(self): cube1 = Cube([1, 2, 3]) cube2 = Cube([1, 2, 3]) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 def test_data_int_not_eq(self): cube1 = Cube([1, 2, 3]) cube2 = Cube([1, 2, 0]) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 # NOTE: since numpy v1.18, boolean array subtract is deprecated. def test_data_bool_eq(self): cube1 = Cube([True, False]) cube2 = Cube([True, False]) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 def test_data_bool_not_eq(self): cube1 = Cube([True, False]) cube2 = Cube([True, True]) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 -class Test__eq__meta(tests.IrisTest): +class Test__eq__meta: def test_ancillary_fail(self): cube1 = Cube([0, 1]) cube2 = Cube([0, 1]) avr = AncillaryVariable([2, 3], long_name="foo") cube2.add_ancillary_variable(avr, 0) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 def test_ancillary_reorder(self): cube1 = Cube([0, 1]) @@ -2912,7 +3047,7 @@ def test_ancillary_reorder(self): cube1.add_ancillary_variable(avr2, 0) cube2.add_ancillary_variable(avr2, 0) cube2.add_ancillary_variable(avr1, 0) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 def test_ancillary_diff_data(self): cube1 = Cube([0, 1]) @@ -2921,14 +3056,14 @@ def test_ancillary_diff_data(self): avr2 = AncillaryVariable([4, 5], long_name="foo") cube1.add_ancillary_variable(avr1, 0) cube2.add_ancillary_variable(avr2, 0) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 def test_cell_measure_fail(self): cube1 = Cube([0, 1]) cube2 = Cube([0, 1]) cms = CellMeasure([2, 3], long_name="foo") cube2.add_cell_measure(cms, 0) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 def test_cell_measure_reorder(self): cube1 = Cube([0, 1]) @@ -2941,7 +3076,7 @@ def test_cell_measure_reorder(self): cube1.add_cell_measure(cms2, 0) cube2.add_cell_measure(cms2, 0) cube2.add_cell_measure(cms1, 0) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 def test_cell_measure_diff_data(self): cube1 = Cube([0, 1]) @@ -2950,14 +3085,14 @@ def test_cell_measure_diff_data(self): cms2 = CellMeasure([4, 5], long_name="foo") cube1.add_cell_measure(cms1, 0) cube2.add_cell_measure(cms2, 0) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 def test_cell_method_fail(self): cube1 = Cube([0, 1]) cube2 = Cube([0, 1]) cmth = CellMethod("mean", "time", "6hr") cube2.add_cell_method(cmth) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 # Unlike cell measures, cell methods are order sensitive. def test_cell_method_reorder_fail(self): @@ -2971,7 +3106,7 @@ def test_cell_method_reorder_fail(self): cube1.add_cell_method(cmth2) cube2.add_cell_method(cmth2) cube2.add_cell_method(cmth1) - self.assertFalse(cube1 == cube2) + assert cube1 != cube2 def test_cell_method_correct_order(self): cube1 = Cube([0, 1]) @@ -2984,10 +3119,10 @@ def test_cell_method_correct_order(self): cube1.add_cell_method(cmth2) cube2.add_cell_method(cmth1) cube2.add_cell_method(cmth2) - self.assertTrue(cube1 == cube2) + assert cube1 == cube2 -@pytest.fixture +@pytest.fixture() def simplecube(): return stock.simple_2d_w_cell_measure_ancil_var() @@ -3065,14 +3200,14 @@ class TestReprs: """ # Note: logically this could be a staticmethod, but that seems to upset Pytest - @pytest.fixture - def patched_cubeprinter(self): + @pytest.fixture() + def patched_cubeprinter(self, mocker): target = "iris._representation.cube_printout.CubePrinter" instance_mock = mock.MagicMock( to_string=mock.MagicMock(return_value="") # NB this must return a string ) - with mock.patch(target, return_value=instance_mock) as class_mock: - yield class_mock, instance_mock + class_mock = mocker.patch(target, return_value=instance_mock) + yield class_mock, instance_mock @staticmethod def _check_expected_effects(simplecube, patched_cubeprinter, oneline, padding): @@ -3122,14 +3257,14 @@ class TestHtmlRepr: """ # Note: logically this could be a staticmethod, but that seems to upset Pytest - @pytest.fixture - def patched_cubehtml(self): + @pytest.fixture() + def patched_cubehtml(self, mocker): target = "iris.experimental.representation.CubeRepresentation" instance_mock = mock.MagicMock( repr_html=mock.MagicMock(return_value="") # NB this must return a string ) - with mock.patch(target, return_value=instance_mock) as class_mock: - yield class_mock, instance_mock + class_mock = mocker.patch(target, return_value=instance_mock) + yield class_mock, instance_mock @staticmethod def test__repr_html__effects(simplecube, patched_cubehtml): @@ -3148,7 +3283,7 @@ def test__repr_html__effects(simplecube, patched_cubehtml): class Test__cell_methods: @pytest.fixture(autouse=True) - def cell_measures_testdata(self): + def _setup(self): self.cube = Cube([0]) self.cm = CellMethod("mean", "time", "6hr") self.cm2 = CellMethod("max", "latitude", "4hr") diff --git a/lib/iris/tests/unit/cube/test_CubeAttrsDict.py b/lib/iris/tests/unit/cube/test_CubeAttrsDict.py index 50de4541e0..4ba65913c6 100644 --- a/lib/iris/tests/unit/cube/test_CubeAttrsDict.py +++ b/lib/iris/tests/unit/cube/test_CubeAttrsDict.py @@ -14,7 +14,7 @@ from iris.fileformats.netcdf.saver import _CF_GLOBAL_ATTRS -@pytest.fixture +@pytest.fixture() def sample_attrs() -> CubeAttrsDict: return CubeAttrsDict(locals={"a": 1, "z": "this"}, globals={"b": 2, "z": "that"}) @@ -367,10 +367,10 @@ def test_local_global_masking(self, sample_attrs): sample_attrs.globals["z"] == "other" assert sample_attrs["z"] == "new" - @pytest.mark.parametrize("globals_or_locals", ("globals", "locals")) + @pytest.mark.parametrize("globals_or_locals", ["globals", "locals"]) @pytest.mark.parametrize( "value_type", - ("replace", "emptylist", "emptytuple", "none", "zero", "false"), + ["replace", "emptylist", "emptytuple", "none", "zero", "false"], ) def test_replace_subdict(self, globals_or_locals, value_type): # Writing to attrs.xx always replaces content with a *new* LimitedAttributeDict diff --git a/lib/iris/tests/unit/cube/test_CubeList.py b/lib/iris/tests/unit/cube/test_CubeList.py index 72ca7d2306..b9cfd23e51 100644 --- a/lib/iris/tests/unit/cube/test_CubeList.py +++ b/lib/iris/tests/unit/cube/test_CubeList.py @@ -3,17 +3,13 @@ # 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.cube.CubeList` class.""" - -# Import iris.tests first so that some things can be initialised before -# importing anything else. -import iris.tests as tests # isort:skip - import collections import copy from unittest import mock from cf_units import Unit import numpy as np +import pytest from iris import Constraint import iris.coord_systems @@ -21,31 +17,34 @@ from iris.cube import Cube, CubeList import iris.exceptions from iris.fileformats.pp import STASH +from iris.tests import _shared_utils import iris.tests.stock NOT_CUBE_MSG = "cannot be put in a cubelist, as it is not a Cube." NON_ITERABLE_MSG = "object is not iterable" -class Test_append(tests.IrisTest): - def setUp(self): +class Test_append: + @pytest.fixture(autouse=True) + def _setup(self): self.cubelist = iris.cube.CubeList() self.cube1 = iris.cube.Cube(1, long_name="foo") self.cube2 = iris.cube.Cube(1, long_name="bar") def test_pass(self): self.cubelist.append(self.cube1) - self.assertEqual(self.cubelist[-1], self.cube1) + assert self.cubelist[-1] == self.cube1 self.cubelist.append(self.cube2) - self.assertEqual(self.cubelist[-1], self.cube2) + assert self.cubelist[-1] == self.cube2 def test_fail(self): - with self.assertRaisesRegex(ValueError, NOT_CUBE_MSG): + with pytest.raises(ValueError, match=NOT_CUBE_MSG): self.cubelist.append(None) -class Test_concatenate_cube(tests.IrisTest): - def setUp(self): +class Test_concatenate_cube: + @pytest.fixture(autouse=True) + def _setup(self): self.units = Unit("days since 1970-01-01 00:00:00", calendar="standard") self.cube1 = Cube([1, 2, 3], "air_temperature", units="K") self.cube1.add_dim_coord(DimCoord([0, 1, 2], "time", units=self.units), 0) @@ -54,13 +53,13 @@ def test_pass(self): self.cube2 = Cube([1, 2, 3], "air_temperature", units="K") self.cube2.add_dim_coord(DimCoord([3, 4, 5], "time", units=self.units), 0) result = CubeList([self.cube1, self.cube2]).concatenate_cube() - self.assertIsInstance(result, Cube) + assert isinstance(result, Cube) def test_fail(self): units = Unit("days since 1970-01-02 00:00:00", calendar="standard") cube2 = Cube([1, 2, 3], "air_temperature", units="K") cube2.add_dim_coord(DimCoord([0, 1, 2], "time", units=units), 0) - with self.assertRaises(iris.exceptions.ConcatenateError): + with pytest.raises(iris.exceptions.ConcatenateError): CubeList([self.cube1, cube2]).concatenate_cube() def test_names_differ_fail(self): @@ -69,17 +68,18 @@ def test_names_differ_fail(self): self.cube3 = Cube([1, 2, 3], "air_pressure", units="Pa") self.cube3.add_dim_coord(DimCoord([3, 4, 5], "time", units=self.units), 0) exc_regexp = "Cube names differ: air_temperature != air_pressure" - with self.assertRaisesRegex(iris.exceptions.ConcatenateError, exc_regexp): + with pytest.raises(iris.exceptions.ConcatenateError, match=exc_regexp): CubeList([self.cube1, self.cube2, self.cube3]).concatenate_cube() def test_empty(self): exc_regexp = "can't concatenate an empty CubeList" - with self.assertRaisesRegex(ValueError, exc_regexp): + with pytest.raises(ValueError, match=exc_regexp): CubeList([]).concatenate_cube() -class Test_extend(tests.IrisTest): - def setUp(self): +class Test_extend: + @pytest.fixture(autouse=True) + def _setup(self): self.cube1 = iris.cube.Cube(1, long_name="foo") self.cube2 = iris.cube.Cube(1, long_name="bar") self.cubelist1 = iris.cube.CubeList([self.cube1]) @@ -88,21 +88,22 @@ def setUp(self): def test_pass(self): cubelist = copy.copy(self.cubelist1) cubelist.extend(self.cubelist2) - self.assertEqual(cubelist, self.cubelist1 + self.cubelist2) + assert cubelist == self.cubelist1 + self.cubelist2 cubelist.extend([self.cube2]) - self.assertEqual(cubelist[-1], self.cube2) + assert cubelist[-1] == self.cube2 def test_fail(self): - with self.assertRaisesRegex(TypeError, NON_ITERABLE_MSG): + with pytest.raises(TypeError, match=NON_ITERABLE_MSG): self.cubelist1.extend(self.cube1) - with self.assertRaisesRegex(TypeError, NON_ITERABLE_MSG): + with pytest.raises(TypeError, match=NON_ITERABLE_MSG): self.cubelist1.extend(None) - with self.assertRaisesRegex(ValueError, NOT_CUBE_MSG): + with pytest.raises(ValueError, match=NOT_CUBE_MSG): self.cubelist1.extend(range(3)) -class Test_extract_overlapping(tests.IrisTest): - def setUp(self): +class Test_extract_overlapping: + @pytest.fixture(autouse=True) + def _setup(self): shape = (6, 14, 19) n_time, n_lat, n_lon = shape n_data = n_time * n_lat * n_lon @@ -133,32 +134,33 @@ def setUp(self): def test_extract_one_str_dim(self): cubes = iris.cube.CubeList([self.cube[2:], self.cube[:4]]) a, b = cubes.extract_overlapping("time") - self.assertEqual(a.coord("time"), self.cube.coord("time")[2:4]) - self.assertEqual(b.coord("time"), self.cube.coord("time")[2:4]) + assert a.coord("time") == self.cube.coord("time")[2:4] + assert b.coord("time") == self.cube.coord("time")[2:4] def test_extract_one_list_dim(self): cubes = iris.cube.CubeList([self.cube[2:], self.cube[:4]]) a, b = cubes.extract_overlapping(["time"]) - self.assertEqual(a.coord("time"), self.cube.coord("time")[2:4]) - self.assertEqual(b.coord("time"), self.cube.coord("time")[2:4]) + assert a.coord("time") == self.cube.coord("time")[2:4] + assert b.coord("time") == self.cube.coord("time")[2:4] def test_extract_two_dims(self): cubes = iris.cube.CubeList([self.cube[2:, 5:], self.cube[:4, :10]]) a, b = cubes.extract_overlapping(["time", "latitude"]) - self.assertEqual(a.coord("time"), self.cube.coord("time")[2:4]) - self.assertEqual(a.coord("latitude"), self.cube.coord("latitude")[5:10]) - self.assertEqual(b.coord("time"), self.cube.coord("time")[2:4]) - self.assertEqual(b.coord("latitude"), self.cube.coord("latitude")[5:10]) + assert a.coord("time") == self.cube.coord("time")[2:4] + assert a.coord("latitude") == self.cube.coord("latitude")[5:10] + assert b.coord("time") == self.cube.coord("time")[2:4] + assert b.coord("latitude") == self.cube.coord("latitude")[5:10] def test_different_orders(self): cubes = iris.cube.CubeList([self.cube[::-1][:4], self.cube[:4]]) a, b = cubes.extract_overlapping("time") - self.assertEqual(a.coord("time"), self.cube[::-1].coord("time")[2:4]) - self.assertEqual(b.coord("time"), self.cube.coord("time")[2:4]) + assert a.coord("time") == self.cube[::-1].coord("time")[2:4] + assert b.coord("time") == self.cube.coord("time")[2:4] -class Test_iadd(tests.IrisTest): - def setUp(self): +class Test_iadd: + @pytest.fixture(autouse=True) + def _setup(self): self.cube1 = iris.cube.Cube(1, long_name="foo") self.cube2 = iris.cube.Cube(1, long_name="bar") self.cubelist1 = iris.cube.CubeList([self.cube1]) @@ -167,36 +169,38 @@ def setUp(self): def test_pass(self): cubelist = copy.copy(self.cubelist1) cubelist += self.cubelist2 - self.assertEqual(cubelist, self.cubelist1 + self.cubelist2) + assert cubelist == self.cubelist1 + self.cubelist2 cubelist += [self.cube2] - self.assertEqual(cubelist[-1], self.cube2) + assert cubelist[-1] == self.cube2 def test_fail(self): - with self.assertRaisesRegex(TypeError, NON_ITERABLE_MSG): + with pytest.raises(TypeError, match=NON_ITERABLE_MSG): self.cubelist1 += self.cube1 - with self.assertRaisesRegex(TypeError, NON_ITERABLE_MSG): + with pytest.raises(TypeError, match=NON_ITERABLE_MSG): self.cubelist1 += 1.0 - with self.assertRaisesRegex(ValueError, NOT_CUBE_MSG): + with pytest.raises(ValueError, match=NOT_CUBE_MSG): self.cubelist1 += range(3) -class Test_insert(tests.IrisTest): - def setUp(self): +class Test_insert: + @pytest.fixture(autouse=True) + def _setup(self): self.cube1 = iris.cube.Cube(1, long_name="foo") self.cube2 = iris.cube.Cube(1, long_name="bar") self.cubelist = iris.cube.CubeList([self.cube1] * 3) def test_pass(self): self.cubelist.insert(1, self.cube2) - self.assertEqual(self.cubelist[1], self.cube2) + assert self.cubelist[1] == self.cube2 def test_fail(self): - with self.assertRaisesRegex(ValueError, NOT_CUBE_MSG): + with pytest.raises(ValueError, match=NOT_CUBE_MSG): self.cubelist.insert(0, None) -class Test_merge_cube(tests.IrisTest): - def setUp(self): +class Test_merge_cube: + @pytest.fixture(autouse=True) + def _setup(self): self.cube1 = Cube([1, 2, 3], "air_temperature", units="K") self.cube1.add_aux_coord(AuxCoord([0], "height", units="m")) @@ -204,29 +208,33 @@ def test_pass(self): cube2 = self.cube1.copy() cube2.coord("height").points = [1] result = CubeList([self.cube1, cube2]).merge_cube() - self.assertIsInstance(result, Cube) + assert isinstance(result, Cube) def test_fail(self): cube2 = self.cube1.copy() cube2.rename("not air temperature") - with self.assertRaises(iris.exceptions.MergeError): + with pytest.raises(iris.exceptions.MergeError): CubeList([self.cube1, cube2]).merge_cube() def test_empty(self): - with self.assertRaises(ValueError): + with pytest.raises(ValueError): CubeList([]).merge_cube() def test_single_cube(self): result = CubeList([self.cube1]).merge_cube() - self.assertEqual(result, self.cube1) - self.assertIsNot(result, self.cube1) + assert result == self.cube1 + assert result is not self.cube1 def test_repeated_cube(self): - with self.assertRaises(iris.exceptions.MergeError): + with pytest.raises(iris.exceptions.MergeError): CubeList([self.cube1, self.cube1]).merge_cube() -class Test_merge__time_triple(tests.IrisTest): +class Test_merge__time_triple: + @pytest.fixture(autouse=True) + def _setup(self, request): + self.request = request + @staticmethod def _make_cube(fp, rt, t, realization=None): cube = Cube(np.arange(20).reshape(4, 5)) @@ -259,7 +267,7 @@ def test_orthogonal_with_realization(self): en2_cubes = [self._make_cube(*triple, realization=2) for triple in triples] cubes = CubeList(en1_cubes) + CubeList(en2_cubes) (cube,) = cubes.merge() - self.assertCML(cube, checksum=False) + _shared_utils.assert_CML(self.request, cube, checksum=False) def test_combination_with_realization(self): # => fp, rt, t: 8; realization: 2 @@ -277,7 +285,7 @@ def test_combination_with_realization(self): en2_cubes = [self._make_cube(*triple, realization=2) for triple in triples] cubes = CubeList(en1_cubes) + CubeList(en2_cubes) (cube,) = cubes.merge() - self.assertCML(cube, checksum=False) + _shared_utils.assert_CML(self.request, cube, checksum=False) def test_combination_with_extra_realization(self): # => fp, rt, t, realization: 17 @@ -298,7 +306,7 @@ def test_combination_with_extra_realization(self): en3_cubes = [self._make_cube(0, 10, 2, realization=3)] cubes = CubeList(en1_cubes) + CubeList(en2_cubes) + CubeList(en3_cubes) (cube,) = cubes.merge() - self.assertCML(cube, checksum=False) + _shared_utils.assert_CML(self.request, cube, checksum=False) def test_combination_with_extra_triple(self): # => fp, rt, t, realization: 17 @@ -320,11 +328,12 @@ def test_combination_with_extra_triple(self): ] cubes = CubeList(en1_cubes) + CubeList(en2_cubes) (cube,) = cubes.merge() - self.assertCML(cube, checksum=False) + _shared_utils.assert_CML(self.request, cube, checksum=False) -class Test_setitem(tests.IrisTest): - def setUp(self): +class Test_setitem: + @pytest.fixture(autouse=True) + def _setup(self): self.cube1 = iris.cube.Cube(1, long_name="foo") self.cube2 = iris.cube.Cube(1, long_name="bar") self.cube3 = iris.cube.Cube(1, long_name="boo") @@ -332,41 +341,40 @@ def setUp(self): def test_pass(self): self.cubelist[1] = self.cube2 - self.assertEqual(self.cubelist[1], self.cube2) + assert self.cubelist[1] == self.cube2 self.cubelist[:2] = (self.cube2, self.cube3) - self.assertEqual( - self.cubelist, - iris.cube.CubeList([self.cube2, self.cube3, self.cube1]), - ) + assert self.cubelist == iris.cube.CubeList([self.cube2, self.cube3, self.cube1]) def test_fail(self): - with self.assertRaisesRegex(ValueError, NOT_CUBE_MSG): + with pytest.raises(ValueError, match=NOT_CUBE_MSG): self.cubelist[0] = None - with self.assertRaisesRegex(ValueError, NOT_CUBE_MSG): + with pytest.raises(ValueError, match=NOT_CUBE_MSG): self.cubelist[0:2] = [self.cube3, None] - with self.assertRaisesRegex(TypeError, NON_ITERABLE_MSG): + with pytest.raises(TypeError, match=NON_ITERABLE_MSG): self.cubelist[:1] = 2.5 - with self.assertRaisesRegex(TypeError, NON_ITERABLE_MSG): + with pytest.raises(TypeError, match=NON_ITERABLE_MSG): self.cubelist[:1] = self.cube1 -class Test_xml(tests.IrisTest): - def setUp(self): +class Test_xml: + @pytest.fixture(autouse=True) + def _setup(self): self.cubes = CubeList([Cube(np.arange(3)), Cube(np.arange(3))]) def test_byteorder_default(self): - self.assertIn("byteorder", self.cubes.xml()) + assert "byteorder" in self.cubes.xml() def test_byteorder_false(self): - self.assertNotIn("byteorder", self.cubes.xml(byteorder=False)) + assert "byteorder" not in self.cubes.xml(byteorder=False) def test_byteorder_true(self): - self.assertIn("byteorder", self.cubes.xml(byteorder=True)) + assert "byteorder" in self.cubes.xml(byteorder=True) -class Test_extract(tests.IrisTest): - def setUp(self): +class Test_extract: + @pytest.fixture(autouse=True) + def _setup(self): self.scalar_cubes = CubeList() for i in range(5): for letter in "abcd": @@ -376,7 +384,7 @@ def test_scalar_cube_name_constraint(self): # Test the name based extraction of a CubeList containing scalar cubes. res = self.scalar_cubes.extract("a") expected = CubeList([Cube(i, long_name="a") for i in range(5)]) - self.assertEqual(res, expected) + assert res == expected def test_scalar_cube_data_constraint(self): # Test the extraction of a CubeList containing scalar cubes @@ -385,7 +393,7 @@ def test_scalar_cube_data_constraint(self): constraint = iris.Constraint(cube_func=lambda c: c.data == val) res = self.scalar_cubes.extract(constraint) expected = CubeList([Cube(val, long_name=letter) for letter in "abcd"]) - self.assertEqual(res, expected) + assert res == expected class ExtractMixin: @@ -393,7 +401,8 @@ class ExtractMixin: # Effectively "abstract" -- inheritor must define this property : # method_name = 'extract_cube' / 'extract_cubes' - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): self.cube_x = Cube(0, long_name="x") self.cube_y = Cube(0, long_name="y") self.cons_x = Constraint("x") @@ -412,20 +421,18 @@ def check_extract(self, cubes, constraints, expected): cubelist = CubeList(cubes) method = getattr(cubelist, self.method_name) if isinstance(expected, str): - with self.assertRaisesRegex( - iris.exceptions.ConstraintMismatchError, expected - ): + with pytest.raises(iris.exceptions.ConstraintMismatchError, match=expected): method(constraints) else: result = method(constraints) if expected is None: - self.assertIsNone(result) + assert result is None elif isinstance(expected, Cube): - self.assertIsInstance(result, Cube) - self.assertEqual(result, expected) + assert isinstance(result, Cube) + assert result == expected elif isinstance(expected, list): - self.assertIsInstance(result, CubeList) - self.assertEqual(result, expected) + assert isinstance(result, CubeList) + assert result == expected else: msg = ( 'Unhandled usage in "check_extract" call: ' @@ -434,7 +441,7 @@ def check_extract(self, cubes, constraints, expected): raise ValueError(msg.format(type(expected), expected)) -class Test_extract_cube(ExtractMixin, tests.IrisTest): +class Test_extract_cube(ExtractMixin): method_name = "extract_cube" def test_empty(self): @@ -466,7 +473,7 @@ def test_none_as_constraint(self): def test_constraint_in_list__fail(self): # Check that we *cannot* use [constraint] msg = "cannot be cast to a constraint" - with self.assertRaisesRegex(TypeError, msg): + with pytest.raises(TypeError, match=msg): self.check_extract([], [self.cons_x], []) def test_multi_cube_ok(self): @@ -493,7 +500,7 @@ class ExtractCubesMixin(ExtractMixin): method_name = "extract_cubes" -class Test_extract_cubes__noconstraint(ExtractCubesMixin, tests.IrisTest): +class Test_extract_cubes__noconstraint(ExtractCubesMixin): """Test with an empty list of constraints.""" def test_empty(self): @@ -553,23 +560,19 @@ def test_multi_cube__fail_too_many(self): ) -class Test_extract_cubes__bare_single_constraint( - ExtractCubesSingleConstraintMixin, tests.IrisTest -): +class Test_extract_cubes__bare_single_constraint(ExtractCubesSingleConstraintMixin): """Testing with a single constraint as the argument.""" wrap_test_constraint_as_list_of_one = False -class Test_extract_cubes__list_single_constraint( - ExtractCubesSingleConstraintMixin, tests.IrisTest -): +class Test_extract_cubes__list_single_constraint(ExtractCubesSingleConstraintMixin): """Testing with a list of one constraint as the argument.""" wrap_test_constraint_as_list_of_one = True -class Test_extract_cubes__multi_constraints(ExtractCubesMixin, tests.IrisTest): +class Test_extract_cubes__multi_constraints(ExtractCubesMixin): """Testing when the 'constraints' arg is a list of multiple constraints.""" def test_empty(self): @@ -627,61 +630,64 @@ def test_multi_cube__fail_too_many(self): ) -class Test_iteration(tests.IrisTest): - def setUp(self): +class Test_iteration: + @pytest.fixture(autouse=True) + def _setup(self): self.scalar_cubes = CubeList() for i in range(5): for letter in "abcd": self.scalar_cubes.append(Cube(i, long_name=letter)) def test_iterable(self): - self.assertIsInstance(self.scalar_cubes, collections.abc.Iterable) + assert isinstance(self.scalar_cubes, collections.abc.Iterable) def test_iteration(self): letters = "abcd" * 5 for i, cube in enumerate(self.scalar_cubes): - self.assertEqual(cube.long_name, letters[i]) + assert cube.long_name == letters[i] -class TestPrint(tests.IrisTest): - def setUp(self): +class TestPrint: + @pytest.fixture(autouse=True) + def _setup(self): self.cubes = CubeList([iris.tests.stock.lat_lon_cube()]) def test_summary(self): expected = "0: unknown / (unknown) (latitude: 3; longitude: 4)" - self.assertEqual(str(self.cubes), expected) + assert str(self.cubes) == expected def test_summary_name_unit(self): self.cubes[0].long_name = "aname" self.cubes[0].units = "1" expected = "0: aname / (1) (latitude: 3; longitude: 4)" - self.assertEqual(str(self.cubes), expected) + assert str(self.cubes) == expected def test_summary_stash(self): self.cubes[0].attributes["STASH"] = STASH.from_msi("m01s00i004") expected = "0: m01s00i004 / (unknown) (latitude: 3; longitude: 4)" - self.assertEqual(str(self.cubes), expected) + assert str(self.cubes) == expected -class TestRealiseData(tests.IrisTest): - def test_realise_data(self): +class TestRealiseData: + def test_realise_data(self, mocker): # Simply check that calling CubeList.realise_data is calling # _lazy_data.co_realise_cubes. mock_cubes_list = [mock.Mock(ident=count) for count in range(3)] test_cubelist = CubeList(mock_cubes_list) - call_patch = self.patch("iris._lazy_data.co_realise_cubes") + call_patch = mocker.patch("iris._lazy_data.co_realise_cubes") test_cubelist.realise_data() # Check it was called once, passing cubes as *args. - self.assertEqual(call_patch.call_args_list, [mock.call(*mock_cubes_list)]) + assert call_patch.call_args_list == [mock.call(*mock_cubes_list)] -class Test_CubeList_copy(tests.IrisTest): - def setUp(self): +class Test_CubeList_copy: + @pytest.fixture(autouse=True) + def _setup(self): self.cube_list = iris.cube.CubeList() self.copied_cube_list = self.cube_list.copy() def test_copy(self): - self.assertIsInstance(self.copied_cube_list, iris.cube.CubeList) + assert isinstance(self.copied_cube_list, iris.cube.CubeList) class TestHtmlRepr: @@ -696,13 +702,13 @@ class TestHtmlRepr: """ @staticmethod - def test__repr_html_(): + def test__repr_html_(mocker): test_cubelist = CubeList([]) target = "iris.experimental.representation.CubeListRepresentation" - with mock.patch(target) as class_mock: - # Exercise the function-under-test. - test_cubelist._repr_html_() + class_mock = mocker.patch(target) + # Exercise the function-under-test. + test_cubelist._repr_html_() assert class_mock.call_args_list == [ # "CubeListRepresentation()" was called exactly once, with the cubelist as arg @@ -712,7 +718,3 @@ def test__repr_html_(): # "CubeListRepresentation(cubelist).repr_html()" was called exactly once, with no args mock.call() ] - - -if __name__ == "__main__": - tests.main() diff --git a/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py b/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py index 64c99ebd4b..bd65ff6773 100644 --- a/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py +++ b/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py @@ -4,14 +4,11 @@ # See LICENSE in the root of the repository for full licensing details. """Unit tests for the `iris.cube.Cube` class aggregated_by method.""" -# 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 Unit import numpy as np +import pytest from iris._lazy_data import as_lazy_data import iris.analysis @@ -20,11 +17,13 @@ import iris.coords from iris.coords import AncillaryVariable, AuxCoord, CellMeasure, DimCoord from iris.cube import Cube +from iris.tests import _shared_utils from iris.tests.stock import realistic_4d -class Test_aggregated_by(tests.IrisTest): - def setUp(self): +class Test_aggregated_by: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = Cube(np.arange(44).reshape(4, 11)) val_coord = AuxCoord([0, 0, 0, 1, 1, 2, 0, 0, 2, 0, 1], long_name="val") @@ -99,13 +98,8 @@ def test_2d_coord_simple_agg(self): res_cube.slices("simple_agg"), self.cube.slices("simple_agg") ): cube_slice_agg = cube_slice.aggregated_by("simple_agg", self.mock_agg) - self.assertEqual( - res_slice.coord("spanning"), cube_slice_agg.coord("spanning") - ) - self.assertEqual( - res_slice.coord("span_label"), - cube_slice_agg.coord("span_label"), - ) + assert res_slice.coord("spanning") == cube_slice_agg.coord("spanning") + assert res_slice.coord("span_label") == cube_slice_agg.coord("span_label") def test_agg_by_label(self): # Aggregate a cube on a string coordinate label where label @@ -122,8 +116,8 @@ def test_agg_by_label(self): long_name="label", units="no_unit", ) - self.assertEqual(res_cube.coord("val"), val_coord) - self.assertEqual(res_cube.coord("label"), label_coord) + assert res_cube.coord("val") == val_coord + assert res_cube.coord("label") == label_coord def test_agg_by_label_bounded(self): # Aggregate a cube on a string coordinate label where label @@ -142,8 +136,8 @@ def test_agg_by_label_bounded(self): long_name="label", units="no_unit", ) - self.assertEqual(res_cube.coord("val"), val_coord) - self.assertEqual(res_cube.coord("label"), label_coord) + assert res_cube.coord("val") == val_coord + assert res_cube.coord("label") == label_coord def test_2d_agg_by_label(self): res_cube = self.cube.aggregated_by("label", self.mock_agg) @@ -153,9 +147,7 @@ def test_2d_agg_by_label(self): res_cube.slices("val"), self.cube.slices("val") ): cube_slice_agg = cube_slice.aggregated_by("label", self.mock_agg) - self.assertEqual( - res_slice.coord("spanning"), cube_slice_agg.coord("spanning") - ) + assert res_slice.coord("spanning") == cube_slice_agg.coord("spanning") def test_agg_by_val(self): # Aggregate a cube on a numeric coordinate val where label @@ -169,8 +161,8 @@ def test_agg_by_val(self): label_coord = AuxCoord( np.array((exp0, exp1, exp2)), long_name="label", units="no_unit" ) - self.assertEqual(res_cube.coord("val"), val_coord) - self.assertEqual(res_cube.coord("label"), label_coord) + assert res_cube.coord("val") == val_coord + assert res_cube.coord("label") == label_coord def test_2d_agg_by_val(self): res_cube = self.cube.aggregated_by("val", self.mock_agg) @@ -180,9 +172,7 @@ def test_2d_agg_by_val(self): res_cube.slices("val"), self.cube.slices("val") ): cube_slice_agg = cube_slice.aggregated_by("val", self.mock_agg) - self.assertEqual( - res_slice.coord("spanning"), cube_slice_agg.coord("spanning") - ) + assert res_slice.coord("spanning") == cube_slice_agg.coord("spanning") def test_single_string_aggregation(self): aux_coords = [ @@ -193,31 +183,31 @@ def test_single_string_aggregation(self): np.arange(12).reshape(3, 4), aux_coords_and_dims=aux_coords ) result = cube.aggregated_by("foo", MEAN) - self.assertEqual(result.shape, (2, 4)) - self.assertEqual(result.coord("bar"), AuxCoord(["a|a", "a"], long_name="bar")) + assert result.shape == (2, 4) + assert result.coord("bar") == AuxCoord(["a|a", "a"], long_name="bar") def test_ancillary_variables_and_cell_measures_kept(self): cube_agg = self.cube.aggregated_by("val", self.mock_agg) - self.assertEqual(cube_agg.ancillary_variables(), [self.ancillary_variable]) - self.assertEqual(cube_agg.cell_measures(), [self.cell_measure]) + assert cube_agg.ancillary_variables() == [self.ancillary_variable] + assert cube_agg.cell_measures() == [self.cell_measure] def test_ancillary_variables_and_cell_measures_removed(self): cube_agg = self.cube.aggregated_by("simple_agg", self.mock_agg) - self.assertEqual(cube_agg.ancillary_variables(), []) - self.assertEqual(cube_agg.cell_measures(), []) + assert cube_agg.ancillary_variables() == [] + assert cube_agg.cell_measures() == [] def test_1d_weights(self): self.cube.aggregated_by( "simple_agg", self.mock_weighted_agg, weights=self.simple_weights ) - self.assertEqual(self.mock_weighted_agg.aggregate.call_count, 2) + assert self.mock_weighted_agg.aggregate.call_count == 2 # A simple mock.assert_called_with does not work due to ValueError: The # truth value of an array with more than one element is ambiguous. Use # a.any() or a.all() call_1 = self.mock_weighted_agg.aggregate.mock_calls[0] - np.testing.assert_array_equal( + _shared_utils.assert_array_equal( call_1.args[0], np.array( [ @@ -226,8 +216,8 @@ def test_1d_weights(self): ] ), ) - self.assertEqual(call_1.kwargs["axis"], 0) - np.testing.assert_array_almost_equal( + assert call_1.kwargs["axis"] == 0 + _shared_utils.assert_array_almost_equal( call_1.kwargs["weights"], np.array( [ @@ -238,7 +228,7 @@ def test_1d_weights(self): ) call_2 = self.mock_weighted_agg.aggregate.mock_calls[1] - np.testing.assert_array_equal( + _shared_utils.assert_array_equal( call_2.args[0], np.array( [ @@ -247,8 +237,8 @@ def test_1d_weights(self): ] ), ) - self.assertEqual(call_2.kwargs["axis"], 0) - np.testing.assert_array_almost_equal( + assert call_2.kwargs["axis"] == 0 + _shared_utils.assert_array_almost_equal( call_2.kwargs["weights"], np.array( [ @@ -261,13 +251,13 @@ def test_1d_weights(self): def test_2d_weights(self): self.cube.aggregated_by("val", self.mock_weighted_agg, weights=self.val_weights) - self.assertEqual(self.mock_weighted_agg.aggregate.call_count, 3) + assert self.mock_weighted_agg.aggregate.call_count == 3 # A simple mock.assert_called_with does not work due to ValueError: The # truth value of an array with more than one element is ambiguous. Use # a.any() or a.all() call_1 = self.mock_weighted_agg.aggregate.mock_calls[0] - np.testing.assert_array_equal( + _shared_utils.assert_array_equal( call_1.args[0], np.array( [ @@ -278,33 +268,39 @@ def test_2d_weights(self): ] ), ) - self.assertEqual(call_1.kwargs["axis"], 1) - np.testing.assert_array_almost_equal(call_1.kwargs["weights"], np.ones((4, 6))) + assert call_1.kwargs["axis"] == 1 + _shared_utils.assert_array_almost_equal( + call_1.kwargs["weights"], np.ones((4, 6)) + ) call_2 = self.mock_weighted_agg.aggregate.mock_calls[1] - np.testing.assert_array_equal( + _shared_utils.assert_array_equal( call_2.args[0], np.array([[3, 4, 10], [14, 15, 21], [25, 26, 32], [36, 37, 43]]), ) - self.assertEqual(call_2.kwargs["axis"], 1) - np.testing.assert_array_almost_equal(call_2.kwargs["weights"], np.ones((4, 3))) + assert call_2.kwargs["axis"] == 1 + _shared_utils.assert_array_almost_equal( + call_2.kwargs["weights"], np.ones((4, 3)) + ) call_3 = self.mock_weighted_agg.aggregate.mock_calls[2] - np.testing.assert_array_equal( + _shared_utils.assert_array_equal( call_3.args[0], np.array([[5, 8], [16, 19], [27, 30], [38, 41]]) ) - self.assertEqual(call_3.kwargs["axis"], 1) - np.testing.assert_array_almost_equal(call_3.kwargs["weights"], np.ones((4, 2))) + assert call_3.kwargs["axis"] == 1 + _shared_utils.assert_array_almost_equal( + call_3.kwargs["weights"], np.ones((4, 2)) + ) def test_returned(self): output = self.cube.aggregated_by( "simple_agg", self.mock_weighted_agg, returned=True ) - self.assertTrue(isinstance(output, tuple)) - self.assertEqual(len(output), 2) - self.assertEqual(output[0].shape, (2, 11)) - self.assertEqual(output[1].shape, (2, 11)) + assert isinstance(output, tuple) + assert len(output) == 2 + assert output[0].shape == (2, 11) + assert output[1].shape == (2, 11) def test_fail_1d_weights_wrong_len(self): wrong_weights = np.array([1.0, 2.0]) @@ -312,7 +308,7 @@ def test_fail_1d_weights_wrong_len(self): r"1D weights must have the same length as the dimension that is " r"aggregated, got 2, expected 11" ) - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): self.cube.aggregated_by( "val", self.mock_weighted_agg, weights=wrong_weights ) @@ -323,14 +319,15 @@ def test_fail_weights_wrong_shape(self): r"Weights must either be 1D or have the same shape as the cube, " r"got shape \(42, 1\) for weights, \(4, 11\) for cube" ) - with self.assertRaisesRegex(ValueError, msg): + with pytest.raises(ValueError, match=msg): self.cube.aggregated_by( "val", self.mock_weighted_agg, weights=wrong_weights ) -class Test_aggregated_by__lazy(tests.IrisTest): - def setUp(self): +class Test_aggregated_by__lazy: + @pytest.fixture(autouse=True) + def _setup(self): self.data = np.arange(44).reshape(4, 11) self.lazydata = as_lazy_data(self.data) self.cube = Cube(self.lazydata) @@ -394,11 +391,11 @@ def test_agg_by_label__lazy(self): long_name="label", units="no_unit", ) - self.assertTrue(res_cube.has_lazy_data()) - self.assertEqual(res_cube.coord("val"), val_coord) - self.assertEqual(res_cube.coord("label"), label_coord) - self.assertArrayEqual(res_cube.data, self.label_mean) - self.assertFalse(res_cube.has_lazy_data()) + assert res_cube.has_lazy_data() + assert res_cube.coord("val") == val_coord + assert res_cube.coord("label") == label_coord + _shared_utils.assert_array_equal(res_cube.data, self.label_mean) + assert not res_cube.has_lazy_data() def test_agg_by_val__lazy(self): # Aggregate a cube on a numeric coordinate val where label @@ -412,11 +409,11 @@ def test_agg_by_val__lazy(self): label_coord = AuxCoord( np.array((exp0, exp1, exp2)), long_name="label", units="no_unit" ) - self.assertTrue(res_cube.has_lazy_data()) - self.assertEqual(res_cube.coord("val"), val_coord) - self.assertEqual(res_cube.coord("label"), label_coord) - self.assertArrayEqual(res_cube.data, self.val_mean) - self.assertFalse(res_cube.has_lazy_data()) + assert res_cube.has_lazy_data() + assert res_cube.coord("val") == val_coord + assert res_cube.coord("label") == label_coord + _shared_utils.assert_array_equal(res_cube.data, self.val_mean) + assert not res_cube.has_lazy_data() def test_single_string_aggregation__lazy(self): aux_coords = [ @@ -429,22 +426,22 @@ def test_single_string_aggregation__lazy(self): ) means = np.array([[4.0, 5.0, 6.0, 7.0], [4.0, 5.0, 6.0, 7.0]]) result = cube.aggregated_by("foo", MEAN) - self.assertTrue(result.has_lazy_data()) - self.assertEqual(result.shape, (2, 4)) - self.assertEqual(result.coord("bar"), AuxCoord(["a|a", "a"], long_name="bar")) - self.assertArrayEqual(result.data, means) - self.assertFalse(result.has_lazy_data()) + assert result.has_lazy_data() + assert result.shape == (2, 4) + assert result.coord("bar") == AuxCoord(["a|a", "a"], long_name="bar") + _shared_utils.assert_array_equal(result.data, means) + assert not result.has_lazy_data() def test_1d_weights__lazy(self): - self.assertTrue(self.cube.has_lazy_data()) + assert self.cube.has_lazy_data() cube_agg = self.cube.aggregated_by( "simple_agg", SUM, weights=self.simple_weights ) - self.assertTrue(self.cube.has_lazy_data()) - self.assertTrue(cube_agg.has_lazy_data()) - self.assertEqual(cube_agg.shape, (2, 11)) + assert self.cube.has_lazy_data() + assert cube_agg.has_lazy_data() + assert cube_agg.shape == (2, 11) row_0 = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] row_1 = [ @@ -460,18 +457,18 @@ def test_1d_weights__lazy(self): 146.0, 150.0, ] - np.testing.assert_array_almost_equal(cube_agg.data, np.array([row_0, row_1])) + _shared_utils.assert_array_almost_equal(cube_agg.data, np.array([row_0, row_1])) def test_2d_weights__lazy(self): - self.assertTrue(self.cube.has_lazy_data()) + assert self.cube.has_lazy_data() cube_agg = self.cube.aggregated_by("val", SUM, weights=self.val_weights) - self.assertTrue(self.cube.has_lazy_data()) - self.assertTrue(cube_agg.has_lazy_data()) + assert self.cube.has_lazy_data() + assert cube_agg.has_lazy_data() - self.assertEqual(cube_agg.shape, (4, 3)) - np.testing.assert_array_almost_equal( + assert cube_agg.shape == (4, 3) + _shared_utils.assert_array_almost_equal( cube_agg.data, np.array( [ @@ -484,21 +481,21 @@ def test_2d_weights__lazy(self): ) def test_returned__lazy(self): - self.assertTrue(self.cube.has_lazy_data()) + assert self.cube.has_lazy_data() output = self.cube.aggregated_by( "simple_agg", SUM, weights=self.simple_weights, returned=True ) - self.assertTrue(self.cube.has_lazy_data()) + assert self.cube.has_lazy_data() - self.assertTrue(isinstance(output, tuple)) - self.assertEqual(len(output), 2) + assert isinstance(output, tuple) + assert len(output) == 2 cube = output[0] - self.assertTrue(isinstance(cube, Cube)) - self.assertTrue(cube.has_lazy_data()) - self.assertEqual(cube.shape, (2, 11)) + assert isinstance(cube, Cube) + assert cube.has_lazy_data() + assert cube.shape == (2, 11) row_0 = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] row_1 = [ 110.0, @@ -513,11 +510,11 @@ def test_returned__lazy(self): 146.0, 150.0, ] - np.testing.assert_array_almost_equal(cube.data, np.array([row_0, row_1])) + _shared_utils.assert_array_almost_equal(cube.data, np.array([row_0, row_1])) weights = output[1] - self.assertEqual(weights.shape, (2, 11)) - np.testing.assert_array_almost_equal( + assert weights.shape == (2, 11) + _shared_utils.assert_array_almost_equal( weights, np.array( [ @@ -528,8 +525,9 @@ def test_returned__lazy(self): ) -class Test_aggregated_by__climatology(tests.IrisTest): - def setUp(self): +class Test_aggregated_by__climatology: + @pytest.fixture(autouse=True) + def _setup(self): self.data = np.arange(100).reshape(20, 5) self.aggregator = iris.analysis.MEAN @@ -623,15 +621,17 @@ def test_basic(self): result = self.get_result() aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(2)) - self.assertArrayEqual(aligned_coord.bounds, np.array([[0, 18], [1, 19]])) - self.assertTrue(aligned_coord.climatological) - self.assertIn(aligned_coord, result.dim_coords) + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(2)) + _shared_utils.assert_array_equal( + aligned_coord.bounds, np.array([[0, 18], [1, 19]]) + ) + assert aligned_coord.climatological + assert aligned_coord in result.dim_coords categorised_coord = result.coord("cat1") - self.assertArrayEqual(categorised_coord.points, np.arange(2)) - self.assertIsNone(categorised_coord.bounds) - self.assertFalse(categorised_coord.climatological) + _shared_utils.assert_array_equal(categorised_coord.points, np.arange(2)) + assert categorised_coord.bounds is None + assert not categorised_coord.climatological def test_2d_other_coord(self): """Check that we can handle aggregation applying to a 2d AuxCoord that @@ -640,19 +640,21 @@ def test_2d_other_coord(self): result = self.get_result(partially_aligned=True) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(2)) - self.assertArrayEqual(aligned_coord.bounds, np.array([[0, 18], [1, 19]])) - self.assertTrue(aligned_coord.climatological) + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(2)) + _shared_utils.assert_array_equal( + aligned_coord.bounds, np.array([[0, 18], [1, 19]]) + ) + assert aligned_coord.climatological part_aligned_coord = result.coord("part_aligned") - self.assertArrayEqual( + _shared_utils.assert_array_equal( part_aligned_coord.points, np.arange(46, 56).reshape(2, 5) ) - self.assertArrayEqual( + _shared_utils.assert_array_equal( part_aligned_coord.bounds, np.array([np.arange(1, 11), np.arange(91, 101)]).T.reshape(2, 5, 2), ) - self.assertFalse(part_aligned_coord.climatological) + assert not part_aligned_coord.climatological def test_2d_timelike_other_coord(self): """Check that we can handle aggregation applying to a 2d AuxCoord that @@ -663,64 +665,74 @@ def test_2d_timelike_other_coord(self): ) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(2)) - self.assertArrayEqual(aligned_coord.bounds, np.array([[0, 18], [1, 19]])) - self.assertTrue(aligned_coord.climatological) + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(2)) + _shared_utils.assert_array_equal( + aligned_coord.bounds, np.array([[0, 18], [1, 19]]) + ) + assert aligned_coord.climatological part_aligned_coord = result.coord("part_aligned") - self.assertArrayEqual(part_aligned_coord.points, np.arange(1, 11).reshape(2, 5)) - self.assertArrayEqual( + _shared_utils.assert_array_equal( + part_aligned_coord.points, np.arange(1, 11).reshape(2, 5) + ) + _shared_utils.assert_array_equal( part_aligned_coord.bounds, np.array([np.arange(1, 11), np.arange(91, 101)]).T.reshape(2, 5, 2), ) - self.assertTrue(part_aligned_coord.climatological) + assert part_aligned_coord.climatological def test_transposed(self): """Check that we can handle the axis of aggregation being a different one.""" result = self.get_result(transpose=True) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(2)) - self.assertArrayEqual(aligned_coord.bounds, np.array([[0, 18], [1, 19]])) - self.assertTrue(aligned_coord.climatological) + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(2)) + _shared_utils.assert_array_equal( + aligned_coord.bounds, np.array([[0, 18], [1, 19]]) + ) + assert aligned_coord.climatological categorised_coord = result.coord("cat1") - self.assertArrayEqual(categorised_coord.points, np.arange(2)) - self.assertIsNone(categorised_coord.bounds) - self.assertFalse(categorised_coord.climatological) + _shared_utils.assert_array_equal(categorised_coord.points, np.arange(2)) + assert categorised_coord.bounds is None + assert not categorised_coord.climatological def test_bounded(self): """Check that we handle bounds correctly.""" result = self.get_result(bounds=True) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, [-0.5, 0.5]) - self.assertArrayEqual( + _shared_utils.assert_array_equal(aligned_coord.points, [-0.5, 0.5]) + _shared_utils.assert_array_equal( aligned_coord.bounds, np.array([[-0.5, 18.5], [0.5, 19.5]]) ) - self.assertTrue(aligned_coord.climatological) + assert aligned_coord.climatological def test_multiple_agg_coords(self): """Check that we can aggregate on multiple coords on the same axis.""" result = self.get_result(second_categorised=True) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(10)) - self.assertArrayEqual( + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(10)) + _shared_utils.assert_array_equal( aligned_coord.bounds, np.array([np.arange(10), np.arange(10, 20)]).T, ) - self.assertTrue(aligned_coord.climatological) + assert aligned_coord.climatological categorised_coord1 = result.coord("cat1") - self.assertArrayEqual(categorised_coord1.points, np.tile(np.arange(2), 5)) - self.assertIsNone(categorised_coord1.bounds) - self.assertFalse(categorised_coord1.climatological) + _shared_utils.assert_array_equal( + categorised_coord1.points, np.tile(np.arange(2), 5) + ) + assert categorised_coord1.bounds is None + assert not categorised_coord1.climatological categorised_coord2 = result.coord("cat2") - self.assertArrayEqual(categorised_coord2.points, np.tile(np.arange(5), 2)) - self.assertIsNone(categorised_coord2.bounds) - self.assertFalse(categorised_coord2.climatological) + _shared_utils.assert_array_equal( + categorised_coord2.points, np.tile(np.arange(5), 2) + ) + assert categorised_coord2.bounds is None + assert not categorised_coord2.climatological def test_non_climatological_units(self): """Check that the failure to set the climatological flag on an incompatible @@ -729,9 +741,11 @@ def test_non_climatological_units(self): result = self.get_result(invalid_units=True) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(9, 11)) - self.assertArrayEqual(aligned_coord.bounds, np.array([[0, 18], [1, 19]])) - self.assertFalse(aligned_coord.climatological) + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(9, 11)) + _shared_utils.assert_array_equal( + aligned_coord.bounds, np.array([[0, 18], [1, 19]]) + ) + assert not aligned_coord.climatological def test_clim_in_clim_op(self): """Check the least complicated version works (set climatological, set @@ -741,16 +755,16 @@ def test_clim_in_clim_op(self): result = self.get_result(bounds=True, already_climatological=True) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, [-0.5, 0.5]) - self.assertArrayEqual( + _shared_utils.assert_array_equal(aligned_coord.points, [-0.5, 0.5]) + _shared_utils.assert_array_equal( aligned_coord.bounds, np.array([[-0.5, 18.5], [0.5, 19.5]]) ) - self.assertTrue(aligned_coord.climatological) + assert aligned_coord.climatological categorised_coord = result.coord("cat1") - self.assertArrayEqual(categorised_coord.points, np.arange(2)) - self.assertIsNone(categorised_coord.bounds) - self.assertFalse(categorised_coord.climatological) + _shared_utils.assert_array_equal(categorised_coord.points, np.arange(2)) + assert categorised_coord.bounds is None + assert not categorised_coord.climatological def test_clim_in_no_clim_op(self): """Check the least complicated version works (set climatological, set @@ -762,20 +776,21 @@ def test_clim_in_no_clim_op(self): ) aligned_coord = result.coord("aligned") - self.assertArrayEqual(aligned_coord.points, np.arange(9, 11)) - self.assertArrayEqual( + _shared_utils.assert_array_equal(aligned_coord.points, np.arange(9, 11)) + _shared_utils.assert_array_equal( aligned_coord.bounds, np.array([[-0.5, 18.5], [0.5, 19.5]]) ) - self.assertTrue(aligned_coord.climatological) + assert aligned_coord.climatological categorised_coord = result.coord("cat1") - self.assertArrayEqual(categorised_coord.points, np.arange(2)) - self.assertIsNone(categorised_coord.bounds) - self.assertFalse(categorised_coord.climatological) + _shared_utils.assert_array_equal(categorised_coord.points, np.arange(2)) + assert categorised_coord.bounds is None + assert not categorised_coord.climatological -class Test_aggregated_by__derived(tests.IrisTest): - def setUp(self): +class Test_aggregated_by__derived: + @pytest.fixture(autouse=True) + def _setup(self): self.cube = realistic_4d()[:, :10, :6, :8] self.time_cat_coord = AuxCoord([0, 0, 1, 1, 2, 2], long_name="time_cat") self.cube.add_aux_coord(self.time_cat_coord, 0) @@ -815,7 +830,3 @@ def test_ungrouped_dim(self): assert len(result.aux_factories) == 1 altitude = result.coord("altitude") assert altitude == self.cube.coord("altitude") - - -if __name__ == "__main__": - tests.main() diff --git a/lib/iris/tests/unit/cube/test_Cube__operators.py b/lib/iris/tests/unit/cube/test_Cube__operators.py index 0afd5a9d70..44778cec17 100644 --- a/lib/iris/tests/unit/cube/test_Cube__operators.py +++ b/lib/iris/tests/unit/cube/test_Cube__operators.py @@ -3,23 +3,20 @@ # 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.cube.Cube` class operators.""" - -# import iris tests first so that some things can be initialised -# before importing anything else. -import iris.tests as tests # isort:skip - import operator import dask.array as da import numpy as np import numpy.ma as ma +import pytest import iris from iris._lazy_data import as_lazy_data from iris.coords import DimCoord +from iris.tests import _shared_utils -class Test_lazy_maths(tests.IrisTest): +class Test_lazy_maths: def build_lazy_cube(self, points, dtype=np.float64, bounds=None, nx=10): data = np.arange(len(points) * nx, dtype=dtype) + 1 # Just avoid 0. data = data.reshape(len(points), nx) @@ -32,23 +29,23 @@ def build_lazy_cube(self, points, dtype=np.float64, bounds=None, nx=10): return cube def check_common(self, base_cube, result): - self.assertTrue(base_cube.has_lazy_data()) - self.assertTrue(result.has_lazy_data()) - self.assertIsInstance(result.lazy_data(), da.core.Array) + assert base_cube.has_lazy_data() + assert result.has_lazy_data() + assert isinstance(result.lazy_data(), da.core.Array) def cube_cube_math_op(self, c1, math_op): result = math_op(c1, c1) self.check_common(c1, result) expected = math_op(c1.data, c1.data) - self.assertArrayAlmostEqual(result.data, expected) + _shared_utils.assert_array_almost_equal(result.data, expected) def cube_scalar_math_op(self, c1, scalar, math_op, commutative=True): result = math_op(c1, scalar) if commutative: - self.assertEqual(math_op(c1, scalar), math_op(scalar, c1)) + assert math_op(c1, scalar) == math_op(scalar, c1) self.check_common(c1, result) expected = math_op(c1.data, scalar) - self.assertArrayAlmostEqual(result.data, expected) + _shared_utils.assert_array_almost_equal(result.data, expected) def test_add_cubes__float(self): c1 = self.build_lazy_cube([1, 2]) @@ -139,12 +136,13 @@ def test_div_scalar__int(self): self.cube_scalar_math_op(c1, scalar, op, commutative=False) -class Test_lazy_maths__scalar_cube(tests.IrisTest): +class Test_lazy_maths__scalar_cube: def build_lazy_cube(self, value, dtype=np.float64): data = as_lazy_data(np.array(value, dtype=dtype)) return iris.cube.Cube(data, standard_name="air_temperature", units="K") - def setUp(self): + @pytest.fixture(autouse=True) + def _setup(self): self.c1 = self.build_lazy_cube(3) self.c2 = self.build_lazy_cube(4) self.c3 = self.build_lazy_cube(3, dtype=np.int64) @@ -153,8 +151,8 @@ def setUp(self): def check_common(self, c1, c2, math_op): cube = math_op(c1, c2) data = cube.data - self.assertTrue(isinstance(data, np.ndarray)) - self.assertEqual(data.shape, ()) + assert isinstance(data, np.ndarray) + assert data.shape == () def test_add_scalar__int(self): c3, c4, op = self.c3, 5, operator.add @@ -221,7 +219,7 @@ def test_div_cubes__float(self): self.check_common(c1, c2, op) -class Test_lazy_maths__masked_data(tests.IrisTest): +class Test_lazy_maths__masked_data: def build_lazy_cube(self, dtype=np.float64): data = ma.array( [[1.0, 1.0], [1.0, 100000.0]], mask=[[0, 0], [0, 1]], dtype=dtype @@ -238,14 +236,10 @@ def test_subtract__float(self): cube_a = self.build_lazy_cube() cube_b = self.build_lazy_cube() cube_c = cube_a - cube_b - self.assertTrue(ma.isMaskedArray(cube_c.data)) + assert ma.isMaskedArray(cube_c.data) def test_subtract__int(self): cube_a = self.build_lazy_cube(dtype=np.int64) cube_b = self.build_lazy_cube(dtype=np.int64) cube_c = cube_a - cube_b - self.assertTrue(ma.isMaskedArray(cube_c.data)) - - -if __name__ == "__main__": - tests.main() + assert ma.isMaskedArray(cube_c.data)