diff --git a/tests/PromiseTest.php b/tests/PromiseTest.php index 344b4114..aa858c4f 100644 --- a/tests/PromiseTest.php +++ b/tests/PromiseTest.php @@ -202,6 +202,16 @@ public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPro $this->assertSame(0, gc_collect_cycles()); } + /** @test */ + public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithCanceller() + { + gc_collect_cycles(); + $promise = new Promise(function () { }, function () { }); + unset($promise); + + $this->assertSame(0, gc_collect_cycles()); + } + /** @test */ public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithThenFollowers() { @@ -213,6 +223,29 @@ public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPro $this->assertSame(0, gc_collect_cycles()); } + /** @test */ + public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithCancellerAndThenFollowers() + { + $this->markTestIncomplete('This requires a cyclic dependency by design. You should explicitly cancel() instead of leaving garbage references.'); + + // Having a cancellation handler and creating a follower creates a cyclic reference by design: + // - $promise holds a reference to $follower to notify when settling + // - $follower holds a reference to $promise to forward cancellation to parent + // + // There are a number of ways to avoid this cyclic reference in consumer code: + // - Ensure the $promise is fulfilled which will clear the reference to its followers + // - Do not create a $follower when it is not needed in the first place + // - Explicitly cancel() $follower when its operation should be cancelled + // - Do not use a cancellation handler when followers are commonly discarded (not recommended) + + gc_collect_cycles(); + $promise = new Promise(function () { }, function () { }); + $promise->then(); + unset($promise); + + $this->assertSame(0, gc_collect_cycles()); + } + /** @test */ public function shouldNotLeaveGarbageCyclesWhenRemovingLastReferenceToPendingPromiseWithDoneFollowers() {