1
0
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:
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:
- 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

View File

@ -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"
}
}
}

View File

@ -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);
}
}

View File

@ -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 () {

View File

@ -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();

View File

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

View File

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

View File

@ -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;

View File

@ -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';
}

View File

@ -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));
}
/**

View File

@ -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.');
}

View File

@ -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) {

View File

@ -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);
}

View File

@ -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());
}
}

View File

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

View File

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

View File

@ -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));
}
}

View File

@ -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>

View File

@ -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();
});
}
}

View File

@ -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();
});
}
}

View File

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

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -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();

View File

@ -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();
}));

View File

@ -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);

View File

@ -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();
}));

View File

@ -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;
}
}

View File

@ -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();
}));

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}