-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCallableThrowsNot.php
144 lines (130 loc) · 5.17 KB
/
CallableThrowsNot.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<?php
/**
* PHPUnitThrowableAssertions - Throwable-related PHPUnit assertions.
*
* @copyright Copyright (c) 2021, Daniel Rudolf (<https://www.daniel-rudolf.de>)
*
* This file is copyrighted by the contributors recorded in the version control
* history of the file, available from the following original location:
*
* <https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/Constraint/CallableThrowsNot.php>
*
* @license http://opensource.org/licenses/MIT The MIT License
*
* SPDX-License-Identifier: MIT
* License-Filename: LICENSE
*/
declare(strict_types=1);
namespace PhrozenByte\PHPUnitThrowableAsserts\Constraint;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\Exception as PHPUnitException;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\InvalidArgumentException;
use Throwable;
/**
* Constraint that asserts that a Callable doesn't throw a specific Throwable.
*
* This constraint calls the given Callable and catches any Throwable matching
* the given class, message and code. All conditions must match, otherwise the
* Throwable is re-thrown.
*
* This is NOT the same as negating the CallableThrows constraint, which
* consumes all non-matching Throwables and throws a ExpectationFailedException
* instead. CallableThrowsNot will rather re-throw any non-matching Throwable.
* A ExpectationFailedException is only thrown when the Callable throws a
* Throwable matching all given conditions.
*
* The class name of the expected Throwable, a optional constraint to match the
* Throwable's message, the optional code to assert, and whether an exact match
* of the Throwable's class is required are passed in the constructor. The
* Callable is the value to evaluate (`$other`).
*/
class CallableThrowsNot extends AbstractCallableThrows
{
/**
* CallableThrowsNot constructor.
*
* @param string $className assert that no Throwable of the given class is thrown
* @param Constraint|string|null $message catch Throwables with a message matching the given constraint only
* @param int|string|null $code catch Throwables with the given code only
* @param bool $exactMatch whether an exact match of the Throwable class is caught only
*
* @throws InvalidArgumentException
*/
public function __construct(
string $className = Throwable::class,
$message = null,
$code = null,
bool $exactMatch = false
) {
parent::__construct($className, $message, $code, $exactMatch);
}
/**
* Returns a human-readable string representation of this Constraint.
*
* @return string string representation of the Constraint
*/
public function toString(): string
{
return sprintf('does not throw a %s', $this->className)
. ($this->exactMatch ? ' (exact match)' : '')
. (($this->code !== null) ? sprintf(' with code %s', $this->exporter()->export($this->code)) : '')
. (($this->messageConstraint && ($this->code !== null)) ? ' and' : '')
. ($this->messageConstraint ? ' whose message ' . $this->messageConstraint->toString() : '');
}
/**
* Evaluates whether the given value matches the Constraint.
*
* If `$returnResult` is set to `false` (default), an exception is thrown
* in case of a failure. `null` is returned otherwise.
*
* If `$returnResult` is `true`, the result of the evaluation is returned
* as a boolean instead: `true` in case of success, `false` in case of a
* failure.
*
* @param mixed $other the value to evaluate
* @param string $description additional information about the test
* @param bool $returnResult whether to return the evaluation result
*
* @return bool|null evaluation result if `$returnResult` is set `true`
*
* @throws ExpectationFailedException
* @throws PHPUnitException
*/
public function evaluate($other, string $description = '', bool $returnResult = false)
{
if (!is_callable($other)) {
if (!$returnResult) {
$this->fail($other, $description);
}
return false;
}
try {
$other();
} catch (Throwable $throwable) {
if (!($throwable instanceof $this->className)) {
throw $throwable;
}
if ($this->exactMatch && (get_class($throwable) !== $this->className)) {
throw $throwable;
}
if ($this->messageConstraint !== null) {
try {
$this->messageConstraint->evaluate($throwable->getMessage());
} catch (ExpectationFailedException $messageException) {
throw $throwable;
}
}
if ($this->code !== null) {
if ($throwable->getCode() !== $this->code) {
throw $throwable;
}
}
if (!$returnResult) {
$this->fail($other, $description, null, $throwable);
}
return false;
}
return $returnResult ? true : null;
}
}