Passed
Pull Request — master (#18)
by Harry
04:03
created

Table::run()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 19
rs 9.2222
c 0
b 0
f 0
ccs 11
cts 11
cp 1
cc 6
nc 12
nop 1
crap 6
1
<?php
2
/**
3
 * This file is part of graze/parallel-process.
4
 *
5
 * Copyright © 2018 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/parallel-process/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/parallel-process
12
 */
13
14
namespace Graze\ParallelProcess\Display;
15
16
use Exception;
17
use Graze\DiffRenderer\DiffConsoleOutput;
18
use Graze\DiffRenderer\Terminal\TerminalInterface;
19
use Graze\ParallelProcess\Event\PoolRunEvent;
20
use Graze\ParallelProcess\Event\RunEvent;
21
use Graze\ParallelProcess\OutputterInterface;
22
use Graze\ParallelProcess\PoolInterface;
23
use Graze\ParallelProcess\PriorityPool;
24
use Graze\ParallelProcess\ProcessRun;
25
use Graze\ParallelProcess\RunInterface;
26
use Symfony\Component\Console\Output\OutputInterface;
27
28
class Table
29
{
30
    use TagsTrait;
31
32
    const SPINNER = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
33
34
    /** @var PoolInterface */
35
    private $pool;
36
    /** @var string[] */
37
    private $rows = [];
38
    /** @var Exception[] */
39
    private $exceptions;
40
    /** @var DiffConsoleOutput */
41
    private $output;
42
    /** @var TerminalInterface */
43
    private $terminal;
44
    /** @var bool */
45
    private $showOutput = true;
46
    /** @var bool */
47
    private $showSummary = true;
48
49
    /**
50
     * Table constructor.
51
     *
52
     * @param OutputInterface    $output
53
     * @param PoolInterface|null $pool
54
     */
55 16
    public function __construct(OutputInterface $output, PoolInterface $pool = null)
56
    {
57 16
        $this->pool = $pool ?: new PriorityPool();
58 16
        if (!$output instanceof DiffConsoleOutput) {
59 3
            $this->output = new DiffConsoleOutput($output);
60 3
            $this->output->setTrim(true);
61
        } else {
62 16
            $this->output = $output;
63
        }
64 16
        $this->terminal = $this->output->getTerminal();
65 16
        $this->exceptions = [];
66
67 16
        $this->pool->addListener(
68 16
            PoolRunEvent::POOL_RUN_ADDED,
69
            function (PoolRunEvent $event) {
70 13
                $this->add($event->getRun());
71 16
            }
72
        );
73
74 16
        array_map([$this, 'add'], $this->pool->getAll());
75 16
    }
76
77
    /**
78
     * @param RunInterface $run
79
     * @param string       $status
80
     *
81
     * @return string
82
     */
83 13
    private function formatRow(RunInterface $run, $status)
84
    {
85 13
        $tags = $this->formatTags($run->getTags());
86 13
        $extra = ($this->showOutput && $run instanceof OutputterInterface && mb_strlen($run->getLastMessage()) > 0)
87 2
            ? '  ' . $this->terminal->filter($run->getLastMessage())
88 13
            : '';
89 13
        return sprintf("%s (<comment>%6.2fs</comment>) %s%s", $tags, $run->getDuration(), $status, $extra);
90
    }
91
92
    /**
93
     * @param RunInterface $run
94
     */
95 13
    public function add(RunInterface $run)
96
    {
97 13
        $index = count($this->rows);
98 13
        $this->rows[$index] = $this->formatRow($run, '');
99 13
        $spinner = 0;
100 13
        $bar = new TinyProgressBar(2, TinyProgressBar::FORMAT_DEFAULT, 1);
101
102 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
103 10
            $run->addListener(
104 10
                RunEvent::UPDATED,
105
                function (RunEvent $event) use ($index, &$spinner, $bar) {
106 10
                    $run = $event->getRun();
107 10
                    $progress = $run->getProgress();
108 10
                    $status = (!is_null($progress))
109 1
                        ? $bar->setPosition($progress[2])
110 1
                              ->setPosition($progress[0])
111 1
                              ->setMax($progress[1])
112 1
                              ->render()
113 10
                        : mb_substr(static::SPINNER, $spinner++, 1);
114 10
                    $this->rows[$index] = $this->formatRow(
115 10
                        $run,
116 10
                        $status
117
                    );
118 10
                    if ($spinner > mb_strlen(static::SPINNER) - 1) {
119 1
                        $spinner = 0;
120
                    }
121 10
                    $this->render();
122 10
                }
123
            );
124
        }
125
126 13
        $run->addListener(
127 13
            RunEvent::SUCCESSFUL,
128
            function (RunEvent $event) use ($index, &$bar, &$spinner) {
0 ignored issues
show
Unused Code introduced by
The import $spinner is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $bar is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
129 11
                $this->rows[$index] = $this->formatRow($event->getRun(), "<info>✓</info>");
130 11
                $this->render($index);
131 13
            }
132
        );
133 13
        $run->addListener(
134 13
            RunEvent::FAILED,
135 13
            function (RunEvent $event) use ($index, &$bar, &$spinner) {
0 ignored issues
show
Unused Code introduced by
The import $spinner is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $bar is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
136 3
                $run = $event->getRun();
137 3
                $this->rows[$index] = $this->formatRow($run, "<error>x</error>");
138 3
                $this->render($index);
139 3
                $this->exceptions += $run->getExceptions();
140 13
            }
141
        );
142 13
        if ($run instanceof ProcessRun) {
143 13
            $run->setUpdateOnProcessOutput(false);
144
        }
145 13
        $this->updateRowKeyLengths($run->getTags());
146 13
    }
147
148
    /**
149
     * @return string
150
     */
151 2
    private function getSummary()
152
    {
153 2
        if ($this->pool->hasStarted()) {
0 ignored issues
show
Bug introduced by
The method hasStarted() does not exist on Graze\ParallelProcess\PoolInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Graze\ParallelProcess\PoolInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
        if ($this->pool->/** @scrutinizer ignore-call */ hasStarted()) {
Loading history...
154 2
            if ($this->pool->isRunning()) {
0 ignored issues
show
Bug introduced by
The method isRunning() does not exist on Graze\ParallelProcess\PoolInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Graze\ParallelProcess\PoolInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

154
            if ($this->pool->/** @scrutinizer ignore-call */ isRunning()) {
Loading history...
155 2
                return sprintf(
156 2
                    '<comment>Total</comment>: %2d, <comment>Running</comment>: %2d, <comment>Waiting</comment>: %2d',
157 2
                    $this->pool->count(),
158 2
                    count($this->pool->getRunning()),
159 2
                    count($this->pool->getWaiting())
160
                );
161
            } else {
162 2
                return '';
163
            }
164
        } else {
165 2
            return 'waiting...';
166
        }
167
    }
168
169
    /**
170
     * Render a specific row
171
     *
172
     * @param int $row
173
     */
174 13
    private function render($row = 0)
175
    {
176 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
177 10
            $rows = ($this->showSummary ? array_merge($this->rows, [$this->getSummary()]) : $this->rows);
178 10
            $this->output->reWrite($rows, !$this->showSummary);
179
        } else {
180 3
            $this->output->writeln($this->rows[$row]);
181
        }
182 13
    }
183
184
    /**
185
     * @param float $checkInterval
186
     *
187
     * @return bool true if all processes were successful
188
     * @throws Exception
189
     */
190 13
    public function run($checkInterval = PriorityPool::CHECK_INTERVAL)
191
    {
192 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
193 10
            $this->render();
194
        }
195 13
        $output = $this->pool->run($checkInterval);
196 13
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE && $this->showSummary) {
197 2
            $this->render();
198
        }
199
200 13
        if (count($this->exceptions) > 0) {
201 3
            foreach ($this->exceptions as $exception) {
202 3
                $this->output->writeln($exception->getMessage());
203
            }
204
205 3
            throw reset($this->exceptions);
206
        }
207
208 10
        return $output;
209
    }
210
211
    /**
212
     * @return bool
213
     */
214 1
    public function isShowOutput()
215
    {
216 1
        return $this->showOutput;
217
    }
218
219
    /**
220
     * @param bool $showOutput
221
     *
222
     * @return $this
223
     */
224 11
    public function setShowOutput($showOutput)
225
    {
226 11
        $this->showOutput = $showOutput;
227 11
        return $this;
228
    }
229
230
    /**
231
     * @return bool
232
     */
233 1
    public function isShowSummary()
234
    {
235 1
        return $this->showSummary;
236
    }
237
238
    /**
239
     * @param bool $showSummary
240
     *
241
     * @return $this
242
     */
243 14
    public function setShowSummary($showSummary)
244
    {
245 14
        $this->showSummary = $showSummary;
246 14
        return $this;
247
    }
248
}
249