function CronSuspendQueueDelayTest::testSuspendQueueOrder

Same name and namespace in other branches
  1. 11.x core/tests/Drupal/Tests/Core/Cron/CronSuspendQueueDelayTest.php \Drupal\Tests\Core\Cron\CronSuspendQueueDelayTest::testSuspendQueueOrder()

Tests queues are executed in order.

If multiple queues are delayed, they must execute in order of time.

File

core/tests/Drupal/Tests/Core/Cron/CronSuspendQueueDelayTest.php, line 308

Class

CronSuspendQueueDelayTest
Test Cron handling of suspended queues with a delay.

Namespace

Drupal\Tests\Core\Cron

Code

public function testSuspendQueueOrder() : void {
  [
    'queue_factory' => $queueFactory,
    'queue_manager' => $queueManager,
    'time' => $time,
  ] = $this->cronConstructorArguments;
  $cron = $this->getMockBuilder(Cron::class)
    ->onlyMethods([
    'usleep',
  ])
    ->setConstructorArgs($this->cronConstructorArguments)
    ->getMock();
  $cron->expects($this->any())
    ->method('usleep');
  $queueManager->expects($this->once())
    ->method('getDefinitions')
    ->willReturn([
    'test_worker_a' => [
      'id' => 'test_worker_a',
      'cron' => [
        'time' => 300,
      ],
    ],
    'test_worker_b' => [
      'id' => 'test_worker_b',
      'cron' => [
        'time' => 300,
      ],
    ],
    'test_worker_c' => [
      'id' => 'test_worker_c',
      'cron' => [
        'time' => 300,
      ],
    ],
    'test_worker_d' => [
      'id' => 'test_worker_d',
      'cron' => [
        'time' => 300,
      ],
    ],
  ]);
  $queueA = $this->createMock(QueueInterface::class);
  $queueB = $this->createMock(QueueInterface::class);
  $queueC = $this->createMock(QueueInterface::class);
  $queueD = $this->createMock(QueueInterface::class);
  $queueFactory->expects($this->exactly(4))
    ->method('get')
    ->willReturnMap([
    [
      'test_worker_a',
      FALSE,
      $queueA,
    ],
    [
      'test_worker_b',
      FALSE,
      $queueB,
    ],
    [
      'test_worker_c',
      FALSE,
      $queueC,
    ],
    [
      'test_worker_d',
      FALSE,
      $queueD,
    ],
  ]);
  $queueA->expects($this->any())
    ->method('claimItem')
    ->willReturnOnConsecutiveCalls((object) [
    'data' => 'test_data_from_queue_a',
  ], FALSE);
  $queueB->expects($this->any())
    ->method('claimItem')
    ->willReturnOnConsecutiveCalls((object) [
    'data' => 'test_data_from_queue_b',
  ], (object) [
    'data' => 'test_data_from_queue_b',
  ], FALSE);
  $queueC->expects($this->any())
    ->method('claimItem')
    ->willReturnOnConsecutiveCalls((object) [
    'data' => 'test_data_from_queue_c',
  ], (object) [
    'data' => 'test_data_from_queue_c',
  ], FALSE);
  $queueD->expects($this->any())
    ->method('claimItem')
    ->willReturnOnConsecutiveCalls((object) [
    'data' => 'test_data_from_queue_d',
  ], FALSE);
  // Recycle the same worker for all queues to test order sanely:
  $queueManager->expects($this->any())
    ->method('createInstance')
    ->willReturnMap([
    [
      'test_worker_a',
      [],
      $this->workerA,
    ],
    [
      'test_worker_b',
      [],
      $this->workerA,
    ],
    [
      'test_worker_c',
      [],
      $this->workerA,
    ],
    [
      'test_worker_d',
      [],
      $this->workerA,
    ],
  ]);
  $queues = [
    // All queues are executed in sequence of definition:
'test_data_from_queue_a',
    'test_data_from_queue_b',
    'test_data_from_queue_c',
    'test_data_from_queue_d',
    // Queue C is executed again, and before queue B.
'test_data_from_queue_c',
    // Queue B is executed again, after queue C since its delay was longer.
'test_data_from_queue_b',
  ];
  $this->workerA
    ->expects($this->exactly(count($queues)))
    ->method('processItem')
    ->with($this->callback(function ($queue) use (&$queues) : bool {
    return array_shift($queues) === $queue;
  }))
    ->willReturnOnConsecutiveCalls(NULL, $this->throwException(new SuspendQueueException('', 0, NULL, 16.0)), $this->throwException(new SuspendQueueException('', 0, NULL, 8.0)), NULL, NULL, NULL);
  $currentTime = 60;
  $time->expects($this->any())
    ->method('getCurrentTime')
    ->willReturnCallback(function () use (&$currentTime) : int {
    return (int) $currentTime;
  });
  $time->expects($this->any())
    ->method('getCurrentMicroTime')
    ->willReturnCallback(function () use (&$currentTime) : float {
    return (double) $currentTime;
  });
  $delays = [
    // Expect to wait for 8 seconds, then accelerate time by 4 seconds.
4,
    8000000,
    // SuspendQueueException requests to delay by 16 seconds, but 4 seconds
    // have passed above, so there are just 12 seconds remaining:
0,
    12000000,
  ];
  $cron->expects($this->exactly(count($delays) / 2))
    ->method('usleep')
    ->with($this->callback(function (int $delay) use (&$currentTime, &$delays) : bool {
    $currentTime += array_shift($delays);
    return array_shift($delays) === $delay;
  }));
  $cron->run();
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.