Completed
Push — master ( a3ba2f...57d762 )
by Erin
02:00
created

ExceptionMatcher::matchMessage()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 10
rs 9.4285
1
<?php
2
3
namespace Peridot\Leo\Matcher;
4
5
use Exception;
6
use Peridot\Leo\Matcher\Template\ArrayTemplate;
7
use Peridot\Leo\Matcher\Template\TemplateInterface;
8
use Throwable;
9
10
/**
11
 * ExceptionMatcher executes a callable and determines if an exception of a given type was thrown. It optionally
12
 * matches the exception message.
13
 *
14
 * @package Peridot\Leo\Matcher
15
 */
16
class ExceptionMatcher implements MatcherInterface
17
{
18
    use MatcherTrait;
19
20
    /**
21
     * @param string $exceptionType
22
     */
23
    public function __construct($exceptionType)
24
    {
25
        $this->expected = $exceptionType;
26
    }
27
28
    /**
29
     * Set arguments to be passed to the callable.
30
     *
31
     * @param  array $arguments
32
     * @return $this
33
     */
34
    public function setArguments(array $arguments)
35
    {
36
        $this->arguments = $arguments;
37
38
        return $this;
39
    }
40
41
    /**
42
     * Set the expected message of the exception.
43
     *
44
     * @param  string $message
45
     * @return $this
46
     */
47
    public function setExpectedMessage($message)
48
    {
49
        $this->expectedMessage = $message;
50
51
        return $this;
52
    }
53
54
    /**
55
     * Set the message thrown from an exception resulting from the
56
     * callable being invoked.
57
     *
58
     * @param string $message
59
     */
60
    public function setMessage($message)
61
    {
62
        $this->message = $message;
63
    }
64
65
    /**
66
     * Returns the arguments passed to the callable.
67
     *
68
     * @return array
69
     */
70
    public function getArguments()
71
    {
72
        return $this->arguments;
73
    }
74
75
    /**
76
     * Return the expected exception message.
77
     *
78
     * @return string
79
     */
80
    public function getExpectedMessage()
81
    {
82
        return $this->expectedMessage;
83
    }
84
85
    /**
86
     * Return the message thrown by an exception resulting from the callable
87
     * being invoked.
88
     *
89
     * @return string
90
     */
91
    public function getMessage()
92
    {
93
        return $this->message;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     *
99
     * If the expected message has been set, the message template will be used.
100
     *
101
     * @return TemplateInterface
102
     */
103
    public function getTemplate()
104
    {
105
        if ($this->expectedMessage) {
106
            return $this->getMessageTemplate();
107
        }
108
109
        if (!isset($this->template)) {
110
            return $this->getDefaultTemplate();
111
        }
112
113
        return $this->template;
114
    }
115
116
    /**
117
     * Set the template to be used when an expected exception message is provided.
118
     *
119
     * @param  TemplateInterface $template
120
     * @return $this
121
     */
122
    public function setMessageTemplate(TemplateInterface $template)
123
    {
124
        $this->messageTemplate = $template;
125
126
        return $this;
127
    }
128
129
    /**
130
     * Return a template for rendering exception message templates.
131
     *
132
     * return TemplateInterface
133
     */
134
    public function getMessageTemplate()
135
    {
136
        if ($this->messageTemplate) {
137
            return $this->messageTemplate;
138
        }
139
140
        return $this->getDefaultMessageTemplate();
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     *
146
     * @return TemplateInterface
147
     */
148
    public function getDefaultTemplate()
149
    {
150
        return new ArrayTemplate([
151
            'default' => 'Expected exception of type {{expected}}',
152
            'negated' => 'Expected type of exception not to be {{expected}}',
153
        ]);
154
    }
155
156
    /**
157
     * Return a default template for exception message assertions.
158
     *
159
     * @return ArrayTemplate
160
     */
161
    public function getDefaultMessageTemplate()
162
    {
163
        return new ArrayTemplate([
164
            'default' => 'Expected exception message {{expected}}, got {{actual}}',
165
            'negated' => 'Expected exception message {{actual}} not to equal {{expected}}',
166
        ]);
167
    }
168
169
    /**
170
     * Executes the callable and matches the exception type and exception message.
171
     *
172
     * @param $actual
173
     * @return Match
174
     */
175
    public function match($actual)
176
    {
177
        $this->validateCallable($actual);
178
179
        list($exception, $message) = $this->callableException($actual);
180
        $this->setMessage($message);
181
182
        return $this->matchMessage($actual, $exception, $message);
183
    }
184
185
    /**
186
     * Validate that expected is indeed a valid callable.
187
     *
188
     * @throws \BadFunctionCallException
189
     */
190
    protected function validateCallable($callable)
191
    {
192
        if (!is_callable($callable)) {
193
            $callable = rtrim(print_r($callable, true));
194
            throw new \BadFunctionCallException('Invalid callable ' . $callable . ' given');
195
        }
196
    }
197
198
    private function callableException($callable)
199
    {
200
        $exception = null;
201
        $message = null;
202
203
        try {
204
            call_user_func_array($callable, $this->arguments);
205
        } catch (Exception $exception) {
206
            $message = $exception->getMessage();
207
            // fall-through ...
208
        } catch (Throwable $exception) {
209
            $message = $exception->getMessage();
210
            // fall-through ...
211
        }
212
213
        return array($exception, $message);
214
    }
215
216
    private function matchMessage($actual, $exception, $message)
217
    {
218
        if (!$this->expectedMessage || $message === $this->expectedMessage) {
219
            return $this->matchType($actual, $exception);
220
        }
221
222
        $isNegated = $this->isNegated();
223
224
        return new Match($isNegated, $this->expectedMessage, $message, $isNegated);
225
    }
226
227
    private function matchType($actual, $exception)
228
    {
229
        $isMatch = $exception instanceof $this->expected;
230
        $isNegated = $this->isNegated();
231
232
        return new Match($isMatch xor $isNegated, $this->expected, $actual, $isNegated);
233
    }
234
235
    /**
236
     * @var mixed
237
     */
238
    protected $expected;
239
240
    /**
241
     * @var array
242
     */
243
    protected $arguments = [];
244
245
    /**
246
     * @var string
247
     */
248
    protected $expectedMessage = '';
249
250
    /**
251
     * A captured exception message.
252
     *
253
     * @var string
254
     */
255
    protected $message;
256
257
    /**
258
     * @var TemplateInterface
259
     */
260
    protected $messageTemplate;
261
}
262