Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#11616 disttrial worker test improvements #11617

Merged
merged 38 commits into from Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
81e94e8
add HasSum, IsSequenceOf, isFailure, similarFrame, and isTuple matchers
exarkun Aug 24, 2022
f6c57c2
Tests for isTuple
exarkun Aug 25, 2022
0c9f6a2
Tests for isFailure and some bug fixes
exarkun Aug 25, 2022
f004be9
Fix WorkerProtocolTests and LocalWorkerAMPTests
exarkun Aug 25, 2022
ed56326
news fragment
exarkun Aug 25, 2022
1f60522
use typing.List to support more Python versions
exarkun Aug 26, 2022
d949c89
switch to typing_extensions.Protocol to support more Python versions
exarkun Aug 26, 2022
dc28cb8
Merge remote-tracking branch 'origin/trunk' into 11616-test_worker-im…
exarkun Aug 26, 2022
e1eac51
some changes which hopefully improve readability
exarkun Aug 26, 2022
e51fb63
Side-step pyhamcrest's nonsensical types
exarkun Aug 26, 2022
9eab861
restore more of the original intent of this test
exarkun Aug 26, 2022
6deb9f1
flatten the matcher in test_runError to see if it helps readability
exarkun Aug 26, 2022
53471a6
Flatten the nested matches into several simpler matches
exarkun Aug 29, 2022
6412a67
remove unused imports
exarkun Aug 29, 2022
aff5118
simplify one more hamcrest usage
exarkun Aug 29, 2022
798952f
type annotate `makeTodo`
exarkun Aug 29, 2022
a45afc4
Force the correct types for trial's TestResult
exarkun Aug 29, 2022
416afd0
Old Python compat
exarkun Aug 29, 2022
9502b66
Merge remote-tracking branch 'origin/trunk' into 11616-test_worker-im…
exarkun Sep 1, 2022
a78ee19
Turn on remote debugging
exarkun Sep 1, 2022
057c67e
do tmate first
exarkun Sep 1, 2022
05077e2
some debug junk
exarkun Sep 1, 2022
0af8813
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 1, 2022
495a662
don't twiddle stdout, it makes debugging hard
exarkun Sep 1, 2022
3ca8770
Merge remote-tracking branch 'mine/11616-test_worker-improvements' in…
exarkun Sep 1, 2022
dd1482c
breaks other tests, hampers reproduction
exarkun Sep 1, 2022
2ab4d64
better failure messages
exarkun Sep 1, 2022
d4b5d21
let tests run first so tox initializes the environment, but end the t…
exarkun Sep 1, 2022
e0f8742
dump more debug info
exarkun Sep 1, 2022
3a3f278
not really usable
exarkun Sep 1, 2022
7a2775d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 1, 2022
b00c683
maybe eek out a bit more info
exarkun Sep 6, 2022
a52d00c
Merge remote-tracking branch 'mine/11616-test_worker-improvements' in…
exarkun Sep 6, 2022
804ceef
debug stuff interferes with test logic! rad
exarkun Sep 6, 2022
d612581
remove debug stuff
exarkun Sep 6, 2022
78deb9f
Merge remote-tracking branch 'origin/trunk' into 11616-test_worker-im…
exarkun Sep 6, 2022
a148193
Merge branch '11638.manhole-vs-excepthook' into 11616-test_worker-imp…
exarkun Sep 6, 2022
60c621d
Merge branch 'trunk' into 11616-test_worker-improvements
exarkun Sep 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.cfg
Expand Up @@ -51,6 +51,7 @@ packages = find:
test =
cython-test-exception-raiser >= 1.0.2, <2
PyHamcrest >= 1.9.0
hypothesis ~= 6.0

; List of dependencies required to build the documentation and test the
; release scripts and process.
Expand Down
Empty file.
173 changes: 172 additions & 1 deletion src/twisted/trial/_dist/test/matchers.py
Expand Up @@ -5,8 +5,48 @@
Hamcrest matchers useful throughout the test suite.
"""

from hamcrest import equal_to, has_length, has_properties
__all__ = [
"matches_result",
"HasSum",
"IsSequenceOf",
]

from typing import List, Sequence, Tuple, TypeVar

from hamcrest import (
contains_exactly,
contains_string,
equal_to,
has_length,
has_properties,
instance_of,
)
from hamcrest.core.base_matcher import BaseMatcher
from hamcrest.core.core.allof import AllOf
from hamcrest.core.description import Description
from hamcrest.core.matcher import Matcher
from typing_extensions import Protocol

from twisted.python.failure import Failure

T = TypeVar("T")


class Semigroup(Protocol[T]):
"""
A type with an associative binary operator.

