1
0
mirror of https://github.com/danog/parallel.git synced 2024-12-02 09:37:57 +01:00

Update tests; code cleanup

This commit is contained in:
Aaron Piotrowski 2016-08-18 17:36:58 -05:00
parent 0e1e0393ff
commit 70097cb225
43 changed files with 475 additions and 721 deletions

View File

@ -2,6 +2,8 @@ language: php
php: php:
- 7.0 - 7.0
- 7.1
- nightly
sudo: false sudo: false
@ -24,6 +26,3 @@ script:
after_script: after_script:
- composer require satooshi/php-coveralls dev-master - composer require satooshi/php-coveralls dev-master
- vendor/bin/coveralls -v --exclude-no-stmt - vendor/bin/coveralls -v --exclude-no-stmt
notifications:
slack: icicleio:zLkB18HsYCz8TbdK7RVqbi48

View File

@ -50,9 +50,8 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"Amp\\Benchmarks\\Concurrent\\": "benchmarks", "Amp\\Concurrent\\Example\\": "examples",
"Amp\\Examples\\Concurrent\\": "examples", "Amp\\Concurrent\\Test\\": "test"
"Amp\\Tests\\Concurrent\\": "tests"
} }
} }
} }

View File

@ -1,11 +1,9 @@
<?php <?php
namespace Amp\Examples\Concurrent; namespace Amp\Concurrent\Example;
use Amp\Concurrent\Worker\Environment; use Amp\Concurrent\Worker\{ Environment, Task };
use Amp\Concurrent\Worker\Task;
class BlockingTask implements Task class BlockingTask implements Task {
{
/** /**
* @var callable * @var callable
*/ */
@ -20,8 +18,7 @@ class BlockingTask implements Task
* @param callable $function Do not use a closure or non-serializable object. * @param callable $function Do not use a closure or non-serializable object.
* @param mixed ...$args Arguments to pass to the function. Must be serializable. * @param mixed ...$args Arguments to pass to the function. Must be serializable.
*/ */
public function __construct(callable $function, ...$args) public function __construct(callable $function, ...$args) {
{
$this->function = $function; $this->function = $function;
$this->args = $args; $this->args = $args;
} }
@ -29,8 +26,7 @@ class BlockingTask implements Task
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function run(Environment $environment) public function run(Environment $environment) {
{
return ($this->function)(...$this->args); return ($this->function)(...$this->args);
} }
} }

View File

