Passed
Pull Request — master (#16)
by Harry
07:40 queued 02:51
created

PoolTest::testFailedRunWithEvents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 57
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 57
rs 9.296
c 0
b 0
f 0
cc 1
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Exception;
18
use Graze\DataStructure\Collection\CollectionInterface;
19
use Graze\ParallelProcess\Event\PoolRunEvent;
20
use Graze\ParallelProcess\Event\RunEvent;
21
use Graze\ParallelProcess\Pool;
22
use Graze\ParallelProcess\Run;
23
use Graze\ParallelProcess\RunInterface;
24
use Graze\ParallelProcess\Test\TestCase;
25
use Mockery;
26
use Symfony\Component\Process\Process;
27
28
class PoolTest extends TestCase
29
{
30
    /** @var mixed */
31
    private $process;
32
33
    public function setUp()
34
    {
35
        parent::setUp();
36
37
        $this->process = Mockery::mock(Process::class)
38
                                ->allows(['stop' => null, 'isStarted' => false, 'isRunning' => false]);
39
    }
40
41
    public function testPoolIsARunInterface()
42
    {
43
        $pool = new Pool();
44
        $this->assertInstanceOf(RunInterface::class, $pool);
45
    }
46
47
    public function testPoolIsACollectionOfRuns()
48
    {
49
        $pool = new Pool();
50
        $this->assertInstanceOf(CollectionInterface::class, $pool);
51
52
        $this->assertSame($pool, $pool->add($this->process));
53
54
        $runs = $pool->getAll();
55
        $this->assertCount(1, $runs);
56
57
        $this->assertInstanceOf(Run::class, reset($runs));
58
    }
59
60
    public function testPoolInitialStateWithProcess()
61
    {
62
        $pool = new Pool();
63
        $pool->add($this->process);
64
65
        $this->assertFalse($pool->isSuccessful());
66
        $this->assertFalse($pool->isRunning());
67
        $this->assertFalse($pool->hasStarted());
68
    }
69
70
    public function testPoolConstructor()
71
    {
72
        $runs = [];
73
        for ($i = 0; $i < 2; $i++) {
74
            $runs[] = Mockery::mock(RunInterface::class)
75
                             ->allows(['isRunning' => false, 'hasStarted' => false, 'addListener' => true]);
76
        }
77
78
        $pool = new Pool($runs);
79
80
        $this->assertEquals(2, $pool->count());
81
    }
82
83
    /**
84
     * @expectedException \InvalidArgumentException
85
     */
86
    public function testAddingNonRunInterfaceWillThrowException()
87
    {
88
        $nope = Mockery::mock();
89
        $pool = new Pool();
90
        $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

90
        $pool->add(/** @scrutinizer ignore-type */ $nope);
Loading history...
91
    }
92
93
    public function testPoolInitialStateWithNoRuns()
94
    {
95
        $pool = new Pool();
96
97
        $this->assertFalse($pool->isSuccessful(), 'should not be successful');
98
        $this->assertFalse($pool->isRunning(), 'should not be running');
99
        $this->assertFalse($pool->hasStarted(), 'should not be started');
100
    }
101
102
    public function testPoolAddingRun()
103
    {
104
        $run = Mockery::mock(RunInterface::class);
105
        $run->allows(['hasStarted' => false, 'isRunning' => false, 'addListener' => $run]);
106
107
        $pool = new Pool();
108
        $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

108
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
109
110
        $this->assertEquals(1, $pool->count());
111
    }
112
113
    public function testPoolAddingRunFiresAnEvent()
114
    {
115
        $run = Mockery::mock(RunInterface::class);
116
        $run->allows(['hasStarted' => false, 'isRunning' => false, 'addListener' => $run]);
117
118
        $hit = false;
119
120
        $pool = new Pool();
121
        $pool->addListener(
122
            PoolRunEvent::POOL_RUN_ADDED,
123
            function (PoolRunEvent $event) use ($pool, $run, &$hit) {
124
                $this->assertSame($pool, $event->getPool());
125
                $this->assertSame($run, $event->getRun());
126
                $hit = true;
127
            }
128
        );
129
        $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

129
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
130
131
        $this->assertEquals(1, $pool->count());
132
        $this->assertTrue($hit);
133
    }
134
135
    public function testPoolAddingProcess()
136
    {
137
        $pool = new Pool();
138
        $pool->add($this->process);
139
140
        $this->assertEquals(1, $pool->count());
141
        $runs = $pool->getAll();
142
        $run = reset($runs);
143
144
        $this->assertEquals($this->process, $run->getProcess());
145
    }
146
147
    public function testPoolAddingProcessFiresAnEvent()
148
    {
149
        $pool = new Pool();
150
        $pool->addListener(
151
            PoolRunEvent::POOL_RUN_ADDED,
152
            function (PoolRunEvent $event) use ($pool, &$hit) {
153
                $this->assertSame($pool, $event->getPool());
154
                $run = $event->getRun();
155
                if ($run instanceof Run) {
156
                    $this->assertSame($this->process, $run->getProcess());
157
                }
158
                $hit = true;
159
            }
160
        );
161
        $pool->add($this->process);
162
163
        $this->assertEquals(1, $pool->count());
164
        $runs = $pool->getAll();
165
        $run = reset($runs);
166
167
        $this->assertEquals($this->process, $run->getProcess());
168
        $this->assertTrue($hit);
169
    }
170
171
    public function testSuccessfulRun()
172
    {
173
        $run = Mockery::mock(RunInterface::class);
174
        $run->shouldReceive('isRunning')
175
            ->andReturn(false);
176
        $run->shouldReceive('poll')
177
            ->andReturn(true, false);
178
        $run->shouldReceive('hasStarted')
179
            ->andReturn(true);
180
        $run->shouldReceive('isSuccessful')
181
            ->andReturn(true);
182
        $run->allows()
183
            ->addListener(RunEvent::FAILED, Mockery::type('callable'));
0 ignored issues
show
Bug introduced by
The method addListener() 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

183
            ->/** @scrutinizer ignore-call */ addListener(RunEvent::FAILED, Mockery::type('callable'));

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...
Bug introduced by
The method addListener() does not exist on Mockery\ExpectationInterface. It seems like you code against a sub-type of Mockery\ExpectationInterface such as Mockery\CompositeExpectation. ( Ignorable by Annotation )

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

183
            ->/** @scrutinizer ignore-call */ addListener(RunEvent::FAILED, Mockery::type('callable'));
Loading history...
184
185
        $pool = new Pool([$run]);
186
187
        $run->shouldReceive('start');
188
        $pool->run(0);
189
190
        $this->assertTrue($pool->hasStarted());
191
        $this->assertFalse($pool->isRunning());
192
        $this->assertTrue($pool->isSuccessful());
193
    }
194
195
    public function testSuccessfulRunWithEvents()
196
    {
197
        $run = Mockery::mock(RunInterface::class);
198
        $run->shouldReceive('isRunning')
199
            ->andReturn(false);
200
        $run->shouldReceive('poll')
201
            ->andReturn(true, false);
202
        $run->shouldReceive('hasStarted')
203
            ->andReturn(true);
204
        $run->shouldReceive('isSuccessful')
205
            ->andReturn(true);
206
        $run->allows()
207
            ->addListener(RunEvent::FAILED, Mockery::type('callable'));
208
209
        $pool = new Pool([$run]);
210
211
        $startedHit = false;
212
        $completedHit = false;
213
214
        $pool->addListener(
215
            RunEvent::STARTED,
216
            function (RunEvent $event) use ($pool, &$startedHit) {
217
                $this->assertSame($pool, $event->getRun());
218
                $startedHit = true;
219
            }
220
        );
221
        $pool->addListener(
222
            RunEvent::COMPLETED,
223
            function (RunEvent $event) use ($pool, &$completedHit) {
224
                $this->assertSame($pool, $event->getRun());
225
                $completedHit = true;
226
            }
227
        );
228
229
        $run->shouldReceive('start');
230
        $pool->run(0);
231
232
        $this->assertTrue($pool->hasStarted());
233
        $this->assertFalse($pool->isRunning());
234
        $this->assertTrue($pool->isSuccessful());
235
236
        $this->assertTrue($startedHit);
237
        $this->assertTrue($completedHit);
238
    }
239
240
    public function testFailedRunWithEvents()
241
    {
242
        $run = Mockery::mock(RunInterface::class);
243
        $run->allows([
244
            'isRunning'    => false,
245
            'hasStarted'   => true,
246
            'isSuccessful' => false,
247
        ]);
248
        $run->shouldReceive('poll')
249
            ->andReturn(true, false);
250
        $failedListener = null;
251
        $run->allows()
252
            ->addListener(
253
                RunEvent::FAILED,
254
                Mockery::on(function (callable $handler) use (&$failedListener) {
255
                    $failedListener = $handler;
256
                    return true;
257
                })
258
            );
259
260
        $pool = new Pool([$run]);
261
262
        $startedHit = false;
263
        $failedHit = false;
264
265
        $pool->addListener(
266
            RunEvent::STARTED,
267
            function (RunEvent $event) use ($pool, &$startedHit) {
268
                $this->assertSame($pool, $event->getRun());
269
                $startedHit = true;
270
            }
271
        );
272
        $pool->addListener(
273
            RunEvent::FAILED,
274
            function (RunEvent $event) use ($pool, &$failedHit) {
275
                $this->assertSame($pool, $event->getRun());
276
                $failedHit = true;
277
            }
278
        );
279
280
        $run->shouldReceive('start');
281
        $pool->run(0);
282
283
        $exception = new Exception('test exception');
284
        $run->allows()
285
            ->getExceptions()->andReturn([$exception]);
0 ignored issues
show
Bug introduced by
The method getExceptions() does not exist on Mockery\Expectation. Did you maybe mean getExceptionMessage()? ( Ignorable by Annotation )

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

285
            ->/** @scrutinizer ignore-call */ getExceptions()->andReturn([$exception]);

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...
Bug introduced by
The method getExceptions() does not exist on Mockery\ExpectationInterface. It seems like you code against a sub-type of Mockery\ExpectationInterface such as Mockery\CompositeExpectation. ( Ignorable by Annotation )

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

285
            ->/** @scrutinizer ignore-call */ getExceptions()->andReturn([$exception]);
Loading history...
286
287
        call_user_func($failedListener, new RunEvent($run));
0 ignored issues
show
Bug introduced by
$run of type Mockery\MockInterface is incompatible with the type Graze\ParallelProcess\RunInterface expected by parameter $run of Graze\ParallelProcess\Ev...RunEvent::__construct(). ( Ignorable by Annotation )

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

287
        call_user_func($failedListener, new RunEvent(/** @scrutinizer ignore-type */ $run));
Loading history...
288
289
        $this->assertTrue($pool->hasStarted());
290
        $this->assertFalse($pool->isRunning());
291
        $this->assertFalse($pool->isSuccessful());
292
293
        $this->assertTrue($startedHit);
294
        $this->assertTrue($failedHit);
295
296
        $this->assertEquals([$exception], $pool->getExceptions());
297
    }
298
299
    /**
300
     * @expectedException \Graze\ParallelProcess\Exceptions\NotRunningException
301
     */
302
    public function testPoolUnableToAddRunningProcessWhenPoolHasNotStarted()
303
    {
304
        $run = Mockery::mock(RunInterface::class);
305
        $run->shouldReceive('isRunning')
306
            ->andReturn(true);
307
308
        $pool = new Pool();
309
310
        $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

310
        $pool->add(/** @scrutinizer ignore-type */ $run);
Loading history...
311
    }
312
313
    public function testPoolAbleToAddRunningProcessWhenPoolHasStarted()
314
    {
315
        $run = Mockery::mock(RunInterface::class)
316
                      ->allows(['isRunning' => false, 'hasStarted' => false, 'start' => null, 'addListener' => true]);
317
        $pool = new Pool([$run]);
318
        $pool->start();
319
320
        $run2 = Mockery::mock(RunInterface::class)
321
                       ->allows(['isRunning' => true, 'start' => null, 'addListener' => true]);
322
        $pool->add($run2);
323
324
        $this->assertEquals(2, $pool->count());
325
        $this->assertTrue($pool->isRunning());
326
    }
327
328
    public function testOnProgressIsCalledDuringProcessRun()
329
    {
330
        $process = Mockery::mock(Process::class);
331
        $process->shouldReceive('stop');
332
        $process->shouldReceive('isStarted')->andReturn(true);
333
        $process->shouldReceive('isRunning')->andReturn(false, false, true, false);
334
        $process->shouldReceive('start')->atLeast()->once();
335
        $process->shouldReceive('isSuccessful')->once()->andReturn(true);
336
337
        $hit = false;
338
339
        $pool = new Pool();
340
        $pool->addListener(
341
            RunEvent::UPDATED,
342
            function (RunEvent $event) use ($pool, &$hit) {
343
                $run = $event->getRun();
344
                $this->assertEquals($pool, $run);
345
                $hit = true;
346
            }
347
        );
348
349
        $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

349
        $pool->add(/** @scrutinizer ignore-type */ $process);
Loading history...
350
        $pool->run(0);
351
        $this->assertTrue($hit);
352
    }
353
354
    public function testPoolInstantRunStates()
355
    {
356
        $pool = new Pool();
357
358
        $this->assertFalse($pool->isRunInstantly());
359
        $this->assertSame($pool, $pool->setRunInstantly(true));
360
        $this->assertTrue($pool->isRunInstantly());
361
    }
362
363
    public function testPoolInitialStateWithInstantRun()
364
    {
365
        $run = Mockery::mock(RunInterface::class)
366
                      ->allows([
367
                          'start'        => null,
368
                          'isRunning'    => false,
369
                          'poll'         => false,
370
                          'hasStarted'   => true,
371
                          'isSuccessful' => true,
372
                          'addListener'  => true,
373
                      ]);
374
375
        $pool = new Pool([$run], Pool::NO_MAX, true);
376
377
        $this->assertTrue($pool->hasStarted());
378
        $this->assertTrue($pool->isRunning());
379
        $this->assertTrue($pool->isSuccessful());
380
        $this->assertTrue($pool->isRunInstantly());
381
382
        $pool->poll();
383
384
        $this->assertTrue($pool->hasStarted());
385
        $this->assertFalse($pool->isRunning());
386
        $this->assertTrue($pool->isSuccessful());
387
    }
388
389
    public function testPoolRunsRunWhenInstantRunIsOn()
390
    {
391
        $pool = new Pool();
392
        $pool->setRunInstantly(true);
393
394
        $run = Mockery::mock(RunInterface::class)
395
                      ->allows([
396
                          'start'        => null,
397
                          'isRunning'    => false,
398
                          'poll'         => false,
399
                          'hasStarted'   => true,
400
                          'isSuccessful' => true,
401
                          'addListener'  => true,
402
                      ]);
403
404
        $pool->add($run);
405
406
        $this->assertTrue($pool->hasStarted());
407
        $this->assertTrue($pool->isRunning());
408
        $this->assertTrue($pool->isSuccessful());
409
410
        $pool->poll();
411
412
        $this->assertTrue($pool->hasStarted());
413
        $this->assertFalse($pool->isRunning());
414
        $this->assertTrue($pool->isSuccessful());
415
    }
416
417
    public function testRunningRunAddingToAnInstantRunProcessCarriesOn()
418
    {
419
        $pool = new Pool();
420
        $pool->setRunInstantly(true);
421
422
        $run = Mockery::mock(RunInterface::class)
423
                      ->allows([
424
                          'start'        => null,
425
                          'isRunning'    => true,
426
                          'poll'         => false,
427
                          'hasStarted'   => true,
428
                          'isSuccessful' => true,
429
                          'addListener'  => true,
430
                      ]);
431
432
        $pool->add($run);
433
434
        $this->assertTrue($pool->hasStarted());
435
        $this->assertTrue($pool->isRunning());
436
        $this->assertTrue($pool->isSuccessful());
437
438
        $pool->poll();
439
440
        $this->assertTrue($pool->hasStarted());
441
        $this->assertFalse($pool->isRunning());
442
        $this->assertTrue($pool->isSuccessful());
443
    }
444
445
    public function testFinished()
446
    {
447
        $run = Mockery::mock(RunInterface::class);
448
        $run->shouldReceive('isRunning')
449
            ->andReturn(false);
450
        $run->shouldReceive('poll')
451
            ->andReturn(true, false);
452
        $run->shouldReceive('hasStarted')
453
            ->andReturn(true);
454
        $run->shouldReceive('isSuccessful')
455
            ->andReturn(true);
456
        $run->allows()
457
            ->addListener(RunEvent::FAILED, Mockery::type('callable'));
458
459
        $pool = new Pool([$run]);
460
461
        $run->shouldReceive('start');
462
        $pool->run(0);
463
464
        $this->assertEquals([$run], $pool->getFinished());
465
    }
466
467
    public function testTags()
468
    {
469
        $pool = new Pool([], Pool::NO_MAX, false, ['tag1', 'key' => 'value']);
470
471
        $this->assertSame(['tag1', 'key' => 'value'], $pool->getTags());
472
    }
473
474
    public function testProgress()
475
    {
476
        $run = Mockery::mock(RunInterface::class);
477
        $run->shouldReceive('isRunning')
478
            ->andReturn(false);
479
        $run->shouldReceive('poll')
480
            ->andReturn(true, false);
481
        $run->shouldReceive('hasStarted')
482
            ->andReturn(true);
483
        $run->shouldReceive('isSuccessful')
484
            ->andReturn(true);
485
        $run->allows()
486
            ->addListener(RunEvent::FAILED, Mockery::type('callable'));
487
488
        $pool = new Pool([$run]);
489
490
        $this->assertEquals([0, 1, 0], $pool->getProgress());
491
492
        $run->shouldReceive('start');
493
        $pool->run(0);
494
495
        $this->assertEquals([1, 1, 1], $pool->getProgress());
496
    }
497
498
    public function testDuration()
499
    {
500
        $run = Mockery::mock(RunInterface::class);
501
        $run->shouldReceive('isRunning')
502
            ->andReturn(false, true, false);
503
        $run->shouldReceive('poll')
504
            ->andReturn(true, false);
505
        $run->shouldReceive('hasStarted')
506
            ->andReturn(false, true);
507
        $run->shouldReceive('isSuccessful')
508
            ->andReturn(true);
509
        $run->allows()
510
            ->addListener(RunEvent::FAILED, Mockery::type('callable'));
511
512
        $pool = new Pool([$run]);
513
514
        $this->assertEquals(0, $pool->getDuration());
515
516
        $run->shouldReceive('start');
517
518
        $pool->start();
519
520
        $this->assertGreaterThan(0, $pool->getDuration());
521
522
        $pool->run(0);
523
524
        $duration = $pool->getDuration();
525
526
        $this->assertEquals($duration, $pool->getDuration());
527
    }
528
}
529