REQUEST_TIME is stale during test runs
I've been writing tests to work against Schedule module, which uses REQUEST_TIME constant in its query during cron. My tests kept failing until I replaced REQUEST_TIME with time().
I am manually creating the node through steps, which a custom step to populate the input fields a second ahead of the current time.
After I save the node, I run cron through DrupaExtension and Schedule never fires because of the stale REQUEST_TIME.
Feature samples
Scenario: As a site editor, I can schedule news to be published.
Given I am logged in as a user with the "Site Editor" role
And I am on "/node/add/news"
When I fill in "title" with "Scheduled Behat News Announcement!"
And I fill in "edit-body-und-0-value" with "We have the tests!"
# @note: No need to click, because only option in vertical tabs
# Then I click "Scheduling options"
And I schedule the news for the future
Then I press "edit-submit"
And I should see "Scheduled Behat News Announcement! has been created."
And I should see an ".node-unpublished" element
When I wait "1" second
And I run cron
Then I reload the page
And I should not see an ".node-unpublished" element
Here is code from scheduler.module, too, which is failing due to constant being incorrect.
// If the time now is greater than the time to publish a node, publish it.
// The INNER join on 'node' and 'users' is just to ensure the nodes are valid.
$query = db_select('scheduler', 's');
$query->addField('s', 'nid');
$query->addJoin('INNER', 'node', 'n', 's.nid = n.nid');
$query->addJoin('INNER', 'users', 'u', 'u.uid = n.uid');
$query->condition('s.publish_on', 0, '>');
$query->condition('s.publish_on', REQUEST_TIME, '<=');
$query_result = $query->execute();
$nids = array();
while ($node = $query_result->fetchObject()) {
$nids[] = $node->nid;
}
Hmm, I can see that being problematic. Any code run natively (meaning not through a new bootstrap via the UI) will have the REQUEST_TIME from when the code is first bootstrapped. One possible workaround for this particular issue would be to run cron through the UI (eg, log in as an admin, and visit the cron link), which would refresh the REQUEST_TIME constant.
@mglaman, I'm one of the co-maintainers of the Scheduler module. We have the exact same problem in Simpletest. It is common to all Drupal web testing frameworks to work with a "test runner" thread which has Drupal bootstrapped so the Drupal API can be used to do some quick actions like running cron. Unfortunately this means that some things such as the REQUEST_TIME constant will be set at the time the test started, which may be many minutes earlier.
In Scheduler we cannot change this constant to time() because it would go against Drupal best practices. We should consider the request time as the exact time all of our actions are based on, since some time might pass between the request is initiated and our hook is actually called. We should also not change production code to satisfy a test case, it's rather the test case that should be adapted.
The simplest solution is to not rely on the test runner to run the cron hook, but to call the "lightweight cron" which is built in Scheduler. This would be executed in a separate thread, with a freshly set REQUEST_TIME constant.
Simply replace this line:
And I run cron
with this line:
And I visit "scheduler/cron"
@jhedstrom I'm afraid we cannot tamper with any of the PHP constants at runtime. That would result in a fatal error. When BrowserTestBase was added to Drupal 8 they had similar problems, which resulted in Issue #2459155: Remove REQUEST_TIME from bootstrap.php.
In that issue the usage of REQUEST_TIME was removed from the "test bootstrap" (not the actual Drupal bootstrap). In Drupal 8 it is now required to rely on $_SERVER['REQUEST_TIME'] in test code, and in production code the request time should be retrieved from the request object:
$request_time = $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME');
This solves the problem both on the testing and production side. Unfortunately we don't have these mechanisms in Drupal 7 and earlier and we will have to rely on workarounds.