Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

matchers.py 2.9KB

1 year ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Hamcrest matchers useful throughout the test suite.
  5. """
  6. from typing import IO, Callable, Optional, TypeVar
  7. from hamcrest.core.base_matcher import BaseMatcher
  8. from hamcrest.core.description import Description
  9. from hamcrest.core.matcher import Matcher
  10. from twisted.python.filepath import IFilePath
  11. from twisted.python.reflect import fullyQualifiedName
  12. _A = TypeVar("_A")
  13. _B = TypeVar("_B")
  14. class _MatchAfter(BaseMatcher[_A]):
  15. """
  16. The implementation of L{after}.
  17. @ivar f: The function to apply.
  18. @ivar m: The matcher to use on the result.
  19. @ivar _e: After trying to apply the function fails with an exception, the
  20. exception that was raised. This can later be used by
  21. L{describe_mismatch}.
  22. """
  23. def __init__(self, f: Callable[[_A], _B], m: Matcher[_B]):
  24. self.f = f
  25. self.m = m
  26. self._e: Optional[Exception] = None
  27. def _matches(self, item: _A) -> bool:
  28. """
  29. Apply the function and delegate matching on the result.
  30. """
  31. try:
  32. transformed = self.f(item)
  33. except Exception as e:
  34. self._e = e
  35. return False
  36. else:
  37. return self.m.matches(transformed)
  38. def describe_mismatch(self, item: _A, mismatch_description: Description) -> None:
  39. """
  40. Describe the mismatching item or the exception that occurred while
  41. pre-processing it.
  42. @note: Since the exception reporting here depends on mutable state it
  43. will only work as long as PyHamcrest calls methods in the right
  44. order. The PyHamcrest Matcher interface doesn't seem to allow
  45. implementing this functionality in a more reliable way (see the
  46. implementation of L{assert_that}).
  47. """
  48. if self._e is None:
  49. super().describe_mismatch(item, mismatch_description)
  50. else:
  51. mismatch_description.append_text(
  52. f"{fullyQualifiedName(self.f)}({item!r}) raised\n"
  53. f"{fullyQualifiedName(self._e.__class__)}: {self._e}"
  54. )
  55. def describe_to(self, description: Description) -> None:
  56. """
  57. Create a text description of the match requirement.
  58. """
  59. description.append_text(f"[after {self.f}] ")
  60. self.m.describe_to(description)
  61. def after(f: Callable[[_A], _B], m: Matcher[_B]) -> Matcher[_A]:
  62. """
  63. Create a matcher which calls C{f} and uses C{m} to match the result.
  64. """
  65. return _MatchAfter(f, m)
  66. def fileContents(m: Matcher[str], encoding: str = "utf-8") -> Matcher[IFilePath]:
  67. """
  68. Create a matcher which matches a L{FilePath} the contents of which are
  69. matched by L{m}.
  70. """
  71. def getContent(p: IFilePath) -> str:
  72. f: IO[bytes]
  73. with p.open() as f:
  74. return f.read().decode(encoding)
  75. return after(getContent, m)