From c12828081f292d5da3002d888074a423dd231680 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Tue, 14 May 2019 21:37:08 +0200 Subject: [PATCH] Fix exceptions bubbling from Coroutine::__destruct This has been an edge case potentially hiding some exceptions. The tests have been refactored to error if the loop has watchers leaking from one test to another test. --- composer.json | 1 + lib/Coroutine.php | 208 ++++++++++++++------------ lib/Internal/Placeholder.php | 3 +- test/AdaptTest.php | 2 +- test/AllTest.php | 3 +- test/AnyTest.php | 2 +- test/BaseTest.php | 45 ++++++ test/CallTest.php | 3 +- test/CallableMakerTest.php | 2 +- test/CancellationTest.php | 3 +- test/ConcatTest.php | 2 +- test/CoroutineTest.php | 21 ++- test/DeferredTest.php | 2 +- test/DelayedTest.php | 19 ++- test/FailureTest.php | 2 +- test/FilterTest.php | 3 +- test/FirstTest.php | 2 +- test/InvalidYieldErrorTest.php | 2 +- test/IteratorFromIterableTest.php | 2 +- test/IteratorToArrayTest.php | 3 +- test/LazyPromiseTest.php | 3 +- test/LoopTest.php | 9 +- test/MapTest.php | 2 +- test/MergeTest.php | 3 +- test/PlaceholderTraitTest.php | 28 ++-- test/ProducerTest.php | 3 +- test/ProducerTraitTest.php | 3 +- test/PromiseTest.php | 14 +- test/RethrowTest.php | 3 +- test/SomeTest.php | 3 +- test/StructTest.php | 2 +- test/SuccessTest.php | 2 +- test/TimeoutCancellationTokenTest.php | 3 +- test/TimeoutTest.php | 2 +- test/TimeoutWithDefaultTest.php | 2 +- test/WaitTest.php | 3 +- test/WrapTest.php | 2 +- 37 files changed, 242 insertions(+), 175 deletions(-) create mode 100644 test/BaseTest.php diff --git a/composer.json b/composer.json index c731a52..eb36afb 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "php": ">=7" }, "require-dev": { + "ext-json": "*", "amphp/phpunit-util": "^1", "amphp/php-cs-fixer-config": "dev-master", "react/promise": "^2", diff --git a/lib/Coroutine.php b/lib/Coroutine.php index 6194270..2fb165f 100644 --- a/lib/Coroutine.php +++ b/lib/Coroutine.php @@ -15,103 +15,16 @@ final class Coroutine implements Promise { use Internal\Placeholder; - /** @var \Generator */ - private $generator; - - /** @var callable(\Throwable|null $exception, mixed $value): void */ - private $onResolve; - - /** @var bool Used to control iterative coroutine continuation. */ - private $immediate = true; - - /** @var \Throwable|null Promise failure reason when executing next coroutine step, null at all other times. */ - private $exception; - - /** @var mixed Promise success value when executing next coroutine step, null at all other times. */ - private $value; - - /** - * @param \Generator $generator - */ - public function __construct(\Generator $generator) - { - $this->generator = $generator; - - try { - $yielded = $this->generator->current(); - - if (!$yielded instanceof Promise) { - if (!$this->generator->valid()) { - $this->resolve($this->generator->getReturn()); - return; - } - - $yielded = $this->transform($yielded); - } - } catch (\Throwable $exception) { - $this->fail($exception); - return; - } - - /** - * @param \Throwable|null $exception Exception to be thrown into the generator. - * @param mixed $value Value to be sent into the generator. - */ - $this->onResolve = function ($exception, $value) { - $this->exception = $exception; - $this->value = $value; - - if (!$this->immediate) { - $this->immediate = true; - return; - } - - try { - do { - if ($this->exception) { - // Throw exception at current execution point. - $yielded = $this->generator->throw($this->exception); - } else { - // Send the new value and execute to next yield statement. - $yielded = $this->generator->send($this->value); - } - - if (!$yielded instanceof Promise) { - if (!$this->generator->valid()) { - $this->resolve($this->generator->getReturn()); - $this->onResolve = null; - return; - } - - $yielded = $this->transform($yielded); - } - - $this->immediate = false; - $yielded->onResolve($this->onResolve); - } while ($this->immediate); - - $this->immediate = true; - } catch (\Throwable $exception) { - $this->fail($exception); - $this->onResolve = null; - } finally { - $this->exception = null; - $this->value = null; - } - }; - - $yielded->onResolve($this->onResolve); - } - /** * Attempts to transform the non-promise yielded from the generator into a promise, otherwise returns an instance * `Amp\Failure` failed with an instance of `Amp\InvalidYieldError`. * - * @param mixed $yielded Non-promise yielded from generator. + * @param mixed $yielded Non-promise yielded from generator. + * @param \Generator $generator No type for performance, we already know the type. * - * @return \Amp\Promise + * @return Promise */ - private function transform($yielded): Promise + private static function transform($yielded, $generator): Promise { try { if (\is_array($yielded)) { @@ -128,7 +41,7 @@ final class Coroutine implements Promise } return new Failure(new InvalidYieldError( - $this->generator, + $generator, \sprintf( "Unexpected yield; Expected an instance of %s or %s or an array of such instances", Promise::class, @@ -137,4 +50,115 @@ final class Coroutine implements Promise $exception ?? null )); } + + /** + * @param \Generator $generator + */ + public function __construct(\Generator $generator) + { + try { + $yielded = $generator->current(); + + if (!$yielded instanceof Promise) { + if (!$generator->valid()) { + $this->resolve($generator->getReturn()); + return; + } + + $yielded = self::transform($yielded, $generator); + } + } catch (\Throwable $exception) { + $this->fail($exception); + return; + } + + /** @var bool Used to control iterative coroutine continuation. */ + $immediate = true; + + /** @var \Throwable|null Promise failure reason when executing next coroutine step, null at all other times. */ + $exception = null; + + /** @var mixed Promise success value when executing next coroutine step, null at all other times. */ + $value = null; + + /** + * @param \Throwable|null $e Exception to be thrown into the generator. + * @param mixed $v Value to be sent into the generator. + */ + $onResolve = function ($e, $v) use ($generator, &$exception, &$value, &$immediate, &$onResolve) { + $exception = $e; + $value = $v; + + if (!$immediate) { + $immediate = true; + return; + } + + try { + do { + if ($exception) { + // Throw exception at current execution point. + $yielded = $generator->throw($exception); + } else { + // Send the new value and execute to next yield statement. + $yielded = $generator->send($value); + } + + if (!$yielded instanceof Promise) { + if (!$generator->valid()) { + $this->resolve($generator->getReturn()); + $onResolve = null; + return; + } + + $yielded = self::transform($yielded, $generator); + } + + $immediate = false; + $yielded->onResolve($onResolve); + } while ($immediate); + + $immediate = true; + } catch (\Throwable $exception) { + try { + $this->fail($exception); + $onResolve = null; + } catch (\Throwable $e) { + Loop::defer(static function () use ($e) { + throw $e; + }); + } + } finally { + try { + $exception = null; + $value = null; + } catch (\Throwable $e) { + Loop::defer(static function () use ($e) { + throw $e; + }); + } + } + }; + + try { + $yielded->onResolve($onResolve); + + unset($generator, $yielded, $exception, $value, $immediate, $onResolve); + } catch (\Throwable $e) { + Loop::defer(static function () use ($e) { + throw $e; + }); + } + } + + public function __destruct() + { + try { + $this->result = null; + } catch (\Throwable $e) { + Loop::defer(static function () use ($e) { + throw $e; + }); + } + } } diff --git a/lib/Internal/Placeholder.php b/lib/Internal/Placeholder.php index ec46c47..6a0936e 100644 --- a/lib/Internal/Placeholder.php +++ b/lib/Internal/Placeholder.php @@ -22,7 +22,7 @@ trait Placeholder /** @var mixed */ private $result; - /** @var callable|\Amp\Internal\ResolutionQueue|null */ + /** @var callable|ResolutionQueue|null */ private $onResolved; /** @var null|array */ @@ -128,6 +128,7 @@ trait Placeholder try { $result = $onResolved(null, $this->result); + $onResolved = null; // allow garbage collection of $onResolved, to catch any exceptions from destructors if ($result === null) { return; diff --git a/test/AdaptTest.php b/test/AdaptTest.php index 7a9eb5f..2635de1 100644 --- a/test/AdaptTest.php +++ b/test/AdaptTest.php @@ -33,7 +33,7 @@ class PromiseMock } } -class AdaptTest extends \PHPUnit\Framework\TestCase +class AdaptTest extends BaseTest { public function testThenCalled() { diff --git a/test/AllTest.php b/test/AllTest.php index 8112d17..79a2304 100644 --- a/test/AllTest.php +++ b/test/AllTest.php @@ -6,10 +6,9 @@ use Amp\Delayed; use Amp\Loop; use Amp\Promise; use Amp\Success; -use PHPUnit\Framework\TestCase; use React\Promise\FulfilledPromise; -class AllTest extends TestCase +class AllTest extends BaseTest { public function testEmptyArray() { diff --git a/test/AnyTest.php b/test/AnyTest.php index 3d20fb8..dbdb749 100644 --- a/test/AnyTest.php +++ b/test/AnyTest.php @@ -8,7 +8,7 @@ use Amp\Loop; use Amp\Promise; use Amp\Success; -class AnyTest extends \PHPUnit\Framework\TestCase +class AnyTest extends BaseTest { public function testEmptyArray() { diff --git a/test/BaseTest.php b/test/BaseTest.php new file mode 100644 index 0000000..aeb6b56 --- /dev/null +++ b/test/BaseTest.php @@ -0,0 +1,45 @@ +clearLoopRethrows(); + } + + private function clearLoopRethrows() + { + $errors = []; + + retry: + + try { + wait(new Delayed(0)); + } catch (\Error $e) { + $errors[] = (string) $e; + + goto retry; + } + + if ($errors) { + \set_error_handler(null); + \trigger_error(\implode("\n", $errors), E_USER_ERROR); + } + + $info = Loop::getInfo(); + if ($info['enabled_watchers']['referenced'] + $info['enabled_watchers']['unreferenced'] > 0) { + \set_error_handler(null); + \trigger_error("Found enabled watchers on test end: " . \json_encode($info, \JSON_PRETTY_PRINT), E_USER_ERROR); + } + } +} diff --git a/test/CallTest.php b/test/CallTest.php index 83c0489..83d5d43 100644 --- a/test/CallTest.php +++ b/test/CallTest.php @@ -9,10 +9,9 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Promise; use Amp\Success; -use PHPUnit\Framework\TestCase; use React\Promise\FulfilledPromise as FulfilledReactPromise; -class CallTest extends TestCase +class CallTest extends BaseTest { public function testCallWithFunctionReturningPromise() { diff --git a/test/CallableMakerTest.php b/test/CallableMakerTest.php index cd323dc..8e8bb89 100644 --- a/test/CallableMakerTest.php +++ b/test/CallableMakerTest.php @@ -20,7 +20,7 @@ class CallableMaker } } -class CallableMakerTest extends \PHPUnit\Framework\TestCase +class CallableMakerTest extends BaseTest { /** @var \Amp\Test\CallableMaker */ private $maker; diff --git a/test/CancellationTest.php b/test/CancellationTest.php index 5a28c9e..11726e2 100644 --- a/test/CancellationTest.php +++ b/test/CancellationTest.php @@ -6,12 +6,11 @@ use Amp\CancellationToken; use Amp\CancellationTokenSource; use Amp\Emitter; use Amp\Loop; -use Amp\PHPUnit\TestCase; use Amp\PHPUnit\TestException; use Amp\Success; use function Amp\asyncCall; -class CancellationTest extends TestCase +class CancellationTest extends BaseTest { private function createAsyncIterator(CancellationToken $cancellationToken) { diff --git a/test/ConcatTest.php b/test/ConcatTest.php index a60b6c8..0e99799 100644 --- a/test/ConcatTest.php +++ b/test/ConcatTest.php @@ -7,7 +7,7 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Producer; -class ConcatTest extends \PHPUnit\Framework\TestCase +class ConcatTest extends BaseTest { public function getArrays() { diff --git a/test/CoroutineTest.php b/test/CoroutineTest.php index ec11c40..9617670 100644 --- a/test/CoroutineTest.php +++ b/test/CoroutineTest.php @@ -10,12 +10,11 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Promise; use Amp\Success; -use PHPUnit\Framework\TestCase; use React\Promise\FulfilledPromise as FulfilledReactPromise; use React\Promise\Promise as ReactPromise; use function Amp\call; -class CoroutineTest extends TestCase +class CoroutineTest extends BaseTest { const TIMEOUT = 100; @@ -505,7 +504,23 @@ class CoroutineTest extends TestCase }); }; - new Coroutine($generator()); + try { + new Coroutine($generator()); + } catch (\Throwable $e) { + $this->fail("Caught exception that shouldn't be thrown at that place."); + } + + $this->expectExceptionObject($exception); + + try { + Promise\wait(new Delayed(0)); // make loop tick once to throw errors from loop + } catch (\Error $e) { + if ($e->getMessage() === "Loop exceptionally stopped without resolving the promise") { + throw $e->getPrevious(); + } + + throw $e; + } } /** diff --git a/test/DeferredTest.php b/test/DeferredTest.php index bb6db7d..5d10edf 100644 --- a/test/DeferredTest.php +++ b/test/DeferredTest.php @@ -5,7 +5,7 @@ namespace Amp\Test; use Amp\Deferred; use Amp\Promise; -class DeferredTest extends \PHPUnit\Framework\TestCase +class DeferredTest extends BaseTest { /** @var \Amp\Deferred */ private $deferred; diff --git a/test/DelayedTest.php b/test/DelayedTest.php index 7c79f8b..560d565 100644 --- a/test/DelayedTest.php +++ b/test/DelayedTest.php @@ -4,8 +4,9 @@ namespace Amp\Test; use Amp\Delayed; use Amp\Loop; +use function Amp\Promise\wait; -class DelayedTest extends \PHPUnit\Framework\TestCase +class DelayedTest extends BaseTest { public function testDelayed() { @@ -13,10 +14,10 @@ class DelayedTest extends \PHPUnit\Framework\TestCase $value = "test"; $start = \microtime(true); - Loop::run(function () use (&$result, $time, $value) { + Loop::run(static function () use (&$result, $time, $value) { $promise = new Delayed($time, $value); - $callback = function ($exception, $value) use (&$result) { + $callback = static function ($exception, $value) use (&$result) { $result = $value; }; @@ -34,11 +35,11 @@ class DelayedTest extends \PHPUnit\Framework\TestCase $start = \microtime(true); $invoked = false; - Loop::run(function () use (&$invoked, $time, $value) { + Loop::run(static function () use (&$invoked, $time, $value, &$promise) { $promise = new Delayed($time, $value); $promise->unreference(); - $callback = function ($exception, $value) use (&$invoked) { + $callback = static function () use (&$invoked) { $invoked = true; }; @@ -47,6 +48,10 @@ class DelayedTest extends \PHPUnit\Framework\TestCase $this->assertLessThanOrEqual($time - 1 /* 1ms grace period */, (\microtime(true) - $start) * 1000); $this->assertFalse($invoked); + + // clear watcher + $promise->reference(); + wait($promise); } /** @@ -59,12 +64,12 @@ class DelayedTest extends \PHPUnit\Framework\TestCase $start = \microtime(true); $invoked = false; - Loop::run(function () use (&$invoked, $time, $value) { + Loop::run(static function () use (&$invoked, $time, $value) { $promise = new Delayed($time, $value); $promise->unreference(); $promise->reference(); - $callback = function ($exception, $value) use (&$invoked) { + $callback = static function ($exception, $value) use (&$invoked) { $invoked = true; }; diff --git a/test/FailureTest.php b/test/FailureTest.php index edb7e4b..19e26cd 100644 --- a/test/FailureTest.php +++ b/test/FailureTest.php @@ -6,7 +6,7 @@ use Amp\Failure; use Amp\Loop; use React\Promise\RejectedPromise as RejectedReactPromise; -class FailureTest extends \PHPUnit\Framework\TestCase +class FailureTest extends BaseTest { /** * @expectedException \TypeError diff --git a/test/FilterTest.php b/test/FilterTest.php index f6e7d88..3c96fe1 100644 --- a/test/FilterTest.php +++ b/test/FilterTest.php @@ -7,9 +7,8 @@ use Amp\Iterator; use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Producer; -use PHPUnit\Framework\TestCase; -class FilterTest extends TestCase +class FilterTest extends BaseTest { public function testNoValuesEmitted() { diff --git a/test/FirstTest.php b/test/FirstTest.php index b09d09d..f6d9c2a 100644 --- a/test/FirstTest.php +++ b/test/FirstTest.php @@ -10,7 +10,7 @@ use Amp\Promise; use Amp\Success; use React\Promise\FulfilledPromise; -class FirstTest extends \PHPUnit\Framework\TestCase +class FirstTest extends BaseTest { /** * @expectedException \Error diff --git a/test/InvalidYieldErrorTest.php b/test/InvalidYieldErrorTest.php index c924693..f92a4a1 100644 --- a/test/InvalidYieldErrorTest.php +++ b/test/InvalidYieldErrorTest.php @@ -4,7 +4,7 @@ namespace Amp\Test; use Amp\InvalidYieldError; -class InvalidYieldErrorTest extends \PHPUnit\Framework\TestCase +class InvalidYieldErrorTest extends BaseTest { public function testWithInvalidGenerator() { diff --git a/test/IteratorFromIterableTest.php b/test/IteratorFromIterableTest.php index efaebca..f68364b 100644 --- a/test/IteratorFromIterableTest.php +++ b/test/IteratorFromIterableTest.php @@ -9,7 +9,7 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Success; -class IteratorFromIterableTest extends \PHPUnit\Framework\TestCase +class IteratorFromIterableTest extends BaseTest { const TIMEOUT = 10; diff --git a/test/IteratorToArrayTest.php b/test/IteratorToArrayTest.php index 217d731..0ebae3b 100644 --- a/test/IteratorToArrayTest.php +++ b/test/IteratorToArrayTest.php @@ -3,10 +3,9 @@ namespace Amp\Test; use Amp\Iterator; -use Amp\PHPUnit\TestCase; use function Amp\Promise\wait; -class IteratorToArrayTest extends TestCase +class IteratorToArrayTest extends BaseTest { public function testNonEmpty() { diff --git a/test/LazyPromiseTest.php b/test/LazyPromiseTest.php index 1f029b2..8bafa95 100644 --- a/test/LazyPromiseTest.php +++ b/test/LazyPromiseTest.php @@ -5,11 +5,10 @@ namespace Amp\Test; use Amp\Failure; use Amp\LazyPromise; use Amp\Success; -use PHPUnit\Framework\TestCase; use React\Promise\FulfilledPromise as FulfilledReactPromise; use React\Promise\RejectedPromise as RejectedReactPromise; -class LazyPromiseTest extends TestCase +class LazyPromiseTest extends BaseTest { public function testPromisorNotCalledOnConstruct() { diff --git a/test/LoopTest.php b/test/LoopTest.php index aec6a9f..178cf63 100644 --- a/test/LoopTest.php +++ b/test/LoopTest.php @@ -3,9 +3,8 @@ namespace Amp\Test; use Amp\Loop; -use PHPUnit\Framework\TestCase; -class LoopTest extends TestCase +class LoopTest extends BaseTest { public function testDelayWithNegativeDelay() { @@ -29,8 +28,9 @@ class LoopTest extends TestCase $ends = \stream_socket_pair(\stripos(PHP_OS, "win") === 0 ? STREAM_PF_INET : STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); \fwrite($ends[0], "trigger readability watcher"); - Loop::onReadable($ends[1], function () { + Loop::onReadable($ends[1], function ($watcher) { $this->assertTrue(true); + Loop::cancel($watcher); Loop::stop(); }); }); @@ -39,8 +39,9 @@ class LoopTest extends TestCase public function testOnWritable() { Loop::run(function () { - Loop::onWritable(STDOUT, function () { + Loop::onWritable(STDOUT, function ($watcher) { $this->assertTrue(true); + Loop::cancel($watcher); Loop::stop(); }); }); diff --git a/test/MapTest.php b/test/MapTest.php index 363b6aa..64f17a5 100644 --- a/test/MapTest.php +++ b/test/MapTest.php @@ -8,7 +8,7 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Producer; -class MapTest extends \PHPUnit\Framework\TestCase +class MapTest extends BaseTest { public function testNoValuesEmitted() { diff --git a/test/MergeTest.php b/test/MergeTest.php index 041b398..050b541 100644 --- a/test/MergeTest.php +++ b/test/MergeTest.php @@ -7,9 +7,8 @@ use Amp\Iterator; use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Producer; -use PHPUnit\Framework\TestCase; -class MergeTest extends TestCase +class MergeTest extends BaseTest { public function getArrays() { diff --git a/test/PlaceholderTraitTest.php b/test/PlaceholderTraitTest.php index 420c35f..6e2a177 100644 --- a/test/PlaceholderTraitTest.php +++ b/test/PlaceholderTraitTest.php @@ -13,9 +13,9 @@ class Placeholder } } -class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase +class PlaceholderTraitTest extends BaseTest { - /** @var \Amp\Test\Placeholder */ + /** @var Placeholder */ private $placeholder; public function setUp() @@ -29,8 +29,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $value; + ++$invoked; }; $this->placeholder->onResolve($callback); @@ -50,8 +50,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $value; + ++$invoked; }; $this->placeholder->onResolve($callback); @@ -73,8 +73,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $value; + ++$invoked; }; $this->placeholder->resolve($value); @@ -94,8 +94,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $value; + ++$invoked; }; $this->placeholder->resolve($value); @@ -118,8 +118,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $expected = new \Exception; Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) { - ++$invoked; $this->assertSame($expected, $exception); + ++$invoked; }); $callback = function () use ($expected) { @@ -144,8 +144,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $expected = new \Exception; Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) { - ++$invoked; $this->assertSame($expected, $exception); + ++$invoked; }); $callback = function () use ($expected) { @@ -166,8 +166,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $exception; + ++$invoked; }; $this->placeholder->onResolve($callback); @@ -187,8 +187,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $exception; + ++$invoked; }; $this->placeholder->onResolve($callback); @@ -210,8 +210,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $exception; + ++$invoked; }; $this->placeholder->fail($exception); @@ -231,8 +231,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $invoked = 0; $callback = function ($exception, $value) use (&$invoked, &$result) { - ++$invoked; $result = $exception; + ++$invoked; }; $this->placeholder->fail($exception); @@ -255,8 +255,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $expected = new \Exception; Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) { - ++$invoked; $this->assertSame($expected, $exception); + ++$invoked; }); $callback = function () use ($expected) { @@ -281,8 +281,8 @@ class PlaceholderTraitTest extends \PHPUnit\Framework\TestCase $expected = new \Exception; Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) { - ++$invoked; $this->assertSame($expected, $exception); + ++$invoked; }); $callback = function () use ($expected) { diff --git a/test/ProducerTest.php b/test/ProducerTest.php index e0a0cbb..3451627 100644 --- a/test/ProducerTest.php +++ b/test/ProducerTest.php @@ -7,9 +7,8 @@ use Amp\Delayed; use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Producer; -use PHPUnit\Framework\TestCase; -class ProducerTest extends TestCase +class ProducerTest extends BaseTest { const TIMEOUT = 100; diff --git a/test/ProducerTraitTest.php b/test/ProducerTraitTest.php index 1450a43..4767d33 100644 --- a/test/ProducerTraitTest.php +++ b/test/ProducerTraitTest.php @@ -8,7 +8,6 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Promise; use Amp\Success; -use PHPUnit\Framework\TestCase; use React\Promise\FulfilledPromise as FulfilledReactPromise; class Producer @@ -20,7 +19,7 @@ class Producer } } -class ProducerTraitTest extends TestCase +class ProducerTraitTest extends BaseTest { /** @var \Amp\Test\Producer */ private $producer; diff --git a/test/PromiseTest.php b/test/PromiseTest.php index f1a165e..394edb6 100644 --- a/test/PromiseTest.php +++ b/test/PromiseTest.php @@ -13,7 +13,7 @@ class Promise implements \Amp\Promise } } -class PromiseTest extends \PHPUnit\Framework\TestCase +class PromiseTest extends BaseTest { private $originalErrorHandler; @@ -36,18 +36,6 @@ class PromiseTest extends \PHPUnit\Framework\TestCase ]; } - public function setUp() - { - $this->originalErrorHandler = Loop::setErrorHandler(function ($e) { - throw $e; - }); - } - - public function tearDown() - { - Loop::setErrorHandler($this->originalErrorHandler); - } - public function provideSuccessValues() { return [ diff --git a/test/RethrowTest.php b/test/RethrowTest.php index 4169930..d30a3e8 100644 --- a/test/RethrowTest.php +++ b/test/RethrowTest.php @@ -5,10 +5,9 @@ namespace Amp\Test; use Amp\Failure; use Amp\Loop; use Amp\Promise; -use PHPUnit\Framework\TestCase; use function React\Promise\reject; -class RethrowTest extends TestCase +class RethrowTest extends BaseTest { public function testRethrow() { diff --git a/test/SomeTest.php b/test/SomeTest.php index fe3b491..c1537b0 100644 --- a/test/SomeTest.php +++ b/test/SomeTest.php @@ -8,10 +8,9 @@ use Amp\Loop; use Amp\MultiReasonException; use Amp\Promise; use Amp\Success; -use PHPUnit\Framework\TestCase; use React\Promise\FulfilledPromise; -class SomeTest extends TestCase +class SomeTest extends BaseTest { public function testEmptyArray() { diff --git a/test/StructTest.php b/test/StructTest.php index b41167f..7920d4f 100644 --- a/test/StructTest.php +++ b/test/StructTest.php @@ -9,7 +9,7 @@ class StructTestFixture public $_foofoofoofoofoofoofoofoobar; } -class StructTest extends \PHPUnit\Framework\TestCase +class StructTest extends BaseTest { /** * @expectedException \Error diff --git a/test/SuccessTest.php b/test/SuccessTest.php index 07c2084..f0b5b30 100644 --- a/test/SuccessTest.php +++ b/test/SuccessTest.php @@ -7,7 +7,7 @@ use Amp\Promise; use Amp\Success; use React\Promise\RejectedPromise as RejectedReactPromise; -class SuccessTest extends \PHPUnit\Framework\TestCase +class SuccessTest extends BaseTest { /** * @expectedException \Error diff --git a/test/TimeoutCancellationTokenTest.php b/test/TimeoutCancellationTokenTest.php index efc952c..8f22641 100644 --- a/test/TimeoutCancellationTokenTest.php +++ b/test/TimeoutCancellationTokenTest.php @@ -5,11 +5,10 @@ namespace Amp\Test; use Amp\CancelledException; use Amp\Delayed; use Amp\Loop; -use Amp\PHPUnit\TestCase; use Amp\TimeoutCancellationToken; use Amp\TimeoutException; -class TimeoutCancellationTokenTest extends TestCase +class TimeoutCancellationTokenTest extends BaseTest { public function testTimeout() { diff --git a/test/TimeoutTest.php b/test/TimeoutTest.php index 5760d11..cdd0617 100644 --- a/test/TimeoutTest.php +++ b/test/TimeoutTest.php @@ -9,7 +9,7 @@ use Amp\Promise; use Amp\Success; use function React\Promise\resolve; -class TimeoutTest extends \PHPUnit\Framework\TestCase +class TimeoutTest extends BaseTest { public function testSuccessfulPromise() { diff --git a/test/TimeoutWithDefaultTest.php b/test/TimeoutWithDefaultTest.php index 48702ac..1cfecdf 100644 --- a/test/TimeoutWithDefaultTest.php +++ b/test/TimeoutWithDefaultTest.php @@ -9,7 +9,7 @@ use Amp\Promise; use Amp\Success; use function React\Promise\resolve; -class TimeoutWithDefaultTest extends \PHPUnit\Framework\TestCase +class TimeoutWithDefaultTest extends BaseTest { public function testSuccessfulPromise() { diff --git a/test/WaitTest.php b/test/WaitTest.php index fb2687e..0ae0c3f 100644 --- a/test/WaitTest.php +++ b/test/WaitTest.php @@ -9,10 +9,9 @@ use Amp\Loop; use Amp\PHPUnit\TestException; use Amp\Promise; use Amp\Success; -use PHPUnit\Framework\TestCase; use function React\Promise\resolve; -class WaitTest extends TestCase +class WaitTest extends BaseTest { public function testWaitOnSuccessfulPromise() { diff --git a/test/WrapTest.php b/test/WrapTest.php index 5db3863..42d3d8d 100644 --- a/test/WrapTest.php +++ b/test/WrapTest.php @@ -5,7 +5,7 @@ namespace Amp\Test; use Amp\Deferred; use Amp\Promise; -class WrapTest extends \PHPUnit\Framework\TestCase +class WrapTest extends BaseTest { public function testSuccess() {