@ -4,7 +4,7 @@ require dirname(__DIR__).'/vendor/autoload.php';
use Amp\Concurrent\Worker\DefaultPool; use Amp\Concurrent\Worker\DefaultPool;
use Amp\Coroutine; use Amp\Coroutine;
use Amp\Examples\Concurrent\BlockingTask; use Amp\Concurrent\Example\BlockingTask;
Amp\execute(function() { Amp\execute(function() {
$timer = Amp\repeat(100, function () { $timer = Amp\repeat(100, function () {

View File

@ -3,7 +3,7 @@
require dirname(__DIR__).'/vendor/autoload.php'; require dirname(__DIR__).'/vendor/autoload.php';
use Amp\Concurrent\Worker\DefaultWorkerFactory; use Amp\Concurrent\Worker\DefaultWorkerFactory;
use Amp\Examples\Concurrent\BlockingTask; use Amp\Concurrent\Example\BlockingTask;
Amp\execute(function () { Amp\execute(function () {
$factory = new DefaultWorkerFactory(); $factory = new DefaultWorkerFactory();

View File

@ -25,7 +25,7 @@ class PanicError extends \Error {
* *
* @return string * @return string
*/ */
public function getPanicTrace() { public function getPanicTrace(): string {
return $this->trace; return $this->trace;
} }
} }

View File

@ -305,8 +305,7 @@ class Process implements ProcessContext {
* *
* @return mixed[] Array of options. * @return mixed[] Array of options.
*/ */
public function getOptions(): array public function getOptions(): array {
{
return $this->options; return $this->options;
} }

View File

@ -2,7 +2,7 @@
namespace Amp\Concurrent\Sync; namespace Amp\Concurrent\Sync;
use Amp\Concurrent\{ChannelException, SerializationException}; use Amp\Concurrent\{ ChannelException, SerializationException };
use Amp\Coroutine; use Amp\Coroutine;
use Amp\Stream\Stream; use Amp\Stream\Stream;
use Interop\Async\Awaitable; use Interop\Async\Awaitable;

View File

@ -32,8 +32,7 @@ class FileMutex implements Mutex {
/** /**
* Creates a new mutex. * Creates a new mutex.
*/ */
public function __construct() public function __construct() {
{
$this->fileName = \tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock'; $this->fileName = \tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock';
} }

View File

@ -149,7 +149,7 @@ class PosixSemaphore implements Semaphore, \Serializable {
if ($errno !== MSG_ENOMSG) { if ($errno !== MSG_ENOMSG) {
throw new SemaphoreException('Failed to acquire a lock.'); throw new SemaphoreException('Failed to acquire a lock.');
} }
} while (yield new Pause(self::LATENCY_TIMEOUT)); } while (yield new Pause(self::LATENCY_TIMEOUT, true));
} }
/** /**

View File

@ -216,8 +216,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* *
* Calling `free()` on an object already freed will have no effect. * Calling `free()` on an object already freed will have no effect.
*/ */
public function free() public function free() {
{
if (!$this->isFreed()) { if (!$this->isFreed()) {
// Invalidate the memory block by setting its state to FREED. // Invalidate the memory block by setting its state to FREED.
$this->setHeader(static::STATE_FREED, 0, 0); $this->setHeader(static::STATE_FREED, 0, 0);
@ -239,8 +238,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* *
* @return string The serialized object handle. * @return string The serialized object handle.
*/ */
public function serialize(): string public function serialize(): string {
{
return \serialize([$this->key, $this->semaphore]); return \serialize([$this->key, $this->semaphore]);
} }
@ -249,8 +247,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* *
* @param string $serialized The serialized object handle. * @param string $serialized The serialized object handle.
*/ */
public function unserialize($serialized) public function unserialize($serialized) {
{
list($this->key, $this->semaphore) = \unserialize($serialized); list($this->key, $this->semaphore) = \unserialize($serialized);
$this->memOpen($this->key, 'w', 0, 0); $this->memOpen($this->key, 'w', 0, 0);
} }
@ -258,8 +255,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function __clone() public function __clone() {
{
$value = $this->unwrap(); $value = $this->unwrap();
$header = $this->getHeader(); $header = $this->getHeader();
$this->init($value, $header['size'], $header['permissions']); $this->init($value, $header['size'], $header['permissions']);
@ -270,8 +266,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* *
* @return array An array of debugging information. * @return array An array of debugging information.
*/ */
public function __debugInfo() public function __debugInfo() {
{
if ($this->isFreed()) { if ($this->isFreed()) {
return [ return [
'id' => $this->key, 'id' => $this->key,
@ -291,8 +286,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* Updates the current memory segment handle, handling any moves made on the * Updates the current memory segment handle, handling any moves made on the
* data. * data.
*/ */
private function handleMovedMemory() private function handleMovedMemory() {
{
// Read from the memory block and handle moved blocks until we find the // Read from the memory block and handle moved blocks until we find the
// correct block. // correct block.
while (true) { while (true) {
@ -315,8 +309,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* *
* @return array An associative array of header data. * @return array An associative array of header data.
*/ */
private function getHeader(): array private function getHeader(): array {
{
$data = $this->memGet(0, self::MEM_DATA_OFFSET); $data = $this->memGet(0, self::MEM_DATA_OFFSET);
return \unpack('Cstate/Lsize/Spermissions', $data); return \unpack('Cstate/Lsize/Spermissions', $data);
} }
@ -328,8 +321,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* @param int $size The size of the stored data, or other value. * @param int $size The size of the stored data, or other value.
* @param int $permissions The permissions mask on the memory segment. * @param int $permissions The permissions mask on the memory segment.
*/ */
private function setHeader(int $state, int $size, int $permissions) private function setHeader(int $state, int $size, int $permissions) {
{
$header = \pack('CLS', $state, $size, $permissions); $header = \pack('CLS', $state, $size, $permissions);
$this->memSet(0, $header); $this->memSet(0, $header);
} }
@ -342,8 +334,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* @param int $permissions Process permissions on the shared memory. * @param int $permissions Process permissions on the shared memory.
* @param int $size The size to crate the shared memory in bytes. * @param int $size The size to crate the shared memory in bytes.
*/ */
private function memOpen(int $key, string $mode, int $permissions, int $size) private function memOpen(int $key, string $mode, int $permissions, int $size) {
{
$this->handle = @\shmop_open($key, $mode, $permissions, $size); $this->handle = @\shmop_open($key, $mode, $permissions, $size);
if ($this->handle === false) { if ($this->handle === false) {
throw new SharedMemoryException('Failed to create shared memory block.'); throw new SharedMemoryException('Failed to create shared memory block.');
@ -358,8 +349,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* *
* @return string The binary data at the given offset. * @return string The binary data at the given offset.
*/ */
private function memGet(int $offset, int $size): string private function memGet(int $offset, int $size): string {
{
$data = \shmop_read($this->handle, $offset, $size); $data = \shmop_read($this->handle, $offset, $size);
if ($data === false) { if ($data === false) {
throw new SharedMemoryException('Failed to read from shared memory block.'); throw new SharedMemoryException('Failed to read from shared memory block.');
@ -373,8 +363,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
* @param int $offset The offset to write to. * @param int $offset The offset to write to.
* @param string $data The binary data to write. * @param string $data The binary data to write.
*/ */
private function memSet(int $offset, string $data) private function memSet(int $offset, string $data) {
{
if (!\shmop_write($this->handle, $data, $offset)) { if (!\shmop_write($this->handle, $data, $offset)) {
throw new SharedMemoryException('Failed to write to shared memory block.'); throw new SharedMemoryException('Failed to write to shared memory block.');
} }
@ -383,8 +372,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
/** /**
* Requests the shared memory segment to be deleted. * Requests the shared memory segment to be deleted.
*/ */
private function memDelete() private function memDelete() {
{
if (!\shmop_delete($this->handle)) { if (!\shmop_delete($this->handle)) {
throw new SharedMemoryException('Failed to discard shared memory block.'); throw new SharedMemoryException('Failed to discard shared memory block.');
} }

View File

@ -75,8 +75,7 @@ class Thread extends \Thread {
} }
if (null === $autoloadPath) { if (null === $autoloadPath) {
echo 'Could not locate autoload.php.'; throw new \Error('Could not locate autoload.php');
exit(1);
} }
require $autoloadPath; require $autoloadPath;
@ -85,11 +84,7 @@ class Thread extends \Thread {
try { try {
\Amp\execute(function () { \Amp\execute(function () {
try { $channel = new ChannelledStream(new Socket($this->socket, false));
$channel = new ChannelledStream(new Socket($this->socket, false));
} catch (\Throwable $exception) {
return 1; // Parent has destroyed Thread object, so just exit.
}
$watcher = \Amp\repeat(self::KILL_CHECK_FREQUENCY, function () { $watcher = \Amp\repeat(self::KILL_CHECK_FREQUENCY, function () {
if ($this->killed) { if ($this->killed) {

View File

@ -25,16 +25,14 @@ class Parcel implements SyncParcel {
* *
* @param mixed $value The value to store in the container. * @param mixed $value The value to store in the container.
*/ */
public function __construct($value) public function __construct($value) {
{
$this->init($value); $this->init($value);
} }
/** /**
* @param mixed $value * @param mixed $value
*/ */
private function init($value) private function init($value) {
{
$this->mutex = new Mutex(); $this->mutex = new Mutex();
$this->storage = new Internal\Storage($value); $this->storage = new Internal\Storage($value);
} }

View File

@ -3,6 +3,7 @@
namespace Amp\Concurrent\Threading; namespace Amp\Concurrent\Threading;
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore; use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
use Interop\Async\Awaitable;
/** /**
* An asynchronous semaphore based on pthreads' synchronization methods. * An asynchronous semaphore based on pthreads' synchronization methods.
@ -28,8 +29,7 @@ class Semaphore implements SyncSemaphore {
* *
* @param int $locks The maximum number of locks that can be acquired from the semaphore. * @param int $locks The maximum number of locks that can be acquired from the semaphore.
*/ */
public function __construct(int $locks) public function __construct(int $locks) {
{
$this->init($locks); $this->init($locks);
} }
@ -38,8 +38,7 @@ class Semaphore implements SyncSemaphore {
* *
* @param int $locks * @param int $locks
*/ */
private function init(int $locks) private function init(int $locks) {
{
$locks = (int) $locks; $locks = (int) $locks;
if ($locks < 1) { if ($locks < 1) {
$locks = 1; $locks = 1;
@ -52,32 +51,28 @@ class Semaphore implements SyncSemaphore {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function count(): int public function count(): int {
{
return $this->semaphore->count(); return $this->semaphore->count();
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getSize(): int public function getSize(): int {
{
return $this->maxLocks; return $this->maxLocks;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function acquire(): \Generator public function acquire(): Awaitable {
{
return $this->semaphore->acquire(); return $this->semaphore->acquire();
} }
/** /**
* Clones the semaphore, creating a new instance with the same number of locks, all available. * Clones the semaphore, creating a new instance with the same number of locks, all available.
*/ */
public function __clone() public function __clone() {
{
$this->init($this->getSize()); $this->init($this->getSize());
} }
} }

View File

@ -253,7 +253,6 @@ class Thread implements Strand {
} }
if ($data instanceof ExitStatus) { if ($data instanceof ExitStatus) {
$this->kill();
throw new \Error('Cannot send exit status objects.'); throw new \Error('Cannot send exit status objects.');
} }

View File

@ -140,8 +140,7 @@ class DefaultPool implements Pool {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getIdleWorkerCount(): int public function getIdleWorkerCount(): int {
{
return $this->idleWorkers->count(); return $this->idleWorkers->count();
} }

View File

@ -9,7 +9,7 @@ use Amp\Concurrent\Process\ChannelledProcess;
*/ */
class WorkerProcess extends AbstractWorker { class WorkerProcess extends AbstractWorker {
public function __construct() { public function __construct() {
$dir = \dirname(\dirname(__DIR__)) . '/bin'; $dir = \dirname(__DIR__, 2) . '/bin';
parent::__construct(new ChannelledProcess($dir . '/worker.php', $dir)); parent::__construct(new ChannelledProcess($dir . '/worker.php', $dir));
} }
} }

View File

@ -17,13 +17,11 @@
<directory>test</directory> <directory>test</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<filter> <filter>
<whitelist> <whitelist>
<directory suffix=".php">src</directory> <directory suffix=".php">lib</directory>
</whitelist> </whitelist>
</filter> </filter>
<logging> <logging>
<log type="coverage-html" target="build/coverage" title="Amp" highlight="true"/> <log type="coverage-html" target="build/coverage" title="Amp" highlight="true"/>
</logging> </logging>

View File

@ -1,18 +1,19 @@
<?php <?php
namespace Amp\Tests\Concurrent; namespace Amp\Concurrent\Test;
use Amp\Concurrent\Sync\Internal\ExitSuccess; use Amp\Concurrent\Sync\Internal\ExitSuccess;
use Amp\Coroutine;
use Amp\Loop;
abstract class AbstractContextTest extends TestCase abstract class AbstractContextTest extends TestCase {
{ /**
* @param callable $function
*
* @return \Amp\Concurrent\Context
*/
abstract public function createContext(callable $function); abstract public function createContext(callable $function);
public function testIsRunning() public function testIsRunning() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
usleep(100); usleep(100);
}); });
@ -23,16 +24,13 @@ abstract class AbstractContextTest extends TestCase
$this->assertTrue($context->isRunning()); $this->assertTrue($context->isRunning());
yield from $context->join(); yield $context->join();
$this->assertFalse($context->isRunning()); $this->assertFalse($context->isRunning());
})->done(); });
Loop\run();
} }
public function testKill() public function testKill() {
{
$context = $this->createContext(function () { $context = $this->createContext(function () {
usleep(1e6); usleep(1e6);
}); });
@ -45,10 +43,9 @@ abstract class AbstractContextTest extends TestCase
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\StatusError * @expectedException \Amp\Concurrent\StatusError
*/ */
public function testStartWhileRunningThrowsError() public function testStartWhileRunningThrowsError() {
{
$context = $this->createContext(function () { $context = $this->createContext(function () {
usleep(100); usleep(100);
}); });
@ -58,224 +55,185 @@ abstract class AbstractContextTest extends TestCase
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\StatusError * @expectedException \Amp\Concurrent\StatusError
*/ */
public function testStartMultipleTimesThrowsError() public function testStartMultipleTimesThrowsError() {
{
Loop\loop();
$this->assertRunTimeGreaterThan(function () { $this->assertRunTimeGreaterThan(function () {
Coroutine\create(function () { \Amp\execute(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
sleep(1); sleep(1);
}); });
$context->start(); $context->start();
yield from $context->join(); yield $context->join();
$context->start(); $context->start();
yield from $context->join(); yield $context->join();
})->done(); });
Loop\run();
}, 2); }, 2);
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\PanicError * @expectedException \Amp\Concurrent\PanicError
*/ */
public function testExceptionInContextPanics() public function testExceptionInContextPanics() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
throw new \Exception('Exception in fork.'); throw new \Exception('Exception in fork.');
}); });
$context->start(); $context->start();
yield from $context->join(); yield $context->join();
})->done(); });
Loop\run();
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\PanicError * @expectedException \Amp\Concurrent\PanicError
*/ */
public function testReturnUnserializableDataPanics() public function testReturnUnserializableDataPanics() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
return yield function () {}; return yield function () {};
}); });
$context->start(); $context->start();
yield from $context->join(); yield $context->join();
})->done(); });
Loop\run();
} }
public function testJoinWaitsForChild() public function testJoinWaitsForChild() {
{
Loop\loop();
$this->assertRunTimeGreaterThan(function () { $this->assertRunTimeGreaterThan(function () {
Coroutine\create(function () { \Amp\execute(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
sleep(1); sleep(1);
}); });
$context->start(); $context->start();
yield from $context->join(); yield $context->join();
})->done(); });
Loop\run();
}, 1); }, 1);
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\StatusError * @expectedException \Amp\Concurrent\StatusError
*/ */
public function testJoinWithoutStartThrowsError() public function testJoinWithoutStartThrowsError() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
usleep(100); usleep(100);
}); });
yield from $context->join(); yield $context->join();
})->done(); });
Loop\run();
} }
public function testJoinResolvesWithContextReturn() public function testJoinResolvesWithContextReturn() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
return 42; return 42;
}); });
$context->start(); $context->start();
$this->assertSame(42, yield from $context->join()); $this->assertSame(42, yield $context->join());
})->done(); });
Loop\run();
} }
public function testSendAndReceive() public function testSendAndReceive() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
yield from $this->send(1); yield $this->send(1);
$value = yield from $this->receive(); $value = yield $this->receive();
return $value; return $value;
}); });
$value = 42; $value = 42;
$context->start(); $context->start();
$this->assertSame(1, yield from $context->receive()); $this->assertSame(1, yield $context->receive());
yield from $context->send($value); yield $context->send($value);
$this->assertSame($value, yield from $context->join()); $this->assertSame($value, yield $context->join());
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendAndReceive * @depends testSendAndReceive
* @expectedException \Amp\Concurrent\Exception\SynchronizationError * @expectedException \Amp\Concurrent\SynchronizationError
*/ */
public function testJoinWhenContextSendingData() public function testJoinWhenContextSendingData() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
yield from $this->send(0); yield $this->send(0);
return 42; return 42;
}); });
$context->start(); $context->start();
$value = yield from $context->join(); $value = yield $context->join();
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendAndReceive * @depends testSendAndReceive
* @expectedException \Amp\Concurrent\Exception\StatusError * @expectedException \Amp\Concurrent\StatusError
*/ */
public function testReceiveBeforeContextHasStarted() public function testReceiveBeforeContextHasStarted() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
yield from $this->send(0); yield $this->send(0);
return 42; return 42;
}); });
$value = yield from $context->receive(); $value = yield $context->receive();
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendAndReceive * @depends testSendAndReceive
* @expectedException \Amp\Concurrent\Exception\StatusError * @expectedException \Amp\Concurrent\StatusError
*/ */
public function testSendBeforeContextHasStarted() public function testSendBeforeContextHasStarted() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
yield from $this->send(0); yield $this->send(0);
return 42; return 42;
}); });
yield from $context->send(0); yield $context->send(0);
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendAndReceive * @depends testSendAndReceive
* @expectedException \Amp\Concurrent\Exception\SynchronizationError * @expectedException \Amp\Concurrent\SynchronizationError
*/ */
public function testReceiveWhenContextHasReturned() public function testReceiveWhenContextHasReturned() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
yield from $this->send(0); yield $this->send(0);
return 42; return 42;
}); });
$context->start(); $context->start();
$value = yield from $context->receive(); $value = yield $context->receive();
$value = yield from $context->receive(); $value = yield $context->receive();
$value = yield from $context->join(); $value = yield $context->join();
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendAndReceive * @depends testSendAndReceive
* @expectedException \Amp\Exception\InvalidArgumentError * @expectedException \Error
*/ */
public function testSendExitStatus() public function testSendExitStatus() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$context = $this->createContext(function () { $context = $this->createContext(function () {
$value = yield from $this->receive(); $value = yield $this->receive();
return 42; return 42;
}); });
$context->start(); $context->start();
yield from $context->send(new ExitSuccess(0)); yield $context->send(new ExitSuccess(0));
$value = yield from $context->join(); $value = yield $context->join();
})->done(); });
Loop\run();
} }
} }

