Completed
Pull Request — master (#86)
by Alessandro
05:32
created

Runner   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 96.83%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 19
lcom 1
cbo 9
dl 0
loc 167
ccs 61
cts 63
cp 0.9683
rs 10
c 1
b 1
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
B run() 0 44 6
A getReturnCode() 0 10 3
A createProcessStackFromFiles() 0 7 2
A runProcess() 0 17 4
A markProcessCompleted() 0 18 3
1
<?php
2
3
namespace Paraunit\Runner;
4
5
use Paraunit\Filter\Filter;
6
use Paraunit\Lifecycle\EngineEvent;
7
use Paraunit\Lifecycle\ProcessEvent;
8
use Paraunit\Printer\DebugPrinter;
9
use Paraunit\Process\AbstractParaunitProcess;
10
use Paraunit\Process\ParaunitProcessInterface;
11
use Paraunit\Process\ProcessFactory;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14
15
/**
16
 * Class Runner
17
 * @package Paraunit\Runner
18
 */
19
class Runner
20
{
21
    /** @var int */
22
    private $maxProcessNumber;
23
24
    /** @var AbstractParaunitProcess[] */
25
    private $processStack;
26
27
    /** @var AbstractParaunitProcess[] */
28
    private $processCompleted;
29
30
    /** @var AbstractParaunitProcess[] */
31
    private $processRunning;
32
33
    /** @var  ProcessFactory */
34
    private $processFactory;
35
36
    /** @var EventDispatcherInterface */
37
    private $eventDispatcher;
38
39
    /** @var Filter */
40
    private $filter;
41
42
    /**
43
     * @param EventDispatcherInterface $eventDispatcher
44
     * @param ProcessFactory $processFactory
45
     * @param Filter $filter
46
     * @param int $maxProcessNumber
47
     */
48 11
    public function __construct(
49
        EventDispatcherInterface $eventDispatcher,
50
        ProcessFactory $processFactory,
51
        Filter $filter,
52
        $maxProcessNumber = 10
53
    ) {
54
55 11
        $this->eventDispatcher = $eventDispatcher;
56 11
        $this->processFactory = $processFactory;
57 11
        $this->filter = $filter;
58 11
        $this->maxProcessNumber = $maxProcessNumber;
59
60 11
        $this->processStack = array();
61 11
        $this->processCompleted = array();
62 11
        $this->processRunning = array();
63 11
    }
64
65
    /**
66
     * @param OutputInterface $outputInterface
67
     * @param bool $debug
68
     * @return int
69
     */
70 11
    public function run(OutputInterface $outputInterface, $debug = false)
71
    {
72 11
        $this->eventDispatcher->dispatch(EngineEvent::BEFORE_START, new EngineEvent($outputInterface));
73 11
        $start = new \Datetime('now');
74
75 11
        $files = $this->filter->filterTestFiles();
76 11
        $this->createProcessStackFromFiles($files);
77
78 11
        $this->eventDispatcher->dispatch(
79 11
            EngineEvent::START,
80 11
            new EngineEvent($outputInterface, array('start' => $start,))
81
        );
82
83 11
        while (count($this->processStack) > 0 || count($this->processRunning) > 0) {
84 11
            if ($process = $this->runProcess($debug)) {
85 11
                $this->eventDispatcher->dispatch(ProcessEvent::PROCESS_STARTED, new ProcessEvent($process));
86
            }
87
88 11
            foreach ($this->processRunning as $process) {
89 11
                if ($process->isTerminated()) {
90 11
                    $this->eventDispatcher->dispatch(
91 11
                        ProcessEvent::PROCESS_TERMINATED,
92 11
                        new ProcessEvent($process, array('output_interface' => $outputInterface,))
93
                    );
94
                    // Completed or back to the stack
95 11
                    $this->markProcessCompleted($process);
96
                }
97
98 11
                usleep(500);
99
            }
100
        }
101
102 11
        $end = new \Datetime('now');
103
104 11
        $this->eventDispatcher->dispatch(
105 11
            EngineEvent::END,
106 11
            new EngineEvent(
107
                $outputInterface,
108 11
                array('end' => $end, 'start' => $start, 'process_completed' => $this->processCompleted)
109
            )
110
        );
111
112 11
        return $this->getReturnCode();
113
    }
114
115
    /**
116
     * @return int
117
     */
118 11
    private function getReturnCode()
119
    {
120 11
        foreach ($this->processCompleted as $process) {
121 11
            if ($process->getExitCode() !== 0) {
122 11
                return 10;
123
            }
124
        }
125
126 4
        return 0;
127
    }
128
129
    /**
130
     * @param string[] $files
131
     */
132 11
    private function createProcessStackFromFiles($files)
133
    {
134 11
        foreach ($files as $file) {
135 11
            $process = $this->processFactory->createProcess($file);
136 11
            $this->processStack[$process->getUniqueId()] = $process;
137
        }
138 11
    }
139
140
    /**
141
     * @param $debug
142
     *
143
     * @return ParaunitProcessInterface | null
144
     */
145 11
    private function runProcess($debug)
146
    {
147 11
        if ($this->maxProcessNumber > count($this->processRunning) && count($this->processStack) > 0) {
148
            /** @var ParaunitProcessInterface $process */
149 11
            $process = array_pop($this->processStack);
150 11
            $process->start();
151 11
            $this->processRunning[$process->getUniqueId()] = $process;
152
153 11
            if ($debug) {
154
                DebugPrinter::printDebugOutput($process, $this->processRunning);
155
            }
156
157 11
            return $process;
158
        }
159
160 11
        return null;
161
    }
162
163
    /**
164
     * @param AbstractParaunitProcess $process
165
     * @throws \RuntimeException
166
     */
167 11
    private function markProcessCompleted(AbstractParaunitProcess $process)
168
    {
169 11
        $pHash = $process->getUniqueId();
170
171 11
        if (array_key_exists($pHash, $this->processRunning)) {
172 11
            unset($this->processRunning[$pHash]);
173
        } else {
174
            throw new \RuntimeException('Trying to remove a non-existing process from running stack\! ID: ' . $pHash);
175
        }
176
177 11
        if ($process->isToBeRetried()) {
178 3
            $process->reset();
179 3
            $process->increaseRetryCount();
180 3
            $this->processStack[$pHash] = $process;
181
        } else {
182 11
            $this->processCompleted[$pHash] = $process;
183
        }
184 11
    }
185
}
186