Completed
Push — master ( 7fae80...8ffdf8 )
by Matthew
07:25
created

BaseJobManagerTest::testPerformance()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
nc 4
cc 3
eloc 12
nop 0
1
<?php
2
3
namespace Dtc\QueueBundle\Tests\Manager;
4
5
use Dtc\QueueBundle\Manager\JobManagerInterface;
6
use Dtc\QueueBundle\Manager\PriorityJobManager;
7
use Dtc\QueueBundle\Manager\RunManager;
8
use Dtc\QueueBundle\Model\BaseJob;
9
use Dtc\QueueBundle\Model\Worker;
10
use Dtc\QueueBundle\ODM\JobTimingManager;
11
use Dtc\QueueBundle\RabbitMQ\JobManager;
12
use PHPUnit\Framework\TestCase;
13
14
abstract class BaseJobManagerTest extends TestCase
15
{
16
    const PERFORMANCE_TOTAL_JOBS = 100;
17
18
    /** @var Worker */
19
    public static $worker;
20
21
    /** @var string */
22
    public static $jobClass;
23
24
    /** @var JobManagerInterface */
25
    public static $jobManager;
26
27
    /** @var RunManager */
28
    public static $runManager;
29
30
    /** @var JobTimingManager */
31
    public static $jobTimingManager;
32
33
    public static function setUpBeforeClass()
34
    {
35
        self::$jobClass = self::$jobManager->getJobClass();
36
        self::$worker->setJobManager(self::$jobManager);
37
    }
38
39
    protected function drain()
40
    {
41
        $limit = 10000;
42
        while ($limit && $job = self::$jobManager->getJob()) {
43
            --$limit;
44
            self::$jobManager->saveHistory($job);
45
        }
46
        self::assertGreaterThan(0, $limit);
47
    }
48
49 View Code Duplication
    public function testSaveJob()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
50
    {
51
        $job = $this->getJob();
52
        $jobInQueue = self::$jobManager->getJob();
53
        self::assertNotNull($jobInQueue, 'There should be a job.');
54
        self::assertEquals(
55
            $job->getId(),
56
            $jobInQueue->getId(),
57
            'Job id returned by manager should be the same'
58
        );
59
        self::$jobManager->deleteJob($job);
60
    }
61
62
    protected function getJob()
63
    {
64
        $job = new self::$jobClass(self::$worker, false, null);
65
        $job->fibonacci(1);
66
        self::assertNotNull($job->getId(), 'Job id should be generated');
67
68
        return $job;
69
    }
70
71
    public function testGetJobByWorker()
72
    {
73
        $job = $this->getJob();
74
        $jobInQueue = self::$jobManager->getJob(self::$worker->getName());
75
        self::assertEquals(
76
            $job->getId(),
77
            $jobInQueue->getId(),
78
            'Job id returned by manager should be the same'
79
        );
80
    }
81
82 View Code Duplication
    public function testDeleteJob()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
    {
84
        $this->drain();
85
86
        $job = $this->getJob();
87
        self::$jobManager->deleteJob($job);
88
89
        $nextJob = self::$jobManager->getJob();
90
        self::assertNull($nextJob, "Shouldn't be any jobs left in queue");
91
92
        return $job;
93
    }
94
95
    /**
96
     * Not all managers will support job priority.
97
     */
98
    public function testJobPriority()
99
    {
100
        $firstJob = new self::$jobClass(self::$worker, false, 12);
101
        $firstJob->fibonacci(1);
102
        self::assertNotNull($firstJob->getId(), 'Job id should be generated');
103
104
        $secondJob = new self::$jobClass(self::$worker, false, 1);
105
        $secondJob->fibonacci(1);
106
        self::assertNotNull($secondJob->getId(), 'Job id should be generated');
107
108
        $thirdJob = new self::$jobClass(self::$worker, false, 5);
109
        $thirdJob->fibonacci(1);
110
        self::assertNotNull($thirdJob->getId(), 'Job id should be generated');
111
112
        if (self::$jobManager instanceof PriorityJobManager && null !== self::$jobManager->getMaxPriority()) {
113
            $jobInQueue = self::$jobManager->getJob();
114
            self::assertNotNull($jobInQueue, 'There should be a job.');
115
            self::assertEquals(
116
                $secondJob->getId(),
117
                $jobInQueue->getId(),
118
                'Second job id should be returned - lower number unload first'
119
            );
120
121
            $jobInQueue = self::$jobManager->getJob();
122
            self::assertEquals(
123
                $thirdJob->getId(),
124
                $jobInQueue->getId(),
125
                'Third job id should be returned - lower number unload first'
126
            );
127
128
            $jobInQueue = self::$jobManager->getJob();
129
            self::assertEquals(
130
                $firstJob->getId(),
131
                $jobInQueue->getId(),
132
                'First job id should be returned - lower number unload first'
133
            );
134
        }
135
    }
136
137
    public function testResetExceptionJobs()
138
    {
139
        $this->expectingException('resetExceptionJobs');
140
    }
141
142
    public function testPruneExceptionJobs()
143
    {
144
        $this->expectingException('pruneExceptionJobs');
145
    }
146
147
    public function testPruneExpiredJobs()
148
    {
149
        $this->expectingException('pruneExpiredJobs');
150
    }
151
152
    public function testGetStatus()
153
    {
154
        $count = self::$jobManager->getWaitingJobCount();
155
        $status = self::$jobManager->getStatus();
156
157
        self::assertEquals($count, $status['all'][BaseJob::STATUS_NEW]);
158
    }
159
160
    public function testPruneArchivedJobs()
161
    {
162
        $failed = false;
163
        try {
164
            $time = time() - 86400;
165
            self::$jobManager->pruneArchivedJobs(new \DateTime("@$time"));
166
            $failed = true;
167
        } catch (\Exception $exception) {
168
            self::assertTrue(true);
169
        }
170
        self::assertFalse($failed);
171
    }
172
173
    /**
174
     * @param string $method
175
     */
176 View Code Duplication
    protected function expectingException($method)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        $failed = false;
179
        try {
180
            self::$jobManager->$method();
181
            $failed = true;
182
        } catch (\Exception $exception) {
183
            self::assertTrue(true);
184
        }
185
        self::assertFalse($failed);
186
    }
