294 lines
8.6 KiB
PHP
Executable File
294 lines
8.6 KiB
PHP
Executable File
<?php
|
|
|
|
namespace React\Promise;
|
|
|
|
use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
|
|
|
|
class PromiseTest extends TestCase
|
|
{
|
|
use PromiseTest\FullTestTrait;
|
|
|
|
public function getPromiseTestAdapter(callable $canceller = null)
|
|
{
|
|
$resolveCallback = $rejectCallback = $progressCallback = null;
|
|
|
|
$promise = new Promise(function ($resolve, $reject, $progress) use (&$resolveCallback, &$rejectCallback, &$progressCallback) {
|
|
$resolveCallback = $resolve;
|
|
$rejectCallback = $reject;
|
|
$progressCallback = $progress;
|
|
}, $canceller);
|
|
|
|
return new CallbackPromiseAdapter([
|
|
'promise' => function () use ($promise) {
|
|
return $promise;
|
|
},
|
|
'resolve' => $resolveCallback,
|
|
'reject' => $rejectCallback,
|
|
'notify' => $progressCallback,
|
|
'settle' => $resolveCallback,
|
|
]);
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectIfResolverThrowsException()
|
|
{
|
|
$exception = new \Exception('foo');
|
|
|
|
$promise = new Promise(function () use ($exception) {
|
|
throw $exception;
|
|
});
|
|
|
|
$mock = $this->createCallableMock();
|
|
$mock
|
|
->expects($this->once())
|
|
->method('__invoke')
|
|
->with($this->identicalTo($exception));
|
|
|
|
$promise
|
|
->then($this->expectCallableNever(), $mock);
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldResolveWithoutCreatingGarbageCyclesIfResolverResolvesWithException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function ($resolve) {
|
|
$resolve(new \Exception('foo'));
|
|
});
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfResolverThrowsExceptionWithoutResolver()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () {
|
|
throw new \Exception('foo');
|
|
});
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfResolverRejectsWithException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function ($resolve, $reject) {
|
|
$reject(new \Exception('foo'));
|
|
});
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function ($resolve, $reject) { }, function ($resolve, $reject) {
|
|
$reject(new \Exception('foo'));
|
|
});
|
|
$promise->cancel();
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfParentCancellerRejectsWithException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function ($resolve, $reject) { }, function ($resolve, $reject) {
|
|
$reject(new \Exception('foo'));
|
|
});
|
|
$promise->then()->then()->then()->cancel();
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfResolverThrowsException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function ($resolve, $reject) {
|
|
throw new \Exception('foo');
|
|
});
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/**
|
|
* Test that checks number of garbage cycles after throwing from a canceller
|
|
* that explicitly uses a reference to the promise. This is rather synthetic,
|
|
* actual use cases often have implicit (hidden) references which ought not
|
|
* to be stored in the stack trace.
|
|
*
|
|
* Reassigned arguments only show up in the stack trace in PHP 7, so we can't
|
|
* avoid this on legacy PHP. As an alternative, consider explicitly unsetting
|
|
* any references before throwing.
|
|
*
|
|
* @test
|
|
* @requires PHP 7
|
|
*/
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerWithReferenceThrowsException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () {}, function () use (&$promise) {
|
|
throw new \Exception('foo');
|
|
});
|
|
$promise->cancel();
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* @requires PHP 7
|
|
* @see self::shouldRejectWithoutCreatingGarbageCyclesIfCancellerWithReferenceThrowsException
|
|
*/
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfResolverWithReferenceThrowsException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () use (&$promise) {
|
|
throw new \Exception('foo');
|
|
});
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/**
|
|
* @test
|
|
* @requires PHP 7
|
|
* @see self::shouldRejectWithoutCreatingGarbageCyclesIfCancellerWithReferenceThrowsException
|
|
*/
|
|
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndResolverThrowsException()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () {
|
|
throw new \Exception('foo');
|
|
}, function () use (&$promise) { });
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldIgnoreNotifyAfterReject()
|
|
{
|
|
$promise = new Promise(function () { }, function ($resolve, $reject, $notify) {
|
|
$reject(new \Exception('foo'));
|
|
$notify(42);
|
|
});
|
|
|
|
$promise->then(null, null, $this->expectCallableNever());
|
|
$promise->cancel();
|
|
}
|
|
|
|
|
|
/** @test */
|
|
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromise()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () { });
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithThenFollowers()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () { });
|
|
$promise->then()->then()->then();
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithDoneFollowers()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () { });
|
|
$promise->done();
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithOtherwiseFollowers()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () { });
|
|
$promise->otherwise(function () { });
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithAlwaysFollowers()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () { });
|
|
$promise->always(function () { });
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithProgressFollowers()
|
|
{
|
|
gc_collect_cycles();
|
|
$promise = new Promise(function () { });
|
|
$promise->then(null, null, function () { });
|
|
unset($promise);
|
|
|
|
$this->assertSame(0, gc_collect_cycles());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldFulfillIfFullfilledWithSimplePromise()
|
|
{
|
|
$adapter = $this->getPromiseTestAdapter();
|
|
|
|
$mock = $this->createCallableMock();
|
|
$mock
|
|
->expects($this->once())
|
|
->method('__invoke')
|
|
->with($this->identicalTo('foo'));
|
|
|
|
$adapter->promise()
|
|
->then($mock);
|
|
|
|
$adapter->resolve(new SimpleFulfilledTestPromise());
|
|
}
|
|
|
|
/** @test */
|
|
public function shouldRejectIfRejectedWithSimplePromise()
|
|
{
|
|
$adapter = $this->getPromiseTestAdapter();
|
|
|
|
$mock = $this->createCallableMock();
|
|
$mock
|
|
->expects($this->once())
|
|
->method('__invoke')
|
|
->with($this->identicalTo('foo'));
|
|
|
|
$adapter->promise()
|
|
->then($this->expectCallableNever(), $mock);
|
|
|
|
$adapter->resolve(new SimpleRejectedTestPromise());
|
|
}
|
|
}
|