Completed
Pull Request — master (#40)
by Matthew
19:38 queued 15:57
created

BaseJobManagerTest::getWaitingJobCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace Dtc\QueueBundle\Tests\Manager;
4
5
use Dtc\QueueBundle\Manager\JobManagerInterface;
6
use Dtc\QueueBundle\Manager\RunManager;
7
use Dtc\QueueBundle\Model\BaseJob;
8
use Dtc\QueueBundle\Model\Worker;
9
use Dtc\QueueBundle\ODM\JobTimingManager;
10
use Dtc\QueueBundle\RabbitMQ\JobManager;
11
use PHPUnit\Framework\TestCase;
12
13
abstract class BaseJobManagerTest extends TestCase
14
{
15
    const PERFORMANCE_TOTAL_JOBS = 100;
16
17
    /** @var Worker */
18
    public static $worker;
19
20
    /** @var string */
21
    public static $jobClass;
22
23
    /** @var JobManagerInterface */
24
    public static $jobManager;
25
26
    /** @var RunManager */
27
    public static $runManager;
28
29
    /** @var JobTimingManager */
30
    public static $jobTimingManager;
31
32
    public static function setUpBeforeClass()
33
    {
34
        self::$jobClass = self::$jobManager->getJobClass();
35
        self::$worker->setJobManager(self::$jobManager);
36
    }
37
38
    public function testSaveJob()
39
    {
40
        $job = $this->getJob();
41
        $jobInQueue = self::$jobManager->getJob();
42
        self::assertNotNull($jobInQueue, 'There should be a job.');
43
        self::assertEquals(
44
            $job->getId(),
45
            $jobInQueue->getId(),
46
            'Job id returned by manager should be the same'
47
        );
48
        self::$jobManager->deleteJob($job);
49
    }
50
51
    protected function getJob()
52
    {
53
        $job = new self::$jobClass(self::$worker, false, null);
54
        $job->fibonacci(1);
55
        self::assertNotNull($job->getId(), 'Job id should be generated');
56
57
        return $job;
58
    }
59
60
    public function testGetJobByWorker()
61
    {
62
        $job = $this->getJob();
63
        $jobInQueue = self::$jobManager->getJob(self::$worker->getName());
64
        self::assertEquals(
65
            $job->getId(),
66
            $jobInQueue->getId(),
67
            'Job id returned by manager should be the same'
68
        );
69
    }
70
71
    public function testDeleteJob()
72
    {
73
        $job = $this->getJob();
74
        self::$jobManager->deleteJob($job);
75
76
        $nextJob = self::$jobManager->getJob();
77
        self::assertNull($nextJob, "Shouldn't be any jobs left in queue");
78
79
        return $job;
80
    }
81
82
    /**
83
     * Not all managers will support job priority.
84
     */
85
    public function testJobPriority()
86
    {
87
        $firstJob = new self::$jobClass(self::$worker, false, 12);
88
        $firstJob->fibonacci(1);
89
        self::assertNotNull($firstJob->getId(), 'Job id should be generated');
90
91
        $secondJob = new self::$jobClass(self::$worker, false, 1);
92
        $secondJob->fibonacci(1);
93
        self::assertNotNull($secondJob->getId(), 'Job id should be generated');
94
95
        $thirdJob = new self::$jobClass(self::$worker, false, 5);
96
        $thirdJob->fibonacci(1);
97
        self::assertNotNull($thirdJob->getId(), 'Job id should be generated');
98
99
        $jobInQueue = self::$jobManager->getJob();
100
        self::assertNotNull($jobInQueue, 'There should be a job.');
101
        self::assertEquals(
102
            $secondJob->getId(),
103
            $jobInQueue->getId(),
104
            'Second job id should be returned - lower number unload first'
105
        );
106
107
        $jobInQueue = self::$jobManager->getJob();
108
        self::assertEquals(
109
            $thirdJob->getId(),
110
            $jobInQueue->getId(),
111
            'Third job id should be returned - lower number unload first'
112
        );
113
114
        $jobInQueue = self::$jobManager->getJob();
115
        self::assertEquals(
116
            $firstJob->getId(),
117
            $jobInQueue->getId(),
118
            'First job id should be returned - lower number unload first'
119
        );
120
    }
121
122
    public function testResetExceptionJobs()
123
    {
124
        $this->expectingException('resetExceptionJobs');
125
    }
126
127
    public function testPruneExceptionJobs()
128
    {
129
        $this->expectingException('pruneExceptionJobs');
130
    }
131
132
    public function testPruneExpiredJobs()
133
    {
134
        $this->expectingException('pruneExpiredJobs');
135
    }
136
137
    public function testGetStatus()
138
    {
139
        $count = self::$jobManager->getWaitingJobCount();
140
        $status = self::$jobManager->getStatus();
141
142
        self::assertEquals($count, $status['all'][BaseJob::STATUS_NEW]);
143
    }
144
145
    public function testPruneArchivedJobs()
146
    {
147
        $failed = false;
148
        try {
149
            $time = time() - 86400;
150
            self::$jobManager->pruneArchivedJobs(new \DateTime("@$time"));
151
            $failed = true;
152
        } catch (\Exception $exception) {
153
            self::assertTrue(true);
154
        }
155
        self::assertFalse($failed);
156
    }
157
158
    /**
159
     * @param string $method
160
     */
161 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...
162
    {
163
        $failed = false;
164
        try {
165
            self::$jobManager->$method();
166
            $failed = true;
167
        } catch (\Exception $exception) {
168
            self::assertTrue(true);
169
        }
170
        self::assertFalse($failed);
171
    }
172
173
    public function performanceEnqueue()
174
    {
175
        $jobsTotal = static::PERFORMANCE_TOTAL_JOBS;
176
        $start = microtime(true);
177
        for ($i = 0; $i < $jobsTotal; ++$i) {
178
            self::$worker->later()->fibonacci(1);
179
        }
180
181
        $total = microtime(true) - $start;
182
        echo "\nTotal of {$jobsTotal} jobs enqueued in {$total} seconds\n";
183
184
        try {
185
            // Have to add a sleep for RabbitMQ to catch up -
186
            $count = self::getWaitingJobCount($jobsTotal);
187
            self::assertEquals($jobsTotal, $count);
188
        } catch (\Exception $e) {
189
            if ('Unsupported' !== $e->getMessage()) {
190
                throw $e;
191
            }
192
        }
193
    }
194
195
    protected static function fudgeRabbitMQCount($count, $expected)
196
    {
197
        if ($expected !== $count) {
198
            sleep(1);
199
            $count = self::$jobManager->getWaitingJobCount();
200
            if ($expected !== $count && $expected > 10) {
201
                if ($expected >= $count - 10) { // 'fudge factor for RabbitMQ'
202
                    return $expected;
203
                }
204
            }
205
        }
206
207
        return $count;
208
    }
209
210
    protected static function getWaitingJobCount($expected)
211
    {
212
        $count = self::$jobManager->getWaitingJobCount();
213
        if (!self::$jobManager instanceof JobManager) {
214
            return $count;
215
        }
216
217
        return self::fudgeRabbitMQCount($count, $expected);
218
    }
219
220
    /**
221
     * @outputBuffering disabled
222
     */
223
    public function testPerformance()
224
    {
225
        echo "\n".static::class.": Testing Performance\n";
226
        flush();
227
        $reflection = new \ReflectionObject(self::$jobManager);
228
        if ($reflection->hasProperty('enableSorting')) {
229
            $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...
230
            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...
231
        }
232
233
        // Dequeue all outstanding jobs
234
        $limit = 10000;
235
        while ($job = self::$jobManager->getJob() && $limit) {
0 ignored issues
show
Unused Code introduced by
$job is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $job = (self::$jobManager->getJob() && $limit), Probably Intended Meaning: ($job = self::$jobManager->getJob()) && $limit
Loading history...
236
            $limit -= 1;
237
        }
238
        self::assertGreaterThan(0, $limit);
239
240
        $this->performanceEnqueue();
241
        $this->performanceDequeue();
242
243
        if ($reflection->hasProperty('enableSorting')) {
244
            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...
245
        }
246
    }
247
248
    public function performanceDequeue()
249
    {
250
        $jobsTotal = static::PERFORMANCE_TOTAL_JOBS;
251
        $start = microtime(true);
252
        $job = null;
253
        for ($i = 0; $i < $jobsTotal; ++$i) {
254
            $job = self::$jobManager->getJob();
255
        }
256
        $total = microtime(true) - $start;
257
        echo "Total of {$jobsTotal} jobs dequeued in {$total} seconds\n";
258
        self::assertNotNull($job, 'The last job in queue is null');
259
260
        $nextJob = self::$jobManager->getJob();
261
        self::assertNull($nextJob, "Shouldn't be any jobs left in queue");
262
    }
263
}
264