187
188
    public function performanceEnqueue()
189
    {
190
        $jobsTotal = static::PERFORMANCE_TOTAL_JOBS;
191
        $start = microtime(true);
192
        for ($i = 0; $i < $jobsTotal; ++$i) {
193
            self::$worker->later()->fibonacci(1);
194
        }
195
196
        $total = microtime(true) - $start;
197
        echo "\nTotal of {$jobsTotal} jobs enqueued in {$total} seconds\n";
198
199
        try {
200
            // Have to add a sleep for RabbitMQ to catch up -
201
            $count = self::getWaitingJobCount($jobsTotal);
202
            self::assertEquals($jobsTotal, $count);
203
        } catch (\Exception $e) {
204
            if ('Unsupported' !== $e->getMessage()) {
205
                throw $e;
206
            }
207
        }
208
    }
209
210
    protected static function fudgeRabbitMQCount($count, $expected)
211
    {
212
        if ($expected !== $count) {
213
            sleep(1);
214
            $count = self::$jobManager->getWaitingJobCount();
215
            if ($expected !== $count && $expected > 10) {
216
                if ($expected >= $count - 10) { // 'fudge factor for RabbitMQ'
217
                    return $expected;
218
                }
219
            }
220
        }
221
222
        return $count;
223
    }
224
225
    protected static function getWaitingJobCount($expected)
226
    {
227
        $count = self::$jobManager->getWaitingJobCount();
228
        if (!self::$jobManager instanceof JobManager) {
229
            return $count;
230
        }
231
232
        return self::fudgeRabbitMQCount($count, $expected);
233
    }
234
235
    /**
236
     * @outputBuffering disabled
237
     */
238
    public function testPerformance()
239
    {
240
        echo "\n".static::class.": Testing Performance\n";
241
        flush();
242
        $reflection = new \ReflectionObject(self::$jobManager);
243
        if ($reflection->hasProperty('enableSorting')) {
244
            $oldEnableSorting = self::$jobManager->enableSorting;
0 ignored issues
show
Bug introduced by
Accessing enableSorting on the interface Dtc\QueueBundle\Manager\JobManagerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
245
            self::$jobManager->enableSorting = false; // Ignore priority
0 ignored issues
show
Bug introduced by
Accessing enableSorting on the interface Dtc\QueueBundle\Manager\JobManagerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
246
        }
247
248
        // Dequeue all outstanding jobs
249
        $this->drain();
250
251
        $this->performanceEnqueue();
252
        $this->performanceDequeue();
253
254
        if ($reflection->hasProperty('enableSorting')) {
255
            self::$jobManager->enableSorting = $oldEnableSorting; // Ignore priority
0 ignored issues
show
Bug introduced by
Accessing enableSorting on the interface Dtc\QueueBundle\Manager\JobManagerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
The variable $oldEnableSorting does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
256
        }
257
    }
258
259
    public function performanceDequeue()
260
    {
261
        $jobsTotal = static::PERFORMANCE_TOTAL_JOBS;
262
        $start = microtime(true);
263
        $job = null;
264
        for ($i = 0; $i < $jobsTotal; ++$i) {
265
            $job = self::$jobManager->getJob();
266
        }
267
        $total = microtime(true) - $start;
268
        echo "Total of {$jobsTotal} jobs dequeued in {$total} seconds\n";
269
        self::assertNotNull($job, 'The last job in queue is null');
270
271
        $nextJob = self::$jobManager->getJob();
272
        self::assertNull($nextJob, "Shouldn't be any jobs left in queue");
273
    }
274
}
275