Starting natural language plugin.

This commit is contained in:
Ad5001 2024-10-16 22:18:53 +02:00
parent 34caf20593
commit 8fab9d8e52
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
12 changed files with 593 additions and 81 deletions

View file

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
from .spy import Spy
from .that import that

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
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

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
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

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
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

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
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)

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
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)

View file

@ -17,35 +17,29 @@
""" """
from typing import Callable, Self 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) PRINT_PREFIX = (" " * 24)
class SpyAssertionFailed(Exception):
def __init__(self, message, calls): class SpyAssertion(Assertion):
self.message = message + "\n" def __init__(self, assertion: bool, message: str, calls: list):
super().__init__(assertion, message + "\n")
if len(calls) > 0: if len(calls) > 0:
self.message += self.render_calls(calls) self.message += self.render_calls(calls)
else: else:
self.message += f"{PRINT_PREFIX}0 registered calls." 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): def render_calls(self, calls):
lines = [f"{PRINT_PREFIX}{len(calls)} registered call(s):"] lines = [f"{PRINT_PREFIX}{len(calls)} registered call(s):"]
for call in calls: for call in calls:
repr_args = [self.repr(arg) for arg in call[0]] repr_args = [repr_(arg) for arg in call[0]]
repr_kwargs =[f"{key}={self.repr(arg)}" for key, arg in call[1].items()] repr_kwargs = [f"{key}={repr_(arg)}" for key, arg in call[1].items()]
lines.append(f" - {', '.join([*repr_args, *repr_kwargs])}") lines.append(f" - {', '.join([*repr_args, *repr_kwargs])}")
return ("\n" + PRINT_PREFIX).join(lines) return ("\n" + PRINT_PREFIX).join(lines)
def __str__(self):
return self.message
class Methods: class Methods:
AT_LEAST_ONCE = "AT_LEAST_ONCE" AT_LEAST_ONCE = "AT_LEAST_ONCE"
@ -55,18 +49,19 @@ class Methods:
MORE_THAN = "AT_LEAST" MORE_THAN = "AT_LEAST"
LESS_THAN = "AT_MOST" LESS_THAN = "AT_MOST"
class CalledInterface:
class CalledInterface(IntComparisonAssertionInterface):
""" """
Internal class generated by Spy.was_called. Internal class generated by Spy.was_called.
""" """
def __init__(self, calls: list[tuple[list, dict]]): def __init__(self, calls: list[tuple[list, dict]]):
super().__init__(len(calls))
self.__calls = calls self.__calls = calls
self.__method = Methods.AT_LEAST_ONCE self.__method = Methods.AT_LEAST_ONCE
self.__times = None
def __apply_method(self, calls): def __apply_method(self, calls):
required = self.__times required = self._compare_to
calls_count = len(calls) calls_count = len(calls)
match self.__method: match self.__method:
case Methods.AT_LEAST_ONCE: case Methods.AT_LEAST_ONCE:
@ -91,61 +86,59 @@ class CalledInterface:
raise RuntimeError(f"Unknown method {self.__method}.") raise RuntimeError(f"Unknown method {self.__method}.")
return compare, error return compare, error
def __bool__(self): def __bool__(self) -> bool:
""" """
Converts to boolean on assertion. Converts to boolean on assertion.
""" """
compare, error = self.__apply_method(self.__calls) compare, error = self.__apply_method(self.__calls)
if not compare: return bool(SpyAssertion(compare, error + ".", self.__calls))
raise SpyAssertionFailed(error+".")
return compare
""" """
Chaining methods Chaining methods
""" """
def __call__(self, *args, **kwargs) -> Self: def __call__(self, *args, **kwargs) -> Self:
if len(args) != 1: if len(args) != 1:
raise RuntimeError("Cannot call called interface with more than one argument.") 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: if self.__method == Methods.AT_LEAST_ONCE:
self.__method = Methods.EXACTLY self.__method = Methods.EXACTLY
return 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 @property
def at_least(self) -> Self: 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 return self
@property @property
def at_most(self) -> Self: 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 return self
@property @property
def more_than(self) -> Self: 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 return self
@property @property
def less_than(self) -> Self: 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 return self
@property @property
@ -155,6 +148,7 @@ class CalledInterface:
""" """
Class properties. Class properties.
""" """
def __match_calls_for_condition(self, condition: Callable[[list, dict], bool]) -> tuple[bool, str]: def __match_calls_for_condition(self, condition: Callable[[list, dict], bool]) -> tuple[bool, str]:
calls = [] calls = []
for call in self.__calls: for call in self.__calls:
@ -163,12 +157,12 @@ class CalledInterface:
compare, error = self.__apply_method(calls) compare, error = self.__apply_method(calls)
return compare, error return compare, error
def with_arguments(self, *args, **kwargs) -> SpyAssertion:
def with_arguments(self, *args, **kwargs):
""" """
Checks if the Spy has been called the given number of times Checks if the Spy has been called the given number of times
with at least the given arguments. with at least the given arguments.
""" """
def some_args_matched(a, kw): def some_args_matched(a, kw):
args_matched = all(( args_matched = all((
arg in a arg in a
@ -179,55 +173,49 @@ class CalledInterface:
for key, arg in kwargs.items() for key, arg in kwargs.items()
)) ))
return args_matched and kwargs_matched return args_matched and kwargs_matched
compare, error = self.__match_calls_for_condition(some_args_matched) compare, error = self.__match_calls_for_condition(some_args_matched)
if not compare: repr_args = ', '.join([repr(arg) for arg in args])
repr_args = ', '.join([repr(arg) for arg in args]) repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()])
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})."
raise SpyAssertionFailed(f"{error} with arguments ({repr_args}) and keyword arguments ({repr_kwargs}).", self.__calls) return SpyAssertion(compare, msg, self.__calls)
return compare
def with_arguments_matching(self, test_condition: Callable[[list, dict], bool]) -> SpyAssertion:
def with_arguments_matching(self, test_condition: Callable[[list, dict], bool]):
""" """
Checks if the Spy has been called the given number of times Checks if the Spy has been called the given number of times
with arguments matching the given conditions. with arguments matching the given conditions.
""" """
compare, error = self.__match_calls_for_condition(test_condition) compare, error = self.__match_calls_for_condition(test_condition)
if not compare: msg = f"{error} with arguments matching given conditions."
raise SpyAssertionFailed(f"{error} with arguments matching given conditions.", self.__calls) return SpyAssertion(compare, msg, self.__calls)
return compare
def with_exact_arguments(self, *args, **kwargs) -> SpyAssertion:
def with_exact_arguments(self, *args, **kwargs):
""" """
Checks if the Spy has been called the given number of times Checks if the Spy has been called the given number of times
with all the given arguments. with all the given arguments.
""" """
compare, error = self.__match_calls_for_condition(lambda a, kw: a == args and kw == kwargs) 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_args = ', '.join([repr(arg) for arg in args]) repr_kwargs = ', '.join([f"{key}={repr(arg)}" for key, arg in kwargs.items()])
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})."
raise SpyAssertionFailed(f"{error} with exact arguments ({repr_args}) and keyword arguments ({repr_kwargs}).", self.__calls) return SpyAssertion(compare, msg, self.__calls)
return compare
def with_no_argument(self): def with_no_argument(self) -> SpyAssertion:
""" """
Checks if the Spy has been called the given number of times Checks if the Spy has been called the given number of times
with all the given arguments. with all the given arguments.
""" """
compare, error = self.__match_calls_for_condition(lambda a, kw: len(a) == 0 and len(kw) == 0) compare, error = self.__match_calls_for_condition(lambda a, kw: len(a) == 0 and len(kw) == 0)
if not compare: return SpyAssertion(compare, f"{error} with no arguments.", self.__calls)
raise SpyAssertionFailed(f"{error} with no arguments.", self.__calls)
return compare
class Spy(AssertionInterface):
class Spy:
""" """
Class to spy into method calls with natural language expressions. Class to spy into method calls with natural language expressions.
""" """
def __init__(self, function: Callable = None): def __init__(self, function: Callable = None):
super().__init__(function)
self.function = function self.function = function
self.calls = [] self.calls = []
@ -237,7 +225,7 @@ class Spy:
self.function(*args, **kwargs) self.function(*args, **kwargs)
@property @property
def was_called(self) -> CalledInterface: def called(self) -> CalledInterface:
""" """
Returns a boolean-able interface to check conditions for a given number of Returns a boolean-able interface to check conditions for a given number of
time the spy was called. time the spy was called.
@ -245,7 +233,7 @@ class Spy:
return CalledInterface(self.calls) return CalledInterface(self.calls)
@property @property
def was_not_called(self) -> CalledInterface: def not_called(self) -> CalledInterface:
""" """
Returns a boolean-able interface to check that conditions were never fulfilled Returns a boolean-able interface to check that conditions were never fulfilled
in the times the spy was called. in the times the spy was called.

