Skip to content

Commit

Permalink
Add uncancellable() to Awaitable
Browse files Browse the repository at this point in the history
uncancellable() returns an awaitable that cannot be cancelled.
  • Loading branch information
trowski committed Nov 18, 2015
1 parent 58287ad commit fdf6b75
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/Awaitable/Awaitable.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ public function cleanup(callable $onResolved);
*/
public function splat(callable $onFulfilled);

/**
* Returns an awaitable that will be resolved in the same way as this awaitable but cannot be cancelled.
*
* @return \Icicle\Awaitable\Awaitable
*/
public function uncancellable();

/**
* This function may be used to synchronously wait for a promise to be resolved. This function should generally
* not be used within a running event loop, but rather to set up a task (or set of tasks, then use join() or another
Expand Down
12 changes: 12 additions & 0 deletions src/Awaitable/Future.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,18 @@ public function delay($time)
return $future;
}

/**
* {@inheritdoc}
*/
public function uncancellable()
{
if (null !== $this->result) {
return $this->unwrap()->uncancellable();
}

return new Internal\UncancellableAwaitable($this);
}

/**
* {@inheritdoc}
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Awaitable/Internal/LazyAwaitable.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ public function isCancelled()
return $this->getPromise()->isCancelled();
}

/**
* {@inheritdoc}
*/
public function uncancellable()
{
return new UncancellableAwaitable($this);
}

/**
* {@inheritdoc}
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Awaitable/Internal/ResolvedAwaitable.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,12 @@ public function delay($time)
{
return $this;
}

/**
* {@inheritdoc}
*/
public function uncancellable()
{
return $this;
}
}
115 changes: 115 additions & 0 deletions src/Awaitable/Internal/UncancellableAwaitable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

/*
* This file is part of Icicle, a library for writing asynchronous code in PHP using promises and coroutines.
*
* @copyright 2014-2015 Aaron Piotrowski. All rights reserved.
* @license MIT See the LICENSE file that was distributed with this source code for more information.
*/

namespace Icicle\Awaitable\Internal;

use Icicle\Awaitable\Awaitable;

class UncancellableAwaitable implements Awaitable
{
use AwaitableMethods;

/**
* @var \Icicle\Awaitable\Awaitable
*/
private $awaitable;

/**
* @param \Icicle\Awaitable\Awaitable $awaitable
*/
public function __construct(Awaitable $awaitable)
{
$this->awaitable = $awaitable;
}

/**
* {@inheritdoc}
*/
public function cancel($reason = null) {}

/**
* {@inheritdoc}
*/
public function timeout($timeout, $reason = null)
{
return $this;
}

/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
return $this->awaitable->then($onFulfilled, $onRejected);
}

/**
* {@inheritdoc}
*/
public function done(callable $onFulfilled = null, callable $onRejected = null)
{
$this->awaitable->done($onFulfilled, $onRejected);
}

/**
* {@inheritdoc}
*/
public function delay($time)
{
return $this->awaitable->delay($time);
}

/**
* {@inheritdoc}
*/
public function uncancellable()
{
return $this;
}

/**
* {@inheritdoc}
*/
public function wait()
{
return $this->awaitable->wait();
}

/**
* {@inheritdoc}
*/
public function isPending()
{
return $this->awaitable->isPending();
}

/**
* {@inheritdoc}
*/
public function isFulfilled()
{
return $this->awaitable->isFulfilled();
}

/**
* {@inheritdoc}
*/
public function isRejected()
{
return $this->awaitable->isRejected();
}

/**
* {@inheritdoc}
*/
public function isCancelled()
{
return $this->awaitable->isCancelled();
}
}
126 changes: 126 additions & 0 deletions tests/Awaitable/Internal/UncancellableAwaitableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

/*
* This file is part of Icicle, a library for writing asynchronous code in PHP using promises and coroutines.
*
* @copyright 2014-2015 Aaron Piotrowski. All rights reserved.
* @license MIT See the LICENSE file that was distributed with this source code for more information.
*/

namespace Icicle\Tests\Awaitable\Internal;

use Icicle\Awaitable\Awaitable;
use Icicle\Awaitable\Delayed;
use Icicle\Awaitable\Internal\UncancellableAwaitable;
use Icicle\Loop;
use Icicle\Loop\SelectLoop;
use Icicle\Tests\TestCase;