View File

@ -1,33 +1,26 @@
<?php <?php
namespace Amp\Tests\Concurrent\Forking; namespace Amp\Concurrent\Test\Forking;
use Amp\Concurrent\Forking\Fork; use Amp\Concurrent\Forking\Fork;
use Amp\Coroutine; use Amp\Concurrent\Test\AbstractContextTest;
use Amp\Loop;
use Amp\Tests\Concurrent\AbstractContextTest;
/** /**
* @group forking * @group forking
* @requires extension pcntl * @requires extension pcntl
*/ */
class ForkTest extends AbstractContextTest class ForkTest extends AbstractContextTest {
{ public function createContext(callable $function) {
public function createContext(callable $function)
{
return new Fork($function); return new Fork($function);
} }
public function testSpawnStartsFork() public function testSpawnStartsFork() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$fork = Fork::spawn(function () { $fork = Fork::spawn(function () {
usleep(100); usleep(100);
}); });
return yield from $fork->join(); return yield $fork->join();
})->done(); });
Loop\run();
} }
} }

View File

@ -1,8 +1,7 @@
<?php <?php
namespace Amp\Tests\Concurrent\Stub; namespace Amp\Concurrent\Test\Stub;
class CallbackStub class CallbackStub {
{
public function __invoke() {} public function __invoke() {}
} }

View File

@ -1,32 +1,26 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Coroutine; use Amp\Concurrent\Test\TestCase;
use Amp\Loop;
use Amp\Tests\Concurrent\TestCase;
abstract class AbstractParcelTest extends TestCase abstract class AbstractParcelTest extends TestCase {
{
/** /**
* @return \Amp\Concurrent\Sync\Parcel * @return \Amp\Concurrent\Sync\Parcel
*/ */
abstract protected function createParcel($value); abstract protected function createParcel($value);
public function testConstructor() public function testConstructor() {
{
$object = $this->createParcel(new \stdClass()); $object = $this->createParcel(new \stdClass());
$this->assertInternalType('object', $object->unwrap()); $this->assertInternalType('object', $object->unwrap());
} }
public function testUnwrapIsOfCorrectType() public function testUnwrapIsOfCorrectType() {
{
$object = $this->createParcel(new \stdClass()); $object = $this->createParcel(new \stdClass());
$this->assertInstanceOf('stdClass', $object->unwrap()); $this->assertInstanceOf('stdClass', $object->unwrap());
} }
public function testUnwrapIsEqual() public function testUnwrapIsEqual() {
{
$object = new \stdClass(); $object = new \stdClass();
$shared = $this->createParcel($object); $shared = $this->createParcel($object);
$this->assertEquals($object, $shared->unwrap()); $this->assertEquals($object, $shared->unwrap());
@ -35,50 +29,46 @@ abstract class AbstractParcelTest extends TestCase
/** /**
* @depends testUnwrapIsEqual * @depends testUnwrapIsEqual
*/ */
public function testSynchronized() public function testSynchronized() {
{
$parcel = $this->createParcel(0); $parcel = $this->createParcel(0);
$coroutine = new Coroutine($parcel->synchronized(function ($value) { $awaitable = $parcel->synchronized(function ($value) {
$this->assertSame(0, $value); $this->assertSame(0, $value);
usleep(1e4); usleep(1e4);
return 1; return 1;
})); });
$callback = $this->createCallback(1); $callback = $this->createCallback(1);
$callback->method('__invoke') $callback->method('__invoke')
->with($this->identicalTo(1)); ->with($this->identicalTo(null), $this->identicalTo(1));
$coroutine->done($callback); $awaitable->when($callback);
$coroutine = new Coroutine($parcel->synchronized(function ($value) { $awaitable = $parcel->synchronized(function ($value) {
$this->assertSame(1, $value); $this->assertSame(1, $value);
usleep(1e4); usleep(1e4);
return 2; return 2;
})); });
$callback = $this->createCallback(1); $callback = $this->createCallback(1);
$callback->method('__invoke') $callback->method('__invoke')
->with($this->identicalTo(2)); ->with($this->identicalTo(null), $this->identicalTo(2));
$coroutine->done($callback); $awaitable->when($callback);
Loop\run();
} }
/** /**
* @depends testSynchronized * @depends testSynchronized
*/ */
public function testCloneIsNewParcel() public function testCloneIsNewParcel() {
{
$original = $this->createParcel(1); $original = $this->createParcel(1);
$clone = clone $original; $clone = clone $original;
$coroutine = new Coroutine($clone->synchronized(function () { $awaitable = $clone->synchronized(function () {
return 2; return 2;
})); });
$coroutine->wait(); \Amp\wait($awaitable);
$this->assertSame(1, $original->unwrap()); $this->assertSame(1, $original->unwrap());
$this->assertSame(2, $clone->unwrap()); $this->assertSame(2, $clone->unwrap());

View File

@ -1,123 +1,109 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Concurrent\Sync\Lock; use Amp\Concurrent\Sync\Lock;
use Amp\Coroutine; use Amp\Concurrent\Test\TestCase;
use Amp\Loop; use Amp\Pause;
use Amp\Tests\Concurrent\TestCase;
abstract class AbstractSemaphoreTest extends TestCase abstract class AbstractSemaphoreTest extends TestCase {
{
/** /**
* @var \Amp\Concurrent\Sync\Semaphore * @var \Amp\Concurrent\Sync\Semaphore
*/ */
protected $semaphore; protected $semaphore;
/** /**
* @param int $locks Number of locks in the semaphore.
*
* @return \Amp\Concurrent\Sync\Semaphore * @return \Amp\Concurrent\Sync\Semaphore
*/ */
abstract public function createSemaphore($locks); abstract public function createSemaphore(int $locks);
public function testCount() public function testCount() {
{
$this->semaphore = $this->createSemaphore(4); $this->semaphore = $this->createSemaphore(4);
$this->assertCount(4, $this->semaphore); $this->assertCount(4, $this->semaphore);
} }
public function testAcquire() public function testAcquire() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$lock = yield from $this->semaphore->acquire(); $lock = yield $this->semaphore->acquire();
$this->assertFalse($lock->isReleased()); $this->assertFalse($lock->isReleased());
$lock->release(); $lock->release();
$this->assertTrue($lock->isReleased()); $this->assertTrue($lock->isReleased());
})->done(); });
Loop\run();
} }
public function testAcquireMultiple() public function testAcquireMultiple() {
{
$this->assertRunTimeGreaterThan(function () { $this->assertRunTimeGreaterThan(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
Coroutine\create(function () { \Amp\execute(function () {
$lock1 = yield from $this->semaphore->acquire(); $lock1 = yield $this->semaphore->acquire();
Loop\timer(0.5, function () use ($lock1) { \Amp\delay(500, function () use ($lock1) {
$lock1->release(); $lock1->release();
}); });
$lock2 = yield from $this->semaphore->acquire(); $lock2 = yield $this->semaphore->acquire();
Loop\timer(0.5, function () use ($lock2) { \Amp\delay(500, function () use ($lock2) {
$lock2->release(); $lock2->release();
}); });
$lock3 = yield from $this->semaphore->acquire(); $lock3 = yield $this->semaphore->acquire();
Loop\timer(0.5, function () use ($lock3) { \Amp\delay(500, function () use ($lock3) {
$lock3->release(); $lock3->release();
}); });
})->done(); });
Loop\run();
}, 1.5); }, 1.5);
} }
public function testCloneIsNewSemaphore() public function testCloneIsNewSemaphore() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$clone = clone $this->semaphore; $clone = clone $this->semaphore;
$lock = yield from $clone->acquire(); $lock = yield $clone->acquire();
$this->assertCount(1, $this->semaphore); $this->assertCount(1, $this->semaphore);
$this->assertCount(0, $clone); $this->assertCount(0, $clone);
$lock->release(); $lock->release();
})->done(); });
Loop\run();
} }
public function testSerializedIsSameSemaphore() public function testSerializedIsSameSemaphore() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$unserialized = unserialize(serialize($this->semaphore)); $unserialized = unserialize(serialize($this->semaphore));
$lock = yield from $unserialized->acquire(); $lock = yield $unserialized->acquire();
$this->assertCount(0, $this->semaphore); $this->assertCount(0, $this->semaphore);
$this->assertCount(0, $unserialized); $this->assertCount(0, $unserialized);
$lock->release(); $lock->release();
})->done(); });
Loop\run();
} }
public function testSimultaneousAcquire() public function testSimultaneousAcquire() {
{
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$coroutine1 = new Coroutine\Coroutine($this->semaphore->acquire()); \Amp\execute(function () {
$coroutine2 = new Coroutine\Coroutine($this->semaphore->acquire()); $awaitable1 = $this->semaphore->acquire();
$awaitable2 = $this->semaphore->acquire();
$coroutine1->delay(0.5)->then(function (Lock $lock) {
$lock->release(); yield new Pause(500);
(yield $awaitable1)->release();
yield new Pause(500);
(yield $awaitable2)->release();
}); });
$coroutine2->delay(0.5)->then(function (Lock $lock) {
$lock->release();
});
$this->assertRunTimeGreaterThan('Amp\Loop\run', 1);
} }
} }

