MDL-38190 (1) Backup/restore: Progress tracking API

This commit is contained in:
sam marshall
2013-08-16 10:50:35 +01:00
parent 838d78a9ff
commit 16cd708835
12 changed files with 800 additions and 0 deletions
@@ -64,6 +64,11 @@ class backup_controller extends backup implements loggable {
protected $destination; // Destination chain object (fs_moodle, fs_os, db, email...)
protected $logger; // Logging chain object (moodle, inline, fs, db, syslog)
/**
* @var core_backup_progress Progress reporting object.
*/
protected $progress;
protected $checksum; // Cache @checksumable results for lighter @is_checksum_correct() uses
/**
@@ -109,6 +114,10 @@ class backup_controller extends backup implements loggable {
// Default logger chain (based on interactive/execution)
$this->logger = backup_factory::get_logger_chain($this->interactive, $this->execution, $this->backupid);
// By default there is no progress reporter. Interfaces that wish to
// display progress must set it.
$this->progress = new core_backup_null_progress();
// Instantiate the output_controller singleton and active it if interactive and inmediate
$oc = output_controller::get_instance();
if ($this->interactive == backup::INTERACTIVE_YES && $this->execution == backup::EXECUTION_INMEDIATE) {
@@ -302,6 +311,25 @@ class backup_controller extends backup implements loggable {
return $this->logger;
}
/**
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
*/
public function get_progress() {
return $this->progress;
}
/**
* Sets the progress reporter.
*
* @param core_backup_progress $progress Progress reporting object
*/
public function set_progress(core_backup_progress $progress) {
$this->progress = $progress;
}
/**
* Executes the backup
* @return void Throws and exception of completes
@@ -57,6 +57,11 @@ class restore_controller extends backup implements loggable {
protected $logger; // Logging chain object (moodle, inline, fs, db, syslog)
/**
* @var core_backup_progress Progress reporting object.
*/
protected $progress;
protected $checksum; // Cache @checksumable results for lighter @is_checksum_correct() uses
/**
@@ -101,6 +106,10 @@ class restore_controller extends backup implements loggable {
// Default logger chain (based on interactive/execution)
$this->logger = backup_factory::get_logger_chain($this->interactive, $this->execution, $this->restoreid);
// By default there is no progress reporter. Interfaces that wish to
// display progress must set it.
$this->progress = new core_backup_null_progress();
// Instantiate the output_controller singleton and active it if interactive and inmediate
$oc = output_controller::get_instance();
if ($this->interactive == backup::INTERACTIVE_YES && $this->execution == backup::EXECUTION_INMEDIATE) {
@@ -300,6 +309,25 @@ class restore_controller extends backup implements loggable {
return $this->logger;
}
/**
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
*/
public function get_progress() {
return $this->progress;
}
/**
* Sets the progress reporter.
*
* @param core_backup_progress $progress Progress reporting object
*/
public function set_progress(core_backup_progress $progress) {
$this->progress = $progress;
}
public function execute_plan() {
// Basic/initial prevention against time/memory limits
set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+2
View File
@@ -71,6 +71,8 @@ require_once($CFG->dirroot . '/backup/util/loggers/error_log_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
require_once($CFG->dirroot . '/backup/util/settings/setting_dependency.class.php');
require_once($CFG->dirroot . '/backup/util/settings/base_setting.class.php');
require_once($CFG->dirroot . '/backup/util/settings/backup_setting.class.php');
@@ -60,6 +60,8 @@ require_once($CFG->dirroot . '/backup/util/loggers/error_log_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
require_once($CFG->dirroot . '/backup/util/factories/backup_factory.class.php');
require_once($CFG->dirroot . '/backup/util/factories/restore_factory.class.php');
require_once($CFG->dirroot . '/backup/util/helper/backup_helper.class.php');
+10
View File
@@ -87,6 +87,16 @@ class backup_plan extends base_plan implements loggable {
return $this->controller->get_logger();
}
/**
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
*/
public function get_progress() {
return $this->controller->get_progress();
}
public function is_excluding_activities() {
return $this->excludingdactivities;
}
+8
View File
@@ -164,6 +164,14 @@ abstract class base_plan implements checksumable, executable {
}
}
/**
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
*/
public abstract function get_progress();
/**
* Destroy all circular references. It helps PHP 5.2 a lot!
*/
+10
View File
@@ -111,6 +111,16 @@ abstract class base_task implements checksumable, executable, loggable {
return $this->plan->get_logger();
}
/**
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
*/
public function get_progress() {
return $this->plan->get_progress();
}
public function log($message, $level, $a = null, $depth = null, $display = false) {
backup_helper::log($message, $level, $a, $depth, $display, $this->get_logger());
}
+10
View File
@@ -94,6 +94,16 @@ class restore_plan extends base_plan implements loggable {
return $this->controller->get_logger();
}
/**
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
*/
public function get_progress() {
return $this->controller->get_progress();
}
public function get_info() {
return $this->controller->get_info();
}
+4
View File
@@ -34,6 +34,10 @@ require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
class mock_base_plan extends base_plan {
public function build() {
}
public function get_progress() {
return null;
}
}
/**
@@ -0,0 +1,28 @@
<?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/>.
/**
* Progress handler that ignores progress entirely.
*
* @package core_backup
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_backup_null_progress extends core_backup_progress {
public function update_progress() {
// Do nothing.
}
}
@@ -0,0 +1,307 @@
<?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/>.
/**
* Base class for handling progress information during a backup and restore.
*
* Subclasses should generally override the current_progress function which
* summarises all progress information.
*
* @package core_backup
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class core_backup_progress {
/**
* @var int Constant indicating that the number of progress calls is unknown.
*/
const INDETERMINATE = -1;
/**
* @var int The number of seconds that can pass without progress() calls.
*/
const TIME_LIMIT_WITHOUT_PROGRESS = 120;
/**
* @var int Time of last progress call.
*/
protected $lastprogresstime;
/**
* @var int Number of progress calls (restricted to ~ 1/second).
*/
protected $count;
/**
* @var array Array of progress descriptions for each stack level.
*/
protected $descriptions = array();
/**
* @var array Array of maximum progress values for each stack level.
*/
protected $maxes = array();
/**
* @var array Array of current progress values.
*/
protected $currents = array();
/**
* @var int Array of counts within parent progress entry (ignored for first)
*/
protected $parentcounts = array();
/**
* Marks the start of an operation that will display progress.
*
* This can be called multiple times for nested progress sections. It must
* be paired with calls to end_progress.
*
* The progress maximum may be INDETERMINATE if the current operation has
* an unknown number of steps. (This is default.)
*
* Calling this function will always result in a new display, so this
* should not be called exceedingly frequently.
*
* When it is complete by calling end_progress, each start_progress section
* automatically adds progress to its parent, as defined by $parentcount.
*
* @param string $description Description to display
* @param int $max Maximum value of progress for this section
* @param int $parentcount How many progress points this section counts for
* @throws coding_exception If max is invalid
*/
public function start_progress($description, $max = self::INDETERMINATE,
$parentcount = 1) {
if ($max != self::INDETERMINATE && $max <= 0) {
throw new coding_exception(
'start_progress() max value cannot be zero or negative');
}
if ($parentcount < 1) {
throw new coding_exception(
'start_progress() parent progress count must be at least 1');
}
if (!empty($this->descriptions)) {
$prevmax = end($this->maxes);
if ($prevmax !== self::INDETERMINATE) {
$prevcurrent = end($this->currents);
if ($prevcurrent + $parentcount > $prevmax) {
throw new coding_exception(
'start_progress() parent progress would exceed max');
}
}
} else {
if ($parentcount != 1) {
throw new coding_exception(
'start_progress() progress count must be 1 when no parent');
}
}
$this->descriptions[] = $description;
$this->maxes[] = $max;
$this->currents[] = 0;
$this->parentcounts[] = $parentcount;
$this->update_progress();
$lastprogresstime = $this->get_time();
}
/**
* Marks the end of an operation that will display progress.
*
* This must be paired with each start_progress call.
*
* If there is a parent progress section, its progress will be increased
* automatically to reflect the end of the child section.
*
* @throws coding_exception If progress hasn't been started
*/
public function end_progress() {
if (!count($this->descriptions)) {
throw new coding_exception('end_progress() without start_progress()');
}
array_pop($this->descriptions);
array_pop($this->maxes);
array_pop($this->currents);
$parentcount = array_pop($this->parentcounts);
if (!empty($this->descriptions)) {
$lastmax = end($this->maxes);
if ($lastmax != self::INDETERMINATE) {
$lastvalue = end($this->currents);
$this->currents[key($this->currents)] = $lastvalue + $parentcount;
}
}
$this->update_progress();
}
/**
* Indicates that progress has occurred.
*
* The progress value should indicate the total progress so far, from 0
* to the value supplied for $max (inclusive) in start_progress.
*
* You do not need to call this function for every value. It is OK to skip
* values. It is also OK to call this function as often as desired; it
* doesn't do anything if called more than once per second.
*
* It must be INDETERMINATE if start_progress was called with $max set to
* INDETERMINATE. Otherwise it must not be indeterminate.
*
* @param int $progress Progress so far
* @throws coding_exception If progress value is invalid
*/
public function progress($progress = self::INDETERMINATE) {
// Ignore too-frequent progress calls (more than once per second).
$now = $this->get_time();
if ($now === $this->lastprogresstime) {
return;
}
// Check we are inside a progress section.
$max = end($this->maxes);
if ($max === false) {
throw new coding_exception(
'progress() without start_progress');
}
// Check and apply new progress.
if ($progress === self::INDETERMINATE) {
// Indeterminate progress.
if ($max !== self::INDETERMINATE) {
throw new coding_exception(
'progress() INDETERMINATE, expecting value');
}
} else {
// Determinate progress.
$current = end($this->currents);
if ($max === self::INDETERMINATE) {
throw new coding_exception(
'progress() with value, expecting INDETERMINATE');
} else if ($progress < 0 || $progress > $max) {
throw new coding_exception(
'progress() value out of range');
} else if ($progress < $current) {
throw new coding_Exception(
'progress() value may not go backwards');
}
$this->currents[key($this->currents)] = $progress;
}
// Update progress.
$this->count++;
$this->lastprogresstime = $now;
set_time_limit(self::TIME_LIMIT_WITHOUT_PROGRESS);
$this->update_progress();
}
/**
* Gets time (this is provided so that unit tests can override it).
*
* @return int Current system time
*/
protected function get_time() {
return time();
}
/**
* Called whenever new progress should be displayed.
*/
protected abstract function update_progress();
/**
* @return bool True if currently inside a progress section
*/
public function is_in_progress_section() {
return !empty($this->descriptions);
}
/**
* @return string Current progress section description
*/
public function get_current_description() {
$description = end($this->descriptions);
if ($description === false) {
throw new coding_exception('Not inside progress section');
}
return $description;
}
/**
* Obtains current progress in a way suitable for drawing a progress bar.
*
* Progress is returned as a minimum and maximum value. If there is no
* indeterminate progress, these values will be identical. If there is
* intermediate progress, these values can be different. (For example, if
* the top level progress sections is indeterminate, then the values will
* always be 0.0 and 1.0.)
*
* @return array Minimum and maximum possible progress proportions
*/
public function get_progress_proportion_range() {
// If there is no progress underway, we must have finished.
if (empty($this->currents)) {
return array(1.0, 1.0);
}
$count = count($this->currents);
$min = 0.0;
$max = 1.0;
for ($i = 0; $i < $count; $i++) {
// Get max value at that section - if it's indeterminate we can tell
// no more.
$sectionmax = $this->maxes[$i];
if ($sectionmax === self::INDETERMINATE) {
return array($min, $max);
}
// Special case if current value is max (this should only happen
// just before ending a section).
$sectioncurrent = $this->currents[$i];
if ($sectioncurrent === $sectionmax) {
return array($max, $max);
}
// Using the current value at that section, we know we are somewhere
// between 'current' and the next 'current' value which depends on
// the parentcount of the nested section (if any).
$newmin = ($sectioncurrent / $sectionmax) * ($max - $min) + $min;
$nextcurrent = $sectioncurrent + 1;
if ($i + 1 < $count) {
$weight = $this->parentcounts[$i + 1];
$nextcurrent = $sectioncurrent + $weight;
}
$newmax = ($nextcurrent / $sectionmax) * ($max - $min) + $min;
$min = $newmin;
$max = $newmax;
}
// If there was nothing indeterminate, we use the min value as current.
return array($min, $min);
}
/**
* Obtains current indeterminate progress in a way suitable for adding to
* the progress display.
*
* This returns the number of indeterminate calls (at any level) during the
* lifetime of this progress reporter, whether or not there is a current
* indeterminate step. (The number will not be ridiculously high because
* progress calls are limited to one per second.)
*
* @return int Number of indeterminate progress calls
*/
public function get_progress_count() {
return $this->count;
}
}
@@ -0,0 +1,363 @@
<?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/>.
/**
* Unit tests for the progress classes.
*
* @package core_backup
* @category phpunit
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// Include all the needed stuff.
global $CFG;
require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
/**
* Progress tests.
*/
class backup_progress_testcase extends basic_testcase {
/**
* Tests for basic use with simple numeric progress.
*/
public function test_basic() {
$progress = new core_backup_mock_progress();
// Check values of empty progress things.
$this->assertFalse($progress->is_in_progress_section());
// Start progress counting, check basic values and check that update
// gets called.
$progress->start_progress('hello', 10);
$this->assertTrue($progress->was_update_called());
$this->assertTrue($progress->is_in_progress_section());
$this->assertEquals('hello', $progress->get_current_description());
// Check numeric position and indeterminate count.
$this->assert_min_max(0.0, 0.0, $progress);
$this->assertEquals(0, $progress->get_progress_count());
// Make some progress and check that the time limit gets added.
$progress->step_time();
$progress->progress(2);
$this->assertTrue($progress->was_update_called());
$this->assertEquals(120, ini_get('max_execution_time'));
// Check the new value.
$this->assert_min_max(0.2, 0.2, $progress);
// Do another progress run at same time, it should be ignored.
$progress->progress(3);
$this->assertFalse($progress->was_update_called());
$this->assert_min_max(0.2, 0.2, $progress);
// End the section. This should cause an update.
$progress->end_progress();
$this->assertTrue($progress->was_update_called());
// Because there are no sections left open, it thinks we finished.
$this->assert_min_max(1.0, 1.0, $progress);
// There was 1 progress call.
$this->assertEquals(1, $progress->get_progress_count());
// Clear the time limit, otherwise phpunit complains.
set_time_limit(0);
}
/**
* Tests progress that is nested and/or indeterminate.
*/
public function test_nested() {
// Outer progress goes from 0 to 10.
$progress = new core_backup_mock_progress();
$progress->start_progress('hello', 10);
// Get up to 4, check position.
$progress->step_time();
$progress->progress(4);
$this->assert_min_max(0.4, 0.4, $progress);
$this->assertEquals('hello', $progress->get_current_description());
// Now start indeterminate progress.
$progress->start_progress('world');
$this->assert_min_max(0.4, 0.5, $progress);
$this->assertEquals('world', $progress->get_current_description());
// Do some indeterminate progress and count it (once per second).
$progress->step_time();
$progress->progress();
$this->assertEquals(2, $progress->get_progress_count());
$progress->progress();
$this->assertEquals(2, $progress->get_progress_count());
$progress->step_time();
$progress->progress();
$this->assertEquals(3, $progress->get_progress_count());
$this->assert_min_max(0.4, 0.5, $progress);
// Exit the indeterminate section.
$progress->end_progress();
$this->assert_min_max(0.5, 0.5, $progress);
$progress->step_time();
$progress->progress(7);
$this->assert_min_max(0.7, 0.7, $progress);
// Enter a numbered section (this time with a range of 5).
$progress->start_progress('frogs', 5);
$this->assert_min_max(0.7, 0.7, $progress);
$progress->step_time();
$progress->progress(1);
$this->assert_min_max(0.72, 0.72, $progress);
$progress->step_time();
$progress->progress(3);
$this->assert_min_max(0.76, 0.76, $progress);
// Now enter another indeterminate section.
$progress->start_progress('and');
$this->assert_min_max(0.76, 0.78, $progress);
// Make some progress, should increment indeterminate count.
$progress->step_time();
$progress->progress();
$this->assertEquals(7, $progress->get_progress_count());
// Enter numbered section, won't make any difference to values.
$progress->start_progress('zombies', 2);
$progress->step_time();
$progress->progress(1);
$this->assert_min_max(0.76, 0.78, $progress);
$this->assertEquals(8, $progress->get_progress_count());
// Leaving it will make no difference too.
$progress->end_progress();
// Leaving the indeterminate section will though.
$progress->end_progress();
$this->assert_min_max(0.78, 0.78, $progress);
// Leave the two numbered sections.
$progress->end_progress();
$this->assert_min_max(0.8, 0.8, $progress);
$progress->end_progress();
$this->assertFalse($progress->is_in_progress_section());
set_time_limit(0);
}
/**
* Tests the feature for 'weighting' nested progress.
*/
public function test_nested_weighted() {
$progress = new core_backup_mock_progress();
$progress->start_progress('', 10);
// First nested child has 2 units of its own and is worth 1 unit.
$progress->start_progress('', 2);
$progress->step_time();
$progress->progress(1);
$this->assert_min_max(0.05, 0.05, $progress);
$progress->end_progress();
$this->assert_min_max(0.1, 0.1, $progress);
// Next child has 2 units of its own but is worth 3 units.
$progress->start_progress('weighted', 2, 3);
$progress->step_time();
$progress->progress(1);
$this->assert_min_max(0.25, 0.25, $progress);
$progress->end_progress();
$this->assert_min_max(0.4, 0.4, $progress);
// Next indeterminate child is worth 6 units.
$progress->start_progress('', core_backup_progress::INDETERMINATE, 6);
$progress->step_time();
$progress->progress();
$this->assert_min_max(0.4, 1.0, $progress);
$progress->end_progress();
$this->assert_min_max(1.0, 1.0, $progress);
set_time_limit(0);
}
/**
* I had some issues with real use in backup/restore, this test is intended
* to be similar.
*/
public function test_realistic() {
$progress = new core_backup_mock_progress();
$progress->start_progress('parent', 100);
$progress->start_progress('child', 1);
$progress->progress(1);
$this->assert_min_max(0.01, 0.01, $progress);
$progress->end_progress();
$this->assert_min_max(0.01, 0.01, $progress);
// Clear the time limit, otherwise phpunit complains.
set_time_limit(0);
}
/**
* Tests for any exceptions due to invalid calls.
*/
public function test_exceptions() {
$progress = new core_backup_mock_progress();
// Check errors when empty.
try {
$progress->progress();
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~without start_progress~', $e->getMessage()));
}
try {
$progress->end_progress();
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~without start_progress~', $e->getMessage()));
}
try {
$progress->get_current_description();
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~Not inside progress~', $e->getMessage()));
}
try {
$progress->start_progress('', 1, 7);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~must be 1~', $e->getMessage()));
}
// Check invalid start (0).
try {
$progress->start_progress('hello', 0);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~cannot be zero or negative~', $e->getMessage()));
}
// Indeterminate when value expected.
$progress->start_progress('hello', 10);
try {
$progress->progress(core_backup_progress::INDETERMINATE);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~expecting value~', $e->getMessage()));
}
// Value when indeterminate expected.
$progress->start_progress('hello');
try {
$progress->progress(4);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~expecting INDETERMINATE~', $e->getMessage()));
}
// Illegal values.
$progress->start_progress('hello', 10);
try {
$progress->progress(-2);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~out of range~', $e->getMessage()));
}
try {
$progress->progress(11);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~out of range~', $e->getMessage()));
}
// You are allowed two with the same value...
$progress->progress(4);
$progress->step_time();
$progress->progress(4);
$progress->step_time();
// ...but not to go backwards.
try {
$progress->progress(3);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~backwards~', $e->getMessage()));
}
// When you go forward, you can't go further than there is room.
try {
$progress->start_progress('', 1, 7);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~would exceed max~', $e->getMessage()));
}
// Clear the time limit, otherwise phpunit complains.
set_time_limit(0);
}
/**
* Checks the current progress values are as expected.
*
* @param number $min Expected min progress
* @param number $max Expected max progress
* @param core_backup_mock_progress $progress
*/
private function assert_min_max($min, $max, core_backup_mock_progress $progress) {
$this->assertEquals(array($min, $max),
$progress->get_progress_proportion_range());
}
}
/**
* Helper class that records when update_progress is called and allows time
* stepping.
*/
class core_backup_mock_progress extends core_backup_progress {
private $updatecalled = false;
private $time = 1;
/**
* Checks if update was called since the last call to this function.
*
* @return boolean True if update was called
*/
public function was_update_called() {
if ($this->updatecalled) {
$this->updatecalled = false;
return true;
}
return false;
}
/**
* Steps the current time by 1 second.
*/
public function step_time() {
$this->time++;
}
protected function update_progress() {
$this->updatecalled = true;
}
protected function get_time() {
return $this->time;
}
}