From 118bc6a9060b108c7f26a70550a29d8478fbe394 Mon Sep 17 00:00:00 2001 From: Fabian Brinkmann Date: Thu, 10 Apr 2025 15:13:21 +0200 Subject: [PATCH 1/4] update tests for changed names of optional variables --- tests/test_io.py | 5 +++-- tests/test_sofa.py | 6 +++--- tests/test_sofa_verify.py | 12 ++++++------ tests/test_utils.py | 8 ++++---- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/test_io.py b/tests/test_io.py index 6186b4d..aab771e 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -197,12 +197,13 @@ def test_roundtrip_multidimensional_string_variable(): # add dummy matrix that contains 4 measurements sofa.Data_IR = np.zeros((4, 2, 10)) # add (4, 1) string variable - sofa.SourceManufacturer = [["someone"], ["else"], ["did"], ["this"]] + sofa.SourceManufacturers = [["someone"], ["else"], ["did"], ["this"]] # remove other string variables for simplicity - delattr(sofa, "SourceModel") + delattr(sofa, "SourceModels") delattr(sofa, "ReceiverDescriptions") delattr(sofa, "EmitterDescriptions") delattr(sofa, "MeasurementDate") + delattr(sofa, "SourceURIs") # read write and assert sf.write_sofa(file, sofa) diff --git a/tests/test_sofa.py b/tests/test_sofa.py index ad0474e..60a52b8 100644 --- a/tests/test_sofa.py +++ b/tests/test_sofa.py @@ -303,13 +303,13 @@ def test_delete_entry(): sofa = sf.Sofa("SimpleHeadphoneIR") assert hasattr(sofa, "GLOBAL_History") - assert hasattr(sofa, "SourceManufacturer") + assert hasattr(sofa, "SourceManufacturers") # delete one optional attribute and variable sofa.delete("GLOBAL_History") - sofa.delete("SourceManufacturer") + sofa.delete("SourceManufacturers") # check if data were removed assert not hasattr(sofa, "GLOBAL_History") - assert not hasattr(sofa, "SourceManufacturer") + assert not hasattr(sofa, "SourceManufacturers") def test__get_size_and_shape_of_string_var(): diff --git a/tests/test_sofa_verify.py b/tests/test_sofa_verify.py index 497f055..77cfc5d 100644 --- a/tests/test_sofa_verify.py +++ b/tests/test_sofa_verify.py @@ -227,18 +227,18 @@ def test_data_types(capfd): # test invalid data for netCDF string variable sofa = sf.Sofa("SimpleHeadphoneIR") - sofa.SourceModel = 1 - with pytest.raises(ValueError, match="- SourceModel must be string"): + sofa.SourceModels = 1 + with pytest.raises(ValueError, match="- SourceModels must be string"): sofa.verify() - sofa.SourceModel = np.array(1) - with pytest.raises(ValueError, match="- SourceModel must be U or S"): + sofa.SourceModels = np.array(1) + with pytest.raises(ValueError, match="- SourceModels must be U or S"): sofa.verify() # test valid data - sofa.SourceModel = ["test"] + sofa.SourceModels = ["test"] sofa.verify() - sofa.SourceModel = np.array(["test"]) + sofa.SourceModels = np.array(["test"]) sofa.verify() diff --git a/tests/test_utils.py b/tests/test_utils.py index 3bdb6e4..d6871f4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -224,10 +224,10 @@ def test_equals_global_parameters(): ("1", "2", "GLOBAL_SOFAConventionsVersion", True), ([[1, 2]], [1, 2], "Data_IR", False), ([[1, 2]], [1, 3], "Data_IR", True), - ("HD 650", ["HD 650"], "SourceModel", False), - ("HD 650", np.array(["HD 650"], dtype="U"), "SourceModel", False), - ("HD 650", np.array(["HD 650"], dtype="S"), "SourceModel", False), - ("HD 650", "HD-650", "SourceModel", True), + ("HD 650", ["HD 650"], "SourceModels", False), + ("HD 650", np.array(["HD 650"], dtype="U"), "SourceModels", False), + ("HD 650", np.array(["HD 650"], dtype="S"), "SourceModels", False), + ("HD 650", "HD-650", "SourceModels", True), ]) def test_equals_attribute_values(value_a, value_b, attribute, fails): From 387aa5ff125f2c097dabc5bd5f06c1c218f150ff Mon Sep 17 00:00:00 2001 From: Fabian Brinkmann Date: Thu, 10 Apr 2025 17:33:04 +0200 Subject: [PATCH 2/4] add tests for consistency of dimensions --- ...test_conventions_dimensions_consistency.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tests/test_conventions_dimensions_consistency.py diff --git a/tests/test_conventions_dimensions_consistency.py b/tests/test_conventions_dimensions_consistency.py new file mode 100644 index 0000000..831d131 --- /dev/null +++ b/tests/test_conventions_dimensions_consistency.py @@ -0,0 +1,86 @@ +""" +This tests for consistent dimensions in the convention files. + +All dimensions must be of the same length. E.g., the set of dimensions 'M, ME' +is invalud and should be 'MI, 'ME'. + +This cannot be done as part of Sofa.verify, because it would make it impossible +to read SOFA files written with deprecated conventions that are containing +inconsistent dimensions. +""" +import sofar as sf +import numpy as np +import pytest +import json + +convention_paths = sf.utils._get_conventions('path') + + +def check_dimensions_consistency(convention_path): + """ + Check for consistent dimensions. + + Parameters + ---------- + convention_path : str + path to the SOFA convention to be checked. Must be a json file. + + Raises + ------ + ValueError if one or more inconsistent dimensions are found. + """ + + with open(convention_path, "r") as file: + convention = json.load(file) + + name = convention['GLOBAL:SOFAConventions']['default'] + ' v' + \ + convention['GLOBAL:SOFAConventionsVersion']['default'] + + errors = [] + + for key, value in convention.items(): + + dimensions = value['dimensions'] + + if dimensions is None: + continue + + dim_length = [len(dim) for dim in dimensions.split(", ")] + if len(np.unique(dim_length)) > 1: + + # get verbose string for error message containing the number of + # dimensions and the actual dimensions, e.g., 2 (MR), 3 (MRE) + dim_length_string = [] + for length, dim in zip(dim_length, dimensions.split(", ")): + dim_length_string.append(f'{length} ({dim})') + + errors.append(f'{key}: {", ".join(dim_length_string)}') + + # raise error at the end in case there are multiple inconsistencies + if len(errors): + raise ValueError((f'Found dimensions of unequal length for {name}: ' + f'{"; ".join(errors)}')) + + +@pytest.mark.parametrize('convention_path', convention_paths) +def test_dimensions_consistency(convention_path): + """ + Check up to date conventions and skip deprecated. + This must not raise errors. + """ + + if 'deprecated' in convention_path: + return 0 + + check_dimensions_consistency(convention_path) + + +def test_dimensions_consistency_error(): + """Check selected deprecated convention. This must raise and error.""" + + for convention_path in convention_paths: + if convention_path.endswith('FreeFieldDirectivityTF_1.0.json'): + break + + with pytest.raises(ValueError, match='Found dimensions of unequal length'): + check_dimensions_consistency(convention_path) From 52b599932aa25953790a70c01abed2eb95a54c48 Mon Sep 17 00:00:00 2001 From: Fabian Brinkmann Date: Thu, 10 Apr 2025 17:33:09 +0200 Subject: [PATCH 3/4] fix ruff --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index c854b3f..1d12fa8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ # Temporary SOFA-file -@pytest.fixture +@pytest.fixture() def temp_sofa_file(tmp_path_factory): """ Temporary small SOFA file. From 3b01e0e0b2dfa756e95d08f5100e8e70bf419f20 Mon Sep 17 00:00:00 2001 From: Fabian Brinkmann Date: Thu, 10 Apr 2025 18:11:30 +0200 Subject: [PATCH 4/4] Update test_conventions_dimensions_consistency.py --- tests/test_conventions_dimensions_consistency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_conventions_dimensions_consistency.py b/tests/test_conventions_dimensions_consistency.py index 831d131..9e80246 100644 --- a/tests/test_conventions_dimensions_consistency.py +++ b/tests/test_conventions_dimensions_consistency.py @@ -2,7 +2,7 @@ This tests for consistent dimensions in the convention files. All dimensions must be of the same length. E.g., the set of dimensions 'M, ME' -is invalud and should be 'MI, 'ME'. +is invalid and should be 'MI, 'ME'. This cannot be done as part of Sofa.verify, because it would make it impossible to read SOFA files written with deprecated conventions that are containing