You are here

protected function CronTest::setUp in Drupal 9

Overrides UnitTestCase::setUp

File

core/tests/Drupal/Tests/Core/CronTest.php, line 59

Class

CronTest
Tests the Cron class.

Namespace

Drupal\Tests\Core

Code

protected function setUp() : void {
  parent::setUp();

  // Construct a state object used for testing logger assertions.
  $this->state = new State(new KeyValueMemoryFactory());

  // Create a mock logger to set a flag in the resulting state.
  $logger = $this
    ->prophesize('Drupal\\Core\\Logger\\LoggerChannelInterface');

  // Safely ignore the cron re-run message when failing to acquire a lock.
  //
  // We don't need to run regular cron tasks, and we're still implicitly
  // testing that queues are being processed.
  //
  // This argument will need to be updated to match the message text in
  // Drupal\Core\Cron::run() should the original text ever be updated.
  $logger
    ->warning(Argument::exact('Attempting to re-run cron while it is already running.'))
    ->shouldBeCalled();

  // Set a flag to track when a message is logged by adding a callback
  // function for each logging method.
  foreach (get_class_methods(LoggerInterface::class) as $logger_method) {
    $logger
      ->{$logger_method}(Argument::cetera())
      ->will(function () {
      \Drupal::state()
        ->set('cron_test.message_logged', TRUE);
    });
  }

  // Create a logger factory to produce the resulting logger.
  $logger_factory = $this
    ->prophesize('Drupal\\Core\\Logger\\LoggerChannelFactoryInterface');
  $logger_factory
    ->get(Argument::exact('cron'))
    ->willReturn($logger
    ->reveal());

  // Create a mock time service.
  $time = $this
    ->prophesize('Drupal\\Component\\Datetime\\TimeInterface');

  // Build the container using the resulting mock objects.
  \Drupal::setContainer(new ContainerBuilder());
  \Drupal::getContainer()
    ->set('logger.factory', $logger_factory
    ->reveal());
  \Drupal::getContainer()
    ->set('datetime.time', $time
    ->reveal());
  \Drupal::getContainer()
    ->set('state', $this->state);

  // Create mock objects for constructing the Cron class.
  $module_handler = $this
    ->prophesize('Drupal\\Core\\Extension\\ModuleHandlerInterface');
  $queue_factory = $this
    ->prophesize('Drupal\\Core\\Queue\\QueueFactory');
  $queue_worker_manager = $this
    ->prophesize('Drupal\\Core\\Queue\\QueueWorkerManagerInterface');
  $state = $this
    ->prophesize('Drupal\\Core\\State\\StateInterface');
  $account_switcher = $this
    ->prophesize('Drupal\\Core\\Session\\AccountSwitcherInterface');

  // Create a lock that will always fail when attempting to acquire; we're
  // only interested in testing ::processQueues(), not the other stuff.
  $lock_backend = $this
    ->prophesize('Drupal\\Core\\Lock\\LockBackendInterface');
  $lock_backend
    ->acquire(Argument::exact('cron'), Argument::cetera())
    ->willReturn(FALSE);

  // Create a queue worker definition for testing purposes.
  $queue_worker = $this
    ->randomMachineName();
  $queue_worker_definition = [
    'id' => $queue_worker,
    'cron' => [
      'time' => &$this->claimTime,
    ],
  ];

  // Create a queue instance for this queue worker.
  $this->queue = new Memory($queue_worker);
  $queue_factory
    ->get($queue_worker)
    ->willReturn($this->queue);

  // Create a mock queue worker plugin instance based on above definition.
  $queue_worker_plugin = $this
    ->prophesize('Drupal\\Core\\Queue\\QueueWorkerInterface');
  $queue_worker_plugin
    ->processItem('Complete')
    ->willReturn();
  $queue_worker_plugin
    ->processItem('Exception')
    ->willThrow(\Exception::class);
  $queue_worker_plugin
    ->processItem('DelayedRequeueException')
    ->willThrow(DelayedRequeueException::class);
  $queue_worker_plugin
    ->processItem('SuspendQueueException')
    ->willThrow(SuspendQueueException::class);

  // 'RequeueException' would normally result in an infinite loop.
  //
  // This is avoided by throwing RequeueException for the first few calls to
  // ::processItem() and then returning void. ::testRequeueException()
  // establishes sanity assertions for this case.
  $queue_worker_plugin
    ->processItem('RequeueException')
    ->will(function ($args, $mock, $method) {

    // Fetch the number of calls to this prophesied method. This value will
    // start at zero during the first call.
    $method_calls = count($mock
      ->findProphecyMethodCalls($method
      ->getMethodName(), new ArgumentsWildcard($args)));

    // Throw the expected exception on the first few calls.
    if ($method_calls < self::REQUEUE_COUNT) {
      \Drupal::state()
        ->set('cron_test.requeue_count', $method_calls + 1);
      throw new RequeueException();
    }
  });

  // Set the mock queue worker manager to return the definition/plugin.
  $queue_worker_manager
    ->getDefinitions()
    ->willReturn([
    $queue_worker => $queue_worker_definition,
  ]);
  $queue_worker_manager
    ->createInstance($queue_worker)
    ->willReturn($queue_worker_plugin
    ->reveal());

  // Construct the Cron class to test.
  $this->cron = new Cron($module_handler
    ->reveal(), $lock_backend
    ->reveal(), $queue_factory
    ->reveal(), $state
    ->reveal(), $account_switcher
    ->reveal(), $logger
    ->reveal(), $queue_worker_manager
    ->reveal(), $time
    ->reveal());
}