diff --git a/README.md b/README.md index 433d696..83a8059 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ # Jobby, a PHP cron job manager # -[![Total Downloads](https://img.shields.io/packagist/dt/hellogerard/jobby.svg)](https://packagist.org/packages/hellogerard/jobby) -[![Latest Version](https://img.shields.io/packagist/v/hellogerard/jobby.svg)](https://packagist.org/packages/hellogerard/jobby) -[![Build Status](https://img.shields.io/travis/jobbyphp/jobby.svg)](https://travis-ci.org/jobbyphp/jobby) -[![MIT License](https://img.shields.io/packagist/l/hellogerard/jobby.svg)](https://github.com/jobbyphp/jobby/blob/master/LICENSE) +[![Total Downloads](https://img.shields.io/packagist/dt/hatimox/jobby.svg)](https://packagist.org/packages/hatimox/jobby) +[![Latest Version](https://img.shields.io/packagist/v/hatimox/jobby.svg)](https://packagist.org/packages/hatimox/jobby) +[![Build Status](https://img.shields.io/travis/hatimox/jobby.svg)](https://travis-ci.org/hatimox/jobby) +[![MIT License](https://img.shields.io/packagist/l/hatimox/jobby.svg)](https://github.com/hatimox/jobby/blob/master/LICENSE) Install the master jobby cron job, and it will manage all your offline tasks. Add jobs without modifying crontab. Jobby can handle logging, locking, error emails and more. -**NEW REPO:** We have moved `jobby` to a Github org. Please update your remotes to `https://github.com/jobbyphp/jobby.git`. - ## Features ## - Maintain one master crontab job. @@ -19,6 +17,13 @@ Jobby can handle logging, locking, error emails and more. - Run job as another user, if crontab user has `sudo` privileges. - Run only on certain hostnames (handy in webfarms). - Theoretical Windows support (but not ever tested) +- Send alerts to Slack or Mattermost whenever a job exits with an error status. + +## Requirements ## + +- PHP 8.0 or higher +- [Composer](http://getcomposer.org) +- `posix` extension (for Unix/Linux systems) ## Getting Started ## @@ -26,7 +31,7 @@ Jobby can handle logging, locking, error emails and more. The recommended way to install Jobby is through [Composer](http://getcomposer.org): ``` -$ composer require hellogerard/jobby +$ composer require hatimox/jobby ``` Then add the following line to your (or whomever's) crontab: @@ -36,7 +41,7 @@ Then add the following line to your (or whomever's) crontab: After Jobby installs, you can copy an example file to the project root. ``` -$ cp vendor/hellogerard/jobby/resources/jobby.php . +$ cp vendor/hatimox/jobby/resources/jobby.php . ``` ### Running a job ### @@ -208,6 +213,7 @@ dateFormat | string | Y-m-d H:i:s | Format for da _**Mailing**_ | | | _**Options for emailing errors**_ recipients | string | null | Comma-separated string of email addresses mailer | string | sendmail | Email method: _sendmail_ or _smtp_ or _mail_ +mailSubject | string | null | Email subject smtpHost | string | null | SMTP host, if `mailer` is smtp smtpPort | integer | 25 | SMTP port, if `mailer` is smtp smtpUsername | string | null | SMTP user, if `mailer` is smtp @@ -215,11 +221,49 @@ smtpPassword | string | null | SMTP password smtpSecurity | string | null | SMTP security option: _ssl_ or _tls_, if `mailer` is smtp smtpSender | string | jobby@<hostname> | The sender and from addresses used in SMTP notices smtpSenderName | string | Jobby | The name used in the from field for SMTP messages +_**Notifications**_ | | | _**Options for sending Alerts when errors**_ +mattermostUrl | string | null | The webhook url from Mattermost +slackChannel | string | null | The name of Slack Channel (#channel) +slackUrl | string | null | The webhook url from Slack +slackSender | string | null | The name used in the from field for Slack ## Symfony integration ## Symfony bundle for Jobby - [imper86/jobby-cron-bundle](https://github.com/imper86/jobby-cron-bundle) +## Changelog ## + +### v3.0.0 (PHP 8+ Upgrade) ### + +**Breaking Changes:** +- Minimum PHP version is now 8.0 +- Replaced SwiftMailer with Symfony Mailer +- PHPUnit upgraded to ^9.0|^10.0|^11.0 + +**New Features & Improvements:** +- Full PHP 8.0+ compatibility with typed properties and return types +- Uses Symfony Mailer DSN format for mail configuration +- Modern PHP syntax: union types, match expressions, named arguments support +- Strict type comparisons throughout the codebase + +**Migration Guide:** + +If you were injecting a custom mailer, update your code: + +```php +// Before (SwiftMailer) +$mailer = new \Swift_Mailer($transport); +$helper = new \Jobby\Helper($mailer); + +// After (Symfony Mailer) +use Symfony\Component\Mailer\Mailer; +use Symfony\Component\Mailer\Transport; + +$transport = Transport::fromDsn('smtp://localhost:25'); +$mailer = new Mailer($transport); +$helper = new \Jobby\Helper($mailer); +``` + ## Credits ## Developed before, but since inspired by [whenever](). diff --git a/composer.json b/composer.json index 326e27b..64960ad 100644 --- a/composer.json +++ b/composer.json @@ -1,30 +1,28 @@ { - "name": "hellogerard/jobby", - "homepage": "https://github.com/jobbyphp/jobby", + "name": "hatimox/jobby", + "homepage": "https://github.com/hatimox/jobby", "license": "MIT", "description": "Manage all your cron jobs without modifying crontab. Handles locking, logging, error emails, and more.", "authors": [ { - "name": "Gerard Sychay", - "email": "hellogerard@gmail.com", - "homepage": "https://github.com/hellogerard" - }, - { - "name": "Michael Contento", - "homepage": "https://github.com/michaelcontento" + "name": "Hatim HAFFANE", + "email": "hatim.haffane@gmail.com", + "homepage": "https://github.com/hatimox" } ], "require": { - "php": ">=5.6", + "php": ">=8.0", + "alek13/slack": "^2.2", "dragonmantank/cron-expression": "^3.0", + "guzzlehttp/guzzle": "^7.0", "opis/closure": "^3.5", - "swiftmailer/swiftmailer": "^5.4|^6.0", - "symfony/process": "^2.7|^3.0|^4.0|^5.0" + "symfony/mailer": "^5.4|^6.0|^7.0", + "symfony/process": "^5.0|^6.0|^7.0" }, "require-dev": { "mp091689/dump-die": "^1.0", - "phpunit/phpunit": "^4.6", - "symfony/filesystem": "^2.7|^3.0|^4.0|^5.0" + "phpunit/phpunit": "^9.0|^10.0|^11.0", + "symfony/filesystem": "^5.0|^6.0|^7.0" }, "autoload": { "psr-4": { diff --git a/src/BackgroundJob.php b/src/BackgroundJob.php index c339747..41b6605 100644 --- a/src/BackgroundJob.php +++ b/src/BackgroundJob.php @@ -2,36 +2,29 @@ namespace Jobby; -use Opis\Closure\SerializableClosure; - class BackgroundJob { /** * @var Helper */ - protected $helper; + protected Helper $helper; /** * @var string */ - protected $job; + protected string $job; /** * @var string */ - protected $tmpDir; + protected string $tmpDir; /** * @var array */ - protected $config; + protected array $config; - /** - * @param string $job - * @param array $config - * @param Helper $helper - */ - public function __construct($job, array $config, Helper $helper = null) + public function __construct(string $job, array $config, ?Helper $helper = null) { $this->job = $job; $this->config = $config + [ @@ -54,7 +47,11 @@ public function __construct($job, array $config, Helper $helper = null) 'dateFormat' => null, 'enabled' => null, 'haltDir' => null, - 'debug' => null, + 'mattermostUrl' => null, + 'slackChannel' => null, + 'slackUrl' => null, + 'slackSender' => null, + 'mailSubject' => null, ]; $this->config['output_stdout'] = $this->config['output_stdout'] === null ? $this->config['output'] : $this->config['output_stdout']; @@ -65,7 +62,7 @@ public function __construct($job, array $config, Helper $helper = null) $this->tmpDir = $this->helper->getTempDir(); } - public function run() + public function run(): void { $lockFile = $this->getLockFile(); @@ -73,7 +70,7 @@ public function run() $this->checkMaxRuntime($lockFile); } catch (Exception $e) { $this->log('ERROR: ' . $e->getMessage(), 'stderr'); - $this->mail($e->getMessage()); + $this->notify($e->getMessage()); return; } @@ -96,7 +93,7 @@ public function run() $this->log('INFO: ' . $e->getMessage(), 'stderr'); } catch (Exception $e) { $this->log('ERROR: ' . $e->getMessage(), 'stderr'); - $this->mail($e->getMessage()); + $this->notify($e->getMessage()); } if ($lockAcquired) { @@ -104,26 +101,21 @@ public function run() // remove log file if empty $logfile = $this->getLogfile(); - if (is_file($logfile) && filesize($logfile) <= 0) { + if ($logfile !== false && is_file($logfile) && (filesize($logfile) <= 2 || file_get_contents($logfile) === "[]")) { unlink($logfile); } } } - /** - * @return array - */ - public function getConfig() + public function getConfig(): array { return $this->config; } /** - * @param string $lockFile - * * @throws Exception */ - protected function checkMaxRuntime($lockFile) + protected function checkMaxRuntime(string $lockFile): void { $maxRuntime = $this->config['maxRuntime']; if ($maxRuntime === null) { @@ -143,9 +135,9 @@ protected function checkMaxRuntime($lockFile) } /** - * @param string $message + * @deprecated */ - protected function mail($message) + protected function mail(string $message): void { if (empty($this->config['recipients'])) { return; @@ -158,11 +150,36 @@ protected function mail($message) ); } - /** - * @param string $output - * @return string - */ - protected function getLogfile($output = 'stdout') + protected function notify(string $message): void + { + if (!empty($this->config['recipients'])) { + $this->helper->sendMail( + $this->job, + $this->config, + $message + ); + } + + if (!empty($this->config['mattermostUrl'])) { + $this->helper->sendMattermostAlert( + $this->job, + $this->config, + $message + ); + } + + if (!empty($this->config['slackChannel']) && !empty($this->config['slackUrl'])) { + $this->helper->sendSlackAlert( + $this->job, + $this->config, + $message + ); + } + + + } + + protected function getLogfile(string $output = 'stdout'): string|false { $logfile = $this->config['output_'.$output]; if ($logfile === null) { @@ -178,10 +195,7 @@ protected function getLogfile($output = 'stdout') return $logfile; } - /** - * @return string - */ - protected function getLockFile() + protected function getLockFile(): string { $tmp = $this->tmpDir; $job = $this->helper->escape($this->job); @@ -195,10 +209,7 @@ protected function getLockFile() } } - /** - * @return bool - */ - protected function shouldRun() + protected function shouldRun(): bool { if (!$this->config['enabled']) { return false; @@ -211,18 +222,14 @@ protected function shouldRun() } $host = $this->helper->getHost(); - if (strcasecmp($this->config['runOnHost'], $host) != 0) { + if (strcasecmp($this->config['runOnHost'], $host) !== 0) { return false; } return true; } - /** - * @param string $message - * @param string $output - */ - protected function log($message, $output = 'stdout') + protected function log(string $message, string $output = 'stdout'): void { $now = date($this->config['dateFormat'], $_SERVER['REQUEST_TIME']); @@ -231,7 +238,7 @@ protected function log($message, $output = 'stdout') } } - protected function runFunction() + protected function runFunction(): void { $command = unserialize($this->config['closure']); @@ -246,7 +253,9 @@ protected function runFunction() } $content = ob_get_contents(); if ($logfile = $this->getLogfile()) { - file_put_contents($this->getLogfile(), $content, FILE_APPEND); + if(strlen($content) > 2){ + file_put_contents($this->getLogfile(), $content, FILE_APPEND); + } } ob_end_clean(); @@ -255,7 +264,7 @@ protected function runFunction() } } - protected function runFile() + protected function runFile(): void { // If job should run as another user, we must be on *nix and // must have sudo privileges. diff --git a/src/Helper.php b/src/Helper.php index 272b4e3..8a3915a 100644 --- a/src/Helper.php +++ b/src/Helper.php @@ -1,104 +1,113 @@ mailer = $mailer; + $this->guzzle = new Guzzle(); } - /** - * @param string $job - * @param array $config - * @param string $message - * - * @return \Swift_Message - */ - public function sendMail($job, array $config, $message) + public function sendMail(string $job, array $config, string $message): Email { $host = $this->getHost(); $body = <<setTo(explode(',', $config['recipients'])); - $mail->setSubject("[$host] '{$job}' needs some attention!"); - $mail->setBody($body); - $mail->setFrom([$config['smtpSender'] => $config['smtpSenderName']]); - $mail->setSender($config['smtpSender']); + $email = (new Email()) + ->from(new Address($config['smtpSender'], $config['smtpSenderName'])) + ->to(...array_map('trim', explode(',', $config['recipients']))) + ->subject(empty($config['mailSubject']) + ? "[$host] '{$job}' needs some attention!" + : $config['mailSubject']) + ->text($body); $mailer = $this->getCurrentMailer($config); - $mailer->send($mail); + $mailer->send($email); - return $mail; + return $email; } - /** - * @param array $config - * - * @return \Swift_Mailer - */ - private function getCurrentMailer(array $config) + private function getCurrentMailer(array $config): MailerInterface { if ($this->mailer !== null) { return $this->mailer; } - $swiftVersion = (int) explode('.', \Swift::VERSION)[0]; + $dsn = $this->buildMailerDsn($config); + $transport = Transport::fromDsn($dsn); + return new Mailer($transport); + } + + private function buildMailerDsn(array $config): string + { if ($config['mailer'] === 'smtp') { - $transport = new \Swift_SmtpTransport( - $config['smtpHost'], - $config['smtpPort'], - $config['smtpSecurity'] - ); - $transport->setUsername($config['smtpUsername']); - $transport->setPassword($config['smtpPassword']); - } elseif ($swiftVersion < 6 && $config['mailer'] === 'mail') { - $transport = \Swift_MailTransport::newInstance(); - } else { - $transport = new \Swift_SendmailTransport(); + $scheme = match ($config['smtpSecurity']) { + 'ssl' => 'smtps', + 'tls' => 'smtp', + default => 'smtp', + }; + + $user = $config['smtpUsername'] ? urlencode($config['smtpUsername']) : ''; + $pass = $config['smtpPassword'] ? urlencode($config['smtpPassword']) : ''; + $auth = ($user && $pass) ? "{$user}:{$pass}@" : ($user ? "{$user}@" : ''); + + $host = $config['smtpHost'] ?? 'localhost'; + $port = $config['smtpPort'] ?? 25; + + return "{$scheme}://{$auth}{$host}:{$port}"; } - return new \Swift_Mailer($transport); + // Default to sendmail + return 'sendmail://default'; } /** - * @param string $lockFile - * * @throws Exception * @throws InfoException */ - public function acquireLock($lockFile) + public function acquireLock(string $lockFile): void { if (array_key_exists($lockFile, $this->lockHandles)) { throw new Exception("Lock already acquired (Lockfile: $lockFile)."); @@ -130,11 +139,9 @@ public function acquireLock($lockFile) } /** - * @param string $lockFile - * * @throws Exception */ - public function releaseLock($lockFile) + public function releaseLock(string $lockFile): void { if (!array_key_exists($lockFile, $this->lockHandles)) { throw new Exception("Lock NOT held - bug? Lockfile: $lockFile"); @@ -148,12 +155,7 @@ public function releaseLock($lockFile) unset($this->lockHandles[$lockFile]); } - /** - * @param string $lockFile - * - * @return int - */ - public function getLockLifetime($lockFile) + public function getLockLifetime(string $lockFile): int { if (!file_exists($lockFile)) { return 0; @@ -170,13 +172,10 @@ public function getLockLifetime($lockFile) $stat = stat($lockFile); - return (time() - $stat['mtime']); + return time() - $stat['mtime']; } - /** - * @return string - */ - public function getTempDir() + public function getTempDir(): string { // @codeCoverageIgnoreStart if (function_exists('sys_get_temp_dir')) { @@ -195,26 +194,17 @@ public function getTempDir() return $tmp; } - /** - * @return string - */ - public function getHost() + public function getHost(): string { return php_uname('n'); } - /** - * @return string|null - */ - public function getApplicationEnv() + public function getApplicationEnv(): ?string { - return isset($_SERVER['APPLICATION_ENV']) ? $_SERVER['APPLICATION_ENV'] : null; + return $_SERVER['APPLICATION_ENV'] ?? null; } - /** - * @return int - */ - public function getPlatform() + public function getPlatform(): int { if (strncasecmp(PHP_OS, 'Win', 3) === 0) { // @codeCoverageIgnoreStart @@ -225,12 +215,7 @@ public function getPlatform() return self::UNIX; } - /** - * @param string $input - * - * @return string - */ - public function escape($input) + public function escape(string $input): string { $input = strtolower($input); $input = preg_replace('/[^a-z0-9_. -]+/', '', $input); @@ -241,7 +226,7 @@ public function escape($input) return $input; } - public function getSystemNullDevice() + public function getSystemNullDevice(): string { $platform = $this->getPlatform(); if ($platform === self::UNIX) { @@ -249,4 +234,39 @@ public function getSystemNullDevice() } return 'NUL'; } + + public function sendSlackAlert(string $job, array $config, string $message): void + { + $host = $this->getHost(); + $body = <<to($config['slackChannel']); + if ($config['slackSender']) { + $client->from($config['slackSender']); + } + $client->send($body); + } + + public function sendMattermostAlert(string $job, array $config, string $message): void + { + $host = $this->getHost(); + $body = << $body]; + $encoded = json_encode($payload, JSON_UNESCAPED_UNICODE); + $this->guzzle->post($config['mattermostUrl'], ['body' => $encoded]); + } } diff --git a/src/Jobby.php b/src/Jobby.php index 821f8c6..b1bb80b 100644 --- a/src/Jobby.php +++ b/src/Jobby.php @@ -12,26 +12,23 @@ class Jobby /** * @var array */ - protected $config = []; + protected array $config = []; /** * @var string */ - protected $script; + protected string $script; /** * @var array */ - protected $jobs = []; + protected array $jobs = []; /** - * @var Helper + * @var Helper|null */ - protected $helper; + protected ?Helper $helper = null; - /** - * @param array $config - */ public function __construct(array $config = []) { $this->setConfig($this->getDefaultConfig()); @@ -40,10 +37,7 @@ public function __construct(array $config = []) $this->script = realpath(__DIR__ . '/../bin/run-job'); } - /** - * @return Helper - */ - protected function getHelper() + protected function getHelper(): Helper { if ($this->helper === null) { $this->helper = new Helper(); @@ -52,10 +46,7 @@ protected function getHelper() return $this->helper; } - /** - * @return array - */ - public function getDefaultConfig() + public function getDefaultConfig(): array { return [ 'jobClass' => 'Jobby\BackgroundJob', @@ -79,29 +70,25 @@ public function getDefaultConfig() 'enabled' => true, 'haltDir' => null, 'debug' => false, + 'mattermostUrl' => null, + 'slackChannel' => null, + 'slackUrl' => null, + 'slackSender' => null, + 'mailSubject' => null, ]; } - /** - * @param array - */ - public function setConfig(array $config) + public function setConfig(array $config): void { $this->config = array_merge($this->config, $config); } - /** - * @return array - */ - public function getConfig() + public function getConfig(): array { return $this->config; } - /** - * @return array - */ - public function getJobs() + public function getJobs(): array { return $this->jobs; } @@ -109,12 +96,9 @@ public function getJobs() /** * Add a job. * - * @param string $job - * @param array $config - * * @throws Exception */ - public function add($job, array $config) + public function add(string $job, array $config): void { if (empty($config['schedule'])) { throw new Exception("'schedule' is required for '$job' job"); @@ -145,9 +129,9 @@ public function add($job, array $config) /** * Run all jobs. */ - public function run() + public function run(): void { - $isUnix = ($this->helper->getPlatform() === Helper::UNIX); + $isUnix = ($this->getHelper()->getPlatform() === Helper::UNIX); if ($isUnix && !extension_loaded('posix')) { throw new Exception('posix extension is required'); @@ -155,7 +139,7 @@ public function run() $scheduleChecker = new ScheduleChecker(new DateTimeImmutable("now")); foreach ($this->jobs as $jobConfig) { - list($job, $config) = $jobConfig; + [$job, $config] = $jobConfig; if (!$scheduleChecker->isDue($config['schedule'])) { continue; } @@ -167,11 +151,7 @@ public function run() } } - /** - * @param string $job - * @param array $config - */ - protected function runUnix($job, array $config) + protected function runUnix(string $job, array $config): void { $command = $this->getExecutableCommand($job, $config); $binary = $this->getPhpBinary(); @@ -181,11 +161,7 @@ protected function runUnix($job, array $config) } // @codeCoverageIgnoreStart - /** - * @param string $job - * @param array $config - */ - protected function runWindows($job, array $config) + protected function runWindows(string $job, array $config): void { // Run in background (non-blocking). From // http://us3.php.net/manual/en/function.exec.php#43834 @@ -196,13 +172,7 @@ protected function runWindows($job, array $config) } // @codeCoverageIgnoreEnd - /** - * @param string $job - * @param array $config - * - * @return string - */ - protected function getExecutableCommand($job, array $config) + protected function getExecutableCommand(string $job, array $config): string { if (isset($config['closure'])) { $wrapper = new SerializableClosure($config['closure']); @@ -217,10 +187,7 @@ protected function getExecutableCommand($job, array $config) return sprintf('"%s" "%s" "%s"', $this->script, $job, http_build_query($config)); } - /** - * @return false|string - */ - protected function getPhpBinary() + protected function getPhpBinary(): string|false { $executableFinder = new PhpExecutableFinder(); diff --git a/src/ScheduleChecker.php b/src/ScheduleChecker.php index ac4a914..89e5aa3 100644 --- a/src/ScheduleChecker.php +++ b/src/ScheduleChecker.php @@ -8,28 +8,24 @@ class ScheduleChecker { /** - * @var DateTimeImmutable|null + * @var DateTimeImmutable */ - private $now; + private DateTimeImmutable $now; - public function __construct(DateTimeImmutable $now = null) + public function __construct(?DateTimeImmutable $now = null) { $this->now = $now instanceof DateTimeImmutable ? $now : new DateTimeImmutable("now"); } - /** - * @param string|callable $schedule - * @return bool - */ - public function isDue($schedule) + public function isDue(string|callable $schedule): bool { if (is_callable($schedule)) { - return call_user_func($schedule, $this->now); + return $schedule($this->now); } $dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $schedule); if ($dateTime !== false) { - return $dateTime->format('Y-m-d H:i') == $this->now->format('Y-m-d H:i'); + return $dateTime->format('Y-m-d H:i') === $this->now->format('Y-m-d H:i'); } return CronExpression::factory((string)$schedule)->isDue($this->now); diff --git a/tests/BackgroundJobTest.php b/tests/BackgroundJobTest.php index 4f23482..0dc1fc4 100644 --- a/tests/BackgroundJobTest.php +++ b/tests/BackgroundJobTest.php @@ -5,12 +5,13 @@ use Jobby\BackgroundJob; use Jobby\Helper; use Opis\Closure\SerializableClosure; +use PHPUnit\Framework\TestCase; use Symfony\Component\Filesystem\Filesystem; /** * @coversDefaultClass Jobby\BackgroundJob */ -class BackgroundJobTest extends \PHPUnit_Framework_TestCase +class BackgroundJobTest extends TestCase { const JOB_NAME = 'name'; @@ -27,7 +28,7 @@ class BackgroundJobTest extends \PHPUnit_Framework_TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { $this->logFile = __DIR__ . '/_files/BackgroundJobTest.log'; if (file_exists($this->logFile)) { @@ -40,14 +41,14 @@ protected function setUp() /** * {@inheritdoc} */ - protected function tearDown() + protected function tearDown(): void { if (file_exists($this->logFile)) { unlink($this->logFile); } } - public function runProvider() + public function runProvider(): array { $echo = function () { echo 'test'; @@ -72,10 +73,10 @@ public function runProvider() /** * @covers ::getConfig */ - public function testGetConfig() + public function testGetConfig(): void { $job = new BackgroundJob('test job',[]); - $this->assertInternalType('array',$job->getConfig()); + $this->assertIsArray($job->getConfig()); } /** @@ -86,7 +87,7 @@ public function testGetConfig() * @param array $config * @param string $expectedOutput */ - public function testRun($config, $expectedOutput) + public function testRun($config, $expectedOutput): void { $this->runJob($config); @@ -96,20 +97,20 @@ public function testRun($config, $expectedOutput) /** * @covers ::runFile */ - public function testInvalidCommand() + public function testInvalidCommand(): void { $this->runJob(['command' => 'invalid-command']); - $this->assertContains('invalid-command', $this->getLogContent()); + $this->assertStringContainsString('invalid-command', $this->getLogContent()); if ($this->helper->getPlatform() === Helper::UNIX) { - $this->assertContains('not found', $this->getLogContent()); - $this->assertContains( + $this->assertStringContainsString('not found', $this->getLogContent()); + $this->assertStringContainsString( "ERROR: Job exited with status '127'", $this->getLogContent() ); } else { - $this->assertContains( + $this->assertStringContainsString( 'not recognized as an internal or external command', $this->getLogContent() ); @@ -119,7 +120,7 @@ public function testInvalidCommand() /** * @covers ::runFunction */ - public function testClosureNotReturnTrue() + public function testClosureNotReturnTrue(): void { $this->runJob( [ @@ -129,7 +130,7 @@ public function testClosureNotReturnTrue() ] ); - $this->assertContains( + $this->assertStringContainsString( 'ERROR: Closure did not return true! Returned:', $this->getLogContent() ); @@ -138,7 +139,7 @@ public function testClosureNotReturnTrue() /** * @covers ::getLogFile */ - public function testHideStdOutByDefault() + public function testHideStdOutByDefault(): void { ob_start(); $this->runJob( @@ -158,7 +159,7 @@ public function testHideStdOutByDefault() /** * @covers ::getLogFile */ - public function testShouldCreateLogFolder() + public function testShouldCreateLogFolder(): void { $logfile = dirname($this->logFile) . '/foo/bar.log'; $this->runJob( @@ -183,7 +184,7 @@ public function testShouldCreateLogFolder() /** * @covers ::getLogFile */ - public function testShouldSplitStderrAndStdout() + public function testShouldSplitStderrAndStdout(): void { $dirname = dirname($this->logFile); $stdout = $dirname . '/stdout.log'; @@ -196,23 +197,35 @@ public function testShouldSplitStderrAndStdout() ] ); - $this->assertContains('stdout output', @file_get_contents($stdout)); - $this->assertContains('stderr output', @file_get_contents($stderr)); + $stdoutContent = file_exists($stdout) ? file_get_contents($stdout) : ''; + $stderrContent = file_exists($stderr) ? file_get_contents($stderr) : ''; - unlink($stderr); - unlink($stdout); + $this->assertStringContainsString('stdout output', $stdoutContent); + $this->assertStringContainsString('stderr output', $stderrContent); + if (file_exists($stderr)) { + unlink($stderr); + } + if (file_exists($stdout)) { + unlink($stdout); + } } /** * @covers ::mail */ - public function testNotSendMailOnMissingRecipients() + public function testNotSendMailOnMissingRecipients(): void { - $helper = $this->getMock('Jobby\Helper', ['sendMail']); + $helper = $this->createMock(Helper::class); $helper->expects($this->never()) ->method('sendMail') ; + $helper->method('getHost')->willReturn('localhost'); + $helper->method('getTempDir')->willReturn(sys_get_temp_dir()); + $helper->method('getPlatform')->willReturn(Helper::UNIX); + $helper->method('escape')->willReturnCallback(function ($input) { + return preg_replace('/[^a-z0-9_. -]+/', '', strtolower($input)); + }); $this->runJob( [ @@ -228,12 +241,18 @@ public function testNotSendMailOnMissingRecipients() /** * @covers ::mail */ - public function testMailShouldTriggerHelper() + public function testMailShouldTriggerHelper(): void { - $helper = $this->getMock('Jobby\Helper', ['sendMail']); + $helper = $this->createMock(Helper::class); $helper->expects($this->once()) ->method('sendMail') ; + $helper->method('getHost')->willReturn('localhost'); + $helper->method('getTempDir')->willReturn(sys_get_temp_dir()); + $helper->method('getPlatform')->willReturn(Helper::UNIX); + $helper->method('escape')->willReturnCallback(function ($input) { + return preg_replace('/[^a-z0-9_. -]+/', '', strtolower($input)); + }); $this->runJob( [ @@ -249,17 +268,26 @@ public function testMailShouldTriggerHelper() /** * @covers ::checkMaxRuntime */ - public function testCheckMaxRuntime() + public function testCheckMaxRuntime(): void { if ($this->helper->getPlatform() !== Helper::UNIX) { $this->markTestSkipped("'maxRuntime' is not supported on Windows"); } - $helper = $this->getMock('Jobby\Helper', ['getLockLifetime']); + $helper = $this->createMock(Helper::class); $helper->expects($this->once()) ->method('getLockLifetime') - ->will($this->returnValue(0)) + ->willReturn(0) ; + $helper->method('getHost')->willReturn('localhost'); + $helper->method('getTempDir')->willReturn(sys_get_temp_dir()); + $helper->method('getPlatform')->willReturn(Helper::UNIX); + $helper->method('escape')->willReturnCallback(function ($input) { + return preg_replace('/[^a-z0-9_. -]+/', '', strtolower($input)); + }); + $helper->method('acquireLock')->willReturn(null); + $helper->method('releaseLock')->willReturn(null); + $helper->method('getSystemNullDevice')->willReturn('/dev/null'); $this->runJob( [ @@ -275,17 +303,23 @@ public function testCheckMaxRuntime() /** * @covers ::checkMaxRuntime */ - public function testCheckMaxRuntimeShouldFailIsExceeded() + public function testCheckMaxRuntimeShouldFailIsExceeded(): void { if ($this->helper->getPlatform() !== Helper::UNIX) { $this->markTestSkipped("'maxRuntime' is not supported on Windows"); } - $helper = $this->getMock('Jobby\Helper', ['getLockLifetime']); + $helper = $this->createMock(Helper::class); $helper->expects($this->once()) ->method('getLockLifetime') - ->will($this->returnValue(2)) + ->willReturn(2) ; + $helper->method('getHost')->willReturn('localhost'); + $helper->method('getTempDir')->willReturn(sys_get_temp_dir()); + $helper->method('getPlatform')->willReturn(Helper::UNIX); + $helper->method('escape')->willReturnCallback(function ($input) { + return preg_replace('/[^a-z0-9_. -]+/', '', strtolower($input)); + }); $this->runJob( [ @@ -295,7 +329,7 @@ public function testCheckMaxRuntimeShouldFailIsExceeded() $helper ); - $this->assertContains( + $this->assertStringContainsString( 'MaxRuntime of 1 secs exceeded! Current runtime: 2 secs', $this->getLogContent() ); @@ -308,7 +342,7 @@ public function testCheckMaxRuntimeShouldFailIsExceeded() * @param bool $createFile * @param bool $jobRuns */ - public function testHaltDir($createFile, $jobRuns) + public function testHaltDir($createFile, $jobRuns): void { $dir = __DIR__ . '/_files'; $file = $dir . '/' . static::JOB_NAME; @@ -338,7 +372,7 @@ public function testHaltDir($createFile, $jobRuns) $this->assertEquals($jobRuns, is_string($content) && !empty($content)); } - public function haltDirProvider() + public function haltDirProvider(): array { return [ [true, false], @@ -348,9 +382,9 @@ public function haltDirProvider() /** * @param array $config - * @param Helper $helper + * @param Helper|null $helper */ - private function runJob(array $config, Helper $helper = null) + private function runJob(array $config, ?Helper $helper = null): void { $config = $this->getJobConfig($config); @@ -363,7 +397,7 @@ private function runJob(array $config, Helper $helper = null) * * @return array */ - private function getJobConfig(array $config) + private function getJobConfig(array $config): array { $helper = new Helper(); @@ -390,8 +424,8 @@ private function getJobConfig(array $config) /** * @return string */ - private function getLogContent() + private function getLogContent(): string { - return @file_get_contents($this->logFile); + return file_exists($this->logFile) ? file_get_contents($this->logFile) : ''; } } diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index c74232d..91a6ff8 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -3,13 +3,14 @@ namespace Jobby\Tests; use Jobby\Exception; +use PHPUnit\Framework\TestCase; /** * @covers Jobby\Exception */ -class ExceptionTest extends \PHPUnit_Framework_TestCase +class ExceptionTest extends TestCase { - public function testInheritsBaseException() + public function testInheritsBaseException(): void { $e = new Exception(); $this->assertTrue($e instanceof \Exception); diff --git a/tests/HelperTest.php b/tests/HelperTest.php index b77521b..d95381a 100644 --- a/tests/HelperTest.php +++ b/tests/HelperTest.php @@ -2,13 +2,18 @@ namespace Jobby\Tests; +use Jobby\Exception; use Jobby\Helper; +use Jobby\InfoException; use Jobby\Jobby; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\Address; /** * @coversDefaultClass Jobby\Helper */ -class HelperTest extends \PHPUnit_Framework_TestCase +class HelperTest extends TestCase { /** * @var Helper @@ -33,7 +38,7 @@ class HelperTest extends \PHPUnit_Framework_TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { $this->helper = new Helper(); $this->tmpDir = $this->helper->getTempDir(); @@ -44,7 +49,7 @@ protected function setUp() /** * {@inheritdoc} */ - protected function tearDown() + protected function tearDown(): void { unset($_SERVER['APPLICATION_ENV']); } @@ -55,7 +60,7 @@ protected function tearDown() * * @dataProvider dataProviderTestEscape */ - public function testEscape($input, $expected) + public function testEscape($input, $expected): void { $actual = $this->helper->escape($input); $this->assertEquals($expected, $actual); @@ -64,7 +69,7 @@ public function testEscape($input, $expected) /** * @return array */ - public function dataProviderTestEscape() + public function dataProviderTestEscape(): array { return [ ['lower', 'lower'], @@ -79,7 +84,7 @@ public function dataProviderTestEscape() /** * @covers ::getPlatform */ - public function testGetPlatform() + public function testGetPlatform(): void { $actual = $this->helper->getPlatform(); $this->assertContains($actual, [Helper::UNIX, Helper::WINDOWS]); @@ -88,7 +93,7 @@ public function testGetPlatform() /** * @covers ::getPlatform */ - public function testPlatformConstants() + public function testPlatformConstants(): void { $this->assertNotEquals(Helper::UNIX, Helper::WINDOWS); } @@ -97,7 +102,7 @@ public function testPlatformConstants() * @covers ::acquireLock * @covers ::releaseLock */ - public function testAquireAndReleaseLock() + public function testAquireAndReleaseLock(): void { $this->helper->acquireLock($this->lockFile); $this->helper->releaseLock($this->lockFile); @@ -109,7 +114,7 @@ public function testAquireAndReleaseLock() * @covers ::acquireLock * @covers ::releaseLock */ - public function testLockFileShouldContainCurrentPid() + public function testLockFileShouldContainCurrentPid(): void { $this->helper->acquireLock($this->lockFile); @@ -131,9 +136,11 @@ public function testLockFileShouldContainCurrentPid() /** * @covers ::getLockLifetime */ - public function testLockLifetimeShouldBeZeroIfFileDoesNotExists() + public function testLockLifetimeShouldBeZeroIfFileDoesNotExists(): void { - unlink($this->lockFile); + if (file_exists($this->lockFile)) { + unlink($this->lockFile); + } $this->assertFalse(file_exists($this->lockFile)); $this->assertEquals(0, $this->helper->getLockLifetime($this->lockFile)); } @@ -141,7 +148,7 @@ public function testLockLifetimeShouldBeZeroIfFileDoesNotExists() /** * @covers ::getLockLifetime */ - public function testLockLifetimeShouldBeZeroIfFileIsEmpty() + public function testLockLifetimeShouldBeZeroIfFileIsEmpty(): void { file_put_contents($this->lockFile, ''); $this->assertEquals(0, $this->helper->getLockLifetime($this->lockFile)); @@ -150,7 +157,7 @@ public function testLockLifetimeShouldBeZeroIfFileIsEmpty() /** * @covers ::getLockLifetime */ - public function testLockLifetimeShouldBeZeroIfItContainsAInvalidPid() + public function testLockLifetimeShouldBeZeroIfItContainsAInvalidPid(): void { if ($this->helper->getPlatform() === Helper::WINDOWS) { $this->markTestSkipped("Test relies on posix_ functions"); @@ -163,7 +170,7 @@ public function testLockLifetimeShouldBeZeroIfItContainsAInvalidPid() /** * @covers ::getLockLifetime */ - public function testGetLocklifetime() + public function testGetLocklifetime(): void { if ($this->helper->getPlatform() === Helper::WINDOWS) { $this->markTestSkipped("Test relies on posix_ functions"); @@ -182,19 +189,20 @@ public function testGetLocklifetime() /** * @covers ::releaseLock - * @expectedException \Jobby\Exception */ - public function testReleaseNonExistin() + public function testReleaseNonExistin(): void { + $this->expectException(Exception::class); $this->helper->releaseLock($this->lockFile); } /** * @covers ::acquireLock - * @expectedException \Jobby\InfoException */ - public function testExceptionIfAquireFails() + public function testExceptionIfAquireFails(): void { + $this->expectException(InfoException::class); + $fh = fopen($this->lockFile, 'r+'); $this->assertTrue(is_resource($fh)); @@ -206,10 +214,10 @@ public function testExceptionIfAquireFails() /** * @covers ::acquireLock - * @expectedException \Jobby\Exception */ - public function testAquireLockShouldFailOnSecondTry() + public function testAquireLockShouldFailOnSecondTry(): void { + $this->expectException(Exception::class); $this->helper->acquireLock($this->lockFile); $this->helper->acquireLock($this->lockFile); } @@ -217,7 +225,7 @@ public function testAquireLockShouldFailOnSecondTry() /** * @covers ::getTempDir */ - public function testGetTempDir() + public function testGetTempDir(): void { $valid = [sys_get_temp_dir(), getcwd()]; foreach (['TMP', 'TEMP', 'TMPDIR'] as $key) { @@ -233,7 +241,7 @@ public function testGetTempDir() /** * @covers ::getApplicationEnv */ - public function testGetApplicationEnv() + public function testGetApplicationEnv(): void { $_SERVER['APPLICATION_ENV'] = 'foo'; @@ -244,7 +252,7 @@ public function testGetApplicationEnv() /** * @covers ::getApplicationEnv */ - public function testGetApplicationEnvShouldBeNullIfUndefined() + public function testGetApplicationEnvShouldBeNullIfUndefined(): void { $actual = $this->helper->getApplicationEnv(); $this->assertNull($actual); @@ -253,7 +261,7 @@ public function testGetApplicationEnvShouldBeNullIfUndefined() /** * @covers ::getHost */ - public function testGetHostname() + public function testGetHostname(): void { $actual = $this->helper->getHost(); $this->assertContains($actual, [gethostname(), php_uname('n')]); @@ -263,9 +271,9 @@ public function testGetHostname() * @covers ::sendMail * @covers ::getCurrentMailer */ - public function testSendMail() + public function testSendMail(): void { - $mailer = $this->getSwiftMailerMock(); + $mailer = $this->createMock(MailerInterface::class); $mailer->expects($this->once()) ->method('send') ; @@ -276,37 +284,30 @@ public function testSendMail() $config['recipients'] = 'a@a.com,b@b.com'; $helper = new Helper($mailer); - $mail = $helper->sendMail('job', $config, 'message'); + $email = $helper->sendMail('job', $config, 'message'); $host = $helper->getHost(); - $email = "jobby@$host"; - $this->assertContains('job', $mail->getSubject()); - $this->assertContains("[$host]", $mail->getSubject()); - $this->assertEquals(1, count($mail->getFrom())); - $this->assertEquals('jobby', current($mail->getFrom())); - $this->assertEquals($email, current(array_keys($mail->getFrom()))); - $this->assertEquals($email, current(array_keys($mail->getSender()))); - $this->assertContains($config['output'], $mail->getBody()); - $this->assertContains('message', $mail->getBody()); - } + $expectedEmail = "jobby@$host"; - /** - * @return \Swift_Mailer - */ - private function getSwiftMailerMock() - { - $nullTransport = new \Swift_NullTransport(); + $this->assertStringContainsString('job', $email->getSubject()); + $this->assertStringContainsString("[$host]", $email->getSubject()); + + $fromAddresses = $email->getFrom(); + $this->assertCount(1, $fromAddresses); + $this->assertInstanceOf(Address::class, $fromAddresses[0]); + $this->assertEquals($expectedEmail, $fromAddresses[0]->getAddress()); + $this->assertEquals('jobby', $fromAddresses[0]->getName()); - return $this->getMock('Swift_Mailer', [], [$nullTransport]); + $this->assertStringContainsString($config['output'], $email->getTextBody()); + $this->assertStringContainsString('message', $email->getTextBody()); } /** * @return void */ - public function testItReturnsTheCorrectNullSystemDeviceForUnix() + public function testItReturnsTheCorrectNullSystemDeviceForUnix(): void { - /** @var Helper $helper */ - $helper = $this->getMock("\\Jobby\\Helper", ["getPlatform"]); + $helper = $this->createPartialMock(Helper::class, ["getPlatform"]); $helper->expects($this->once()) ->method("getPlatform") ->willReturn(Helper::UNIX); @@ -317,10 +318,9 @@ public function testItReturnsTheCorrectNullSystemDeviceForUnix() /** * @return void */ - public function testItReturnsTheCorrectNullSystemDeviceForWindows() + public function testItReturnsTheCorrectNullSystemDeviceForWindows(): void { - /** @var Helper $helper */ - $helper = $this->getMock("\\Jobby\\Helper", ["getPlatform"]); + $helper = $this->createPartialMock(Helper::class, ["getPlatform"]); $helper->expects($this->once()) ->method("getPlatform") ->willReturn(Helper::WINDOWS); diff --git a/tests/JobbyTest.php b/tests/JobbyTest.php index 9891942..95fc2e9 100644 --- a/tests/JobbyTest.php +++ b/tests/JobbyTest.php @@ -2,14 +2,16 @@ namespace Jobby\Tests; +use Jobby\Exception; use Jobby\Helper; use Jobby\Jobby; use Opis\Closure\SerializableClosure; +use PHPUnit\Framework\TestCase; /** * @coversDefaultClass Jobby\Jobby */ -class JobbyTest extends \PHPUnit_Framework_TestCase +class JobbyTest extends TestCase { /** * @var string @@ -24,20 +26,20 @@ class JobbyTest extends \PHPUnit_Framework_TestCase /** * {@inheritdoc} */ - protected function setUp() + protected function setUp(): void { $this->logFile = __DIR__ . '/_files/JobbyTest.log'; if (file_exists($this->logFile)) { unlink($this->logFile); } - + $this->helper = new Helper(); } /** * {@inheritdoc} */ - protected function tearDown() + protected function tearDown(): void { if (file_exists($this->logFile)) { unlink($this->logFile); @@ -48,7 +50,7 @@ protected function tearDown() * @covers ::add * @covers ::run */ - public function testShell() + public function testShell(): void { $jobby = new Jobby(); $jobby->add( @@ -70,7 +72,7 @@ public function testShell() /** * @return void */ - public function testBackgroundProcessIsNotSpawnedIfJobIsNotDueToBeRun() + public function testBackgroundProcessIsNotSpawnedIfJobIsNotDueToBeRun(): void { $hour = date("H", strtotime("+1 hour")); $jobby = new Jobby(); @@ -97,7 +99,7 @@ public function testBackgroundProcessIsNotSpawnedIfJobIsNotDueToBeRun() * @covers ::add * @covers ::run */ - public function testOpisClosure() + public function testOpisClosure(): void { $fn = static function () { echo 'Another function!'; @@ -131,7 +133,7 @@ public function testOpisClosure() * @covers ::add * @covers ::run */ - public function testClosure() + public function testClosure(): void { $jobby = new Jobby(); $jobby->add( @@ -158,7 +160,7 @@ public function testClosure() * @covers ::add * @covers ::run */ - public function testShouldRunAllJobsAdded() + public function testShouldRunAllJobsAdded(): void { $jobby = new Jobby(['output' => $this->logFile]); $jobby->add( @@ -188,15 +190,15 @@ public function testShouldRunAllJobsAdded() // Job runs asynchronously, so wait a bit sleep($this->getSleepTime()); - $this->assertContains('job-1', $this->getLogContent()); - $this->assertContains('job-2', $this->getLogContent()); + $this->assertStringContainsString('job-1', $this->getLogContent()); + $this->assertStringContainsString('job-2', $this->getLogContent()); } /** * This is the same test as testClosure but (!) we use the default * options to set the output file. */ - public function testDefaultOptionsShouldBeMerged() + public function testDefaultOptionsShouldBeMerged(): void { $jobby = new Jobby(['output' => $this->logFile]); $jobby->add( @@ -221,7 +223,7 @@ public function testDefaultOptionsShouldBeMerged() /** * @covers ::getDefaultConfig */ - public function testDefaultConfig() + public function testDefaultConfig(): void { $jobby = new Jobby(); $config = $jobby->getDefaultConfig(); @@ -239,7 +241,7 @@ public function testDefaultConfig() * @covers ::setConfig * @covers ::getConfig */ - public function testSetConfig() + public function testSetConfig(): void { $jobby = new Jobby(); $oldCfg = $jobby->getConfig(); @@ -254,11 +256,11 @@ public function testSetConfig() /** * @covers ::getJobs */ - public function testGetJobs() + public function testGetJobs(): void { $jobby = new Jobby(); $this->assertCount(0,$jobby->getJobs()); - + $jobby->add( 'test job1', [ @@ -280,10 +282,11 @@ public function testGetJobs() /** * @covers ::add - * @expectedException \Jobby\Exception */ - public function testExceptionOnMissingJobOptionCommand() + public function testExceptionOnMissingJobOptionCommand(): void { + $this->expectException(Exception::class); + $jobby = new Jobby(); $jobby->add( @@ -296,10 +299,11 @@ public function testExceptionOnMissingJobOptionCommand() /** * @covers ::add - * @expectedException \Jobby\Exception */ - public function testExceptionOnMissingJobOptionSchedule() + public function testExceptionOnMissingJobOptionSchedule(): void { + $this->expectException(Exception::class); + $jobby = new Jobby(); $jobby->add( @@ -316,7 +320,7 @@ public function testExceptionOnMissingJobOptionSchedule() * @covers ::runWindows * @covers ::runUnix */ - public function testShouldRunJobsAsync() + public function testShouldRunJobsAsync(): void { $jobby = new Jobby(); $jobby->add( @@ -336,7 +340,7 @@ public function testShouldRunJobsAsync() $this->assertLessThan(0.5, $duration); } - public function testShouldFailIfMaxRuntimeExceeded() + public function testShouldFailIfMaxRuntimeExceeded(): void { if ($this->helper->getPlatform() === Helper::WINDOWS) { $this->markTestSkipped("'maxRuntime' is not supported on Windows"); @@ -358,18 +362,18 @@ public function testShouldFailIfMaxRuntimeExceeded() $jobby->run(); sleep(2); - $this->assertContains('ERROR: MaxRuntime of 1 secs exceeded!', $this->getLogContent()); + $this->assertStringContainsString('ERROR: MaxRuntime of 1 secs exceeded!', $this->getLogContent()); } /** * @return string */ - private function getLogContent() + private function getLogContent(): string { return file_get_contents($this->logFile); } - private function getSleepTime() + private function getSleepTime(): int { return $this->helper->getPlatform() === Helper::UNIX ? 1 : 2; } diff --git a/tests/ScheduleCheckerTest.php b/tests/ScheduleCheckerTest.php index 032386f..bdd40d8 100644 --- a/tests/ScheduleCheckerTest.php +++ b/tests/ScheduleCheckerTest.php @@ -4,9 +4,9 @@ use DateTimeImmutable; use Jobby\ScheduleChecker; -use PHPUnit_Framework_TestCase; +use PHPUnit\Framework\TestCase; -class ScheduleCheckerTest extends PHPUnit_Framework_TestCase +class ScheduleCheckerTest extends TestCase { /** * @var ScheduleChecker @@ -16,7 +16,7 @@ class ScheduleCheckerTest extends PHPUnit_Framework_TestCase /** * @return void */ - protected function setUp() + protected function setUp(): void { parent::setUp(); @@ -26,7 +26,7 @@ protected function setUp() /** * @return void */ - public function test_it_can_detect_a_due_job_from_a_datetime_string() + public function test_it_can_detect_a_due_job_from_a_datetime_string(): void { $this->assertTrue($this->scheduleChecker->isDue(date('Y-m-d H:i:s'))); } @@ -34,7 +34,7 @@ public function test_it_can_detect_a_due_job_from_a_datetime_string() /** * @return void */ - public function test_it_can_detect_if_a_job_is_due_with_a_passed_in_DateTimeImmutable() + public function test_it_can_detect_if_a_job_is_due_with_a_passed_in_DateTimeImmutable(): void { $scheduleChecker = new ScheduleChecker(new DateTimeImmutable("2017-01-02 13:14:59")); @@ -45,7 +45,7 @@ public function test_it_can_detect_if_a_job_is_due_with_a_passed_in_DateTimeImmu /** * @return void */ - public function test_it_can_detect_a_non_due_job_from_a_datetime_string() + public function test_it_can_detect_a_non_due_job_from_a_datetime_string(): void { $this->assertFalse($this->scheduleChecker->isDue(date('Y-m-d H:i:s', strtotime('tomorrow')))); } @@ -53,7 +53,7 @@ public function test_it_can_detect_a_non_due_job_from_a_datetime_string() /** * @return void */ - public function test_it_can_detect_a_due_job_from_a_cron_expression() + public function test_it_can_detect_a_due_job_from_a_cron_expression(): void { $this->assertTrue($this->scheduleChecker->isDue("* * * * *")); } @@ -61,7 +61,7 @@ public function test_it_can_detect_a_due_job_from_a_cron_expression() /** * @return void */ - public function test_it_can_detect_a_due_job_from_a_non_trivial_cron_expression() + public function test_it_can_detect_a_due_job_from_a_non_trivial_cron_expression(): void { $scheduleChecker = new ScheduleChecker(new DateTimeImmutable("2017-04-01 00:00:00")); @@ -71,7 +71,7 @@ public function test_it_can_detect_a_due_job_from_a_non_trivial_cron_expression( /** * @return void */ - public function test_it_can_detect_a_non_due_job_from_a_cron_expression() + public function test_it_can_detect_a_non_due_job_from_a_cron_expression(): void { $hour = date("H", strtotime('+1 hour')); $this->assertFalse($this->scheduleChecker->isDue("* {$hour} * * *")); @@ -80,7 +80,7 @@ public function test_it_can_detect_a_non_due_job_from_a_cron_expression() /** * @return void */ - public function test_it_can_use_a_closure_to_detect_a_due_job() + public function test_it_can_use_a_closure_to_detect_a_due_job(): void { $this->assertTrue( $this->scheduleChecker->isDue(function() { @@ -92,7 +92,7 @@ public function test_it_can_use_a_closure_to_detect_a_due_job() /** * @return void */ - public function test_it_can_use_a_closure_to_detect_a_non_due_job() + public function test_it_can_use_a_closure_to_detect_a_non_due_job(): void { $this->assertFalse( $this->scheduleChecker->isDue(function() { @@ -104,7 +104,7 @@ public function test_it_can_use_a_closure_to_detect_a_non_due_job() /** * @return void */ - public function test_it_can_detect_if_a_job_is_due_with_a_passed_in_DateTimeImmutable_from_a_cron_expression() + public function test_it_can_detect_if_a_job_is_due_with_a_passed_in_DateTimeImmutable_from_a_cron_expression(): void { $scheduleChecker = new ScheduleChecker(new DateTimeImmutable("2017-01-02 18:14:59"));