Passed
Pull Request — master (#18)
by Harry
04:35 queued 23s
created

PriorityPoolTest   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 267
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 18
eloc 142
dl 0
loc 267
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A testPriorityWillRunTheHighestFirst() 0 47 1
A testPriorityPoolRunsRunWhenInstantRunIsOn() 0 27 1
A setUp() 0 6 1
A testAddingNonRunInterfaceWillThrowException() 0 5 1
A testPriorityPoolInitialStateWithNoRuns() 0 7 1
A testPriorityPoolAddingProcessFiresAnEvent() 0 22 2
A testPriorityPoolInitialStateWithProcess() 0 10 1
A testPriorityPoolConstructor() 0 11 2
A testPriorityPoolInitialStateWithInstantRun() 0 21 1
A testPriorityPoolAddingRun() 0 9 1
A testPriorityPoolInstantRunStates() 0 7 1
A testPriorityPoolAddingProcess() 0 10 1
A testPriorityPoolIsARunInterface() 0 4 1
A testPriorityPoolIsACollectionOfRuns() 0 11 1
A testPriorityPoolUnableToAddRunningProcessWhenPoolHasNotStarted() 0 9 1
A testPriorityPoolAddingRunFiresAnEvent() 0 20 1
1
<?php
2
3
/**
4
 * This file is part of graze/parallel-process.
5
 *
6
 * Copyright © 2018 Nature Delivered Ltd. <https://www.graze.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @license https://github.com/graze/parallel-process/blob/master/LICENSE.md
12
 * @link    https://github.com/graze/parallel-process
13
 */
14
15
namespace Graze\ParallelProcess\Test\Unit;
16
17
use Graze\DataStructure\Collection\CollectionInterface;
18
use Graze\ParallelProcess\CallbackRun;
19
use Graze\ParallelProcess\Event\PoolRunEvent;
20
use Graze\ParallelProcess\PriorityPool;
21
use Graze\ParallelProcess\ProcessRun;
22
use Graze\ParallelProcess\RunInterface;
23
use Graze\ParallelProcess\Test\TestCase;
24
use Mockery;
25
use Symfony\Component\Process\Process;
26
27
class PriorityPoolTest extends TestCase
28
{
29
    /** @var mixed */
30
    private $process;
31
32
    public function setUp()
33
    {
34
        parent::setUp();
35
36
        $this->process = Mockery::mock(Process::class)
37
                                ->allows(['stop' => null, 'isStarted' => false, 'isRunning' => false]);
38
    }
39
40
    public function testPriorityPoolIsARunInterface()
41
    {
42
        $priorityPool = new PriorityPool();
43
        $this->assertInstanceOf(RunInterface::class, $priorityPool);
44
    }
45
46
    public function testPriorityPoolIsACollectionOfRuns()
47
    {
48
        $priorityPool = new PriorityPool();
49
        $this->assertInstanceOf(CollectionInterface::class, $priorityPool);
50
51
        $this->assertSame($priorityPool, $priorityPool->add($this->process));
52
53
        $runs = $priorityPool->getAll();
54
        $this->assertCount(1, $runs);
55
56
        $this->assertInstanceOf(ProcessRun::class, reset($runs));
57
    }
58
59
    public function testPriorityPoolInitialStateWithProcess()
60
    {
61
        $priorityPool = new PriorityPool();
62
        $priorityPool->add($this->process);
63
64
        $this->assertFalse($priorityPool->isSuccessful());
65
        $this->assertFalse($priorityPool->isRunning());
66
        $this->assertFalse($priorityPool->hasStarted());
67
        $this->assertFalse($priorityPool->isRunInstantly());
68
        $this->assertEquals(PriorityPool::NO_MAX, $priorityPool->getMaxSimultaneous());
69
    }
70
71
    public function testPriorityPoolConstructor()
72
    {
73
        $runs = [];
74
        for ($i = 0; $i < 2; $i++) {
75
            $runs[] = Mockery::mock(RunInterface::class)
76
                             ->allows(['isRunning' => false, 'hasStarted' => false, 'addListener' => true, 'getPriority' => 1.0]);
77
        }
78
79
        $priorityPool = new PriorityPool($runs);
80
81
        $this->assertEquals(2, $priorityPool->count());
82
    }
83
84
    /**
85
     * @expectedException \InvalidArgumentException
86
     */
87
    public function testAddingNonRunInterfaceWillThrowException()
88
    {
89
        $nope = Mockery::mock();
90
        $priorityPool = new PriorityPool();
91
        $priorityPool->add($nope);
0 ignored issues
show
Bug introduced by
$nope of type Mockery\MockInterface is incompatible with the type Graze\ParallelProcess\Po...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\PriorityPool::add(). ( Ignorable by Annotation )

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

91
        $priorityPool->add(/** @scrutinizer ignore-type */ $nope);
Loading history...
92
    }
93
94
    public function testPriorityPoolInitialStateWithNoRuns()
95
    {
96
        $priorityPool = new PriorityPool();
97
98
        $this->assertFalse($priorityPool->isSuccessful(), 'should not be successful');
99
        $this->assertFalse($priorityPool->isRunning(), 'should not be running');
100
        $this->assertFalse($priorityPool->hasStarted(), 'should not be started');
101
    }
102
103
    public function testPriorityPoolAddingRun()
104
    {
105
        $run = Mockery::mock(RunInterface::class);
106
        $run->allows(['hasStarted' => false, 'isRunning' => false, 'addListener' => $run, 'getPriority' => 1.0]);
107
108
        $priorityPool = new PriorityPool();
109
        $priorityPool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Graze\ParallelProcess\Po...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\PriorityPool::add(). ( Ignorable by Annotation )

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

109
        $priorityPool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
110
111
        $this->assertEquals(1, $priorityPool->count());
112
    }
113
114
    public function testPriorityPoolAddingRunFiresAnEvent()
115
    {
116
        $run = Mockery::mock(RunInterface::class);
117
        $run->allows(['hasStarted' => false, 'isRunning' => false, 'addListener' => $run, 'getPriority' => 1.0]);
118
119
        $hit = false;
120
121
        $priorityPool = new PriorityPool();
122
        $priorityPool->addListener(
123
            PoolRunEvent::POOL_RUN_ADDED,
124
            function (PoolRunEvent $event) use ($priorityPool, $run, &$hit) {
125
                $this->assertSame($priorityPool, $event->getPool());
126
                $this->assertSame($run, $event->getRun());
127
                $hit = true;
128
            }
129
        );
130
        $priorityPool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Graze\ParallelProcess\Po...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\PriorityPool::add(). ( Ignorable by Annotation )

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

130
        $priorityPool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
131
132
        $this->assertEquals(1, $priorityPool->count());
133
        $this->assertTrue($hit);
134
    }
135
136
    public function testPriorityPoolAddingProcess()
137
    {
138
        $priorityPool = new PriorityPool();
139
        $priorityPool->add($this->process);
140
141
        $this->assertEquals(1, $priorityPool->count());
142
        $runs = $priorityPool->getAll();
143
        $run = reset($runs);
144
145
        $this->assertEquals($this->process, $run->getProcess());
146
    }
147
148
    public function testPriorityPoolAddingProcessFiresAnEvent()
149
    {
150
        $priorityPool = new PriorityPool();
151
        $priorityPool->addListener(
152
            PoolRunEvent::POOL_RUN_ADDED,
153
            function (PoolRunEvent $event) use ($priorityPool, &$hit) {
154
                $this->assertSame($priorityPool, $event->getPool());
155
                $run = $event->getRun();
156
                if ($run instanceof ProcessRun) {
157
                    $this->assertSame($this->process, $run->getProcess());
158
                }
159
                $hit = true;
160
            }
161
        );
162
        $priorityPool->add($this->process);
163
164
        $this->assertEquals(1, $priorityPool->count());
165
        $runs = $priorityPool->getAll();
166
        $run = reset($runs);
167
168
        $this->assertEquals($this->process, $run->getProcess());
169
        $this->assertTrue($hit);
170
    }
171
172
    /**
173
     * @expectedException \Graze\ParallelProcess\Exceptions\NotRunningException
174
     */
175
    public function testPriorityPoolUnableToAddRunningProcessWhenPoolHasNotStarted()
176
    {
177
        $run = Mockery::mock(RunInterface::class);
178
        $run->shouldReceive('isRunning')
179
            ->andReturn(true);
180
181
        $priorityPool = new PriorityPool();
182
183
        $priorityPool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Graze\ParallelProcess\Po...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\PriorityPool::add(). ( Ignorable by Annotation )

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

183
        $priorityPool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
184
    }
185
186
    public function testPriorityPoolInstantRunStates()
187
    {
188
        $priorityPool = new PriorityPool();
189
190
        $this->assertFalse($priorityPool->isRunInstantly());
191
        $this->assertSame($priorityPool, $priorityPool->setRunInstantly(true));
192
        $this->assertTrue($priorityPool->isRunInstantly());
193
    }
194
195
    public function testPriorityPoolInitialStateWithInstantRun()
196
    {
197
        $process = Mockery::mock(Process::class);
198
        $process->shouldReceive('stop');
199
        $process->shouldReceive('isStarted')->andReturn(false, false, false, true);
200
        $process->shouldReceive('isRunning')->andReturn(false, false, true, false);
201
        $process->shouldReceive('start')->atLeast()->once();
202
        $process->shouldReceive('isSuccessful')->once()->andReturn(true);
203
204
        $priorityPool = new PriorityPool([$process], PriorityPool::NO_MAX, true);
205
206
        $this->assertTrue($priorityPool->hasStarted());
207
        $this->assertTrue($priorityPool->isRunning());
208
        $this->assertFalse($priorityPool->isSuccessful());
209
        $this->assertTrue($priorityPool->isRunInstantly());
210
211
        $priorityPool->poll();
212
213
        $this->assertTrue($priorityPool->hasStarted());
214
        $this->assertFalse($priorityPool->isRunning());
215
        $this->assertTrue($priorityPool->isSuccessful());
216
    }
217
218
    public function testPriorityPoolRunsRunWhenInstantRunIsOn()
219
    {
220
        $priorityPool = new PriorityPool();
221
        $priorityPool->setRunInstantly(true);
222
223
        $process = Mockery::mock(Process::class);
224
        $process->shouldReceive('stop');
225
        $process->shouldReceive('isStarted')->andReturn(false, false, false, true);
226
        $process->shouldReceive('isRunning')->andReturn(false, false, true, false);
227
        $process->shouldReceive('start')->atLeast()->once();
228
        $process->shouldReceive('isSuccessful')->once()->andReturn(true);
229
230
        $this->assertFalse($priorityPool->hasStarted());
231
        $this->assertFalse($priorityPool->isRunning());
232
        $this->assertFalse($priorityPool->isSuccessful());
233
234
        $priorityPool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Graze\ParallelProcess\Po...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\PriorityPool::add(). ( Ignorable by Annotation )

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

234
        $priorityPool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
235
236
        $this->assertTrue($priorityPool->hasStarted());
237
        $this->assertTrue($priorityPool->isRunning());
238
        $this->assertFalse($priorityPool->isSuccessful());
239
240
        $priorityPool->poll();
241
242
        $this->assertTrue($priorityPool->hasStarted());
243
        $this->assertFalse($priorityPool->isRunning());
244
        $this->assertTrue($priorityPool->isSuccessful());
245
    }
246
247
    public function testPriorityWillRunTheHighestFirst()
248
    {
249
        $priorityPool = new PriorityPool();
250
        $priorityPool->setRunInstantly(false)
251
                     ->setMaxSimultaneous(1);
252
253
        $run1 = new CallbackRun(
254
            function () {
255
                return true;
256
            },
257
            [],
258
            1.5
259
        );
260
261
        $priorityPool->add($run1);
262
263
        $run2 = new CallbackRun(
264
            function () {
265
                return true;
266
            },
267
            [],
268
            1.6
269
        );
270
271
        $priorityPool->add($run2);
272
273
        $this->assertFalse($priorityPool->hasStarted());
274
        $this->assertFalse($priorityPool->isRunning());
275
        $this->assertFalse($priorityPool->isSuccessful());
276
277
        $priorityPool->poll();
278
279
        $this->assertTrue($priorityPool->hasStarted());
280
        $this->assertTrue($priorityPool->isRunning());
281
        $this->assertFalse($priorityPool->isSuccessful());
282
283
        $waiting = $priorityPool->getWaiting();
284
285
        $this->assertSame($run1, reset($waiting));
286
287
        $priorityPool->poll();
288
289
        $this->assertTrue($priorityPool->hasStarted());
290
        $this->assertFalse($priorityPool->isRunning());
291
        $this->assertTrue($priorityPool->isSuccessful());
292
293
        $priorityPool->poll();
294
    }
295
}
296