Skip to content

Commit 2e68c1a

Browse files
authored
Merge pull request #1331 from UXARRAY/rajeeja/io_test_consolidation
Base class for IO test files
2 parents a47bbb8 + 8fbe592 commit 2e68c1a

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

test/test_io_common.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
Common IO tests that apply to all grid formats. These tests make sure the
3+
same basic things work no matter which file format you start with.
4+
"""
5+
6+
import pytest
7+
import numpy as np
8+
import xarray as xr
9+
import uxarray as ux
10+
from pathlib import Path
11+
import tempfile
12+
import os
13+
import warnings
14+
from numpy.testing import assert_array_equal, assert_allclose
15+
from uxarray.constants import ERROR_TOLERANCE, INT_DTYPE, INT_FILL_VALUE
16+
17+
18+
19+
current_path = Path(os.path.dirname(os.path.realpath(__file__)))
20+
21+
# Define all testable format combinations
22+
# Format: (format_type, subpath, filename)
23+
IO_READ_TEST_FORMATS = [
24+
("ugrid", "ugrid/quad-hexagon", "grid.nc"),
25+
("ugrid", "ugrid/outCSne30", "outCSne30.ug"),
26+
("ugrid", "ugrid/outRLL1deg", "outRLL1deg.ug"),
27+
("mpas", "mpas/QU/480", "grid.nc"),
28+
("esmf", "esmf/ne30", "ne30pg3.grid.nc"),
29+
("exodus", "exodus/outCSne8", "outCSne8.g"),
30+
("exodus", "exodus/mixed", "mixed.exo"),
31+
("scrip", "scrip/outCSne8", "outCSne8.nc"),
32+
("icon", "icon/R02B04", "icon_grid_0010_R02B04_G.nc"),
33+
("fesom", "fesom/pi", None), # Special case - multiple files
34+
("healpix", None, None), # Constructed via classmethod
35+
]
36+
37+
# Formats that support writing
38+
WRITABLE_FORMATS = ["ugrid", "exodus", "scrip", "esmf"]
39+
40+
# Format conversion test pairs - removed for now as format conversion
41+
# requires more sophisticated handling than simple to_netcdf
42+
43+
44+
@pytest.fixture(params=IO_READ_TEST_FORMATS)
45+
def grid_from_format(request):
46+
"""Load a Grid from each supported format for parameterized tests.
47+
48+
Handles special cases (FESOM multi-file, HEALPix) and tags the grid with
49+
``_test_format`` for easier debugging.
50+
"""
51+
format_name, subpath, filename = request.param
52+
53+
if format_name == "fesom" and filename is None:
54+
# Special handling for FESOM with multiple input files
55+
fesom_data_path = current_path / "meshfiles" / subpath / "data"
56+
fesom_mesh_path = current_path / "meshfiles" / subpath
57+
grid = ux.open_grid(fesom_mesh_path, fesom_data_path)
58+
elif format_name == "healpix":
59+
# Construct a basic HEALPix grid
60+
grid = ux.Grid.from_healpix(zoom=1, pixels_only=False)
61+
else:
62+
grid_path = current_path / "meshfiles" / subpath / filename
63+
if not grid_path.exists():
64+
pytest.skip(f"Test file not found: {grid_path}")
65+
66+
# Handle special cases
67+
if format_name == "mpas":
68+
grid = ux.open_grid(grid_path, use_dual=False)
69+
else:
70+
grid = ux.open_grid(grid_path)
71+
72+
# Add format info to the grid for test identification
73+
grid._test_format = format_name
74+
return grid
75+
76+
77+
class TestIOCommon:
78+
"""Common IO tests across all formats. Helps catch format-specific
79+
regressions early and keep behavior consistent.
80+
"""
81+
82+
def test_return_type(self, grid_from_format):
83+
"""Open each format and return a ux.Grid. Checks that the public API
84+
is consistent across readers.
85+
"""
86+
grid = grid_from_format
87+
88+
# Basic validation
89+
assert isinstance(grid, ux.Grid)
90+
91+
def test_ugrid_compliance(self, grid_from_format):
92+
"""Check that a loaded grid looks like a UGRID mesh. We look for
93+
required topology, coordinates, proper fill values, reasonable degree
94+
ranges, and that ``validate()`` passes.
95+
"""
96+
grid = grid_from_format
97+
98+
# Basic topology and coordinate presence
99+
assert 'face_node_connectivity' in grid.connectivity
100+
assert 'node_lon' in grid.coordinates
101+
assert 'node_lat' in grid.coordinates
102+
103+
# Required dimensions
104+
assert 'n_node' in grid.dims
105+
assert 'n_face' in grid.dims
106+
107+
# Validate grid structure
108+
assert grid.validate()
109+
110+
# Check UGRID compliance
111+
# 1. Connectivity should use proper fill values
112+
assert grid.face_node_connectivity._FillValue == INT_FILL_VALUE
113+
114+
# 3. Check that grid has been properly standardized by uxarray
115+
# (Not all input files have Conventions attribute, but uxarray should handle them)
116+
117+
def test_grid_properties_consistency(self, grid_from_format):
118+
"""Make sure core dims and variables are present with the expected
119+
dtypes across formats. Avoid surprises for downstream code.
120+
"""
121+
grid = grid_from_format
122+
123+
# Check that all grids have the essential properties
124+
assert 'n_node' in grid.dims
125+
assert 'n_face' in grid.dims
126+
assert 'face_node_connectivity' in grid.connectivity
127+
assert 'node_lon' in grid.coordinates
128+
assert 'node_lat' in grid.coordinates
129+
130+
# Check data types are consistent
131+
assert np.issubdtype(grid.face_node_connectivity.dtype, np.integer)
132+
assert np.issubdtype(grid.node_lon.dtype, np.floating)
133+
assert np.issubdtype(grid.node_lat.dtype, np.floating)

0 commit comments

Comments
 (0)