mirror of
https://github.com/danog/parallel.git
synced 2024-11-30 04:39:01 +01:00
Update tests; code cleanup
This commit is contained in:
parent
0e1e0393ff
commit
70097cb225
@ -2,6 +2,8 @@ language: php
|
||||
|
||||
php:
|
||||
- 7.0
|
||||
- 7.1
|
||||
- nightly
|
||||
|
||||
sudo: false
|
||||
|
||||
@ -24,6 +26,3 @@ script:
|
||||
after_script:
|
||||
- composer require satooshi/php-coveralls dev-master
|
||||
- vendor/bin/coveralls -v --exclude-no-stmt
|
||||
|
||||
notifications:
|
||||
slack: icicleio:zLkB18HsYCz8TbdK7RVqbi48
|
||||
|
@ -50,9 +50,8 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Amp\\Benchmarks\\Concurrent\\": "benchmarks",
|
||||
"Amp\\Examples\\Concurrent\\": "examples",
|
||||
"Amp\\Tests\\Concurrent\\": "tests"
|
||||
"Amp\\Concurrent\\Example\\": "examples",
|
||||
"Amp\\Concurrent\\Test\\": "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
<?php
|
||||
namespace Amp\Examples\Concurrent;
|
||||
namespace Amp\Concurrent\Example;
|
||||
|
||||
use Amp\Concurrent\Worker\Environment;
|
||||
use Amp\Concurrent\Worker\Task;
|
||||
use Amp\Concurrent\Worker\{ Environment, Task };
|
||||
|
||||
class BlockingTask implements Task
|
||||
{
|
||||
class BlockingTask implements Task {
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
@ -20,8 +18,7 @@ class BlockingTask implements Task
|
||||
* @param callable $function Do not use a closure or non-serializable object.
|
||||
* @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->args = $args;
|
||||
}
|
||||
@ -29,8 +26,7 @@ class BlockingTask implements Task
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function run(Environment $environment)
|
||||
{
|
||||
public function run(Environment $environment) {
|
||||
return ($this->function)(...$this->args);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
use Amp\Concurrent\Worker\DefaultPool;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Examples\Concurrent\BlockingTask;
|
||||
use Amp\Concurrent\Example\BlockingTask;
|
||||
|
||||
Amp\execute(function() {
|
||||
$timer = Amp\repeat(100, function () {
|
||||
|
@ -3,7 +3,7 @@
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
use Amp\Concurrent\Worker\DefaultWorkerFactory;
|
||||
use Amp\Examples\Concurrent\BlockingTask;
|
||||
use Amp\Concurrent\Example\BlockingTask;
|
||||
|
||||
Amp\execute(function () {
|
||||
$factory = new DefaultWorkerFactory();
|
||||
|
@ -25,7 +25,7 @@ class PanicError extends \Error {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPanicTrace() {
|
||||
public function getPanicTrace(): string {
|
||||
return $this->trace;
|
||||
}
|
||||
}
|
||||
|
@ -305,8 +305,7 @@ class Process implements ProcessContext {
|
||||
*
|
||||
* @return mixed[] Array of options.
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
public function getOptions(): array {
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Amp\Concurrent\Sync;
|
||||
|
||||
use Amp\Concurrent\{ChannelException, SerializationException};
|
||||
use Amp\Concurrent\{ ChannelException, SerializationException };
|
||||
use Amp\Coroutine;
|
||||
use Amp\Stream\Stream;
|
||||
use Interop\Async\Awaitable;
|
||||
|
@ -32,8 +32,7 @@ class FileMutex implements Mutex {
|
||||
/**
|
||||
* Creates a new mutex.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
public function __construct() {
|
||||
$this->fileName = \tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock';
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ class PosixSemaphore implements Semaphore, \Serializable {
|
||||
if ($errno !== MSG_ENOMSG) {
|
||||
throw new SemaphoreException('Failed to acquire a lock.');
|
||||
}
|
||||
} while (yield new Pause(self::LATENCY_TIMEOUT));
|
||||
} while (yield new Pause(self::LATENCY_TIMEOUT, true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -216,8 +216,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
*
|
||||
* Calling `free()` on an object already freed will have no effect.
|
||||
*/
|
||||
public function free()
|
||||
{
|
||||
public function free() {
|
||||
if (!$this->isFreed()) {
|
||||
// Invalidate the memory block by setting its state to FREED.
|
||||
$this->setHeader(static::STATE_FREED, 0, 0);
|
||||
@ -239,8 +238,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
*
|
||||
* @return string The serialized object handle.
|
||||
*/
|
||||
public function serialize(): string
|
||||
{
|
||||
public function serialize(): string {
|
||||
return \serialize([$this->key, $this->semaphore]);
|
||||
}
|
||||
|
||||
@ -249,8 +247,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
*
|
||||
* @param string $serialized The serialized object handle.
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
public function unserialize($serialized) {
|
||||
list($this->key, $this->semaphore) = \unserialize($serialized);
|
||||
$this->memOpen($this->key, 'w', 0, 0);
|
||||
}
|
||||
@ -258,8 +255,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
public function __clone() {
|
||||
$value = $this->unwrap();
|
||||
$header = $this->getHeader();
|
||||
$this->init($value, $header['size'], $header['permissions']);
|
||||
@ -270,8 +266,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
*
|
||||
* @return array An array of debugging information.
|
||||
*/
|
||||
public function __debugInfo()
|
||||
{
|
||||
public function __debugInfo() {
|
||||
if ($this->isFreed()) {
|
||||
return [
|
||||
'id' => $this->key,
|
||||
@ -291,8 +286,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
* Updates the current memory segment handle, handling any moves made on the
|
||||
* data.
|
||||
*/
|
||||
private function handleMovedMemory()
|
||||
{
|
||||
private function handleMovedMemory() {
|
||||
// Read from the memory block and handle moved blocks until we find the
|
||||
// correct block.
|
||||
while (true) {
|
||||
@ -315,8 +309,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
*
|
||||
* @return array An associative array of header data.
|
||||
*/
|
||||
private function getHeader(): array
|
||||
{
|
||||
private function getHeader(): array {
|
||||
$data = $this->memGet(0, self::MEM_DATA_OFFSET);
|
||||
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 $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);
|
||||
$this->memSet(0, $header);
|
||||
}
|
||||
@ -342,8 +334,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
||||
* @param int $permissions Process permissions on the shared memory.
|
||||
* @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);
|
||||
if ($this->handle === false) {
|
||||
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.
|
||||
*/
|
||||
private function memGet(int $offset, int $size): string
|
||||
{
|
||||
private function memGet(int $offset, int $size): string {
|
||||
$data = \shmop_read($this->handle, $offset, $size);
|
||||
if ($data === false) {
|
||||
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 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)) {
|
||||
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.
|
||||
*/
|
||||
private function memDelete()
|
||||
{
|
||||
private function memDelete() {
|
||||
if (!\shmop_delete($this->handle)) {
|
||||
throw new SharedMemoryException('Failed to discard shared memory block.');
|
||||
}
|
||||
|
@ -75,8 +75,7 @@ class Thread extends \Thread {
|
||||
}
|
||||
|
||||
if (null === $autoloadPath) {
|
||||
echo 'Could not locate autoload.php.';
|
||||
exit(1);
|
||||
throw new \Error('Could not locate autoload.php');
|
||||
}
|
||||
|
||||
require $autoloadPath;
|
||||
@ -85,11 +84,7 @@ class Thread extends \Thread {
|
||||
|
||||
try {
|
||||
\Amp\execute(function () {
|
||||
try {
|
||||
$channel = new ChannelledStream(new Socket($this->socket, false));
|
||||
} catch (\Throwable $exception) {
|
||||
return 1; // Parent has destroyed Thread object, so just exit.
|
||||
}
|
||||
$channel = new ChannelledStream(new Socket($this->socket, false));
|
||||
|
||||
$watcher = \Amp\repeat(self::KILL_CHECK_FREQUENCY, function () {
|
||||
if ($this->killed) {
|
||||
|
@ -25,16 +25,14 @@ class Parcel implements SyncParcel {
|
||||
*
|
||||
* @param mixed $value The value to store in the container.
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
public function __construct($value) {
|
||||
$this->init($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function init($value)
|
||||
{
|
||||
private function init($value) {
|
||||
$this->mutex = new Mutex();
|
||||
$this->storage = new Internal\Storage($value);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Amp\Concurrent\Threading;
|
||||
|
||||
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
|
||||
use Interop\Async\Awaitable;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function __construct(int $locks)
|
||||
{
|
||||
public function __construct(int $locks) {
|
||||
$this->init($locks);
|
||||
}
|
||||
|
||||
@ -38,8 +38,7 @@ class Semaphore implements SyncSemaphore {
|
||||
*
|
||||
* @param int $locks
|
||||
*/
|
||||
private function init(int $locks)
|
||||
{
|
||||
private function init(int $locks) {
|
||||
$locks = (int) $locks;
|
||||
if ($locks < 1) {
|
||||
$locks = 1;
|
||||
@ -52,32 +51,28 @@ class Semaphore implements SyncSemaphore {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
public function count(): int {
|
||||
return $this->semaphore->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSize(): int
|
||||
{
|
||||
public function getSize(): int {
|
||||
return $this->maxLocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function acquire(): \Generator
|
||||
{
|
||||
public function acquire(): Awaitable {
|
||||
return $this->semaphore->acquire();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +253,6 @@ class Thread implements Strand {
|
||||
}
|
||||
|
||||
if ($data instanceof ExitStatus) {
|
||||
$this->kill();
|
||||
throw new \Error('Cannot send exit status objects.');
|
||||
}
|
||||
|
||||
|
@ -140,8 +140,7 @@ class DefaultPool implements Pool {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdleWorkerCount(): int
|
||||
{
|
||||
public function getIdleWorkerCount(): int {
|
||||
return $this->idleWorkers->count();
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ use Amp\Concurrent\Process\ChannelledProcess;
|
||||
*/
|
||||
class WorkerProcess extends AbstractWorker {
|
||||
public function __construct() {
|
||||
$dir = \dirname(\dirname(__DIR__)) . '/bin';
|
||||
$dir = \dirname(__DIR__, 2) . '/bin';
|
||||
parent::__construct(new ChannelledProcess($dir . '/worker.php', $dir));
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,11 @@
|
||||
<directory>test</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src</directory>
|
||||
<directory suffix=".php">lib</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-html" target="build/coverage" title="Amp" highlight="true"/>
|
||||
</logging>
|
||||
|
@ -1,18 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent;
|
||||
namespace Amp\Concurrent\Test;
|
||||
|
||||
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);
|
||||
|
||||
public function testIsRunning()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testIsRunning() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
usleep(100);
|
||||
});
|
||||
@ -23,16 +24,13 @@ abstract class AbstractContextTest extends TestCase
|
||||
|
||||
$this->assertTrue($context->isRunning());
|
||||
|
||||
yield from $context->join();
|
||||
yield $context->join();
|
||||
|
||||
$this->assertFalse($context->isRunning());
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
});
|
||||
}
|
||||
|
||||
public function testKill()
|
||||
{
|
||||
public function testKill() {
|
||||
$context = $this->createContext(function () {
|
||||
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 () {
|
||||
usleep(100);
|
||||
});
|
||||
@ -58,224 +55,185 @@ abstract class AbstractContextTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
||||
* @expectedException \Amp\Concurrent\StatusError
|
||||
*/
|
||||
public function testStartMultipleTimesThrowsError()
|
||||
{
|
||||
Loop\loop();
|
||||
|
||||
public function testStartMultipleTimesThrowsError() {
|
||||
$this->assertRunTimeGreaterThan(function () {
|
||||
Coroutine\create(function () {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
sleep(1);
|
||||
});
|
||||
|
||||
$context->start();
|
||||
yield from $context->join();
|
||||
yield $context->join();
|
||||
|
||||
$context->start();
|
||||
yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
yield $context->join();
|
||||
});
|
||||
}, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Concurrent\Exception\PanicError
|
||||
* @expectedException \Amp\Concurrent\PanicError
|
||||
*/
|
||||
public function testExceptionInContextPanics()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testExceptionInContextPanics() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
throw new \Exception('Exception in fork.');
|
||||
});
|
||||
|
||||
$context->start();
|
||||
yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
yield $context->join();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Concurrent\Exception\PanicError
|
||||
* @expectedException \Amp\Concurrent\PanicError
|
||||
*/
|
||||
public function testReturnUnserializableDataPanics()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testReturnUnserializableDataPanics() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
return yield function () {};
|
||||
});
|
||||
|
||||
$context->start();
|
||||
yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
yield $context->join();
|
||||
});
|
||||
}
|
||||
|
||||
public function testJoinWaitsForChild()
|
||||
{
|
||||
Loop\loop();
|
||||
|
||||
public function testJoinWaitsForChild() {
|
||||
$this->assertRunTimeGreaterThan(function () {
|
||||
Coroutine\create(function () {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
sleep(1);
|
||||
});
|
||||
|
||||
$context->start();
|
||||
yield from $context->join();
|
||||
})->done();
|
||||
yield $context->join();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
||||
* @expectedException \Amp\Concurrent\StatusError
|
||||
*/
|
||||
public function testJoinWithoutStartThrowsError()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testJoinWithoutStartThrowsError() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
usleep(100);
|
||||
});
|
||||
|
||||
yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
yield $context->join();
|
||||
});
|
||||
}
|
||||
|
||||
public function testJoinResolvesWithContextReturn()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testJoinResolvesWithContextReturn() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
return 42;
|
||||
});
|
||||
|
||||
$context->start();
|
||||
$this->assertSame(42, yield from $context->join());
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
$this->assertSame(42, yield $context->join());
|
||||
});
|
||||
}
|
||||
|
||||
public function testSendAndReceive()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSendAndReceive() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
yield from $this->send(1);
|
||||
$value = yield from $this->receive();
|
||||
yield $this->send(1);
|
||||
$value = yield $this->receive();
|
||||
return $value;
|
||||
});
|
||||
|
||||
$value = 42;
|
||||
|
||||
$context->start();
|
||||
$this->assertSame(1, yield from $context->receive());
|
||||
yield from $context->send($value);
|
||||
$this->assertSame($value, yield from $context->join());
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
$this->assertSame(1, yield $context->receive());
|
||||
yield $context->send($value);
|
||||
$this->assertSame($value, yield $context->join());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendAndReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\SynchronizationError
|
||||
* @expectedException \Amp\Concurrent\SynchronizationError
|
||||
*/
|
||||
public function testJoinWhenContextSendingData()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testJoinWhenContextSendingData() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
yield from $this->send(0);
|
||||
yield $this->send(0);
|
||||
return 42;
|
||||
});
|
||||
|
||||
$context->start();
|
||||
$value = yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
$value = yield $context->join();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendAndReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
||||
* @expectedException \Amp\Concurrent\StatusError
|
||||
*/
|
||||
public function testReceiveBeforeContextHasStarted()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testReceiveBeforeContextHasStarted() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
yield from $this->send(0);
|
||||
yield $this->send(0);
|
||||
return 42;
|
||||
});
|
||||
|
||||
$value = yield from $context->receive();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
$value = yield $context->receive();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendAndReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
||||
* @expectedException \Amp\Concurrent\StatusError
|
||||
*/
|
||||
public function testSendBeforeContextHasStarted()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSendBeforeContextHasStarted() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
yield from $this->send(0);
|
||||
yield $this->send(0);
|
||||
return 42;
|
||||
});
|
||||
|
||||
yield from $context->send(0);
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
yield $context->send(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendAndReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\SynchronizationError
|
||||
* @expectedException \Amp\Concurrent\SynchronizationError
|
||||
*/
|
||||
public function testReceiveWhenContextHasReturned()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testReceiveWhenContextHasReturned() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
yield from $this->send(0);
|
||||
yield $this->send(0);
|
||||
return 42;
|
||||
});
|
||||
|
||||
$context->start();
|
||||
$value = yield from $context->receive();
|
||||
$value = yield from $context->receive();
|
||||
$value = yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
$value = yield $context->receive();
|
||||
$value = yield $context->receive();
|
||||
$value = yield $context->join();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendAndReceive
|
||||
* @expectedException \Amp\Exception\InvalidArgumentError
|
||||
* @expectedException \Error
|
||||
*/
|
||||
public function testSendExitStatus()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSendExitStatus() {
|
||||
\Amp\execute(function () {
|
||||
$context = $this->createContext(function () {
|
||||
$value = yield from $this->receive();
|
||||
$value = yield $this->receive();
|
||||
return 42;
|
||||
});
|
||||
|
||||
$context->start();
|
||||
yield from $context->send(new ExitSuccess(0));
|
||||
$value = yield from $context->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
yield $context->send(new ExitSuccess(0));
|
||||
$value = yield $context->join();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Forking;
|
||||
namespace Amp\Concurrent\Test\Forking;
|
||||
|
||||
use Amp\Concurrent\Forking\Fork;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\AbstractContextTest;
|
||||
use Amp\Concurrent\Test\AbstractContextTest;
|
||||
|
||||
/**
|
||||
* @group forking
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
class ForkTest extends AbstractContextTest
|
||||
{
|
||||
public function createContext(callable $function)
|
||||
{
|
||||
class ForkTest extends AbstractContextTest {
|
||||
public function createContext(callable $function) {
|
||||
return new Fork($function);
|
||||
}
|
||||
|
||||
public function testSpawnStartsFork()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSpawnStartsFork() {
|
||||
\Amp\execute(function () {
|
||||
$fork = Fork::spawn(function () {
|
||||
usleep(100);
|
||||
});
|
||||
|
||||
return yield from $fork->join();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
return yield $fork->join();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Stub;
|
||||
namespace Amp\Concurrent\Test\Stub;
|
||||
|
||||
class CallbackStub
|
||||
{
|
||||
class CallbackStub {
|
||||
public function __invoke() {}
|
||||
}
|
||||
|
@ -1,32 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
|
||||
abstract class AbstractParcelTest extends TestCase
|
||||
{
|
||||
abstract class AbstractParcelTest extends TestCase {
|
||||
/**
|
||||
* @return \Amp\Concurrent\Sync\Parcel
|
||||
*/
|
||||
abstract protected function createParcel($value);
|
||||
|
||||
public function testConstructor()
|
||||
{
|
||||
public function testConstructor() {
|
||||
$object = $this->createParcel(new \stdClass());
|
||||
$this->assertInternalType('object', $object->unwrap());
|
||||
}
|
||||
|
||||
public function testUnwrapIsOfCorrectType()
|
||||
{
|
||||
public function testUnwrapIsOfCorrectType() {
|
||||
$object = $this->createParcel(new \stdClass());
|
||||
$this->assertInstanceOf('stdClass', $object->unwrap());
|
||||
}
|
||||
|
||||
public function testUnwrapIsEqual()
|
||||
{
|
||||
public function testUnwrapIsEqual() {
|
||||
$object = new \stdClass();
|
||||
$shared = $this->createParcel($object);
|
||||
$this->assertEquals($object, $shared->unwrap());
|
||||
@ -35,50 +29,46 @@ abstract class AbstractParcelTest extends TestCase
|
||||
/**
|
||||
* @depends testUnwrapIsEqual
|
||||
*/
|
||||
public function testSynchronized()
|
||||
{
|
||||
public function testSynchronized() {
|
||||
$parcel = $this->createParcel(0);
|
||||
|
||||
$coroutine = new Coroutine($parcel->synchronized(function ($value) {
|
||||
$awaitable = $parcel->synchronized(function ($value) {
|
||||
$this->assertSame(0, $value);
|
||||
usleep(1e4);
|
||||
return 1;
|
||||
}));
|
||||
});
|
||||
|
||||
$callback = $this->createCallback(1);
|
||||
$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);
|
||||
usleep(1e4);
|
||||
return 2;
|
||||
}));
|
||||
});
|
||||
|
||||
$callback = $this->createCallback(1);
|
||||
$callback->method('__invoke')
|
||||
->with($this->identicalTo(2));
|
||||
->with($this->identicalTo(null), $this->identicalTo(2));
|
||||
|
||||
$coroutine->done($callback);
|
||||
|
||||
Loop\run();
|
||||
$awaitable->when($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSynchronized
|
||||
*/
|
||||
public function testCloneIsNewParcel()
|
||||
{
|
||||
public function testCloneIsNewParcel() {
|
||||
$original = $this->createParcel(1);
|
||||
|
||||
$clone = clone $original;
|
||||
|
||||
$coroutine = new Coroutine($clone->synchronized(function () {
|
||||
$awaitable = $clone->synchronized(function () {
|
||||
return 2;
|
||||
}));
|
||||
$coroutine->wait();
|
||||
});
|
||||
\Amp\wait($awaitable);
|
||||
|
||||
$this->assertSame(1, $original->unwrap());
|
||||
$this->assertSame(2, $clone->unwrap());
|
||||
|
@ -1,123 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Concurrent\Sync\Lock;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
use Amp\Pause;
|
||||
|
||||
abstract class AbstractSemaphoreTest extends TestCase
|
||||
{
|
||||
abstract class AbstractSemaphoreTest extends TestCase {
|
||||
/**
|
||||
* @var \Amp\Concurrent\Sync\Semaphore
|
||||
*/
|
||||
protected $semaphore;
|
||||
|
||||
/**
|
||||
* @param int $locks Number of locks in the 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->assertCount(4, $this->semaphore);
|
||||
}
|
||||
|
||||
public function testAcquire()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testAcquire() {
|
||||
\Amp\execute(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
|
||||
$lock = yield from $this->semaphore->acquire();
|
||||
$lock = yield $this->semaphore->acquire();
|
||||
|
||||
$this->assertFalse($lock->isReleased());
|
||||
|
||||
$lock->release();
|
||||
|
||||
$this->assertTrue($lock->isReleased());
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
});
|
||||
}
|
||||
|
||||
public function testAcquireMultiple()
|
||||
{
|
||||
public function testAcquireMultiple() {
|
||||
$this->assertRunTimeGreaterThan(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
|
||||
Coroutine\create(function () {
|
||||
$lock1 = yield from $this->semaphore->acquire();
|
||||
Loop\timer(0.5, function () use ($lock1) {
|
||||
\Amp\execute(function () {
|
||||
$lock1 = yield $this->semaphore->acquire();
|
||||
\Amp\delay(500, function () use ($lock1) {
|
||||
$lock1->release();
|
||||
});
|
||||
|
||||
$lock2 = yield from $this->semaphore->acquire();
|
||||
Loop\timer(0.5, function () use ($lock2) {
|
||||
$lock2 = yield $this->semaphore->acquire();
|
||||
\Amp\delay(500, function () use ($lock2) {
|
||||
$lock2->release();
|
||||
});
|
||||
|
||||
$lock3 = yield from $this->semaphore->acquire();
|
||||
Loop\timer(0.5, function () use ($lock3) {
|
||||
$lock3 = yield $this->semaphore->acquire();
|
||||
\Amp\delay(500, function () use ($lock3) {
|
||||
$lock3->release();
|
||||
});
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
});
|
||||
}, 1.5);
|
||||
}
|
||||
|
||||
public function testCloneIsNewSemaphore()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testCloneIsNewSemaphore() {
|
||||
\Amp\execute(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
$clone = clone $this->semaphore;
|
||||
|
||||
$lock = yield from $clone->acquire();
|
||||
$lock = yield $clone->acquire();
|
||||
|
||||
$this->assertCount(1, $this->semaphore);
|
||||
$this->assertCount(0, $clone);
|
||||
|
||||
$lock->release();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
});
|
||||
}
|
||||
|
||||
public function testSerializedIsSameSemaphore()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSerializedIsSameSemaphore() {
|
||||
\Amp\execute(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
$unserialized = unserialize(serialize($this->semaphore));
|
||||
|
||||
$lock = yield from $unserialized->acquire();
|
||||
$lock = yield $unserialized->acquire();
|
||||
|
||||
$this->assertCount(0, $this->semaphore);
|
||||
$this->assertCount(0, $unserialized);
|
||||
|
||||
$lock->release();
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
});
|
||||
}
|
||||
|
||||
public function testSimultaneousAcquire()
|
||||
{
|
||||
public function testSimultaneousAcquire() {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
|
||||
$coroutine1 = new Coroutine\Coroutine($this->semaphore->acquire());
|
||||
$coroutine2 = new Coroutine\Coroutine($this->semaphore->acquire());
|
||||
|
||||
$coroutine1->delay(0.5)->then(function (Lock $lock) {
|
||||
$lock->release();
|
||||
\Amp\execute(function () {
|
||||
$awaitable1 = $this->semaphore->acquire();
|
||||
$awaitable2 = $this->semaphore->acquire();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Concurrent\Sync\ChannelledStream;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Stream\{DuplexStream, ReadableStream};
|
||||
use Amp\Stream\Exception\{UnreadableException, UnwritableException};
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Stream\Stream;
|
||||
use Amp\Stream\ClosedException;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
use Amp\Success;
|
||||
|
||||
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()
|
||||
{
|
||||
$mock = $this->getMock(DuplexStream::class);
|
||||
protected function createMockStream() {
|
||||
$mock = $this->createMock(Stream::class);
|
||||
|
||||
$buffer = '';
|
||||
|
||||
$mock->method('write')
|
||||
->will($this->returnCallback(function ($data) use (&$buffer) {
|
||||
$buffer .= $data;
|
||||
return yield strlen($data);
|
||||
return new Success(\strlen($data));
|
||||
}));
|
||||
|
||||
$mock->method('read')
|
||||
->will($this->returnCallback(function ($length, $byte = null, $timeout = 0) use (&$buffer) {
|
||||
$result = substr($buffer, 0, $length);
|
||||
$buffer = substr($buffer, $length);
|
||||
return yield $result;
|
||||
$result = \substr($buffer, 0, $length);
|
||||
$buffer = \substr($buffer, $length);
|
||||
return new Success($result);
|
||||
}));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Exception\InvalidArgumentError
|
||||
*/
|
||||
public function testReadableWithoutWritable()
|
||||
{
|
||||
$mock = $this->getMock(ReadableStream::class);
|
||||
|
||||
$channel = new ChannelledStream($mock);
|
||||
}
|
||||
|
||||
public function testSendReceive()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
|
||||
public function testSendReceive() {
|
||||
\Amp\execute(function () {
|
||||
$mock = $this->createMockStream();
|
||||
$a = new ChannelledStream($mock);
|
||||
$b = new ChannelledStream($mock);
|
||||
|
||||
$message = 'hello';
|
||||
|
||||
yield from $a->send($message);
|
||||
$data = yield from $b->receive();
|
||||
yield $a->send($message);
|
||||
$data = yield $b->receive();
|
||||
$this->assertSame($message, $data);
|
||||
})->done();
|
||||
|
||||
Loop\run();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendReceive
|
||||
*/
|
||||
public function testSendReceiveLongData()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSendReceiveLongData() {
|
||||
\Amp\execute(function () {
|
||||
$mock = $this->createMockStream();
|
||||
$a = new ChannelledStream($mock);
|
||||
$b = new ChannelledStream($mock);
|
||||
@ -79,90 +62,81 @@ class ChannelledStreamTest extends TestCase
|
||||
$message .= chr(mt_rand(0, 255));
|
||||
}
|
||||
|
||||
yield from $a->send($message);
|
||||
$data = yield from $b->receive();
|
||||
yield $a->send($message);
|
||||
$data = yield $b->receive();
|
||||
$this->assertSame($message, $data);
|
||||
})->done();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
||||
* @expectedException \Amp\Concurrent\ChannelException
|
||||
*/
|
||||
public function testInvalidDataReceived()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testInvalidDataReceived() {
|
||||
\Amp\execute(function () {
|
||||
$mock = $this->createMockStream();
|
||||
$a = new ChannelledStream($mock);
|
||||
$b = new ChannelledStream($mock);
|
||||
|
||||
// Close $a. $b should close on next read...
|
||||
yield from $mock->write(pack('L', 10) . '1234567890');
|
||||
$data = yield from $b->receive();
|
||||
})->done();
|
||||
yield $mock->write(pack('L', 10) . '1234567890');
|
||||
$data = yield $b->receive();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
||||
* @expectedException \Amp\Concurrent\ChannelException
|
||||
*/
|
||||
public function testSendUnserializableData()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSendUnserializableData() {
|
||||
\Amp\execute(function () {
|
||||
$mock = $this->createMockStream();
|
||||
$a = new ChannelledStream($mock);
|
||||
$b = new ChannelledStream($mock);
|
||||
|
||||
// Close $a. $b should close on next read...
|
||||
yield from $a->send(function () {});
|
||||
$data = yield from $b->receive();
|
||||
})->done();
|
||||
yield $a->send(function () {});
|
||||
$data = yield $b->receive();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
||||
* @expectedException \Amp\Concurrent\ChannelException
|
||||
*/
|
||||
public function testSendAfterClose()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
$mock = $this->getMock(DuplexStream::class);
|
||||
public function testSendAfterClose() {
|
||||
\Amp\execute(function () {
|
||||
$mock = $this->createMock(Stream::class);
|
||||
$mock->expects($this->once())
|
||||
->method('write')
|
||||
->will($this->throwException(new UnwritableException()));
|
||||
->will($this->throwException(new ClosedException));
|
||||
|
||||
$a = new ChannelledStream($mock);
|
||||
$b = new ChannelledStream($this->getMock(DuplexStream::class));
|
||||
$b = new ChannelledStream($this->createMock(Stream::class));
|
||||
|
||||
yield from $a->send('hello');
|
||||
})->done();
|
||||
yield $a->send('hello');
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSendReceive
|
||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
||||
* @expectedException \Amp\Concurrent\ChannelException
|
||||
*/
|
||||
public function testReceiveAfterClose()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
$mock = $this->getMock(DuplexStream::class);
|
||||
public function testReceiveAfterClose() {
|
||||
\Amp\execute(function () {
|
||||
$mock = $this->createMock(Stream::class);
|
||||
$mock->expects($this->once())
|
||||
->method('read')
|
||||
->will($this->throwException(new UnreadableException()));
|
||||
->will($this->throwException(new ClosedException));
|
||||
|
||||
$a = new ChannelledStream($mock);
|
||||
|
||||
$data = yield from $a->receive();
|
||||
})->done();
|
||||
$data = yield $a->receive();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Concurrent\Sync\FileMutex;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
|
||||
class FileMutexTest extends TestCase
|
||||
{
|
||||
public function testAcquire()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
class FileMutexTest extends TestCase {
|
||||
public function testAcquire() {
|
||||
\Amp\execute(function () {
|
||||
$mutex = new FileMutex();
|
||||
$lock = yield from $mutex->acquire();
|
||||
$lock = yield $mutex->acquire();
|
||||
$lock->release();
|
||||
$this->assertTrue($lock->isReleased());
|
||||
})->done();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testAcquireMultiple()
|
||||
{
|
||||
Loop\loop();
|
||||
|
||||
public function testAcquireMultiple() {
|
||||
$this->assertRunTimeGreaterThan(function () {
|
||||
Coroutine\create(function () {
|
||||
\Amp\execute(function () {
|
||||
$mutex = new FileMutex();
|
||||
|
||||
$lock1 = yield from $mutex->acquire();
|
||||
Loop\timer(0.5, function () use ($lock1) {
|
||||
$lock1 = yield $mutex->acquire();
|
||||
\Amp\delay(500, function () use ($lock1) {
|
||||
$lock1->release();
|
||||
});
|
||||
|
||||
$lock2 = yield from $mutex->acquire();
|
||||
Loop\timer(0.5, function () use ($lock2) {
|
||||
$lock2 = yield $mutex->acquire();
|
||||
\Amp\delay(500, function () use ($lock2) {
|
||||
$lock2->release();
|
||||
});
|
||||
|
||||
$lock3 = yield from $mutex->acquire();
|
||||
Loop\timer(0.5, function () use ($lock3) {
|
||||
$lock3 = yield $mutex->acquire();
|
||||
\Amp\delay(500, function () use ($lock3) {
|
||||
$lock3->release();
|
||||
});
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}, 1.5);
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Concurrent\Sync\Lock;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
|
||||
class LockTest extends TestCase
|
||||
{
|
||||
public function testIsReleased()
|
||||
{
|
||||
class LockTest extends TestCase {
|
||||
public function testIsReleased() {
|
||||
$lock = new Lock($this->createCallback(1));
|
||||
$this->assertFalse($lock->isReleased());
|
||||
$lock->release();
|
||||
$this->assertTrue($lock->isReleased());
|
||||
}
|
||||
|
||||
public function testIsReleasedOnDestruct()
|
||||
{
|
||||
public function testIsReleasedOnDestruct() {
|
||||
$lock = new Lock($this->createCallback(1));
|
||||
unset($lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Amp\Concurrent\Exception\LockAlreadyReleasedError
|
||||
* @expectedException \Amp\Concurrent\LockAlreadyReleasedError
|
||||
*/
|
||||
public function testThrowsOnMultiRelease()
|
||||
{
|
||||
public function testThrowsOnMultiRelease() {
|
||||
$lock = new Lock($this->createCallback(1));
|
||||
$lock->release();
|
||||
$lock->release();
|
||||
|
@ -1,37 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Concurrent\Forking\Fork;
|
||||
use Amp\Concurrent\Sync\{PosixSemaphore, Semaphore};
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
|
||||
/**
|
||||
* @group posix
|
||||
* @requires extension sysvmsg
|
||||
*/
|
||||
class PosixSemaphoreTest extends AbstractSemaphoreTest
|
||||
{
|
||||
public function createSemaphore($locks)
|
||||
{
|
||||
class PosixSemaphoreTest extends AbstractSemaphoreTest {
|
||||
/**
|
||||
* @param $locks
|
||||
*
|
||||
* @return \Amp\Concurrent\Sync\PosixSemaphore
|
||||
*/
|
||||
public function createSemaphore(int $locks) {
|
||||
return new PosixSemaphore($locks);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
public function tearDown() {
|
||||
if ($this->semaphore && !$this->semaphore->isFreed()) {
|
||||
$this->semaphore->free();
|
||||
}
|
||||
}
|
||||
|
||||
public function testCloneIsNewSemaphore()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testCloneIsNewSemaphore() {
|
||||
\Amp\execute(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
$clone = clone $this->semaphore;
|
||||
|
||||
$lock = yield from $clone->acquire();
|
||||
$lock = yield $clone->acquire();
|
||||
|
||||
$this->assertCount(1, $this->semaphore);
|
||||
$this->assertCount(0, $clone);
|
||||
@ -39,13 +38,11 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
|
||||
$lock->release();
|
||||
|
||||
$clone->free();
|
||||
})->done();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testFree()
|
||||
{
|
||||
public function testFree() {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
|
||||
$this->assertFalse($this->semaphore->isFreed());
|
||||
@ -58,29 +55,28 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
|
||||
/**
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
public function testAcquireInMultipleForks()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testAcquireInMultipleForks() {
|
||||
\Amp\execute(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
|
||||
$fork1 = new Fork(function (Semaphore $semaphore) {
|
||||
$lock = yield from $semaphore->acquire();
|
||||
$lock = yield $semaphore->acquire();
|
||||
|
||||
usleep(1e5);
|
||||
|
||||
$lock->release();
|
||||
|
||||
yield 0;
|
||||
return 0;
|
||||
}, $this->semaphore);
|
||||
|
||||
$fork2 = new Fork(function (Semaphore $semaphore) {
|
||||
$lock = yield from $semaphore->acquire();
|
||||
$lock = yield $semaphore->acquire();
|
||||
|
||||
usleep(1e5);
|
||||
|
||||
$lock->release();
|
||||
|
||||
yield 1;
|
||||
return 1;
|
||||
}, $this->semaphore);
|
||||
|
||||
$start = microtime(true);
|
||||
@ -88,12 +84,10 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
|
||||
$fork1->start();
|
||||
$fork2->start();
|
||||
|
||||
yield from $fork1->join();
|
||||
yield from $fork2->join();
|
||||
yield $fork1->join();
|
||||
yield $fork2->join();
|
||||
|
||||
$this->assertGreaterThan(1, microtime(true) - $start);
|
||||
$this->assertGreaterThan(0.1, microtime(true) - $start);
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Sync;
|
||||
namespace Amp\Concurrent\Test\Sync;
|
||||
|
||||
use Amp\Concurrent\Sync\SharedMemoryParcel;
|
||||
use Amp\Coroutine;
|
||||
|
||||
/**
|
||||
* @requires extension shmop
|
||||
* @requires extension sysvmsg
|
||||
*/
|
||||
class SharedMemoryParcelTest extends AbstractParcelTest
|
||||
{
|
||||
class SharedMemoryParcelTest extends AbstractParcelTest {
|
||||
private $parcel;
|
||||
|
||||
protected function createParcel($value)
|
||||
{
|
||||
protected function createParcel($value) {
|
||||
$this->parcel = new SharedMemoryParcel($value);
|
||||
return $this->parcel;
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
public function tearDown() {
|
||||
if ($this->parcel !== null) {
|
||||
$this->parcel->free();
|
||||
}
|
||||
}
|
||||
|
||||
public function testNewObjectIsNotFreed()
|
||||
{
|
||||
public function testNewObjectIsNotFreed() {
|
||||
$object = new SharedMemoryParcel(new \stdClass());
|
||||
$this->assertFalse($object->isFreed());
|
||||
$object->free();
|
||||
}
|
||||
|
||||
public function testFreeReleasesObject()
|
||||
{
|
||||
public function testFreeReleasesObject() {
|
||||
$object = new SharedMemoryParcel(new \stdClass());
|
||||
$object->free();
|
||||
$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->free();
|
||||
$object->unwrap();
|
||||
}
|
||||
|
||||
public function testCloneIsNewObject()
|
||||
{
|
||||
public function testCloneIsNewObject() {
|
||||
$object = new \stdClass();
|
||||
$shared = new SharedMemoryParcel($object);
|
||||
$clone = clone $shared;
|
||||
@ -64,13 +56,12 @@ class SharedMemoryParcelTest extends AbstractParcelTest
|
||||
$shared->free();
|
||||
}
|
||||
|
||||
public function testObjectOverflowMoved()
|
||||
{
|
||||
public function testObjectOverflowMoved() {
|
||||
$object = new SharedMemoryParcel('hi', 14);
|
||||
$coroutine = new Coroutine($object->synchronized(function () {
|
||||
$awaitable = $object->synchronized(function () {
|
||||
return 'hello world';
|
||||
}));
|
||||
$coroutine->wait();
|
||||
});
|
||||
\Amp\wait($awaitable);
|
||||
|
||||
$this->assertEquals('hello world', $object->unwrap());
|
||||
$object->free();
|
||||
@ -80,15 +71,14 @@ class SharedMemoryParcelTest extends AbstractParcelTest
|
||||
* @group posix
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
public function testSetInSeparateProcess()
|
||||
{
|
||||
public function testSetInSeparateProcess() {
|
||||
$object = new SharedMemoryParcel(42);
|
||||
|
||||
$this->doInFork(function () use ($object) {
|
||||
$coroutine = new Coroutine($object->synchronized(function () {
|
||||
$awaitable = $object->synchronized(function () {
|
||||
return 43;
|
||||
}));
|
||||
$coroutine->wait();
|
||||
});
|
||||
\Amp\wait($awaitable);
|
||||
});
|
||||
|
||||
$this->assertEquals(43, $object->unwrap());
|
||||
@ -99,8 +89,7 @@ class SharedMemoryParcelTest extends AbstractParcelTest
|
||||
* @group posix
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
public function testFreeInSeparateProcess()
|
||||
{
|
||||
public function testFreeInSeparateProcess() {
|
||||
$object = new SharedMemoryParcel(42);
|
||||
|
||||
$this->doInFork(function () use ($object) {
|
||||
|
@ -1,14 +1,13 @@
|
||||
<?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 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.
|
||||
|
||||
/**
|
||||
@ -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.
|
||||
*/
|
||||
public function createCallback($count)
|
||||
{
|
||||
$mock = $this->getMock(CallbackStub::class);
|
||||
public function createCallback($count) {
|
||||
$mock = $this->createMock(CallbackStub::class);
|
||||
|
||||
$mock->expects($this->exactly($count))
|
||||
->method('__invoke');
|
||||
@ -35,8 +33,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
* @param float $maxRunTime
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -47,8 +44,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
* @param float $minRunTime
|
||||
* @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);
|
||||
}
|
||||
|
||||
@ -61,19 +57,18 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
* @param float $maxRunTime
|
||||
* @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);
|
||||
|
||||
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) {
|
||||
$this->assertLessThanOrEqual(
|
||||
$maxRunTime,
|
||||
$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(
|
||||
$minRunTime,
|
||||
$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)
|
||||
{
|
||||
switch ($pid = pcntl_fork()) {
|
||||
final protected function doInFork(callable $function) {
|
||||
switch ($pid = \pcntl_fork()) {
|
||||
case -1:
|
||||
$this->fail('Failed to fork process.');
|
||||
break;
|
||||
@ -96,7 +90,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
||||
$status = (int) $function();
|
||||
exit($status);
|
||||
default:
|
||||
if (pcntl_waitpid($pid, $status) === -1) {
|
||||
if (\pcntl_waitpid($pid, $status) === -1) {
|
||||
$this->fail('Failed to fork process.');
|
||||
}
|
||||
return $status;
|
||||
|
@ -1,55 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Threading;
|
||||
namespace Amp\Concurrent\Test\Threading;
|
||||
|
||||
use Amp\Concurrent\Threading\Mutex;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
|
||||
/**
|
||||
* @group threading
|
||||
* @requires extension pthreads
|
||||
*/
|
||||
class MutexTest extends TestCase
|
||||
{
|
||||
public function testAcquire()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
class MutexTest extends TestCase {
|
||||
public function testAcquire() {
|
||||
\Amp\execute(function () {
|
||||
$mutex = new Mutex();
|
||||
$lock = yield from $mutex->acquire();
|
||||
$lock = yield $mutex->acquire();
|
||||
$lock->release();
|
||||
$this->assertTrue($lock->isReleased());
|
||||
})->done();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testAcquireMultiple()
|
||||
{
|
||||
Loop\loop();
|
||||
|
||||
public function testAcquireMultiple() {
|
||||
$this->assertRunTimeGreaterThan(function () {
|
||||
Coroutine\create(function () {
|
||||
\Amp\execute(function () {
|
||||
$mutex = new Mutex();
|
||||
|
||||
$lock1 = yield from $mutex->acquire();
|
||||
Loop\timer(0.5, function () use ($lock1) {
|
||||
$lock1 = yield $mutex->acquire();
|
||||
\Amp\delay(500, function () use ($lock1) {
|
||||
$lock1->release();
|
||||
});
|
||||
|
||||
$lock2 = yield from $mutex->acquire();
|
||||
Loop\timer(0.5, function () use ($lock2) {
|
||||
$lock2 = yield $mutex->acquire();
|
||||
\Amp\delay(500, function () use ($lock2) {
|
||||
$lock2->release();
|
||||
});
|
||||
|
||||
$lock3 = yield from $mutex->acquire();
|
||||
Loop\timer(0.5, function () use ($lock3) {
|
||||
$lock3 = yield $mutex->acquire();
|
||||
\Amp\delay(500, function () use ($lock3) {
|
||||
$lock3->release();
|
||||
});
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}, 1.5);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Threading;
|
||||
namespace Amp\Concurrent\Test\Threading;
|
||||
|
||||
use Amp\Concurrent\Threading\Parcel;
|
||||
use Amp\Tests\Concurrent\Sync\AbstractParcelTest;
|
||||
use Amp\Concurrent\Test\Sync\AbstractParcelTest;
|
||||
|
||||
/**
|
||||
* @requires extension pthreads
|
||||
*/
|
||||
class ParcelTest extends AbstractParcelTest
|
||||
{
|
||||
protected function createParcel($value)
|
||||
{
|
||||
class ParcelTest extends AbstractParcelTest {
|
||||
protected function createParcel($value) {
|
||||
return new Parcel($value);
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Threading;
|
||||
namespace Amp\Concurrent\Test\Threading;
|
||||
|
||||
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
|
||||
use Amp\Concurrent\Threading\{Semaphore, Thread};
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\Sync\AbstractSemaphoreTest;
|
||||
use Amp\Concurrent\Test\Sync\AbstractSemaphoreTest;
|
||||
|
||||
/**
|
||||
* @group threading
|
||||
* @requires extension pthreads
|
||||
*/
|
||||
class SemaphoreTest extends AbstractSemaphoreTest
|
||||
{
|
||||
public function createSemaphore($locks)
|
||||
{
|
||||
class SemaphoreTest extends AbstractSemaphoreTest {
|
||||
public function createSemaphore(int $locks) {
|
||||
return new Semaphore($locks);
|
||||
}
|
||||
|
||||
public function testAcquireInMultipleThreads()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testAcquireInMultipleThreads() {
|
||||
\Amp\execute(function () {
|
||||
$this->semaphore = $this->createSemaphore(1);
|
||||
|
||||
$thread1 = new Thread(function (SyncSemaphore $semaphore) {
|
||||
$lock = yield from $semaphore->acquire();
|
||||
$lock = yield $semaphore->acquire();
|
||||
|
||||
usleep(1e5);
|
||||
|
||||
@ -35,7 +30,7 @@ class SemaphoreTest extends AbstractSemaphoreTest
|
||||
}, $this->semaphore);
|
||||
|
||||
$thread2 = new Thread(function (SyncSemaphore $semaphore) {
|
||||
$lock = yield from $semaphore->acquire();
|
||||
$lock = yield $semaphore->acquire();
|
||||
|
||||
usleep(1e5);
|
||||
|
||||
@ -49,12 +44,10 @@ class SemaphoreTest extends AbstractSemaphoreTest
|
||||
$thread1->start();
|
||||
$thread2->start();
|
||||
|
||||
yield from $thread1->join();
|
||||
yield from $thread2->join();
|
||||
yield $thread1->join();
|
||||
yield $thread2->join();
|
||||
|
||||
$this->assertGreaterThan(1, microtime(true) - $start);
|
||||
$this->assertGreaterThan(0.1, microtime(true) - $start);
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Threading;
|
||||
namespace Amp\Concurrent\Test\Threading;
|
||||
|
||||
use Amp\Concurrent\Threading\Thread;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\AbstractContextTest;
|
||||
use Amp\Concurrent\Test\AbstractContextTest;
|
||||
|
||||
/**
|
||||
* @group threading
|
||||
* @requires extension pthreads
|
||||
*/
|
||||
class ThreadTest extends AbstractContextTest
|
||||
{
|
||||
public function createContext(callable $function)
|
||||
{
|
||||
class ThreadTest extends AbstractContextTest {
|
||||
public function createContext(callable $function) {
|
||||
return new Thread($function);
|
||||
}
|
||||
|
||||
public function testSpawnStartsThread()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testSpawnStartsThread() {
|
||||
\Amp\execute(function () {
|
||||
$thread = Thread::spawn(function () {
|
||||
usleep(100);
|
||||
});
|
||||
|
||||
return yield from $thread->join();
|
||||
})->done();
|
||||
return yield $thread->join();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Worker;
|
||||
namespace Amp\Concurrent\Test\Worker;
|
||||
|
||||
use Amp\Awaitable;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
|
||||
abstract class AbstractPoolTest extends TestCase
|
||||
{
|
||||
abstract class AbstractPoolTest extends TestCase {
|
||||
/**
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
@ -17,105 +13,96 @@ abstract class AbstractPoolTest extends TestCase
|
||||
*/
|
||||
abstract protected function createPool($min = null, $max = null);
|
||||
|
||||
public function testIsRunning()
|
||||
{
|
||||
Coroutine\run(function () {
|
||||
public function testIsRunning() {
|
||||
\Amp\execute(function () {
|
||||
$pool = $this->createPool();
|
||||
$this->assertFalse($pool->isRunning());
|
||||
|
||||
$pool->start();
|
||||
$this->assertTrue($pool->isRunning());
|
||||
|
||||
yield from $pool->shutdown();
|
||||
yield $pool->shutdown();
|
||||
$this->assertFalse($pool->isRunning());
|
||||
});
|
||||
}
|
||||
|
||||
public function testIsIdleOnStart()
|
||||
{
|
||||
Coroutine\run(function () {
|
||||
public function testIsIdleOnStart() {
|
||||
\Amp\execute(function () {
|
||||
$pool = $this->createPool();
|
||||
$pool->start();
|
||||
|
||||
$this->assertTrue($pool->isIdle());
|
||||
|
||||
yield from $pool->shutdown();
|
||||
yield $pool->shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
public function testGetMinSize()
|
||||
{
|
||||
public function testGetMinSize() {
|
||||
$pool = $this->createPool(7, 24);
|
||||
$this->assertEquals(7, $pool->getMinSize());
|
||||
}
|
||||
|
||||
public function testGetMaxSize()
|
||||
{
|
||||
public function testGetMaxSize() {
|
||||
$pool = $this->createPool(3, 17);
|
||||
$this->assertEquals(17, $pool->getMaxSize());
|
||||
}
|
||||
|
||||
public function testMinWorkersSpawnedOnStart()
|
||||
{
|
||||
Coroutine\run(function () {
|
||||
public function testMinWorkersSpawnedOnStart() {
|
||||
\Amp\execute(function () {
|
||||
$pool = $this->createPool(8, 32);
|
||||
$pool->start();
|
||||
|
||||
$this->assertEquals(8, $pool->getWorkerCount());
|
||||
|
||||
yield from $pool->shutdown();
|
||||
yield $pool->shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
public function testWorkersIdleOnStart()
|
||||
{
|
||||
Coroutine\run(function () {
|
||||
public function testWorkersIdleOnStart() {
|
||||
\Amp\execute(function () {
|
||||
$pool = $this->createPool(8, 32);
|
||||
$pool->start();
|
||||
|
||||
$this->assertEquals(8, $pool->getIdleWorkerCount());
|
||||
|
||||
yield from $pool->shutdown();
|
||||
yield $pool->shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
public function testEnqueue()
|
||||
{
|
||||
Coroutine\run(function () {
|
||||
public function testEnqueue() {
|
||||
\Amp\execute(function () {
|
||||
$pool = $this->createPool();
|
||||
$pool->start();
|
||||
|
||||
$returnValue = yield from $pool->enqueue(new TestTask(42));
|
||||
$returnValue = yield $pool->enqueue(new TestTask(42));
|
||||
$this->assertEquals(42, $returnValue);
|
||||
|
||||
yield from $pool->shutdown();
|
||||
yield $pool->shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
public function testEnqueueMultiple()
|
||||
{
|
||||
Coroutine\run(function () {
|
||||
public function testEnqueueMultiple() {
|
||||
\Amp\execute(function () {
|
||||
$pool = $this->createPool();
|
||||
$pool->start();
|
||||
|
||||
$values = yield Awaitable\all([
|
||||
new Coroutine\Coroutine($pool->enqueue(new TestTask(42))),
|
||||
new Coroutine\Coroutine($pool->enqueue(new TestTask(56))),
|
||||
new Coroutine\Coroutine($pool->enqueue(new TestTask(72)))
|
||||
$values = yield \Amp\all([
|
||||
$pool->enqueue(new TestTask(42)),
|
||||
$pool->enqueue(new TestTask(56)),
|
||||
$pool->enqueue(new TestTask(72))
|
||||
]);
|
||||
|
||||
$this->assertEquals([42, 56, 72], $values);
|
||||
|
||||
yield from $pool->shutdown();
|
||||
yield $pool->shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
public function testKill()
|
||||
{
|
||||
public function testKill() {
|
||||
$pool = $this->createPool();
|
||||
$pool->start();
|
||||
|
||||
$this->assertRunTimeLessThan([$pool, 'kill'], 0.5);
|
||||
$this->assertRunTimeLessThan([$pool, 'kill'], 1);
|
||||
$this->assertFalse($pool->isRunning());
|
||||
}
|
||||
}
|
||||
|
@ -1,102 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Worker;
|
||||
namespace Amp\Concurrent\Test\Worker;
|
||||
|
||||
use Amp\Awaitable;
|
||||
use Amp\Coroutine;
|
||||
use Amp\Loop;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
|
||||
abstract class AbstractWorkerTest extends TestCase
|
||||
{
|
||||
abstract class AbstractWorkerTest extends TestCase {
|
||||
/**
|
||||
* @return \Amp\Concurrent\Worker\Worker
|
||||
*/
|
||||
abstract protected function createWorker();
|
||||
|
||||
public function testIsRunning()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testIsRunning() {
|
||||
\Amp\execute(function () {
|
||||
$worker = $this->createWorker();
|
||||
$this->assertFalse($worker->isRunning());
|
||||
|
||||
$worker->start();
|
||||
$this->assertTrue($worker->isRunning());
|
||||
|
||||
yield from $worker->shutdown();
|
||||
yield $worker->shutdown();
|
||||
$this->assertFalse($worker->isRunning());
|
||||
})->done();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testIsIdleOnStart()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testIsIdleOnStart() {
|
||||
\Amp\execute(function () {
|
||||
$worker = $this->createWorker();
|
||||
$worker->start();
|
||||
|
||||
$this->assertTrue($worker->isIdle());
|
||||
|
||||
yield from $worker->shutdown();
|
||||
})->done();
|
||||
yield $worker->shutdown();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testEnqueue()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testEnqueue() {
|
||||
\Amp\execute(function () {
|
||||
$worker = $this->createWorker();
|
||||
$worker->start();
|
||||
|
||||
$returnValue = yield from $worker->enqueue(new TestTask(42));
|
||||
$returnValue = yield $worker->enqueue(new TestTask(42));
|
||||
$this->assertEquals(42, $returnValue);
|
||||
|
||||
yield from $worker->shutdown();
|
||||
})->done();
|
||||
yield $worker->shutdown();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testEnqueueMultiple()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testEnqueueMultiple() {
|
||||
\Amp\execute(function () {
|
||||
$worker = $this->createWorker();
|
||||
$worker->start();
|
||||
|
||||
$values = yield Awaitable\all([
|
||||
new Coroutine\Coroutine($worker->enqueue(new TestTask(42))),
|
||||
new Coroutine\Coroutine($worker->enqueue(new TestTask(56))),
|
||||
new Coroutine\Coroutine($worker->enqueue(new TestTask(72)))
|
||||
|
||||
$values = yield \Amp\all([
|
||||
$worker->enqueue(new TestTask(42)),
|
||||
$worker->enqueue(new TestTask(56)),
|
||||
$worker->enqueue(new TestTask(72))
|
||||
]);
|
||||
|
||||
$this->assertEquals([42, 56, 72], $values);
|
||||
|
||||
yield from $worker->shutdown();
|
||||
})->done();
|
||||
yield $worker->shutdown();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testNotIdleOnEnqueue()
|
||||
{
|
||||
Coroutine\create(function () {
|
||||
public function testNotIdleOnEnqueue() {
|
||||
\Amp\execute(function () {
|
||||
$worker = $this->createWorker();
|
||||
$worker->start();
|
||||
|
||||
$coroutine = new Coroutine\Coroutine($worker->enqueue(new TestTask(42)));
|
||||
$coroutine = $worker->enqueue(new TestTask(42));
|
||||
$this->assertFalse($worker->isIdle());
|
||||
yield $coroutine;
|
||||
|
||||
yield from $worker->shutdown();
|
||||
})->done();
|
||||
yield $worker->shutdown();
|
||||
});
|
||||
|
||||
Loop\run();
|
||||
}
|
||||
|
||||
public function testKill()
|
||||
{
|
||||
public function testKill() {
|
||||
$worker = $this->createWorker();
|
||||
$worker->start();
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
<?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
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
class ForkPoolTest extends AbstractPoolTest
|
||||
{
|
||||
protected function createPool($min = null, $max = null)
|
||||
{
|
||||
$factory = $this->getMock(WorkerFactory::class);
|
||||
class ForkPoolTest extends AbstractPoolTest {
|
||||
protected function createPool($min = null, $max = null) {
|
||||
$factory = $this->createMock(WorkerFactory::class);
|
||||
$factory->method('create')->will($this->returnCallback(function () {
|
||||
return new WorkerFork();
|
||||
}));
|
||||
|
@ -1,17 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Worker;
|
||||
namespace Amp\Concurrent\Test\Worker;
|
||||
|
||||
use Amp\Concurrent\Worker;
|
||||
use Amp\Concurrent\Worker\{Environment, Pool, Task, WorkerFactory};
|
||||
use Amp\Coroutine;
|
||||
use Amp\Tests\Concurrent\TestCase;
|
||||
use Amp\Concurrent\Worker\{ Environment, Pool, Task, WorkerFactory };
|
||||
use Amp\Concurrent\Test\TestCase;
|
||||
use Amp\Success;
|
||||
use Interop\Async\Awaitable;
|
||||
|
||||
class FunctionsTest extends TestCase
|
||||
{
|
||||
public function testPool()
|
||||
{
|
||||
$pool = $this->getMock(Pool::class);
|
||||
class FunctionsTest extends TestCase {
|
||||
public function testPool() {
|
||||
$pool = $this->createMock(Pool::class);
|
||||
|
||||
Worker\pool($pool);
|
||||
|
||||
@ -21,12 +20,11 @@ class FunctionsTest extends TestCase
|
||||
/**
|
||||
* @depends testPool
|
||||
*/
|
||||
public function testEnqueue()
|
||||
{
|
||||
$pool = $this->getMock(Pool::class);
|
||||
public function testEnqueue() {
|
||||
$pool = $this->createMock(Pool::class);
|
||||
$pool->method('enqueue')
|
||||
->will($this->returnCallback(function (Task $task) {
|
||||
return yield $task->run($this->getMock(Environment::class));
|
||||
->will($this->returnCallback(function (Task $task): Awaitable {
|
||||
return new Success($task->run($this->createMock(Environment::class)));
|
||||
}));
|
||||
|
||||
Worker\pool($pool);
|
||||
@ -35,29 +33,27 @@ class FunctionsTest extends TestCase
|
||||
|
||||
$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
|
||||
*/
|
||||
public function testGet()
|
||||
{
|
||||
$pool = $this->getMock(Pool::class);
|
||||
public function testGet() {
|
||||
$pool = $this->createMock(Pool::class);
|
||||
$pool->expects($this->once())
|
||||
->method('get')
|
||||
->will($this->returnValue($this->getMock(Worker\Worker::class)));
|
||||
->will($this->returnValue($this->createMock(Worker\Worker::class)));
|
||||
|
||||
Worker\pool($pool);
|
||||
|
||||
$worker = Worker\get();
|
||||
}
|
||||
|
||||
public function testFactory()
|
||||
{
|
||||
$factory = $this->getMock(WorkerFactory::class);
|
||||
public function testFactory() {
|
||||
$factory = $this->createMock(WorkerFactory::class);
|
||||
|
||||
Worker\factory($factory);
|
||||
|
||||
@ -67,12 +63,11 @@ class FunctionsTest extends TestCase
|
||||
/**
|
||||
* @depends testFactory
|
||||
*/
|
||||
public function testCreate()
|
||||
{
|
||||
$factory = $this->getMock(WorkerFactory::class);
|
||||
public function testCreate() {
|
||||
$factory = $this->createMock(WorkerFactory::class);
|
||||
$factory->expects($this->once())
|
||||
->method('create')
|
||||
->will($this->returnValue($this->getMock(Worker\Worker::class)));
|
||||
->will($this->returnValue($this->createMock(Worker\Worker::class)));
|
||||
|
||||
Worker\factory($factory);
|
||||
|
||||
|
@ -1,17 +1,15 @@
|
||||
<?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
|
||||
*/
|
||||
class ProcessPoolTest extends AbstractPoolTest
|
||||
{
|
||||
protected function createPool($min = null, $max = null)
|
||||
{
|
||||
$factory = $this->getMock(WorkerFactory::class);
|
||||
class ProcessPoolTest extends AbstractPoolTest {
|
||||
protected function createPool($min = null, $max = null) {
|
||||
$factory = $this->createMock(WorkerFactory::class);
|
||||
$factory->method('create')->will($this->returnCallback(function () {
|
||||
return new WorkerProcess();
|
||||
}));
|
||||
|
@ -1,20 +1,17 @@
|
||||
<?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;
|
||||
|
||||
public function __construct($returnValue)
|
||||
{
|
||||
public function __construct($returnValue) {
|
||||
$this->returnValue = $returnValue;
|
||||
}
|
||||
|
||||
public function run(Environment $environment)
|
||||
{
|
||||
public function run(Environment $environment) {
|
||||
return $this->returnValue;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
<?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
|
||||
* @requires extension pthreads
|
||||
*/
|
||||
class ThreadPoolTest extends AbstractPoolTest
|
||||
{
|
||||
protected function createPool($min = null, $max = null)
|
||||
{
|
||||
$factory = $this->getMock(WorkerFactory::class);
|
||||
class ThreadPoolTest extends AbstractPoolTest {
|
||||
protected function createPool($min = null, $max = null) {
|
||||
$factory = $this->createMock(WorkerFactory::class);
|
||||
$factory->method('create')->will($this->returnCallback(function () {
|
||||
return new WorkerThread();
|
||||
}));
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Worker;
|
||||
namespace Amp\Concurrent\Test\Worker;
|
||||
|
||||
use Amp\Concurrent\Worker\WorkerFork;
|
||||
|
||||
@ -8,10 +8,8 @@ use Amp\Concurrent\Worker\WorkerFork;
|
||||
* @group forking
|
||||
* @requires extension pcntl
|
||||
*/
|
||||
class WorkerForkTest extends AbstractWorkerTest
|
||||
{
|
||||
protected function createWorker()
|
||||
{
|
||||
class WorkerForkTest extends AbstractWorkerTest {
|
||||
protected function createWorker() {
|
||||
return new WorkerFork();
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Worker;
|
||||
namespace Amp\Concurrent\Test\Worker;
|
||||
|
||||
use Amp\Concurrent\Worker\WorkerProcess;
|
||||
|
||||
class WorkerProcessTest extends AbstractWorkerTest
|
||||
{
|
||||
protected function createWorker()
|
||||
{
|
||||
class WorkerProcessTest extends AbstractWorkerTest {
|
||||
protected function createWorker() {
|
||||
return new WorkerProcess();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Amp\Tests\Concurrent\Worker;
|
||||
namespace Amp\Concurrent\Test\Worker;
|
||||
|
||||
use Amp\Concurrent\Worker\WorkerThread;
|
||||
|
||||
@ -8,10 +8,8 @@ use Amp\Concurrent\Worker\WorkerThread;
|
||||
* @group threading
|
||||
* @requires extension pthreads
|
||||
*/
|
||||
class WorkerThreadTest extends AbstractWorkerTest
|
||||
{
|
||||
protected function createWorker()
|
||||
{
|
||||
class WorkerThreadTest extends AbstractWorkerTest {
|
||||
protected function createWorker() {
|
||||
return new WorkerThread();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user