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: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"); } /** * 测试 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"); } 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"); } /** * 测试 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();