View file

@ -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 <https://www.gnu.org/licenses/>.
"""
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

View file

@ -17,11 +17,9 @@
""" """
from time import sleep from time import sleep
import pytest
from PySide6.QtCore import QObject
from PySide6.QtQml import QJSValue 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.js import PyJSValue
from LogarithmPlotter.util.promise import PyPromise from LogarithmPlotter.util.promise import PyPromise
@ -95,10 +93,10 @@ class TestPyPromise:
# Check on our spy. # Check on our spy.
with qtbot.waitSignal(promise.fulfilled, timeout=10000): with qtbot.waitSignal(promise.fulfilled, timeout=10000):
pass pass
assert spy_fulfilled.was_called.once assert that(spy_fulfilled).was.called.once
assert spy_fulfilled.was_not_called.with_arguments(3) assert that(spy_fulfilled).was.not_called.with_arguments(3)
assert spy_fulfilled.was_called.with_arguments_matching(check_promise_result(3)) assert that(spy_fulfilled).was.called.with_arguments_matching(check_promise_result(3))
assert spy_rejected.was_not_called assert spy_rejected.was.not_called
def test_rejected(self, qtbot): def test_rejected(self, qtbot):
spy_fulfilled = Spy() spy_fulfilled = Spy()
@ -106,10 +104,10 @@ class TestPyPromise:
promise = PyPromise(async_throw) promise = PyPromise(async_throw)
then_res = promise.then(spy_fulfilled, spy_rejected) then_res = promise.then(spy_fulfilled, spy_rejected)
# Check if the return value is the same promise (so we can chain then) # 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. # Check on our spies.
with qtbot.waitSignal(promise.rejected, timeout=10000): with qtbot.waitSignal(promise.rejected, timeout=10000):
pass pass
assert spy_rejected.was_called.once assert that(spy_rejected).was.called.once
assert spy_rejected.was_called.with_arguments("Exception('aaaa')") assert that(spy_rejected).was.called.with_arguments("Exception('aaaa')")
assert spy_fulfilled.was_not_called assert that(spy_fulfilled).was.not_called