Completed
Pull Request — master (#109)
by Robbie
01:54
created

QueuedJobsTest   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 370
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 7
dl 0
loc 370
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 8 1
A tearDown() 0 5 1
A getService() 0 4 1
B testQueueJob() 0 24 3
A testJobRunAs() 0 20 2
A testQueueSignature() 0 20 1
A testProcessJob() 0 16 1
A testResumeJob() 0 22 1
A testInitialiseJob() 0 17 1
A testStartJob() 0 21 1
B testImmediateQueuedJob() 0 24 1
B testNextJob() 0 41 2
B testJobHealthCheck() 0 109 1
1
<?php
2
3
use SilverStripe\Dev\SapphireTest;
4
use SilverStripe\Core\Config\Config;
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\QueuedJobs\DataObjects\QueuedJobDescriptor;
7
use SilverStripe\QueuedJobs\Services\AbstractQueuedJob;
8
use SilverStripe\QueuedJobs\Services\QueuedJob;
9
use SilverStripe\QueuedJobs\Services\QueuedJobService;
10
11
/**
12
 *
13
 *
14
 * @author Marcus Nyeholt <[email protected]>
15
 */
16
class QueuedJobsTest extends SapphireTest
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * We need the DB for this test
20
     *
21
     * @var bool
22
     */
23
    protected $usesDatabase = true;
24
25
    /**
26
     * {@inheritDoc}
27
     */
28
    public function setUp()
29
    {
30
        parent::setUp();
31
32
        Config::nest();
33
        // Two restarts are allowed per job
34
        Config::inst()->update('SilverStripe\\QueuedJobs\\Services\\QueuedJobService', 'stall_threshold', 2);
35
    }
36
37
    /**
38
     * {@inheritDoc}
39
     */
40
    public function tearDown()
41
    {
42
        Config::unnest();
43
        parent::tearDown();
44
    }
45
46
    /**
47
     * @return QueuedJobService
48
     */
49
    protected function getService()
50
    {
51
        return singleton('TestQJService');
52
    }
53
54
    public function testQueueJob()
55
    {
56
        $svc = $this->getService();
57
58
        // lets create a new job and add it tio the queue
59
        $job = new TestQueuedJob();
60
        $jobId = $svc->queueJob($job);
61
        $list = $svc->getJobList();
62
63
        $this->assertEquals(1, $list->count());
64
65
        $myJob = null;
66
        foreach ($list as $job) {
67
            if ($job->Implementation == 'TestQueuedJob') {
68
                $myJob = $job;
69
                break;
70
            }
71
        }
72
73
        $this->assertNotNull($myJob);
74
        $this->assertTrue($jobId > 0);
75
        $this->assertEquals('TestQueuedJob', $myJob->Implementation);
76
        $this->assertNotNull($myJob->SavedJobData);
77
    }
78
79
    public function testJobRunAs()
80
    {
81
        $svc = $this->getService();
82
        $list = $svc->getJobList();
83
        foreach ($list as $job) {
84
            $job->delete();
85
        }
86
87
        $this->logInWithPermission('DUMMY');
88
89
        // lets create a new job and add it tio the queue
90
        $job = new TestQueuedJob();
91
        $job->runningAs = "DUMMY";
0 ignored issues
show
Documentation introduced by
The property runningAs does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
92
        $jobId = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$jobId 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...
93
        $list = $svc->getJobList();
94
95
        $myJob = $list->First();
96
97
        $this->assertEquals("[email protected]", $myJob->RunAs()->Email);
98
    }
99
100
    public function testQueueSignature()
101
    {
102
        $svc = $this->getService();
103
104
        // lets create a new job and add it tio the queue
105
        $job = new TestQueuedJob();
106
        $jobId = $svc->queueJob($job);
107
108
        $newJob = new TestQueuedJob();
109
        $newId = $svc->queueJob($newJob);
110
111
        $this->assertEquals($jobId, $newId);
112
113
        // now try another, but with different params
114
        $newJob = new TestQueuedJob();
115
        $newJob->randomParam = 'stuff';
0 ignored issues
show
Documentation introduced by
The property randomParam does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
116
        $newId = $svc->queueJob($newJob);
117
118
        $this->assertNotEquals($jobId, $newId);
119
    }
120
121
    public function testProcessJob()
122
    {
123
        $job = new TestQueuedJob();
124
        $job->setup();
125
        $job->process();
126
        // we should now have some  data
127
        $data = $job->getJobData();
128
        $this->assertNotNull($data->messages);
129
        $this->assertFalse($data->isComplete);
130
131
        $jd = $data->jobData;
132
        $this->assertTrue(isset($jd->times));
133
        $this->assertEquals(1, count($jd->times));
134
135
        // now take the 'saved' data and try restoring the job
136
    }
137
138
    public function testResumeJob()
139
    {
140
        $job = new TestQueuedJob();
141
        $job->setup();
142
        $job->process();
143
        // we should now have some  data
144
        $data = $job->getJobData();
145
146
        // so create a new job and restore it from this data
147
148
        $job = new TestQueuedJob();
149
        $job->setup();
150
151
        $job->setJobData($data->totalSteps, $data->currentStep, $data->isComplete, $data->jobData, $data->messages);
152
        $job->process();
153
154
        $data = $job->getJobData();
155
        $this->assertFalse($data->isComplete);
156
        $jd = $data->jobData;
157
        $this->assertTrue(isset($jd->times));
158
        $this->assertEquals(2, count($jd->times));
159
    }
160
161
    public function testInitialiseJob()
162
    {
163
        // okay, lets test it out on the actual service
164
        $svc = $this->getService();
165
        // lets create a new job and add it to the queue
166
        $job = new TestQueuedJob();
167
        $id = $svc->queueJob($job);
168
169
        $descriptor = DataObject::get_by_id('SilverStripe\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', $id);
170
171
        $job = $svc->testInit($descriptor);
172
        $this->assertInstanceOf('TestQueuedJob', $job, 'Job has been triggered');
173
174
        $descriptor = DataObject::get_by_id('SilverStripe\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', $id);
175
176
        $this->assertEquals(QueuedJob::STATUS_INIT, $descriptor->JobStatus);
177
    }
178
179
    public function testStartJob()
180
    {
181
        // okay, lets test it out on the actual service
182
        $svc = $this->getService();
183
        // lets create a new job and add it to the queue
184
185
        $this->logInWithPermission('DUMMYUSER');
186
187
        $job = new TestQueuedJob();
188
        $job->testingStartJob = true;
0 ignored issues
show
Documentation introduced by
The property testingStartJob does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
189
        $id = $svc->queueJob($job);
190
191
        $this->logInWithPermission('ADMIN');
192
193
        $result = $svc->runJob($id);
194
        $this->assertTrue($result);
195
196
        // we want to make sure that the current user is the runas user of the job
197
        $descriptor = DataObject::get_by_id('SilverStripe\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', $id);
198
        $this->assertEquals('Complete', $descriptor->JobStatus);
199
    }
200
201
    public function testImmediateQueuedJob()
202
    {
203
        // okay, lets test it out on the actual service
204
        $svc = $this->getService();
205
        // lets create a new job and add it to the queue
206
207
        $job = new TestQueuedJob(QueuedJob::IMMEDIATE);
208
        $job->firstJob = true;
0 ignored issues
show
Documentation introduced by
The property firstJob does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
209
        $id = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$id 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...
210
211
        $job = new TestQueuedJob(QueuedJob::IMMEDIATE);
212
        $job->secondJob = true;
0 ignored issues
show
Documentation introduced by
The property secondJob does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
213
        $id = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$id 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...
214
215
        $jobs = $svc->getJobList(QueuedJob::IMMEDIATE);
216
        $this->assertEquals(2, $jobs->count());
217
218
        // now fake a shutdown
219
        $svc->onShutdown();
220
221
        $jobs = $svc->getJobList(QueuedJob::IMMEDIATE);
222
        $this->assertInstanceOf('SilverStripe\\ORM\\DataList', $jobs);
223
        $this->assertEquals(0, $jobs->count());
224
    }
225
226
    public function testNextJob()
227
    {
228
        $svc = $this->getService();
229
        $list = $svc->getJobList();
230
231
        foreach ($list as $job) {
232
            $job->delete();
233
        }
234
235
        $list = $svc->getJobList();
236
        $this->assertEquals(0, $list->count());
237
238
        $job = new TestQueuedJob();
239
        $id1 = $svc->queueJob($job);
240
241
        $job = new TestQueuedJob();
242
        // to get around the signature checks
243
        $job->randomParam = 'me';
0 ignored issues
show
Documentation introduced by
The property randomParam does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
244
        $id2 = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$id2 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...
245
246
        $job = new TestQueuedJob();
247
        // to get around the signature checks
248
        $job->randomParam = 'mo';
0 ignored issues
show
Documentation introduced by
The property randomParam does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
249
        $id3 = $svc->queueJob($job);
250
251
        $this->assertEquals(2, $id3 - $id1);
252
253
        $list = $svc->getJobList();
254
        $this->assertEquals(3, $list->count());
255
256
        // okay, lets get the first one and initialise it, then make sure that a subsequent init attempt fails
257
        $job = $svc->getNextPendingJob();
258
259
        $this->assertEquals($id1, $job->ID);
260
        $svc->testInit($job);
261
262
        // now try and get another, it should be === false
263
        $next = $svc->getNextPendingJob();
264
265
        $this->assertFalse($next);
266
    }
267
268
    /**
269
     * Verify that broken jobs are correctly verified for health and restarted as necessary
270
     *
271
     * Order of checkJobHealth() and getNextPendingJob() is important
272
     *
273
     * Execution of this job is broken into several "loops", each of which represents one invocation
274
     * of ProcessJobQueueTask
275
     */
276
    public function testJobHealthCheck()
277
    {
278
        // Create a job and add it to the queue
279
        $svc = $this->getService();
280
        $job = new TestQueuedJob(QueuedJob::IMMEDIATE);
281
        $job->firstJob = true;
0 ignored issues
show
Documentation introduced by
The property firstJob does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
282
        $id = $svc->queueJob($job);
283
        $descriptor = QueuedJobDescriptor::get()->byID($id);
284
285
        // Verify initial state is new and LastProcessedCount is not marked yet
286
        $this->assertEquals(QueuedJob::STATUS_NEW, $descriptor->JobStatus);
287
        $this->assertEquals(0, $descriptor->StepsProcessed);
288
        $this->assertEquals(-1, $descriptor->LastProcessedCount);
289
        $this->assertEquals(0, $descriptor->ResumeCounts);
290
291
        // Loop 1 - Pick up new job and attempt to run it
292
        // Job health should not attempt to cleanup unstarted jobs
293
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
294
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
295
296
        // Ensure that this is the next job ready to go
297
        $descriptor = QueuedJobDescriptor::get()->byID($id);
298
        $this->assertEquals($nextJob->ID, $descriptor->ID);
299
        $this->assertEquals(QueuedJob::STATUS_NEW, $descriptor->JobStatus);
300
        $this->assertEquals(0, $descriptor->StepsProcessed);
301
        $this->assertEquals(-1, $descriptor->LastProcessedCount);
302
        $this->assertEquals(0, $descriptor->ResumeCounts);
303
304
        // Run 1 - Start the job (no work is done)
305
        $descriptor->JobStatus = QueuedJob::STATUS_INIT;
306
        $descriptor->write();
307
308
        // Assume that something bad happens at this point, the process dies during execution, and
309
        // the task is re-initiated somewhere down the track
310
311
        // Loop 2 - Detect broken job, and mark it for future checking.
312
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
313
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
314
315
        // Note that we don't immediately try to restart it until StepsProcessed = LastProcessedCount
316
        $descriptor = QueuedJobDescriptor::get()->byID($id);
317
        $this->assertFalse($nextJob); // Don't run it this round please!
318
        $this->assertEquals(QueuedJob::STATUS_INIT, $descriptor->JobStatus);
319
        $this->assertEquals(0, $descriptor->StepsProcessed);
320
        $this->assertEquals(0, $descriptor->LastProcessedCount);
321
        $this->assertEquals(0, $descriptor->ResumeCounts);
322
323
        // Loop 3 - We've previously marked this job as broken, so restart it this round
324
        // If no more work has been done on the job at this point, assume that we are able to
325
        // restart it
326
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
327
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
328
329
        // This job is resumed and exeuction is attempted this round
330
        $descriptor = QueuedJobDescriptor::get()->byID($id);
331
        $this->assertEquals($nextJob->ID, $descriptor->ID);
332
        $this->assertEquals(QueuedJob::STATUS_WAIT, $descriptor->JobStatus);
333
        $this->assertEquals(0, $descriptor->StepsProcessed);
334
        $this->assertEquals(0, $descriptor->LastProcessedCount);
335
        $this->assertEquals(1, $descriptor->ResumeCounts);
336
337
        // Run 2 - First restart (work is done)
338
        $descriptor->JobStatus = QueuedJob::STATUS_RUN;
339
        $descriptor->StepsProcessed++; // Essentially delays the next restart by 1 loop
340
        $descriptor->write();
341
342
        // Once again, at this point, assume the job fails and crashes
343
344
        // Loop 4 - Assuming a job has LastProcessedCount < StepsProcessed we are in the same
345
        // situation as step 2.
346
        // Because the last time the loop ran, StepsProcessed was incremented,
347
        // this indicates that it's likely that another task could be working on this job, so
348
        // don't run this.
349
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
350
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
351
352
        $descriptor = QueuedJobDescriptor::get()->byID($id);
353
        $this->assertFalse($nextJob); // Don't run jobs we aren't sure should be restarted
354
        $this->assertEquals(QueuedJob::STATUS_RUN, $descriptor->JobStatus);
355
        $this->assertEquals(1, $descriptor->StepsProcessed);
356
        $this->assertEquals(1, $descriptor->LastProcessedCount);
357
        $this->assertEquals(1, $descriptor->ResumeCounts);
358
359
        // Loop 5 - Job is again found to not have been restarted since last iteration, so perform second
360
        // restart. The job should be attempted to run this loop
361
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
362
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
363
364
        // This job is resumed and exeuction is attempted this round
365
        $descriptor = QueuedJobDescriptor::get()->byID($id);
366
        $this->assertEquals($nextJob->ID, $descriptor->ID);
367
        $this->assertEquals(QueuedJob::STATUS_WAIT, $descriptor->JobStatus);
368
        $this->assertEquals(1, $descriptor->StepsProcessed);
369
        $this->assertEquals(1, $descriptor->LastProcessedCount);
370
        $this->assertEquals(2, $descriptor->ResumeCounts);
371
372
        // Run 3 - Second and last restart (no work is done)
373
        $descriptor->JobStatus = QueuedJob::STATUS_RUN;
374
        $descriptor->write();
375
376
        // Loop 6 - As no progress has been made since loop 3, we can mark this as dead
377
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
378
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
379
380
        // Since no StepsProcessed has been done, don't wait another loop to mark this as dead
381
        $descriptor = QueuedJobDescriptor::get()->byID($id);
382
        $this->assertEquals(QueuedJob::STATUS_PAUSED, $descriptor->JobStatus);
383
        $this->assertEmpty($nextJob);
384
    }
385
}
386
387
// stub class to be able to call init from an external context
388
class TestQJService extends QueuedJobService
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
389
{
390
    /**
391
     * Not inherited from QueuedJobService unfortunately...
392
     * @var array
393
     */
394
    private static $dependencies = [
0 ignored issues
show
Unused Code introduced by
The property $dependencies is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
395
        'queueHandler' => '%$QueueHandler'
396
    ];
397
398
    public function testInit($descriptor)
399
    {
400
        return $this->initialiseJob($descriptor);
401
    }
402
}
403
404
class TestQueuedJob extends AbstractQueuedJob implements QueuedJob
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
405
{
406
    private $type = QueuedJob::QUEUED;
407
408
    public function __construct($type = null)
409
    {
410
        if ($type) {
411
            $this->type = $type;
412
        }
413
        $this->times = array();
0 ignored issues
show
Documentation introduced by
The property times does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
414
    }
415
416
    public function getJobType()
417
    {
418
        return $this->type;
419
    }
420
421
    public function getTitle()
422
    {
423
        return "A Test job";
424
    }
425
426
    public function setup()
427
    {
428
        $this->totalSteps = 5;
429
    }
430
431 View Code Duplication
    public function process()
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...
432
    {
433
        $times = $this->times;
0 ignored issues
show
Documentation introduced by
The property times does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
434
        // needed due to quirks with __set
435
        $times[] = date('Y-m-d H:i:s');
436
        $this->times = $times;
0 ignored issues
show
Documentation introduced by
The property times does not exist on object<TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
437
438
        $this->addMessage("Updated time to " . date('Y-m-d H:i:s'));
439
        sleep(1);
440
441
        // make sure we're incrementing
442
        $this->currentStep++;
443
444
        // and checking whether we're complete
445
        if ($this->currentStep == 5) {
446
            $this->isComplete = true;
447
        }
448
    }
449
}
450