diff --git a/runtime-pyside6/tests/__init__.py b/runtime-pyside6/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/runtime-pyside6/tests/plugins/__init__.py b/runtime-pyside6/tests/plugins/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/runtime-pyside6/tests/plugins/natural/__init__.py b/runtime-pyside6/tests/plugins/natural/__init__.py
new file mode 100644
index 0000000..7a67869
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/__init__.py
@@ -0,0 +1,21 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+
+from .spy import Spy
+from .that import that
+
diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/__init__.py b/runtime-pyside6/tests/plugins/natural/interfaces/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/assertion.py b/runtime-pyside6/tests/plugins/natural/interfaces/assertion.py
new file mode 100644
index 0000000..f2504a2
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/interfaces/assertion.py
@@ -0,0 +1,30 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+
+class Assertion(Exception):
+ def __init__(self, assertion: bool, message: str):
+ self.assertion = assertion
+ self.message = message
+
+ def __str__(self):
+ return self.message
+
+ def __bool__(self):
+ if not self.assertion:
+ raise self
+ return self.assertion
\ No newline at end of file
diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/base.py b/runtime-pyside6/tests/plugins/natural/interfaces/base.py
new file mode 100644
index 0000000..88b9e06
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/interfaces/base.py
@@ -0,0 +1,101 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+from typing import Self
+
+from tests.plugins.natural.interfaces.assertion import Assertion
+from tests.plugins.natural.interfaces.utils import repr_
+
+
+class AssertionInterface:
+ """
+ Most basic assertion interface.
+ You probably want to use BaseAssertionInterface
+ """
+ def __init__(self, value):
+ self._value = value
+
+ @property
+ def was(self) -> Self:
+ return self
+
+ @property
+ def be(self) -> Self:
+ return self
+
+ @property
+ def been(self) -> Self:
+ return self
+
+ @property
+ def have(self) -> Self:
+ return self
+
+ @property
+ def has(self) -> Self:
+ return self
+
+ @property
+ def a(self) -> Self:
+ return self
+
+ @property
+ def an(self) -> Self:
+ return self
+
+ def is_a(self, type_) -> Assertion:
+ """
+ Checks if the current value is equal to the provided value
+ """
+ value_type_name = type(self._value).__name__
+ return Assertion(isinstance(type_, type_), f"The value ({value_type_name} {repr_(self._value)}) is not a {type_.__name__}.")
+
+
+
+class EqualAssertionInterface(AssertionInterface):
+ """
+ Interface created for when its value should be checked for equality
+ """
+ def __init__(self, value):
+ super().__init__(value)
+
+ def __call__(self, value) -> Assertion:
+ return Assertion(value == self._value, f"The value ({repr_(self._value)}) is not equal to {repr(value)}.")
+
+ def to(self, value) -> Self:
+ return self(value)
+
+ def of(self, value) -> Self:
+ return self(value)
+
+
+
+class BaseAssertionInterface(AssertionInterface):
+
+ @property
+ def equals(self) -> EqualAssertionInterface:
+ """
+ Checks if the current value is equal to the provided value
+ """
+ return EqualAssertionInterface(self._value)
+
+ @property
+ def is_equal(self) -> EqualAssertionInterface:
+ """
+ Checks if the current value is equal to the provided value
+ """
+ return self.equals
\ No newline at end of file
diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/basic.py b/runtime-pyside6/tests/plugins/natural/interfaces/basic.py
new file mode 100644
index 0000000..1d5a878
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/interfaces/basic.py
@@ -0,0 +1,61 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+from tests.plugins.natural.interfaces.assertion import Assertion
+from tests.plugins.natural.interfaces.base import BaseAssertionInterface
+from tests.plugins.natural.interfaces.int import IntInterface
+from tests.plugins.natural.interfaces.utils import repr_
+
+
+class FixedIteratorInterface(BaseAssertionInterface):
+ @property
+ def length(self) -> IntInterface:
+ return IntInterface(len(self._value))
+
+ def elements(self, *elements) -> Assertion:
+ tests = [elem for elem in elements if elem in self._value]
+ return Assertion(
+ len(tests) == 0,
+ f"This value ({repr_(self._value)}) does not have elements ({repr_(tests)})"
+ )
+
+ def element(self, element) -> Assertion:
+ return Assertion(
+ element in self._value,
+ f"This value ({repr_(self._value)}) does not have element ({repr_(element)})"
+ )
+
+ def contains(self, *elements) -> Assertion:
+ return self.elements(*elements)
+
+class BoolInterface(BaseAssertionInterface):
+ @property
+ def is_true(self):
+ return Assertion(
+ self._value == True,
+ f"The value ({repr_(self._value)}) is not True."
+ )
+
+ @property
+ def is_false(self):
+ return Assertion(
+ self._value == False,
+ f"The value ({repr_(self._value)}) is not False."
+ )
+
+class StringInterface(LengthInterface):
+ pass
\ No newline at end of file
diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/int.py b/runtime-pyside6/tests/plugins/natural/interfaces/int.py
new file mode 100644
index 0000000..aaf0511
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/interfaces/int.py
@@ -0,0 +1,229 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+from typing import Self
+
+from tests.plugins.natural.interfaces.assertion import Assertion
+from tests.plugins.natural.interfaces.base import BaseAssertionInterface, EqualAssertionInterface, AssertionInterface
+from tests.plugins.natural.interfaces.utils import repr_
+
+
+class IntComparisonAssertionInterface(AssertionInterface):
+ def __init__(self, value):
+ super().__init__(value)
+ self._compare_to = None
+
+ def _compare(self) -> Assertion:
+ raise RuntimeError(f"No comparison method defined in {type(self).__name__}.")
+
+ def __bool__(self) -> bool:
+ return bool(self._compare())
+
+ def __call__(self, compare_to: int) -> Self:
+ if self._compare_to is None:
+ self._compare_to = int(compare_to)
+ else:
+ self._compare_to *= int(compare_to)
+ return self
+
+ @property
+ def time(self) -> Self:
+ return self
+
+ @property
+ def times(self) -> Self:
+ return self
+
+ @property
+ def never(self) -> Self:
+ return self(0)
+
+ @property
+ def once(self) -> Self:
+ return self(1)
+
+ @property
+ def twice(self) -> Self:
+ return self(2)
+
+ @property
+ def thrice(self) -> Self:
+ return self(3)
+
+ @property
+ def zero(self) -> Self:
+ return self(0)
+
+ @property
+ def one(self) -> Self:
+ return self(1)
+
+ @property
+ def two(self) -> Self:
+ return self(2)
+
+ @property
+ def three(self) -> Self:
+ return self(3)
+
+ @property
+ def four(self) -> Self:
+ return self(4)
+
+ @property
+ def five(self) -> Self:
+ return self(5)
+
+ @property
+ def six(self) -> Self:
+ return self(6)
+
+ @property
+ def seven(self) -> Self:
+ return self(7)
+
+ @property
+ def eight(self) -> Self:
+ return self(8)
+
+ @property
+ def nine(self) -> Self:
+ return self(9)
+
+ @property
+ def ten(self) -> Self:
+ return self(10)
+
+ @property
+ def twenty(self) -> Self:
+ return self(20)
+
+ @property
+ def thirty(self) -> Self:
+ return self(30)
+
+ @property
+ def forty(self) -> Self:
+ return self(40)
+
+ @property
+ def fifty(self) -> Self:
+ return self(50)
+
+ @property
+ def sixty(self) -> Self:
+ return self(60)
+
+ @property
+ def seventy(self) -> Self:
+ return self(70)
+
+ @property
+ def eighty(self) -> Self:
+ return self(70)
+
+ @property
+ def ninety(self) -> Self:
+ return self(70)
+
+ @property
+ def hundred(self) -> Self:
+ return self(100)
+
+ @property
+ def thousand(self) -> Self:
+ return self(1_000)
+
+ @property
+ def million(self) -> Self:
+ return self(1_000_000)
+
+ @property
+ def billion(self) -> Self:
+ return self(1_000_000_000)
+
+
+class LessThanComparisonInterface(IntComparisonAssertionInterface):
+ def _compare(self) -> Assertion:
+ return Assertion(
+ self._value < self._compare_to,
+ f"The value ({repr_(self._value)}) is not less than to {repr_(self._compare_to)}."
+ )
+
+class MoreThanComparisonInterface(IntComparisonAssertionInterface):
+ def _compare(self) -> Assertion:
+ return Assertion(
+ self._value > self._compare_to,
+ f"The value ({repr_(self._value)}) is not more than to {repr_(self._compare_to)}."
+ )
+
+class AtLeastComparisonInterface(IntComparisonAssertionInterface):
+ def _compare(self) -> Assertion:
+ return Assertion(
+ self._value >= self._compare_to,
+ f"The value ({repr_(self._value)}) is not at least to {repr_(self._compare_to)}."
+ )
+
+class AtMostComparisonInterface(IntComparisonAssertionInterface):
+ def _compare(self) -> Assertion:
+ return Assertion(
+ self._value <= self._compare_to,
+ f"The value ({repr_(self._value)}) is not at least to {repr_(self._compare_to)}."
+ )
+
+class EqualComparisonInterface(IntComparisonAssertionInterface):
+ def _compare(self) -> Assertion:
+ return Assertion(
+ self._value == self._compare_to,
+ f"The value ({repr_(self._value)}) is not equal to {repr_(self._compare_to)}."
+ )
+
+ def to(self) -> Self:
+ return self
+
+ def of(self) -> Self:
+ return self
+
+
+class IntInterface(AssertionInterface):
+ def less_than(self) -> LessThanComparisonInterface:
+ return LessThanComparisonInterface(self._value)
+
+ @property
+ def more_than(self) -> MoreThanComparisonInterface:
+ return MoreThanComparisonInterface(self._value)
+
+ @property
+ def at_least(self) -> AtLeastComparisonInterface:
+ return AtLeastComparisonInterface(self._value)
+
+ @property
+ def at_most(self) -> AtMostComparisonInterface:
+ return AtMostComparisonInterface(self._value)
+
+ @property
+ def equals(self) -> EqualComparisonInterface:
+ return EqualComparisonInterface(self._value)
+
+ @property
+ def is_equal(self) -> EqualComparisonInterface:
+ return EqualComparisonInterface(self._value)
+
+ @property
+ def exactly(self) -> EqualComparisonInterface:
+ return EqualComparisonInterface(self._value)
+
diff --git a/runtime-pyside6/tests/plugins/natural/interfaces/utils.py b/runtime-pyside6/tests/plugins/natural/interfaces/utils.py
new file mode 100644
index 0000000..50e3777
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/interfaces/utils.py
@@ -0,0 +1,27 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+
+from PySide6.QtQml import QJSValue
+
+
+def repr_(data):
+ if isinstance(data, QJSValue):
+ variant = data.toVariant()
+ return f"QJSValue<{type(variant).__name__}>({repr(variant)})"
+ else:
+ return repr(data)
\ No newline at end of file
diff --git a/runtime-pyside6/tests/spy.py b/runtime-pyside6/tests/plugins/natural/spy.py
similarity index 69%
rename from runtime-pyside6/tests/spy.py
rename to runtime-pyside6/tests/plugins/natural/spy.py
index 01f9aa0..1d77629 100644
--- a/runtime-pyside6/tests/spy.py
+++ b/runtime-pyside6/tests/plugins/natural/spy.py
@@ -17,35 +17,29 @@
"""
from typing import Callable, Self
-from PySide6.QtQml import QJSValue
+from tests.plugins.natural.interfaces.base import Assertion, repr_, AssertionInterface
+from tests.plugins.natural.interfaces.int import IntComparisonAssertionInterface
PRINT_PREFIX = (" " * 24)
-class SpyAssertionFailed(Exception):
- def __init__(self, message, calls):
- self.message = message + "\n"
+
+class SpyAssertion(Assertion):
+ def __init__(self, assertion: bool, message: str, calls: list):
+ super().__init__(assertion, message + "\n")
if len(calls) > 0:
self.message += self.render_calls(calls)
else:
self.message += f"{PRINT_PREFIX}0 registered calls."
- def repr(self, data):
- if isinstance(data, QJSValue):
- variant = data.toVariant()
- return f"QJSValue<{type(variant).__name__}>({repr(variant)})"
- else:
- return repr(data)
def render_calls(self, calls):
lines = [f"{PRINT_PREFIX}{len(calls)} registered call(s):"]
for call in calls:
- repr_args = [self.repr(arg) for arg in call[0]]
- repr_kwargs =[f"{key}={self.repr(arg)}" for key, arg in call[1].items()]
+ repr_args = [repr_(arg) for arg in call[0]]
+ repr_kwargs = [f"{key}={repr_(arg)}" for key, arg in call[1].items()]
lines.append(f" - {', '.join([*repr_args, *repr_kwargs])}")
return ("\n" + PRINT_PREFIX).join(lines)
- def __str__(self):
- return self.message
class Methods:
AT_LEAST_ONCE = "AT_LEAST_ONCE"
@@ -55,18 +49,19 @@ class Methods:
MORE_THAN = "AT_LEAST"
LESS_THAN = "AT_MOST"
-class CalledInterface:
+
+class CalledInterface(IntComparisonAssertionInterface):
"""
Internal class generated by Spy.was_called.
"""
def __init__(self, calls: list[tuple[list, dict]]):
+ super().__init__(len(calls))
self.__calls = calls
self.__method = Methods.AT_LEAST_ONCE
- self.__times = None
def __apply_method(self, calls):
- required = self.__times
+ required = self._compare_to
calls_count = len(calls)
match self.__method:
case Methods.AT_LEAST_ONCE:
@@ -91,61 +86,59 @@ class CalledInterface:
raise RuntimeError(f"Unknown method {self.__method}.")
return compare, error
- def __bool__(self):
+ def __bool__(self) -> bool:
"""
Converts to boolean on assertion.
"""
compare, error = self.__apply_method(self.__calls)
- if not compare:
- raise SpyAssertionFailed(error+".")
- return compare
-
+ return bool(SpyAssertion(compare, error + ".", self.__calls))
"""
Chaining methods
"""
+
def __call__(self, *args, **kwargs) -> Self:
if len(args) != 1:
raise RuntimeError("Cannot call called interface with more than one argument.")
- self.__times = int(args[0])
+ self._compare_to = int(args[0])
if self.__method == Methods.AT_LEAST_ONCE:
self.__method = Methods.EXACTLY
return self
- @property
- def never(self) -> Self:
- return self(0)
-
- @property
- def once(self) -> Self:
- return self(1)
-
- @property
- def twice(self) -> Self:
- return self(2)
-
- @property
- def thrice(self) -> Self:
- return self(3)
-
@property
def at_least(self) -> Self:
- self.__method = Methods.AT_LEAST
+ if self.__method == Methods.AT_LEAST_ONCE:
+ self.__method = Methods.AT_LEAST
+ else:
+ raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.AT_MOST}")
return self
@property
def at_most(self) -> Self:
- self.__method = Methods.AT_MOST
+ if self.__method == Methods.AT_LEAST_ONCE:
+ self.__method = Methods.AT_MOST
+ else:
+ raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.AT_MOST}")
return self
@property
def more_than(self) -> Self:
- self.__method = Methods.MORE_THAN
+ if self.__method == Methods.AT_LEAST_ONCE:
+ self.__method = Methods.MORE_THAN
+ else:
+ raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.MORE_THAN}")
return self
@property
def less_than(self) -> Self:
- self.__method = Methods.LESS_THAN
+ if self.__method == Methods.AT_LEAST_ONCE:
+ self.__method = Methods.LESS_THAN
+ else:
+ raise RuntimeError(f"Cannot redefine method from {self.__method} to {Methods.LESS_THAN}")
+ return self
+
+ @property
+ def time(self) -> Self:
return self
@property
@@ -155,6 +148,7 @@ class CalledInterface:
"""
Class properties.
"""
+
def __match_calls_for_condition(self, condition: Callable[[list, dict], bool]) -> tuple[bool, str]:
calls = []
for call in self.__calls:
@@ -163,12 +157,12 @@ class CalledInterface:
compare, error = self.__apply_method(calls)
return compare, error
-
- def with_arguments(self, *args, **kwargs):
+ def with_arguments(self, *args, **kwargs) -> SpyAssertion:
"""
Checks if the Spy has been called the given number of times
with at least the given arguments.
"""
+
def some_args_matched(a, kw):
args_matched = all((
arg in a
@@ -179,55 +173,49 @@ class CalledInterface:
for key, arg in kwargs.items()
))
return args_matched and kwargs_matched
+
compare, error = self.__match_calls_for_condition(some_args_matched)
- if not compare:
- repr_args = ', '.join([repr(arg) for arg in args])
- repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()])
- raise SpyAssertionFailed(f"{error} with arguments ({repr_args}) and keyword arguments ({repr_kwargs}).", self.__calls)
- return compare
+ repr_args = ', '.join([repr(arg) for arg in args])
+ repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()])
+ msg = f"{error} with arguments ({repr_args}) and keyword arguments ({repr_kwargs})."
+ return SpyAssertion(compare, msg, self.__calls)
-
- def with_arguments_matching(self, test_condition: Callable[[list, dict], bool]):
+ def with_arguments_matching(self, test_condition: Callable[[list, dict], bool]) -> SpyAssertion:
"""
Checks if the Spy has been called the given number of times
with arguments matching the given conditions.
"""
compare, error = self.__match_calls_for_condition(test_condition)
- if not compare:
- raise SpyAssertionFailed(f"{error} with arguments matching given conditions.", self.__calls)
- return compare
+ msg = f"{error} with arguments matching given conditions."
+ return SpyAssertion(compare, msg, self.__calls)
-
- def with_exact_arguments(self, *args, **kwargs):
+ def with_exact_arguments(self, *args, **kwargs) -> SpyAssertion:
"""
Checks if the Spy has been called the given number of times
with all the given arguments.
"""
compare, error = self.__match_calls_for_condition(lambda a, kw: a == args and kw == kwargs)
- if not compare:
- repr_args = ', '.join([repr(arg) for arg in args])
- repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()])
- raise SpyAssertionFailed(f"{error} with exact arguments ({repr_args}) and keyword arguments ({repr_kwargs}).", self.__calls)
- return compare
+ repr_args = ', '.join([repr(arg) for arg in args])
+ repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()])
+ msg = f"{error} with exact arguments ({repr_args}) and keyword arguments ({repr_kwargs})."
+ return SpyAssertion(compare, msg, self.__calls)
- def with_no_argument(self):
+ def with_no_argument(self) -> SpyAssertion:
"""
Checks if the Spy has been called the given number of times
with all the given arguments.
"""
compare, error = self.__match_calls_for_condition(lambda a, kw: len(a) == 0 and len(kw) == 0)
- if not compare:
- raise SpyAssertionFailed(f"{error} with no arguments.", self.__calls)
- return compare
+ return SpyAssertion(compare, f"{error} with no arguments.", self.__calls)
-
-class Spy:
+class Spy(AssertionInterface):
"""
Class to spy into method calls with natural language expressions.
"""
def __init__(self, function: Callable = None):
+ super().__init__(function)
self.function = function
self.calls = []
@@ -237,7 +225,7 @@ class Spy:
self.function(*args, **kwargs)
@property
- def was_called(self) -> CalledInterface:
+ def called(self) -> CalledInterface:
"""
Returns a boolean-able interface to check conditions for a given number of
time the spy was called.
@@ -245,7 +233,7 @@ class Spy:
return CalledInterface(self.calls)
@property
- def was_not_called(self) -> CalledInterface:
+ def not_called(self) -> CalledInterface:
"""
Returns a boolean-able interface to check that conditions were never fulfilled
in the times the spy was called.
diff --git a/runtime-pyside6/tests/plugins/natural/that.py b/runtime-pyside6/tests/plugins/natural/that.py
new file mode 100644
index 0000000..38ee54e
--- /dev/null
+++ b/runtime-pyside6/tests/plugins/natural/that.py
@@ -0,0 +1,57 @@
+"""
+ * LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
+ * Copyright (C) 2021-2024 Ad5001
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+"""
+from typing import overload, Generic, TypeVar
+
+from .spy import Spy
+from .interfaces.base import AssertionInterface, BaseAssertionInterface
+from .interfaces.basic import StringInterface, BoolInterface
+from .interfaces.int import IntInterface
+
+Interface = TypeVar("Interface", bound=AssertionInterface)
+
+MATCHES = [
+ (str, StringInterface),
+ (int, IntInterface),
+ (bool, BoolInterface),
+]
+
+
+@overload
+def that(value: str) -> StringInterface: ...
+
+
+@overload
+def that(value: int) -> IntInterface: ...
+
+
+@overload
+def that(value: bool) -> BoolInterface: ...
+
+
+@overload
+def that[Interface](value: Interface) -> Interface: ...
+
+
+def that(value: any) -> AssertionInterface:
+ if not isinstance(value, AssertionInterface):
+ interface = next((i for t, i in MATCHES if isinstance(value, t)), None)
+ if interface is not None:
+ value = interface(value)
+ else:
+ value = BaseAssertionInterface(value)
+ return value
diff --git a/runtime-pyside6/tests/test_promise.py b/runtime-pyside6/tests/test_promise.py
index 06c0b8a..a97f864 100644
--- a/runtime-pyside6/tests/test_promise.py
+++ b/runtime-pyside6/tests/test_promise.py
@@ -17,11 +17,9 @@
"""
from time import sleep
-import pytest
-from PySide6.QtCore import QObject
from PySide6.QtQml import QJSValue
-from spy import Spy
+from tests.plugins.natural import that, Spy
from LogarithmPlotter.util.js import PyJSValue
from LogarithmPlotter.util.promise import PyPromise
@@ -95,10 +93,10 @@ class TestPyPromise:
# Check on our spy.
with qtbot.waitSignal(promise.fulfilled, timeout=10000):
pass
- assert spy_fulfilled.was_called.once
- assert spy_fulfilled.was_not_called.with_arguments(3)
- assert spy_fulfilled.was_called.with_arguments_matching(check_promise_result(3))
- assert spy_rejected.was_not_called
+ assert that(spy_fulfilled).was.called.once
+ assert that(spy_fulfilled).was.not_called.with_arguments(3)
+ assert that(spy_fulfilled).was.called.with_arguments_matching(check_promise_result(3))
+ assert spy_rejected.was.not_called
def test_rejected(self, qtbot):
spy_fulfilled = Spy()
@@ -106,10 +104,10 @@ class TestPyPromise:
promise = PyPromise(async_throw)
then_res = promise.then(spy_fulfilled, spy_rejected)
# Check if the return value is the same promise (so we can chain then)
- assert then_res == promise
+ assert that(then_res).is_equal.to(promise)
# Check on our spies.
with qtbot.waitSignal(promise.rejected, timeout=10000):
pass
- assert spy_rejected.was_called.once
- assert spy_rejected.was_called.with_arguments("Exception('aaaa')")
- assert spy_fulfilled.was_not_called
+ assert that(spy_rejected).was.called.once
+ assert that(spy_rejected).was.called.with_arguments("Exception('aaaa')")
+ assert that(spy_fulfilled).was.not_called