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