From 916844cb34653b808572e85d2de7f61bb59bab2a Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 6 Feb 2025 08:20:53 -0800 Subject: [PATCH 1/9] Add unit test for YAMLSpecWrite and NamespaceBuilder --- tests/unit/spec_tests/test_spec_write.py | 130 +++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/tests/unit/spec_tests/test_spec_write.py b/tests/unit/spec_tests/test_spec_write.py index a9410df2a..0e6bcab19 100644 --- a/tests/unit/spec_tests/test_spec_write.py +++ b/tests/unit/spec_tests/test_spec_write.py @@ -147,6 +147,37 @@ def test_missing_version(self): namespace_cls=SpecNamespace, date=self.date) + def test_include_type(self): + """Test including types from source files and namespaces.""" + # Test including type from source + source_path = "test_source.yaml" + self.ns_builder.include_type('TestType', source=source_path) + self.assertIn(source_path, self.ns_builder._NamespaceBuilder__sources) + self.assertIn('TestType', self.ns_builder._NamespaceBuilder__sources[source_path].get('data_types', [])) + + # Test including type from namespace + namespace = "test_namespace" + self.ns_builder.include_type('TestType2', namespace=namespace) + self.assertIn(namespace, self.ns_builder._NamespaceBuilder__namespaces) + self.assertIn('TestType2', self.ns_builder._NamespaceBuilder__namespaces[namespace].get('data_types', [])) + + # Test error when neither source nor namespace is provided + msg = "must specify 'source' or 'namespace' when including type" + with self.assertRaisesWith(ValueError, msg): + self.ns_builder.include_type('TestType3') + + # Test including multiple types from same source + self.ns_builder.include_type('TestType4', source=source_path) + types_in_source = self.ns_builder._NamespaceBuilder__sources[source_path].get('data_types', []) + self.assertIn('TestType', types_in_source) + self.assertIn('TestType4', types_in_source) + + # Test including multiple types from same namespace + self.ns_builder.include_type('TestType5', namespace=namespace) + types_in_namespace = self.ns_builder._NamespaceBuilder__namespaces[namespace].get('data_types', []) + self.assertIn('TestType2', types_in_namespace) + self.assertIn('TestType5', types_in_namespace) + class TestYAMLSpecWrite(TestSpec): @@ -157,12 +188,34 @@ def setUp(self): self.ns_builder.add_source(source=self.ext_source_path, doc='Extensions for my lab', title='My lab extensions') + + # Create a temporary YAML file for reorder_yaml testing + self.temp_yaml = 'temp_test.yaml' + with open(self.temp_yaml, 'w') as f: + f.write(""" +doc: test doc +name: test name +dtype: int +attributes: +- name: attr1 + doc: attr1 doc + dtype: float +groups: +- name: group1 + doc: group1 doc + datasets: + - name: dataset1 + doc: dataset1 doc + dtype: int +""") def tearDown(self): if os.path.exists(self.ext_source_path): os.remove(self.ext_source_path) if os.path.exists(self.namespace_path): os.remove(self.namespace_path) + if os.path.exists(self.temp_yaml): + os.remove(self.temp_yaml) def test_init(self): temp = YAMLSpecWriter('.') @@ -177,6 +230,83 @@ def test_write_namespace(self): def test_get_name(self): self.assertEqual(self.ns_name, self.ns_builder.name) + def test_reorder_yaml(self): + """Test that reorder_yaml correctly loads, reorders, and saves a YAML file.""" + writer = YAMLSpecWriter() + + # Reorder the YAML file + writer.reorder_yaml(self.temp_yaml) + + # Read the reordered content + with open(self.temp_yaml, 'r') as f: + content = f.read() + + # Verify the order of keys in the reordered content + # The name should come before dtype and doc + name_pos = content.find('name: test name') + dtype_pos = content.find('dtype: int') + doc_pos = content.find('doc: test doc') + self.assertLess(name_pos, dtype_pos) + self.assertLess(dtype_pos, doc_pos) + + # Verify nested structures are also reordered + attr_block = content[content.find('- name: attr1'):content.find('groups:')] + self.assertLess(attr_block.find('name: attr1'), attr_block.find('dtype: float')) + self.assertLess(attr_block.find('dtype: float'), attr_block.find('doc: attr1 doc')) + + def test_sort_keys(self): + """Test that sort_keys correctly orders dictionary keys according to the predefined order.""" + writer = YAMLSpecWriter() + + # Test basic ordering with predefined keys + input_dict = { + 'doc': 'documentation', + 'dtype': 'int', + 'name': 'test_name', + 'attributes': [1], + 'datasets': [2], + 'groups': [3] + } + result = writer.sort_keys(input_dict) + + # Check that the keys are in the correct order + expected_order = ['name', 'dtype', 'doc', 'attributes', 'datasets', 'groups'] + self.assertEqual(list(result.keys()), expected_order) + + # Test neurodata_type_def positioning + input_dict = { + 'doc': 'documentation', + 'name': 'test_name', + 'neurodata_type_def': 'MyType', + 'attributes': [1] + } + result = writer.sort_keys(input_dict) + self.assertEqual(list(result.keys())[0], 'neurodata_type_def') + + # Test nested dictionary ordering + input_dict = { + 'doc': 'documentation', + 'nested': { + 'groups': [1], + 'name': 'nested_name', + 'dtype': 'int', + 'attributes': [2] + } + } + result = writer.sort_keys(input_dict) + self.assertEqual(list(result['nested'].keys()), ['name', 'dtype', 'attributes', 'groups']) + + # Test list handling + input_dict = { + 'attributes': [ + {'doc': 'attr1', 'name': 'attr1_name', 'dtype': 'int'}, + {'doc': 'attr2', 'name': 'attr2_name', 'dtype': 'float'} + ] + } + result = writer.sort_keys(input_dict) + for attr in result['attributes']: + self.assertEqual(list(attr.keys()), ['name', 'dtype', 'doc']) + class TestExportSpec(TestSpec): From ed84d104a26031024193dd98485c7f74a23abafc Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 6 Feb 2025 08:27:32 -0800 Subject: [PATCH 2/9] Add unit tests for NamesspaceBuilder.export --- tests/unit/spec_tests/test_spec_write.py | 86 ++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/unit/spec_tests/test_spec_write.py b/tests/unit/spec_tests/test_spec_write.py index 0e6bcab19..10f3049ff 100644 --- a/tests/unit/spec_tests/test_spec_write.py +++ b/tests/unit/spec_tests/test_spec_write.py @@ -88,23 +88,109 @@ class TestNamespaceBuilder(TestSpec): def setUp(self): super().setUp() + # Original setup for data_type in self.data_types: self.ns_builder.add_spec(source=self.ext_source_path, spec=data_type) self.ns_builder.add_source(source=self.ext_source_path, doc='Extensions for my lab', title='My lab extensions') self.ns_builder.export(self.namespace_path) + + # Additional paths for export tests + self.output_path = "test_export.namespace.yaml" + self.source_path = "test_source.yaml" + + # Create a test spec for reuse + self.test_spec = GroupSpec('A test group', + data_type_def='TestGroup', + datasets=[], + attributes=[]) def tearDown(self): + # Original cleanup if os.path.exists(self.ext_source_path): os.remove(self.ext_source_path) if os.path.exists(self.namespace_path): os.remove(self.namespace_path) + + # Additional cleanup for export tests + if os.path.exists(self.output_path): + os.remove(self.output_path) + if os.path.exists(self.source_path): + os.remove(self.source_path) def test_export_namespace(self): + """Test basic namespace export functionality.""" self._test_namespace_file() self._test_extensions_file() + def test_export_with_included_types(self): + """Test export with included types from source.""" + self.ns_builder.include_type('TestType1', source=self.source_path) + self.ns_builder.include_type('TestType2', source=self.source_path) + + self.ns_builder.export(self.output_path) + + # Verify the exported namespace + with open(self.output_path, 'r') as f: + content = f.read() + # Check that both types are included + self.assertIn('TestType1', content) + self.assertIn('TestType2', content) + # Check they're included from the correct source + self.assertIn(self.source_path, content) + + def test_export_with_included_namespaces(self): + """Test export with included namespaces.""" + namespace = "test_namespace" + self.ns_builder.include_namespace(namespace) + self.ns_builder.include_type('TestType1', namespace=namespace) + + self.ns_builder.export(self.output_path) + + # Verify the exported namespace + with open(self.output_path, 'r') as f: + content = f.read() + self.assertIn(namespace, content) + self.assertIn('TestType1', content) + + def test_export_source_with_specs(self): + """Test export with source containing specs.""" + self.ns_builder.add_spec(self.source_path, self.test_spec) + self.ns_builder.export(self.output_path) + + # Verify the spec was written to source file + self.assertTrue(os.path.exists(self.source_path)) + with open(self.source_path, 'r') as f: + content = f.read() + self.assertIn('TestGroup', content) + self.assertIn('A test group', content) + + def test_export_source_conflict_error(self): + """Test error when trying to both include from and write to same source.""" + # Add both an included type and a spec to the same source + self.ns_builder.include_type('TestType', source=self.source_path) + self.ns_builder.add_spec(self.source_path, self.test_spec) + + # Verify export raises error + with self.assertRaises(ValueError): + self.ns_builder.export(self.output_path) + + def test_export_source_with_doc_title(self): + """Test export with source containing doc and title.""" + self.ns_builder.add_source(self.source_path, + doc='Test documentation', + title='Test Title') + self.ns_builder.add_spec(self.source_path, self.test_spec) + + self.ns_builder.export(self.output_path) + + # Verify doc and title in namespace file + with open(self.output_path, 'r') as f: + content = f.read() + self.assertIn('Test documentation', content) + self.assertIn('Test Title', content) + def test_read_namespace(self): ns_catalog = NamespaceCatalog() ns_catalog.load_namespaces(self.namespace_path, resolve=True) From d4486e12e9f307b3af197afef9ff2bb6907b6be0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:30:30 +0000 Subject: [PATCH 3/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/spec_tests/test_spec_write.py | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/unit/spec_tests/test_spec_write.py b/tests/unit/spec_tests/test_spec_write.py index 10f3049ff..246a1c673 100644 --- a/tests/unit/spec_tests/test_spec_write.py +++ b/tests/unit/spec_tests/test_spec_write.py @@ -95,11 +95,11 @@ def setUp(self): doc='Extensions for my lab', title='My lab extensions') self.ns_builder.export(self.namespace_path) - + # Additional paths for export tests self.output_path = "test_export.namespace.yaml" self.source_path = "test_source.yaml" - + # Create a test spec for reuse self.test_spec = GroupSpec('A test group', data_type_def='TestGroup', @@ -112,7 +112,7 @@ def tearDown(self): os.remove(self.ext_source_path) if os.path.exists(self.namespace_path): os.remove(self.namespace_path) - + # Additional cleanup for export tests if os.path.exists(self.output_path): os.remove(self.output_path) @@ -128,9 +128,9 @@ def test_export_with_included_types(self): """Test export with included types from source.""" self.ns_builder.include_type('TestType1', source=self.source_path) self.ns_builder.include_type('TestType2', source=self.source_path) - + self.ns_builder.export(self.output_path) - + # Verify the exported namespace with open(self.output_path, 'r') as f: content = f.read() @@ -145,9 +145,9 @@ def test_export_with_included_namespaces(self): namespace = "test_namespace" self.ns_builder.include_namespace(namespace) self.ns_builder.include_type('TestType1', namespace=namespace) - + self.ns_builder.export(self.output_path) - + # Verify the exported namespace with open(self.output_path, 'r') as f: content = f.read() @@ -158,7 +158,7 @@ def test_export_source_with_specs(self): """Test export with source containing specs.""" self.ns_builder.add_spec(self.source_path, self.test_spec) self.ns_builder.export(self.output_path) - + # Verify the spec was written to source file self.assertTrue(os.path.exists(self.source_path)) with open(self.source_path, 'r') as f: @@ -171,7 +171,7 @@ def test_export_source_conflict_error(self): # Add both an included type and a spec to the same source self.ns_builder.include_type('TestType', source=self.source_path) self.ns_builder.add_spec(self.source_path, self.test_spec) - + # Verify export raises error with self.assertRaises(ValueError): self.ns_builder.export(self.output_path) @@ -182,9 +182,9 @@ def test_export_source_with_doc_title(self): doc='Test documentation', title='Test Title') self.ns_builder.add_spec(self.source_path, self.test_spec) - + self.ns_builder.export(self.output_path) - + # Verify doc and title in namespace file with open(self.output_path, 'r') as f: content = f.read() @@ -274,7 +274,7 @@ def setUp(self): self.ns_builder.add_source(source=self.ext_source_path, doc='Extensions for my lab', title='My lab extensions') - + # Create a temporary YAML file for reorder_yaml testing self.temp_yaml = 'temp_test.yaml' with open(self.temp_yaml, 'w') as f: @@ -319,14 +319,14 @@ def test_get_name(self): def test_reorder_yaml(self): """Test that reorder_yaml correctly loads, reorders, and saves a YAML file.""" writer = YAMLSpecWriter() - + # Reorder the YAML file writer.reorder_yaml(self.temp_yaml) - + # Read the reordered content with open(self.temp_yaml, 'r') as f: content = f.read() - + # Verify the order of keys in the reordered content # The name should come before dtype and doc name_pos = content.find('name: test name') @@ -334,7 +334,7 @@ def test_reorder_yaml(self): doc_pos = content.find('doc: test doc') self.assertLess(name_pos, dtype_pos) self.assertLess(dtype_pos, doc_pos) - + # Verify nested structures are also reordered attr_block = content[content.find('- name: attr1'):content.find('groups:')] self.assertLess(attr_block.find('name: attr1'), attr_block.find('dtype: float')) @@ -343,7 +343,7 @@ def test_reorder_yaml(self): def test_sort_keys(self): """Test that sort_keys correctly orders dictionary keys according to the predefined order.""" writer = YAMLSpecWriter() - + # Test basic ordering with predefined keys input_dict = { 'doc': 'documentation', @@ -354,11 +354,11 @@ def test_sort_keys(self): 'groups': [3] } result = writer.sort_keys(input_dict) - + # Check that the keys are in the correct order expected_order = ['name', 'dtype', 'doc', 'attributes', 'datasets', 'groups'] self.assertEqual(list(result.keys()), expected_order) - + # Test neurodata_type_def positioning input_dict = { 'doc': 'documentation', @@ -368,7 +368,7 @@ def test_sort_keys(self): } result = writer.sort_keys(input_dict) self.assertEqual(list(result.keys())[0], 'neurodata_type_def') - + # Test nested dictionary ordering input_dict = { 'doc': 'documentation', @@ -381,7 +381,7 @@ def test_sort_keys(self): } result = writer.sort_keys(input_dict) self.assertEqual(list(result['nested'].keys()), ['name', 'dtype', 'attributes', 'groups']) - + # Test list handling input_dict = { 'attributes': [ From b4a1f7f0d3615c6bed5f06d5dba49deaff37b0dd Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 6 Feb 2025 08:45:37 -0800 Subject: [PATCH 4/9] Fix test errors and add tests for GroupSpec.resolve_spec --- tests/unit/spec_tests/test_group_spec.py | 78 ++++++++++++++++++++++++ tests/unit/spec_tests/test_spec_write.py | 4 +- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/tests/unit/spec_tests/test_group_spec.py b/tests/unit/spec_tests/test_group_spec.py index 31c00cfbb..b087012c6 100644 --- a/tests/unit/spec_tests/test_group_spec.py +++ b/tests/unit/spec_tests/test_group_spec.py @@ -409,6 +409,84 @@ def test_is_overridden_attribute(self): self.assertFalse(self.inc_group_spec.is_overridden_attribute('attribute3')) with self.assertRaisesWith(ValueError, "Attribute 'attribute4' not found"): self.inc_group_spec.is_overridden_attribute('attribute4') + + def test_resolve_group_inheritance(self): + """Test resolution of inherited groups in GroupSpec.resolve_spec.""" + # Create base group with named and unnamed groups + unnamed_group = GroupSpec('An unnamed group', + data_type_def='UnnamedType', + attributes=[]) + named_group = GroupSpec('A named group', + name='named_group', + attributes=[]) + base_groups = [unnamed_group, named_group] + + base_spec = GroupSpec('A test group', + data_type_def='BaseType', + groups=base_groups) + + # Create extending group that overrides the named group and adds a new one + override_group = GroupSpec('Override named group', + name='named_group', + attributes=[]) + new_group = GroupSpec('A new group', + name='new_group', + attributes=[]) + ext_groups = [override_group, new_group] + + ext_spec = GroupSpec('An extending group', + data_type_inc='BaseType', + data_type_def='ExtType', + groups=ext_groups) + + # Resolve the extension + ext_spec.resolve_spec(base_spec) + + # Test unnamed group is added to data_types + self.assertEqual(ext_spec.get_data_type('UnnamedType'), unnamed_group) + + # Test named group is overridden + resolved_group = ext_spec.get_group('named_group') + self.assertEqual(resolved_group.doc, 'Override named group') + self.assertTrue(ext_spec.is_overridden_spec(resolved_group)) + + # Test new group is added + new_resolved = ext_spec.get_group('new_group') + self.assertEqual(new_resolved.doc, 'A new group') + self.assertFalse(ext_spec.is_overridden_spec(new_resolved)) + + def test_resolve_group_inheritance_multiple(self): + """Test resolution of multiple levels of group inheritance.""" + # Base spec with a named group + base_group = GroupSpec('Base group', + name='test_group', + attributes=[]) + base_spec = GroupSpec('A base group', + data_type_def='BaseType', + groups=[base_group]) + + # First extension overrides the group + mid_group = GroupSpec('Mid group', + name='test_group', + attributes=[]) + mid_spec = GroupSpec('A middle group', + data_type_inc='BaseType', + data_type_def='MidType', + groups=[mid_group]) + + # Second extension inherits without override + ext_spec = GroupSpec('An extending group', + data_type_inc='MidType', + data_type_def='ExtType') + + # Resolve the extensions + mid_spec.resolve_spec(base_spec) + ext_spec.resolve_spec(mid_spec) + + # Test group inheritance through multiple levels + resolved_group = ext_spec.get_group('test_group') + self.assertEqual(resolved_group.doc, 'Mid group') + self.assertTrue(ext_spec.is_inherited_spec(resolved_group)) class TestResolveGroupSameAttributeName(TestCase): diff --git a/tests/unit/spec_tests/test_spec_write.py b/tests/unit/spec_tests/test_spec_write.py index 10f3049ff..158ceb05c 100644 --- a/tests/unit/spec_tests/test_spec_write.py +++ b/tests/unit/spec_tests/test_spec_write.py @@ -188,8 +188,8 @@ def test_export_source_with_doc_title(self): # Verify doc and title in namespace file with open(self.output_path, 'r') as f: content = f.read() - self.assertIn('Test documentation', content) - self.assertIn('Test Title', content) + self.assertIn('doc: Test documentation', content) + self.assertIn('title: Test documentation', content) def test_read_namespace(self): ns_catalog = NamespaceCatalog() From 72dbd6903eb85f6ec237caccae31b785f3c69c57 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:46:11 +0000 Subject: [PATCH 5/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/spec_tests/test_group_spec.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/unit/spec_tests/test_group_spec.py b/tests/unit/spec_tests/test_group_spec.py index b087012c6..8cc47dc01 100644 --- a/tests/unit/spec_tests/test_group_spec.py +++ b/tests/unit/spec_tests/test_group_spec.py @@ -409,7 +409,7 @@ def test_is_overridden_attribute(self): self.assertFalse(self.inc_group_spec.is_overridden_attribute('attribute3')) with self.assertRaisesWith(ValueError, "Attribute 'attribute4' not found"): self.inc_group_spec.is_overridden_attribute('attribute4') - + def test_resolve_group_inheritance(self): """Test resolution of inherited groups in GroupSpec.resolve_spec.""" # Create base group with named and unnamed groups @@ -420,7 +420,7 @@ def test_resolve_group_inheritance(self): name='named_group', attributes=[]) base_groups = [unnamed_group, named_group] - + base_spec = GroupSpec('A test group', data_type_def='BaseType', groups=base_groups) @@ -433,23 +433,23 @@ def test_resolve_group_inheritance(self): name='new_group', attributes=[]) ext_groups = [override_group, new_group] - + ext_spec = GroupSpec('An extending group', data_type_inc='BaseType', data_type_def='ExtType', groups=ext_groups) - + # Resolve the extension ext_spec.resolve_spec(base_spec) - + # Test unnamed group is added to data_types self.assertEqual(ext_spec.get_data_type('UnnamedType'), unnamed_group) - + # Test named group is overridden resolved_group = ext_spec.get_group('named_group') self.assertEqual(resolved_group.doc, 'Override named group') self.assertTrue(ext_spec.is_overridden_spec(resolved_group)) - + # Test new group is added new_resolved = ext_spec.get_group('new_group') self.assertEqual(new_resolved.doc, 'A new group') @@ -464,7 +464,7 @@ def test_resolve_group_inheritance_multiple(self): base_spec = GroupSpec('A base group', data_type_def='BaseType', groups=[base_group]) - + # First extension overrides the group mid_group = GroupSpec('Mid group', name='test_group', @@ -473,16 +473,16 @@ def test_resolve_group_inheritance_multiple(self): data_type_inc='BaseType', data_type_def='MidType', groups=[mid_group]) - + # Second extension inherits without override ext_spec = GroupSpec('An extending group', data_type_inc='MidType', data_type_def='ExtType') - + # Resolve the extensions mid_spec.resolve_spec(base_spec) ext_spec.resolve_spec(mid_spec) - + # Test group inheritance through multiple levels resolved_group = ext_spec.get_group('test_group') self.assertEqual(resolved_group.doc, 'Mid group') From 5650feff000eb50db78107cbaf10ba1a34dee2fa Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 6 Feb 2025 08:55:17 -0800 Subject: [PATCH 6/9] Add unit test for GroupSpec.is_overridden_group --- tests/unit/spec_tests/test_group_spec.py | 39 ++++++++++++++++++++++++ tests/unit/spec_tests/test_spec_write.py | 2 -- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/unit/spec_tests/test_group_spec.py b/tests/unit/spec_tests/test_group_spec.py index b087012c6..d3a768351 100644 --- a/tests/unit/spec_tests/test_group_spec.py +++ b/tests/unit/spec_tests/test_group_spec.py @@ -401,6 +401,45 @@ def test_is_inherited_attribute(self): with self.assertRaisesWith(ValueError, "Attribute 'attribute4' not found"): self.inc_group_spec.is_inherited_attribute('attribute4') + def test_is_overridden_group(self): + """Test that is_overridden_group correctly identifies overridden groups.""" + # Create base spec with a group + base_group = GroupSpec('Base group', + name='test_group', + attributes=[]) + base_spec = GroupSpec('A base group', + data_type_def='BaseType', + groups=[base_group]) + + # Create extending spec that overrides the group + override_group = GroupSpec('Override group', + name='test_group', + attributes=[]) + ext_spec = GroupSpec('An extending group', + data_type_inc='BaseType', + data_type_def='ExtType', + groups=[override_group]) + + # Resolve the extension + ext_spec.resolve_spec(base_spec) + + # Test base spec has no overridden groups + self.assertFalse(base_spec.is_overridden_group('test_group')) + + # Test extending spec correctly identifies overridden group + self.assertTrue(ext_spec.is_overridden_group('test_group')) + + # Test non-existent group raises error + with self.assertRaisesWith(ValueError, "Group 'nonexistent_group' not found in spec"): + ext_spec.is_overridden_group('nonexistent_group') + + # Test new group in extending spec is not overridden + new_group = GroupSpec('New group', + name='new_group', + attributes=[]) + ext_spec.set_group(new_group) + self.assertFalse(ext_spec.is_overridden_group('new_group')) + def test_is_overridden_attribute(self): self.assertFalse(self.def_group_spec.is_overridden_attribute('attribute1')) self.assertFalse(self.def_group_spec.is_overridden_attribute('attribute2')) diff --git a/tests/unit/spec_tests/test_spec_write.py b/tests/unit/spec_tests/test_spec_write.py index c1b16d547..d37b8c4c4 100644 --- a/tests/unit/spec_tests/test_spec_write.py +++ b/tests/unit/spec_tests/test_spec_write.py @@ -88,7 +88,6 @@ class TestNamespaceBuilder(TestSpec): def setUp(self): super().setUp() - # Original setup for data_type in self.data_types: self.ns_builder.add_spec(source=self.ext_source_path, spec=data_type) self.ns_builder.add_source(source=self.ext_source_path, @@ -107,7 +106,6 @@ def setUp(self): attributes=[]) def tearDown(self): - # Original cleanup if os.path.exists(self.ext_source_path): os.remove(self.ext_source_path) if os.path.exists(self.namespace_path): From 8cdf451b1db5f7b2aabc6cbaf48e9ae52a68689b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:55:49 +0000 Subject: [PATCH 7/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/unit/spec_tests/test_group_spec.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/spec_tests/test_group_spec.py b/tests/unit/spec_tests/test_group_spec.py index e5387eea6..b1da524e2 100644 --- a/tests/unit/spec_tests/test_group_spec.py +++ b/tests/unit/spec_tests/test_group_spec.py @@ -410,7 +410,7 @@ def test_is_overridden_group(self): base_spec = GroupSpec('A base group', data_type_def='BaseType', groups=[base_group]) - + # Create extending spec that overrides the group override_group = GroupSpec('Override group', name='test_group', @@ -419,20 +419,20 @@ def test_is_overridden_group(self): data_type_inc='BaseType', data_type_def='ExtType', groups=[override_group]) - + # Resolve the extension ext_spec.resolve_spec(base_spec) - + # Test base spec has no overridden groups self.assertFalse(base_spec.is_overridden_group('test_group')) - + # Test extending spec correctly identifies overridden group self.assertTrue(ext_spec.is_overridden_group('test_group')) - + # Test non-existent group raises error with self.assertRaisesWith(ValueError, "Group 'nonexistent_group' not found in spec"): ext_spec.is_overridden_group('nonexistent_group') - + # Test new group in extending spec is not overridden new_group = GroupSpec('New group', name='new_group', From 8586fe2177cab5745471372bf9fb07f2e50635b8 Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 6 Feb 2025 09:07:06 -0800 Subject: [PATCH 8/9] Enhance testing of GroupSpec.is_overridden_spec --- tests/unit/spec_tests/test_group_spec.py | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/unit/spec_tests/test_group_spec.py b/tests/unit/spec_tests/test_group_spec.py index b1da524e2..4cf6ad71d 100644 --- a/tests/unit/spec_tests/test_group_spec.py +++ b/tests/unit/spec_tests/test_group_spec.py @@ -401,6 +401,52 @@ def test_is_inherited_attribute(self): with self.assertRaisesWith(ValueError, "Attribute 'attribute4' not found"): self.inc_group_spec.is_inherited_attribute('attribute4') + def test_is_overridden_spec_nested(self): + """Test that is_overridden_spec correctly identifies overridden specs in nested structures.""" + # Create base spec with a dataset containing an attribute + base_dataset = DatasetSpec('Base dataset', + 'int', + name='test_dataset', + attributes=[AttributeSpec('attr1', 'Base attr', 'text')]) + base_group = GroupSpec('Base group', + name='test_group', + attributes=[AttributeSpec('attr1', 'Base attr', 'text')]) + base_spec = GroupSpec('A base group', + data_type_def='BaseType', + datasets=[base_dataset], + groups=[base_group]) + + # Create extending spec that overrides both dataset and group with new attribute values + override_dataset = DatasetSpec('Override dataset', + 'int', + name='test_dataset', + attributes=[AttributeSpec('attr1', 'Override attr', 'text')]) + override_group = GroupSpec('Override group', + name='test_group', + attributes=[AttributeSpec('attr1', 'Override attr', 'text')]) + ext_spec = GroupSpec('An extending group', + data_type_inc='BaseType', + data_type_def='ExtType', + datasets=[override_dataset], + groups=[override_group]) + + # Resolve the extension + ext_spec.resolve_spec(base_spec) + + # Test attribute in overridden dataset is marked as overridden + dataset_attr = ext_spec.get_dataset('test_dataset').get_attribute('attr1') + self.assertTrue(ext_spec.is_overridden_spec(dataset_attr)) + + # Test attribute in overridden group is marked as overridden + group_attr = ext_spec.get_group('test_group').get_attribute('attr1') + self.assertTrue(ext_spec.is_overridden_spec(group_attr)) + + # Test attributes in base spec are not marked as overridden + base_dataset_attr = base_spec.get_dataset('test_dataset').get_attribute('attr1') + base_group_attr = base_spec.get_group('test_group').get_attribute('attr1') + self.assertFalse(base_spec.is_overridden_spec(base_dataset_attr)) + self.assertFalse(base_spec.is_overridden_spec(base_group_attr)) + def test_is_overridden_group(self): """Test that is_overridden_group correctly identifies overridden groups.""" # Create base spec with a group From 8ddea90d4878bb2e37f3822d45866029a7a306db Mon Sep 17 00:00:00 2001 From: Oliver Ruebel Date: Thu, 6 Feb 2025 09:25:11 -0800 Subject: [PATCH 9/9] Update test for sort_keys to also test tuples --- tests/unit/spec_tests/test_spec_write.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit/spec_tests/test_spec_write.py b/tests/unit/spec_tests/test_spec_write.py index d37b8c4c4..8582e80e4 100644 --- a/tests/unit/spec_tests/test_spec_write.py +++ b/tests/unit/spec_tests/test_spec_write.py @@ -391,6 +391,19 @@ def test_sort_keys(self): for attr in result['attributes']: self.assertEqual(list(attr.keys()), ['name', 'dtype', 'doc']) + # Test tuple handling + input_tuple = ( + {'doc': 'item1', 'name': 'name1', 'dtype': 'int'}, + {'doc': 'item2', 'name': 'name2', 'dtype': 'float'} + ) + result = writer.sort_keys(input_tuple) + # Convert generator to list for testing + result_list = list(result) + for item in result_list: + self.assertEqual(list(item.keys()), ['name', 'dtype', 'doc']) + # Verify the original order is maintained + self.assertEqual(result_list[0]['name'], 'name1') + self.assertEqual(result_list[1]['name'], 'name2') class TestExportSpec(TestSpec):