mirror of
https://github.com/danog/amp.git
synced 2025-01-21 21:01:16 +01:00
Update tests and remove unused tests
This commit is contained in:
parent
dad93a6da0
commit
5a06b1cdc1
12
phpunit.xml
12
phpunit.xml
@ -1,12 +0,0 @@
|
||||
<phpunit bootstrap="./test/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Tests">
|
||||
<directory>./test</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory>./lib</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
abstract class BaseTest extends \PHPUnit_Framework_TestCase {
|
||||
protected function setUp() {
|
||||
\Amp\reactor($assign = new \Amp\NativeReactor);
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\EvReactor;
|
||||
|
||||
class EvReactorTest extends ReactorTest {
|
||||
protected function setUp() {
|
||||
if (extension_loaded("ev")) {
|
||||
\Amp\reactor($assign = new EvReactor);
|
||||
} else {
|
||||
$this->markTestSkipped(
|
||||
"ev extension not loaded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetLoop() {
|
||||
$result = \Amp\reactor()->getLoop();
|
||||
$this->assertInstanceOf("EvLoop", $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* We take care to cancel the signal watcher because Ev spazzes if
|
||||
* multiple watchers exist for the same signal in different loops
|
||||
*/
|
||||
public function testOnSignalWatcherKeepAliveRunResult() {
|
||||
if (!\extension_loaded("pcntl")) {
|
||||
$this->markTestSkipped("ext/pcntl required to test onSignal() registration");
|
||||
}
|
||||
|
||||
$watcherId = null;
|
||||
\Amp\run(function () use (&$watcherId) {
|
||||
$watcherId = \Amp\onSignal(SIGUSR1, function () {
|
||||
// empty
|
||||
}, $options = ["keep_alive" => false]);
|
||||
});
|
||||
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
|
||||
public function testImmediateCoroutineResolutionError() {
|
||||
if (\extension_loaded("xdebug")) {
|
||||
$this->markTestSkipped(
|
||||
"Cannot run this test with xdebug enabled: it causes zend_mm_heap corrupted"
|
||||
);
|
||||
} else {
|
||||
parent::testImmediateCoroutineResolutionError();
|
||||
}
|
||||
}
|
||||
|
||||
public function testOnErrorFailure() {
|
||||
if (\extension_loaded("xdebug")) {
|
||||
$this->markTestSkipped(
|
||||
"Cannot run this test with xdebug enabled: it causes zend_mm_heap corrupted"
|
||||
);
|
||||
} else {
|
||||
parent::testImmediateCoroutineResolutionError();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,905 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\CombinatorException;
|
||||
use Amp\NativeReactor;
|
||||
use Amp\Success;
|
||||
use Amp\Failure;
|
||||
use Amp\Deferred;
|
||||
use Amp\PromiseStream;
|
||||
use Amp\Pause;
|
||||
use Amp\CoroutineResult;
|
||||
|
||||
class FunctionsTest extends \PHPUnit_Framework_TestCase {
|
||||
protected function setUp() {
|
||||
\Amp\reactor($assign = new NativeReactor);
|
||||
}
|
||||
|
||||
public function testMap() {
|
||||
$promises = ["test1", new Success("test2"), new Success("test3")];
|
||||
$functor = "strtoupper";
|
||||
$promise = \Amp\map($promises, $functor);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertNull($error);
|
||||
$this->assertSame(["TEST1", "TEST2", "TEST3"], $result);
|
||||
}
|
||||
|
||||
public function testMapReturnsEmptySuccessOnEmptyInput() {
|
||||
$promise = \Amp\map([], function () {});
|
||||
$this->assertInstanceOf('Amp\Success', $promise);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessage hulk smash
|
||||
*/
|
||||
public function testMapFailsIfFunctorThrowsOnUnwrappedValue() {
|
||||
\Amp\run(function () {
|
||||
yield \Amp\map(["test"], function () { throw new \Exception("hulk smash"); });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessage hulk smash
|
||||
*/
|
||||
public function testMapFailsIfFunctorThrowsOnResolvedValue() {
|
||||
\Amp\run(function () {
|
||||
$promises = [new Success("test")];
|
||||
yield \Amp\map($promises, function () { throw new \Exception("hulk smash"); });
|
||||
});
|
||||
}
|
||||
|
||||
public function testMapFailsIfAnyPromiseFails() {
|
||||
$e = new \RuntimeException(
|
||||
"true progress is to know more, and be more, and to do more"
|
||||
);
|
||||
$promise = \Amp\map([new Failure($e)], function () {});
|
||||
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertSame($e, $error);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testMapIgnoresFurtherResultsOnceFailed() {
|
||||
$completed = false;
|
||||
\Amp\run(function () use (&$completed) {
|
||||
$p1 = new Deferred;
|
||||
\Amp\once(function () use ($p1) {
|
||||
$p1->succeed("woot");
|
||||
}, 50);
|
||||
|
||||
$e = new \RuntimeException(
|
||||
"true progress is to know more, and be more, and to do more"
|
||||
);
|
||||
|
||||
$p2 = new Deferred;
|
||||
\Amp\once(function () use ($p2, $e) {
|
||||
$p2->fail($e);
|
||||
}, 10);
|
||||
|
||||
$toMap = \Amp\promises([$p1, $p2]);
|
||||
|
||||
try {
|
||||
yield \Amp\map($toMap, "strtoupper");
|
||||
$this->fail("this line should not be reached");
|
||||
} catch (\RuntimeException $error) {
|
||||
$this->assertSame($e, $error);
|
||||
}
|
||||
|
||||
yield $p1->promise();
|
||||
|
||||
$completed = true;
|
||||
});
|
||||
|
||||
$this->assertTrue($completed);
|
||||
}
|
||||
|
||||
public function testFilter() {
|
||||
$promises = ["test1", new Success("test2"), new Success("test2"), "test2"];
|
||||
$functor = function ($el) { return $el === "test2"; };
|
||||
$promise = \Amp\filter($promises, $functor);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([1=>"test2", 2=>"test2", 3=>"test2"], $result);
|
||||
}
|
||||
|
||||
public function testFilterUsesBoolComparisonOnUnspecifiedFunctor() {
|
||||
$promises = ["test1", new Success, null, false, 0, new Success("test2"), new Success(0), new Success(false) ];
|
||||
$promise = \Amp\filter($promises);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([0=>"test1", 5=>"test2"], $result);
|
||||
}
|
||||
|
||||
public function testFilter2() {
|
||||
$promises = ["test1", "test2", new Success("test2"), new Success("test2")];
|
||||
$functor = function ($el) { return $el === "test2"; };
|
||||
$promise = \Amp\filter($promises, $functor);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([1=>"test2", 2=>"test2", 3=>"test2"], $result);
|
||||
}
|
||||
|
||||
public function testFilterReturnsEmptySuccessOnEmptyInput() {
|
||||
$promise = \Amp\filter([], function () {});
|
||||
$this->assertInstanceOf('Amp\Success', $promise);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessage hulk smash
|
||||
*/
|
||||
public function testFilterFailsIfFunctorThrowsOnUnwrappedValue() {
|
||||
\Amp\run(function () {
|
||||
yield \Amp\filter(["test"], function () { throw new \Exception("hulk smash"); });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessage hulk smash
|
||||
*/
|
||||
public function testFilterFailsIfFunctorThrowsOnResolvedValue() {
|
||||
\Amp\run(function () {
|
||||
$promises = [new Success("test")];
|
||||
yield \Amp\filter($promises, function () { throw new \Exception("hulk smash"); });
|
||||
});
|
||||
}
|
||||
|
||||
public function testFilterFailsIfAnyPromiseFails() {
|
||||
$e = new \RuntimeException(
|
||||
"true progress is to know more, and be more, and to do more"
|
||||
);
|
||||
$promise = \Amp\filter([new Failure($e)], function () {});
|
||||
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
|
||||
$this->assertSame($e, $error);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testFilterIgnoresFurtherResultsOnceFailed() {
|
||||
$completed = false;
|
||||
\Amp\run(function () use (&$completed) {
|
||||
$p1 = new Deferred;
|
||||
$p2 = new Deferred;
|
||||
$e = new \RuntimeException(
|
||||
"true progress is to know more, and be more, and to do more"
|
||||
);
|
||||
\Amp\once(function () use ($p1, $p2, $e) {
|
||||
$p2->fail($e);
|
||||
\Amp\immediately(function () use ($p1) {
|
||||
$p1->succeed("woot");
|
||||
});
|
||||
}, 10);
|
||||
|
||||
$toMap = \Amp\promises([$p1, $p2]);
|
||||
$functor = function () { return true; };
|
||||
|
||||
try {
|
||||
yield \Amp\filter($toMap, $functor);
|
||||
$this->fail("this line should not be reached");
|
||||
} catch (\RuntimeException $error) {
|
||||
$this->assertSame($e, $error);
|
||||
}
|
||||
|
||||
yield $p1->promise();
|
||||
|
||||
$completed = true;
|
||||
});
|
||||
|
||||
$this->assertTrue($completed);
|
||||
}
|
||||
|
||||
public function testPipeWrapsRawValue() {
|
||||
$invoked = 0;
|
||||
$promise = \Amp\pipe(21, function ($r) { return $r * 2; });
|
||||
$promise->when(function ($e, $r) use (&$invoked) {
|
||||
$invoked++;
|
||||
$this->assertSame(42, $r);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testPipeTransformsEventualPromiseResult() {
|
||||
$result = 0;
|
||||
\Amp\run(function () use (&$result) {
|
||||
$promisor = new Deferred;
|
||||
\Amp\once(function () use ($promisor) {
|
||||
$promisor->succeed("woot");
|
||||
}, 10);
|
||||
|
||||
$promise = $promisor->promise();
|
||||
$result = (yield \Amp\pipe($promise, "strtoupper"));
|
||||
});
|
||||
|
||||
$this->assertSame("WOOT", $result);
|
||||
}
|
||||
|
||||
public function testPipeAbortsIfOriginalPromiseFails() {
|
||||
$invoked = 0;
|
||||
$failure = new Failure(new \RuntimeException);
|
||||
$promise = \Amp\pipe($failure, function (){});
|
||||
$promise->when(function ($e, $r) use (&$invoked) {
|
||||
$invoked++;
|
||||
$this->assertInstanceOf("RuntimeException", $e);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testPipeAbortsIfFunctorThrowsOnRawValue() {
|
||||
$invoked = 0;
|
||||
$promise = \Amp\pipe(42, function (){ throw new \RuntimeException; });
|
||||
$promise->when(function ($e, $r) use (&$invoked) {
|
||||
$invoked++;
|
||||
$this->assertInstanceOf("RuntimeException", $e);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testPipeAbortsIfFunctorThrows() {
|
||||
$invoked = 0;
|
||||
$promise = \Amp\pipe(new Success(42), function (){ throw new \RuntimeException; });
|
||||
$promise->when(function ($e, $r) use (&$invoked) {
|
||||
$invoked++;
|
||||
$this->assertInstanceOf("RuntimeException", $e);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testSomeResolutionWhenAllPromisesFail() {
|
||||
$ex1 = new \RuntimeException("1");
|
||||
$ex2 = new \RuntimeException("2");
|
||||
$promises = [new Failure($ex1), new Failure($ex2)];
|
||||
\Amp\some($promises)->when(function ($e, $r) use ($ex1, $ex2) {
|
||||
$this->assertNull($r);
|
||||
$this->assertInstanceOf(CombinatorException::class, $e);
|
||||
$this->assertSame($e->getExceptions()[0], $ex1);
|
||||
$this->assertSame($e->getExceptions()[1], $ex2);
|
||||
});
|
||||
}
|
||||
|
||||
public function testAllResolutionWhenNoPromiseInstancesCombined() {
|
||||
$promises = [null, 1, 2, true];
|
||||
\Amp\all($promises)->when(function ($e, $r) {
|
||||
list($a, $b, $c, $d) = $r;
|
||||
$this->assertNull($a);
|
||||
$this->assertSame(1, $b);
|
||||
$this->assertSame(2, $c);
|
||||
$this->assertSame(true, $d);
|
||||
});
|
||||
}
|
||||
|
||||
public function testSomeResolutionWhenNoPromiseInstancesCombined() {
|
||||
$promises = [null, 1, 2, true];
|
||||
\Amp\some($promises)->when(function ($e, $r) {
|
||||
list($errors, $results) = $r;
|
||||
list($a, $b, $c, $d) = $results;
|
||||
$this->assertNull($a);
|
||||
$this->assertSame(1, $b);
|
||||
$this->assertSame(2, $c);
|
||||
$this->assertSame(true, $d);
|
||||
});
|
||||
}
|
||||
|
||||
public function testAnyResolutionWhenNoPromiseInstancesCombined() {
|
||||
$promises = [null, 1, 2, true];
|
||||
\Amp\any($promises)->when(function ($e, $r) {
|
||||
list($errors, $results) = $r;
|
||||
list($a, $b, $c, $d) = $results;
|
||||
$this->assertNull($a);
|
||||
$this->assertSame(1, $b);
|
||||
$this->assertSame(2, $c);
|
||||
$this->assertSame(true, $d);
|
||||
});
|
||||
}
|
||||
|
||||
public function testAnyReturnsImmediatelyOnEmptyPromiseArray() {
|
||||
$promise = \Amp\any([]);
|
||||
$this->assertInstanceOf('Amp\Success', $promise);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([[], []], $result);
|
||||
}
|
||||
|
||||
public function testAllResolvesWithArrayOfResults() {
|
||||
\Amp\all(['r1' => 42, 'r2' => new Success(41)])->when(function ($error, $result) {
|
||||
$expected = ['r1' => 42, 'r2' => 41];
|
||||
$this->assertSame($expected, $result);
|
||||
});
|
||||
}
|
||||
|
||||
public function testAllReturnsImmediatelyOnEmptyPromiseArray() {
|
||||
$promise = \Amp\all([]);
|
||||
$this->assertInstanceOf('Amp\Success', $promise);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
$this->assertNull($error);
|
||||
$this->assertSame([], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage zanzibar
|
||||
*/
|
||||
public function testAllThrowsIfAnyIndividualPromiseFails() {
|
||||
$exception = new \RuntimeException('zanzibar');
|
||||
\Amp\all([
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Failure($exception),
|
||||
'r3' => new Success(40),
|
||||
])->when(function (\Exception $error) {
|
||||
throw $error;
|
||||
});
|
||||
}
|
||||
|
||||
public function testSomeReturnsArrayOfErrorsAndResults() {
|
||||
$exception = new \RuntimeException('zanzibar');
|
||||
\Amp\some([
|
||||
'r1' => new Success(42),
|
||||
'r2' => new Failure($exception),
|
||||
'r3' => new Success(40),
|
||||
])->when(function ($error, $result) use ($exception) {
|
||||
$this->assertNull($error);
|
||||
list($errors, $results) = $result;
|
||||
$this->assertSame(['r2' => $exception], $errors);
|
||||
$this->assertSame(['r1' => 42, 'r3' => 40], $results);
|
||||
});
|
||||
}
|
||||
|
||||
public function testSomeFailsImmediatelyOnEmptyPromiseArrayInput() {
|
||||
$promise = \Amp\some([]);
|
||||
$this->assertInstanceOf('Amp\Failure', $promise);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
$this->assertNull($result);
|
||||
$this->assertInstanceOf('\LogicException', $error);
|
||||
$this->assertSame("No promises or values provided for resolution", $error->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\CombinatorException
|
||||
*/
|
||||
public function testSomeThrowsIfNoPromisesResolveSuccessfully() {
|
||||
\Amp\some([
|
||||
'r1' => new Failure(new \RuntimeException),
|
||||
'r2' => new Failure(new \RuntimeException),
|
||||
])->when(function ($error) {
|
||||
throw $error;
|
||||
});
|
||||
}
|
||||
|
||||
public function testFirstFailsImmediatelyOnEmptyPromiseArrayInput() {
|
||||
$promise = \Amp\first([]);
|
||||
$this->assertInstanceOf('Amp\Failure', $promise);
|
||||
$error = null;
|
||||
$result = null;
|
||||
$promise->when(function ($e, $r) use (&$error, &$result) {
|
||||
$error = $e;
|
||||
$result = $r;
|
||||
});
|
||||
$this->assertNull($result);
|
||||
$this->assertInstanceOf('\LogicException', $error);
|
||||
$this->assertSame("No promises or values provided", $error->getMessage());
|
||||
}
|
||||
|
||||
public function testFirst() {
|
||||
$resolutionCount = 0;
|
||||
$result = 0;
|
||||
\Amp\run(function () use (&$resolutionCount, &$result) {
|
||||
$p1 = new Deferred;
|
||||
\Amp\once(function () use ($p1, &$resolutionCount) {
|
||||
$p1->succeed(1);
|
||||
$resolutionCount++;
|
||||
}, 10);
|
||||
|
||||
$p2 = new Deferred;
|
||||
\Amp\once(function () use ($p2, &$resolutionCount) {
|
||||
$p2->succeed(2);
|
||||
$resolutionCount++;
|
||||
}, 20);
|
||||
|
||||
$p3 = new Deferred;
|
||||
\Amp\once(function () use ($p3, &$resolutionCount) {
|
||||
$p3->succeed(3);
|
||||
$resolutionCount++;
|
||||
}, 30);
|
||||
|
||||
$promises = [$p1->promise(), $p2->promise(), $p3->promise()];
|
||||
$allPromise = \Amp\all($promises);
|
||||
$allPromise->when('\Amp\stop');
|
||||
|
||||
$result = (yield \Amp\first($promises));
|
||||
});
|
||||
|
||||
$this->assertSame(3, $resolutionCount);
|
||||
$this->assertSame(1, $result);
|
||||
}
|
||||
|
||||
public function testNonPromiseValueImmediatelyResolvesFirstCombinator() {
|
||||
$result = 0;
|
||||
\Amp\run(function () use (&$result) {
|
||||
$p1 = 42;
|
||||
$p2 = (new Deferred)->promise();
|
||||
$result = (yield \Amp\first([$p1, $p2]));
|
||||
});
|
||||
$this->assertSame(42, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\CombinatorException
|
||||
* @expectedExceptionMessage All promises failed
|
||||
*/
|
||||
public function testFirstFailsIfAllPromisesFail() {
|
||||
\Amp\run(function () use (&$result) {
|
||||
$e1 = new \Exception("foo");
|
||||
$e2 = new \Exception("bar");
|
||||
$promises = [new Failure($e1), new Failure($e2)];
|
||||
yield \Amp\first($promises);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\TimeoutException
|
||||
* @expectedExceptionMessage Promise resolution timed out
|
||||
*/
|
||||
public function testTimeout() {
|
||||
\Amp\run(function () {
|
||||
$pause = new \Amp\Pause(3000);
|
||||
yield \Amp\timeout($pause, 10);
|
||||
});
|
||||
}
|
||||
|
||||
public function testTimeoutOnSuccess() {
|
||||
$invoked = false;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$promisor = new Deferred;
|
||||
\Amp\once(function () use ($promisor) {
|
||||
$promisor->succeed(42);
|
||||
}, 10);
|
||||
|
||||
$result = (yield \Amp\timeout($promisor->promise(), 10000));
|
||||
$this->assertSame(42, $result);
|
||||
$invoked = true;
|
||||
});
|
||||
|
||||
$this->assertTrue($invoked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage nothing that is worth knowing can be taught
|
||||
*/
|
||||
public function testTimeoutOnFailure() {
|
||||
\Amp\run(function () {
|
||||
$promisor = new Deferred;
|
||||
\Amp\once(function () use ($promisor) {
|
||||
$promisor->fail(new \RuntimeException(
|
||||
"nothing that is worth knowing can be taught"
|
||||
));
|
||||
}, 10);
|
||||
|
||||
$result = (yield \Amp\timeout($promisor->promise(), 10000));
|
||||
});
|
||||
}
|
||||
|
||||
public function testTimeoutIgnoresResultIfAlreadyComplete() {
|
||||
$invoked = false;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$promisor = new Deferred;
|
||||
\Amp\once(function () use ($promisor) {
|
||||
$promisor->succeed(42);
|
||||
}, 100);
|
||||
try {
|
||||
$result = (yield \Amp\timeout($promisor->promise(), 10));
|
||||
} catch (\RuntimeException $e) {
|
||||
// ignore this
|
||||
}
|
||||
yield $promisor->promise();
|
||||
$invoked = true;
|
||||
});
|
||||
|
||||
$this->assertTrue($invoked);
|
||||
}
|
||||
|
||||
public function testAllCombinatorResolution() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
list($a, $b) = (yield \Amp\all([
|
||||
new Success(21),
|
||||
new Success(2),
|
||||
]));
|
||||
|
||||
$result = ($a * $b);
|
||||
$this->assertSame(42, $result);
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testAllCombinatorResolutionWithNonPromises() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
list($a, $b, $c) = (yield \Amp\all([new Success(21), new Success(2), 10]));
|
||||
$result = ($a * $b * $c);
|
||||
$this->assertSame(420, $result);
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage When in the chronicle of wasted time
|
||||
*/
|
||||
public function testAllCombinatorResolutionThrowsIfAnyOnePromiseFails() {
|
||||
\Amp\run(function () {
|
||||
list($a, $b) = (yield \Amp\all([
|
||||
new Success(21),
|
||||
new Failure(new \Exception('When in the chronicle of wasted time')),
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
public function testExplicitAllCombinatorResolution() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
list($a, $b, $c) = (yield \Amp\all([
|
||||
new Success(21),
|
||||
new Success(2),
|
||||
10
|
||||
]));
|
||||
|
||||
$this->assertSame(420, ($a * $b * $c));
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testExplicitAnyCombinatorResolution() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
list($errors, $results) = (yield \Amp\any([
|
||||
'a' => new Success(21),
|
||||
'b' => new Failure(new \Exception('test')),
|
||||
]));
|
||||
$this->assertSame('test', $errors['b']->getMessage());
|
||||
$this->assertSame(21, $results['a']);
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\CombinatorException
|
||||
*/
|
||||
public function testExplicitSomeCombinatorResolutionFailsOnError() {
|
||||
\Amp\run(function () {
|
||||
yield \Amp\some([
|
||||
'r1' => new Failure(new \RuntimeException),
|
||||
'r2' => new Failure(new \RuntimeException),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function testPromisesNormalization() {
|
||||
$completed = false;
|
||||
\Amp\run(function () use (&$completed) {
|
||||
$promisor = new Deferred;
|
||||
$promisor->succeed(41);
|
||||
$values = [
|
||||
$promisor,
|
||||
42,
|
||||
new Success(43),
|
||||
];
|
||||
|
||||
list($a, $b, $c) = (yield \Amp\all(\Amp\promises($values)));
|
||||
$this->assertSame(41, $a);
|
||||
$this->assertSame(42, $b);
|
||||
$this->assertSame(43, $c);
|
||||
$completed = true;
|
||||
});
|
||||
$this->assertTrue($completed);
|
||||
}
|
||||
|
||||
public function testCoroutine() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$co = function () use (&$invoked) {
|
||||
yield new Success;
|
||||
yield;
|
||||
yield new Pause(25);
|
||||
$invoked++;
|
||||
};
|
||||
$wrapped = \Amp\coroutine($co);
|
||||
$wrapped();
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testCoroutineWrapsNonGeneratorReturnInPromise() {
|
||||
$co = \Amp\coroutine(function () {
|
||||
return 42;
|
||||
});
|
||||
$out = $co();
|
||||
$this->assertInstanceOf("\Amp\Success", $out);
|
||||
$invoked = false;
|
||||
$out->when(function ($error, $result) use (&$invoked) {
|
||||
$this->assertNull($error);
|
||||
$this->assertSame(42, $result);
|
||||
$invoked = true;
|
||||
});
|
||||
$this->assertTrue($invoked);
|
||||
}
|
||||
|
||||
public function testCoroutineReturnsPromiseResultUnmodified() {
|
||||
$success = new \Amp\Success;
|
||||
$co = \Amp\coroutine(function () use ($success) {
|
||||
return $success;
|
||||
});
|
||||
$out = $co();
|
||||
$this->assertSame($success, $out);
|
||||
}
|
||||
|
||||
public function testNestedCoroutineResolutionContinuation() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$co = function () use (&$invoked) {
|
||||
yield new Success;
|
||||
yield new Success;
|
||||
yield new Success;
|
||||
yield new Success;
|
||||
yield new Success;
|
||||
yield new CoroutineResult(42);
|
||||
$invoked++;
|
||||
};
|
||||
$result = (yield \Amp\resolve($co()));
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testCoroutineFauxReturnValue() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$co = function () use (&$invoked) {
|
||||
yield;
|
||||
yield new CoroutineResult(42);
|
||||
yield;
|
||||
$invoked++;
|
||||
};
|
||||
$result = (yield \Amp\resolve($co()));
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testResolveAcceptsGeneratorCallable() {
|
||||
\Amp\run(function() {
|
||||
$result = (yield \Amp\resolve(function () {
|
||||
yield new CoroutineResult(42);
|
||||
}));
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testResolveNonGeneratorCallableThrows() {
|
||||
\Amp\run(function() {
|
||||
yield \Amp\resolve(function () {
|
||||
return 42;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testResolveInvalidArgumentThrows() {
|
||||
\Amp\run(function() {
|
||||
yield \Amp\resolve(42);
|
||||
});
|
||||
}
|
||||
|
||||
public function testResolutionFailuresAreThrownIntoGeneratorCoroutine() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$foo = function () {
|
||||
$a = (yield new Success(21));
|
||||
$b = 1;
|
||||
try {
|
||||
yield new Failure(new \Exception("test"));
|
||||
$this->fail("Code path should not be reached");
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame("test", $e->getMessage());
|
||||
$b = 2;
|
||||
}
|
||||
};
|
||||
$result = (yield \Amp\resolve($foo()));
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage a moveable feast
|
||||
*/
|
||||
public function testExceptionOnInitialAdvanceFailsCoroutineResolution() {
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$co = function () {
|
||||
throw new \Exception("a moveable feast");
|
||||
yield;
|
||||
};
|
||||
$result = (yield \Amp\resolve($co()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidYields
|
||||
*/
|
||||
public function testInvalidYieldFailsCoroutineResolution($badYield) {
|
||||
try {
|
||||
\Amp\run(function () use (&$invoked, $badYield) {
|
||||
$gen = function () use ($badYield) {
|
||||
yield;
|
||||
yield $badYield;
|
||||
yield;
|
||||
};
|
||||
yield \Amp\resolve($gen());
|
||||
});
|
||||
$this->fail("execution should not reach this point");
|
||||
} catch (\DomainException $e) {
|
||||
$pos = strpos($e->getMessage(), "Unexpected yield (Promise|CoroutineResult|null expected);");
|
||||
$this->assertSame(0, $pos);
|
||||
return;
|
||||
}
|
||||
$this->fail("execution should not reach this point");
|
||||
}
|
||||
|
||||
public function provideInvalidYields() {
|
||||
return [
|
||||
[42],
|
||||
[3.14],
|
||||
["string"],
|
||||
[true],
|
||||
[new \StdClass],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage When in the chronicle of wasted time
|
||||
*/
|
||||
public function testUncaughtGeneratorExceptionFailsCoroutineResolution() {
|
||||
$invoked = 0;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
$gen = function () {
|
||||
yield;
|
||||
throw new \Exception("When in the chronicle of wasted time");
|
||||
yield;
|
||||
};
|
||||
|
||||
yield \Amp\resolve($gen());
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testWait() {
|
||||
$promisor = new Deferred;
|
||||
\Amp\once(function () use ($promisor) {
|
||||
$promisor->succeed(42);
|
||||
}, 10);
|
||||
|
||||
$result = \Amp\wait($promisor->promise());
|
||||
$this->assertSame(42, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage If the reader prefers, this code may be regarded as fiction
|
||||
*/
|
||||
public function testWaitError() {
|
||||
$promisor = new Deferred;
|
||||
\Amp\once(function () use ($promisor) {
|
||||
$promisor->fail(new \Exception(
|
||||
"If the reader prefers, this code may be regarded as fiction"
|
||||
));
|
||||
}, 10);
|
||||
|
||||
\Amp\wait($promisor->promise());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\LibeventReactor;
|
||||
|
||||
class LibeventReactorTest extends ReactorTest {
|
||||
protected function setUp() {
|
||||
if (extension_loaded("libevent")) {
|
||||
\Amp\reactor($assign = new LibeventReactor);
|
||||
} else {
|
||||
$this->markTestSkipped(
|
||||
"libevent extension not loaded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetLoop() {
|
||||
$result = \Amp\reactor()->getLoop();
|
||||
$this->assertInternalType("resource", $result);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\NativeReactor;
|
||||
|
||||
class NativeReactorTest extends ReactorTest {
|
||||
protected function setUp() {
|
||||
\Amp\reactor($assign = new NativeReactor);
|
||||
}
|
||||
|
||||
public function testOnSignalWatcher() {
|
||||
if (!\extension_loaded("pcntl")) {
|
||||
$this->markTestSkipped(
|
||||
"ext/pcntl required to test onSignal() capture"
|
||||
);
|
||||
} else {
|
||||
parent::testOnSignalWatcher();
|
||||
}
|
||||
}
|
||||
|
||||
public function testInitiallyDisabledOnSignalWatcher() {
|
||||
if (!\extension_loaded("pcntl")) {
|
||||
$this->markTestSkipped(
|
||||
"ext/pcntl required to test onSignal() capture"
|
||||
);
|
||||
} else {
|
||||
parent::testInitiallyDisabledOnSignalWatcher();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,39 +2,42 @@
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp;
|
||||
use Amp\Pause;
|
||||
use Amp\NativeReactor;
|
||||
use Interop\Async\Loop;
|
||||
|
||||
class PauseTest extends \PHPUnit_Framework_TestCase {
|
||||
protected function setUp() {
|
||||
\Amp\reactor($assign = new NativeReactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideBadMillisecondArgs
|
||||
* @expectedException \DomainException
|
||||
* @expectedExceptionMessage Pause timeout must be greater than or equal to 1 millisecond
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testCtorThrowsOnBadMillisecondParam($arg) {
|
||||
\Amp\run(function () use ($arg) {
|
||||
new Pause($arg);
|
||||
});
|
||||
$pause = new Pause($arg);
|
||||
}
|
||||
|
||||
public function provideBadMillisecondArgs() {
|
||||
return [
|
||||
[0],
|
||||
[-3.14],
|
||||
[-1],
|
||||
];
|
||||
}
|
||||
|
||||
public function testPauseYield() {
|
||||
$endReached = false;
|
||||
\Amp\run(function () use (&$endReached) {
|
||||
$result = (yield new Pause(1));
|
||||
$this->assertNull($result);
|
||||
$endReached = true;
|
||||
public function testPause() {
|
||||
$time = 100;
|
||||
$value = "test";
|
||||
$start = microtime(true);
|
||||
|
||||
Loop::execute(function () use (&$result, $time, $value) {
|
||||
$awaitable = new Pause($time, $value);
|
||||
|
||||
$callback = function ($exception, $value) use (&$result) {
|
||||
$result = $value;
|
||||
};
|
||||
|
||||
$awaitable->when($callback);
|
||||
});
|
||||
$this->assertTrue($endReached);
|
||||
|
||||
$this->assertLessThanOrEqual($time, microtime(true) - $start);
|
||||
$this->assertSame($value, $result);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
class PlaceholderPrivateTest extends PlaceholderTest {
|
||||
protected function getPromisor() {
|
||||
return new PromisorPrivateImpl;
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
class PlaceholderPublicTest extends PlaceholderTest {
|
||||
protected function getPromisor() {
|
||||
return new PromisorPublicImpl;
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\NativeReactor;
|
||||
|
||||
abstract class PlaceholderTest {
|
||||
abstract protected function getPromisor();
|
||||
|
||||
public function testWhenCallbackDataPassed() {
|
||||
$invoked = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed(42);
|
||||
$promise->when(function($e, $r, $d) use (&$invoked) {
|
||||
$this->assertNull($e);
|
||||
$this->assertSame(42, $r);
|
||||
$this->assertSame("zanzibar", $d);
|
||||
++$invoked;
|
||||
}, "zanzibar");
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testWatchCallbackDataPassed() {
|
||||
$invoked = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promise->watch(function($p, $d) use (&$invoked) {
|
||||
$this->assertSame(42, $p);
|
||||
$this->assertSame("zanzibar", $d);
|
||||
$invoked++;
|
||||
}, "zanzibar");
|
||||
$promisor->update(42);
|
||||
$promisor->update(42);
|
||||
$this->assertSame(2, $invoked);
|
||||
}
|
||||
|
||||
public function testWatchCallbackNotInvokedIfAlreadySucceeded() {
|
||||
$invoked = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed(42);
|
||||
$promise->watch(function($p, $d) use (&$invoked) {
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(0, $invoked);
|
||||
}
|
||||
|
||||
public function testWatchCallbackNotInvokedIfAlreadyFailed() {
|
||||
$invoked = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$exception = new \Exception('test');
|
||||
$promisor->fail($exception);
|
||||
$promise->watch(function($p, $d) use (&$invoked) {
|
||||
$invoked++;
|
||||
});
|
||||
$this->assertSame(0, $invoked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testSucceedThrowsIfAlreadyResolved() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->succeed(42);
|
||||
$promisor->succeed('zanzibar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage A Promise cannot act as its own resolution result
|
||||
*/
|
||||
public function testSucceedThrowsIfPromiseIsTheResolutionValue() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testFailThrowsIfAlreadyResolved() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->succeed(42);
|
||||
$promisor->fail(new \Exception);
|
||||
}
|
||||
|
||||
public function testSucceedingWithPromisePipelinesResult() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promisor = $this->getPromisor();
|
||||
$next = $this->getPromisor();
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 1);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
|
||||
$this->assertSame(42, (yield $promisor->promise()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage fugazi
|
||||
*/
|
||||
public function testFailingWithPromisePipelinesResult() {
|
||||
(new NativeReactor)->run(function($reactor) {
|
||||
$promisor = $this->getPromisor();
|
||||
$next = $this->getPromisor();
|
||||
|
||||
$reactor->once(function() use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
|
||||
$promisor->succeed($next->promise());
|
||||
yield $promisor->promise();
|
||||
});
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
class PromiseStreamTest extends BaseTest {
|
||||
public function testStream() {
|
||||
$endReached = false;
|
||||
\Amp\run(function () use (&$endReached) {
|
||||
$promisor = new \Amp\Deferred;
|
||||
$stream = new \Amp\PromiseStream($promisor->promise());
|
||||
$i = 0;
|
||||
\Amp\repeat(function ($watcherId) use ($promisor, &$i) {
|
||||
$i++;
|
||||
$promisor->update("test{$i}");
|
||||
if ($i === 3) {
|
||||
$promisor->succeed();
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
}, 10);
|
||||
|
||||
$results = [];
|
||||
while (yield $stream->valid()) {
|
||||
$results[] = $stream->consume();
|
||||
}
|
||||
|
||||
$this->assertSame(["test1", "test2", "test3"], $results);
|
||||
$endReached = true;
|
||||
});
|
||||
$this->assertTrue($endReached);
|
||||
}
|
||||
|
||||
public function testStreamReturnsPromiseResolutionForFirstConsumeCallAfterSuccess() {
|
||||
$endReached = false;
|
||||
\Amp\run(function () use (&$endReached) {
|
||||
$promisor = new \Amp\Deferred;
|
||||
$stream = new \Amp\PromiseStream($promisor->promise());
|
||||
$i = 0;
|
||||
\Amp\repeat(function ($watcherId) use ($promisor, &$i) {
|
||||
$i++;
|
||||
$promisor->update("test{$i}");
|
||||
if ($i === 3) {
|
||||
$promisor->succeed(42);
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
}, 10);
|
||||
|
||||
$results = [];
|
||||
while (yield $stream->valid()) {
|
||||
$stream->consume();
|
||||
}
|
||||
|
||||
$this->assertSame(42, $stream->consume());
|
||||
$endReached = true;
|
||||
});
|
||||
$this->assertTrue($endReached);
|
||||
}
|
||||
|
||||
public function testStreamRetainsUpdatesUntilInitialized() {
|
||||
$endReached = false;
|
||||
\Amp\run(function () use (&$endReached) {
|
||||
$promisor = new \Amp\Deferred;
|
||||
$stream = new \Amp\PromiseStream($promisor->promise());
|
||||
$promisor->update("foo");
|
||||
$promisor->update("bar");
|
||||
$promisor->update("baz");
|
||||
$promisor->succeed();
|
||||
|
||||
$results = [];
|
||||
while (yield $stream->valid()) {
|
||||
$results[] = $stream->consume();
|
||||
}
|
||||
$endReached = true;
|
||||
$this->assertSame(["foo", "bar", "baz"], $results);
|
||||
});
|
||||
$this->assertTrue($endReached);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage test
|
||||
*/
|
||||
public function testStreamThrowsIfPromiseFails() {
|
||||
\Amp\run(function () {
|
||||
$i = 0;
|
||||
$promisor = new \Amp\Deferred;
|
||||
\Amp\repeat(function ($watcherId) use (&$i, $promisor) {
|
||||
$i++;
|
||||
$promisor->update($i);
|
||||
if ($i === 2) {
|
||||
\Amp\cancel($watcherId);
|
||||
$promisor->fail(new \Exception(
|
||||
"test"
|
||||
));
|
||||
}
|
||||
}, 10);
|
||||
|
||||
$stream = new \Amp\PromiseStream($promisor->promise());
|
||||
|
||||
$results = [];
|
||||
while (yield $stream->valid()) {
|
||||
$results[] = $stream->consume();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot advance PromiseStream beyond unresolved index 0
|
||||
*/
|
||||
public function testPrematureConsumptionThrows() {
|
||||
\Amp\run(function () {
|
||||
$promisor = new \Amp\Deferred;
|
||||
$stream = new \Amp\PromiseStream($promisor->promise());
|
||||
$results = [];
|
||||
$stream->consume();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot advance PromiseStream beyond completed index 1
|
||||
*/
|
||||
public function testConsumeAfterStreamCompletionThrows() {
|
||||
\Amp\run(function () {
|
||||
$promisor = new \Amp\Deferred;
|
||||
$promisor->update(0);
|
||||
$promisor->succeed(1);
|
||||
$stream = new \Amp\PromiseStream($promisor->promise());
|
||||
$results = [];
|
||||
$stream->consume();
|
||||
$stream->consume();
|
||||
$stream->consume();
|
||||
});
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\Promisor;
|
||||
use Amp\Test\PromisorPrivateImpl;
|
||||
|
||||
class PromisorPrivateTest extends PromisorTest {
|
||||
protected function getPromisor() {
|
||||
return new PromisorPrivateImpl;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\Promisor;
|
||||
use Amp\Test\PromisorPublicImpl;
|
||||
|
||||
class PromisorPublicTest extends PromisorTest {
|
||||
protected function getPromisor() {
|
||||
return new PromisorPublicImpl;
|
||||
}
|
||||
|
||||
public function testPromiseReturnsSelf() {
|
||||
$promisor = new PromisorPublicImpl;
|
||||
$this->assertSame($promisor, $promisor->promise());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Throwable Exception instance required to fail a promise
|
||||
* @dataProvider provideBadFailureArguments
|
||||
*/
|
||||
public function testResolvingErrorWithNonExceptionThrows($badArg) {
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->fail($badArg);
|
||||
}
|
||||
|
||||
public function provideBadFailureArguments() {
|
||||
return [
|
||||
[1],
|
||||
[true],
|
||||
[new \StdClass],
|
||||
];
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\NativeReactor;
|
||||
|
||||
abstract class PromisorTest extends \PHPUnit_Framework_TestCase {
|
||||
abstract protected function getPromisor();
|
||||
|
||||
protected function setUp() {
|
||||
\Amp\reactor($assign = new NativeReactor);
|
||||
}
|
||||
|
||||
public function testWhenInvokesCallbackWithResultIfAlreadySucceeded() {
|
||||
$invoked = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed(42);
|
||||
$promise->when(function ($e, $r) use (&$invoked) {
|
||||
$this->assertSame(42, $r);
|
||||
$this->assertNull($e);
|
||||
++$invoked;
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testWhenInvokesCallbackWithErrorIfAlreadyFailed() {
|
||||
$invoked = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$exception = new \Exception('test');
|
||||
$promisor->fail($exception);
|
||||
$promise->when(function ($e, $r) use ($exception, &$invoked) {
|
||||
$invoked++;
|
||||
$this->assertSame($exception, $e);
|
||||
$this->assertNull($r);
|
||||
});
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testSucceedThrowsIfAlreadyResolved() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->succeed(42);
|
||||
$promisor->succeed('zanzibar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage A Promise cannot act as its own resolution result
|
||||
*/
|
||||
public function testSucceedThrowsIfPromiseIsTheResolutionValue() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promisor->succeed($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Promise already resolved
|
||||
*/
|
||||
public function testFailThrowsIfAlreadyResolved() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->succeed(42);
|
||||
$promisor->fail(new \Exception);
|
||||
}
|
||||
|
||||
public function testSucceedingWithPromisePipelinesResult() {
|
||||
\Amp\run(function () {
|
||||
$next = $this->getPromisor();
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->succeed($next->promise());
|
||||
\Amp\once(function () use ($next) {
|
||||
$next->succeed(42);
|
||||
}, $msDelay = 10);
|
||||
yield;
|
||||
$result = (yield $promisor->promise());
|
||||
$this->assertSame(42, $result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage fugazi
|
||||
*/
|
||||
public function testFailingWithPromisePipelinesResult() {
|
||||
\Amp\run(function () {
|
||||
$promisor = $this->getPromisor();
|
||||
$next = $this->getPromisor();
|
||||
\Amp\once(function () use ($next) {
|
||||
$next->fail(new \RuntimeException('fugazi'));
|
||||
}, $msDelay = 10);
|
||||
yield;
|
||||
$promisor->succeed($next->promise());
|
||||
yield $promisor->promise();
|
||||
});
|
||||
}
|
||||
|
||||
public function testUpdate() {
|
||||
$updatable = 0;
|
||||
\Amp\run(function () use (&$updatable) {
|
||||
$i = 0;
|
||||
$promisor = $this->getPromisor();
|
||||
$updater = function ($watcherId) use ($promisor, &$i) {
|
||||
$promisor->update(++$i);
|
||||
if ($i === 3) {
|
||||
\Amp\cancel($watcherId);
|
||||
// reactor run loop should now be able to exit
|
||||
}
|
||||
};
|
||||
$promise = $promisor->promise();
|
||||
|
||||
$promise->watch(function ($updateData) use (&$updatable) {
|
||||
$updatable += $updateData;
|
||||
});
|
||||
\Amp\repeat($updater, $msDelay = 10);
|
||||
});
|
||||
|
||||
$this->assertSame(6, $updatable);
|
||||
}
|
||||
|
||||
public function testUpdateArgs() {
|
||||
$updates = new \StdClass;
|
||||
$updates->arr = [];
|
||||
|
||||
$promisor = $this->getPromisor();
|
||||
$promise = $promisor->promise();
|
||||
$promise->watch(function ($progress, $cbData) use ($updates) {
|
||||
$updates->arr[] = \func_get_args();
|
||||
}, "cb_data");
|
||||
|
||||
$promisor->update(1);
|
||||
$promisor->update(2);
|
||||
$promisor->update(3);
|
||||
|
||||
$expected = [
|
||||
[1, "cb_data"],
|
||||
[2, "cb_data"],
|
||||
[3, "cb_data"],
|
||||
];
|
||||
|
||||
$this->assertSame($expected, $updates->arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot update resolved promise
|
||||
*/
|
||||
public function testUpdateThrowsIfPromiseAlreadyResolved() {
|
||||
$promisor = $this->getPromisor();
|
||||
$promisor->succeed();
|
||||
$promisor->update(42);
|
||||
}
|
||||
}
|
@ -1,716 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\UvReactor;
|
||||
|
||||
abstract class ReactorTest extends BaseTest {
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot stop(); event reactor not currently active
|
||||
*/
|
||||
public function testStopThrowsIfNotCurrentlyRunning() {
|
||||
\Amp\stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot run() recursively; event reactor already active
|
||||
*/
|
||||
public function testRecursiveRunCallThrows() {
|
||||
\Amp\run(function () {
|
||||
\Amp\run();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot tick() recursively; event reactor already active
|
||||
*/
|
||||
public function testRecursiveTickCallThrows() {
|
||||
\Amp\immediately('\Amp\tick');
|
||||
\Amp\tick();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
* @expectedExceptionMessage Cannot tick() recursively; event reactor already active
|
||||
*/
|
||||
public function testRecursiveTickCallThrowsInsideRun() {
|
||||
\Amp\run(function () {
|
||||
\Amp\tick();
|
||||
});
|
||||
}
|
||||
|
||||
public function testImmediatelyWatcherKeepAliveRunResult() {
|
||||
$invoked = false;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
\Amp\immediately(function () use (&$invoked) {
|
||||
$invoked = true;
|
||||
}, ["keep_alive" => false]);
|
||||
});
|
||||
$this->assertFalse($invoked);
|
||||
}
|
||||
|
||||
public function testOnceWatcherKeepAliveRunResult() {
|
||||
$invoked = false;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
\Amp\once(function () use (&$invoked) {
|
||||
$invoked = true;
|
||||
}, 2000, $options = ["keep_alive" => false]);
|
||||
});
|
||||
$this->assertFalse($invoked);
|
||||
}
|
||||
|
||||
public function testRepeatWatcherKeepAliveRunResult() {
|
||||
$invoked = false;
|
||||
\Amp\run(function () use (&$invoked) {
|
||||
\Amp\repeat(function () use (&$invoked) {
|
||||
$invoked = true;
|
||||
}, 2000, $options = ["keep_alive" => false]);
|
||||
});
|
||||
$this->assertFalse($invoked);
|
||||
}
|
||||
|
||||
public function testOnReadableWatcherKeepAliveRunResult() {
|
||||
\Amp\run(function () {
|
||||
\Amp\onReadable(STDIN, function () {
|
||||
// empty
|
||||
}, $options = ["keep_alive" => false]);
|
||||
});
|
||||
}
|
||||
|
||||
public function testOnWritableWatcherKeepAliveRunResult() {
|
||||
\Amp\run(function () {
|
||||
\Amp\onWritable(STDOUT, function () {
|
||||
// empty
|
||||
}, $options = ["keep_alive" => false]);
|
||||
});
|
||||
}
|
||||
|
||||
public function testOnSignalWatcherKeepAliveRunResult() {
|
||||
if (!\extension_loaded("pcntl") && !\Amp\reactor() instanceof UvReactor) {
|
||||
$this->markTestSkipped("ext/pcntl or UvReactor required to test onSignal() registration");
|
||||
}
|
||||
|
||||
\Amp\run(function () {
|
||||
\Amp\onSignal(SIGUSR1, function () {
|
||||
// empty
|
||||
}, $options = ["keep_alive" => false]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRegistrationArgs
|
||||
*/
|
||||
public function testWatcherKeepAliveRegistrationInfo($type, $args) {
|
||||
if ($type === "onSignal") {
|
||||
if (!\extension_loaded("pcntl") && !\Amp\reactor() instanceof UvReactor) {
|
||||
$this->markTestSkipped("ext/pcntl or UvReactor required to test onSignal() registration");
|
||||
}
|
||||
$requiresCancel = true;
|
||||
} else {
|
||||
$requiresCancel = false;
|
||||
}
|
||||
|
||||
$func = '\Amp\\' . $type;
|
||||
if (substr($type, 0, 2) === "on" && $type !== "once") {
|
||||
$type = "on_" . lcfirst(substr($type, 2));
|
||||
}
|
||||
|
||||
// keep_alive is the default
|
||||
$watcherId1 = \call_user_func_array($func, $args);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 1, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
$this->assertSame(1, $info["keep_alive"]);
|
||||
|
||||
// explicitly keep_alive even though it's the default setting
|
||||
$argsCopy = $args;
|
||||
$argsCopy[] = ["keep_alive" => true];
|
||||
$watcherId2 = \call_user_func_array($func, $argsCopy);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 2, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
$this->assertSame(2, $info["keep_alive"]);
|
||||
|
||||
// disabling a keep_alive watcher should decrement the count
|
||||
\Amp\disable($watcherId2);
|
||||
$info = \Amp\info();
|
||||
$this->assertSame(1, $info["keep_alive"]);
|
||||
|
||||
// enabling a keep_alive watcher should increment the count
|
||||
\Amp\enable($watcherId2);
|
||||
$info = \Amp\info();
|
||||
$this->assertSame(2, $info["keep_alive"]);
|
||||
|
||||
// cancelling a keep_alive watcher should decrement the count
|
||||
\Amp\cancel($watcherId2);
|
||||
$info = \Amp\info();
|
||||
$this->assertSame(1, $info["keep_alive"]);
|
||||
|
||||
// keep_alive => false should leave the count untouched
|
||||
$argsCopy = $args;
|
||||
$argsCopy[] = ["keep_alive" => false];
|
||||
$watcherId2 = \call_user_func_array($func, $argsCopy);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 2, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
$this->assertSame(1, $info["keep_alive"]);
|
||||
|
||||
if ($requiresCancel) {
|
||||
\Amp\cancel($watcherId1);
|
||||
\Amp\cancel($watcherId2);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideRegistrationArgs() {
|
||||
$args = [
|
||||
["immediately", [function () {}]],
|
||||
["once", [function () {}, 5000]],
|
||||
["repeat", [function () {}, 5000]],
|
||||
["onWritable", [\STDOUT, function () {}]],
|
||||
["onReadable", [\STDIN, function () {}]],
|
||||
];
|
||||
|
||||
$args[] = ["onSignal", [defined('SIGUSR1') ? \SIGUSR1 : -1, function () {}]];
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRegistrationArgs
|
||||
*/
|
||||
public function testWatcherRegistrationAndCancellationInfo($type, $args) {
|
||||
if ($type === "onSignal") {
|
||||
if (!\extension_loaded("pcntl") && !\Amp\reactor() instanceof UvReactor) {
|
||||
$this->markTestSkipped("ext/pcntl or UvReactor required to test onSignal() registration");
|
||||
}
|
||||
}
|
||||
|
||||
$func = '\Amp\\' . $type;
|
||||
if (substr($type, 0, 2) === "on" && $type !== "once") {
|
||||
$type = "on_" . lcfirst(substr($type, 2));
|
||||
}
|
||||
|
||||
$watcherId = \call_user_func_array($func, $args);
|
||||
$this->assertInternalType("string", $watcherId);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 1, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
// invoke enable() on active watcher to ensure it has no side-effects
|
||||
\Amp\enable($watcherId);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 1, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
// invoke disable() twice to ensure it has no side-effects
|
||||
\Amp\disable($watcherId);
|
||||
\Amp\disable($watcherId);
|
||||
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 0, "disabled" => 1];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
\Amp\cancel($watcherId);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 0, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
$watcherId = \call_user_func_array($func, $args);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 1, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
\Amp\disable($watcherId);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 0, "disabled" => 1];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
\Amp\enable($watcherId);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 1, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
\Amp\cancel($watcherId);
|
||||
$info = \Amp\info();
|
||||
$expected = ["enabled" => 0, "disabled" => 0];
|
||||
$this->assertSame($expected, $info[$type]);
|
||||
|
||||
// invoke cancel() again to ensure it has no side-effects
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
|
||||
public function testEnableHasNoEffectOnNonexistentWatcher() {
|
||||
\Amp\enable("nonexistentWatcher");
|
||||
}
|
||||
|
||||
public function testDisableHasNoEffectOnNonexistentWatcher() {
|
||||
\Amp\disable("nonexistentWatcher");
|
||||
}
|
||||
|
||||
public function testCancelHasNoEffectOnNonexistentWatcher() {
|
||||
\Amp\cancel("nonexistentWatcher");
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage coroutine error
|
||||
*/
|
||||
public function testImmediateCoroutineResolutionError() {
|
||||
\Amp\run(function () {
|
||||
yield;
|
||||
yield new \Amp\Pause(10);
|
||||
throw new \Exception("coroutine error");
|
||||
});
|
||||
}
|
||||
|
||||
public function testOnErrorCapturesUncaughtException() {
|
||||
$msg = "";
|
||||
\Amp\onError(function ($error) use (&$msg) {
|
||||
$msg = $error->getMessage();
|
||||
});
|
||||
\Amp\run(function () {
|
||||
throw new \Exception("coroutine error");
|
||||
});
|
||||
$this->assertSame("coroutine error", $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage errorception
|
||||
*/
|
||||
public function testOnErrorFailure() {
|
||||
\Amp\onError(function () {
|
||||
throw new \Exception("errorception");
|
||||
});
|
||||
\Amp\run(function () {
|
||||
yield;
|
||||
yield new \Amp\Pause(10);
|
||||
throw new \Exception("coroutine error");
|
||||
});
|
||||
}
|
||||
|
||||
public function testEnablingWatcherAllowsSubsequentInvocation() {
|
||||
$increment = 0;
|
||||
$watcherId = \Amp\immediately(function () use (&$increment) {
|
||||
$increment++;
|
||||
});
|
||||
\Amp\disable($watcherId);
|
||||
\Amp\once('\Amp\stop', $msDelay = 50);
|
||||
\Amp\run();
|
||||
$this->assertEquals(0, $increment);
|
||||
\Amp\enable($watcherId);
|
||||
\Amp\once('\Amp\stop', $msDelay = 50);
|
||||
\Amp\run();
|
||||
$this->assertEquals(1, $increment);
|
||||
}
|
||||
|
||||
public function testUnresolvedEventsAreReenabledOnRunFollowingPreviousStop() {
|
||||
$increment = 0;
|
||||
\Amp\once(function () use (&$increment) {
|
||||
$increment++;
|
||||
\Amp\stop();
|
||||
}, $msDelay = 150);
|
||||
|
||||
\Amp\run('\Amp\stop');
|
||||
|
||||
$this->assertEquals(0, $increment);
|
||||
\usleep(150000);
|
||||
\Amp\run();
|
||||
$this->assertEquals(1, $increment);
|
||||
}
|
||||
|
||||
public function testTimerWatcherParameterOrder() {
|
||||
$counter = 0;
|
||||
\Amp\immediately(function ($watcherId) use (&$counter) {
|
||||
$this->assertInternalType("string", $watcherId);
|
||||
if (++$counter === 3) {
|
||||
\Amp\stop();
|
||||
}
|
||||
});
|
||||
\Amp\once(function ($watcherId) use (&$counter) {
|
||||
$this->assertInternalType("string", $watcherId);
|
||||
if (++$counter === 3) {
|
||||
\Amp\stop();
|
||||
}
|
||||
}, $msDelay = 1);
|
||||
\Amp\repeat(function ($watcherId) use (&$counter) {
|
||||
$this->assertInternalType("string", $watcherId);
|
||||
\Amp\cancel($watcherId);
|
||||
if (++$counter === 3) {
|
||||
\Amp\stop();
|
||||
}
|
||||
}, $msDelay = 1);
|
||||
|
||||
\Amp\run();
|
||||
}
|
||||
|
||||
public function testStreamWatcherParameterOrder() {
|
||||
$invoked = 0;
|
||||
\Amp\onWritable(STDOUT, function ($watcherId, $stream) use (&$invoked) {
|
||||
$this->assertInternalType("string", $watcherId);
|
||||
$this->assertSame(STDOUT, $stream);
|
||||
$invoked++;
|
||||
\Amp\cancel($watcherId);
|
||||
});
|
||||
\Amp\run();
|
||||
$this->assertSame(1, $invoked);
|
||||
}
|
||||
|
||||
public function testDisablingWatcherPreventsSubsequentInvocation() {
|
||||
$increment = 0;
|
||||
$watcherId = \Amp\immediately(function () use (&$increment) {
|
||||
$increment++;
|
||||
});
|
||||
|
||||
\Amp\disable($watcherId);
|
||||
\Amp\once('\Amp\stop', $msDelay = 50);
|
||||
\Amp\run();
|
||||
|
||||
$this->assertEquals(0, $increment);
|
||||
}
|
||||
|
||||
public function testImmediateExecution() {
|
||||
$increment = 0;
|
||||
\Amp\immediately(function () use (&$increment) {
|
||||
$increment++;
|
||||
});
|
||||
\Amp\tick();
|
||||
|
||||
$this->assertEquals(1, $increment);
|
||||
}
|
||||
|
||||
public function testImmediatelyCallbacksDontRecurseInSameTick() {
|
||||
$increment = 0;
|
||||
\Amp\immediately(function () use (&$increment) {
|
||||
$increment++;
|
||||
\Amp\immediately(function () use (&$increment) {
|
||||
$increment++;
|
||||
});
|
||||
});
|
||||
\Amp\tick();
|
||||
$this->assertEquals(1, $increment);
|
||||
}
|
||||
|
||||
public function testTickExecutesReadyEvents() {
|
||||
$increment = 0;
|
||||
\Amp\immediately(function () use (&$increment) {
|
||||
$increment++;
|
||||
});
|
||||
\Amp\tick();
|
||||
$this->assertEquals(1, $increment);
|
||||
}
|
||||
|
||||
public function testRunExecutesEventsUntilExplicitlyStopped() {
|
||||
$increment = 0;
|
||||
\Amp\repeat(function ($watcherId) use (&$increment) {
|
||||
$increment++;
|
||||
if ($increment === 10) {
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
}, $msInterval = 5);
|
||||
\Amp\run();
|
||||
$this->assertEquals(10, $increment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
* @expectedExceptionMessage test
|
||||
*/
|
||||
public function testReactorAllowsExceptionToBubbleUpDuringTick() {
|
||||
\Amp\immediately(function () {
|
||||
throw new \RuntimeException("test");
|
||||
});
|
||||
\Amp\tick();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
* @expectedExceptionMessage test
|
||||
*/
|
||||
public function testReactorAllowsExceptionToBubbleUpDuringRun() {
|
||||
\Amp\immediately(function () {
|
||||
throw new \RuntimeException("test");
|
||||
});
|
||||
\Amp\run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
* @expectedExceptionMessage test
|
||||
*/
|
||||
public function testReactorAllowsExceptionToBubbleUpFromRepeatingAlarmDuringRun() {
|
||||
\Amp\repeat(function () {
|
||||
throw new \RuntimeException("test");
|
||||
}, $msInterval = 0);
|
||||
\Amp\run();
|
||||
}
|
||||
|
||||
public function testOnSignalWatcher() {
|
||||
if (!\extension_loaded("posix") || !(\extension_loaded("pcntl") || \Amp\reactor() instanceof UvReactor)) {
|
||||
$this->markTestSkipped(
|
||||
"ext/posix and UvReactor or ext/pcntl required to test onSignal() capture"
|
||||
);
|
||||
}
|
||||
$this->expectOutputString("caught SIGUSR1");
|
||||
\Amp\run(function () {
|
||||
\Amp\once(function () {
|
||||
\posix_kill(\getmypid(), \SIGUSR1);
|
||||
\Amp\once(function () {
|
||||
\Amp\stop();
|
||||
}, 100);
|
||||
}, 1);
|
||||
|
||||
\Amp\onSignal(SIGUSR1, function ($watcherId) {
|
||||
\Amp\cancel($watcherId);
|
||||
echo "caught SIGUSR1";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function testInitiallyDisabledOnSignalWatcher() {
|
||||
if (!\extension_loaded("posix") || !(\extension_loaded("pcntl") || \Amp\reactor() instanceof UvReactor)) {
|
||||
$this->markTestSkipped(
|
||||
"ext/posix and UvReactor or ext/pcntl required to test onSignal() capture"
|
||||
);
|
||||
}
|
||||
$this->expectOutputString("caught SIGUSR1");
|
||||
|
||||
\Amp\run(function () {
|
||||
$sigWatcherId = \Amp\onSignal(SIGUSR1, function () {
|
||||
echo "caught SIGUSR1";
|
||||
\Amp\stop();
|
||||
}, $options = ["enable" => false]);
|
||||
|
||||
\Amp\once(function () use ($sigWatcherId) {
|
||||
\Amp\enable($sigWatcherId);
|
||||
\Amp\once(function () use ($sigWatcherId) {
|
||||
\posix_kill(\getmypid(), \SIGUSR1);
|
||||
}, 10);
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
|
||||
public function testCancelRemovesWatcher() {
|
||||
$watcherId = \Amp\once(function (){
|
||||
$this->fail('Watcher was not cancelled as expected');
|
||||
}, $msDelay = 20);
|
||||
|
||||
\Amp\immediately(function () use ($watcherId) {
|
||||
\Amp\cancel($watcherId);
|
||||
});
|
||||
\Amp\once('\Amp\stop', $msDelay = 5);
|
||||
\Amp\run();
|
||||
}
|
||||
|
||||
public function testOnWritableWatcher() {
|
||||
$flag = false;
|
||||
\Amp\onWritable(STDOUT, function () use (&$flag) {
|
||||
$flag = true;
|
||||
\Amp\stop();
|
||||
});
|
||||
\Amp\once('\Amp\stop', $msDelay = 50);
|
||||
|
||||
\Amp\run();
|
||||
$this->assertTrue($flag);
|
||||
}
|
||||
|
||||
public function testInitiallyDisabledWriteWatcher() {
|
||||
$increment = 0;
|
||||
$options = ["enable" => false];
|
||||
\Amp\onWritable(STDOUT, function () use (&$increment) {
|
||||
$increment++;
|
||||
}, $options);
|
||||
\Amp\once('\Amp\stop', $msDelay = 50);
|
||||
\Amp\run();
|
||||
|
||||
$this->assertSame(0, $increment);
|
||||
}
|
||||
|
||||
public function testInitiallyDisabledWriteWatcherIsTriggeredOnceEnabled() {
|
||||
$increment = 0;
|
||||
$options = ["enable" => false];
|
||||
$watcherId = \Amp\onWritable(STDOUT, function () use (&$increment) {
|
||||
$increment++;
|
||||
}, $options);
|
||||
\Amp\immediately(function () use ($watcherId) {
|
||||
\Amp\enable($watcherId);
|
||||
});
|
||||
|
||||
\Amp\once('\Amp\stop', $msDelay = 250);
|
||||
\Amp\run();
|
||||
|
||||
$this->assertTrue($increment > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testStreamWatcherDoesntSwallowExceptions() {
|
||||
\Amp\onWritable(STDOUT, function () { throw new \RuntimeException; });
|
||||
\Amp\once('\Amp\stop', $msDelay = 50);
|
||||
\Amp\run();
|
||||
}
|
||||
|
||||
public function testGarbageCollection() {
|
||||
\Amp\once('\Amp\stop', $msDelay = 100);
|
||||
\Amp\run();
|
||||
}
|
||||
|
||||
public function testOnStartGeneratorResolvesAutomatically() {
|
||||
$test = '';
|
||||
\Amp\run(function () use (&$test) {
|
||||
yield;
|
||||
$test = "Thus Spake Zarathustra";
|
||||
\Amp\once('\Amp\stop', 1);
|
||||
});
|
||||
$this->assertSame("Thus Spake Zarathustra", $test);
|
||||
}
|
||||
|
||||
public function testImmediatelyGeneratorResolvesAutomatically() {
|
||||
$test = '';
|
||||
\Amp\immediately(function () use (&$test) {
|
||||
yield;
|
||||
$test = "The abyss will gaze back into you";
|
||||
\Amp\once('\Amp\stop', 50);
|
||||
});
|
||||
\Amp\run();
|
||||
$this->assertSame("The abyss will gaze back into you", $test);
|
||||
}
|
||||
|
||||
public function testOnceGeneratorResolvesAutomatically() {
|
||||
$test = '';
|
||||
$gen = function () use (&$test) {
|
||||
yield;
|
||||
$test = "There are no facts, only interpretations.";
|
||||
\Amp\once('\Amp\stop', 50);
|
||||
};
|
||||
\Amp\once($gen, 1);
|
||||
\Amp\run();
|
||||
$this->assertSame("There are no facts, only interpretations.", $test);
|
||||
}
|
||||
|
||||
public function testRepeatGeneratorResolvesAutomatically() {
|
||||
$test = '';
|
||||
$gen = function ($watcherId) use (&$test) {
|
||||
\Amp\cancel($watcherId);
|
||||
yield;
|
||||
$test = "Art is the supreme task";
|
||||
\Amp\stop();
|
||||
};
|
||||
\Amp\repeat($gen, 50);
|
||||
\Amp\run();
|
||||
$this->assertSame("Art is the supreme task", $test);
|
||||
}
|
||||
|
||||
public function testOnErrorCallbackInterceptsUncaughtException() {
|
||||
$var = null;
|
||||
\Amp\onError(function ($e) use (&$var) {
|
||||
$var = $e->getMessage();
|
||||
});
|
||||
\Amp\run(function () { throw new \Exception('test'); });
|
||||
$this->assertSame('test', $var);
|
||||
}
|
||||
|
||||
public function testReactorRunsUntilNoWatchersRemain() {
|
||||
$var1 = 0;
|
||||
\Amp\repeat(function ($watcherId) use (&$var1) {
|
||||
if (++$var1 === 3) {
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
$var2 = 0;
|
||||
\Amp\onWritable(STDOUT, function ($watcherId) use (&$var2) {
|
||||
if (++$var2 === 4) {
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
});
|
||||
|
||||
\Amp\run();
|
||||
|
||||
$this->assertSame(3, $var1);
|
||||
$this->assertSame(4, $var2);
|
||||
}
|
||||
|
||||
public function testReactorRunsUntilNoWatchersRemainWhenStartedImmediately() {
|
||||
$var1 = 0;
|
||||
$var2 = 0;
|
||||
\Amp\run(function () use (&$var1, &$var2) {
|
||||
\Amp\repeat(function ($watcherId) use (&$var1) {
|
||||
if (++$var1 === 3) {
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
\Amp\onWritable(STDOUT, function ($watcherId) use (&$var2) {
|
||||
if (++$var2 === 4) {
|
||||
\Amp\cancel($watcherId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$this->assertSame(3, $var1);
|
||||
$this->assertSame(4, $var2);
|
||||
}
|
||||
|
||||
public function testOptionalCallbackDataPassedOnInvocation() {
|
||||
$callbackData = new \StdClass;
|
||||
$options = ["cb_data" => $callbackData];
|
||||
|
||||
\Amp\immediately(function ($watcherId, $callbackData) {
|
||||
$callbackData->immediately = true;
|
||||
}, $options);
|
||||
\Amp\once(function ($watcherId, $callbackData) {
|
||||
$callbackData->once = true;
|
||||
}, 1, $options);
|
||||
\Amp\repeat(function ($watcherId, $callbackData) {
|
||||
$callbackData->repeat = true;
|
||||
\Amp\cancel($watcherId);
|
||||
}, 1, $options);
|
||||
\Amp\onWritable(STDERR, function ($watcherId, $stream, $callbackData) {
|
||||
$callbackData->onWritable = true;
|
||||
\Amp\cancel($watcherId);
|
||||
}, $options);
|
||||
\Amp\run();
|
||||
|
||||
$this->assertTrue($callbackData->immediately);
|
||||
$this->assertTrue($callbackData->once);
|
||||
$this->assertTrue($callbackData->repeat);
|
||||
$this->assertTrue($callbackData->onWritable);
|
||||
}
|
||||
|
||||
public function testOptionalRepeatWatcherDelay() {
|
||||
$invoked = false;
|
||||
\Amp\repeat(function ($watcherId) use (&$invoked) {
|
||||
$invoked = true;
|
||||
\Amp\cancel($watcherId);
|
||||
}, $msInterval = 10000, $options = ["ms_delay" => 1]);
|
||||
\Amp\once('\Amp\stop', 50);
|
||||
\Amp\run();
|
||||
$this->assertTrue($invoked);
|
||||
}
|
||||
|
||||
public function testOptionalDisable() {
|
||||
$options = ["enable" => false];
|
||||
|
||||
\Amp\immediately(function ($watcherId, $callbackData) {
|
||||
$this->fail("disabled watcher should not invoke callback");
|
||||
}, $options);
|
||||
\Amp\once(function ($watcherId, $callbackData) {
|
||||
$this->fail("disabled watcher should not invoke callback");
|
||||
}, 1, $options);
|
||||
\Amp\repeat(function ($watcherId, $callbackData) {
|
||||
$this->fail("disabled watcher should not invoke callback");
|
||||
\Amp\cancel($watcherId);
|
||||
}, 1, $options);
|
||||
\Amp\onWritable(STDERR, function ($watcherId, $stream, $callbackData) {
|
||||
$this->fail("disabled watcher should not invoke callback");
|
||||
\Amp\cancel($watcherId);
|
||||
}, $options);
|
||||
|
||||
\Amp\run();
|
||||
}
|
||||
}
|
@ -2,6 +2,11 @@
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
class StructTestFixture {
|
||||
use \Amp\Struct;
|
||||
public $callback;
|
||||
}
|
||||
|
||||
class StructTest extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @expectedException \DomainException
|
||||
|
@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
use Amp\UvReactor;
|
||||
|
||||
class UvReactorTest extends ReactorTest {
|
||||
public static function setUpBeforeClass() {
|
||||
if (!defined('SIGUSR1') && extension_loaded("uv")) {
|
||||
define('SIGUSR1', \Uv::SIGUSR1);
|
||||
}
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
if (extension_loaded("uv")) {
|
||||
\Amp\reactor($assign = new UvReactor);
|
||||
} else {
|
||||
$this->markTestSkipped(
|
||||
"php-uv extension not loaded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetLoop() {
|
||||
$result = \Amp\reactor()->getLoop();
|
||||
$this->assertInternalType("resource", $result);
|
||||
}
|
||||
|
||||
public function testOnSignalWatcherKeepAliveRunResult() {
|
||||
\Amp\run(function () {
|
||||
\Amp\onSignal(\Uv::SIGUSR1, function () {
|
||||
// empty
|
||||
}, $options = ["keep_alive" => false]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to override the default ReactorTest function to use the correct signal constant
|
||||
*/
|
||||
public function provideRegistrationArgs() {
|
||||
$result = [
|
||||
["immediately", [function () {}]],
|
||||
["once", [function () {}, 5000]],
|
||||
["repeat", [function () {}, 5000]],
|
||||
["onWritable", [\STDOUT, function () {}]],
|
||||
["onReadable", [\STDIN, function () {}]],
|
||||
];
|
||||
|
||||
if (\extension_loaded("uv")) {
|
||||
$result[] = ["onSignal", [\Uv::SIGUSR1, function () {}]];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Test;
|
||||
|
||||
require __DIR__ . "/../vendor/autoload.php";
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
class PromisorPrivateImpl implements \Amp\Promisor {
|
||||
use \Amp\PrivatePromisor;
|
||||
}
|
||||
class PromisorPublicImpl implements \Amp\Promisor, \Amp\Promise {
|
||||
use \Amp\PublicPromisor;
|
||||
}
|
||||
class StructTestFixture {
|
||||
use \Amp\Struct;
|
||||
public $callback;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user