200 lines
5.8 KiB
PHP
200 lines
5.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* CronExpression 解析器单元测试
|
|
*
|
|
* 运行: php tests/CronExpressionTest.php
|
|
*/
|
|
|
|
use Kiri\Crontab\CronExpression;
|
|
|
|
// 手动加载源文件 (未安装 composer 时)
|
|
require_once __DIR__ . '/../src/CronExpression.php';
|
|
|
|
class CronExpressionTest
|
|
{
|
|
|
|
private CronExpression $parser;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->parser = new CronExpression();
|
|
}
|
|
|
|
/**
|
|
* 运行所有测试
|
|
*/
|
|
public function run(): void
|
|
{
|
|
$methods = get_class_methods($this);
|
|
$passed = 0;
|
|
$failed = 0;
|
|
|
|
foreach ($methods as $method) {
|
|
if (!str_starts_with($method, 'test')) {
|
|
continue;
|
|
}
|
|
|
|
echo " 运行: {$method}..." . PHP_EOL;
|
|
try {
|
|
$this->{$method}();
|
|
$passed++;
|
|
echo " ✓ 通过" . PHP_EOL;
|
|
} catch (\Throwable $throwable) {
|
|
$failed++;
|
|
echo " ✗ 失败: {$throwable->getMessage()}" . PHP_EOL;
|
|
}
|
|
}
|
|
|
|
echo PHP_EOL;
|
|
echo "总计: " . ($passed + $failed) . " 个测试, {$passed} 通过, {$failed} 失败" . PHP_EOL;
|
|
}
|
|
|
|
/**
|
|
* 基础断言
|
|
*/
|
|
private function assertTrue(bool $condition, string $message = ''): void
|
|
{
|
|
if (!$condition) {
|
|
throw new \RuntimeException($message ?: '断言失败');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 测试 every:60 表达式
|
|
*/
|
|
public function testEverySeconds(): void
|
|
{
|
|
$now = time();
|
|
$next = $this->parser->getNextRunTime('every:60', $now);
|
|
$this->assertTrue($next === $now + 60, "期望 " . ($now + 60) . " 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 every:5m 表达式
|
|
*/
|
|
public function testEveryMinutes(): void
|
|
{
|
|
$now = time();
|
|
$next = $this->parser->getNextRunTime('every:5m', $now);
|
|
$this->assertTrue($next === $now + 300, "期望 " . ($now + 300) . " 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 every:1h 表达式
|
|
*/
|
|
public function testEveryHours(): void
|
|
{
|
|
$now = time();
|
|
$next = $this->parser->getNextRunTime('every:1h', $now);
|
|
$this->assertTrue($next === $now + 3600, "期望 " . ($now + 3600) . " 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 daily 表达式
|
|
*/
|
|
public function testDaily(): void
|
|
{
|
|
$now = mktime(10, 0, 0, 1, 15, 2026); // 2026-01-15 10:00:00
|
|
$next = $this->parser->getNextRunTime('daily:03:00', $now);
|
|
$expectedToday = mktime(3, 0, 0, 1, 15, 2026);
|
|
|
|
if ($now >= $expectedToday) {
|
|
$expected = mktime(3, 0, 0, 1, 16, 2026);
|
|
} else {
|
|
$expected = $expectedToday;
|
|
}
|
|
|
|
$this->assertTrue($next === $expected, "期望 $expected 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 hourly 表达式
|
|
*/
|
|
public function testHourly(): void
|
|
{
|
|
$now = mktime(10, 15, 0, 1, 15, 2026); // 10:15
|
|
$next = $this->parser->getNextRunTime('hourly:30', $now);
|
|
// 10:15 之后的下一个 10:30
|
|
$expected = mktime(10, 30, 0, 1, 15, 2026);
|
|
$this->assertTrue($next === $expected, "期望 $expected 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 at 一次性任务
|
|
*/
|
|
public function testAt(): void
|
|
{
|
|
$now = time();
|
|
$future = $now + 3600;
|
|
$next = $this->parser->getNextRunTime("at:{$future}", $now);
|
|
$this->assertTrue($next === $future, "期望 $future 实际 $next");
|
|
|
|
// 已过期的 at 任务返回 0
|
|
$past = $now - 3600;
|
|
$expired = $this->parser->getNextRunTime("at:{$past}", $now);
|
|
$this->assertTrue($expired === 0, "期望 0 实际 $expired");
|
|
}
|
|
|
|
/**
|
|
* 测试 isOneShot
|
|
*/
|
|
public function testIsOneShot(): void
|
|
{
|
|
$this->assertTrue($this->parser->isOneShot('at:1234567890'));
|
|
$this->assertTrue(!$this->parser->isOneShot('every:60'));
|
|
$this->assertTrue(!$this->parser->isOneShot('daily:03:00'));
|
|
$this->assertTrue(!$this->parser->isOneShot('cron:* * * * *'));
|
|
}
|
|
|
|
/**
|
|
* 测试 cron 表达式 每5分钟
|
|
*/
|
|
public function testCronEvery5Minutes(): void
|
|
{
|
|
$now = mktime(10, 2, 0, 1, 15, 2026); // 10:02
|
|
$next = $this->parser->getNextRunTime('cron:*/5 * * * *', $now);
|
|
// 下一个 */5 是 10:05
|
|
$expected = mktime(10, 5, 0, 1, 15, 2026);
|
|
$this->assertTrue($next === $expected, "期望 $expected 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 cron 表达式精确时间
|
|
*/
|
|
public function testCronExactTime(): void
|
|
{
|
|
$now = mktime(10, 0, 0, 1, 15, 2026);
|
|
$next = $this->parser->getNextRunTime('cron:30 8 * * *', $now);
|
|
// 下一个 8:30 是明天
|
|
$expected = mktime(8, 30, 0, 1, 16, 2026);
|
|
$this->assertTrue($next === $expected, "期望 $expected 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试 cron 无效表达式
|
|
*/
|
|
public function testInvalidCronExpression(): void
|
|
{
|
|
$next = $this->parser->getNextRunTime('cron:invalid', time());
|
|
$this->assertTrue($next === 0, "期望 0 实际 $next");
|
|
}
|
|
|
|
/**
|
|
* 测试间隔描述
|
|
*/
|
|
public function testIntervalDescription(): void
|
|
{
|
|
$this->assertTrue($this->parser->getIntervalDescription('every:60') === '每 60 秒');
|
|
$this->assertTrue($this->parser->getIntervalDescription('every:5m') === '每 5 分钟');
|
|
$this->assertTrue($this->parser->getIntervalDescription('every:1h') === '每 1 小时');
|
|
$this->assertTrue($this->parser->getIntervalDescription('daily:03:00') === '每天 03:00');
|
|
$this->assertTrue($this->parser->getIntervalDescription('at:1234567890') === '一次性任务');
|
|
}
|
|
}
|
|
|
|
// 运行测试
|
|
$test = new CronExpressionTest();
|
|
$test->run();
|