1
0
mirror of https://github.com/danog/amp.git synced 2024-12-11 00:49:48 +01:00
amp/test/CancellationTest.php
Niklas Keller c12828081f 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.
2019-05-31 11:38:02 -05:00

154 lines
4.2 KiB
PHP

<?php
namespace Amp\Test;
use Amp\CancellationToken;
use Amp\CancellationTokenSource;
use Amp\Emitter;
use Amp\Loop;
use Amp\PHPUnit\TestException;
use Amp\Success;
use function Amp\asyncCall;
class CancellationTest extends BaseTest
{
private function createAsyncIterator(CancellationToken $cancellationToken)
{
$emitter = new Emitter;
asyncCall(function () use ($emitter, $cancellationToken) {
$running = true;
$cancellationToken->subscribe(function () use (&$running) {
$running = false;
});
$i = 0;
while ($running) {
yield $emitter->emit($i++);
}
});
return $emitter->iterate();
}
public function testCancellationCancelsIterator()
{
Loop::run(function () {
$cancellationSource = new CancellationTokenSource;
$iterator = $this->createAsyncIterator($cancellationSource->getToken());
$current = null;
while (yield $iterator->advance()) {
$current = $iterator->getCurrent();
$this->assertInternalType("int", $current);
if ($current === 3) {
$cancellationSource->cancel();
}
}
$this->assertSame(3, $current);
});
}
public function testUnsubscribeWorks()
{
Loop::run(function () {
$cancellationSource = new CancellationTokenSource;
$id = $cancellationSource->getToken()->subscribe(function () {
$this->fail("Callback has been called");
});
$cancellationSource->getToken()->subscribe(function () {
$this->assertTrue(true);
});
$cancellationSource->getToken()->unsubscribe($id);
$cancellationSource->cancel();
});
}
public function testSubscriptionsRunAsCoroutine()
{
$this->expectOutputString("abc");
Loop::run(function () {
$cancellationSource = new CancellationTokenSource;
$cancellationSource->getToken()->subscribe(function () {
print yield new Success("a");
print yield new Success("b");
print yield new Success("c");
});
$cancellationSource->cancel();
});
}
public function testThrowingCallbacksEndUpInLoop()
{
Loop::run(function () {
$this->expectException(TestException::class);
$cancellationSource = new CancellationTokenSource;
$cancellationSource->getToken()->subscribe(function () {
throw new TestException;
});
try {
$cancellationSource->cancel();
} catch (TestException $e) {
$this->fail("Exception thrown from cancel instead of being thrown into the loop.");
}
});
}
public function testThrowingCallbacksEndUpInLoopIfCoroutine()
{
Loop::run(function () {
$this->expectException(TestException::class);
$cancellationSource = new CancellationTokenSource;
$cancellationSource->getToken()->subscribe(function () {
if (false) {
yield;
}
throw new TestException;
});
try {
$cancellationSource->cancel();
} catch (TestException $e) {
$this->fail("Exception thrown from cancel instead of being thrown into the loop.");
}
});
}
public function testDoubleCancelOnlyInvokesOnce()
{
Loop::run(function () {
$cancellationSource = new CancellationTokenSource;
$cancellationSource->getToken()->subscribe($this->createCallback(1));
$cancellationSource->cancel();
$cancellationSource->cancel();
});
}
public function testCalledIfSubscribingAfterCancel()
{
Loop::run(function () {
$cancellationSource = new CancellationTokenSource;
$cancellationSource->cancel();
$cancellationSource->getToken()->subscribe($this->createCallback(1));
});
}
}