Completed
Pull Request — master (#15)
by Harry
05:02
created

testRunningRunAddingToAnInstantRunProcessCarriesOn()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 20
nc 1
nop 0
1
<?php
2
3
/**
4
 * This file is part of graze/parallel-process.
5
 *
6
 * Copyright (c) 2017 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\Pool;
19
use Graze\ParallelProcess\Run;
20
use Graze\ParallelProcess\RunInterface;
21
use Graze\ParallelProcess\Test\TestCase;
22
use Mockery;
23
use Symfony\Component\Process\Process;
24
25
class PoolTest extends TestCase
26
{
27
    /** @var mixed */
28
    private $process;
29
30
    public function setUp()
31
    {
32
        parent::setUp();
33
34
        $this->process = Mockery::mock(Process::class);
35
        $this->process->shouldReceive('stop');
36
        $this->process->shouldReceive('isStarted')->andReturn(false);
37
        $this->process->shouldReceive('isRunning')->andReturn(false);
38
    }
39
40
    public function testPoolIsARunInterface()
41
    {
42
        $pool = new Pool();
43
        $this->assertInstanceOf(RunInterface::class, $pool);
44
    }
45
46
    public function testPoolIsACollectionOfRuns()
47
    {
48
        $pool = new Pool();
49
        $this->assertInstanceOf(CollectionInterface::class, $pool);
50
51
        $this->assertSame($pool, $pool->add($this->process));
52
53
        $runs = $pool->getAll();
54
        $this->assertCount(1, $runs);
55
56
        $this->assertInstanceOf(Run::class, reset($runs));
57
    }
58
59
    public function testPoolInitialStateWithProcess()
60
    {
61
        $pool = new Pool();
62
        $pool->add($this->process);
63
64
        $this->assertFalse($pool->isSuccessful());
65
        $this->assertFalse($pool->isRunning());
66
        $this->assertFalse($pool->hasStarted());
67
    }
68
69
    public function testPoolConstructor()
70
    {
71
        $runs = [];
72
        for ($i = 0; $i < 2; $i++) {
73
            $runs[] = Mockery::mock(RunInterface::class)
74
                             ->shouldReceive('isRunning')
75
                             ->andReturn(false)
76
                             ->getMock()
77
                             ->shouldReceive('hasStarted')
0 ignored issues
show
Bug introduced by
The method shouldReceive() does not exist on Mockery\Expectation. ( Ignorable by Annotation )

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

77
                             ->/** @scrutinizer ignore-call */ shouldReceive('hasStarted')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
78
                             ->andReturn(false)
79
                             ->getMock();
80
        }
81
82
        $pool = new Pool($runs);
83
84
        $this->assertEquals(2, $pool->count());
85
    }
86
87
    /**
88
     * @expectedException \InvalidArgumentException
89
     */
90
    public function testAddingNonRunInterfaceWillThrowException()
91
    {
92
        $nope = Mockery::mock();
93
        $pool = new Pool();
94
        $pool->add($nope);
0 ignored issues
show
Bug introduced by
$nope of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

94
        $pool->add(/** @scrutinizer ignore-type */ $nope);
Loading history...
95
    }
96
97
    public function testPoolInitialStateWithNoRuns()
98
    {
99
        $pool = new Pool();
100
101
        $this->assertFalse($pool->isSuccessful(), 'should not be successful');
102
        $this->assertFalse($pool->isRunning(), 'should not be running');
103
        $this->assertFalse($pool->hasStarted(), 'should not be started');
104
    }
105
106
    public function testPoolAddingRun()
107
    {
108
        $run = Mockery::mock(RunInterface::class);
109
        $run->shouldReceive('hasStarted')
110
            ->andReturn(false);
111
        $run->shouldReceive('isRunning')
112
            ->andReturn(false);
113
        $pool = new Pool();
114
        $pool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

114
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
115
116
        $this->assertEquals(1, $pool->count());
117
    }
118
119
    public function testPoolAddingProcess()
120
    {
121
        $pool = new Pool();
122
        $pool->add($this->process);
123
124
        $this->assertEquals(1, $pool->count());
125
        $runs = $pool->getAll();
126
        $run = reset($runs);
127
128
        $this->assertEquals($this->process, $run->getProcess());
129
    }
130
131
    public function testSuccessfulRun()
132
    {
133
        $run = Mockery::mock(RunInterface::class);
134
        $run->shouldReceive('isRunning')
135
            ->andReturn(false);
136
        $run->shouldReceive('poll')
137
            ->andReturn(true, false);
138
        $run->shouldReceive('hasStarted')
139
            ->andReturn(true);
140
        $run->shouldReceive('isSuccessful')
141
            ->andReturn(true);
142
143
        $pool = new Pool([$run]);
144
145
        $run->shouldReceive('start');
146
        $pool->run(0);
147
148
        $this->assertTrue($pool->hasStarted());
149
        $this->assertFalse($pool->isRunning());
150
        $this->assertTrue($pool->isSuccessful());
151
    }
152
153
    /**
154
     * @expectedException \Graze\ParallelProcess\Exceptions\NotRunningException
155
     */
156
    public function testPoolUnableToAddRunningProcessWhenPoolHasNotStarted()
157
    {
158
        $run = Mockery::mock(RunInterface::class);
159
        $run->shouldReceive('isRunning')
160
            ->andReturn(true);
161
162
        $pool = new Pool();
163
164
        $pool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

164
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
165
    }
166
167
    public function testPoolAbleToAddRunningProcessWhenPoolHasStarted()
168
    {
169
        $run = Mockery::mock(RunInterface::class);
170
        $run->shouldReceive('isRunning')
171
            ->andReturn(false);
172
        $run->shouldReceive('hasStarted')
173
            ->andReturn(false);
174
        $run->shouldReceive('start');
175
        $pool = new Pool([$run]);
176
        $pool->start();
177
178
        $run2 = Mockery::mock(RunInterface::class);
179
        $run2->shouldReceive('isRunning')
180
             ->andReturn(true);
181
        $run2->shouldReceive('start');
182
        $pool->add($run2);
0 ignored issues
show
Bug introduced by
$run2 of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

182
        $pool->add(/** @scrutinizer ignore-type */ $run2);
Loading history...
183
184
        $this->assertEquals(2, $pool->count());
185
        $this->assertTrue($pool->isRunning());
186
    }
187
188
    public function testOnSuccessIsCalledOnSuccessfulProcess()
189
    {
190
        $process = Mockery::mock(Process::class);
191
        $process->shouldReceive('stop');
192
        $process->shouldReceive('isStarted')->andReturn(true);
193
        $process->shouldReceive('isRunning')->andReturn(false);
194
        $process->shouldReceive('start')->atLeast()->once();
195
        $process->shouldReceive('isSuccessful')->once()->andReturn(true);
196
197
        $hit = false;
198
199
        $pool = new Pool(
200
            [],
201
            function ($proc) use ($process, &$hit) {
202
                $this->assertSame($proc, $process);
203
                $hit = true;
204
            }
205
        );
206
207
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

207
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
208
209
        $pool->run(0);
210
211
        $this->assertTrue($hit);
212
    }
213
214
    public function testOnFailureIsCalledForErroredProcess()
215
    {
216
        $process = Mockery::mock(Process::class);
217
        $process->shouldReceive('stop');
218
        $process->shouldReceive('isStarted')->andReturn(true);
219
        $process->shouldReceive('isRunning')->andReturn(false);
220
        $process->shouldReceive('start')->atLeast()->once();
221
        $process->shouldReceive('isSuccessful')->once()->andReturn(false);
222
223
        $hit = false;
224
225
        $pool = new Pool(
226
            [],
227
            null,
228
            function ($proc) use ($process, &$hit) {
229
                $this->assertEquals($proc, $process);
230
                $hit = true;
231
            }
232
        );
233
234
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::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
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
235
236
        $pool->run(0);
237
238
        $this->assertTrue($hit);
239
    }
240
241
    public function testOnProgressIsCalledDuringProcessRun()
242
    {
243
        $process = Mockery::mock(Process::class);
244
        $process->shouldReceive('stop');
245
        $process->shouldReceive('isStarted')->andReturn(true);
246
        $process->shouldReceive('isRunning')->andReturn(false, false, true, false);
247
        $process->shouldReceive('start')->atLeast()->once();
248
        $process->shouldReceive('isSuccessful')->once()->andReturn(false);
249
250
        $hit = false;
251
252
        $pool = new Pool(
253
            [],
254
            null,
255
            null,
256
            function ($proc) use ($process, &$hit) {
257
                $this->assertEquals($proc, $process);
258
                $hit = true;
259
            }
260
        );
261
262
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

262
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
263
264
        $pool->run(0);
265
266
        $this->assertTrue($hit);
267
    }
268
269
    public function testOnSuccessSetterIsCalledOnSuccessfulProcess()
270
    {
271
        $process = Mockery::mock(Process::class);
272
        $process->shouldReceive('stop');
273
        $process->shouldReceive('isStarted')->andReturn(true);
274
        $process->shouldReceive('isRunning')->andReturn(false);
275
        $process->shouldReceive('start')->atLeast()->once();
276
        $process->shouldReceive('isSuccessful')->once()->andReturn(true);
277
278
        $hit = false;
279
280
        $pool = new Pool();
281
        $this->assertSame(
282
            $pool,
283
            $pool->setOnSuccess(function ($proc) use ($process, &$hit) {
284
                $this->assertEquals($proc, $process);
285
                $hit = true;
286
            })
287
        );
288
289
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

289
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
290
291
        $pool->run(0);
292
293
        $this->assertTrue($hit);
294
    }
295
296
    public function testOnFailureSetterIsCalledForErroredProcess()
297
    {
298
        $process = Mockery::mock(Process::class);
299
        $process->shouldReceive('stop');
300
        $process->shouldReceive('isStarted')->andReturn(true);
301
        $process->shouldReceive('isRunning')->andReturn(false);
302
        $process->shouldReceive('poll')->andReturn(false);
303
        $process->shouldReceive('start')->atLeast()->once();
304
        $process->shouldReceive('isSuccessful')->once()->andReturn(false);
305
306
        $hit = false;
307
308
        $pool = new Pool();
309
        $this->assertSame(
310
            $pool,
311
            $pool->setOnFailure(function ($proc) use ($process, &$hit) {
312
                $this->assertEquals($proc, $process);
313
                $hit = true;
314
            })
315
        );
316
317
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

317
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
318
319
        $pool->run(0);
320
321
        $this->assertTrue($hit);
322
    }
323
324
    public function testOnProgressSetterIsCalledDuringProcessRun()
325
    {
326
        $process = Mockery::mock(Process::class);
327
        $process->shouldReceive('stop');
328
        $process->shouldReceive('isStarted')->andReturn(true);
329
        $process->shouldReceive('isRunning')->andReturn(false, false, true, false);
330
        $process->shouldReceive('start')->atLeast()->once();
331
        $process->shouldReceive('isSuccessful')->once()->andReturn(false);
332
333
        $hit = false;
334
335
        $pool = new Pool();
336
        $this->assertSame(
337
            $pool,
338
            $pool->setOnProgress(function ($proc) use ($process, &$hit) {
339
                $this->assertEquals($proc, $process);
340
                $hit = true;
341
            })
342
        );
343
344
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

344
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
345
346
        $pool->run(0);
347
348
        $this->assertTrue($hit);
349
    }
350
351
    public function testOnStartSetterIsCalledDuringProcessRun()
352
    {
353
        $process = Mockery::mock(Process::class);
354
        $process->shouldReceive('stop');
355
        $process->shouldReceive('isStarted')->andReturn(true);
356
        $process->shouldReceive('isRunning')->andReturn(false, false, true, false);
357
        $process->shouldReceive('start')->atLeast()->once();
358
        $process->shouldReceive('isSuccessful')->once()->andReturn(false);
359
360
        $hit = false;
361
362
        $pool = new Pool();
363
        $this->assertSame(
364
            $pool,
365
            $pool->setOnStart(function ($proc) use ($process, &$hit) {
366
                $this->assertEquals($proc, $process);
367
                $hit = true;
368
            })
369
        );
370
371
        $pool->add($process);
0 ignored issues
show
Bug introduced by
$process of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

371
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
372
373
        $pool->run(0);
374
375
        $this->assertTrue($hit);
376
    }
377
378
    public function testPoolInstantRunStates()
379
    {
380
        $pool = new Pool();
381
382
        $this->assertFalse($pool->isRunInstantly());
383
        $this->assertSame($pool, $pool->setRunInstantly(true));
384
        $this->assertTrue($pool->isRunInstantly());
385
    }
386
387
    public function testPoolInitialStateWithInstantRun()
388
    {
389
        $run = Mockery::mock(RunInterface::class);
390
        $run->shouldReceive('start');
391
        $run->shouldReceive('isRunning')
392
            ->andReturn(false);
393
        $run->shouldReceive('poll')
394
            ->andReturn(false);
395
        $run->shouldReceive('hasStarted')
396
            ->andReturn(true);
397
        $run->shouldReceive('isSuccessful')
398
            ->andReturn(true);
399
400
        $pool = new Pool([$run], null, null, null, null, Pool::NO_MAX, true);
401
402
        $this->assertTrue($pool->hasStarted());
403
        $this->assertTrue($pool->isRunning());
404
        $this->assertTrue($pool->isSuccessful());
405
        $this->assertTrue($pool->isRunInstantly());
406
407
        $pool->poll();
408
409
        $this->assertTrue($pool->hasStarted());
410
        $this->assertFalse($pool->isRunning());
411
        $this->assertTrue($pool->isSuccessful());
412
    }
413
414
    public function testPoolRunsRunWhenInstantRunIsOn()
415
    {
416
        $run = Mockery::mock(RunInterface::class);
417
        $pool = new Pool();
418
        $pool->setRunInstantly(true);
419
420
        $run->shouldReceive('start');
421
        $run->shouldReceive('isRunning')
422
            ->andReturn(false);
423
        $run->shouldReceive('poll')
424
            ->andReturn(false);
425
        $run->shouldReceive('hasStarted')
426
            ->andReturn(true);
427
        $run->shouldReceive('isSuccessful')
428
            ->andReturn(true);
429
430
        $pool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

430
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
431
432
        $this->assertTrue($pool->hasStarted());
433
        $this->assertTrue($pool->isRunning());
434
        $this->assertTrue($pool->isSuccessful());
435
436
        $pool->poll();
437
438
        $this->assertTrue($pool->hasStarted());
439
        $this->assertFalse($pool->isRunning());
440
        $this->assertTrue($pool->isSuccessful());
441
    }
442
443
    public function testRunningRunAddingToAnInstantRunProcessCarriesOn()
444
    {
445
        $run = Mockery::mock(RunInterface::class);
446
        $pool = new Pool();
447
        $pool->setRunInstantly(true);
448
449
        $run->shouldReceive('isRunning')
450
            ->andReturn(true);
451
        $run->shouldReceive('poll')
452
            ->andReturn(false);
453
        $run->shouldReceive('hasStarted')
454
            ->andReturn(true);
455
        $run->shouldReceive('isSuccessful')
456
            ->andReturn(true);
457
        $run->shouldReceive('start');
458
459
        $pool->add($run);
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Symfony\Component\Proces...lelProcess\RunInterface expected by parameter $item of Graze\ParallelProcess\Pool::add(). ( Ignorable by Annotation )

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

459
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
460
461
        $this->assertTrue($pool->hasStarted());
462
        $this->assertTrue($pool->isRunning());
463
        $this->assertTrue($pool->isSuccessful());
464
465
        $pool->poll();
466
467
        $this->assertTrue($pool->hasStarted());
468
        $this->assertFalse($pool->isRunning());
469
        $this->assertTrue($pool->isSuccessful());
470
    }
471
}
472