From 0fdd6a4f55c37a9c99dea12761d518b7fc599d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 26 Oct 2022 11:26:02 +0200 Subject: [PATCH 1/2] Add PHPStan to test environment --- .gitattributes | 1 + .github/workflows/ci.yml | 17 +++++++++++++++++ README.md | 6 ++++++ composer.json | 1 + phpstan.neon.dist | 11 +++++++++++ src/functions.php | 6 +++--- tests/AsyncTest.php | 4 ++++ tests/AwaitTest.php | 2 +- tests/CoroutineTest.php | 4 ++++ tests/ParallelTest.php | 1 + tests/SeriesTest.php | 1 + tests/WaterfallTest.php | 1 + 12 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 phpstan.neon.dist diff --git a/.gitattributes b/.gitattributes index aa6c312..838c8fa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ /.gitattributes export-ignore /.github/ export-ignore /.gitignore export-ignore +/phpstan.neon.dist export-ignore /phpunit.xml.dist export-ignore /tests/ export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e13e6f..75213de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,3 +22,20 @@ jobs: ini-file: development - run: composer install - run: vendor/bin/phpunit --coverage-text + + PHPStan: + name: PHPStan (PHP ${{ matrix.php }}) + runs-on: ubuntu-22.04 + strategy: + matrix: + php: + - 8.2 + - 8.1 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + - run: composer install + - run: vendor/bin/phpstan diff --git a/README.md b/README.md index 4380c86..71a6288 100644 --- a/README.md +++ b/README.md @@ -659,6 +659,12 @@ To run the test suite, go to the project root and run: vendor/bin/phpunit ``` +On top of this, we use PHPStan on level 3 to ensure type safety across the project: + +```bash +vendor/bin/phpstan +``` + ## License MIT, see [LICENSE file](LICENSE). diff --git a/composer.json b/composer.json index 2d1ad50..848dd38 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,7 @@ "react/promise": "^3.0 || ^2.8 || ^1.2.1" }, "require-dev": { + "phpstan/phpstan": "1.10.18", "phpunit/phpunit": "^9.5" }, "autoload": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..903fb1f --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,11 @@ +parameters: + level: 3 + + paths: + - src/ + - tests/ + + reportUnmatchedIgnoredErrors: false + ignoreErrors: + # ignore generic usage like `PromiseInterface` until fixed upstream + - '/^PHPDoc .* contains generic type React\\Promise\\PromiseInterface<.+> but interface React\\Promise\\PromiseInterface is not generic\.$/' diff --git a/src/functions.php b/src/functions.php index 797911b..12f7d03 100644 --- a/src/functions.php +++ b/src/functions.php @@ -177,7 +177,7 @@ * ``` * * @param callable(mixed ...$args):mixed $function - * @return callable(): PromiseInterface + * @return callable(mixed ...$args): PromiseInterface * @since 4.0.0 * @see coroutine() */ @@ -587,7 +587,7 @@ function delay(float $seconds): void * }); * ``` * - * @param callable(...$args):\Generator $function + * @param callable(mixed ...$args):\Generator $function * @param mixed ...$args Optional list of additional arguments that will be passed to the given `$function` as is * @return PromiseInterface * @since 3.0.0 @@ -730,9 +730,9 @@ function series(iterable $tasks): PromiseInterface assert($tasks instanceof \Iterator); } - /** @var callable():void $next */ $taskCallback = function ($result) use (&$results, &$next) { $results[] = $result; + assert($next instanceof \Closure); $next(); }; diff --git a/tests/AsyncTest.php b/tests/AsyncTest.php index 7698b42..6e07112 100644 --- a/tests/AsyncTest.php +++ b/tests/AsyncTest.php @@ -194,6 +194,7 @@ public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPro })); })(); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Operation cancelled'))); @@ -211,6 +212,7 @@ public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPr } })(); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $promise->then($this->expectCallableOnceWith(42)); @@ -230,6 +232,7 @@ public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseR } })(); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $promise->then($this->expectCallableNever(), $this->expectCallableNever()); @@ -259,6 +262,7 @@ public function testCancelAsyncWillCancelNestedAwait() return time(); })(); + assert(method_exists($promise, 'cancel')); $promise->cancel(); await($promise); } diff --git a/tests/AwaitTest.php b/tests/AwaitTest.php index 3d2b886..3333062 100644 --- a/tests/AwaitTest.php +++ b/tests/AwaitTest.php @@ -361,7 +361,7 @@ public function testNestedAwaits(callable $await) $resolve($await(new Promise(function ($resolve) use ($await) { $resolve($await(new Promise(function ($resolve) use ($await) { $resolve($await(new Promise(function ($resolve) use ($await) { - $resolve($await(new Promise(function ($resolve) use ($await) { + $resolve($await(new Promise(function ($resolve) { Loop::addTimer(0.01, function () use ($resolve) { $resolve(true); }); diff --git a/tests/CoroutineTest.php b/tests/CoroutineTest.php index adc82bc..5ec4cde 100644 --- a/tests/CoroutineTest.php +++ b/tests/CoroutineTest.php @@ -114,6 +114,7 @@ public function testCancelCoroutineWillReturnRejectedPromiseWhenCancellingPendin }); }); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Operation cancelled'))); @@ -131,6 +132,7 @@ public function testCancelCoroutineWillReturnFulfilledPromiseWhenCancellingPendi } }); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $promise->then($this->expectCallableOnceWith(42)); @@ -150,6 +152,7 @@ public function testCancelCoroutineWillReturnPendigPromiseWhenCancellingFirstPro } }); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $promise->then($this->expectCallableNever(), $this->expectCallableNever()); @@ -209,6 +212,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseReject }); }); + assert(method_exists($promise, 'cancel')); $promise->cancel(); unset($promise); diff --git a/tests/ParallelTest.php b/tests/ParallelTest.php index 1a5759b..98bbce2 100644 --- a/tests/ParallelTest.php +++ b/tests/ParallelTest.php @@ -193,6 +193,7 @@ function () use (&$cancelled) { ); $promise = React\Async\parallel($tasks); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $this->assertSame(2, $cancelled); diff --git a/tests/SeriesTest.php b/tests/SeriesTest.php index 404c907..0bc5017 100644 --- a/tests/SeriesTest.php +++ b/tests/SeriesTest.php @@ -185,6 +185,7 @@ function () use (&$cancelled) { ); $promise = React\Async\series($tasks); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $this->assertSame(1, $cancelled); diff --git a/tests/WaterfallTest.php b/tests/WaterfallTest.php index 2fbbc23..d2f947f 100644 --- a/tests/WaterfallTest.php +++ b/tests/WaterfallTest.php @@ -199,6 +199,7 @@ function () use (&$cancelled) { ); $promise = React\Async\waterfall($tasks); + assert(method_exists($promise, 'cancel')); $promise->cancel(); $this->assertSame(1, $cancelled); From 6185725a7bf708f030fa456fb9edc986c222caf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 26 Oct 2022 12:37:20 +0200 Subject: [PATCH 2/2] Improve type definitions and update to PHPStan level `max` --- README.md | 2 +- phpstan.neon.dist | 2 +- src/FiberMap.php | 12 +++++++++-- src/SimpleFiber.php | 10 +++++++++- src/functions.php | 25 ++++++++++++++++++----- tests/AsyncTest.php | 31 +++++++++++++++-------------- tests/AwaitTest.php | 44 ++++++++++++++++++++++------------------- tests/CoroutineTest.php | 42 +++++++++++++++++++-------------------- tests/DelayTest.php | 16 +++++++++------ tests/ParallelTest.php | 25 ++++++++++++----------- tests/SeriesTest.php | 27 +++++++++++++------------ tests/TestCase.php | 25 +++++++++++------------ tests/Timer.php | 18 ++++++++--------- tests/WaterfallTest.php | 27 +++++++++++++------------ 14 files changed, 173 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index 71a6288..523dd67 100644 --- a/README.md +++ b/README.md @@ -659,7 +659,7 @@ To run the test suite, go to the project root and run: vendor/bin/phpunit ``` -On top of this, we use PHPStan on level 3 to ensure type safety across the project: +On top of this, we use PHPStan on max level to ensure type safety across the project: ```bash vendor/bin/phpstan diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 903fb1f..b7f8ddb 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - level: 3 + level: max paths: - src/ diff --git a/src/FiberMap.php b/src/FiberMap.php index 36846b4..0648788 100644 --- a/src/FiberMap.php +++ b/src/FiberMap.php @@ -9,35 +9,43 @@ */ final class FiberMap { + /** @var array */ private static array $status = []; - private static array $map = []; + /** @var array */ + private static array $map = []; + + /** @param \Fiber $fiber */ public static function register(\Fiber $fiber): void { self::$status[\spl_object_id($fiber)] = false; - self::$map[\spl_object_id($fiber)] = []; } + /** @param \Fiber $fiber */ public static function cancel(\Fiber $fiber): void { self::$status[\spl_object_id($fiber)] = true; } + /** @param \Fiber $fiber */ public static function setPromise(\Fiber $fiber, PromiseInterface $promise): void { self::$map[\spl_object_id($fiber)] = $promise; } + /** @param \Fiber $fiber */ public static function unsetPromise(\Fiber $fiber, PromiseInterface $promise): void { unset(self::$map[\spl_object_id($fiber)]); } + /** @param \Fiber $fiber */ public static function getPromise(\Fiber $fiber): ?PromiseInterface { return self::$map[\spl_object_id($fiber)] ?? null; } + /** @param \Fiber $fiber */ public static function unregister(\Fiber $fiber): void { unset(self::$status[\spl_object_id($fiber)], self::$map[\spl_object_id($fiber)]); diff --git a/src/SimpleFiber.php b/src/SimpleFiber.php index acf3fad..8c5460a 100644 --- a/src/SimpleFiber.php +++ b/src/SimpleFiber.php @@ -9,8 +9,12 @@ */ final class SimpleFiber implements FiberInterface { + /** @var ?\Fiber */ private static ?\Fiber $scheduler = null; + private static ?\Closure $suspend = null; + + /** @var ?\Fiber */ private ?\Fiber $fiber = null; public function __construct() @@ -57,13 +61,17 @@ public function suspend(): mixed self::$scheduler = new \Fiber(static fn() => Loop::run()); // Run event loop to completion on shutdown. \register_shutdown_function(static function (): void { + assert(self::$scheduler instanceof \Fiber); if (self::$scheduler->isSuspended()) { self::$scheduler->resume(); } }); } - return (self::$scheduler->isStarted() ? self::$scheduler->resume() : self::$scheduler->start())(); + $ret = (self::$scheduler->isStarted() ? self::$scheduler->resume() : self::$scheduler->start()); + assert(\is_callable($ret)); + + return $ret(); } return \Fiber::suspend(); diff --git a/src/functions.php b/src/functions.php index 12f7d03..a3ce111 100644 --- a/src/functions.php +++ b/src/functions.php @@ -176,8 +176,8 @@ * await($promise); * ``` * - * @param callable(mixed ...$args):mixed $function - * @return callable(mixed ...$args): PromiseInterface + * @param callable $function + * @return callable(mixed ...): PromiseInterface * @since 4.0.0 * @see coroutine() */ @@ -192,6 +192,7 @@ function async(callable $function): callable } catch (\Throwable $exception) { $reject($exception); } finally { + assert($fiber instanceof \Fiber); FiberMap::unregister($fiber); } }); @@ -200,6 +201,7 @@ function async(callable $function): callable $fiber->start(); }, function () use (&$fiber): void { + assert($fiber instanceof \Fiber); FiberMap::cancel($fiber); $promise = FiberMap::getPromise($fiber); if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { @@ -287,6 +289,7 @@ function (mixed $value) use (&$resolved, &$resolvedValue, &$fiber, $lowLevelFibe FiberMap::unsetPromise($lowLevelFiber, $promise); } + /** @var ?\Fiber $fiber */ if ($fiber === null) { $resolved = true; $resolvedValue = $value; @@ -309,6 +312,7 @@ function (mixed $throwable) use (&$rejected, &$rejectedThrowable, &$fiber, $lowL // what a lovely piece of code! $r = new \ReflectionProperty('Exception', 'trace'); $trace = $r->getValue($throwable); + assert(\is_array($trace)); // Exception trace arguments only available when zend.exception_ignore_args is not set // @codeCoverageIgnoreStart @@ -340,6 +344,7 @@ function (mixed $throwable) use (&$rejected, &$rejectedThrowable, &$fiber, $lowL } if ($rejected) { + assert($rejectedThrowable instanceof \Throwable); throw $rejectedThrowable; } @@ -587,7 +592,7 @@ function delay(float $seconds): void * }); * ``` * - * @param callable(mixed ...$args):\Generator $function + * @param callable(mixed ...$args):(\Generator|mixed) $function * @param mixed ...$args Optional list of additional arguments that will be passed to the given `$function` as is * @return PromiseInterface * @since 3.0.0 @@ -606,6 +611,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface $promise = null; $deferred = new Deferred(function () use (&$promise) { + /** @var ?PromiseInterface $promise */ if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { $promise->cancel(); } @@ -626,6 +632,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface return; } + /** @var mixed $promise */ $promise = $generator->current(); if (!$promise instanceof PromiseInterface) { $next = null; @@ -635,6 +642,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface return; } + assert($next instanceof \Closure); $promise->then(function ($value) use ($generator, $next) { $generator->send($value); $next(); @@ -657,6 +665,7 @@ function coroutine(callable $function, mixed ...$args): PromiseInterface */ function parallel(iterable $tasks): PromiseInterface { + /** @var array $pending */ $pending = []; $deferred = new Deferred(function () use (&$pending) { foreach ($pending as $promise) { @@ -718,6 +727,7 @@ function series(iterable $tasks): PromiseInterface { $pending = null; $deferred = new Deferred(function () use (&$pending) { + /** @var ?PromiseInterface $pending */ if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) { $pending->cancel(); } @@ -732,7 +742,7 @@ function series(iterable $tasks): PromiseInterface $taskCallback = function ($result) use (&$results, &$next) { $results[] = $result; - assert($next instanceof \Closure); + /** @var \Closure $next */ $next(); }; @@ -746,9 +756,11 @@ function series(iterable $tasks): PromiseInterface $task = $tasks->current(); $tasks->next(); } else { + assert(\is_array($tasks)); $task = \array_shift($tasks); } + assert(\is_callable($task)); $promise = \call_user_func($task); assert($promise instanceof PromiseInterface); $pending = $promise; @@ -762,13 +774,14 @@ function series(iterable $tasks): PromiseInterface } /** - * @param iterable> $tasks + * @param iterable<(callable():PromiseInterface)|(callable(mixed):PromiseInterface)> $tasks * @return PromiseInterface */ function waterfall(iterable $tasks): PromiseInterface { $pending = null; $deferred = new Deferred(function () use (&$pending) { + /** @var ?PromiseInterface $pending */ if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) { $pending->cancel(); } @@ -791,9 +804,11 @@ function waterfall(iterable $tasks): PromiseInterface $task = $tasks->current(); $tasks->next(); } else { + assert(\is_array($tasks)); $task = \array_shift($tasks); } + assert(\is_callable($task)); $promise = \call_user_func_array($task, func_get_args()); assert($promise instanceof PromiseInterface); $pending = $promise; diff --git a/tests/AsyncTest.php b/tests/AsyncTest.php index 6e07112..70a84b1 100644 --- a/tests/AsyncTest.php +++ b/tests/AsyncTest.php @@ -14,7 +14,7 @@ class AsyncTest extends TestCase { - public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsValue() + public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsValue(): void { $promise = async(function () { return 42; @@ -28,7 +28,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsV $this->assertEquals(42, $value); } - public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsPromiseThatFulfillsWithValue() + public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsPromiseThatFulfillsWithValue(): void { $promise = async(function () { return resolve(42); @@ -42,7 +42,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsP $this->assertEquals(42, $value); } - public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrows() + public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrows(): void { $promise = async(function () { throw new \RuntimeException('Foo', 42); @@ -59,7 +59,7 @@ public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrow $this->assertEquals(42, $exception->getCode()); } - public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackReturnsPromiseThatRejectsWithException() + public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackReturnsPromiseThatRejectsWithException(): void { $promise = async(function () { return reject(new \RuntimeException('Foo', 42)); @@ -76,7 +76,7 @@ public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackRetur $this->assertEquals(42, $exception->getCode()); } - public function testAsyncReturnsPendingPromiseWhenCallbackReturnsPendingPromise() + public function testAsyncReturnsPendingPromiseWhenCallbackReturnsPendingPromise(): void { $promise = async(function () { return new Promise(function () { }); @@ -85,7 +85,7 @@ public function testAsyncReturnsPendingPromiseWhenCallbackReturnsPendingPromise( $promise->then($this->expectCallableNever(), $this->expectCallableNever()); } - public function testAsyncWithAwaitReturnsReturnsPromiseFulfilledWithValueImmediatelyWhenPromiseIsFulfilled() + public function testAsyncWithAwaitReturnsReturnsPromiseFulfilledWithValueImmediatelyWhenPromiseIsFulfilled(): void { $deferred = new Deferred(); @@ -105,7 +105,7 @@ public function testAsyncWithAwaitReturnsReturnsPromiseFulfilledWithValueImmedia $this->assertEquals(42, $return); } - public function testAsyncWithAwaitReturnsPromiseRejectedWithExceptionImmediatelyWhenPromiseIsRejected() + public function testAsyncWithAwaitReturnsPromiseRejectedWithExceptionImmediatelyWhenPromiseIsRejected(): void { $deferred = new Deferred(); @@ -122,13 +122,13 @@ public function testAsyncWithAwaitReturnsPromiseRejectedWithExceptionImmediately $deferred->reject(new \RuntimeException('Test', 42)); + /** @var \RuntimeException $exception */ $this->assertInstanceof(\RuntimeException::class, $exception); - assert($exception instanceof \RuntimeException); $this->assertEquals('Test', $exception->getMessage()); $this->assertEquals(42, $exception->getCode()); } - public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingPromise() + public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingPromise(): void { $promise = async(function () { $promise = new Promise(function ($resolve) { @@ -143,7 +143,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsA $this->assertEquals(42, $value); } - public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrowsAfterAwaitingPromise() + public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrowsAfterAwaitingPromise(): void { $promise = async(function () { $promise = new Promise(function ($_, $reject) { @@ -159,7 +159,7 @@ public function testAsyncReturnsPromiseThatRejectsWithExceptionWhenCallbackThrow await($promise); } - public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingTwoConcurrentPromises() + public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsAfterAwaitingTwoConcurrentPromises(): void { $promise1 = async(function () { $promise = new Promise(function ($resolve) { @@ -174,6 +174,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsA Loop::addTimer(0.11, fn () => $resolve($theAnswerToLifeTheUniverseAndEverything)); }); + /** @var int */ return await($promise); })(42); @@ -186,7 +187,7 @@ public function testAsyncReturnsPromiseThatFulfillsWithValueWhenCallbackReturnsA $this->assertLessThan(0.12, $time); } - public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPromiseRejects() + public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPromiseRejects(): void { $promise = async(function () { await(new Promise(function () { }, function () { @@ -200,7 +201,7 @@ public function testCancelAsyncWillReturnRejectedPromiseWhenCancellingPendingPro $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Operation cancelled'))); } - public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPromiseRejectsInsideCatchThatReturnsValue() + public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPromiseRejectsInsideCatchThatReturnsValue(): void { $promise = async(function () { try { @@ -218,7 +219,7 @@ public function testCancelAsyncWillReturnFulfilledPromiseWhenCancellingPendingPr $promise->then($this->expectCallableOnceWith(42)); } - public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseRejectsInsideCatchThatAwaitsSecondPromise() + public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseRejectsInsideCatchThatAwaitsSecondPromise(): void { $promise = async(function () { try { @@ -238,7 +239,7 @@ public function testCancelAsycWillReturnPendigPromiseWhenCancellingFirstPromiseR $promise->then($this->expectCallableNever(), $this->expectCallableNever()); } - public function testCancelAsyncWillCancelNestedAwait() + public function testCancelAsyncWillCancelNestedAwait(): void { self::expectOutputString('abc'); $this->expectException(\RuntimeException::class); diff --git a/tests/AwaitTest.php b/tests/AwaitTest.php index 3333062..3158a1b 100644 --- a/tests/AwaitTest.php +++ b/tests/AwaitTest.php @@ -6,6 +6,7 @@ use React\EventLoop\Loop; use React\Promise\Deferred; use React\Promise\Promise; +use React\Promise\PromiseInterface; use function React\Async\async; class AwaitTest extends TestCase @@ -13,7 +14,7 @@ class AwaitTest extends TestCase /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException(callable $await) + public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException(callable $await): void { $promise = new Promise(function () { throw new \Exception('test'); @@ -27,7 +28,7 @@ public function testAwaitThrowsExceptionWhenPromiseIsRejectedWithException(calla /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsExceptionWithoutRunningLoop(callable $await) + public function testAwaitThrowsExceptionWithoutRunningLoop(callable $await): void { $now = true; Loop::futureTick(function () use (&$now) { @@ -48,7 +49,7 @@ public function testAwaitThrowsExceptionWithoutRunningLoop(callable $await) /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsExceptionImmediatelyWhenPromiseIsRejected(callable $await) + public function testAwaitThrowsExceptionImmediatelyWhenPromiseIsRejected(callable $await): void { $deferred = new Deferred(); @@ -72,7 +73,7 @@ public function testAwaitThrowsExceptionImmediatelyWhenPromiseIsRejected(callabl /** * @dataProvider provideAwaiters */ - public function testAwaitAsyncThrowsExceptionImmediatelyWhenPromiseIsRejected(callable $await) + public function testAwaitAsyncThrowsExceptionImmediatelyWhenPromiseIsRejected(callable $await): void { $deferred = new Deferred(); @@ -100,7 +101,7 @@ public function testAwaitAsyncThrowsExceptionImmediatelyWhenPromiseIsRejected(ca /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsExceptionImmediatelyInCustomFiberWhenPromiseIsRejected(callable $await) + public function testAwaitThrowsExceptionImmediatelyInCustomFiberWhenPromiseIsRejected(callable $await): void { $fiber = new \Fiber(function () use ($await) { $promise = new Promise(function ($resolve) { @@ -121,7 +122,7 @@ public function testAwaitThrowsExceptionImmediatelyInCustomFiberWhenPromiseIsRej /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithFalse(callable $await) + public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithFalse(callable $await): void { if (!interface_exists('React\Promise\CancellablePromiseInterface')) { $this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3'); @@ -139,7 +140,7 @@ public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWith /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithNull(callable $await) + public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWithNull(callable $await): void { if (!interface_exists('React\Promise\CancellablePromiseInterface')) { $this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3'); @@ -163,7 +164,7 @@ public function testAwaitThrowsUnexpectedValueExceptionWhenPromiseIsRejectedWith /** * @dataProvider provideAwaiters */ - public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError(callable $await) + public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError(callable $await): void { $promise = new Promise(function ($_, $reject) { throw new \Error('Test', 42); @@ -178,7 +179,7 @@ public function testAwaitThrowsErrorWhenPromiseIsRejectedWithError(callable $awa /** * @dataProvider provideAwaiters */ - public function testAwaitReturnsValueWhenPromiseIsFullfilled(callable $await) + public function testAwaitReturnsValueWhenPromiseIsFullfilled(callable $await): void { $promise = new Promise(function ($resolve) { $resolve(42); @@ -190,7 +191,7 @@ public function testAwaitReturnsValueWhenPromiseIsFullfilled(callable $await) /** * @dataProvider provideAwaiters */ - public function testAwaitReturnsValueImmediatelyWithoutRunningLoop(callable $await) + public function testAwaitReturnsValueImmediatelyWithoutRunningLoop(callable $await): void { $now = true; Loop::futureTick(function () use (&$now) { @@ -208,7 +209,7 @@ public function testAwaitReturnsValueImmediatelyWithoutRunningLoop(callable $awa /** * @dataProvider provideAwaiters */ - public function testAwaitReturnsValueImmediatelyWhenPromiseIsFulfilled(callable $await) + public function testAwaitReturnsValueImmediatelyWhenPromiseIsFulfilled(callable $await): void { $deferred = new Deferred(); @@ -229,7 +230,7 @@ public function testAwaitReturnsValueImmediatelyWhenPromiseIsFulfilled(callable /** * @dataProvider provideAwaiters */ - public function testAwaitAsyncReturnsValueImmediatelyWhenPromiseIsFulfilled(callable $await) + public function testAwaitAsyncReturnsValueImmediatelyWhenPromiseIsFulfilled(callable $await): void { $deferred = new Deferred(); @@ -254,7 +255,7 @@ public function testAwaitAsyncReturnsValueImmediatelyWhenPromiseIsFulfilled(call /** * @dataProvider provideAwaiters */ - public function testAwaitReturnsValueImmediatelyInCustomFiberWhenPromiseIsFulfilled(callable $await) + public function testAwaitReturnsValueImmediatelyInCustomFiberWhenPromiseIsFulfilled(callable $await): void { $fiber = new \Fiber(function () use ($await) { $promise = new Promise(function ($resolve) { @@ -273,7 +274,7 @@ public function testAwaitReturnsValueImmediatelyInCustomFiberWhenPromiseIsFulfil /** * @dataProvider provideAwaiters */ - public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise(callable $await) + public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise(callable $await): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); @@ -293,7 +294,7 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForResolvedPromise(c /** * @dataProvider provideAwaiters */ - public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise(callable $await) + public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise(callable $await): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); @@ -317,7 +318,7 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForRejectedPromise(c /** * @dataProvider provideAwaiters */ - public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithNullValue(callable $await) + public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithNullValue(callable $await): void { if (!interface_exists('React\Promise\CancellablePromiseInterface')) { $this->markTestSkipped('Promises must be rejected with a \Throwable instance since Promise v3'); @@ -345,7 +346,7 @@ public function testAwaitShouldNotCreateAnyGarbageReferencesForPromiseRejectedWi /** * @dataProvider provideAwaiters */ - public function testAlreadyFulfilledPromiseShouldNotSuspendFiber(callable $await) + public function testAlreadyFulfilledPromiseShouldNotSuspendFiber(callable $await): void { for ($i = 0; $i < 6; $i++) { $this->assertSame($i, $await(React\Promise\resolve($i))); @@ -355,7 +356,7 @@ public function testAlreadyFulfilledPromiseShouldNotSuspendFiber(callable $await /** * @dataProvider provideAwaiters */ - public function testNestedAwaits(callable $await) + public function testNestedAwaits(callable $await): void { $this->assertTrue($await(new Promise(function ($resolve) use ($await) { $resolve($await(new Promise(function ($resolve) use ($await) { @@ -375,10 +376,11 @@ public function testNestedAwaits(callable $await) /** * @dataProvider provideAwaiters */ - public function testResolvedPromisesShouldBeDetached(callable $await) + public function testResolvedPromisesShouldBeDetached(callable $await): void { $await(async(function () use ($await): int { $fiber = \Fiber::getCurrent(); + assert($fiber instanceof \Fiber); $await(new Promise(function ($resolve) { Loop::addTimer(0.01, fn() => $resolve(null)); })); @@ -391,13 +393,14 @@ public function testResolvedPromisesShouldBeDetached(callable $await) /** * @dataProvider provideAwaiters */ - public function testRejectedPromisesShouldBeDetached(callable $await) + public function testRejectedPromisesShouldBeDetached(callable $await): void { $this->expectException(\Exception::class); $this->expectExceptionMessage('Boom!'); $await(async(function () use ($await): int { $fiber = \Fiber::getCurrent(); + assert($fiber instanceof \Fiber); try { $await(React\Promise\reject(new \Exception('Boom!'))); } catch (\Throwable $throwable) { @@ -410,6 +413,7 @@ public function testRejectedPromisesShouldBeDetached(callable $await) })()); } + /** @return iterable> */ public function provideAwaiters(): iterable { yield 'await' => [static fn (React\Promise\PromiseInterface $promise): mixed => React\Async\await($promise)]; diff --git a/tests/CoroutineTest.php b/tests/CoroutineTest.php index 5ec4cde..c9b7439 100644 --- a/tests/CoroutineTest.php +++ b/tests/CoroutineTest.php @@ -9,7 +9,7 @@ class CoroutineTest extends TestCase { - public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsWithoutGenerator() + public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsWithoutGenerator(): void { $promise = coroutine(function () { return 42; @@ -18,10 +18,10 @@ public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsWithoutGene $promise->then($this->expectCallableOnceWith(42)); } - public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsImmediately() + public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsImmediately(): void { $promise = coroutine(function () { - if (false) { + if (false) { // @phpstan-ignore-line yield; } return 42; @@ -30,7 +30,7 @@ public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsImmediately $promise->then($this->expectCallableOnceWith(42)); } - public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsAfterYieldingPromise() + public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsAfterYieldingPromise(): void { $promise = coroutine(function () { $value = yield resolve(42); @@ -40,7 +40,7 @@ public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsAfterYieldi $promise->then($this->expectCallableOnceWith(42)); } - public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsWithoutGenerator() + public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsWithoutGenerator(): void { $promise = coroutine(function () { throw new \RuntimeException('Foo'); @@ -49,10 +49,10 @@ public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsWithoutGenera $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Foo'))); } - public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsImmediately() + public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsImmediately(): void { $promise = coroutine(function () { - if (false) { + if (false) { // @phpstan-ignore-line yield; } throw new \RuntimeException('Foo'); @@ -61,7 +61,7 @@ public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsImmediately() $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Foo'))); } - public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsAfterYieldingPromise() + public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsAfterYieldingPromise(): void { $promise = coroutine(function () { $reason = yield resolve('Foo'); @@ -71,7 +71,7 @@ public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsAfterYielding $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Foo'))); } - public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsAfterYieldingRejectedPromise() + public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsAfterYieldingRejectedPromise(): void { $promise = coroutine(function () { try { @@ -84,7 +84,7 @@ public function testCoroutineReturnsRejectedPromiseIfFunctionThrowsAfterYielding $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Foo'))); } - public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsAfterYieldingRejectedPromise() + public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsAfterYieldingRejectedPromise(): void { $promise = coroutine(function () { try { @@ -97,7 +97,7 @@ public function testCoroutineReturnsFulfilledPromiseIfFunctionReturnsAfterYieldi $promise->then($this->expectCallableOnceWith(42)); } - public function testCoroutineReturnsRejectedPromiseIfFunctionYieldsInvalidValue() + public function testCoroutineReturnsRejectedPromiseIfFunctionYieldsInvalidValue(): void { $promise = coroutine(function () { yield 42; @@ -106,7 +106,7 @@ public function testCoroutineReturnsRejectedPromiseIfFunctionYieldsInvalidValue( $promise->then(null, $this->expectCallableOnceWith(new \UnexpectedValueException('Expected coroutine to yield React\Promise\PromiseInterface, but got integer'))); } - public function testCancelCoroutineWillReturnRejectedPromiseWhenCancellingPendingPromiseRejects() + public function testCancelCoroutineWillReturnRejectedPromiseWhenCancellingPendingPromiseRejects(): void { $promise = coroutine(function () { yield new Promise(function () { }, function () { @@ -120,7 +120,7 @@ public function testCancelCoroutineWillReturnRejectedPromiseWhenCancellingPendin $promise->then(null, $this->expectCallableOnceWith(new \RuntimeException('Operation cancelled'))); } - public function testCancelCoroutineWillReturnFulfilledPromiseWhenCancellingPendingPromiseRejectsInsideCatchThatReturnsValue() + public function testCancelCoroutineWillReturnFulfilledPromiseWhenCancellingPendingPromiseRejectsInsideCatchThatReturnsValue(): void { $promise = coroutine(function () { try { @@ -138,7 +138,7 @@ public function testCancelCoroutineWillReturnFulfilledPromiseWhenCancellingPendi $promise->then($this->expectCallableOnceWith(42)); } - public function testCancelCoroutineWillReturnPendigPromiseWhenCancellingFirstPromiseRejectsInsideCatchThatYieldsSecondPromise() + public function testCancelCoroutineWillReturnPendigPromiseWhenCancellingFirstPromiseRejectsInsideCatchThatYieldsSecondPromise(): void { $promise = coroutine(function () { try { @@ -158,7 +158,7 @@ public function testCancelCoroutineWillReturnPendigPromiseWhenCancellingFirstPro $promise->then($this->expectCallableNever(), $this->expectCallableNever()); } - public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorReturns() + public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorReturns(): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); @@ -168,7 +168,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorRet gc_collect_cycles(); $promise = coroutine(function () { - if (false) { + if (false) { // @phpstan-ignore-line yield; } return 42; @@ -179,7 +179,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorRet $this->assertEquals(0, gc_collect_cycles()); } - public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithExceptionImmediately() + public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithExceptionImmediately(): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); @@ -198,7 +198,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseReject $this->assertEquals(0, gc_collect_cycles()); } - public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithExceptionOnCancellation() + public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseRejectedWithExceptionOnCancellation(): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); @@ -219,7 +219,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesForPromiseReject $this->assertEquals(0, gc_collect_cycles()); } - public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorThrowsBeforeFirstYield() + public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorThrowsBeforeFirstYield(): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); @@ -229,7 +229,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorThr $promise = coroutine(function () { throw new \RuntimeException('Failed', 42); - yield; + yield; // @phpstan-ignore-line }); unset($promise); @@ -237,7 +237,7 @@ public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorThr $this->assertEquals(0, gc_collect_cycles()); } - public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorYieldsInvalidValue() + public function testCoroutineShouldNotCreateAnyGarbageReferencesWhenGeneratorYieldsInvalidValue(): void { if (class_exists('React\Promise\When')) { $this->markTestSkipped('Not supported on legacy Promise v1 API'); diff --git a/tests/DelayTest.php b/tests/DelayTest.php index 46f0fba..2cadd6f 100644 --- a/tests/DelayTest.php +++ b/tests/DelayTest.php @@ -10,7 +10,7 @@ class DelayTest extends TestCase { - public function testDelayBlocksForGivenPeriod() + public function testDelayBlocksForGivenPeriod(): void { $time = microtime(true); delay(0.02); @@ -20,7 +20,7 @@ public function testDelayBlocksForGivenPeriod() $this->assertLessThan(0.03, $time); } - public function testDelaySmallPeriodBlocksForCloseToZeroSeconds() + public function testDelaySmallPeriodBlocksForCloseToZeroSeconds(): void { $time = microtime(true); delay(0.000001); @@ -29,7 +29,7 @@ public function testDelaySmallPeriodBlocksForCloseToZeroSeconds() $this->assertLessThan(0.01, $time); } - public function testDelayNegativePeriodBlocksForCloseToZeroSeconds() + public function testDelayNegativePeriodBlocksForCloseToZeroSeconds(): void { $time = microtime(true); delay(-1); @@ -38,7 +38,7 @@ public function testDelayNegativePeriodBlocksForCloseToZeroSeconds() $this->assertLessThan(0.01, $time); } - public function testAwaitAsyncDelayBlocksForGivenPeriod() + public function testAwaitAsyncDelayBlocksForGivenPeriod(): void { $promise = async(function () { delay(0.02); @@ -52,11 +52,13 @@ public function testAwaitAsyncDelayBlocksForGivenPeriod() $this->assertLessThan(0.03, $time); } - public function testAwaitAsyncDelayCancelledImmediatelyStopsTimerAndBlocksForCloseToZeroSeconds() + public function testAwaitAsyncDelayCancelledImmediatelyStopsTimerAndBlocksForCloseToZeroSeconds(): void { $promise = async(function () { delay(1.0); })(); + + assert(method_exists($promise, 'cancel')); $promise->cancel(); $time = microtime(true); @@ -70,11 +72,13 @@ public function testAwaitAsyncDelayCancelledImmediatelyStopsTimerAndBlocksForClo $this->assertLessThan(0.03, $time); } - public function testAwaitAsyncDelayCancelledAfterSmallPeriodStopsTimerAndBlocksUntilCancelled() + public function testAwaitAsyncDelayCancelledAfterSmallPeriodStopsTimerAndBlocksUntilCancelled(): void { $promise = async(function () { delay(1.0); })(); + + assert(method_exists($promise, 'cancel')); Loop::addTimer(0.02, fn() => $promise->cancel()); $time = microtime(true); diff --git a/tests/ParallelTest.php b/tests/ParallelTest.php index 98bbce2..37b1e10 100644 --- a/tests/ParallelTest.php +++ b/tests/ParallelTest.php @@ -6,10 +6,11 @@ use React\EventLoop\Loop; use React\Promise\Promise; use function React\Promise\reject; +use function React\Promise\resolve; class ParallelTest extends TestCase { - public function testParallelWithoutTasks() + public function testParallelWithoutTasks(): void { $tasks = array(); @@ -18,11 +19,11 @@ public function testParallelWithoutTasks() $promise->then($this->expectCallableOnceWith(array())); } - public function testParallelWithoutTasksFromEmptyGeneratorResolvesWithEmptyArray() + public function testParallelWithoutTasksFromEmptyGeneratorResolvesWithEmptyArray(): void { $tasks = (function () { - if (false) { - yield; + if (false) { // @phpstan-ignore-line + yield fn () => resolve(null); } })(); @@ -31,7 +32,7 @@ public function testParallelWithoutTasksFromEmptyGeneratorResolvesWithEmptyArray $promise->then($this->expectCallableOnceWith([])); } - public function testParallelWithTasks() + public function testParallelWithTasks(): void { $tasks = array( function () { @@ -63,7 +64,7 @@ function () { $timer->assertInRange(0.1, 0.2); } - public function testParallelWithTasksFromGeneratorResolvesWithArrayOfFulfillmentValues() + public function testParallelWithTasksFromGeneratorResolvesWithArrayOfFulfillmentValues(): void { $tasks = (function () { yield function () { @@ -95,7 +96,7 @@ public function testParallelWithTasksFromGeneratorResolvesWithArrayOfFulfillment $timer->assertInRange(0.1, 0.2); } - public function testParallelWithErrorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks() + public function testParallelWithErrorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks(): void { $called = 0; @@ -127,12 +128,12 @@ function () use (&$called) { $this->assertSame(2, $called); } - public function testParallelWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks() + public function testParallelWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks(): void { $called = 0; $tasks = (function () use (&$called) { - while (true) { + while (true) { // @phpstan-ignore-line yield function () use (&$called) { return reject(new \RuntimeException('Rejected ' . ++$called)); }; @@ -146,7 +147,7 @@ public function testParallelWithErrorFromInfiniteGeneratorReturnsPromiseRejected $this->assertSame(1, $called); } - public function testParallelWithErrorWillCancelPendingPromises() + public function testParallelWithErrorWillCancelPendingPromises(): void { $cancelled = 0; @@ -175,7 +176,7 @@ function () use (&$cancelled) { $this->assertSame(1, $cancelled); } - public function testParallelWillCancelPendingPromisesWhenCallingCancelOnResultingPromise() + public function testParallelWillCancelPendingPromisesWhenCallingCancelOnResultingPromise(): void { $cancelled = 0; @@ -199,7 +200,7 @@ function () use (&$cancelled) { $this->assertSame(2, $cancelled); } - public function testParallelWithDelayedErrorReturnsPromiseRejectedWithExceptionFromTask() + public function testParallelWithDelayedErrorReturnsPromiseRejectedWithExceptionFromTask(): void { $called = 0; diff --git a/tests/SeriesTest.php b/tests/SeriesTest.php index 0bc5017..9b20815 100644 --- a/tests/SeriesTest.php +++ b/tests/SeriesTest.php @@ -6,10 +6,11 @@ use React\EventLoop\Loop; use React\Promise\Promise; use function React\Promise\reject; +use function React\Promise\resolve; class SeriesTest extends TestCase { - public function testSeriesWithoutTasks() + public function testSeriesWithoutTasks(): void { $tasks = array(); @@ -18,11 +19,11 @@ public function testSeriesWithoutTasks() $promise->then($this->expectCallableOnceWith(array())); } - public function testSeriesWithoutTasksFromEmptyGeneratorResolvesWithEmptyArray() + public function testSeriesWithoutTasksFromEmptyGeneratorResolvesWithEmptyArray(): void { $tasks = (function () { - if (false) { - yield; + if (false) { // @phpstan-ignore-line + yield fn () => resolve(null); } })(); @@ -31,7 +32,7 @@ public function testSeriesWithoutTasksFromEmptyGeneratorResolvesWithEmptyArray() $promise->then($this->expectCallableOnceWith([])); } - public function testSeriesWithTasks() + public function testSeriesWithTasks(): void { $tasks = array( function () { @@ -63,7 +64,7 @@ function () { $timer->assertInRange(0.10, 0.20); } - public function testSeriesWithTasksFromGeneratorResolvesWithArrayOfFulfillmentValues() + public function testSeriesWithTasksFromGeneratorResolvesWithArrayOfFulfillmentValues(): void { $tasks = (function () { yield function () { @@ -95,7 +96,7 @@ public function testSeriesWithTasksFromGeneratorResolvesWithArrayOfFulfillmentVa $timer->assertInRange(0.10, 0.20); } - public function testSeriesWithError() + public function testSeriesWithError(): void { $called = 0; @@ -126,12 +127,12 @@ function () use (&$called) { $this->assertSame(1, $called); } - public function testSeriesWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks() + public function testSeriesWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks(): void { $called = 0; $tasks = (function () use (&$called) { - while (true) { + while (true) { // @phpstan-ignore-line yield function () use (&$called) { return reject(new \RuntimeException('Rejected ' . ++$called)); }; @@ -145,14 +146,14 @@ public function testSeriesWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWi $this->assertSame(1, $called); } - public function testSeriesWithErrorFromInfiniteIteratorAggregateReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks() + public function testSeriesWithErrorFromInfiniteIteratorAggregateReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks(): void { $tasks = new class() implements \IteratorAggregate { - public $called = 0; + public int $called = 0; public function getIterator(): \Iterator { - while (true) { + while (true) { // @phpstan-ignore-line yield function () { return reject(new \RuntimeException('Rejected ' . ++$this->called)); }; @@ -167,7 +168,7 @@ public function getIterator(): \Iterator $this->assertSame(1, $tasks->called); } - public function testSeriesWillCancelFirstPendingPromiseWhenCallingCancelOnResultingPromise() + public function testSeriesWillCancelFirstPendingPromiseWhenCallingCancelOnResultingPromise(): void { $cancelled = 0; diff --git a/tests/TestCase.php b/tests/TestCase.php index 904c63b..e43397d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,42 +2,39 @@ namespace React\Tests\Async; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase as BaseTestCase; class TestCase extends BaseTestCase { - protected function expectCallableOnce() + protected function expectCallableOnce(): callable { $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke'); + $mock->expects($this->once())->method('__invoke'); + assert(is_callable($mock)); return $mock; } - protected function expectCallableOnceWith($value) + protected function expectCallableOnceWith(mixed $value): callable { $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($value); + $mock->expects($this->once())->method('__invoke')->with($value); + assert(is_callable($mock)); return $mock; } - protected function expectCallableNever() + protected function expectCallableNever(): callable { $mock = $this->createCallableMock(); - $mock - ->expects($this->never()) - ->method('__invoke'); + $mock->expects($this->never())->method('__invoke'); + assert(is_callable($mock)); return $mock; } - protected function createCallableMock() + protected function createCallableMock(): MockObject { return $this->getMockBuilder(\stdClass::class)->addMethods(['__invoke'])->getMock(); } diff --git a/tests/Timer.php b/tests/Timer.php index 0a37a73..0e755a7 100644 --- a/tests/Timer.php +++ b/tests/Timer.php @@ -4,41 +4,41 @@ class Timer { - private $testCase; - private $start; - private $stop; + private TestCase $testCase; + private float $start; + private float $stop; public function __construct(TestCase $testCase) { $this->testCase = $testCase; } - public function start() + public function start(): void { $this->start = microtime(true); } - public function stop() + public function stop(): void { $this->stop = microtime(true); } - public function getInterval() + public function getInterval(): float { return $this->stop - $this->start; } - public function assertLessThan($milliseconds) + public function assertLessThan(float $milliseconds): void { $this->testCase->assertLessThan($milliseconds, $this->getInterval()); } - public function assertGreaterThan($milliseconds) + public function assertGreaterThan(float $milliseconds): void { $this->testCase->assertGreaterThan($milliseconds, $this->getInterval()); } - public function assertInRange($minMs, $maxMs) + public function assertInRange(float $minMs, float $maxMs): void { $this->assertGreaterThan($minMs); $this->assertLessThan($maxMs); diff --git a/tests/WaterfallTest.php b/tests/WaterfallTest.php index d2f947f..2b274b2 100644 --- a/tests/WaterfallTest.php +++ b/tests/WaterfallTest.php @@ -6,10 +6,11 @@ use React\EventLoop\Loop; use React\Promise\Promise; use function React\Promise\reject; +use function React\Promise\resolve; class WaterfallTest extends TestCase { - public function testWaterfallWithoutTasks() + public function testWaterfallWithoutTasks(): void { $tasks = array(); @@ -18,11 +19,11 @@ public function testWaterfallWithoutTasks() $promise->then($this->expectCallableOnceWith(null)); } - public function testWaterfallWithoutTasksFromEmptyGeneratorResolvesWithNull() + public function testWaterfallWithoutTasksFromEmptyGeneratorResolvesWithNull(): void { $tasks = (function () { - if (false) { - yield; + if (false) { // @phpstan-ignore-line + yield fn () => resolve(null); } })(); @@ -31,7 +32,7 @@ public function testWaterfallWithoutTasksFromEmptyGeneratorResolvesWithNull() $promise->then($this->expectCallableOnceWith(null)); } - public function testWaterfallWithTasks() + public function testWaterfallWithTasks(): void { $tasks = array( function ($foo = 'foo') { @@ -70,7 +71,7 @@ function ($bar) { $timer->assertInRange(0.15, 0.30); } - public function testWaterfallWithTasksFromGeneratorResolvesWithFinalFulfillmentValue() + public function testWaterfallWithTasksFromGeneratorResolvesWithFinalFulfillmentValue(): void { $tasks = (function () { yield function ($foo = 'foo') { @@ -109,7 +110,7 @@ public function testWaterfallWithTasksFromGeneratorResolvesWithFinalFulfillmentV $timer->assertInRange(0.15, 0.30); } - public function testWaterfallWithError() + public function testWaterfallWithError(): void { $called = 0; @@ -140,12 +141,12 @@ function () use (&$called) { $this->assertSame(1, $called); } - public function testWaterfallWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks() + public function testWaterfallWithErrorFromInfiniteGeneratorReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks(): void { $called = 0; $tasks = (function () use (&$called) { - while (true) { + while (true) { // @phpstan-ignore-line yield function () use (&$called) { return reject(new \RuntimeException('Rejected ' . ++$called)); }; @@ -159,14 +160,14 @@ public function testWaterfallWithErrorFromInfiniteGeneratorReturnsPromiseRejecte $this->assertSame(1, $called); } - public function testWaterfallWithErrorFromInfiniteIteratorAggregateReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks() + public function testWaterfallWithErrorFromInfiniteIteratorAggregateReturnsPromiseRejectedWithExceptionFromTaskAndStopsCallingAdditionalTasks(): void { $tasks = new class() implements \IteratorAggregate { - public $called = 0; + public int $called = 0; public function getIterator(): \Iterator { - while (true) { + while (true) { // @phpstan-ignore-line yield function () { return reject(new \RuntimeException('Rejected ' . ++$this->called)); }; @@ -181,7 +182,7 @@ public function getIterator(): \Iterator $this->assertSame(1, $tasks->called); } - public function testWaterfallWillCancelFirstPendingPromiseWhenCallingCancelOnResultingPromise() + public function testWaterfallWillCancelFirstPendingPromiseWhenCallingCancelOnResultingPromise(): void { $cancelled = 0; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy