mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-23 13:38:41 +00:00
471 lines
12 KiB
PHP
471 lines
12 KiB
PHP
|
<?php namespace GO\Job\Tests;
|
||
|
|
||
|
use GO\Job;
|
||
|
use PHPUnit\Framework\TestCase;
|
||
|
|
||
|
class JobTest extends TestCase
|
||
|
{
|
||
|
public function testShouldAlwaysGenerateAnId()
|
||
|
{
|
||
|
$job1 = new Job('ls');
|
||
|
$this->assertTrue(is_string($job1->getId()));
|
||
|
|
||
|
$job2 = new Job(function () {
|
||
|
return true;
|
||
|
});
|
||
|
$this->assertTrue(is_string($job2->getId()));
|
||
|
|
||
|
$job3 = new Job(['MyClass', 'myMethod']);
|
||
|
$this->assertTrue(is_string($job3->getId()));
|
||
|
}
|
||
|
|
||
|
public function testShouldGenerateIdFromSignature()
|
||
|
{
|
||
|
$job1 = new Job('ls');
|
||
|
$this->assertEquals(md5('ls'), $job1->getId());
|
||
|
|
||
|
$job2 = new Job('whoami');
|
||
|
$this->assertNotEquals($job1->getId(), $job2->getId());
|
||
|
|
||
|
$job3 = new Job(['MyClass', 'myMethod']);
|
||
|
$this->assertNotEquals($job1->getId(), $job3->getId());
|
||
|
}
|
||
|
|
||
|
public function testShouldAllowCustomId()
|
||
|
{
|
||
|
$job = new Job('ls', [], 'aCustomId');
|
||
|
|
||
|
$this->assertNotEquals(md5('ls'), $job->getId());
|
||
|
$this->assertEquals('aCustomId', $job->getId());
|
||
|
|
||
|
$job2 = new Job(['MyClass', 'myMethod'], null, 'myCustomId');
|
||
|
$this->assertEquals('myCustomId', $job2->getId());
|
||
|
}
|
||
|
|
||
|
public function testShouldKnowIfDue()
|
||
|
{
|
||
|
$job1 = new Job('ls');
|
||
|
$this->assertTrue($job1->isDue());
|
||
|
|
||
|
$job2 = new Job('ls');
|
||
|
$job2->at('* * * * *');
|
||
|
$this->assertTrue($job2->isDue());
|
||
|
|
||
|
$job3 = new Job('ls');
|
||
|
$job3->at('10 * * * *');
|
||
|
$this->assertTrue($job3->isDue(\DateTime::createFromFormat('i', '10')));
|
||
|
$this->assertFalse($job3->isDue(\DateTime::createFromFormat('i', '12')));
|
||
|
}
|
||
|
|
||
|
public function testShouldKnowIfCanRunInBackground()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
$this->assertTrue($job->canRunInBackground());
|
||
|
|
||
|
$job2 = new Job(function () {
|
||
|
return "I can't run in background";
|
||
|
});
|
||
|
$this->assertFalse($job2->canRunInBackground());
|
||
|
}
|
||
|
|
||
|
public function testShouldForceTheJobToRunInForeground()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$this->assertTrue($job->canRunInBackground());
|
||
|
$this->assertFalse($job->inForeground()->canRunInBackground());
|
||
|
}
|
||
|
|
||
|
public function testShouldReturnCompiledJobCommand()
|
||
|
{
|
||
|
$job1 = new Job('ls');
|
||
|
$this->assertEquals('ls', $job1->inForeground()->compile());
|
||
|
|
||
|
$fn = function () {
|
||
|
return true;
|
||
|
};
|
||
|
$job2 = new Job($fn);
|
||
|
$this->assertEquals($fn, $job2->compile());
|
||
|
}
|
||
|
|
||
|
public function testShouldCompileWithArguments()
|
||
|
{
|
||
|
$job = new Job('ls', [
|
||
|
'-l' => null,
|
||
|
'-arg' => 'value',
|
||
|
]);
|
||
|
|
||
|
$this->assertEquals("ls '-l' '-arg' 'value'", $job->inForeground()->compile());
|
||
|
}
|
||
|
|
||
|
public function testShouldCompileCommandInBackground()
|
||
|
{
|
||
|
$job1 = new Job('ls');
|
||
|
$job1->at('* * * * *');
|
||
|
|
||
|
$this->assertEquals('(ls) > /dev/null 2>&1 &', $job1->compile());
|
||
|
}
|
||
|
|
||
|
public function testShouldRunInBackground()
|
||
|
{
|
||
|
// This script has a 5 seconds sleep
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
$startTime = microtime(true);
|
||
|
$job->at('* * * * *')->run();
|
||
|
$endTime = microtime(true);
|
||
|
|
||
|
$this->assertTrue(5 > ($endTime - $startTime));
|
||
|
|
||
|
$startTime = microtime(true);
|
||
|
$job->at('* * * * *')->inForeground()->run();
|
||
|
$endTime = microtime(true);
|
||
|
|
||
|
$this->assertTrue(($endTime - $startTime) >= 5);
|
||
|
}
|
||
|
|
||
|
public function testShouldRunInForegroundIfSendsEmails()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
$job->email('test@mail.com');
|
||
|
|
||
|
$this->assertFalse($job->canRunInBackground());
|
||
|
}
|
||
|
|
||
|
public function testShouldAcceptSingleOrMultipleEmails()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$this->assertInstanceOf(Job::class, $job->email('test@mail.com'));
|
||
|
$this->assertInstanceOf(Job::class, $job->email(['test@mail.com', 'other@mail.com']));
|
||
|
}
|
||
|
|
||
|
public function testShouldFailIfEmailInputIsNotStringOrArray()
|
||
|
{
|
||
|
$this->expectException(\InvalidArgumentException::class);
|
||
|
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$job->email(1);
|
||
|
}
|
||
|
|
||
|
public function testShouldAcceptEmailConfigurationAndItShouldBeChainable()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
$this->assertInstanceOf(Job::class, $job->configure([
|
||
|
'email' => [],
|
||
|
]));
|
||
|
}
|
||
|
|
||
|
public function testShouldFailIfEmailConfigurationIsNotArray()
|
||
|
{
|
||
|
$this->expectException(\InvalidArgumentException::class);
|
||
|
|
||
|
$job = new Job('ls');
|
||
|
$job->configure([
|
||
|
'email' => 123,
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
public function testShouldCreateLockFileIfOnlyOne()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
// Default temp dir
|
||
|
$tmpDir = sys_get_temp_dir();
|
||
|
$lockFile = $tmpDir . '/' . $job->getId() . '.lock';
|
||
|
|
||
|
@unlink($lockFile);
|
||
|
|
||
|
$this->assertFalse(file_exists($lockFile));
|
||
|
|
||
|
$job->onlyOne()->run();
|
||
|
|
||
|
$this->assertTrue(file_exists($lockFile));
|
||
|
}
|
||
|
|
||
|
public function testShouldCreateLockFilesInCustomPath()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
// Default temp dir
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
$lockFile = $tmpDir . '/' . $job->getId() . '.lock';
|
||
|
|
||
|
@unlink($lockFile);
|
||
|
|
||
|
$this->assertFalse(file_exists($lockFile));
|
||
|
|
||
|
$job->onlyOne($tmpDir)->run();
|
||
|
|
||
|
$this->assertTrue(file_exists($lockFile));
|
||
|
}
|
||
|
|
||
|
public function testShouldRemoveLockFileAfterRunningClosures()
|
||
|
{
|
||
|
$job = new Job(function () {
|
||
|
sleep(3);
|
||
|
});
|
||
|
|
||
|
// Default temp dir
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
$lockFile = $tmpDir . '/' . $job->getId() . '.lock';
|
||
|
|
||
|
$job->onlyOne($tmpDir)->run();
|
||
|
|
||
|
$this->assertFalse(file_exists($lockFile));
|
||
|
}
|
||
|
|
||
|
public function testShouldRemoveLockFileAfterRunningCommands()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
// Default temp dir
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
$lockFile = $tmpDir . '/' . $job->getId() . '.lock';
|
||
|
|
||
|
$job->onlyOne($tmpDir)->run();
|
||
|
|
||
|
sleep(1);
|
||
|
|
||
|
$this->assertTrue(file_exists($lockFile));
|
||
|
|
||
|
sleep(5);
|
||
|
|
||
|
$this->assertFalse(file_exists($lockFile));
|
||
|
}
|
||
|
|
||
|
public function testShouldKnowIfOverlapping()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
$this->assertFalse($job->isOverlapping());
|
||
|
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
|
||
|
$job->onlyOne($tmpDir)->run();
|
||
|
|
||
|
sleep(1);
|
||
|
|
||
|
$this->assertTrue($job->isOverlapping());
|
||
|
|
||
|
sleep(5);
|
||
|
|
||
|
$this->assertFalse($job->isOverlapping());
|
||
|
}
|
||
|
|
||
|
public function testShouldNotRunIfOverlapping()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
$this->assertFalse($job->isOverlapping());
|
||
|
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
|
||
|
$job->onlyOne($tmpDir);
|
||
|
|
||
|
sleep(1);
|
||
|
|
||
|
$this->assertTrue($job->run());
|
||
|
$this->assertFalse($job->run());
|
||
|
|
||
|
sleep(6);
|
||
|
$this->assertTrue($job->run());
|
||
|
}
|
||
|
|
||
|
public function testShouldRunIfOverlappingCallbackReturnsTrue()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
$this->assertFalse($job->isOverlapping());
|
||
|
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
|
||
|
$job->onlyOne($tmpDir, function ($lastExecution) {
|
||
|
return time() - $lastExecution > 2;
|
||
|
})->run();
|
||
|
|
||
|
// The job should not run as it is overlapping
|
||
|
$this->assertFalse($job->run());
|
||
|
sleep(3);
|
||
|
// The job should run now as the function should now return true,
|
||
|
// while it's still being executed
|
||
|
$lockFile = $tmpDir . '/' . $job->getId() . '.lock';
|
||
|
$this->assertTrue(file_exists($lockFile));
|
||
|
$this->assertTrue($job->run());
|
||
|
}
|
||
|
|
||
|
public function testShouldAcceptTempDirInConfiguration()
|
||
|
{
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../async_job.php';
|
||
|
$job = new Job($command);
|
||
|
|
||
|
$tmpDir = __DIR__ . '/../tmp';
|
||
|
|
||
|
$job->configure([
|
||
|
'tempDir' => $tmpDir,
|
||
|
])->onlyOne()->run();
|
||
|
|
||
|
sleep(1);
|
||
|
|
||
|
$this->assertTrue(file_exists($tmpDir . '/' . $job->getId() . '.lock'));
|
||
|
}
|
||
|
|
||
|
public function testWhenMethodShouldBeChainable()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$this->assertInstanceOf(Job::class, $job->when(function () {
|
||
|
return true;
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
public function testShouldNotRunIfTruthTestFails()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$this->assertFalse($job->when(function () {
|
||
|
return false;
|
||
|
})->run());
|
||
|
|
||
|
$this->assertTrue($job->when(function () {
|
||
|
return true;
|
||
|
})->run());
|
||
|
}
|
||
|
|
||
|
public function testShouldReturnOutputOfJobExecution()
|
||
|
{
|
||
|
$job1 = new Job(function () {
|
||
|
echo 'hi';
|
||
|
});
|
||
|
$job1->run();
|
||
|
$this->assertEquals('hi', $job1->getOutput());
|
||
|
|
||
|
$job2 = new Job(function () {
|
||
|
return 'hello';
|
||
|
});
|
||
|
$job2->run();
|
||
|
$this->assertEquals('hello', $job2->getOutput());
|
||
|
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../test_job.php';
|
||
|
$job3 = new Job($command);
|
||
|
$job3->inForeground()->run();
|
||
|
$this->assertEquals(['hi'], $job3->getOutput());
|
||
|
}
|
||
|
|
||
|
public function testShouldRunCallbackBeforeJobExecution()
|
||
|
{
|
||
|
$job = new Job(function () {
|
||
|
return 'Job for testing before function';
|
||
|
});
|
||
|
|
||
|
$callbackWasExecuted = false;
|
||
|
$outputWasSet = false;
|
||
|
|
||
|
$job->before(function () use ($job, &$callbackWasExecuted, &$outputWasSet) {
|
||
|
$callbackWasExecuted = true;
|
||
|
$outputWasSet = ! is_null($job->getOutput());
|
||
|
})->run();
|
||
|
|
||
|
$this->assertTrue($callbackWasExecuted);
|
||
|
$this->assertFalse($outputWasSet);
|
||
|
}
|
||
|
|
||
|
public function testShouldRunCallbackAfterJobExecution()
|
||
|
{
|
||
|
$job = new Job(function () {
|
||
|
$visitors = 1000;
|
||
|
|
||
|
return 'Daily visitors: ' . $visitors;
|
||
|
});
|
||
|
|
||
|
$jobResult = null;
|
||
|
|
||
|
$job->then(function ($output) use (&$jobResult) {
|
||
|
$jobResult = $output;
|
||
|
})->run();
|
||
|
|
||
|
$this->assertEquals($jobResult, $job->getOutput());
|
||
|
|
||
|
$command = PHP_BINARY . ' ' . __DIR__ . '/../test_job.php';
|
||
|
$job2 = new Job($command);
|
||
|
|
||
|
$job2Result = null;
|
||
|
|
||
|
$job2->then(function ($output) use (&$job2Result) {
|
||
|
$job2Result = $output;
|
||
|
}, true)->run();
|
||
|
|
||
|
// Commands in background should return an empty string
|
||
|
$this->assertTrue(empty($job2Result));
|
||
|
|
||
|
$job2Result = null;
|
||
|
$job2->then(function ($output) use (&$job2Result) {
|
||
|
$job2Result = $output;
|
||
|
})->inForeground()->run();
|
||
|
$this->assertTrue(! empty($job2Result) &&
|
||
|
$job2Result === $job2->getOutput());
|
||
|
}
|
||
|
|
||
|
public function testThenMethodShouldPassReturnCode()
|
||
|
{
|
||
|
$command_success = PHP_BINARY . ' ' . __DIR__ . '/../test_job.php';
|
||
|
$command_fail = $command_success . ' fail';
|
||
|
|
||
|
$run = function ($command) {
|
||
|
$job = new Job($command);
|
||
|
$testReturnCode = null;
|
||
|
|
||
|
$job->then(function ($output, $returnCode) use (&$testReturnCode, &$testOutput) {
|
||
|
$testReturnCode = $returnCode;
|
||
|
})->run();
|
||
|
|
||
|
return $testReturnCode;
|
||
|
};
|
||
|
|
||
|
$this->assertEquals(0, $run($command_success));
|
||
|
$this->assertNotEquals(0, $run($command_fail));
|
||
|
}
|
||
|
|
||
|
public function testThenMethodShouldBeChainable()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$this->assertInstanceOf(Job::class, $job->then(function () {
|
||
|
return true;
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
public function testShouldDefaultExecutionInForegroundIfMethodThenIsDefined()
|
||
|
{
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$job->then(function () {
|
||
|
return true;
|
||
|
});
|
||
|
|
||
|
$this->assertFalse($job->canRunInBackground());
|
||
|
}
|
||
|
|
||
|
public function testShouldAllowForcingTheJobToRunInBackgroundIfMethodThenIsDefined()
|
||
|
{
|
||
|
// This is a use case when you want to execute a callback every time your
|
||
|
// job is executed, but you don't care about the output of the job
|
||
|
|
||
|
$job = new Job('ls');
|
||
|
|
||
|
$job->then(function () {
|
||
|
return true;
|
||
|
}, true);
|
||
|
|
||
|
$this->assertTrue($job->canRunInBackground());
|
||
|
}
|
||
|
}
|