diff --git a/swh/model/collections.py b/swh/model/collections.py --- a/swh/model/collections.py +++ b/swh/model/collections.py @@ -42,6 +42,9 @@ def items(self): yield from self.data + def __hash__(self): + return hash(tuple(sorted(self.data))) + def copy_pop(self, popped_key) -> Tuple[Optional[VT], "ImmutableDict[KT, VT]"]: """Returns a copy of this ImmutableDict without the given key, as well as the value associated to the key.""" diff --git a/swh/model/tests/test_collections.py b/swh/model/tests/test_collections.py --- a/swh/model/tests/test_collections.py +++ b/swh/model/tests/test_collections.py @@ -64,3 +64,23 @@ assert d.copy_pop("foo") == ("bar", ImmutableDict({"baz": "qux"})) assert d.copy_pop("not a key") == (None, d) + + +def test_hash(): + assert hash(ImmutableDict()) == hash(ImmutableDict({})) + assert hash(ImmutableDict({"foo": "bar"})) == hash(ImmutableDict({"foo": "bar"})) + assert hash(ImmutableDict({"foo": "bar", "baz": "qux"})) == hash( + ImmutableDict({"foo": "bar", "baz": "qux"}) + ) + assert hash(ImmutableDict({"foo": "bar", "baz": "qux"})) == hash( + ImmutableDict({"baz": "qux", "foo": "bar"}) + ) + + +def test_equality_order(): + assert ImmutableDict({"foo": "bar", "baz": "qux"}) == ImmutableDict( + {"foo": "bar", "baz": "qux"} + ) + assert ImmutableDict({"foo": "bar", "baz": "qux"}) == ImmutableDict( + {"baz": "qux", "foo": "bar"} + )