Skip to content

Commit 35986b9

Browse files
authored
Merge pull request #359 from MatusBoa/feat-timebox
feat(support): Port Timebox
2 parents a6352aa + 9817b82 commit 35986b9

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

src/support/src/Timebox.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Support;
6+
7+
use Throwable;
8+
9+
class Timebox
10+
{
11+
public bool $earlyReturn = false;
12+
13+
/**
14+
* @template TValue of mixed
15+
*
16+
* @param (callable(static): TValue) $callback
17+
*
18+
* @return TValue
19+
*
20+
* @throws Throwable
21+
*/
22+
public function call(callable $callback, int $microseconds): mixed
23+
{
24+
$exception = null;
25+
26+
$start = microtime(true);
27+
28+
try {
29+
$result = $callback($this);
30+
} catch (Throwable $caught) {
31+
$exception = $caught;
32+
}
33+
34+
$remainder = (int) ($microseconds - ((microtime(true) - $start) * 1_000_000));
35+
36+
if (! $this->earlyReturn && $remainder > 0) {
37+
$this->usleep($remainder);
38+
}
39+
40+
if ($exception) {
41+
throw $exception;
42+
}
43+
44+
return $result;
45+
}
46+
47+
public function returnEarly(): static
48+
{
49+
$this->earlyReturn = true;
50+
51+
return $this;
52+
}
53+
54+
public function dontReturnEarly(): static
55+
{
56+
$this->earlyReturn = false;
57+
58+
return $this;
59+
}
60+
61+
protected function usleep(int $microseconds)
62+
{
63+
Sleep::usleep($microseconds);
64+
}
65+
}

tests/Support/TimeboxTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hypervel\Tests\Support;
6+
7+
use Exception;
8+
use Hypervel\Support\Timebox;
9+
use Mockery;
10+
use PHPUnit\Framework\TestCase;
11+
12+
/**
13+
* @internal
14+
* @covers \Hypervel\Support\Timebox
15+
*/
16+
class TimeboxTest extends TestCase
17+
{
18+
public function testMakeExecutesCallback(): void
19+
{
20+
$callback = function () {
21+
$this->assertTrue(true);
22+
};
23+
24+
(new Timebox())->call($callback, 0);
25+
}
26+
27+
public function testMakeWaitsForMicroseconds(): void
28+
{
29+
$mock = Mockery::spy(Timebox::class)->shouldAllowMockingProtectedMethods()->makePartial();
30+
$mock->shouldReceive('usleep')->once();
31+
32+
$mock->call(function () {
33+
}, 10000);
34+
35+
$mock->shouldHaveReceived('usleep')->once();
36+
}
37+
38+
public function testMakeShouldNotSleepWhenEarlyReturnHasBeenFlagged(): void
39+
{
40+
$mock = Mockery::spy(Timebox::class)->shouldAllowMockingProtectedMethods()->makePartial();
41+
$mock->call(function ($timebox) {
42+
$timebox->returnEarly();
43+
}, 10000);
44+
45+
$mock->shouldNotHaveReceived('usleep');
46+
}
47+
48+
public function testMakeShouldSleepWhenDontEarlyReturnHasBeenFlagged(): void
49+
{
50+
$mock = Mockery::spy(Timebox::class)->shouldAllowMockingProtectedMethods()->makePartial();
51+
$mock->shouldReceive('usleep')->once();
52+
53+
$mock->call(function ($timebox) {
54+
$timebox->returnEarly();
55+
$timebox->dontReturnEarly();
56+
}, 10000);
57+
58+
$mock->shouldHaveReceived('usleep')->once();
59+
}
60+
61+
public function testMakeWaitsForMicrosecondsWhenExceptionIsThrown(): void
62+
{
63+
$mock = Mockery::spy(Timebox::class)->shouldAllowMockingProtectedMethods()->makePartial();
64+
$mock->shouldReceive('usleep')->once();
65+
66+
try {
67+
$this->expectExceptionMessage('Exception within Timebox callback.');
68+
69+
$mock->call(function () {
70+
throw new Exception('Exception within Timebox callback.');
71+
}, 10000);
72+
} finally {
73+
$mock->shouldHaveReceived('usleep')->once();
74+
}
75+
}
76+
77+
public function testMakeShouldNotSleepWhenEarlyReturnHasBeenFlaggedAndExceptionIsThrown(): void
78+
{
79+
$mock = Mockery::spy(Timebox::class)->shouldAllowMockingProtectedMethods()->makePartial();
80+
81+
try {
82+
$this->expectExceptionMessage('Exception within Timebox callback.');
83+
84+
$mock->call(function ($timebox) {
85+
$timebox->returnEarly();
86+
throw new Exception('Exception within Timebox callback.');
87+
}, 10000);
88+
} finally {
89+
$mock->shouldNotHaveReceived('usleep');
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)