1<?php
2
3namespace React\Promise;
4
5use React\Promise\PromiseAdapter\CallbackPromiseAdapter;
6
7class DeferredTest extends TestCase
8{
9    use PromiseTest\FullTestTrait;
10
11    public function getPromiseTestAdapter(callable $canceller = null)
12    {
13        $d = new Deferred($canceller);
14
15        return new CallbackPromiseAdapter([
16            'promise' => [$d, 'promise'],
17            'resolve' => [$d, 'resolve'],
18            'reject'  => [$d, 'reject'],
19            'notify'  => [$d, 'progress'],
20            'settle'  => [$d, 'resolve'],
21        ]);
22    }
23
24    /** @test */
25    public function progressIsAnAliasForNotify()
26    {
27        $deferred = new Deferred();
28
29        $sentinel = new \stdClass();
30
31        $mock = $this->createCallableMock();
32        $mock
33            ->expects($this->once())
34            ->method('__invoke')
35            ->with($sentinel);
36
37        $deferred->promise()
38            ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock);
39
40        $deferred->progress($sentinel);
41    }
42
43    /** @test */
44    public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithException()
45    {
46        gc_collect_cycles();
47        $deferred = new Deferred(function ($resolve, $reject) {
48            $reject(new \Exception('foo'));
49        });
50        $deferred->promise()->cancel();
51        unset($deferred);
52
53        $this->assertSame(0, gc_collect_cycles());
54    }
55
56    /** @test */
57    public function shouldRejectWithoutCreatingGarbageCyclesIfParentCancellerRejectsWithException()
58    {
59        gc_collect_cycles();
60        $deferred = new Deferred(function ($resolve, $reject) {
61            $reject(new \Exception('foo'));
62        });
63        $deferred->promise()->then()->cancel();
64        unset($deferred);
65
66        $this->assertSame(0, gc_collect_cycles());
67    }
68
69    /** @test */
70    public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndExplicitlyRejectWithException()
71    {
72        gc_collect_cycles();
73        $deferred = new Deferred(function () use (&$deferred) { });
74        $deferred->reject(new \Exception('foo'));
75        unset($deferred);
76
77        $this->assertSame(0, gc_collect_cycles());
78    }
79
80    /** @test */
81    public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingDeferred()
82    {
83        gc_collect_cycles();
84        $deferred = new Deferred();
85        $deferred->promise();
86        unset($deferred);
87
88        $this->assertSame(0, gc_collect_cycles());
89    }
90
91    /** @test */
92    public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingDeferredWithUnusedCanceller()
93    {
94        gc_collect_cycles();
95        $deferred = new Deferred(function () { });
96        $deferred->promise();
97        unset($deferred);
98
99        $this->assertSame(0, gc_collect_cycles());
100    }
101
102    /** @test */
103    public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingDeferredWithNoopCanceller()
104    {
105        gc_collect_cycles();
106        $deferred = new Deferred(function () { });
107        $deferred->promise()->cancel();
108        unset($deferred);
109
110        $this->assertSame(0, gc_collect_cycles());
111    }
112}
113