ea
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
<?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();
|
||||
Reference in New Issue
Block a user