This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tez\Utils\Tests\Clock;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Tez\Utils\Clock\ClockInterface;
|
||||
use Tez\Utils\Clock\FrozenClock;
|
||||
use Tez\Utils\Clock\Stopwatch;
|
||||
use Tez\Utils\Clock\SystemClock;
|
||||
|
||||
final class ClockTest extends TestCase
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// SystemClock
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testSystemClockImplementsInterface(): void
|
||||
{
|
||||
self::assertInstanceOf(ClockInterface::class, new SystemClock());
|
||||
}
|
||||
|
||||
public function testSystemClockReturnsDateTimeImmutable(): void
|
||||
{
|
||||
$clock = new SystemClock();
|
||||
|
||||
self::assertInstanceOf(\DateTimeImmutable::class, $clock->now());
|
||||
}
|
||||
|
||||
public function testSystemClockReturnsCurrentTime(): void
|
||||
{
|
||||
$before = new \DateTimeImmutable();
|
||||
$now = (new SystemClock())->now();
|
||||
$after = new \DateTimeImmutable();
|
||||
|
||||
self::assertGreaterThanOrEqual($before->getTimestamp(), $now->getTimestamp());
|
||||
self::assertLessThanOrEqual($after->getTimestamp(), $now->getTimestamp());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// FrozenClock
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testFrozenClockImplementsInterface(): void
|
||||
{
|
||||
$clock = new FrozenClock(new \DateTimeImmutable('2026-01-01 12:00:00'));
|
||||
|
||||
self::assertInstanceOf(ClockInterface::class, $clock);
|
||||
}
|
||||
|
||||
public function testFrozenClockAlwaysReturnsSameTime(): void
|
||||
{
|
||||
$fixed = new \DateTimeImmutable('2026-01-01 12:00:00');
|
||||
$clock = new FrozenClock($fixed);
|
||||
|
||||
self::assertSame($fixed, $clock->now());
|
||||
self::assertSame($fixed, $clock->now());
|
||||
}
|
||||
|
||||
public function testFrozenClockSetAdvancesTime(): void
|
||||
{
|
||||
$clock = new FrozenClock(new \DateTimeImmutable('2026-01-01 12:00:00'));
|
||||
$later = new \DateTimeImmutable('2026-06-15 08:30:00');
|
||||
|
||||
$clock->set($later);
|
||||
|
||||
self::assertSame($later, $clock->now());
|
||||
}
|
||||
|
||||
public function testFrozenClockIsUsableInTypedContext(): void
|
||||
{
|
||||
$clock = new FrozenClock(new \DateTimeImmutable('2026-01-01 00:00:00'));
|
||||
|
||||
$timestamp = $this->readTimestamp($clock);
|
||||
|
||||
self::assertSame('2026-01-01 00:00:00', $timestamp);
|
||||
}
|
||||
|
||||
private function readTimestamp(ClockInterface $clock): string
|
||||
{
|
||||
return $clock->now()->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Stopwatch
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function testStopwatchStartReturnsInstance(): void
|
||||
{
|
||||
self::assertInstanceOf(Stopwatch::class, Stopwatch::start());
|
||||
}
|
||||
|
||||
public function testStopwatchElapsedIsNonNegative(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
|
||||
self::assertGreaterThanOrEqual(0.0, $sw->elapsed());
|
||||
}
|
||||
|
||||
public function testStopwatchElapsedMsIsNonNegative(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
|
||||
self::assertGreaterThanOrEqual(0.0, $sw->elapsedMs());
|
||||
}
|
||||
|
||||
public function testStopwatchElapsedMsIsLargerThanElapsedSeconds(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
// A non-trivial amount of time: elapsed in ms should be > in seconds
|
||||
// for any time >= 1ms (both should be close to zero but ms > s for small values)
|
||||
$elapsedSec = $sw->elapsed();
|
||||
$elapsedMs = $sw->elapsedMs();
|
||||
|
||||
// For any positive time: ms value >= s value * 1 (since 1ms = 0.001s)
|
||||
// This holds as long as elapsed < 1 second (which it will be in a test)
|
||||
self::assertGreaterThanOrEqual($elapsedSec, $elapsedMs);
|
||||
}
|
||||
|
||||
public function testStopwatchResetMakesElapsedNearZero(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
// Let some time pass conceptually (we can't sleep), then reset
|
||||
$sw->reset();
|
||||
|
||||
self::assertLessThan(0.01, $sw->elapsed()); // under 10ms
|
||||
}
|
||||
|
||||
public function testStopwatchLapReturnsNonNegative(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
|
||||
self::assertGreaterThanOrEqual(0.0, $sw->lap());
|
||||
}
|
||||
|
||||
public function testStopwatchSubsequentLapsAreIndependent(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
$lap1 = $sw->lap();
|
||||
$lap2 = $sw->lap();
|
||||
|
||||
// Both laps are non-negative and their sum should not exceed total elapsed
|
||||
self::assertGreaterThanOrEqual(0.0, $lap1);
|
||||
self::assertGreaterThanOrEqual(0.0, $lap2);
|
||||
}
|
||||
|
||||
public function testStopwatchLapDoesNotResetTotalElapsed(): void
|
||||
{
|
||||
$sw = Stopwatch::start();
|
||||
$sw->lap();
|
||||
$total = $sw->elapsed();
|
||||
|
||||
self::assertGreaterThanOrEqual(0.0, $total);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user