Completed
Push — master ( 1945fe...43f058 )
by Brian
9s
created

Test   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 152
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 8
Bugs 1 Features 1
Metric Value
wmc 21
c 8
b 1
f 1
lcom 1
cbo 4
dl 0
loc 152
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A run() 0 11 2
A runSetup() 0 9 2
A restoreErrorHandler() 0 9 2
A failIfPassing() 0 6 2
A executeTest() 0 15 3
A runTearDown() 0 16 4
B handleErrors() 0 22 4
1
<?php
2
3
namespace Peridot\Core;
4
5
use Error;
6
use ErrorException;
7
use Exception;
8
use Throwable;
9
10
/**
11
 * The main test fixture for Peridot.
12
 *
13
 * @package Peridot\Core
14
 */
15
class Test extends AbstractTest
16
{
17
    /**
18
     * @param string $description
19
     * @param callable $definition
20
     */
21
    public function __construct($description, callable $definition = null)
22
    {
23
        if ($definition === null) {
24
            $this->pending = true;
25
            $definition = function () {
26
                //noop
27
            };
28
        }
29
        parent::__construct($description, $definition);
30
    }
31
32
    /**
33
     * Execute the test along with any setup and tear down functions.
34
     *
35
     * @param  TestResult $result
36
     * @return void
37
     */
38
    public function run(TestResult $result)
39
    {
40
        $result->startTest($this);
41
42
        if ($this->getPending()) {
43
            $result->pendTest($this);
44
            return;
45
        }
46
        $this->executeTest($result);
47
        $result->endTest($this);
48
    }
49
50
    /**
51
     * Attempt to execute setup functions and run the test definition
52
     *
53
     * @param TestResult $result
54
     */
55
    protected function executeTest(TestResult $result)
56
    {
57
        $action = ['passTest', $this];
58
        $handler = $this->handleErrors($result, $action);
59
        try {
60
            $this->runSetup();
61
            call_user_func_array($this->getDefinition(), $this->getDefinitionArguments());
62
        } catch (Throwable $e) {
63
            $this->failIfPassing($action, $e);
64
        } catch (Exception $e) {
65
            $this->failIfPassing($action, $e);
66
        }
67
        $this->runTearDown($result, $action);
68
        $this->restoreErrorHandler($handler);
69
    }
70
71
    /**
72
     * Excecute the test's setup functions
73
     */
74
    protected function runSetup()
75
    {
76
        $this->forEachNodeTopDown(function (TestInterface $node) {
77
            $setups = $node->getSetupFunctions();
78
            foreach ($setups as $setup) {
79
                $setup();
80
            }
81
        });
82
    }
83
84
    /**
85
     * Run the tests tear down methods and have the result
86
     * perform the method indicated by $action
87
     *
88
     * @param TestResult $result
89
     * @param array $action
90
     */
91
    protected function runTearDown(TestResult $result, array $action)
92
    {
93
        $this->forEachNodeBottomUp(function (TestInterface $test) use ($result, &$action) {
94
            $tearDowns = $test->getTearDownFunctions();
95
            foreach ($tearDowns as $tearDown) {
96
                try {
97
                    $tearDown();
98
                } catch (Throwable $e) {
99
                    $this->failIfPassing($action, $e);
100
                } catch (Exception $e) {
101
                    $this->failIfPassing($action, $e);
102
                }
103
            }
104
        });
105
        call_user_func_array([$result, $action[0]], array_slice($action, 1));
106
    }
107
108
    /**
109
     * Set an error handler to handle errors within the test
110
     *
111
     * @param TestResult $result
112
     * @param array      &$action
113
     *
114
     * @return callable|null
115
     */
116
    protected function handleErrors(TestResult $result, array &$action)
117
    {
118
        $handler = null;
119
        $handler = set_error_handler(function ($severity, $message, $path, $line) use ($result, &$action, &$handler) {
120
            // if there is an existing error handler, call it and record the result
121
            $isHandled = $handler && false !== $handler($severity, $message, $path, $line);
122
123
            if (!$isHandled) {
124
                $result->getEventEmitter()->emit('error', [$severity, $message, $path, $line]);
125
126
                // honor the error reporting configuration - this also takes care of the error control operator (@)
127
                $errorReporting = error_reporting();
128
                $shouldHandle = $severity === ($severity & $errorReporting);
129
130
                if ($shouldHandle) {
131
                    $this->failIfPassing($action, new ErrorException($message, 0, $severity, $path, $line));
132
                }
133
            }
134
        });
135
136
        return $handler;
137
    }
138
139
    /**
140
     * Restore the previous error handler
141
     *
142
     * @param callable|null $handler
143
     */
144
    protected function restoreErrorHandler($handler)
145
    {
146
        if ($handler) {
147
            set_error_handler($handler);
148
        } else {
149
            // unfortunately, we can't pass null until PHP 5.5
150
            set_error_handler(function () { return false; });
151
        }
152
    }
153
154
    /**
155
     * Fail the test, but do not overwrite existing failures
156
     *
157
     * @param array &$action
158
     * @param mixed $error
159
     */
160
    protected function failIfPassing(array &$action, $error)
161
    {
162
        if ('passTest' === $action[0]) {
163
            $action = ['failTest', $this, $error];
164
        }
165
    }
166
}
167