View File

@ -1,74 +1,57 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Concurrent\Sync\ChannelledStream; use Amp\Concurrent\Sync\ChannelledStream;
use Amp\Coroutine; use Amp\Stream\Stream;
use Amp\Loop; use Amp\Stream\ClosedException;
use Amp\Stream\{DuplexStream, ReadableStream}; use Amp\Concurrent\Test\TestCase;
use Amp\Stream\Exception\{UnreadableException, UnwritableException}; use Amp\Success;
use Amp\Tests\Concurrent\TestCase;
class ChannelledStreamTest extends TestCase class ChannelledStreamTest extends TestCase {
{
/** /**
* @return \Amp\Stream\DuplexStream|\PHPUnit_Framework_MockObject_MockObject * @return \Amp\Stream\Stream|\PHPUnit_Framework_MockObject_MockObject
*/ */
protected function createMockStream() protected function createMockStream() {
{ $mock = $this->createMock(Stream::class);
$mock = $this->getMock(DuplexStream::class);
$buffer = ''; $buffer = '';
$mock->method('write') $mock->method('write')
->will($this->returnCallback(function ($data) use (&$buffer) { ->will($this->returnCallback(function ($data) use (&$buffer) {
$buffer .= $data; $buffer .= $data;
return yield strlen($data); return new Success(\strlen($data));
})); }));
$mock->method('read') $mock->method('read')
->will($this->returnCallback(function ($length, $byte = null, $timeout = 0) use (&$buffer) { ->will($this->returnCallback(function ($length, $byte = null, $timeout = 0) use (&$buffer) {
$result = substr($buffer, 0, $length); $result = \substr($buffer, 0, $length);
$buffer = substr($buffer, $length); $buffer = \substr($buffer, $length);
return yield $result; return new Success($result);
})); }));
return $mock; return $mock;
} }
/** public function testSendReceive() {
* @expectedException \Amp\Exception\InvalidArgumentError \Amp\execute(function () {
*/
public function testReadableWithoutWritable()
{
$mock = $this->getMock(ReadableStream::class);
$channel = new ChannelledStream($mock);
}
public function testSendReceive()
{
Coroutine\create(function () {
$mock = $this->createMockStream(); $mock = $this->createMockStream();
$a = new ChannelledStream($mock); $a = new ChannelledStream($mock);
$b = new ChannelledStream($mock); $b = new ChannelledStream($mock);
$message = 'hello'; $message = 'hello';
yield from $a->send($message); yield $a->send($message);
$data = yield from $b->receive(); $data = yield $b->receive();
$this->assertSame($message, $data); $this->assertSame($message, $data);
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendReceive * @depends testSendReceive
*/ */
public function testSendReceiveLongData() public function testSendReceiveLongData() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$mock = $this->createMockStream(); $mock = $this->createMockStream();
$a = new ChannelledStream($mock); $a = new ChannelledStream($mock);
$b = new ChannelledStream($mock); $b = new ChannelledStream($mock);
@ -79,90 +62,81 @@ class ChannelledStreamTest extends TestCase
$message .= chr(mt_rand(0, 255)); $message .= chr(mt_rand(0, 255));
} }
yield from $a->send($message); yield $a->send($message);
$data = yield from $b->receive(); $data = yield $b->receive();
$this->assertSame($message, $data); $this->assertSame($message, $data);
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendReceive * @depends testSendReceive
* @expectedException \Amp\Concurrent\Exception\ChannelException * @expectedException \Amp\Concurrent\ChannelException
*/ */
public function testInvalidDataReceived() public function testInvalidDataReceived() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$mock = $this->createMockStream(); $mock = $this->createMockStream();
$a = new ChannelledStream($mock); $a = new ChannelledStream($mock);
$b = new ChannelledStream($mock); $b = new ChannelledStream($mock);
// Close $a. $b should close on next read... // Close $a. $b should close on next read...
yield from $mock->write(pack('L', 10) . '1234567890'); yield $mock->write(pack('L', 10) . '1234567890');
$data = yield from $b->receive(); $data = yield $b->receive();
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendReceive * @depends testSendReceive
* @expectedException \Amp\Concurrent\Exception\ChannelException * @expectedException \Amp\Concurrent\ChannelException
*/ */
public function testSendUnserializableData() public function testSendUnserializableData() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$mock = $this->createMockStream(); $mock = $this->createMockStream();
$a = new ChannelledStream($mock); $a = new ChannelledStream($mock);
$b = new ChannelledStream($mock); $b = new ChannelledStream($mock);
// Close $a. $b should close on next read... // Close $a. $b should close on next read...
yield from $a->send(function () {}); yield $a->send(function () {});
$data = yield from $b->receive(); $data = yield $b->receive();
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendReceive * @depends testSendReceive
* @expectedException \Amp\Concurrent\Exception\ChannelException * @expectedException \Amp\Concurrent\ChannelException
*/ */
public function testSendAfterClose() public function testSendAfterClose() {
{ \Amp\execute(function () {
Coroutine\create(function () { $mock = $this->createMock(Stream::class);
$mock = $this->getMock(DuplexStream::class);
$mock->expects($this->once()) $mock->expects($this->once())
->method('write') ->method('write')
->will($this->throwException(new UnwritableException())); ->will($this->throwException(new ClosedException));
$a = new ChannelledStream($mock); $a = new ChannelledStream($mock);
$b = new ChannelledStream($this->getMock(DuplexStream::class)); $b = new ChannelledStream($this->createMock(Stream::class));
yield from $a->send('hello'); yield $a->send('hello');
})->done(); });
Loop\run();
} }
/** /**
* @depends testSendReceive * @depends testSendReceive
* @expectedException \Amp\Concurrent\Exception\ChannelException * @expectedException \Amp\Concurrent\ChannelException
*/ */
public function testReceiveAfterClose() public function testReceiveAfterClose() {
{ \Amp\execute(function () {
Coroutine\create(function () { $mock = $this->createMock(Stream::class);
$mock = $this->getMock(DuplexStream::class);
$mock->expects($this->once()) $mock->expects($this->once())
->method('read') ->method('read')
->will($this->throwException(new UnreadableException())); ->will($this->throwException(new ClosedException));
$a = new ChannelledStream($mock); $a = new ChannelledStream($mock);
$data = yield from $a->receive(); $data = yield $a->receive();
})->done(); });
Loop\run();
} }
} }

View File

@ -1,51 +1,41 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Concurrent\Sync\FileMutex; use Amp\Concurrent\Sync\FileMutex;
use Amp\Coroutine; use Amp\Concurrent\Test\TestCase;
use Amp\Loop;
use Amp\Tests\Concurrent\TestCase;
class FileMutexTest extends TestCase class FileMutexTest extends TestCase {
{ public function testAcquire() {
public function testAcquire() \Amp\execute(function () {
{
Coroutine\create(function () {
$mutex = new FileMutex(); $mutex = new FileMutex();
$lock = yield from $mutex->acquire(); $lock = yield $mutex->acquire();
$lock->release(); $lock->release();
$this->assertTrue($lock->isReleased()); $this->assertTrue($lock->isReleased());
})->done(); });
Loop\run();
} }
public function testAcquireMultiple() public function testAcquireMultiple() {
{
Loop\loop();
$this->assertRunTimeGreaterThan(function () { $this->assertRunTimeGreaterThan(function () {
Coroutine\create(function () { \Amp\execute(function () {
$mutex = new FileMutex(); $mutex = new FileMutex();
$lock1 = yield from $mutex->acquire(); $lock1 = yield $mutex->acquire();
Loop\timer(0.5, function () use ($lock1) { \Amp\delay(500, function () use ($lock1) {
$lock1->release(); $lock1->release();
}); });
$lock2 = yield from $mutex->acquire(); $lock2 = yield $mutex->acquire();
Loop\timer(0.5, function () use ($lock2) { \Amp\delay(500, function () use ($lock2) {
$lock2->release(); $lock2->release();
}); });
$lock3 = yield from $mutex->acquire(); $lock3 = yield $mutex->acquire();
Loop\timer(0.5, function () use ($lock3) { \Amp\delay(500, function () use ($lock3) {
$lock3->release(); $lock3->release();
}); });
}); });
Loop\run();
}, 1.5); }, 1.5);
} }
} }

View File

@ -1,31 +1,27 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Concurrent\Sync\Lock; use Amp\Concurrent\Sync\Lock;
use Amp\Tests\Concurrent\TestCase; use Amp\Concurrent\Test\TestCase;
class LockTest extends TestCase class LockTest extends TestCase {
{ public function testIsReleased() {
public function testIsReleased()
{
$lock = new Lock($this->createCallback(1)); $lock = new Lock($this->createCallback(1));
$this->assertFalse($lock->isReleased()); $this->assertFalse($lock->isReleased());
$lock->release(); $lock->release();
$this->assertTrue($lock->isReleased()); $this->assertTrue($lock->isReleased());
} }
public function testIsReleasedOnDestruct() public function testIsReleasedOnDestruct() {
{
$lock = new Lock($this->createCallback(1)); $lock = new Lock($this->createCallback(1));
unset($lock); unset($lock);
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\LockAlreadyReleasedError * @expectedException \Amp\Concurrent\LockAlreadyReleasedError
*/ */
public function testThrowsOnMultiRelease() public function testThrowsOnMultiRelease() {
{
$lock = new Lock($this->createCallback(1)); $lock = new Lock($this->createCallback(1));
$lock->release(); $lock->release();
$lock->release(); $lock->release();

View File

@ -1,37 +1,36 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Concurrent\Forking\Fork; use Amp\Concurrent\Forking\Fork;
use Amp\Concurrent\Sync\{PosixSemaphore, Semaphore}; use Amp\Concurrent\Sync\{PosixSemaphore, Semaphore};
use Amp\Coroutine;
use Amp\Loop;
/** /**
* @group posix * @group posix
* @requires extension sysvmsg * @requires extension sysvmsg
*/ */
class PosixSemaphoreTest extends AbstractSemaphoreTest class PosixSemaphoreTest extends AbstractSemaphoreTest {
{ /**
public function createSemaphore($locks) * @param $locks
{ *
* @return \Amp\Concurrent\Sync\PosixSemaphore
*/
public function createSemaphore(int $locks) {
return new PosixSemaphore($locks); return new PosixSemaphore($locks);
} }
public function tearDown() public function tearDown() {
{
if ($this->semaphore && !$this->semaphore->isFreed()) { if ($this->semaphore && !$this->semaphore->isFreed()) {
$this->semaphore->free(); $this->semaphore->free();
} }
} }
public function testCloneIsNewSemaphore() public function testCloneIsNewSemaphore() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$clone = clone $this->semaphore; $clone = clone $this->semaphore;
$lock = yield from $clone->acquire(); $lock = yield $clone->acquire();
$this->assertCount(1, $this->semaphore); $this->assertCount(1, $this->semaphore);
$this->assertCount(0, $clone); $this->assertCount(0, $clone);
@ -39,13 +38,11 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
$lock->release(); $lock->release();
$clone->free(); $clone->free();
})->done(); });
Loop\run();
} }
public function testFree() public function testFree() {
{
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$this->assertFalse($this->semaphore->isFreed()); $this->assertFalse($this->semaphore->isFreed());
@ -58,29 +55,28 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
/** /**
* @requires extension pcntl * @requires extension pcntl
*/ */
public function testAcquireInMultipleForks() public function testAcquireInMultipleForks() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$fork1 = new Fork(function (Semaphore $semaphore) { $fork1 = new Fork(function (Semaphore $semaphore) {
$lock = yield from $semaphore->acquire(); $lock = yield $semaphore->acquire();
usleep(1e5); usleep(1e5);
$lock->release(); $lock->release();
yield 0; return 0;
}, $this->semaphore); }, $this->semaphore);
$fork2 = new Fork(function (Semaphore $semaphore) { $fork2 = new Fork(function (Semaphore $semaphore) {
$lock = yield from $semaphore->acquire(); $lock = yield $semaphore->acquire();
usleep(1e5); usleep(1e5);
$lock->release(); $lock->release();
yield 1; return 1;
}, $this->semaphore); }, $this->semaphore);
$start = microtime(true); $start = microtime(true);
@ -88,12 +84,10 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
$fork1->start(); $fork1->start();
$fork2->start(); $fork2->start();
yield from $fork1->join(); yield $fork1->join();
yield from $fork2->join(); yield $fork2->join();
$this->assertGreaterThan(1, microtime(true) - $start); $this->assertGreaterThan(0.1, microtime(true) - $start);
}); });
Loop\run();
} }
} }