/**
* Tests the constructor only. All other methods are covered by PromiseTest.
*/
class UncancellableAwaitableTest extends TestCase
{
const TIMEOUT = 0.1;

/**
* @var \Icicle\Awaitable\Delayed
*/
private $delayed;

/**
* @var \Icicle\Awaitable\Internal\UncancellableAwaitable
*/
private $uncancellable;

public function setUp()
{
Loop\loop(new SelectLoop());

$this->delayed = new Delayed();
$this->uncancellable = new UncancellableAwaitable($this->delayed);
}

public function testResolution()
{
$this->assertTrue($this->uncancellable->isPending());
$this->assertFalse($this->uncancellable->isFulfilled());
$this->assertFalse($this->uncancellable->isRejected());

$this->delayed->resolve();

$this->assertFalse($this->uncancellable->isPending());
$this->assertTrue($this->uncancellable->isFulfilled());
$this->assertFalse($this->uncancellable->isRejected());
$this->assertFalse($this->uncancellable->isCancelled());
}

public function testThen()
{
$value = 1;

$callback = $this->createCallback(1);
$callback->method('__invoke')
->with($this->identicalTo($value))
->will($this->returnValue($value));

$awaitable = $this->uncancellable->then($callback);

$this->assertInstanceOf(Awaitable::class, $awaitable);

$this->delayed->resolve($value);

Loop\run();
}

public function testDone()
{
$value = 1;

$callback = $this->createCallback(1);
$callback->method('__invoke')
->with($this->identicalTo($value));

$this->uncancellable->done($callback);

$this->delayed->resolve($value);

Loop\run();
}

public function testTimeout()
{
$awaitable = $this->uncancellable->timeout(self::TIMEOUT);

$timer = Loop\timer(self::TIMEOUT * 2, $this->createCallback(1));

Loop\run();

$this->assertTrue($awaitable->isPending());
}

public function testDelay()
{
$awaitable = $this->uncancellable->delay(self::TIMEOUT);

$this->delayed->resolve();

$this->assertRunTimeGreaterThan('Icicle\Loop\run', self::TIMEOUT - self::RUNTIME_PRECISION);

$this->assertTrue($awaitable->isFulfilled());
}

public function testUncancellable()
{
$this->assertSame($this->uncancellable, $this->uncancellable->uncancellable());
}

public function testWait()
{
$value = 1;

$this->delayed->resolve($value);

$this->assertSame($value, $this->uncancellable->wait());
}
}
20 changes: 20 additions & 0 deletions tests/Awaitable/LazyAwaitableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,26 @@ public function testPromisorCalledWhenTimeoutCalled()

$this->assertTrue($called);
}

/**
* @depends testPromisorNotCalledOnConstruct
*/
public function testPromisorNotCalledWhenUncancellableCalled()
{
$called = false;

$promisor = function () use (&$called) {
$called = true;
};

$lazy = Awaitable\lazy($promisor);

$this->assertFalse($called);

$awaitable = $lazy->uncancellable();

$this->assertFalse($called);
}

/**
* @depends testPromisorCalledWhenThenCalled
Expand Down
41 changes: 41 additions & 0 deletions tests/Awaitable/PromiseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2632,4 +2632,45 @@ public function testPromiseWithNoResolutionPathThrowsException()

$result = $promise->wait();
}

/**
* @depends testCancellation
*/
public function testUncancellable()
{
$promise = $this->promise->uncancellable();

$promise->cancel();

$this->assertFalse($this->promise->isCancelled());
$this->assertTrue($promise->isPending());
}

/**
* @depends testUncancellable
*/
public function testUncancellableAfterResolve()
{
$value = 1;

$this->resolve($value);

$promise = $this->promise->uncancellable();

$this->assertFalse($promise->isPending());
$this->assertTrue($promise->isFulfilled());
$this->assertSame($value, $promise->wait());
}

/**
* @depends testUncancellable
*/
public function testUncancellableAfterCancel()
{
$this->promise->cancel();

$promise = $this->promise->uncancellable();

$this->assertTrue($promise->isCancelled());
}
}

0 comments on commit fdf6b75

Please sign in to comment.