Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * @package   core_backup
  19   * @category  phpunit
  20   * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  21   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22   */
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  // Include all the needed stuff
  27  global $CFG;
  28  require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php');
  29  require_once($CFG->dirroot . '/backup/backup.class.php');
  30  require_once($CFG->dirroot . '/backup/util/settings/base_setting.class.php');
  31  require_once($CFG->dirroot . '/backup/util/settings/backup_setting.class.php');
  32  require_once($CFG->dirroot . '/backup/util/settings/setting_dependency.class.php');
  33  require_once($CFG->dirroot . '/backup/util/settings/root/root_backup_setting.class.php');
  34  require_once($CFG->dirroot . '/backup/util/settings/activity/activity_backup_setting.class.php');
  35  require_once($CFG->dirroot . '/backup/util/settings/section/section_backup_setting.class.php');
  36  require_once($CFG->dirroot . '/backup/util/settings/course/course_backup_setting.class.php');
  37  require_once($CFG->dirroot . '/backup/util/ui/backup_ui_setting.class.php');
  38  
  39  
  40  /**
  41   * setting tests (all)
  42   */
  43  class backp_settings_testcase extends basic_testcase {
  44  
  45      /**
  46       * test base_setting class
  47       */
  48      public function test_base_setting() {
  49          // Instantiate base_setting and check everything
  50          $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
  51          $this->assertTrue($bs instanceof base_setting);
  52          $this->assertEquals($bs->get_name(), 'test');
  53          $this->assertEquals($bs->get_vtype(), base_setting::IS_BOOLEAN);
  54          $this->assertTrue(is_null($bs->get_value()));
  55          $this->assertEquals($bs->get_visibility(), base_setting::VISIBLE);
  56          $this->assertEquals($bs->get_status(), base_setting::NOT_LOCKED);
  57  
  58          // Instantiate base_setting with explicit nulls
  59          $bs = new mock_base_setting('test', base_setting::IS_FILENAME, 'filename.txt', null, null);
  60          $this->assertEquals($bs->get_value() , 'filename.txt');
  61          $this->assertEquals($bs->get_visibility(), base_setting::VISIBLE);
  62          $this->assertEquals($bs->get_status(), base_setting::NOT_LOCKED);
  63  
  64          // Instantiate base_setting and set value, visibility and status
  65          $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
  66          $bs->set_value(true);
  67          $this->assertNotEmpty($bs->get_value());
  68          $bs->set_visibility(base_setting::HIDDEN);
  69          $this->assertEquals($bs->get_visibility(), base_setting::HIDDEN);
  70          $bs->set_status(base_setting::LOCKED_BY_HIERARCHY);
  71          $this->assertEquals($bs->get_status(), base_setting::LOCKED_BY_HIERARCHY);
  72  
  73          // Instantiate with wrong vtype
  74          try {
  75              $bs = new mock_base_setting('test', 'one_wrong_type');
  76              $this->assertTrue(false, 'base_setting_exception expected');
  77          } catch (exception $e) {
  78              $this->assertTrue($e instanceof base_setting_exception);
  79              $this->assertEquals($e->errorcode, 'setting_invalid_type');
  80          }
  81  
  82          // Instantiate with wrong integer value
  83          try {
  84              $bs = new mock_base_setting('test', base_setting::IS_INTEGER, 99.99);
  85              $this->assertTrue(false, 'base_setting_exception expected');
  86          } catch (exception $e) {
  87              $this->assertTrue($e instanceof base_setting_exception);
  88              $this->assertEquals($e->errorcode, 'setting_invalid_integer');
  89          }
  90  
  91          // Instantiate with wrong filename value
  92          try {
  93              $bs = new mock_base_setting('test', base_setting::IS_FILENAME, '../../filename.txt');
  94              $this->assertTrue(false, 'base_setting_exception expected');
  95          } catch (exception $e) {
  96              $this->assertTrue($e instanceof base_setting_exception);
  97              $this->assertEquals($e->errorcode, 'setting_invalid_filename');
  98          }
  99  
 100          // Instantiate with wrong visibility
 101          try {
 102              $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN, null, 'one_wrong_visibility');
 103              $this->assertTrue(false, 'base_setting_exception expected');
 104          } catch (exception $e) {
 105              $this->assertTrue($e instanceof base_setting_exception);
 106              $this->assertEquals($e->errorcode, 'setting_invalid_visibility');
 107          }
 108  
 109          // Instantiate with wrong status
 110          try {
 111              $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN, null, null, 'one_wrong_status');
 112              $this->assertTrue(false, 'base_setting_exception expected');
 113          } catch (exception $e) {
 114              $this->assertTrue($e instanceof base_setting_exception);
 115              $this->assertEquals($e->errorcode, 'setting_invalid_status');
 116          }
 117  
 118          // Instantiate base_setting and try to set wrong ui_type
 119          // We need a custom error handler to catch the type hinting error
 120          // that should return incorrect_object_passed
 121          $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
 122          set_error_handler('backup_setting_error_handler', E_RECOVERABLE_ERROR);
 123          try {
 124              $bs->set_ui('one_wrong_ui_type', 'label', array(), array());
 125              $this->assertTrue(false, 'base_setting_exception expected');
 126          } catch (exception $e) {
 127              $this->assertTrue($e instanceof base_setting_exception);
 128              $this->assertEquals($e->errorcode, 'incorrect_object_passed');
 129          } catch (TypeError $e) {
 130              // On PHP7+ we get a TypeError raised, lets check we've the right error.
 131              $this->assertRegexp('/must be an instance of backup_setting_ui/', $e->getMessage());
 132          }
 133          restore_error_handler();
 134  
 135          // Instantiate base_setting and try to set wrong ui_label
 136          // We need a custom error handler to catch the type hinting error
 137          // that should return incorrect_object_passed
 138          $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
 139          set_error_handler('backup_setting_error_handler', E_RECOVERABLE_ERROR);
 140          try {
 141              $bs->set_ui(base_setting::UI_HTML_CHECKBOX, 'one/wrong/label', array(), array());
 142              $this->assertTrue(false, 'base_setting_exception expected');
 143          } catch (exception $e) {
 144              $this->assertTrue($e instanceof base_setting_exception);
 145              $this->assertEquals($e->errorcode, 'incorrect_object_passed');
 146          } catch (TypeError $e) {
 147              // On PHP7+ we get a TypeError raised, lets check we've the right error.
 148              $this->assertRegexp('/must be an instance of backup_setting_ui/', $e->getMessage());
 149          }
 150          restore_error_handler();
 151  
 152          // Try to change value of locked setting by permission
 153          $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN, null, null, base_setting::LOCKED_BY_PERMISSION);
 154          try {
 155              $bs->set_value(true);
 156              $this->assertTrue(false, 'base_setting_exception expected');
 157          } catch (exception $e) {
 158              $this->assertTrue($e instanceof base_setting_exception);
 159              $this->assertEquals($e->errorcode, 'setting_locked_by_permission');
 160          }
 161  
 162          // Try to change value of locked setting by config
 163          $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN, null, null, base_setting::LOCKED_BY_CONFIG);
 164          try {
 165              $bs->set_value(true);
 166              $this->assertTrue(false, 'base_setting_exception expected');
 167          } catch (exception $e) {
 168              $this->assertTrue($e instanceof base_setting_exception);
 169              $this->assertEquals($e->errorcode, 'setting_locked_by_config');
 170          }
 171  
 172          // Try to add same setting twice
 173          $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
 174          $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
 175          $bs1->add_dependency($bs2, null, array('value'=>0));
 176          try {
 177              $bs1->add_dependency($bs2);
 178              $this->assertTrue(false, 'base_setting_exception expected');
 179          } catch (exception $e) {
 180              $this->assertTrue($e instanceof base_setting_exception);
 181              $this->assertEquals($e->errorcode, 'setting_already_added');
 182          }
 183  
 184          // Try to create one circular reference
 185          $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
 186          try {
 187              $bs1->add_dependency($bs1); // self
 188              $this->assertTrue(false, 'base_setting_exception expected');
 189          } catch (exception $e) {
 190              $this->assertTrue($e instanceof base_setting_exception);
 191              $this->assertEquals($e->errorcode, 'setting_circular_reference');
 192              $this->assertTrue($e->a instanceof stdclass);
 193              $this->assertEquals($e->a->main, 'test1');
 194              $this->assertEquals($e->a->alreadydependent, 'test1');
 195          }
 196  
 197          $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
 198          $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
 199          $bs3 = new mock_base_setting('test3', base_setting::IS_INTEGER, null);
 200          $bs4 = new mock_base_setting('test4', base_setting::IS_INTEGER, null);
 201          $bs1->add_dependency($bs2, null, array('value'=>0));
 202          $bs2->add_dependency($bs3, null, array('value'=>0));
 203          $bs3->add_dependency($bs4, null, array('value'=>0));
 204          try {
 205              $bs4->add_dependency($bs1, null, array('value'=>0));
 206              $this->assertTrue(false, 'base_setting_exception expected');
 207          } catch (exception $e) {
 208              $this->assertTrue($e instanceof base_setting_exception);
 209              $this->assertEquals($e->errorcode, 'setting_circular_reference');
 210              $this->assertTrue($e->a instanceof stdclass);
 211              $this->assertEquals($e->a->main, 'test1');
 212              $this->assertEquals($e->a->alreadydependent, 'test4');
 213          }
 214  
 215          $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
 216          $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
 217          $bs1->register_dependency(new setting_dependency_disabledif_empty($bs1, $bs2));
 218          try {
 219              // $bs1 is already dependent on $bs2 so this should fail.
 220              $bs2->register_dependency(new setting_dependency_disabledif_empty($bs2, $bs1));
 221              $this->assertTrue(false, 'base_setting_exception expected');
 222          } catch (exception $e) {
 223              $this->assertTrue($e instanceof base_setting_exception);
 224              $this->assertEquals($e->errorcode, 'setting_circular_reference');
 225              $this->assertTrue($e->a instanceof stdclass);
 226              $this->assertEquals($e->a->main, 'test1');
 227              $this->assertEquals($e->a->alreadydependent, 'test2');
 228          }
 229  
 230          // Create 3 settings and observe between them, last one must
 231          // automatically inherit all the settings defined in the main one
 232          $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
 233          $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
 234          $bs3 = new mock_base_setting('test3', base_setting::IS_INTEGER, null);
 235          $bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
 236          $bs2->add_dependency($bs3, setting_dependency::DISABLED_NOT_EMPTY);
 237          // Check values are spreaded ok
 238          $bs1->set_value(123);
 239          $this->assertEquals($bs1->get_value(), 123);
 240          $this->assertEquals($bs2->get_value(), $bs1->get_value());
 241          $this->assertEquals($bs3->get_value(), $bs1->get_value());
 242  
 243          // Add one more setting and set value again
 244          $bs4 = new mock_base_setting('test4', base_setting::IS_INTEGER, null);
 245          $bs2->add_dependency($bs4, setting_dependency::DISABLED_NOT_EMPTY);
 246          $bs2->set_value(321);
 247          // The above change should change
 248          $this->assertEquals($bs1->get_value(), 123);
 249          $this->assertEquals($bs2->get_value(), 321);
 250          $this->assertEquals($bs3->get_value(), 321);
 251          $this->assertEquals($bs4->get_value(), 321);
 252  
 253          // Check visibility is spreaded ok
 254          $bs1->set_visibility(base_setting::HIDDEN);
 255          $this->assertEquals($bs2->get_visibility(), $bs1->get_visibility());
 256          $this->assertEquals($bs3->get_visibility(), $bs1->get_visibility());
 257          // Check status is spreaded ok
 258          $bs1->set_status(base_setting::LOCKED_BY_HIERARCHY);
 259          $this->assertEquals($bs2->get_status(), $bs1->get_status());
 260          $this->assertEquals($bs3->get_status(), $bs1->get_status());
 261  
 262          // Create 3 settings and observe between them, put them in one array,
 263          // force serialize/deserialize to check the observable pattern continues
 264          // working after that
 265          $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
 266          $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
 267          $bs3 = new mock_base_setting('test3', base_setting::IS_INTEGER, null);
 268          $bs1->add_dependency($bs2, null, array('value'=>0));
 269          $bs2->add_dependency($bs3, null, array('value'=>0));
 270          // Serialize
 271          $arr = array($bs1, $bs2, $bs3);
 272          $ser = base64_encode(serialize($arr));
 273          // Unserialize and copy to new objects
 274          $newarr = unserialize(base64_decode($ser));
 275          $ubs1 = $newarr[0];
 276          $ubs2 = $newarr[1];
 277          $ubs3 = $newarr[2];
 278          // Must continue being base settings
 279          $this->assertTrue($ubs1 instanceof base_setting);
 280          $this->assertTrue($ubs2 instanceof base_setting);
 281          $this->assertTrue($ubs3 instanceof base_setting);
 282          // Set parent setting
 283          $ubs1->set_value(1234);
 284          $ubs1->set_visibility(base_setting::HIDDEN);
 285          $ubs1->set_status(base_setting::LOCKED_BY_HIERARCHY);
 286          // Check changes have been spreaded
 287          $this->assertEquals($ubs2->get_visibility(), $ubs1->get_visibility());
 288          $this->assertEquals($ubs3->get_visibility(), $ubs1->get_visibility());
 289          $this->assertEquals($ubs2->get_status(), $ubs1->get_status());
 290          $this->assertEquals($ubs3->get_status(), $ubs1->get_status());
 291      }
 292  
 293      /**
 294       * Test that locked and unlocked states on dependent backup settings at the same level
 295       * correctly do not flow from the parent to the child setting when the setting is locked by permissions.
 296       */
 297      public function test_dependency_empty_locked_by_permission_child_is_not_unlocked() {
 298          // Check dependencies are working ok.
 299          $bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, 2);
 300          $bs1->set_level(1);
 301          $bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, 2);
 302          $bs2->set_level(1); // Same level *must* work.
 303          $bs1->add_dependency($bs2, setting_dependency::DISABLED_EMPTY);
 304  
 305          $bs1->set_status(base_setting::LOCKED_BY_PERMISSION);
 306          $this->assertEquals(base_setting::LOCKED_BY_HIERARCHY, $bs2->get_status());
 307          $this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs1->get_status());
 308          $bs2->set_status(base_setting::LOCKED_BY_PERMISSION);
 309          $this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs1->get_status());
 310  
 311          // Unlocking the parent should NOT unlock the child.
 312          $bs1->set_status(base_setting::NOT_LOCKED);
 313  
 314          $this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs2->get_status());
 315      }
 316  
 317      /**
 318       * Test that locked and unlocked states on dependent backup settings at the same level
 319       * correctly do flow from the parent to the child setting when the setting is locked by config.
 320       */
 321      public function test_dependency_not_empty_locked_by_config_parent_is_unlocked() {
 322          $bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, 0);
 323          $bs1->set_level(1);
 324          $bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, 0);
 325          $bs2->set_level(1); // Same level *must* work.
 326          $bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
 327  
 328          $bs1->set_status(base_setting::LOCKED_BY_CONFIG);
 329          $this->assertEquals(base_setting::LOCKED_BY_HIERARCHY, $bs2->get_status());
 330          $this->assertEquals(base_setting::LOCKED_BY_CONFIG, $bs1->get_status());
 331  
 332          // Unlocking the parent should unlock the child.
 333          $bs1->set_status(base_setting::NOT_LOCKED);
 334          $this->assertEquals(base_setting::NOT_LOCKED, $bs2->get_status());
 335      }
 336  
 337      /**
 338       * test backup_setting class
 339       */
 340      public function test_backup_setting() {
 341          // Instantiate backup_setting class and set level
 342          $bs = new mock_backup_setting('test', base_setting::IS_INTEGER, null);
 343          $bs->set_level(1);
 344          $this->assertEquals($bs->get_level(), 1);
 345  
 346          // Instantiate backup setting class and try to add one non backup_setting dependency
 347          set_error_handler('backup_setting_error_handler', E_RECOVERABLE_ERROR);
 348          $bs = new mock_backup_setting('test', base_setting::IS_INTEGER, null);
 349          try {
 350              $bs->add_dependency(new stdclass());
 351              $this->assertTrue(false, 'backup_setting_exception expected');
 352          } catch (exception $e) {
 353              $this->assertTrue($e instanceof backup_setting_exception);
 354              $this->assertEquals($e->errorcode, 'incorrect_object_passed');
 355          } catch (TypeError $e) {
 356              // On PHP7+ we get a TypeError raised, lets check we've the right error.
 357              $this->assertRegexp('/must be an instance of base_setting/', $e->getMessage());
 358          }
 359          restore_error_handler();
 360  
 361          // Try to assing upper level dependency
 362          $bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, null);
 363          $bs1->set_level(1);
 364          $bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, null);
 365          $bs2->set_level(2);
 366          try {
 367              $bs2->add_dependency($bs1);
 368              $this->assertTrue(false, 'backup_setting_exception expected');
 369          } catch (exception $e) {
 370              $this->assertTrue($e instanceof backup_setting_exception);
 371              $this->assertEquals($e->errorcode, 'cannot_add_upper_level_dependency');
 372          }
 373  
 374          // Check dependencies are working ok
 375          $bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, null);
 376          $bs1->set_level(1);
 377          $bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, null);
 378          $bs2->set_level(1); // Same level *must* work
 379          $bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
 380          $bs1->set_value(123456);
 381          $this->assertEquals($bs2->get_value(), $bs1->get_value());
 382      }
 383  
 384      /**
 385       * test activity_backup_setting class
 386       */
 387      public function test_activity_backup_setting() {
 388          $bs = new mock_activity_backup_setting('test', base_setting::IS_INTEGER, null);
 389          $this->assertEquals($bs->get_level(), backup_setting::ACTIVITY_LEVEL);
 390  
 391          // Check checksum implementation is working
 392          $bs1 = new mock_activity_backup_setting('test', base_setting::IS_INTEGER, null);
 393          $bs1->set_value(123);
 394          $checksum = $bs1->calculate_checksum();
 395          $this->assertNotEmpty($checksum);
 396          $this->assertTrue($bs1->is_checksum_correct($checksum));
 397      }
 398  
 399      /**
 400       * test section_backup_setting class
 401       */
 402      public function test_section_backup_setting() {
 403          $bs = new mock_section_backup_setting('test', base_setting::IS_INTEGER, null);
 404          $this->assertEquals($bs->get_level(), backup_setting::SECTION_LEVEL);
 405  
 406          // Check checksum implementation is working
 407          $bs1 = new mock_section_backup_setting('test', base_setting::IS_INTEGER, null);
 408          $bs1->set_value(123);
 409          $checksum = $bs1->calculate_checksum();
 410          $this->assertNotEmpty($checksum);
 411          $this->assertTrue($bs1->is_checksum_correct($checksum));
 412      }
 413  
 414      /**
 415       * test course_backup_setting class
 416       */
 417      public function test_course_backup_setting() {
 418          $bs = new mock_course_backup_setting('test', base_setting::IS_INTEGER, null);
 419          $this->assertEquals($bs->get_level(), backup_setting::COURSE_LEVEL);
 420  
 421          // Check checksum implementation is working
 422          $bs1 = new mock_course_backup_setting('test', base_setting::IS_INTEGER, null);
 423          $bs1->set_value(123);
 424          $checksum = $bs1->calculate_checksum();
 425          $this->assertNotEmpty($checksum);
 426          $this->assertTrue($bs1->is_checksum_correct($checksum));
 427      }
 428  }
 429  
 430  /**
 431   * helper extended base_setting class that makes some methods public for testing
 432   */
 433  class mock_base_setting extends base_setting {
 434      public function get_vtype() {
 435          return $this->vtype;
 436      }
 437  
 438      public function process_change($setting, $ctype, $oldv) {
 439          // Simply, inherit from the main object
 440          $this->set_value($setting->get_value());
 441          $this->set_visibility($setting->get_visibility());
 442          $this->set_status($setting->get_status());
 443      }
 444  
 445      public function get_ui_info() {
 446          // Return an array with all the ui info to be tested
 447          return array($this->ui_type, $this->ui_label, $this->ui_values, $this->ui_options);
 448      }
 449  }
 450  
 451  /**
 452   * helper extended backup_setting class that makes some methods public for testing
 453   */
 454  class mock_backup_setting extends backup_setting {
 455      public function set_level($level) {
 456          $this->level = $level;
 457      }
 458  
 459      public function process_change($setting, $ctype, $oldv) {
 460          // Simply, inherit from the main object
 461          $this->set_value($setting->get_value());
 462          $this->set_visibility($setting->get_visibility());
 463          $this->set_status($setting->get_status());
 464      }
 465  }
 466  
 467  /**
 468   * helper extended activity_backup_setting class that makes some methods public for testing
 469   */
 470  class mock_activity_backup_setting extends activity_backup_setting {
 471      public function process_change($setting, $ctype, $oldv) {
 472          // Do nothing
 473      }
 474  }
 475  
 476  /**
 477   * helper extended section_backup_setting class that makes some methods public for testing
 478   */
 479  class mock_section_backup_setting extends section_backup_setting {
 480      public function process_change($setting, $ctype, $oldv) {
 481          // Do nothing
 482      }
 483  }
 484  
 485  /**
 486   * helper extended course_backup_setting class that makes some methods public for testing
 487   */
 488  class mock_course_backup_setting extends course_backup_setting {
 489      public function process_change($setting, $ctype, $oldv) {
 490          // Do nothing
 491      }
 492  }
 493  
 494  /**
 495   * This error handler is used to convert errors to excpetions so that simepltest can
 496   * catch them.
 497   *
 498   * This is required in order to catch type hint mismatches that result in a error
 499   * being thrown. It should only ever be used to catch E_RECOVERABLE_ERROR's.
 500   *
 501   * It throws a backup_setting_exception with 'incorrect_object_passed'
 502   *
 503   * @param int $errno E_RECOVERABLE_ERROR
 504   * @param string $errstr
 505   * @param string $errfile
 506   * @param int $errline
 507   * @param array $errcontext
 508   * @return null
 509   */
 510  function backup_setting_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
 511      if ($errno !== E_RECOVERABLE_ERROR) {
 512          // Currently we only want to deal with type hinting errors
 513          return false;
 514      }
 515      throw new backup_setting_exception('incorrect_object_passed');
 516  }