View File

@ -1,57 +1,49 @@
<?php <?php
namespace Amp\Tests\Concurrent\Sync; namespace Amp\Concurrent\Test\Sync;
use Amp\Concurrent\Sync\SharedMemoryParcel; use Amp\Concurrent\Sync\SharedMemoryParcel;
use Amp\Coroutine;
/** /**
* @requires extension shmop * @requires extension shmop
* @requires extension sysvmsg * @requires extension sysvmsg
*/ */
class SharedMemoryParcelTest extends AbstractParcelTest class SharedMemoryParcelTest extends AbstractParcelTest {
{
private $parcel; private $parcel;
protected function createParcel($value) protected function createParcel($value) {
{
$this->parcel = new SharedMemoryParcel($value); $this->parcel = new SharedMemoryParcel($value);
return $this->parcel; return $this->parcel;
} }
public function tearDown() public function tearDown() {
{
if ($this->parcel !== null) { if ($this->parcel !== null) {
$this->parcel->free(); $this->parcel->free();
} }
} }
public function testNewObjectIsNotFreed() public function testNewObjectIsNotFreed() {
{
$object = new SharedMemoryParcel(new \stdClass()); $object = new SharedMemoryParcel(new \stdClass());
$this->assertFalse($object->isFreed()); $this->assertFalse($object->isFreed());
$object->free(); $object->free();
} }
public function testFreeReleasesObject() public function testFreeReleasesObject() {
{
$object = new SharedMemoryParcel(new \stdClass()); $object = new SharedMemoryParcel(new \stdClass());
$object->free(); $object->free();
$this->assertTrue($object->isFreed()); $this->assertTrue($object->isFreed());
} }
/** /**
* @expectedException \Amp\Concurrent\Exception\SharedMemoryException * @expectedException \Amp\Concurrent\SharedMemoryException
*/ */
public function testUnwrapThrowsErrorIfFreed() public function testUnwrapThrowsErrorIfFreed() {
{
$object = new SharedMemoryParcel(new \stdClass()); $object = new SharedMemoryParcel(new \stdClass());
$object->free(); $object->free();
$object->unwrap(); $object->unwrap();
} }
public function testCloneIsNewObject() public function testCloneIsNewObject() {
{
$object = new \stdClass(); $object = new \stdClass();
$shared = new SharedMemoryParcel($object); $shared = new SharedMemoryParcel($object);
$clone = clone $shared; $clone = clone $shared;
@ -64,13 +56,12 @@ class SharedMemoryParcelTest extends AbstractParcelTest
$shared->free(); $shared->free();
} }
public function testObjectOverflowMoved() public function testObjectOverflowMoved() {
{
$object = new SharedMemoryParcel('hi', 14); $object = new SharedMemoryParcel('hi', 14);
$coroutine = new Coroutine($object->synchronized(function () { $awaitable = $object->synchronized(function () {
return 'hello world'; return 'hello world';
})); });
$coroutine->wait(); \Amp\wait($awaitable);
$this->assertEquals('hello world', $object->unwrap()); $this->assertEquals('hello world', $object->unwrap());
$object->free(); $object->free();
@ -80,15 +71,14 @@ class SharedMemoryParcelTest extends AbstractParcelTest
* @group posix * @group posix
* @requires extension pcntl * @requires extension pcntl
*/ */
public function testSetInSeparateProcess() public function testSetInSeparateProcess() {
{
$object = new SharedMemoryParcel(42); $object = new SharedMemoryParcel(42);
$this->doInFork(function () use ($object) { $this->doInFork(function () use ($object) {
$coroutine = new Coroutine($object->synchronized(function () { $awaitable = $object->synchronized(function () {
return 43; return 43;
})); });
$coroutine->wait(); \Amp\wait($awaitable);
}); });
$this->assertEquals(43, $object->unwrap()); $this->assertEquals(43, $object->unwrap());
@ -99,8 +89,7 @@ class SharedMemoryParcelTest extends AbstractParcelTest
* @group posix * @group posix
* @requires extension pcntl * @requires extension pcntl
*/ */
public function testFreeInSeparateProcess() public function testFreeInSeparateProcess() {
{
$object = new SharedMemoryParcel(42); $object = new SharedMemoryParcel(42);
$this->doInFork(function () use ($object) { $this->doInFork(function () use ($object) {

View File

@ -1,14 +1,13 @@
<?php <?php
namespace Amp\Tests\Concurrent; namespace Amp\Concurrent\Test;
use Amp\Tests\Concurrent\Stub\CallbackStub; use Amp\Concurrent\Test\Stub\CallbackStub;
/** /**
* Abstract test class with methods for creating callbacks and asserting runtimes. * Abstract test class with methods for creating callbacks and asserting runtimes.
*/ */
abstract class TestCase extends \PHPUnit_Framework_TestCase abstract class TestCase extends \PHPUnit_Framework_TestCase {
{
const RUNTIME_PRECISION = 2; // Number of decimals to use in runtime calculations/comparisons. const RUNTIME_PRECISION = 2; // Number of decimals to use in runtime calculations/comparisons.
/** /**
@ -18,9 +17,8 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
* *
* @return callable Object that is callable and expects to be called the given number of times. * @return callable Object that is callable and expects to be called the given number of times.
*/ */
public function createCallback($count) public function createCallback($count) {
{ $mock = $this->createMock(CallbackStub::class);
$mock = $this->getMock(CallbackStub::class);
$mock->expects($this->exactly($count)) $mock->expects($this->exactly($count))
->method('__invoke'); ->method('__invoke');
@ -35,8 +33,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
* @param float $maxRunTime * @param float $maxRunTime
* @param mixed[]|null $args Function arguments. * @param mixed[]|null $args Function arguments.
*/ */
public function assertRunTimeLessThan(callable $callback, $maxRunTime, array $args = null) public function assertRunTimeLessThan(callable $callback, $maxRunTime, array $args = null) {
{
$this->assertRunTimeBetween($callback, 0, $maxRunTime, $args); $this->assertRunTimeBetween($callback, 0, $maxRunTime, $args);
} }
@ -47,8 +44,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
* @param float $minRunTime * @param float $minRunTime
* @param mixed[]|null $args Function arguments. * @param mixed[]|null $args Function arguments.
*/ */
public function assertRunTimeGreaterThan(callable $callback, $minRunTime, array $args = null) public function assertRunTimeGreaterThan(callable $callback, $minRunTime, array $args = null) {
{
$this->assertRunTimeBetween($callback, $minRunTime, 0, $args); $this->assertRunTimeBetween($callback, $minRunTime, 0, $args);
} }
@ -61,19 +57,18 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
* @param float $maxRunTime * @param float $maxRunTime
* @param mixed[]|null $args Function arguments. * @param mixed[]|null $args Function arguments.
*/ */
public function assertRunTimeBetween(callable $callback, $minRunTime, $maxRunTime, array $args = null) public function assertRunTimeBetween(callable $callback, $minRunTime, $maxRunTime, array $args = null) {
{
$start = microtime(true); $start = microtime(true);
call_user_func_array($callback, $args ?: []); \call_user_func_array($callback, $args ?: []);
$runTime = round(microtime(true) - $start, self::RUNTIME_PRECISION); $runTime = \round(\microtime(true) - $start, self::RUNTIME_PRECISION);
if (0 < $maxRunTime) { if (0 < $maxRunTime) {
$this->assertLessThanOrEqual( $this->assertLessThanOrEqual(
$maxRunTime, $maxRunTime,
$runTime, $runTime,
sprintf('The run time of %.2fs was greater than the max run time of %.2fs.', $runTime, $maxRunTime) \sprintf('The run time of %.2fs was greater than the max run time of %.2fs.', $runTime, $maxRunTime)
); );
} }
@ -81,14 +76,13 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
$this->assertGreaterThanOrEqual( $this->assertGreaterThanOrEqual(
$minRunTime, $minRunTime,
$runTime, $runTime,
sprintf('The run time of %.2fs was less than the min run time of %.2fs.', $runTime, $minRunTime) \sprintf('The run time of %.2fs was less than the min run time of %.2fs.', $runTime, $minRunTime)
); );
} }
} }
final protected function doInFork(callable $function) final protected function doInFork(callable $function) {
{ switch ($pid = \pcntl_fork()) {
switch ($pid = pcntl_fork()) {
case -1: case -1:
$this->fail('Failed to fork process.'); $this->fail('Failed to fork process.');
break; break;
@ -96,7 +90,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
$status = (int) $function(); $status = (int) $function();
exit($status); exit($status);
default: default:
if (pcntl_waitpid($pid, $status) === -1) { if (\pcntl_waitpid($pid, $status) === -1) {
$this->fail('Failed to fork process.'); $this->fail('Failed to fork process.');
} }
return $status; return $status;

View File

@ -1,55 +1,45 @@
<?php <?php
namespace Amp\Tests\Concurrent\Threading; namespace Amp\Concurrent\Test\Threading;
use Amp\Concurrent\Threading\Mutex; use Amp\Concurrent\Threading\Mutex;
use Amp\Coroutine; use Amp\Concurrent\Test\TestCase;
use Amp\Loop;
use Amp\Tests\Concurrent\TestCase;
/** /**
* @group threading * @group threading
* @requires extension pthreads * @requires extension pthreads
*/ */
class MutexTest extends TestCase class MutexTest extends TestCase {
{ public function testAcquire() {
public function testAcquire() \Amp\execute(function () {
{
Coroutine\create(function () {
$mutex = new Mutex(); $mutex = new Mutex();
$lock = yield from $mutex->acquire(); $lock = yield $mutex->acquire();
$lock->release(); $lock->release();
$this->assertTrue($lock->isReleased()); $this->assertTrue($lock->isReleased());
})->done(); });
Loop\run();
} }
public function testAcquireMultiple() public function testAcquireMultiple() {
{
Loop\loop();
$this->assertRunTimeGreaterThan(function () { $this->assertRunTimeGreaterThan(function () {
Coroutine\create(function () { \Amp\execute(function () {
$mutex = new Mutex(); $mutex = new Mutex();
$lock1 = yield from $mutex->acquire(); $lock1 = yield $mutex->acquire();
Loop\timer(0.5, function () use ($lock1) { \Amp\delay(500, function () use ($lock1) {
$lock1->release(); $lock1->release();
}); });
$lock2 = yield from $mutex->acquire(); $lock2 = yield $mutex->acquire();
Loop\timer(0.5, function () use ($lock2) { \Amp\delay(500, function () use ($lock2) {
$lock2->release(); $lock2->release();
}); });
$lock3 = yield from $mutex->acquire(); $lock3 = yield $mutex->acquire();
Loop\timer(0.5, function () use ($lock3) { \Amp\delay(500, function () use ($lock3) {
$lock3->release(); $lock3->release();
}); });
}); });
Loop\run();
}, 1.5); }, 1.5);
} }
} }

View File

@ -1,17 +1,15 @@
<?php <?php
namespace Amp\Tests\Concurrent\Threading; namespace Amp\Concurrent\Test\Threading;
use Amp\Concurrent\Threading\Parcel; use Amp\Concurrent\Threading\Parcel;
use Amp\Tests\Concurrent\Sync\AbstractParcelTest; use Amp\Concurrent\Test\Sync\AbstractParcelTest;
/** /**
* @requires extension pthreads * @requires extension pthreads
*/ */
class ParcelTest extends AbstractParcelTest class ParcelTest extends AbstractParcelTest {
{ protected function createParcel($value) {
protected function createParcel($value)
{
return new Parcel($value); return new Parcel($value);
} }
} }

View File

@ -1,31 +1,26 @@
<?php <?php
namespace Amp\Tests\Concurrent\Threading; namespace Amp\Concurrent\Test\Threading;
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore; use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
use Amp\Concurrent\Threading\{Semaphore, Thread}; use Amp\Concurrent\Threading\{Semaphore, Thread};
use Amp\Coroutine; use Amp\Concurrent\Test\Sync\AbstractSemaphoreTest;
use Amp\Loop;
use Amp\Tests\Concurrent\Sync\AbstractSemaphoreTest;
/** /**
* @group threading * @group threading
* @requires extension pthreads * @requires extension pthreads
*/ */
class SemaphoreTest extends AbstractSemaphoreTest class SemaphoreTest extends AbstractSemaphoreTest {
{ public function createSemaphore(int $locks) {
public function createSemaphore($locks)
{
return new Semaphore($locks); return new Semaphore($locks);
} }
public function testAcquireInMultipleThreads() public function testAcquireInMultipleThreads() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$this->semaphore = $this->createSemaphore(1); $this->semaphore = $this->createSemaphore(1);
$thread1 = new Thread(function (SyncSemaphore $semaphore) { $thread1 = new Thread(function (SyncSemaphore $semaphore) {
$lock = yield from $semaphore->acquire(); $lock = yield $semaphore->acquire();
usleep(1e5); usleep(1e5);
@ -35,7 +30,7 @@ class SemaphoreTest extends AbstractSemaphoreTest
}, $this->semaphore); }, $this->semaphore);
$thread2 = new Thread(function (SyncSemaphore $semaphore) { $thread2 = new Thread(function (SyncSemaphore $semaphore) {
$lock = yield from $semaphore->acquire(); $lock = yield $semaphore->acquire();
usleep(1e5); usleep(1e5);
@ -49,12 +44,10 @@ class SemaphoreTest extends AbstractSemaphoreTest
$thread1->start(); $thread1->start();
$thread2->start(); $thread2->start();
yield from $thread1->join(); yield $thread1->join();
yield from $thread2->join(); yield $thread2->join();
$this->assertGreaterThan(1, microtime(true) - $start); $this->assertGreaterThan(0.1, microtime(true) - $start);
}); });
Loop\run();
} }
} }

View File

@ -1,33 +1,27 @@
<?php <?php
namespace Amp\Tests\Concurrent\Threading; namespace Amp\Concurrent\Test\Threading;
use Amp\Concurrent\Threading\Thread; use Amp\Concurrent\Threading\Thread;
use Amp\Coroutine; use Amp\Concurrent\Test\AbstractContextTest;
use Amp\Loop;
use Amp\Tests\Concurrent\AbstractContextTest;
/** /**
* @group threading * @group threading
* @requires extension pthreads * @requires extension pthreads
*/ */
class ThreadTest extends AbstractContextTest class ThreadTest extends AbstractContextTest {
{ public function createContext(callable $function) {
public function createContext(callable $function)
{
return new Thread($function); return new Thread($function);
} }
public function testSpawnStartsThread() public function testSpawnStartsThread() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$thread = Thread::spawn(function () { $thread = Thread::spawn(function () {
usleep(100); usleep(100);
}); });
return yield from $thread->join(); return yield $thread->join();
})->done(); });
Loop\run();
} }
} }

View File

@ -1,14 +1,10 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Awaitable; use Amp\Concurrent\Test\TestCase;
use Amp\Coroutine;
use Amp\Loop;
use Amp\Tests\Concurrent\TestCase;
abstract class AbstractPoolTest extends TestCase abstract class AbstractPoolTest extends TestCase {
{
/** /**
* @param int $min * @param int $min
* @param int $max * @param int $max
@ -17,105 +13,96 @@ abstract class AbstractPoolTest extends TestCase
*/ */
abstract protected function createPool($min = null, $max = null); abstract protected function createPool($min = null, $max = null);
public function testIsRunning() public function testIsRunning() {
{ \Amp\execute(function () {
Coroutine\run(function () {
$pool = $this->createPool(); $pool = $this->createPool();
$this->assertFalse($pool->isRunning()); $this->assertFalse($pool->isRunning());
$pool->start(); $pool->start();
$this->assertTrue($pool->isRunning()); $this->assertTrue($pool->isRunning());
yield from $pool->shutdown(); yield $pool->shutdown();
$this->assertFalse($pool->isRunning()); $this->assertFalse($pool->isRunning());
}); });
} }
public function testIsIdleOnStart() public function testIsIdleOnStart() {
{ \Amp\execute(function () {
Coroutine\run(function () {
$pool = $this->createPool(); $pool = $this->createPool();
$pool->start(); $pool->start();
$this->assertTrue($pool->isIdle()); $this->assertTrue($pool->isIdle());
yield from $pool->shutdown(); yield $pool->shutdown();
}); });
} }
public function testGetMinSize() public function testGetMinSize() {
{
$pool = $this->createPool(7, 24); $pool = $this->createPool(7, 24);
$this->assertEquals(7, $pool->getMinSize()); $this->assertEquals(7, $pool->getMinSize());
} }
public function testGetMaxSize() public function testGetMaxSize() {
{
$pool = $this->createPool(3, 17); $pool = $this->createPool(3, 17);
$this->assertEquals(17, $pool->getMaxSize()); $this->assertEquals(17, $pool->getMaxSize());
} }
public function testMinWorkersSpawnedOnStart() public function testMinWorkersSpawnedOnStart() {
{ \Amp\execute(function () {
Coroutine\run(function () {
$pool = $this->createPool(8, 32); $pool = $this->createPool(8, 32);
$pool->start(); $pool->start();
$this->assertEquals(8, $pool->getWorkerCount()); $this->assertEquals(8, $pool->getWorkerCount());
yield from $pool->shutdown(); yield $pool->shutdown();
}); });
} }
public function testWorkersIdleOnStart() public function testWorkersIdleOnStart() {
{ \Amp\execute(function () {
Coroutine\run(function () {
$pool = $this->createPool(8, 32); $pool = $this->createPool(8, 32);
$pool->start(); $pool->start();
$this->assertEquals(8, $pool->getIdleWorkerCount()); $this->assertEquals(8, $pool->getIdleWorkerCount());
yield from $pool->shutdown(); yield $pool->shutdown();
}); });
} }
public function testEnqueue() public function testEnqueue() {
{ \Amp\execute(function () {
Coroutine\run(function () {
$pool = $this->createPool(); $pool = $this->createPool();
$pool->start(); $pool->start();
$returnValue = yield from $pool->enqueue(new TestTask(42)); $returnValue = yield $pool->enqueue(new TestTask(42));
$this->assertEquals(42, $returnValue); $this->assertEquals(42, $returnValue);
yield from $pool->shutdown(); yield $pool->shutdown();
}); });
} }
public function testEnqueueMultiple() public function testEnqueueMultiple() {
{ \Amp\execute(function () {
Coroutine\run(function () {
$pool = $this->createPool(); $pool = $this->createPool();
$pool->start(); $pool->start();
$values = yield Awaitable\all([ $values = yield \Amp\all([
new Coroutine\Coroutine($pool->enqueue(new TestTask(42))), $pool->enqueue(new TestTask(42)),
new Coroutine\Coroutine($pool->enqueue(new TestTask(56))), $pool->enqueue(new TestTask(56)),
new Coroutine\Coroutine($pool->enqueue(new TestTask(72))) $pool->enqueue(new TestTask(72))
]); ]);
$this->assertEquals([42, 56, 72], $values); $this->assertEquals([42, 56, 72], $values);
yield from $pool->shutdown(); yield $pool->shutdown();
}); });
} }
public function testKill() public function testKill() {
{
$pool = $this->createPool(); $pool = $this->createPool();
$pool->start(); $pool->start();
$this->assertRunTimeLessThan([$pool, 'kill'], 0.5); $this->assertRunTimeLessThan([$pool, 'kill'], 1);
$this->assertFalse($pool->isRunning()); $this->assertFalse($pool->isRunning());
} }
} }

View File

@ -1,102 +1,87 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Awaitable; use Amp\Concurrent\Test\TestCase;
use Amp\Coroutine;
use Amp\Loop;
use Amp\Tests\Concurrent\TestCase;
abstract class AbstractWorkerTest extends TestCase abstract class AbstractWorkerTest extends TestCase {
{
/** /**
* @return \Amp\Concurrent\Worker\Worker * @return \Amp\Concurrent\Worker\Worker
*/ */
abstract protected function createWorker(); abstract protected function createWorker();
public function testIsRunning() public function testIsRunning() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$worker = $this->createWorker(); $worker = $this->createWorker();
$this->assertFalse($worker->isRunning()); $this->assertFalse($worker->isRunning());
$worker->start(); $worker->start();
$this->assertTrue($worker->isRunning()); $this->assertTrue($worker->isRunning());
yield from $worker->shutdown(); yield $worker->shutdown();
$this->assertFalse($worker->isRunning()); $this->assertFalse($worker->isRunning());
})->done(); });
Loop\run();
} }
public function testIsIdleOnStart() public function testIsIdleOnStart() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$worker = $this->createWorker(); $worker = $this->createWorker();
$worker->start(); $worker->start();
$this->assertTrue($worker->isIdle()); $this->assertTrue($worker->isIdle());
yield from $worker->shutdown(); yield $worker->shutdown();
})->done(); });
Loop\run();
} }
public function testEnqueue() public function testEnqueue() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$worker = $this->createWorker(); $worker = $this->createWorker();
$worker->start(); $worker->start();
$returnValue = yield from $worker->enqueue(new TestTask(42)); $returnValue = yield $worker->enqueue(new TestTask(42));
$this->assertEquals(42, $returnValue); $this->assertEquals(42, $returnValue);
yield from $worker->shutdown(); yield $worker->shutdown();
})->done(); });
Loop\run();
} }
public function testEnqueueMultiple() public function testEnqueueMultiple() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$worker = $this->createWorker(); $worker = $this->createWorker();
$worker->start(); $worker->start();
$values = yield Awaitable\all([ $values = yield \Amp\all([
new Coroutine\Coroutine($worker->enqueue(new TestTask(42))), $worker->enqueue(new TestTask(42)),
new Coroutine\Coroutine($worker->enqueue(new TestTask(56))), $worker->enqueue(new TestTask(56)),
new Coroutine\Coroutine($worker->enqueue(new TestTask(72))) $worker->enqueue(new TestTask(72))
]); ]);
$this->assertEquals([42, 56, 72], $values); $this->assertEquals([42, 56, 72], $values);
yield from $worker->shutdown(); yield $worker->shutdown();
})->done(); });
Loop\run();
} }
public function testNotIdleOnEnqueue() public function testNotIdleOnEnqueue() {
{ \Amp\execute(function () {
Coroutine\create(function () {
$worker = $this->createWorker(); $worker = $this->createWorker();
$worker->start(); $worker->start();
$coroutine = new Coroutine\Coroutine($worker->enqueue(new TestTask(42))); $coroutine = $worker->enqueue(new TestTask(42));
$this->assertFalse($worker->isIdle()); $this->assertFalse($worker->isIdle());
yield $coroutine; yield $coroutine;
yield from $worker->shutdown(); yield $worker->shutdown();
})->done(); });
Loop\run();
} }
public function testKill() public function testKill() {
{
$worker = $this->createWorker(); $worker = $this->createWorker();
$worker->start(); $worker->start();

View File

@ -1,18 +1,16 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\{DefaultPool, WorkerFactory, WorkerFork}; use Amp\Concurrent\Worker\{ DefaultPool, WorkerFactory, WorkerFork };
/** /**
* @group forking * @group forking
* @requires extension pcntl * @requires extension pcntl
*/ */
class ForkPoolTest extends AbstractPoolTest class ForkPoolTest extends AbstractPoolTest {
{ protected function createPool($min = null, $max = null) {
protected function createPool($min = null, $max = null) $factory = $this->createMock(WorkerFactory::class);
{
$factory = $this->getMock(WorkerFactory::class);
$factory->method('create')->will($this->returnCallback(function () { $factory->method('create')->will($this->returnCallback(function () {
return new WorkerFork(); return new WorkerFork();
})); }));

View File

@ -1,17 +1,16 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker; use Amp\Concurrent\Worker;
use Amp\Concurrent\Worker\{Environment, Pool, Task, WorkerFactory}; use Amp\Concurrent\Worker\{ Environment, Pool, Task, WorkerFactory };
use Amp\Coroutine; use Amp\Concurrent\Test\TestCase;
use Amp\Tests\Concurrent\TestCase; use Amp\Success;
use Interop\Async\Awaitable;
class FunctionsTest extends TestCase class FunctionsTest extends TestCase {
{ public function testPool() {
public function testPool() $pool = $this->createMock(Pool::class);
{
$pool = $this->getMock(Pool::class);
Worker\pool($pool); Worker\pool($pool);
@ -21,12 +20,11 @@ class FunctionsTest extends TestCase
/** /**
* @depends testPool * @depends testPool
*/ */
public function testEnqueue() public function testEnqueue() {
{ $pool = $this->createMock(Pool::class);
$pool = $this->getMock(Pool::class);
$pool->method('enqueue') $pool->method('enqueue')
->will($this->returnCallback(function (Task $task) { ->will($this->returnCallback(function (Task $task): Awaitable {
return yield $task->run($this->getMock(Environment::class)); return new Success($task->run($this->createMock(Environment::class)));
})); }));
Worker\pool($pool); Worker\pool($pool);
@ -35,29 +33,27 @@ class FunctionsTest extends TestCase
$task = new TestTask($value); $task = new TestTask($value);
$coroutine = new Coroutine(Worker\enqueue($task)); $awaitable = Worker\enqueue($task);
$this->assertSame($value, $coroutine->wait()); $this->assertSame($value, \Amp\wait($awaitable));
} }
/** /**
* @depends testPool * @depends testPool
*/ */
public function testGet() public function testGet() {
{ $pool = $this->createMock(Pool::class);
$pool = $this->getMock(Pool::class);
$pool->expects($this->once()) $pool->expects($this->once())
->method('get') ->method('get')
->will($this->returnValue($this->getMock(Worker\Worker::class))); ->will($this->returnValue($this->createMock(Worker\Worker::class)));
Worker\pool($pool); Worker\pool($pool);
$worker = Worker\get(); $worker = Worker\get();
} }
public function testFactory() public function testFactory() {
{ $factory = $this->createMock(WorkerFactory::class);
$factory = $this->getMock(WorkerFactory::class);
Worker\factory($factory); Worker\factory($factory);
@ -67,12 +63,11 @@ class FunctionsTest extends TestCase
/** /**
* @depends testFactory * @depends testFactory
*/ */
public function testCreate() public function testCreate() {
{ $factory = $this->createMock(WorkerFactory::class);
$factory = $this->getMock(WorkerFactory::class);
$factory->expects($this->once()) $factory->expects($this->once())
->method('create') ->method('create')
->will($this->returnValue($this->getMock(Worker\Worker::class))); ->will($this->returnValue($this->createMock(Worker\Worker::class)));
Worker\factory($factory); Worker\factory($factory);

View File

@ -1,17 +1,15 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\{DefaultPool, WorkerFactory, WorkerProcess}; use Amp\Concurrent\Worker\{ DefaultPool, WorkerFactory, WorkerProcess };
/** /**
* @group process * @group process
*/ */
class ProcessPoolTest extends AbstractPoolTest class ProcessPoolTest extends AbstractPoolTest {
{ protected function createPool($min = null, $max = null) {
protected function createPool($min = null, $max = null) $factory = $this->createMock(WorkerFactory::class);
{
$factory = $this->getMock(WorkerFactory::class);
$factory->method('create')->will($this->returnCallback(function () { $factory->method('create')->will($this->returnCallback(function () {
return new WorkerProcess(); return new WorkerProcess();
})); }));

View File

@ -1,20 +1,17 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\{Environment, Task}; use Amp\Concurrent\Worker\{ Environment, Task };
class TestTask implements Task class TestTask implements Task {
{
private $returnValue; private $returnValue;
public function __construct($returnValue) public function __construct($returnValue) {
{
$this->returnValue = $returnValue; $this->returnValue = $returnValue;
} }
public function run(Environment $environment) public function run(Environment $environment) {
{
return $this->returnValue; return $this->returnValue;
} }
} }

View File

@ -1,18 +1,16 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\{DefaultPool, WorkerFactory, WorkerThread}; use Amp\Concurrent\Worker\{ DefaultPool, WorkerFactory, WorkerThread };
/** /**
* @group threading * @group threading
* @requires extension pthreads * @requires extension pthreads
*/ */
class ThreadPoolTest extends AbstractPoolTest class ThreadPoolTest extends AbstractPoolTest {
{ protected function createPool($min = null, $max = null) {
protected function createPool($min = null, $max = null) $factory = $this->createMock(WorkerFactory::class);
{
$factory = $this->getMock(WorkerFactory::class);
$factory->method('create')->will($this->returnCallback(function () { $factory->method('create')->will($this->returnCallback(function () {
return new WorkerThread(); return new WorkerThread();
})); }));

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\WorkerFork; use Amp\Concurrent\Worker\WorkerFork;
@ -8,10 +8,8 @@ use Amp\Concurrent\Worker\WorkerFork;
* @group forking * @group forking
* @requires extension pcntl * @requires extension pcntl
*/ */
class WorkerForkTest extends AbstractWorkerTest class WorkerForkTest extends AbstractWorkerTest {
{ protected function createWorker() {
protected function createWorker()
{
return new WorkerFork(); return new WorkerFork();
} }
} }

View File

@ -1,13 +1,11 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\WorkerProcess; use Amp\Concurrent\Worker\WorkerProcess;
class WorkerProcessTest extends AbstractWorkerTest class WorkerProcessTest extends AbstractWorkerTest {
{ protected function createWorker() {
protected function createWorker()
{
return new WorkerProcess(); return new WorkerProcess();
} }
} }

View File

@ -1,6 +1,6 @@
<?php <?php
namespace Amp\Tests\Concurrent\Worker; namespace Amp\Concurrent\Test\Worker;
use Amp\Concurrent\Worker\WorkerThread; use Amp\Concurrent\Worker\WorkerThread;
@ -8,10 +8,8 @@ use Amp\Concurrent\Worker\WorkerThread;
* @group threading * @group threading
* @requires extension pthreads * @requires extension pthreads
*/ */
class WorkerThreadTest extends AbstractWorkerTest class WorkerThreadTest extends AbstractWorkerTest {
{ protected function createWorker() {
protected function createWorker()
{
return new WorkerThread(); return new WorkerThread();
} }
} }