Files

226 lines
6.8 KiB
PHP
Raw Permalink Normal View History

2026-06-28 17:06:12 +08:00
<?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");
}
2026-07-01 17:03:59 +08:00
/**
* 测试毫秒级 every:100ms 表达式
*/
public function testEveryMilliseconds(): void
{
$nowMs = 1760000000123;
$next = $this->parser->getNextRunTimeMs('every:100ms', $nowMs);
$this->assertTrue($next === $nowMs + 100, "期望 " . ($nowMs + 100) . " 实际 $next");
}
/**
* 测试秒级表达式在毫秒级接口中返回毫秒时间戳
*/
public function testEverySecondsAsMilliseconds(): void
{
$nowMs = 1760000000123;
$next = $this->parser->getNextRunTimeMs('every:1s', $nowMs);
$this->assertTrue($next === $nowMs + 1000, "期望 " . ($nowMs + 1000) . " 实际 $next");
}
2026-06-28 17:06:12 +08:00
/**
* 测试 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");
}
2026-07-01 17:03:59 +08:00
public function testCronAlignsToMinuteBoundary(): void
{
$now = mktime(10, 2, 30, 1, 15, 2026); // 10:02:30
$next = $this->parser->getNextRunTime('cron:*/5 * * * *', $now);
$expected = mktime(10, 5, 0, 1, 15, 2026);
$this->assertTrue($next === $expected, "期望 $expected 实际 $next");
}
2026-06-28 17:06:12 +08:00
/**
* 测试 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();