from __future__ import annotations import pytest from daglib.set import DAGSetView class TestDAGSetInit: """Test DAGSet initialization.""" def test_empty(self) -> None: s = DAGSetView() assert len(s) == 0 assert set(s) == set() assert list(s) == [] assert tuple(s) == () def test_from_set(self) -> None: s = DAGSetView({"foo", 2, 3}) assert len(s) == 3 assert set(s) == {"foo", 2, 3} def test_from_list(self) -> None: s = DAGSetView(["foo", 2, 3, 2]) assert len(s) == 3 assert set(s) == {"foo", 2, 3} def test_from_tuple(self) -> None: s = DAGSetView((4, 5, 6)) assert len(s) == 3 assert set(s) == {4, 5, 6} def test_from_none(self) -> None: s = DAGSetView(None) assert len(s) == 0 def test_from_invalid_type(self) -> None: with pytest.raises(TypeError): DAGSetView(42) # type: ignore class TestDAGSetBasicOps: """Test basic set operations.""" def test_len(self) -> None: s = DAGSetView({"foo", 2, 3}) assert len(s) == 3 def test_iter(self) -> None: s = DAGSetView({"foo", 2, 3}) for _ in enumerate(s): assert True def test_contains(self) -> None: s = DAGSetView({"foo", 2, 3}) assert "foo" in s assert 2 in s assert 3 in s assert 4 not in s def test_add(self) -> None: s = DAGSetView() s.add("foo") assert "foo" in s assert len(s) == 1 def test_discard(self) -> None: s = DAGSetView({"foo", 2, 3}) s.discard(2) assert 2 not in s assert len(s) == 2 def test_discard_missing(self) -> None: s = DAGSetView({"foo", 2, 3}) s.discard(99) assert len(s) == 3 def test_remove(self) -> None: s = DAGSetView({"foo", 2, 3}) s.remove(2) assert 2 not in s def test_remove_missing(self) -> None: s = DAGSetView({"foo", 2, 3}) with pytest.raises(KeyError): s.remove(99) class TestDAGSetCallbacks: """Test callback mechanisms.""" class TestBasicOps: def test_on_add_callback(self) -> None: added: list[int] = [] s = DAGSetView({"foo", 2}) s.on_add = lambda v: added.append(v) s.add(3) assert 3 in added assert len(added) == 1 def test_on_remove_callback(self) -> None: removed: list[int] = [] s = DAGSetView({"foo", 2, 3}) s.on_remove = lambda v: removed.append(v) s.remove(2) assert 2 in removed assert len(removed) == 1 def test_on_discard_callback(self) -> None: removed: list[int] = [] s = DAGSetView({"foo", 2, 3}) s.on_remove = lambda v: removed.append(v) s.discard(2) assert 2 in removed assert len(removed) == 1 def test_callbacks_none_by_default(self) -> None: s = DAGSetView() assert s.on_add is None assert s.on_remove is None class TestInPlaceOperators: def test_ior_triggers_callbacks(self) -> None: added: list[int] = [] s = DAGSetView({"foo"}) s.on_add = lambda v: added.append(v) s |= {2, 3} assert 2 in added assert 3 in added def test_iadd_triggers_callbacks(self) -> None: added: list[int] = [] s = DAGSetView({"foo"}) s.on_add = lambda v: added.append(v) s += [2, 3] assert 2 in added assert 3 in added def test_isub_triggers_callbacks(self) -> None: removed: list[int] = [] s = DAGSetView({"foo", 2, 3}) s.on_remove = lambda v: removed.append(v) s -= {2, 3} assert 2 in removed assert 3 in removed def test_iand_triggers_callbacks(self) -> None: removed: list[int] = [] s = DAGSetView({"foo", 2, 3, 4}) s.on_remove = lambda v: removed.append(v) s &= {2, 3} assert "foo" in removed assert 4 in removed assert len(removed) == 2 def test_ixor_triggers_callbacks(self) -> None: added: list[int] = [] removed: list[int] = [] s = DAGSetView({"foo", 2, 3}) s.on_add = lambda v: added.append(v) s.on_remove = lambda v: removed.append(v) s ^= {3, 4, 5} assert 3 in removed assert 4 in added assert 5 in added class TestDAGSetInPlaceOperators: """Test in-place set operators.""" class TestIOR: def test_set(self) -> None: s = DAGSetView({"foo", 2}) s |= {3, 4} assert set(s) == {"foo", 2, 3, 4} def test_list(self) -> None: s = DAGSetView({"foo"}) s |= [2, 3] assert set(s) == {"foo", 2, 3} def test_string(self) -> None: s = DAGSetView({"abc", "def"}) s |= "xyz" assert "xyz" in s def test_dagset(self) -> None: s = DAGSetView({"foo", 2}) other = DAGSetView({3, 4}) s |= other assert set(s) == {"foo", 2, 3, 4} class TestIAdd: def test_set(self) -> None: s = DAGSetView({"foo", 2}) s += {3, 4} assert set(s) == {"foo", 2, 3, 4} def test_list(self) -> None: s = DAGSetView({"foo"}) s += [2, 3] assert set(s) == {"foo", 2, 3} def test_string(self) -> None: s = DAGSetView({"a"}) s += "b" assert "b" in s def test_dagset(self) -> None: s = DAGSetView({"foo", 2}) other = DAGSetView({3, 4}) s += other assert set(s) == {"foo", 2, 3, 4} class TestISub: def test_set(self) -> None: s = DAGSetView({"foo", 2, 3, 4}) s -= {2, 3} assert set(s) == {"foo", 4} def test_list(self) -> None: s = DAGSetView({"foo", 2, 3}) s -= [2] assert set(s) == {"foo", 3} def test_string(self) -> None: s = DAGSetView({"a", "b", "c"}) s -= "b" assert set(s) == {"a", "c"} def test_dagset(self) -> None: s = DAGSetView({"foo", 2, 3, 4}) other = DAGSetView({2, 3}) s -= other assert set(s) == {"foo", 4} class TestIAnd: def test_set(self) -> None: s = DAGSetView({"foo", 2, 3}) s &= {2, 3, 4} assert set(s) == {2, 3} def test_list(self) -> None: s = DAGSetView({"foo", 2, 3}) s &= [2, 3] assert set(s) == {2, 3} def test_dagset(self) -> None: s = DAGSetView({"foo", 2, 3}) other = DAGSetView({2, 3, 4}) s &= other assert set(s) == {2, 3} class TestIXor: def test_set(self) -> None: s = DAGSetView({"foo", 2, 3}) s ^= {3, 4, 5} assert set(s) == {"foo", 2, 4, 5} def test_list(self) -> None: s = DAGSetView({"foo", 2}) s ^= [2, 3] assert set(s) == {"foo", 3} def test_dagset(self) -> None: s = DAGSetView({"foo", 2, 3}) other = DAGSetView({3, 4, 5}) s ^= other assert set(s) == {"foo", 2, 4, 5}