1<?php
2
3namespace Drupal\Tests\system\Functional\System;
4
5use Drupal\Tests\BrowserTestBase;
6use Drupal\Tests\Traits\Core\CronRunTrait;
7
8/**
9 * Tests cron runs.
10 *
11 * @group system
12 */
13class CronRunTest extends BrowserTestBase {
14
15  use CronRunTrait;
16
17  /**
18   * Modules to enable.
19   *
20   * @var array
21   */
22  protected static $modules = [
23    'common_test',
24    'common_test_cron_helper',
25    'automated_cron',
26  ];
27
28  /**
29   * {@inheritdoc}
30   */
31  protected $defaultTheme = 'stark';
32
33  /**
34   * Tests cron runs.
35   */
36  public function testCronRun() {
37    // Run cron anonymously without any cron key.
38    $this->drupalGet('cron');
39    $this->assertSession()->statusCodeEquals(404);
40
41    // Run cron anonymously with a random cron key.
42    $key = $this->randomMachineName(16);
43    $this->drupalGet('cron/' . $key);
44    $this->assertSession()->statusCodeEquals(403);
45
46    // Run cron anonymously with the valid cron key.
47    $key = \Drupal::state()->get('system.cron_key');
48    $this->drupalGet('cron/' . $key);
49    $this->assertSession()->statusCodeEquals(204);
50  }
51
52  /**
53   * Ensure that the automated cron run module is working.
54   *
55   * In these tests we do not use REQUEST_TIME to track start time, because we
56   * need the exact time when cron is triggered.
57   */
58  public function testAutomatedCron() {
59    // Test with a logged in user; anonymous users likely don't cause Drupal to
60    // fully bootstrap, because of the internal page cache or an external
61    // reverse proxy. Reuse this user for disabling cron later in the test.
62    $admin_user = $this->drupalCreateUser(['administer site configuration']);
63    $this->drupalLogin($admin_user);
64
65    // Ensure cron does not run when a non-zero cron interval is specified and
66    // was not passed.
67    $cron_last = time();
68    $cron_safe_interval = 100;
69    \Drupal::state()->set('system.cron_last', $cron_last);
70    $this->config('automated_cron.settings')
71      ->set('interval', $cron_safe_interval)
72      ->save();
73    $this->drupalGet('');
74    $this->assertSame($cron_last, \Drupal::state()->get('system.cron_last'), 'Cron does not run when the cron interval is not passed.');
75
76    // Test if cron runs when the cron interval was passed.
77    $cron_last = time() - 200;
78    \Drupal::state()->set('system.cron_last', $cron_last);
79    $this->drupalGet('');
80    sleep(1);
81    // Verify that cron runs when the cron interval has passed.
82    $this->assertLessThan(\Drupal::state()->get('system.cron_last'), $cron_last);
83
84    // Disable cron through the interface by setting the interval to zero.
85    $this->drupalGet('admin/config/system/cron');
86    $this->submitForm(['interval' => 0], 'Save configuration');
87    $this->assertSession()->pageTextContains('The configuration options have been saved.');
88    $this->drupalLogout();
89
90    // Test if cron does not run when the cron interval is set to zero.
91    $cron_last = time() - 200;
92    \Drupal::state()->set('system.cron_last', $cron_last);
93    $this->drupalGet('');
94    $this->assertSame($cron_last, \Drupal::state()->get('system.cron_last'), 'Cron does not run when the cron threshold is disabled.');
95  }
96
97  /**
98   * Make sure exceptions thrown on hook_cron() don't affect other modules.
99   */
100  public function testCronExceptions() {
101    \Drupal::state()->delete('common_test.cron');
102    // The common_test module throws an exception. If it isn't caught, the tests
103    // won't finish successfully.
104    // The common_test_cron_helper module sets the 'common_test_cron' variable.
105    $this->cronRun();
106    $result = \Drupal::state()->get('common_test.cron');
107    $this->assertEquals('success', $result, 'Cron correctly handles exceptions thrown during hook_cron() invocations.');
108  }
109
110  /**
111   * Make sure the cron UI reads from the state storage.
112   */
113  public function testCronUI() {
114    $admin_user = $this->drupalCreateUser(['administer site configuration']);
115    $this->drupalLogin($admin_user);
116    $this->drupalGet('admin/config/system/cron');
117    // Don't use REQUEST to calculate the exact time, because that will
118    // fail randomly. Look for the word 'years', because without a timestamp,
119    // the time will start at 1 January 1970.
120    $this->assertSession()->pageTextNotContains('years');
121
122    $cron_last = time() - 200;
123    \Drupal::state()->set('system.cron_last', $cron_last);
124
125    $this->submitForm([], 'Save configuration');
126    $this->assertSession()->pageTextContains('The configuration options have been saved.');
127    $this->assertSession()->addressEquals('admin/config/system/cron');
128
129    // Check that cron does not run when saving the configuration form.
130    $this->assertEquals($cron_last, \Drupal::state()->get('system.cron_last'), 'Cron does not run when saving the configuration form.');
131
132    // Check that cron runs when triggered manually.
133    $this->submitForm([], 'Run cron');
134    // Verify that cron runs when triggered manually.
135    $this->assertLessThan(\Drupal::state()->get('system.cron_last'), $cron_last);
136  }
137
138  /**
139   * Ensure that the manual cron run is working.
140   */
141  public function testManualCron() {
142    $admin_user = $this->drupalCreateUser(['administer site configuration']);
143    $this->drupalLogin($admin_user);
144
145    $this->drupalGet('admin/reports/status/run-cron');
146    $this->assertSession()->statusCodeEquals(403);
147
148    $this->drupalGet('admin/reports/status');
149    $this->clickLink('Run cron');
150    $this->assertSession()->statusCodeEquals(200);
151    $this->assertSession()->pageTextContains('Cron ran successfully.');
152  }
153
154}
155