Completed
Pull Request — master (#177)
by Erin
04:31
created

Test::handleErrors()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
rs 8.6737
cc 5
eloc 11
nc 1
nop 2
1
<?php
2
3
namespace Peridot\Core;
4
5
use ErrorException;
6
use Exception;
7
use Throwable;
8
9
/**
10
 * The main test fixture for Peridot.
11
 *
12
 * @package Peridot\Core
13
 */
14
class Test extends AbstractTest
15
{
16
    /**
17
     * @param string $description
18
     * @param callable $definition
19
     */
20
    public function __construct($description, callable $definition = null)
21
    {
22
        if ($definition === null) {
23
            $this->pending = true;
24
            $definition = function () {
25
                //noop
26
            };
27
        }
28
        parent::__construct($description, $definition);
29
    }
30
31
    /**
32
     * Execute the test along with any setup and tear down functions.
33
     *
34
     * @param  TestResult $result
35
     * @return void
36
     */
37
    public function run(TestResult $result)
38
    {
39
        $result->startTest($this);
40
41
        if ($this->getPending()) {
42
            $result->pendTest($this);
43
            return;
44
        }
45
        $this->executeTest($result);
46
        $result->endTest($this);
47
    }
48
49
    /**
50
     * Attempt to execute setup functions and run the test definition
51
     *
52
     * @param TestResult $result
53
     */
54
    protected function executeTest(TestResult $result)
55
    {
56
        $action = ['passTest', $this];
57
        $originalErrorHandler = $this->handleErrors($result, $action);
58
59
        try {
60
            $this->runSetup();
61
            call_user_func_array($this->getDefinition(), $this->getDefinitionArguments());
62
        } catch (Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
63
            if ('passTest' === $action[0]) {
64
                $action = ['failTest', $this, $e];
65
            }
66
        } catch (Exception $e) {
67
            if ('passTest' === $action[0]) {
68
                $action = ['failTest', $this, $e];
69
            }
70
        }
71
        $this->runTearDown($result, $action);
72
73
        // don't use restore_error_handler(), because the error handler may be replaced by the system under test
74
        set_error_handler($originalErrorHandler);
75
    }
76
77
    /**
78
     * Excecute the test's setup functions
79
     */
80
    protected function runSetup()
81
    {
82
        $this->forEachNodeTopDown(function (TestInterface $node) {
83
            $setups = $node->getSetupFunctions();
84
            foreach ($setups as $setup) {
85
                $setup();
86
            }
87
        });
88
    }
89
90
    /**
91
     * Run the tests tear down methods and have the result
92
     * perform the method indicated by $action
93
     *
94
     * @param TestResult $result
95
     * @param array $action
96
     */
97
    protected function runTearDown(TestResult $result, $action)
98
    {
99
        $this->forEachNodeBottomUp(function (TestInterface $test) use ($result, &$action) {
100
            $tearDowns = $test->getTearDownFunctions();
101
            foreach ($tearDowns as $tearDown) {
102
                try {
103
                    $tearDown();
104
                } catch (Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
105
                    if ('passTest' === $action[0]) {
106
                        $action = ['failTest', $this, $e];
107
                    }
108
                } catch (Exception $e) {
109
                    if ('passTest' === $action[0]) {
110
                        $action = ['failTest', $this, $e];
111
                    }
112
                }
113
            }
114
        });
115
        call_user_func_array([$result, $action[0]], array_slice($action, 1));
116
    }
117
118
    /**
119
     * Set an error handler to handle errors within the test
120
     *
121
     * @param TestResult $result
122
     * @param array      &$action
123
     *
124
     * @return callable|null
125
     */
126
    protected function handleErrors(TestResult $result, &$action)
127
    {
128
        $handler = null;
129
        $handler = set_error_handler(function ($severity, $message, $path, $line) use ($result, &$action, &$handler) {
130
            // if there is an existing error handler, call it and record the result
131
            $isHandled = $handler && false !== $handler($severity, $message, $path, $line);
132
133
            if (!$isHandled) {
134
                $result->getEventEmitter()->emit('error', [$severity, $message, $path, $line]);
135
136
                // honor the error reporting configuration - this also takes care of the error control operator (@)
137
                $errorReporting = error_reporting();
138
                $shouldHandle = $severity === ($severity & $errorReporting);
139
140
                if ($shouldHandle && 'passTest' === $action[0]) {
141
                    $action = ['failTest', $this, new ErrorException($message, 0, $severity, $path, $line)];
142
                }
143
            }
144
        });
145
146
        return $handler;
147
    }
148
}
149