123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- <?php
- namespace Illuminate\Support;
- use Carbon\CarbonInterval;
- use DateInterval;
- use Illuminate\Support\Traits\Macroable;
- use PHPUnit\Framework\Assert as PHPUnit;
- use RuntimeException;
- class Sleep
- {
- use Macroable;
- /**
- * The fake sleep callbacks.
- *
- * @var array
- */
- public static $fakeSleepCallbacks = [];
- /**
- * Keep Carbon's "now" in sync when sleeping.
- *
- * @var bool
- */
- protected static $syncWithCarbon = false;
- /**
- * The total duration to sleep.
- *
- * @var \Carbon\CarbonInterval
- */
- public $duration;
- /**
- * The pending duration to sleep.
- *
- * @var int|float|null
- */
- protected $pending = null;
- /**
- * Indicates that all sleeping should be faked.
- *
- * @var bool
- */
- protected static $fake = false;
- /**
- * The sequence of sleep durations encountered while faking.
- *
- * @var array<int, \Carbon\CarbonInterval>
- */
- protected static $sequence = [];
- /**
- * Indicates if the instance should sleep.
- *
- * @var bool
- */
- protected $shouldSleep = true;
- /**
- * Create a new class instance.
- *
- * @param int|float|\DateInterval $duration
- * @return void
- */
- public function __construct($duration)
- {
- $this->duration($duration);
- }
- /**
- * Sleep for the given duration.
- *
- * @param \DateInterval|int|float $duration
- * @return static
- */
- public static function for($duration)
- {
- return new static($duration);
- }
- /**
- * Sleep until the given timestamp.
- *
- * @param \DateTimeInterface|int|float|numeric-string $timestamp
- * @return static
- */
- public static function until($timestamp)
- {
- if (is_numeric($timestamp)) {
- $timestamp = Carbon::createFromTimestamp($timestamp);
- }
- return new static(Carbon::now()->diff($timestamp));
- }
- /**
- * Sleep for the given number of microseconds.
- *
- * @param int $duration
- * @return static
- */
- public static function usleep($duration)
- {
- return (new static($duration))->microseconds();
- }
- /**
- * Sleep for the given number of seconds.
- *
- * @param int|float $duration
- * @return static
- */
- public static function sleep($duration)
- {
- return (new static($duration))->seconds();
- }
- /**
- * Sleep for the given duration. Replaces any previously defined duration.
- *
- * @param \DateInterval|int|float $duration
- * @return $this
- */
- protected function duration($duration)
- {
- if (! $duration instanceof DateInterval) {
- $this->duration = CarbonInterval::microsecond(0);
- $this->pending = $duration;
- } else {
- $duration = CarbonInterval::instance($duration);
- if ($duration->totalMicroseconds < 0) {
- $duration = CarbonInterval::seconds(0);
- }
- $this->duration = $duration;
- $this->pending = null;
- }
- return $this;
- }
- /**
- * Sleep for the given number of minutes.
- *
- * @return $this
- */
- public function minutes()
- {
- $this->duration->add('minutes', $this->pullPending());
- return $this;
- }
- /**
- * Sleep for one minute.
- *
- * @return $this
- */
- public function minute()
- {
- return $this->minutes();
- }
- /**
- * Sleep for the given number of seconds.
- *
- * @return $this
- */
- public function seconds()
- {
- $this->duration->add('seconds', $this->pullPending());
- return $this;
- }
- /**
- * Sleep for one second.
- *
- * @return $this
- */
- public function second()
- {
- return $this->seconds();
- }
- /**
- * Sleep for the given number of milliseconds.
- *
- * @return $this
- */
- public function milliseconds()
- {
- $this->duration->add('milliseconds', $this->pullPending());
- return $this;
- }
- /**
- * Sleep for one millisecond.
- *
- * @return $this
- */
- public function millisecond()
- {
- return $this->milliseconds();
- }
- /**
- * Sleep for the given number of microseconds.
- *
- * @return $this
- */
- public function microseconds()
- {
- $this->duration->add('microseconds', $this->pullPending());
- return $this;
- }
- /**
- * Sleep for on microsecond.
- *
- * @return $this
- */
- public function microsecond()
- {
- return $this->microseconds();
- }
- /**
- * Add additional time to sleep for.
- *
- * @param int|float $duration
- * @return $this
- */
- public function and($duration)
- {
- $this->pending = $duration;
- return $this;
- }
- /**
- * Handle the object's destruction.
- *
- * @return void
- */
- public function __destruct()
- {
- if (! $this->shouldSleep) {
- return;
- }
- if ($this->pending !== null) {
- throw new RuntimeException('Unknown duration unit.');
- }
- if (static::$fake) {
- static::$sequence[] = $this->duration;
- if (static::$syncWithCarbon) {
- Carbon::setTestNow(Carbon::now()->add($this->duration));
- }
- foreach (static::$fakeSleepCallbacks as $callback) {
- $callback($this->duration);
- }
- return;
- }
- $remaining = $this->duration->copy();
- $seconds = (int) $remaining->totalSeconds;
- if ($seconds > 0) {
- sleep($seconds);
- $remaining = $remaining->subSeconds($seconds);
- }
- $microseconds = (int) $remaining->totalMicroseconds;
- if ($microseconds > 0) {
- usleep($microseconds);
- }
- }
- /**
- * Resolve the pending duration.
- *
- * @return int|float
- */
- protected function pullPending()
- {
- if ($this->pending === null) {
- $this->shouldNotSleep();
- throw new RuntimeException('No duration specified.');
- }
- if ($this->pending < 0) {
- $this->pending = 0;
- }
- return tap($this->pending, function () {
- $this->pending = null;
- });
- }
- /**
- * Stay awake and capture any attempts to sleep.
- *
- * @param bool $value
- * @param bool $syncWithCarbon
- * @return void
- */
- public static function fake($value = true, $syncWithCarbon = false)
- {
- static::$fake = $value;
- static::$sequence = [];
- static::$fakeSleepCallbacks = [];
- static::$syncWithCarbon = $syncWithCarbon;
- }
- /**
- * Assert a given amount of sleeping occurred a specific number of times.
- *
- * @param \Closure $expected
- * @param int $times
- * @return void
- */
- public static function assertSlept($expected, $times = 1)
- {
- $count = collect(static::$sequence)->filter($expected)->count();
- PHPUnit::assertSame(
- $times,
- $count,
- "The expected sleep was found [{$count}] times instead of [{$times}]."
- );
- }
- /**
- * Assert sleeping occurred a given number of times.
- *
- * @param int $expected
- * @return void
- */
- public static function assertSleptTimes($expected)
- {
- PHPUnit::assertSame($expected, $count = count(static::$sequence), "Expected [{$expected}] sleeps but found [{$count}].");
- }
- /**
- * Assert the given sleep sequence was encountered.
- *
- * @param array $sequence
- * @return void
- */
- public static function assertSequence($sequence)
- {
- static::assertSleptTimes(count($sequence));
- collect($sequence)
- ->zip(static::$sequence)
- ->eachSpread(function (?Sleep $expected, CarbonInterval $actual) {
- if ($expected === null) {
- return;
- }
- PHPUnit::assertTrue(
- $expected->shouldNotSleep()->duration->equalTo($actual),
- vsprintf('Expected sleep duration of [%s] but actually slept for [%s].', [
- $expected->duration->cascade()->forHumans([
- 'options' => 0,
- 'minimumUnit' => 'microsecond',
- ]),
- $actual->cascade()->forHumans([
- 'options' => 0,
- 'minimumUnit' => 'microsecond',
- ]),
- ])
- );
- });
- }
- /**
- * Assert that no sleeping occurred.
- *
- * @return void
- */
- public static function assertNeverSlept()
- {
- return static::assertSleptTimes(0);
- }
- /**
- * Assert that no sleeping occurred.
- *
- * @return void
- */
- public static function assertInsomniac()
- {
- if (static::$sequence === []) {
- PHPUnit::assertTrue(true);
- }
- foreach (static::$sequence as $duration) {
- PHPUnit::assertSame(0, $duration->totalMicroseconds, vsprintf('Unexpected sleep duration of [%s] found.', [
- $duration->cascade()->forHumans([
- 'options' => 0,
- 'minimumUnit' => 'microsecond',
- ]),
- ]));
- }
- }
- /**
- * Indicate that the instance should not sleep.
- *
- * @return $this
- */
- protected function shouldNotSleep()
- {
- $this->shouldSleep = false;
- return $this;
- }
- /**
- * Only sleep when the given condition is true.
- *
- * @param (\Closure($this): bool)|bool $condition
- * @return $this
- */
- public function when($condition)
- {
- $this->shouldSleep = (bool) value($condition, $this);
- return $this;
- }
- /**
- * Don't sleep when the given condition is true.
- *
- * @param (\Closure($this): bool)|bool $condition
- * @return $this
- */
- public function unless($condition)
- {
- return $this->when(! value($condition, $this));
- }
- /**
- * Specify a callback that should be invoked when faking sleep within a test.
- *
- * @param callable $callback
- * @return void
- */
- public static function whenFakingSleep($callback)
- {
- static::$fakeSleepCallbacks[] = $callback;
- }
- /**
- * Indicate that Carbon's "now" should be kept in sync when sleeping.
- *
- * @return void
- */
- public static function syncWithCarbon($value = true)
- {
- static::$syncWithCarbon = $value;
- }
- }
|