Common examples of a semigroup are integers with addition and strings with
concatenation.
"""

def __add__(self, other: T) -> T:
"""
This must be associative: a + (b + c) == (a + b) + c
"""


S = TypeVar("S", bound=Semigroup)


def matches_result(
Expand All @@ -30,3 +70,134 @@ def matches_result(
"unexpectedSuccesses": unexpectedSuccesses,
}
)


class HasSum(BaseMatcher[Sequence[S]]):
"""
Match a sequence the elements of which sum to a value matched by
another matcher.

:ivar sumMatcher: The matcher which must match the sum.
:ivar zero: The zero value for the matched type.
"""

def __init__(self, sumMatcher: Matcher[S], zero: S) -> None:
self.sumMatcher = sumMatcher
self.zero = zero

def _sum(self, sequence: Sequence[S]) -> S:
if not sequence:
return self.zero
result = self.zero
for elem in sequence:
result = result + elem
return result

def _matches(self, item: Sequence[S]) -> bool:
"""
Determine whether the sum of the sequence is matched.
"""
s = self._sum(item)
return self.sumMatcher.matches(s)

def describe_mismatch(self, item: Sequence[S], description: Description) -> None:
"""
Describe the mismatch.
"""
s = self._sum(item)
description.append_description_of(self)
self.sumMatcher.describe_mismatch(s, description)
return None

def describe_to(self, description: Description) -> None:
"""
Describe this matcher for error messages.
"""
description.append_text("a sequence with sum ")
description.append_description_of(self.sumMatcher)
description.append_text(", ")


class IsSequenceOf(BaseMatcher[Sequence[T]]):
"""
Match a sequence where every element is matched by another matcher.

:ivar elementMatcher: The matcher which must match every element of the
sequence.
"""

def __init__(self, elementMatcher: Matcher[T]) -> None:
self.elementMatcher = elementMatcher

def _matches(self, item: Sequence[T]) -> bool:
"""
Determine whether every element of the sequence is matched.
"""
for elem in item:
if not self.elementMatcher.matches(elem):
return False
return True

def describe_mismatch(self, item: Sequence[T], description: Description) -> None:
"""
Describe the mismatch.
"""
for idx, elem in enumerate(item):
if not self.elementMatcher.matches(elem):
description.append_description_of(self)
description.append_text(f"not sequence with element #{idx} {elem!r}")

def describe_to(self, description: Description) -> None:
"""
Describe this matcher for error messages.
"""
description.append_text("a sequence containing only ")
description.append_description_of(self.elementMatcher)
description.append_text(", ")


def isFailure(**properties: Matcher[object]) -> Matcher[object]:
"""
Match an instance of L{Failure} with matching attributes.
"""
return AllOf(
instance_of(Failure),
has_properties(**properties),
)


def similarFrame(
functionName: str, fileName: str
) -> Matcher[Sequence[Tuple[str, str, int, List[object], List[object]]]]:
"""
Match a tuple representation of a frame like those used by
L{twisted.python.failure.Failure}.
"""
# The frames depend on exact layout of the source
# code in files and on the filesystem so we won't
# bother being very precise here. Just verify we
# see some distinctive fragments.
#
# In particular, the last frame should be a tuple like
#
# (functionName, fileName, someint, [], [])
return contains_exactly(
equal_to(functionName),
contains_string(fileName), # type: ignore[arg-type]
instance_of(int), # type: ignore[arg-type]
# Unfortunately Failure makes them sometimes tuples, sometimes
# dict_items.
has_length(0), # type: ignore[arg-type]
has_length(0), # type: ignore[arg-type]
)


def isTuple(*matchers: Matcher[object]) -> Matcher[object]:
"""
Match tuples for which the elements are matched by corresponding
elements of a tuple of matchers.
"""
return AllOf(
instance_of(tuple),
contains_exactly(*matchers), # type: ignore[arg-type]
)