1
0
mirror of https://github.com/danog/amp.git synced 2024-11-26 20:15:00 +01:00

Initial tests plus some fixes

This commit is contained in:
Aaron Piotrowski 2016-07-12 11:20:06 -05:00
parent 4ed8e65468
commit 3f654cbd5b
18 changed files with 1822 additions and 5 deletions

View File

@ -18,7 +18,8 @@
"async-interop/event-loop-implementation": "dev-master" "async-interop/event-loop-implementation": "dev-master"
}, },
"require-dev": { "require-dev": {
"amphp/loop": "dev-master" "amphp/loop": "dev-master",
"phpunit/phpunit": "^4|^5"
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"autoload": { "autoload": {

View File

@ -4,6 +4,7 @@ namespace Amp;
use Interop\Async\Awaitable; use Interop\Async\Awaitable;
// @codeCoverageIgnoreStart
try { try {
if (@assert(false)) { if (@assert(false)) {
production: // PHP 7 production environment (zend.assertions=0) production: // PHP 7 production environment (zend.assertions=0)
@ -75,4 +76,4 @@ try {
} }
} catch (\AssertionError $exception) { } catch (\AssertionError $exception) {
goto development; // zend.assertions=1 and assert.exception=1, use development definition. goto development; // zend.assertions=1 and assert.exception=1, use development definition.
} } // @codeCoverageIgnoreEnd

View File

@ -144,13 +144,23 @@ function capture(Awaitable $awaitable, $className, callable $functor) {
*/ */
function timeout(Awaitable $awaitable, $timeout) { function timeout(Awaitable $awaitable, $timeout) {
$deferred = new Deferred; $deferred = new Deferred;
$resolved = false;
$watcher = Loop::delay($timeout, function () use ($deferred) { $watcher = Loop::delay($timeout, function () use (&$resolved, $deferred) {
$deferred->fail(new TimeoutException); if (!$resolved) {
$resolved = true;
$deferred->fail(new TimeoutException);
}
}); });
$awaitable->when(function () use ($awaitable, $deferred, $watcher) { $awaitable->when(function () use (&$resolved, $awaitable, $deferred, $watcher) {
Loop::cancel($watcher); Loop::cancel($watcher);
if ($resolved) {
return;
}
$resolved = true;
$deferred->resolve($awaitable); $deferred->resolve($awaitable);
}); });

31
phpunit.xml.dist Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<testsuites>
<testsuite name="Amp">
<directory>test</directory>
<!-- Remove tags below once PHP 7.0 is required -->
<exclude>test/ExtendedCoroutineTest.php</exclude>
<file phpVersion="7.0.0">test/ExtendedCoroutineTest.php</file>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">lib</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="build/coverage" title="Icicle" highlight="true"/>
</logging>
</phpunit>

105
test/AdaptTest.php Normal file
View File

@ -0,0 +1,105 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Amp\Success;
use Interop\Async\Awaitable;
class PromiseMock {
/**
* @var \Interop\Async\Awaitable
*/
private $awaitable;
public function __construct(Awaitable $awaitable) {
$this->awaitable = $awaitable;
}
public function then(callable $onFulfilled = null, callable $onRejected = null) {
$this->awaitable->when(function ($exception, $value) use ($onFulfilled, $onRejected) {
if ($exception) {
if ($onRejected) {
$onRejected($exception);
}
return;
}
if ($onFulfilled) {
$onFulfilled($value);
}
});
}
}
class AdaptTest extends \PHPUnit_Framework_TestCase {
public function testThenCalled() {
$mock = $this->getMockBuilder(PromiseMock::class)
->disableOriginalConstructor()
->getMock();
$mock->expects($this->once())
->method("then")
->with(
$this->callback(function ($resolve) {
return is_callable($resolve);
}),
$this->callback(function ($reject) {
return is_callable($reject);
})
);
$awaitable = Amp\adapt($mock);
$this->assertInstanceOf(Awaitable::class, $awaitable);
}
/**
* @depends testThenCalled
*/
public function testAwaitableFulfilled() {
$value = 1;
$promise = new PromiseMock(new Success($value));
$awaitable = Amp\adapt($promise);
$awaitable->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
/**
* @depends testThenCalled
*/
public function testAwaitableRejected() {
$exception = new \Exception;
$promise = new PromiseMock(new Failure($exception));
$awaitable = Amp\adapt($promise);
$awaitable->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
$this->assertSame($exception, $reason);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testScalarValue() {
Amp\adapt(1);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testNonThenableObject() {
Amp\adapt(new \stdClass);
}
}

77
test/AllTest.php Normal file
View File

@ -0,0 +1,77 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Pause;
use Amp\Success;
use Interop\Async\Loop;
class AllTest extends \PHPUnit_Framework_TestCase {
public function testEmptyArray() {
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\all([])->when($callback);
$this->assertSame([], $result);
}
public function testSuccessfulAwaitablesArray() {
$awaitables = [new Success(1), new Success(2), new Success(3)];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\all($awaitables)->when($callback);
$this->assertSame([1, 2, 3], $result);
}
public function testPendingAwatiablesArray() {
Loop::execute(function () use (&$result) {
$awaitables = [
new Pause(0.2, 1),
new Pause(0.3, 2),
new Pause(0.1, 3),
];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\all($awaitables)->when($callback);
});
$this->assertEquals([1, 2, 3], $result);
}
public function testArrayKeysPreserved() {
$expected = ['one' => 1, 'two' => 2, 'three' => 3];
Loop::execute(function () use (&$result) {
$awaitables = [
'one' => new Pause(0.2, 1),
'two' => new Pause(0.3, 2),
'three' => new Pause(0.1, 3),
];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\all($awaitables)->when($callback);
});
$this->assertEquals($expected, $result);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testNonAwaitable() {
Amp\all([1]);
}
}

113
test/CaptureTest.php Normal file
View File

@ -0,0 +1,113 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Amp\Success;
use Interop\Async\Awaitable;
class CaptureTest extends \PHPUnit_Framework_TestCase {
public function testSuccessfulAwaitable() {
$invoked = false;
$callback = function ($exception) use (&$invoked) {
$invoked = true;
return -1;
};
$value = 1;
$awaitable = new Success($value);
$awaitable = Amp\capture($awaitable, \Exception::class, $callback);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
$awaitable->when($callback);
$this->assertFalse($invoked);
$this->assertSame($value, $result);
}
public function testFailedAwaitable() {
$invoked = false;
$callback = function ($exception) use (&$invoked, &$reason) {
$invoked = true;
$reason = $exception;
return -1;
};
$exception = new \Exception;
$awaitable = new Failure($exception);
$awaitable = Amp\capture($awaitable, \Exception::class, $callback);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
$awaitable->when($callback);
$this->assertTrue($invoked);
$this->assertSame($exception, $reason);
$this->assertSame(-1, $result);
}
/**
* @depends testFailedAwaitable
*/
public function testCallbackThrowing() {
$invoked = false;
$callback = function ($exception) use (&$invoked) {
$invoked = true;
throw new \Exception;
};
$exception = new \Exception;
$awaitable = new Failure($exception);
$awaitable = Amp\capture($awaitable, \Exception::class, $callback);
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
$awaitable->when($callback);
$this->assertTrue($invoked);
$this->assertNotSame($exception, $reason);
}
/**
* @depends testFailedAwaitable
*/
public function testUnmatchedExceptionClass() {
$invoked = false;
$callback = function ($exception) use (&$invoked, &$reason) {
$invoked = true;
$reason = $exception;
return -1;
};
$exception = new \LogicException;
$awaitable = new Failure($exception);
$awaitable = Amp\capture($awaitable, \RuntimeException::class, $callback);
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
$awaitable->when($callback);
$this->assertFalse($invoked);
$this->assertSame($exception, $reason);
}
}

539
test/CoroutineTest.php Normal file
View File

@ -0,0 +1,539 @@
<?php
namespace Icicle\Tests\Coroutine;
use Amp;
use Amp\Coroutine;
use Amp\Failure;
use Amp\InvalidYieldException;
use Amp\Pause;
use Amp\Success;
use Interop\Async\Loop;
class CoroutineTest extends \PHPUnit_Framework_TestCase {
const TIMEOUT = 0.1;
public function testYieldSuccessfulAwaitable() {
$value = 1;
$generator = function () use (&$yielded, $value) {
$yielded = (yield new Success($value));
};
$coroutine = new Coroutine($generator());
$this->assertSame($value, $yielded);
}
public function testYieldFailedAwaitable() {
$exception = new \Exception;
$generator = function () use (&$yielded, $exception) {
$yielded = (yield new Failure($exception));
};
$coroutine = new Coroutine($generator());
$this->assertNull($yielded);
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertSame($exception, $reason);
}
/**
* @depends testYieldSuccessfulAwaitable
*/
public function testYieldPendingAwaitable() {
$value = 1;
Loop::execute(function () use (&$yielded, $value) {
$generator = function () use (&$yielded, $value) {
$yielded = (yield new Pause(self::TIMEOUT, $value));
};
$coroutine = new Coroutine($generator());
});
$this->assertSame($value, $yielded);
}
/**
* @depends testYieldFailedAwaitable
*/
public function testCatchingFailedAwaitableException() {
$exception = new \Exception;
$fail = false;
$generator = function () use (&$fail, &$result, $exception) {
try {
yield new Failure($exception);
} catch (\Exception $exception) {
$result = $exception;
return;
}
$fail = true;
};
$coroutine = new Coroutine($generator());
if ($fail) {
$this->fail("Failed awaitable reason not thrown into generator");
}
}
/**
* @todo Remove once PHP 7 is required.
*/
public function testSucceedsWithCoroutineResult() {
$value = 1;
$generator = function () use ($value) {
yield Coroutine::result($value);
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
/**
* @depends testSucceedsWithCoroutineResult
* @todo Remove once PHP 7 is required.
*/
public function testSucceedsWithCoroutineAfterSuccessfulAwaitable() {
$value = 1;
$generator = function () use ($value) {
yield Coroutine::result(yield new Success($value));
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
/**
* @depends testSucceedsWithCoroutineResult
* @todo Remove once PHP 7 is required.
*/
public function testSucceedsWithCoroutineAfterFailedAwaitable() {
$value = 1;
$generator = function () use ($value) {
try {
yield new Failure(new \Exception);
} catch (\Exception $exception) {
yield Coroutine::result($value);
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
public function testInvalidYield() {
$generator = function () {
yield 1;
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertInstanceOf(InvalidYieldException::class, $reason);
}
/**
* @depends testInvalidYield
*/
public function testInvalidYieldAfterYieldAwaitable() {
$generator = function () {
yield new Success;
yield 1;
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertInstanceOf(InvalidYieldException::class, $reason);
}
/**
* @depends testInvalidYield
*/
public function testCatchesExceptionAfterInvalidYield() {
$generator = function () {
try {
yield 1;
} catch (\Exception $exception) {
yield Coroutine::result(1);
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertInstanceOf(InvalidYieldException::class, $reason);
}
/**
* @depends testSucceedsWithCoroutineResult
* @todo Remove once PHP 7 is required.
*/
public function testYieldsAfterCoroutineResult() {
$generator = function () {
yield Coroutine::result(1);
yield new Success;
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertInstanceOf(InvalidYieldException::class, $reason);
}
/**
* @depends testYieldsAfterCoroutineResult
* @todo Remove once PHP 7 is required.
*/
public function testCatchesExceptionAfterInvalidResult() {
$generator = function () {
yield Coroutine::result(1);
try {
yield new Success;
} catch (\Exception $exception) {
yield Coroutine::result(2);
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertInstanceOf(InvalidYieldException::class, $reason);
}
/**
* @depends testInvalidYield
*/
public function testThrowAfterInvalidYield() {
$exception = new \Exception;
$generator = function () use ($exception) {
try {
yield 1;
} catch (\Exception $reason) {
throw $exception;
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception) use (&$reason) {
$reason = $exception;
});
$this->assertInstanceOf(InvalidYieldException::class, $reason);
$this->assertSame($exception, $reason->getPrevious());
}
/**
* @depends testYieldFailedAwaitable
*/
public function testCatchingFailedAwaitableExceptionWithNoFurtherYields() {
$exception = new \Exception;
$generator = function () use ($exception) {
try {
yield new Failure($exception);
} catch (\Exception $exception) {
// No further yields in generator.
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertNull($result);
}
public function testGeneratorThrowingExceptionFailsCoroutine() {
$exception = new \Exception;
$generator = function () use ($exception) {
throw $exception;
yield;
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
$this->assertSame($exception, $reason);
}
/**
* @depends testGeneratorThrowingExceptionFailsCoroutine
*/
public function testGeneratorThrowingExceptionWithFinallyFailsCoroutine() {
$exception = new \Exception;
$invoked = false;
$generator = function () use (&$invoked, $exception) {
try {
throw $exception;
yield;
} finally {
$invoked = true;
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
$this->assertSame($exception, $reason);
$this->assertTrue($invoked);
}
/**
* @depends testYieldFailedAwaitable
* @depends testGeneratorThrowingExceptionWithFinallyFailsCoroutine
*/
public function testGeneratorYieldingFailedAwaitableWithFinallyFailsCoroutine() {
$exception = new \Exception;
$invoked = false;
$generator = function () use (&$invoked, $exception) {
try {
yield new Failure($exception);
} finally {
$invoked = true;
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
$this->assertSame($exception, $reason);
$this->assertTrue($invoked);
}
/**
* @depends testGeneratorThrowingExceptionFailsCoroutine
*/
public function testGeneratorThrowingExceptionAfterPendingAwaitableWithFinallyFailsCoroutine() {
$exception = new \Exception;
$value = 1;
Loop::execute(function () use (&$yielded, &$invoked, &$reason, $exception, $value) {
$invoked = false;
$generator = function () use (&$yielded, &$invoked, $exception, $value) {
try {
$yielded = (yield new Pause(self::TIMEOUT, $value));
throw $exception;
} finally {
$invoked = true;
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
});
$this->assertSame($exception, $reason);
$this->assertTrue($invoked);
$this->assertSame($value, $yielded);
}
/**
* Note that yielding in a finally block is not recommended.
*
* @depends testYieldPendingAwaitable
* @depends testGeneratorThrowingExceptionWithFinallyFailsCoroutine
*/
public function testGeneratorThrowingExceptionWithFinallyYieldingPendingAwaitable() {
$exception = new \Exception;
$value = 1;
Loop::execute(function () use (&$yielded, &$reason, $exception, $value) {
$generator = function () use (&$yielded, $exception, $value) {
try {
throw $exception;
} finally {
$yielded = (yield new Pause(self::TIMEOUT, $value));
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
});
$this->assertSame($value, $yielded);
$this->assertSame($exception, $reason);
}
/**
* @depends testYieldPendingAwaitable
* @depends testGeneratorThrowingExceptionWithFinallyFailsCoroutine
*/
public function testGeneratorThrowingExceptionWithFinallyBlockThrowing() {
$exception = new \Exception;
$generator = function () use ($exception) {
try {
throw new \Exception;
} finally {
throw $exception;
}
yield; // Unreachable, but makes function a generator.
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$reason) {
$reason = $exception;
});
$this->assertSame($exception, $reason);
}
/**
* @depends testYieldSuccessfulAwaitable
*/
public function testYieldConsecutiveSucceeded() {
$invoked = false;
Loop::execute(function () use (&$invoked) {
$count = 1000;
$awaitable = new Success;
$generator = function () use ($count, $awaitable) {
for ($i = 0; $i < $count; ++$i) {
yield $awaitable;
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$invoked) {
$invoked = true;
});
});
$this->assertTrue($invoked);
}
/**
* @depends testYieldFailedAwaitable
*/
public function testYieldConsecutiveFailed() {
$invoked = false;
Loop::execute(function () use (&$invoked) {
$count = 1000;
$awaitable = new Failure(new \Exception);
$generator = function () use ($count, $awaitable) {
for ($i = 0; $i < $count; ++$i) {
try {
yield $awaitable;
} catch (\Exception $exception) {
// Ignore and continue.
}
}
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$invoked) {
$invoked = true;
});
});
}
/**
* @depends testYieldSuccessfulAwaitable
*/
public function testFastInvalidGenerator() {
$generator = function () {
if (false) {
yield new Success;
}
};
$coroutine = new Coroutine($generator());
$invoked = false;
$coroutine->when(function ($exception, $value) use (&$invoked) {
$invoked = true;
});
$this->assertTrue($invoked);
}
public function testCoroutineFunction() {
$callable = Amp\coroutine(function () {
yield;
});
$this->assertInstanceOf(Coroutine::class, $callable());
}
/**
* @depends testCoroutineFunction
* @expectedException \LogicException
*/
public function testCoroutineFunctionWithNonGeneratorCallback() {
$callable = Amp\coroutine(function () {});
$this->assertInstanceOf(Coroutine::class, $callable());
}
}

60
test/DeferredTest.php Normal file
View File

@ -0,0 +1,60 @@
<?php
namespace Amp\Test;
use Amp\Deferred;
use Interop\Async\Awaitable;
class DeferredTest extends \PHPUnit_Framework_TestCase {
/**
* @var \Amp\Deferred
*/
private $deferred;
public function setUp() {
$this->deferred = new Deferred;
}
public function testGetAwaitable() {
$awaitable = $this->deferred->getAwaitable();
$this->assertInstanceOf(Awaitable::class, $awaitable);
}
/**
* @depends testGetAwaitable
*/
public function testResolve() {
$value = "Resolution value";
$awaitable = $this->deferred->getAwaitable();
$invoked = false;
$awaitable->when(function ($exception, $value) use (&$invoked, &$result) {
$invoked = true;
$result = $value;
});
$this->deferred->resolve($value);
$this->assertTrue($invoked);
$this->assertSame($value, $result);
}
/**
* @depends testGetAwaitable
*/
public function testFail() {
$exception = new \Exception;
$awaitable = $this->deferred->getAwaitable();
$invoked = false;
$awaitable->when(function ($exception, $value) use (&$invoked, &$result) {
$invoked = true;
$result = $exception;
});
$this->deferred->fail($exception);
$this->assertTrue($invoked);
$this->assertSame($exception, $result);
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Amp\Test;
use Amp\Coroutine;
use Amp\Success;
/**
* @todo Combine with CoroutineTest once PHP 7 is required.
*/
class ExtendedCoroutineTest extends \PHPUnit_Framework_TestCase {
public function testCoroutineResolvedWithReturn() {
$value = 1;
$generator = function () use ($value) {
return $value;
yield; // Unreachable, but makes function a coroutine.
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
/**
* @depends testCoroutineResolvedWithReturn
*/
public function testYieldFromGenerator() {
$value = 1;
$generator = function () use ($value) {
$generator = function () use ($value) {
return yield new Success($value);
};
return yield from $generator();
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
/**
* @depends testCoroutineResolvedWithReturn
*/
public function testFastReturningGenerator()
{
$value = 1;
$generator = function () use ($value) {
if (true) {
return $value;
}
yield;
return -$value;
};
$coroutine = new Coroutine($generator());
$coroutine->when(function ($exception, $value) use (&$result) {
$result = $value;
});
$this->assertSame($value, $result);
}
}

57
test/FailureTest.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace Amp\Test;
use Amp\Failure;
use Interop\Async\Loop;
class FailureTest extends \PHPUnit_Framework_TestCase {
/**
* @expectedException \LogicException
*/
public function testConstructWithNonException() {
$failure = new Failure(1);
}
public function testWhen() {
$exception = new \Exception;
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$reason) {
++$invoked;
$reason = $exception;
};
$success = new Failure($exception);
$success->when($callback);
$this->assertSame(1, $invoked);
$this->assertSame($exception, $reason);
}
/**
* @depends testWhen
*/
public function testWhenThrowingForwardsToLoopHandlerOnSuccess() {
Loop::execute(function () use (&$invoked) {
$invoked = 0;
$expected = new \Exception;
Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) {
++$invoked;
$this->assertSame($expected, $exception);
});
$callback = function () use ($expected) {
throw $expected;
};
$success = new Failure(new \Exception);
$success->when($callback);
});
$this->assertSame(1, $invoked);
}
}

312
test/FutureTest.php Normal file
View File

@ -0,0 +1,312 @@
<?php
namespace Amp\Test;
use Amp\Future;
use Interop\Async\Awaitable;
use Interop\Async\Loop;
class FutureTest extends \PHPUnit_Framework_TestCase {
/**
* @var \Amp\Future
*/
private $future;
public function setUp() {
$this->future = new Future;
}
public function testWhenOnSuccess() {
$value = "Resolution value";
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $value;
};
$this->future->when($callback);
$this->future->resolve($value);
$this->assertSame(1, $invoked);
$this->assertSame($value, $result);
}
/**
* @depends testWhenOnSuccess
*/
public function testMultipleWhensOnSuccess() {
$value = "Resolution value";
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $value;
};
$this->future->when($callback);
$this->future->when($callback);
$this->future->when($callback);
$this->future->resolve($value);
$this->assertSame(3, $invoked);
$this->assertSame($value, $result);
}
/**
* @depends testWhenOnSuccess
*/
public function testWhenAfterSuccess() {
$value = "Resolution value";
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $value;
};
$this->future->resolve($value);
$this->future->when($callback);
$this->assertSame(1, $invoked);
$this->assertSame($value, $result);
}
/**
* @depends testWhenAfterSuccess
*/
public function testMultipleWhenAfterSuccess() {
$value = "Resolution value";
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $value;
};
$this->future->resolve($value);
$this->future->when($callback);
$this->future->when($callback);
$this->future->when($callback);
$this->assertSame(3, $invoked);
$this->assertSame($value, $result);
}
/**
* @depends testWhenOnSuccess
*/
public function testWhenThrowingForwardsToLoopHandlerOnSuccess() {
Loop::execute(function () use (&$invoked) {
$invoked = 0;
$expected = new \Exception;
Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) {
++$invoked;
$this->assertSame($expected, $exception);
});
$callback = function () use ($expected) {
throw $expected;
};
$this->future->when($callback);
$this->future->resolve($expected);
});
$this->assertSame(1, $invoked);
}
/**
* @depends testWhenAfterSuccess
*/
public function testWhenThrowingForwardsToLoopHandlerAfterSuccess() {
Loop::execute(function () use (&$invoked) {
$invoked = 0;
$expected = new \Exception;
Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) {
++$invoked;
$this->assertSame($expected, $exception);
});
$callback = function () use ($expected) {
throw $expected;
};
$this->future->resolve($expected);
$this->future->when($callback);
});
$this->assertSame(1, $invoked);
}
public function testWhenOnFail() {
$exception = new \Exception;
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $exception;
};
$this->future->when($callback);
$this->future->fail($exception);
$this->assertSame(1, $invoked);
$this->assertSame($exception, $result);
}
/**
* @depends testWhenOnFail
*/
public function testMultipleWhensOnFail() {
$exception = new \Exception;
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $exception;
};
$this->future->when($callback);
$this->future->when($callback);
$this->future->when($callback);
$this->future->fail($exception);
$this->assertSame(3, $invoked);
$this->assertSame($exception, $result);
}
/**
* @depends testWhenOnFail
*/
public function testWhenAfterFail() {
$exception = new \Exception;
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $exception;
};
$this->future->fail($exception);
$this->future->when($callback);
$this->assertSame(1, $invoked);
$this->assertSame($exception, $result);
}
/**
* @depends testWhenAfterFail
*/
public function testMultipleWhensAfterFail() {
$exception = new \Exception;
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $exception;
};
$this->future->fail($exception);
$this->future->when($callback);
$this->future->when($callback);
$this->future->when($callback);
$this->assertSame(3, $invoked);
$this->assertSame($exception, $result);
}
/**
* @depends testWhenOnSuccess
*/
public function testWhenThrowingForwardsToLoopHandlerOnFail() {
Loop::execute(function () use (&$invoked) {
$invoked = 0;
$expected = new \Exception;
Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) {
++$invoked;
$this->assertSame($expected, $exception);
});
$callback = function () use ($expected) {
throw $expected;
};
$this->future->when($callback);
$this->future->fail(new \Exception);
});
$this->assertSame(1, $invoked);
}
/**
* @depends testWhenOnSuccess
*/
public function testWhenThrowingForwardsToLoopHandlerAfterFail() {
Loop::execute(function () use (&$invoked) {
$invoked = 0;
$expected = new \Exception;
Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) {
++$invoked;
$this->assertSame($expected, $exception);
});
$callback = function () use ($expected) {
throw $expected;
};
$this->future->fail(new \Exception);
$this->future->when($callback);
});
$this->assertSame(1, $invoked);
}
public function testResolveWithAwaitableBeforeWhen() {
$awaitable = $this->getMockBuilder(Awaitable::class)->getMock();
$awaitable->expects($this->once())
->method("when")
->with($this->callback("is_callable"));
$this->future->resolve($awaitable);
$this->future->when(function () {});
}
public function testResolveWithAwaitableAfterWhen() {
$awaitable = $this->getMockBuilder(Awaitable::class)->getMock();
$awaitable->expects($this->once())
->method("when")
->with($this->callback("is_callable"));
$this->future->when(function () {});
$this->future->resolve($awaitable);
}
/**
* @expectedException \LogicException
*/
public function testDoubleResolve() {
$this->future->resolve();
$this->future->resolve();
}
}

84
test/PipeTest.php Normal file
View File

@ -0,0 +1,84 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Amp\Success;
use Interop\Async\Awaitable;
class PipeTest extends \PHPUnit_Framework_TestCase {
public function testSuccessfulAwaitable() {
$invoked = false;
$callback = function ($value) use (&$invoked) {
$invoked = true;
return $value + 1;
};
$value = 1;
$awaitable = new Success($value);
$awaitable = Amp\pipe($awaitable, $callback);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
$awaitable->when($callback);
$this->assertTrue($invoked);
$this->assertSame($value + 1, $result);
}
public function testFailedAwaitable() {
$invoked = false;
$callback = function ($value) use (&$invoked) {
$invoked = true;
return $value + 1;
};
$exception = new \Exception;
$awaitable = new Failure($exception);
$awaitable = Amp\pipe($awaitable, $callback);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
$awaitable->when($callback);
$this->assertFalse($invoked);
$this->assertSame($exception, $reason);
}
/**
* @depends testSuccessfulAwaitable
*/
public function testCallbackThrowing() {
$exception = new \Exception;
$callback = function ($value) use (&$invoked, $exception) {
$invoked = true;
throw $exception;
};
$value = 1;
$awaitable = new Success($value);
$awaitable = Amp\pipe($awaitable, $callback);
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
$awaitable->when($callback);
$this->assertTrue($invoked);
$this->assertSame($exception, $reason);
}
}

26
test/RethrowTest.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Interop\Async\Loop;
class RethrowTest extends \PHPUnit_Framework_TestCase {
public function testWaitOnPendingAwaitable() {
$exception = new \Exception;
try {
Loop::execute(function () use ($exception) {
$awaitable = new Failure($exception);
Amp\rethrow($awaitable);
});
} catch (\Exception $reason) {
$this->assertSame($exception, $reason);
return;
}
$this->fail('Failed awaitable reason should be thrown from loop');
}
}

106
test/SomeTest.php Normal file
View File

@ -0,0 +1,106 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Amp\MultiReasonException;
use Amp\Pause;
use Amp\Success;
use Interop\Async\Loop;
class SomeTest extends \PHPUnit_Framework_TestCase {
/**
* @expectedException \InvalidArgumentException
*/
public function testEmptyArray() {
Amp\some([], 1);
}
public function testSuccessfulAwaitablesArray() {
$awaitables = [new Success(1), new Success(2), new Success(3)];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\some($awaitables, 2)->when($callback);
$this->assertSame([1, 2], $result);
}
public function testSuccessfulAndFailedAwaitablesArray() {
$awaitables = [new Failure(new \Exception), new Failure(new \Exception), new Success(3)];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\some($awaitables, 1)->when($callback);
$this->assertSame([2 => 3], $result);
}
public function testTooManyFailedAwaitables() {
$awaitables = [new Failure(new \Exception), new Failure(new \Exception), new Success(3)];
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
Amp\some($awaitables, 2)->when($callback);
$this->assertInstanceOf(MultiReasonException::class, $reason);
$reasons = $reason->getReasons();
foreach ($reasons as $reason) {
$this->assertInstanceOf(\Exception::class, $reason);
}
}
public function testPendingAwatiablesArray() {
Loop::execute(function () use (&$result) {
$awaitables = [
new Pause(0.2, 1),
new Pause(0.3, 2),
new Pause(0.1, 3),
];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\some($awaitables, 2)->when($callback);
});
$this->assertEquals([0 => 1, 2 => 3], $result);
}
public function testArrayKeysPreserved() {
$expected = ['one' => 1, 'two' => 2, 'three' => 3];
Loop::execute(function () use (&$result) {
$awaitables = [
'one' => new Pause(0.2, 1),
'two' => new Pause(0.3, 2),
'three' => new Pause(0.1, 3),
];
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
Amp\some($awaitables, 3)->when($callback);
});
$this->assertEquals($expected, $result);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testNonAwaitable() {
Amp\some([1], 1);
}
}

58
test/SuccessTest.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace Amp\Test;
use Amp\Success;
use Interop\Async\Awaitable;
use Interop\Async\Loop;
class SuccessTest extends \PHPUnit_Framework_TestCase {
/**
* @expectedException \LogicException
*/
public function testConstructWithNonException() {
$failure = new Success($this->getMockBuilder(Awaitable::class)->getMock());
}
public function testWhen() {
$value = "Resolution value";
$invoked = 0;
$callback = function ($exception, $value) use (&$invoked, &$result) {
++$invoked;
$result = $value;
};
$success = new Success($value);
$success->when($callback);
$this->assertSame(1, $invoked);
$this->assertSame($value, $result);
}
/**
* @depends testWhen
*/
public function testWhenThrowingForwardsToLoopHandlerOnSuccess() {
Loop::execute(function () use (&$invoked) {
$invoked = 0;
$expected = new \Exception;
Loop::setErrorHandler(function ($exception) use (&$invoked, $expected) {
++$invoked;
$this->assertSame($expected, $exception);
});
$callback = function () use ($expected) {
throw $expected;
};
$success = new Success;
$success->when($callback);
});
$this->assertSame(1, $invoked);
}
}

92
test/TimeoutTest.php Normal file
View File

@ -0,0 +1,92 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Amp\Pause;
use Amp\Success;
use Interop\Async\Awaitable;
use Interop\Async\Loop;
class TimeoutTest extends \PHPUnit_Framework_TestCase {
public function testSuccessfulAwaitable() {
Loop::execute(function () {
$value = 1;
$awaitable = new Success($value);
$awaitable = Amp\timeout($awaitable, 100);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
$awaitable->when($callback);
$this->assertSame($value, $result);
});
}
public function testFailedAwaitable() {
Loop::execute(function () {
$exception = new \Exception;
$awaitable = new Failure($exception);
$awaitable = Amp\timeout($awaitable, 100);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
$awaitable->when($callback);
$this->assertSame($exception, $reason);
});
}
/**
* @depends testSuccessfulAwaitable
*/
public function testFastPending() {
$value = 1;
Loop::execute(function () use (&$result, $value) {
$awaitable = new Pause(50, $value);
$awaitable = Amp\timeout($awaitable, 100);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$result) {
$result = $value;
};
$awaitable->when($callback);
});
$this->assertSame($value, $result);
}
/**
* @depends testSuccessfulAwaitable
*/
public function testSlowPending() {
Loop::execute(function () use (&$reason) {
$awaitable = new Pause(200);
$awaitable = Amp\timeout($awaitable, 100);
$this->assertInstanceOf(Awaitable::class, $awaitable);
$callback = function ($exception, $value) use (&$reason) {
$reason = $exception;
};
$awaitable->when($callback);
});
$this->assertInstanceOf(Amp\TimeoutException::class, $reason);
}
}

65
test/WaitTest.php Normal file
View File

@ -0,0 +1,65 @@
<?php
namespace Amp\Test;
use Amp;
use Amp\Failure;
use Amp\Future;
use Amp\Pause;
use Amp\Success;
use Interop\Async\Loop;
class WaitTest extends \PHPUnit_Framework_TestCase {
public function testWaitOnSuccessfulAwaitable()
{
$value = 1;
$awaitable = new Success($value);
$result = Amp\wait($awaitable);
$this->assertSame($value, $result);
}
public function testWaitOnFailedAwaitable()
{
$exception = new \Exception();
$awaitable = new Failure($exception);
try {
$result = Amp\wait($awaitable);
} catch (\Exception $e) {
$this->assertSame($exception, $e);
return;
}
$this->fail('Rejection exception should be thrown from wait().');
}
/**
* @depends testWaitOnSuccessfulAwaitable
*/
public function testWaitOnPendingAwaitable()
{
Loop::execute(function () {
$value = 1;
$awaitable = new Pause(100, $value);
$result = Amp\wait($awaitable);
$this->assertSame($value, $result);
});
}
/**
* @expectedException \LogicException
*/
public function testAwaitableWithNoResolutionPathThrowsException()
{
$awaitable = new Future;
$result = Amp\wait($awaitable);
}
}