+ */
+interface RouterInterface
+{
+ public function fetchMessages();
+
+ public function consume(MessageCollection $messageCollection);
+}
diff --git a/src/Symfony/Component/Worker/Tests/Command/WorkerListCommandTest.php b/src/Symfony/Component/Worker/Tests/Command/WorkerListCommandTest.php
new file mode 100644
index 0000000000000..c03e3f33964d6
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/Command/WorkerListCommandTest.php
@@ -0,0 +1,36 @@
+execute(array());
+ $this->assertSame($expected, $tester->getDisplay());
+ }
+
+ public function testExecuteNoWorker()
+ {
+ $tester = new CommandTester(new WorkerListCommand());
+ $tester->execute(array(), array('decorated' => false));
+ $this->assertContains('[ERROR] There are no available workers.', $tester->getDisplay());
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/Loop/LoopTest.php b/src/Symfony/Component/Worker/Tests/Loop/LoopTest.php
new file mode 100644
index 0000000000000..20655f0fc79f6
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/Loop/LoopTest.php
@@ -0,0 +1,276 @@
+messageFetcher = new MessageFetcher();
+ $this->consumer = new ConsumerMock();
+ $this->eventDispatcher = new EventDispatcherMock();
+ $this->logger = new LoggerMock();
+
+ $this->loop = new Loop(new DirectRouter($this->messageFetcher, $this->consumer), $this->eventDispatcher, $this->logger, 'a_queue_name');
+ }
+
+ public function provideReturnStatus()
+ {
+ yield array(false, 'warning Messages consumed with failure.');
+ yield array(true, 'info Messages consumed successfully.');
+ }
+
+ /**
+ * @dataProvider provideReturnStatus
+ */
+ public function testConsumeAllPendingMessagesInOneRow($returnStatus, $expectedLog)
+ {
+ $this->messageFetcher->messages = array('a', 'b');
+ $this->consumer->setConsumeCode(function () use ($returnStatus) {
+ return $returnStatus;
+ });
+
+ $this->loop->run();
+
+ $this->assertSame(array('a', 'b'), $this->consumer->messages);
+
+ $expectedEvents = array(
+ 'worker.run',
+ 'worker.health_check',
+ 'worker.wake_up',
+ 'worker.stop',
+ );
+
+ $this->assertSame($expectedEvents, $this->eventDispatcher->dispatchedEvents);
+
+ $expectedLogs = array(
+ 'notice Worker a_queue_name started.',
+ 'notice New message.',
+ $expectedLog,
+ 'notice New message.',
+ $expectedLog,
+ 'notice Worker a_queue_name stopped (Force shut down of the worker because a StopException has been thrown.).',
+ );
+
+ $this->assertEquals($expectedLogs, $this->logger->logs);
+ }
+
+ public function testConsumePendingMessages()
+ {
+ $this->messageFetcher->messages = array('a', false, 'b');
+
+ $this->loop->run();
+
+ $this->assertSame(array('a', 'b'), $this->consumer->messages);
+
+ $expectedEvents = array(
+ 'worker.run',
+ 'worker.health_check',
+ 'worker.wake_up',
+ 'worker.sleep',
+ 'worker.wake_up',
+ 'worker.stop',
+ );
+
+ $this->assertEquals($expectedEvents, $this->eventDispatcher->dispatchedEvents);
+
+ $expectedLogs = array(
+ 'notice Worker a_queue_name started.',
+ 'notice New message.',
+ 'info Messages consumed successfully.',
+ 'notice New message.',
+ 'info Messages consumed successfully.',
+ 'notice Worker a_queue_name stopped (Force shut down of the worker because a StopException has been thrown.).',
+ );
+
+ $this->assertSame($expectedLogs, $this->logger->logs);
+ }
+
+ public function testSignal()
+ {
+ $this->messageFetcher->messages = array('a');
+
+ // After 1 second a SIGALRM signal will be fired and it will stop the
+ // loop.
+ pcntl_signal(SIGALRM, function () {
+ $this->loop->stop('Signaled with SIGALRM');
+ });
+ pcntl_alarm(1);
+
+ // Let's wait 1 second in the consumer, to avoid too many loop iteration
+ // in order to avoid too many event.
+ $this->consumer->setConsumeCode(function () {
+ // we don't want to use the mock sleep here
+ \sleep(1);
+ });
+
+ $this->loop->run();
+
+ $expectedEvents = array(
+ 'worker.run',
+ 'worker.health_check',
+ 'worker.wake_up',
+ 'worker.stop',
+ );
+
+ $this->assertSame($expectedEvents, $this->eventDispatcher->dispatchedEvents);
+
+ $expectedLogs = array(
+ 'notice Worker a_queue_name started.',
+ 'notice New message.',
+ 'info Messages consumed successfully.',
+ 'notice Worker a_queue_name stopped (Signaled with SIGALRM).',
+ );
+
+ $this->assertSame($expectedLogs, $this->logger->logs);
+ }
+
+ public function testHealthCheck()
+ {
+ $this->messageFetcher->messages = array('a');
+
+ // default health check is done every 10 seconds
+ $this->consumer->setConsumeCode(function () {
+ sleep(10);
+ });
+
+ $this->loop->run();
+
+ $expectedEvents = array(
+ 'worker.run',
+ 'worker.health_check',
+ 'worker.wake_up',
+ 'worker.health_check',
+ 'worker.stop',
+ );
+
+ $this->assertEquals($expectedEvents, $this->eventDispatcher->dispatchedEvents);
+ }
+
+ public function provideException()
+ {
+ yield array(new \AMQPConnectionException('AMQP connexion error.'), 'error Worker a_queue_name has errored, shutting down. (AMQP connexion error.)');
+ yield array(new \Exception('oups.'), 'error Worker a_queue_name has errored, shutting down. (oups.)');
+ }
+
+ /**
+ * @dataProvider provideException
+ */
+ public function testException(\Exception $exception, $expectedLog)
+ {
+ $this->messageFetcher->messages = array('a');
+
+ $this->consumer->setConsumeCode(function () use ($exception) {
+ throw $exception;
+ });
+
+ $expectedLogs = array(
+ 'notice Worker a_queue_name started.',
+ 'notice New message.',
+ $expectedLog,
+ );
+
+ try {
+ $this->loop->run();
+
+ $this->fail('An exception should be thrown.');
+ } catch (\Exception $e) {
+ $this->assertSame($e, $exception);
+ }
+
+ $this->assertSame($expectedLogs, $this->logger->logs);
+ }
+}
+
+class ConsumerMock implements ConsumerInterface
+{
+ public $loop;
+ public $messages = array();
+
+ private $consumeCode;
+
+ public function consume(MessageCollection $messageCollection)
+ {
+ foreach ($messageCollection as $message) {
+ $this->messages[] = $message;
+ }
+
+ if ($this->consumeCode) {
+ return call_user_func($this->consumeCode);
+ }
+ }
+
+ public function setConsumeCode(callable $consumeCode)
+ {
+ $this->consumeCode = $consumeCode;
+ }
+}
+
+class MessageFetcher implements MessageFetcherInterface
+{
+ public $messages = array();
+
+ public function fetchMessages()
+ {
+ if (!$this->messages) {
+ throw new StopException();
+ }
+
+ $message = array_shift($this->messages);
+
+ if (false === $message) {
+ return false;
+ }
+
+ return new MessageCollection($message);
+ }
+}
+
+class EventDispatcherMock extends EventDispatcher
+{
+ public $dispatchedEvents = array();
+
+ public function dispatch($eventName, \Symfony\Component\EventDispatcher\Event $event = null)
+ {
+ $this->dispatchedEvents[] = $eventName;
+ }
+}
+
+class LoggerMock extends AbstractLogger
+{
+ public $logs = array();
+
+ public function log($level, $message, array $context = array())
+ {
+ $replacements = array();
+ foreach ($context as $key => $val) {
+ if (null === $val || is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) {
+ $replacements['{'.$key.'}'] = $val;
+ }
+ }
+
+ $message = strtr($message, $replacements);
+
+ $this->logs[] = sprintf('%-8s %s', $level, $message);
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/MessageCollectionTest.php b/src/Symfony/Component/Worker/Tests/MessageCollectionTest.php
new file mode 100644
index 0000000000000..fc75380f13709
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/MessageCollectionTest.php
@@ -0,0 +1,32 @@
+add('B');
+
+ $this->assertCount(2, $col);
+ $this->assertEquals(array('A', 'B'), $col->all());
+
+ $this->assertCount(0, $col);
+ $this->assertEquals(array(), $col->all());
+
+ $col->add('D');
+ $col->add('E');
+
+ $this->assertCount(2, $col);
+ $this->assertSame(array('D', 'E'), iterator_to_array($col));
+ $this->assertEquals('D', $col->pop());
+ $this->assertEquals('E', $col->pop());
+ $this->assertEquals(null, $col->pop());
+ $this->assertEquals(array(), $col->all());
+ $this->assertCount(0, $col);
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/MessageFetcher/AmqpMessageFetcherTest.php b/src/Symfony/Component/Worker/Tests/MessageFetcher/AmqpMessageFetcherTest.php
new file mode 100644
index 0000000000000..7c7e11aeb6e3b
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/MessageFetcher/AmqpMessageFetcherTest.php
@@ -0,0 +1,77 @@
+getMockBuilder(Broker::class)->disableOriginalConstructor()->getMock();
+ $broker
+ ->expects($this->once())
+ ->method('get')
+ ->with('queue', \AMQP_AUTOACK)
+ ->willReturn(false)
+ ;
+
+ $messageFetcher = new AmqpMessageFetcher($broker, 'queue', true);
+
+ $collection = $messageFetcher->fetchMessages();
+ $this->assertFalse($collection);
+ }
+
+ public function testWithAutoAckAndOneMessage()
+ {
+ $broker = $this->getMockBuilder(Broker::class)->disableOriginalConstructor()->getMock();
+ $broker
+ ->expects($this->once())
+ ->method('get')
+ ->with('queue', \AMQP_AUTOACK)
+ ->willReturn('A')
+ ;
+
+ $messageFetcher = new AmqpMessageFetcher($broker, 'queue', true);
+
+ $collection = $messageFetcher->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array('A'), iterator_to_array($collection));
+ }
+
+ public function testWithoutAutoAckAndNoMessage()
+ {
+ $broker = $this->getMockBuilder(Broker::class)->disableOriginalConstructor()->getMock();
+ $broker
+ ->expects($this->once())
+ ->method('get')
+ ->with('queue', \AMQP_NOPARAM)
+ ->willReturn(false)
+ ;
+
+ $messageFetcher = new AmqpMessageFetcher($broker, 'queue', false);
+
+ $collection = $messageFetcher->fetchMessages();
+ $this->assertFalse($collection);
+ }
+
+ public function testWithoutAutoAckAndOneMessage()
+ {
+ $broker = $this->getMockBuilder(Broker::class)->disableOriginalConstructor()->getMock();
+ $broker
+ ->expects($this->once())
+ ->method('get')
+ ->with('queue', \AMQP_NOPARAM)
+ ->willReturn('A')
+ ;
+
+ $messageFetcher = new AmqpMessageFetcher($broker, 'queue', false);
+
+ $collection = $messageFetcher->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array('A'), iterator_to_array($collection));
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/MessageFetcher/BufferedMessageFetcherTest.php b/src/Symfony/Component/Worker/Tests/MessageFetcher/BufferedMessageFetcherTest.php
new file mode 100644
index 0000000000000..3aa859c1f76a7
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/MessageFetcher/BufferedMessageFetcherTest.php
@@ -0,0 +1,43 @@
+ 2,
+ ));
+
+ $collection = $buffer->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array(1, 2), iterator_to_array($collection));
+
+ $collection = $buffer->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array(3, 4), iterator_to_array($collection));
+
+ // Wait for another message
+ $collection = $buffer->fetchMessages();
+ $this->assertFalse($collection);
+
+ sleep(10);
+
+ $collection = $buffer->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array(5), iterator_to_array($collection));
+
+ $collection = $buffer->fetchMessages();
+ $this->assertFalse($collection);
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/MessageFetcher/InMemoryMessageFetcherTest.php b/src/Symfony/Component/Worker/Tests/MessageFetcher/InMemoryMessageFetcherTest.php
new file mode 100644
index 0000000000000..868cc685a084d
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/MessageFetcher/InMemoryMessageFetcherTest.php
@@ -0,0 +1,38 @@
+fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array('A'), iterator_to_array($collection));
+
+ $collection = $fetcher->fetchMessages();
+ $this->assertFalse($collection);
+
+ $collection = $fetcher->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array('C'), iterator_to_array($collection));
+
+ $collection = $fetcher->fetchMessages();
+ $this->assertFalse($collection);
+
+ $fetcher->queueMessage('D');
+
+ $collection = $fetcher->fetchMessages();
+ $this->assertInstanceOf(MessageCollection::class, $collection);
+ $this->assertSame(array('D'), iterator_to_array($collection));
+
+ $collection = $fetcher->fetchMessages();
+ $this->assertFalse($collection);
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/Router/DirectRouterTest.php b/src/Symfony/Component/Worker/Tests/Router/DirectRouterTest.php
new file mode 100644
index 0000000000000..2cab5db24ebac
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/Router/DirectRouterTest.php
@@ -0,0 +1,36 @@
+createMock(MessageFetcherInterface::class);
+ $messageFetcher->expects($this->once())->method('fetchMessages');
+ $consumer = $this->createMock(ConsumerInterface::class);
+
+ $router = new DirectRouter($messageFetcher, $consumer);
+
+ $router->fetchMessages();
+ }
+
+ public function testConsume()
+ {
+ $messageCollection = new MessageCollection();
+
+ $messageFetcher = $this->createMock(MessageFetcherInterface::class);
+ $consumer = $this->createMock(ConsumerInterface::class);
+ $consumer->expects($this->once())->method('consume')->with($messageCollection);
+
+ $router = new DirectRouter($messageFetcher, $consumer);
+
+ $router->consume($messageCollection);
+ }
+}
diff --git a/src/Symfony/Component/Worker/Tests/Router/RoundRobinRouterTest.php b/src/Symfony/Component/Worker/Tests/Router/RoundRobinRouterTest.php
new file mode 100644
index 0000000000000..105a59a0242f3
--- /dev/null
+++ b/src/Symfony/Component/Worker/Tests/Router/RoundRobinRouterTest.php
@@ -0,0 +1,137 @@
+fetchMessages();
+ $this->assertSame(array('A'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('A'), $consumer1->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('B'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('A', 'B'), $consumer1->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('D'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('D'), $consumer2->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('E'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('D', 'E'), $consumer2->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(false, $messageCollection);
+ }
+
+ public function testConsumeEverythingInSequence()
+ {
+ $fetcher1 = new InMemoryMessageFetcher(array('A', false, 'B'));
+ $consumer1 = new ConsumerMock();
+ $router1 = new DirectRouter($fetcher1, $consumer1);
+
+ $fetcher2 = new InMemoryMessageFetcher(array('D', false, 'E'));
+ $consumer2 = new ConsumerMock();
+ $router2 = new DirectRouter($fetcher2, $consumer2);
+
+ $router = new RoundRobinRouter(array($router1, $router2), true);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('A'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('A'), $consumer1->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('D'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('D'), $consumer2->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('B'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('A', 'B'), $consumer1->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('E'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('D', 'E'), $consumer2->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(false, $messageCollection);
+ }
+
+ public function testConsumeInSequence()
+ {
+ $fetcher1 = new InMemoryMessageFetcher(array('A', false, 'B'));
+ $consumer1 = new ConsumerMock();
+ $router1 = new DirectRouter($fetcher1, $consumer1);
+
+ $fetcher2 = new InMemoryMessageFetcher(array('D', false, 'E'));
+ $consumer2 = new ConsumerMock();
+ $router2 = new DirectRouter($fetcher2, $consumer2);
+
+ $router = new RoundRobinRouter(array($router1, $router2), false);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('A'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('A'), $consumer1->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('D'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('D'), $consumer2->messages);
+
+ // Both message fetch return false
+ $messageCollection = $router->fetchMessages();
+ $this->assertFalse($messageCollection);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('B'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('A', 'B'), $consumer1->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(array('E'), iterator_to_array($messageCollection));
+ $router->consume($messageCollection);
+ $this->assertSame(array('D', 'E'), $consumer2->messages);
+
+ $messageCollection = $router->fetchMessages();
+ $this->assertSame(false, $messageCollection);
+ }
+}
+
+class ConsumerMock implements ConsumerInterface
+{
+ public $messages = array();
+
+ public function consume(MessageCollection $messageCollection)
+ {
+ foreach ($messageCollection as $message) {
+ $this->messages[] = $message;
+ }
+ }
+}
diff --git a/src/Symfony/Component/Worker/composer.json b/src/Symfony/Component/Worker/composer.json
new file mode 100644
index 0000000000000..1dc954cde387a
--- /dev/null
+++ b/src/Symfony/Component/Worker/composer.json
@@ -0,0 +1,41 @@
+{
+ "name": "symfony/worker",
+ "type": "library",
+ "description": "Library to build workers",
+ "keywords": ["worker", "consumer", "queue", "message", "amqp"],
+ "homepage": "http://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Grégoire Pineau",
+ "email": "lyrixx@lyrixx.info"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.5.9",
+ "ext-pcntl": "*",
+ "symfony/event-dispatcher": "^2.3|^3.0|^4.0",
+ "psr/log": "~1.0"
+ },
+ "require-dev": {
+ "symfony/amqp": "^3.4",
+ "symfony/console": "^3.0",
+ "symfony/phpunit-bridge": "^3.2"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Worker\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.4-dev"
+ }
+ }
+}
diff --git a/src/Symfony/Component/Worker/phpunit.xml.dist b/src/Symfony/Component/Worker/phpunit.xml.dist
new file mode 100644
index 0000000000000..395cd60135fd8
--- /dev/null
+++ b/src/Symfony/Component/Worker/phpunit.xml.dist
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+ ./Tests/
+
+
+
+
+
+ ./
+
+ ./Tests
+ ./vendor
+
+
+
+
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy