MDL-83316 Behat: New step allows system clock change

This commit is contained in:
sam marshall 2024-10-01 09:53:45 +01:00 committed by Simey Lameze
parent acff633e39
commit d4564b876e
5 changed files with 159 additions and 3 deletions

View File

@ -0,0 +1,61 @@
@tool @tool_behat
Feature: Frozen clock in Behat
In order to write tests that depend on the current system time
As a test writer
I need to set the time using a Behat step
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "activities" exist:
| activity | course | name | idnumber | externalurl |
| url | C1 | Fixture | url1 | #wwwroot#/admin/tool/behat/tests/fixtures/core/showtime.php |
| forum | C1 | TestForum | forum1 | |
Scenario: Time has been frozen
# Set up 2 forum discussions at different times. This tests the clock in the Behat CLI process.
Given the time is frozen at "2024-03-01 12:34:56"
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Subject1 | Message1 |
And the time is frozen at "2024-08-01 12:34:56"
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Subject2 | Message2 |
When I am on the "TestForum" "forum activity" page logged in as admin
Then I should see "1 Mar 2024" in the "Subject1" "table_row"
And I should see "1 Aug 2024" in the "Subject2" "table_row"
# Also view time on the fixture page. This tests the clock for Behat web server requests.
And I am on the "Fixture" "url activity" page
And I should see "Behat time is not the same as real time"
# This Unix time corresponds to 12:34:56 in Perth time zone.
And I should see "Unix time 1722486896"
And I should see "Date-time 2024-08-01 12:34:56"
# This scenario is second, to verify that the clock automatically goes back to normal after test.
Scenario: Time is normal
Given the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Subject1 | Message1 |
When I am on the "TestForum" "forum activity" page logged in as admin
# The time should be the real current time, not the frozen time.
Then I should see "## today ##%d %b %Y##" in the "Subject1" "table_row"
And I am on the "Fixture" "url activity" page
And I should see "Behat time is the same as real time"
Scenario: Time is frozen and then unfrozen
Given the time is frozen at "2024-03-01 12:34:56"
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Subject1 | Message1 |
And the time is no longer frozen
And the following "mod_forum > discussions" exist:
| user | forum | name | message |
| admin | forum1 | Subject2 | Message2 |
When I am on the "TestForum" "forum activity" page logged in as admin
Then I should see "1 Mar 2024" in the "Subject1" "table_row"
# The time should be the real current time, not the frozen time for this entry.
Then I should see "## today ##%d %b %Y##" in the "Subject2" "table_row"
And I am on the "Fixture" "url activity" page
And I should see "Behat time is the same as real time"

View File

@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Fixture to show the current server time using \core\clock.
*
* @package tool_behat
* @copyright 2024 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// phpcs:disable moodle.Files.RequireLogin.Missing
require(__DIR__ . '/../../../../../../config.php');
defined('BEHAT_SITE_RUNNING') || die('Behat fixture');
$PAGE->set_context(\context_system::instance());
$PAGE->set_url(new \moodle_url('/admin/tool/behat/tests/fixtures/core/showtime.php'));
echo $OUTPUT->header();
$clock = \core\di::get(\core\clock::class);
$dt = $clock->now();
$realbefore = time();
$time = $clock->time();
$realafter = time();
echo html_writer::div('Unix time ' . $time);
echo html_writer::div('Date-time ' . $dt->format('Y-m-d H:i:s'));
echo html_writer::div('TZ ' . $dt->getTimezone()->getName());
if ($time >= $realbefore && $time <= $realafter) {
echo html_writer::div('Behat time is the same as real time');
} else {
echo html_writer::div('Behat time is not the same as real time');
}
echo $OUTPUT->footer();

View File

@ -120,7 +120,16 @@ class di {
// The Moodle Clock implementation, which itself is an extension of PSR-20.
// Alias the PSR-20 clock interface to the Moodle clock. They are compatible.
\core\clock::class => fn() => new \core\system_clock(),
\core\clock::class => function () {
global $CFG;
// Web requests to the Behat site can use a frozen clock if configured.
if (defined('BEHAT_SITE_RUNNING') && !empty($CFG->behat_frozen_clock)) {
require_once($CFG->libdir . '/testing/classes/frozen_clock.php');
return new \frozen_clock((int)$CFG->behat_frozen_clock);
}
return new \core\system_clock();
},
\Psr\Clock\ClockInterface::class => \DI\get(\core\clock::class),
// Note: libphonenumber PhoneNumberUtil uses a singleton.

View File

@ -35,7 +35,10 @@ class frozen_clock implements \core\clock {
?int $time = null,
) {
if ($time) {
$this->time = new \DateTimeImmutable("@{$time}");
// Note that the constructor with time zone does not work when specifying a timestamp,
// so we have to set timezone separately afterward.
$this->time = (new \DateTimeImmutable("@{$time}"))
->setTimezone(\core_date::get_server_timezone_object());
} else {
$this->time = new \DateTimeImmutable();
}
@ -55,7 +58,8 @@ class frozen_clock implements \core\clock {
* @param int $time
*/
public function set_to(int $time): void {
$this->time = new \DateTimeImmutable("@{$time}");
$this->time = (new \DateTimeImmutable("@{$time}"))
->setTimezone(\core_date::get_server_timezone_object());
}
/**

View File

@ -2708,4 +2708,34 @@ EOF;
throw new Exception("Text '{$text}' found in the row containing '{$rowtext}'");
}
}
/**
* Sets the current time for the remainder of this Behat test.
*
* This is not supported everywhere in Moodle: if code uses \core\clock through DI then
* it will work, but if it just calls time() it will still get the real time.
*
* @Given the time is frozen at :datetime
* @param string $datetime Date and time in a format that strtotime understands
*/
public function the_time_is_frozen_at(string $datetime): void {
global $CFG;
require_once($CFG->libdir . '/testing/classes/frozen_clock.php');
$timestamp = strtotime($datetime);
// The config variable is used to set up a frozen clock in each Behat web request.
set_config('behat_frozen_clock', $timestamp);
// Simply setting a frozen clock in DI should work for future steps in Behat CLI process.
\core\di::set(\core\clock::class, new \frozen_clock($timestamp));
}
/**
* Stops freezing time so that it goes back to real time.
*
* @Given the time is no longer frozen
*/
public function the_time_is_no_longer_frozen(): void {
unset_config('behat_frozen_clock');
\core\di::set(\core\clock::class, new \core\system_clock());
}
}