Skip to content

Commit 45dbef1

Browse files
committed
Addressed copilot review
1 parent 680346e commit 45dbef1

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

hed/models/query_util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def merge_and_result(self, other):
5151
new_children.append(child)
5252
new_children.sort(key=str)
5353

54-
if self.group != other.group:
54+
if self.group is not other.group:
5555
raise ValueError("Internal error")
5656
return SearchResult(self.group, new_children)
5757

@@ -64,7 +64,7 @@ def has_same_children(self, other):
6464
Returns:
6565
bool: True if both results have the same group and identical children.
6666
"""
67-
if self.group != other.group:
67+
if self.group is not other.group:
6868
return False
6969

7070
if len(self.children) != len(other.children):

tests/models/test_query_util.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
- children = [Red] (only the matched tag)
1111
"""
1212

13+
import os
1314
import unittest
1415
from hed.models.query_util import SearchResult
16+
from hed.models.hed_string import HedString
17+
from hed import schema
1518
from unittest.mock import Mock
1619

1720

@@ -282,5 +285,78 @@ def test_merge_and_groups_preserves_distinct_results(self):
282285
self.assertEqual(len(result), 2, "two distinct merged results should both be returned")
283286

284287

288+
class TestSearchResultGroupIdentitySemantics(unittest.TestCase):
289+
"""Tests that merge_and_result and has_same_children use object identity
290+
(is/is not) for group comparison, consistent with __eq__ and __hash__.
291+
292+
The key invariant: two distinct HedGroup objects that happen to have equal
293+
content must be treated as *different* groups throughout the API.
294+
295+
Uses two HedString objects parsed from identical content with a real schema:
296+
HedGroup.__eq__ is content-based, so hs_a == hs_b is True while hs_a is not hs_b
297+
is also True — exactly the condition a buggy content-equality check would miss.
298+
"""
299+
300+
@classmethod
301+
def setUpClass(cls):
302+
base_data_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), "../data/"))
303+
hed_xml_file = os.path.join(base_data_dir, "schema_tests/HED8.0.0t.xml")
304+
cls.hed_schema = schema.load_schema(hed_xml_file)
305+
306+
def setUp(self):
307+
# Two HedStrings with identical content parsed independently.
308+
# HedGroup.__eq__ compares children structurally, so hs_a == hs_b,
309+
# but they are distinct objects: hs_a is not hs_b.
310+
hs_a = HedString("Red, Blue", self.hed_schema)
311+
hs_b = HedString("Red, Blue", self.hed_schema)
312+
self.group_a = hs_a
313+
self.group_b = hs_b
314+
# Use a real tag from group_a as the matched child
315+
self.child = list(hs_a.get_all_tags())[0]
316+
317+
def test_merge_and_result_raises_for_content_equal_but_distinct_groups(self):
318+
"""merge_and_result must raise ValueError when groups are distinct objects
319+
even if those objects compare as content-equal.
320+
"""
321+
r1 = SearchResult(self.group_a, [self.child])
322+
r2 = SearchResult(self.group_b, [self.child])
323+
324+
# group_a == group_b is True (content equality), so a buggy == check
325+
# would NOT raise. The correct identity check (is not) MUST raise.
326+
with self.assertRaises(ValueError):
327+
r1.merge_and_result(r2)
328+
329+
def test_has_same_children_false_for_content_equal_but_distinct_groups(self):
330+
"""has_same_children must return False when groups are distinct objects
331+
even if those objects compare as content-equal.
332+
"""
333+
r1 = SearchResult(self.group_a, [self.child])
334+
r2 = SearchResult(self.group_b, [self.child])
335+
336+
# A buggy content-equality check would return True; identity check must return False.
337+
self.assertFalse(r1.has_same_children(r2))
338+
339+
def test_eq_false_for_content_equal_but_distinct_groups(self):
340+
"""__eq__ must return False when groups are distinct objects
341+
even if those objects compare as content-equal.
342+
"""
343+
r1 = SearchResult(self.group_a, [self.child])
344+
r2 = SearchResult(self.group_b, [self.child])
345+
346+
self.assertNotEqual(r1, r2)
347+
348+
def test_same_group_object_all_apis_agree(self):
349+
"""When both results share the exact same group object, all three
350+
APIs must consistently treat them as same-group.
351+
"""
352+
r1 = SearchResult(self.group_a, [self.child])
353+
r2 = SearchResult(self.group_a, [self.child])
354+
355+
self.assertTrue(r1.has_same_children(r2))
356+
self.assertEqual(r1, r2)
357+
merged = r1.merge_and_result(r2) # must not raise
358+
self.assertIs(merged.group, self.group_a)
359+
360+
285361
if __name__ == "__main__":
286362
unittest.main()

0 commit comments

Comments
 (0)