From 3eeeafa97e30752aa8082e35142ef6ab83ca5e46 Mon Sep 17 00:00:00 2001 From: Tobias Stolzmann Date: Thu, 29 Feb 2024 15:13:22 +0100 Subject: [PATCH] Add an assertion for equivalence --- clintest/assertion.py | 30 ++++++++++++++++++++++++++++++ tests/test_assertion.py | 15 +++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/clintest/assertion.py b/clintest/assertion.py index 712eb81..1af88ec 100644 --- a/clintest/assertion.py +++ b/clintest/assertion.py @@ -256,3 +256,33 @@ def __repr__(self): def holds_for(self, model: Model) -> bool: return not self.__antecedent.holds_for(model) or self.__consequent.holds_for(model) + + +class Equivalent(Assertion): + """ + The equivalence of a list of given assertions. + This assertion holds if all `args` simultaneously hold or not hold. + + Parameters + ---------- + args + The `Assertion`s to be combined. + """ + + def __init__(self, *args: Assertion) -> None: + self.__operands = args + + def __repr__(self): + name = self.__class__.__name__ + operands = ", ".join(repr(operand) for operand in self.__operands) + return f"{name}({operands})" + + def holds_for(self, model: Model) -> bool: + operands = iter(self.__operands) + + try: + first = next(operands).holds_for(model) + except StopIteration: + return True + + return all((first == operand.holds_for(model) for operand in operands)) diff --git a/tests/test_assertion.py b/tests/test_assertion.py index ca3908c..64852c7 100644 --- a/tests/test_assertion.py +++ b/tests/test_assertion.py @@ -112,3 +112,18 @@ def test_implies(frame): ], [ Implies(True_(), False_()), ]) + +def test_equivalent(frame): + from clintest.assertion import Equivalent, True_, False_ + frame( + [ + Equivalent(), + Equivalent(False_()), + Equivalent(True_()), + Equivalent(False_(), False_()), + Equivalent(True_(), True_()), + ], [ + Equivalent(False_(), True_()), + Equivalent(True_(), False_()), + ] + )