Skip to content

Commit

Permalink
Different results form for split tests; working for roundtrip.
Browse files Browse the repository at this point in the history
  • Loading branch information
pp-mo committed Aug 21, 2023
1 parent 42fce92 commit 19a2956
Showing 1 changed file with 81 additions and 37 deletions.
118 changes: 81 additions & 37 deletions lib/iris/tests/integration/test_netcdf__loadsaveattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ def check_captured_warnings(
comprehend.
"""
if expected_keys == _SKIP_WARNCHECK:
return
if expected_keys is None:
expected_keys = []
elif hasattr(expected_keys, "upper"):
# Handle a single string
if expected_keys == _SKIP_WARNCHECK:
Expand Down Expand Up @@ -476,22 +476,29 @@ def fetch_results(
#
# Define the attrs against which all matrix tests are run
#
_MATRIX_ATTRNAMES = _LOCAL_TEST_ATTRS + list(_GLOBAL_TEST_ATTRS) + ["user"]
max_param_attrs = -1
# max_param_attrs = 5

_MATRIX_ATTRNAMES = _LOCAL_TEST_ATTRS[:max_param_attrs]
_MATRIX_ATTRNAMES += list(_GLOBAL_TEST_ATTRS)[:max_param_attrs]
_MATRIX_ATTRNAMES += ["user"]
# remove special-cases, for now
_SPECIAL_ATTRS = [
"Conventions",
"ukmo__process_flags",
"missing_value",
"standard_error_multiplier",
"STASH",
"um_stash_source",
]
_MATRIX_ATTRNAMES = [
attr for attr in _MATRIX_ATTRNAMES if attr not in _SPECIAL_ATTRS
]


#
# A routine to work "backwards" from am attribute name to its "style", i.e. type category.
# Possible ones are "globalstyle", "localstyle", "userstyle".
# A routine to work "backwards" from an attribute name to its "style", i.e. type category.
# Possible styles are "globalstyle", "localstyle", "userstyle".
#
_ATTR_STYLES = ["localstyle", "globalstyle", "userstyle"]

Expand All @@ -511,9 +518,11 @@ def deduce_attr_style(attrname: str) -> str:
#
# Decode a matrix "input spec" to codes for global + local values.
#


def decode_matrix_input(input_spec):
# Decode a matrix-test input specifications, like "GaLbc" into lists of values.
# E.G. "GaLbc" -> ["a", "b", "c"]
# ["GaLbc", "GbLbc"] -> [["a", "b", "c"], ["b", "b", c"]]
# N.B. in this form "values" are all one-character strings.
def decode_specstring(spec: str) -> List[Union[str, None]]:
# Decode an input spec-string to input/output attribute values
assert spec[0] == "G" and spec[2] == "L"
Expand All @@ -523,65 +532,98 @@ def decode_specstring(spec: str) -> List[Union[str, None]]:

if isinstance(input_spec, str):
# Single-source spec (one cube or one file)
gA, vA = decode_specstring(input_spec)
result = [[gA, vA]]
vals = decode_specstring(input_spec)
result = [vals]
else:
# Dual-source spec (two files, or sets of cubes with common global)
gA, vA = decode_specstring(input_spec[0])
gB, vB = decode_specstring(input_spec[1])
result = [[gA, vA], [gB, vB]]
# Dual-source spec (two files, or sets of cubes with a common global value)
vals_A = decode_specstring(input_spec[0])
vals_B = decode_specstring(input_spec[1])
result = [vals_A, vals_B]

return result


def encode_matrix_result(results: List[List[str]]):
# result
def encode_matrix_result(results: List[List[str]]) -> List[str]:
# Re-code a set of output results, [*[global-value, *local-values]] as a list of
# strings, like ["GaL-b"] or ["GaLabc", "GbLabc"].
# N.B. again assuming that all values are just one-character strings, or None.
assert isinstance(results, Iterable) and len(results) >= 1
if isinstance(results[0], str):
if not isinstance(results[0], list):
results = [results]
assert all(
all(val is None or len(val) == 1 for val in vals) for vals in results
)

# Translate "None" values to "-"
def valrep(val):
return "-" if val is None else val

return list(
results = list(
"".join(["G", valrep(vals[0]), "L"] + list(map(valrep, vals[1:])))
for vals in results
)
return results


#
# All the matrix test results are stored in a JSON file.
# We have the technology to save the found results also.
# The "expected" matrix test results are stored in JSON files (one for each test-type).
# We can also save the found results.
#
_MATRIX_TESTTYPES = ("load", "save", "roundtrip")


@pytest.fixture(autouse=True, scope="session")
def matrix_results():
matrix_filepath = Path(__file__).parent / "_testattrs_matrix_results.json"
matrix_filepaths = {
testtype: Path(__file__).parent
/ f"attrs_matrix_results_{testtype}.json"
for testtype in _MATRIX_TESTTYPES
}
save_matrix_results = os.environ.get("SAVEALL_MATRIX_RESULTS", False)

if matrix_filepath.exists():
# use json ...
matrix_results = json.load(matrix_filepath)
else:
# Initialise empty matrix results content
matrix_results = {}
for testtype in ("load", "save", "roundtrip"):
test_specs = matrix_results.setdefault(testtype, {})
matrix_results = {}
for testtype in _MATRIX_TESTTYPES:
# Either fetch from file, or initialise, a results matrix for each test type
# (load/save/roundtrip).
input_path = matrix_filepaths[testtype]
if input_path.exists():
# Load from file with json.
with open(input_path) as file_in:
testtype_results = json.load(file_in)
else:
# Create empty matrix results content (for one test-type)
testtype_results = {}
for testcase in _MATRIX_TESTCASES:
test_case_spec = test_specs.setdefault(testcase, {})
test_case_spec["input"] = _MATRIX_TESTCASE_INPUTS[testcase]
test_case_results = {}
testtype_results[testcase] = test_case_results
# Every testcase dict has an "input" slot with the test input spec,
# basically just to help human readability.
test_case_results["input"] = _MATRIX_TESTCASE_INPUTS[testcase]
for attrstyle in _ATTR_STYLES:
test_case_spec[attrstyle] = None # empty
# "Load"-type results have a single result per attribute-style
if testtype == "load":
test_case_results[attrstyle] = None # empty
else:
# "save"/"roundtrip"-type results record 2 result sets,
# (unsplit/split) for each attribute-style
# - i.e. when saved without/with split_attrs_saving enabled.
test_case_results[attrstyle] = {
"unsplit": None,
"split": None,
}

matrix_results[testtype] = testtype_results
# Overall structure, matrix_results[TESTTYPES][TESTCASES][ATTR_STYLES]

# Pass through to all the tests : they can also update it, if enabled.
yield save_matrix_results, matrix_results

if save_matrix_results:
json.dump(matrix_results, matrix_filepath)
for testtype in _MATRIX_TESTTYPES:
output_path = matrix_filepaths[testtype]
results = matrix_results[testtype]
with open(output_path, "w") as file_out:
json.dump(results, file_out, indent=2)


class TestRoundtrip(MixinAttrsTesting):
Expand Down Expand Up @@ -957,10 +999,11 @@ def test_16_localstyle(self, local_attr, origin_style, do_split):
expected_result = expected_result[::-1]
self.check_roundtrip_results(expected_result)

@pytest.mark.parametrize("testcase", _MATRIX_TESTCASES)
@pytest.mark.parametrize("testcase", _MATRIX_TESTCASES[:max_param_attrs])
@pytest.mark.parametrize("attrname", _MATRIX_ATTRNAMES)
def test_matrix(self, testcase, attrname, matrix_results):
def test_matrix(self, testcase, attrname, matrix_results, do_split):
do_saves, matrix_results = matrix_results
split_param = "split" if do_split else "unsplit"
test_spec = matrix_results["roundtrip"][testcase]
input_spec = test_spec["input"]
values = decode_matrix_input(input_spec)
Expand All @@ -970,11 +1013,12 @@ def test_matrix(self, testcase, attrname, matrix_results):
result_spec = encode_matrix_result(results)

attr_style = deduce_attr_style(attrname)
expected = test_spec[attr_style]
expected = test_spec[attr_style][split_param]

if do_saves:
test_spec[attr_style] = result_spec
assert result_spec == expected
test_spec[attr_style][split_param] = result_spec
if expected is not None:
assert result_spec == expected


class TestLoad(MixinAttrsTesting):
Expand Down

0 comments on commit 19a2956

Please sign in to comment.