Passed
Push — 26-use-psr-14-event-dispatcher ( ab95da )
by Jakub
02:36
created

Tester::__construct()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 60
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 12.893

Importance

Changes 16
Bugs 1 Features 2
Metric Value
cc 4
eloc 39
c 16
b 1
f 2
nc 8
nop 5
dl 0
loc 60
ccs 8
cts 45
cp 0.1778
crap 12.893
rs 9.296

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace MyTester;
5
6
use Composer\InstalledVersions;
7
use MyTester\Bridges\NetteRobotLoader\TestSuitesFinder;
8
use MyTester\ResultsFormatters\Helper as ResultsHelper;
9
use Nette\CommandLine\Console;
10
use Psr\EventDispatcher\EventDispatcherInterface;
11
12
/**
13
 * Automated tests runner
14
 *
15
 * @author Jakub Konečný
16
 * @property-read string[] $suites
17
 * @property bool $useColors
18
 * @method void onExecute()
19
 * @method void onFinish()
20
 */
21
final class Tester
22
{
23
    use \Nette\SmartObject;
24
25
    private const PACKAGE_NAME = "konecnyjakub/mytester";
26
27
    /** @var string[] */
28
    private array $suites = [];
29
    /** @var callable[] */
30
    public array $onExecute = [];
31
    /** @var callable[] */
32
    public array $onFinish = [];
33
    public ITestSuiteFactory $testSuiteFactory;
34
    public ITestSuitesFinder $testSuitesFinder;
35
    private IResultsFormatter $resultsFormatter;
36
    private Console $console;
37
    private readonly string $folder;
38
    private bool $useColors = false;
39
    /** @var ITesterExtension[] */
40
    private array $extensions = [];
41
    private EventDispatcherInterface $eventDispatcher;
42
43
    /**
44
     * @param ITesterExtension[] $extensions
45
     */
46
    public function __construct(
47
        string $folder,
48
        ITestSuitesFinder $testSuitesFinder = null,
49
        ITestSuiteFactory $testSuiteFactory = new TestSuiteFactory(),
50
        array $extensions = [],
51
        ?IResultsFormatter $resultsFormatter = null
52
    ) {
53
        if ($testSuitesFinder === null) {
54
            $testSuitesFinder = new ChainTestSuitesFinder();
55
            $testSuitesFinder->registerFinder(new ComposerTestSuitesFinder());
56
            $testSuitesFinder->registerFinder(new TestSuitesFinder());
57
        }
58
        $this->testSuitesFinder = $testSuitesFinder;
59
        $this->testSuiteFactory = $testSuiteFactory;
60
        $this->folder = $folder;
61
        $this->console = new Console();
62
        $this->resultsFormatter = $resultsFormatter ?? new ResultsFormatters\Console();
63
        if (is_subclass_of($this->resultsFormatter, ITestsFolderAwareResultsFormatter::class)) {
64
            $this->resultsFormatter->setTestsFolder($this->folder);
65
        }
66
        if (is_subclass_of($this->resultsFormatter, IConsoleAwareResultsFormatter::class)) {
67
            $this->resultsFormatter->setConsole($this->console);
68
        }
69
        $this->extensions = $extensions;
70
71
        $listenerProvider = new TesterListenerProvider($this->extensions);
72
        $listenerProvider->registerListener(Events\TestsStartedEvent::class, function () {
73
            $this->printInfo();
74
        });
75
        $listenerProvider->registerListener(Events\TestsStartedEvent::class, [$this->resultsFormatter, "setup"]);
76
        $listenerProvider->registerListener(Events\TestsStartedEvent::class, [$this, "onExecute"]);
77
        $listenerProvider->registerListener(
78
            Events\TestsStartedEvent::class,
79
            function (Events\TestsStartedEvent $event) {
80
                $this->resultsFormatter->reportTestsStarted($event->testCases);
81
            }
82
        );
83
        $listenerProvider->registerListener(Events\TestsFinishedEvent::class, function () {
84 1
            $this->printResults();
85 1
        });
86
        $listenerProvider->registerListener(Events\TestsFinishedEvent::class, [$this, "onFinish"]);
87
        $listenerProvider->registerListener(
88
            Events\TestsFinishedEvent::class,
89
            function (Events\TestsFinishedEvent $event) {
90 1
                $this->resultsFormatter->reportTestsFinished($event->testCases);
91 1
            }
92
        );
93
        $listenerProvider->registerListener(
94
            Events\TestCaseStarted::class,
95
            function (Events\TestCaseStarted $event) {
96 1
                $this->resultsFormatter->reportTestCaseStarted($event->testCase);
97 1
            }
98
        );
99
        $listenerProvider->registerListener(
100
            Events\TestCaseFinished::class,
101
            function (Events\TestCaseFinished $event) {
102 1
                $this->resultsFormatter->reportTestCaseFinished($event->testCase);
103 1
            }
104
        );
105
        $this->eventDispatcher = new TesterEventDispatcher($listenerProvider);
106
    }
107
108
    /**
109
     * @return string[]
110
     */
111
    protected function getSuites(): array
112
    {
113
        if (count($this->suites) === 0) {
114
            $this->suites = $this->testSuitesFinder->getSuites($this->folder);
115
        }
116
        return $this->suites;
117
    }
118
119
    protected function isUseColors(): bool
120
    {
121
        return $this->useColors;
122
    }
123
124
    protected function setUseColors(bool $useColors): void
125
    {
126
        $this->useColors = $useColors;
127
        $this->console->useColors($useColors);
128
    }
129
130
    /**
131
     * Execute all tests
132
     */
133
    public function execute(): void
134
    {
135
        $failed = false;
136
137
        /** @var TestCase[] $testCases */
138
        $testCases = [];
139
        foreach ($this->getSuites() as $suite) {
140
            $testCases[] = $this->testSuiteFactory->create($suite);
141
        }
142
143
        $this->eventDispatcher->dispatch(new Events\TestsStartedEvent($testCases));
144
145 1
        foreach ($testCases as $testCase) {
146 1
            $this->eventDispatcher->dispatch(new Events\TestCaseStarted($testCase));
147 1
            if (!$testCase->run()) {
148
                $failed = true;
149
            }
150 1
            $this->eventDispatcher->dispatch(new Events\TestCaseFinished($testCase));
151
        }
152
153 1
        $this->eventDispatcher->dispatch(new Events\TestsFinishedEvent($testCases));
154
155
        exit((int) $failed);
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
156
    }
157
158
    /**
159
     * Print version of My Tester and PHP
160
     */
161
    private function printInfo(): void
162
    {
163
        $version = InstalledVersions::getPrettyVersion(static::PACKAGE_NAME);
164
        echo $this->console->color("silver", "My Tester $version\n");
165
        echo "\n";
166
        echo $this->console->color("silver", "PHP " . PHP_VERSION . "(" . PHP_SAPI . ")\n");
167
        echo "\n";
168
    }
169
170
    private function printResults(): void
171
    {
172 1
        $filename = $this->resultsFormatter->getOutputFileName((string) getcwd());
173 1
        if (ResultsHelper::isFileOutput($filename)) {
174
            echo "Results are redirected into file $filename\n";
175
        }
176
177
        /** @var resource $outputFile */
178 1
        $outputFile = fopen($filename, "w");
179 1
        fwrite($outputFile, $this->resultsFormatter->render());
180 1
        fclose($outputFile);
181 1
    }
182
}
183