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] [Versions 39 and 310]

   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  require_once (__DIR__.'/fixtures/structure_fixtures.php');
  28  
  29  global $CFG;
  30  require_once($CFG->dirroot . '/backup/util/xml/output/memory_xml_output.class.php');
  31  
  32  
  33  /**
  34   * Unit test case the all the backup structure classes. Note: Uses database
  35   */
  36  class backup_structure_testcase extends advanced_testcase {
  37  
  38      /** @var int Store the inserted forum->id for use in test functions */
  39      protected $forumid;
  40      /** @var int Store the inserted discussion1->id for use in test functions */
  41      protected $discussionid1;
  42      /** @var int Store the inserted discussion2->id for use in test functions */
  43      protected $discussionid2;
  44      /** @var int Store the inserted post1->id for use in test functions */
  45      protected $postid1;
  46      /** @var int Store the inserted post2->id for use in test functions */
  47      protected $postid2;
  48      /** @var int Store the inserted post3->id for use in test functions */
  49      protected $postid3;
  50      /** @var int Store the inserted post4->id for use in test functions */
  51      protected $postid4;
  52      /** @var int Official contextid for these tests */
  53      protected $contextid;
  54  
  55  
  56      protected function setUp(): void {
  57          parent::setUp();
  58  
  59          $this->resetAfterTest(true);
  60  
  61          $this->contextid = 666; // Let's assume this is the context for the forum
  62          $this->fill_records(); // Add common stuff needed by various test methods
  63      }
  64  
  65      private function fill_records() {
  66          global $DB;
  67  
  68          // Create one forum
  69          $forum_data = (object)array('course' => 1, 'name' => 'Test forum', 'intro' => 'Intro forum');
  70          $this->forumid = $DB->insert_record('forum', $forum_data);
  71          // With two related file
  72          $f1_forum_data = (object)array(
  73              'contenthash' => 'testf1', 'contextid' => $this->contextid,
  74              'component'=>'mod_forum', 'filearea' => 'intro', 'filename' => 'tf1', 'itemid' => 0,
  75              'filesize' => 123, 'timecreated' => 0, 'timemodified' => 0,
  76              'pathnamehash' => 'testf1'
  77          );
  78          $DB->insert_record('files', $f1_forum_data);
  79          $f2_forum_data = (object)array(
  80              'contenthash' => 'tesft2', 'contextid' => $this->contextid,
  81              'component'=>'mod_forum', 'filearea' => 'intro', 'filename' => 'tf2', 'itemid' => 0,
  82              'filesize' => 123, 'timecreated' => 0, 'timemodified' => 0,
  83              'pathnamehash' => 'testf2'
  84          );
  85          $DB->insert_record('files', $f2_forum_data);
  86  
  87          // Create two discussions
  88          $discussion1 = (object)array('course' => 1, 'forum' => $this->forumid, 'name' => 'd1', 'userid' => 100, 'groupid' => 200);
  89          $this->discussionid1 = $DB->insert_record('forum_discussions', $discussion1);
  90          $discussion2 = (object)array('course' => 1, 'forum' => $this->forumid, 'name' => 'd2', 'userid' => 101, 'groupid' => 201);
  91          $this->discussionid2 = $DB->insert_record('forum_discussions', $discussion2);
  92  
  93          // Create four posts
  94          $post1 = (object)array('discussion' => $this->discussionid1, 'userid' => 100, 'subject' => 'p1', 'message' => 'm1');
  95          $this->postid1 = $DB->insert_record('forum_posts', $post1);
  96          $post2 = (object)array('discussion' => $this->discussionid1, 'parent' => $this->postid1, 'userid' => 102, 'subject' => 'p2', 'message' => 'm2');
  97          $this->postid2 = $DB->insert_record('forum_posts', $post2);
  98          $post3 = (object)array('discussion' => $this->discussionid1, 'parent' => $this->postid2, 'userid' => 103, 'subject' => 'p3', 'message' => 'm3');
  99          $this->postid3 = $DB->insert_record('forum_posts', $post3);
 100          $post4 = (object)array('discussion' => $this->discussionid2, 'userid' => 101, 'subject' => 'p4', 'message' => 'm4');
 101          $this->postid4 = $DB->insert_record('forum_posts', $post4);
 102          // With two related file
 103          $f1_post1 = (object)array(
 104              'contenthash' => 'testp1', 'contextid' => $this->contextid, 'component'=>'mod_forum',
 105              'filearea' => 'post', 'filename' => 'tp1', 'itemid' => $this->postid1,
 106              'filesize' => 123, 'timecreated' => 0, 'timemodified' => 0,
 107              'pathnamehash' => 'testp1'
 108          );
 109          $DB->insert_record('files', $f1_post1);
 110          $f1_post2 = (object)array(
 111              'contenthash' => 'testp2', 'contextid' => $this->contextid, 'component'=>'mod_forum',
 112              'filearea' => 'attachment', 'filename' => 'tp2', 'itemid' => $this->postid2,
 113              'filesize' => 123, 'timecreated' => 0, 'timemodified' => 0,
 114              'pathnamehash' => 'testp2'
 115          );
 116          $DB->insert_record('files', $f1_post2);
 117  
 118          // Create two ratings
 119          $rating1 = (object)array(
 120              'contextid' => $this->contextid, 'userid' => 104, 'itemid' => $this->postid1, 'rating' => 2,
 121              'scaleid' => -1, 'timecreated' => time(), 'timemodified' => time());
 122          $r1id = $DB->insert_record('rating', $rating1);
 123          $rating2 = (object)array(
 124              'contextid' => $this->contextid, 'userid' => 105, 'itemid' => $this->postid1, 'rating' => 3,
 125              'scaleid' => -1, 'timecreated' => time(), 'timemodified' => time());
 126          $r2id = $DB->insert_record('rating', $rating2);
 127  
 128          // Create 1 reads
 129          $read1 = (object)array('userid' => 102, 'forumid' => $this->forumid, 'discussionid' => $this->discussionid2, 'postid' => $this->postid4);
 130          $DB->insert_record('forum_read', $read1);
 131      }
 132  
 133      /**
 134       * Backup structures tests (construction, definition and execution)
 135       */
 136      function test_backup_structure_construct() {
 137          global $DB;
 138  
 139          $backupid = 'Testing Backup ID'; // Official backupid for these tests
 140  
 141          // Create all the elements that will conform the tree
 142          $forum = new backup_nested_element('forum',
 143              array('id'),
 144              array(
 145                  'type', 'name', 'intro', 'introformat',
 146                  'assessed', 'assesstimestart', 'assesstimefinish', 'scale',
 147                  'maxbytes', 'maxattachments', 'forcesubscribe', 'trackingtype',
 148                  'rsstype', 'rssarticles', 'timemodified', 'warnafter',
 149                  'blockafter',
 150                  new backup_final_element('blockperiod'),
 151                  new mock_skip_final_element('completiondiscussions'),
 152                  new mock_modify_final_element('completionreplies'),
 153                  new mock_final_element_interceptor('completionposts'))
 154          );
 155          $discussions = new backup_nested_element('discussions');
 156          $discussion = new backup_nested_element('discussion',
 157              array('id'),
 158              array(
 159                  'forum', 'name', 'firstpost', 'userid',
 160                  'groupid', 'assessed', 'timemodified', 'usermodified',
 161                  'timestart', 'timeend')
 162          );
 163          $posts = new backup_nested_element('posts');
 164          $post = new backup_nested_element('post',
 165              array('id'),
 166              array(
 167                  'discussion', 'parent', 'userid', 'created',
 168                  'modified', 'mailed', 'subject', 'message',
 169                  'messageformat', 'messagetrust', 'attachment', 'totalscore',
 170                  'mailnow')
 171          );
 172          $ratings = new backup_nested_element('ratings');
 173          $rating = new backup_nested_element('rating',
 174              array('id'),
 175              array('userid', 'itemid', 'time', 'post_rating')
 176          );
 177          $reads = new backup_nested_element('readposts');
 178          $read = new backup_nested_element('read',
 179              array('id'),
 180              array(
 181                  'userid', 'discussionid', 'postid',
 182                  'firstread', 'lastread')
 183          );
 184          $inventeds = new backup_nested_element('invented_elements',
 185              array('reason', 'version')
 186          );
 187          $invented = new backup_nested_element('invented',
 188              null,
 189              array('one', 'two', 'three')
 190          );
 191          $one = $invented->get_final_element('one');
 192          $one->add_attributes(array('attr1', 'attr2'));
 193  
 194          // Build the tree
 195          $forum->add_child($discussions);
 196          $discussions->add_child($discussion);
 197  
 198          $discussion->add_child($posts);
 199          $posts->add_child($post);
 200  
 201          $post->add_child($ratings);
 202          $ratings->add_child($rating);
 203  
 204          $forum->add_child($reads);
 205          $reads->add_child($read);
 206  
 207          $forum->add_child($inventeds);
 208          $inventeds->add_child($invented);
 209  
 210          // Let's add 1 optigroup with 4 elements
 211          $alternative1 = new backup_optigroup_element('alternative1',
 212              array('name', 'value'), '../../id', $this->postid1);
 213          $alternative2 = new backup_optigroup_element('alternative2',
 214              array('name', 'value'), backup::VAR_PARENTID, $this->postid2);
 215          $alternative3 = new backup_optigroup_element('alternative3',
 216              array('name', 'value'), '/forum/discussions/discussion/posts/post/id', $this->postid3);
 217          $alternative4 = new backup_optigroup_element('alternative4',
 218              array('forumtype', 'forumname')); // Alternative without conditions
 219          // Create the optigroup, adding one element
 220          $optigroup = new backup_optigroup('alternatives', $alternative1, false);
 221          // Add second opti element
 222          $optigroup->add_child($alternative2);
 223  
 224          // Add optigroup to post element
 225          $post->add_optigroup($optigroup);
 226          // Add third opti element, on purpose after the add_optigroup() line above to check param evaluation works ok
 227          $optigroup->add_child($alternative3);
 228          // Add 4th opti element (the one without conditions, so will be present always)
 229          $optigroup->add_child($alternative4);
 230  
 231          /// Create some new nested elements, both named 'dupetest1', and add them to alternative1 and alternative2
 232          /// (not problem as far as the optigroup in not unique)
 233          $dupetest1 = new backup_nested_element('dupetest1', null, array('field1', 'field2'));
 234          $dupetest2 = new backup_nested_element('dupetest2', null, array('field1', 'field2'));
 235          $dupetest3 = new backup_nested_element('dupetest3', null, array('field1', 'field2'));
 236          $dupetest4 = new backup_nested_element('dupetest1', null, array('field1', 'field2'));
 237          $dupetest1->add_child($dupetest3);
 238          $dupetest2->add_child($dupetest4);
 239          $alternative1->add_child($dupetest1);
 240          $alternative2->add_child($dupetest2);
 241  
 242          // Define sources
 243          $forum->set_source_table('forum', array('id' => backup::VAR_ACTIVITYID));
 244          $discussion->set_source_sql('SELECT *
 245                                         FROM {forum_discussions}
 246                                        WHERE forum = ?',
 247              array('/forum/id')
 248          );
 249          $post->set_source_table('forum_posts', array('discussion' => '/forum/discussions/discussion/id'));
 250          $rating->set_source_sql('SELECT *
 251                                     FROM {rating}
 252                                    WHERE itemid = ?',
 253              array(backup::VAR_PARENTID)
 254          );
 255  
 256          $read->set_source_table('forum_read', array('forumid' => '../../id'));
 257  
 258          $inventeds->set_source_array(array((object)array('reason' => 'I love Moodle', 'version' => '1.0'),
 259              (object)array('reason' => 'I love Moodle', 'version' => '2.0'))); // 2 object array
 260          $invented->set_source_array(array((object)array('one' => 1, 'two' => 2, 'three' => 3),
 261              (object)array('one' => 11, 'two' => 22, 'three' => 33))); // 2 object array
 262  
 263          // Set optigroup_element sources
 264          $alternative1->set_source_array(array((object)array('name' => 'alternative1', 'value' => 1))); // 1 object array
 265          // Skip alternative2 source definition on purpose (will be tested)
 266          // $alternative2->set_source_array(array((object)array('name' => 'alternative2', 'value' => 2))); // 1 object array
 267          $alternative3->set_source_array(array((object)array('name' => 'alternative3', 'value' => 3))); // 1 object array
 268          // Alternative 4 source is the forum type and name, so we'll get that in ALL posts (no conditions) that
 269          // have not another alternative (post4 in our testing data in the only not matching any other alternative)
 270          $alternative4->set_source_sql('SELECT type AS forumtype, name AS forumname
 271                                           FROM {forum}
 272                                          WHERE id = ?',
 273              array('/forum/id')
 274          );
 275          // Set children of optigroup_element source
 276          $dupetest1->set_source_array(array((object)array('field1' => '1', 'field2' => 1))); // 1 object array
 277          $dupetest2->set_source_array(array((object)array('field1' => '2', 'field2' => 2))); // 1 object array
 278          $dupetest3->set_source_array(array((object)array('field1' => '3', 'field2' => 3))); // 1 object array
 279          $dupetest4->set_source_array(array((object)array('field1' => '4', 'field2' => 4))); // 1 object array
 280  
 281          // Define some aliases
 282          $rating->set_source_alias('rating', 'post_rating'); // Map the 'rating' value from DB to 'post_rating' final element
 283  
 284          // Mark to detect files of type 'forum_intro' in forum (and not item id)
 285          $forum->annotate_files('mod_forum', 'intro', null);
 286  
 287          // Mark to detect file of type 'forum_post' and 'forum_attachment' in post (with itemid being post->id)
 288          $post->annotate_files('mod_forum', 'post', 'id');
 289          $post->annotate_files('mod_forum', 'attachment', 'id');
 290  
 291          // Mark various elements to be annotated
 292          $discussion->annotate_ids('user1', 'userid');
 293          $post->annotate_ids('forum_post', 'id');
 294          $rating->annotate_ids('user2', 'userid');
 295          $rating->annotate_ids('forum_post', 'itemid');
 296  
 297          // Create the backup_ids_temp table
 298          backup_controller_dbops::create_backup_ids_temp_table($backupid);
 299  
 300          // Instantiate in memory xml output
 301          $xo = new memory_xml_output();
 302  
 303          // Instantiate xml_writer and start it
 304          $xw = new xml_writer($xo);
 305          $xw->start();
 306  
 307          // Instantiate the backup processor
 308          $processor = new backup_structure_processor($xw);
 309  
 310          // Set some variables
 311          $processor->set_var(backup::VAR_ACTIVITYID, $this->forumid);
 312          $processor->set_var(backup::VAR_BACKUPID, $backupid);
 313          $processor->set_var(backup::VAR_CONTEXTID,$this->contextid);
 314  
 315          // Process the backup structure with the backup processor
 316          $forum->process($processor);
 317  
 318          // Stop the xml_writer
 319          $xw->stop();
 320  
 321          // Check various counters
 322          $this->assertEquals($forum->get_counter(), $DB->count_records('forum'));
 323          $this->assertEquals($discussion->get_counter(), $DB->count_records('forum_discussions'));
 324          $this->assertEquals($rating->get_counter(), $DB->count_records('rating'));
 325          $this->assertEquals($read->get_counter(), $DB->count_records('forum_read'));
 326          $this->assertEquals($inventeds->get_counter(), 2); // Array
 327  
 328          // Perform some validations with the generated XML
 329          $dom = new DomDocument();
 330          $dom->loadXML($xo->get_allcontents());
 331          $xpath = new DOMXPath($dom);
 332          // Some more counters
 333          $query = '/forum/discussions/discussion/posts/post';
 334          $posts = $xpath->query($query);
 335          $this->assertEquals($posts->length, $DB->count_records('forum_posts'));
 336          $query = '/forum/invented_elements/invented';
 337          $inventeds = $xpath->query($query);
 338          $this->assertEquals($inventeds->length, 2*2);
 339  
 340          // Check ratings information against DB
 341          $ratings = $dom->getElementsByTagName('rating');
 342          $this->assertEquals($ratings->length, $DB->count_records('rating'));
 343          foreach ($ratings as $rating) {
 344              $ratarr = array();
 345              $ratarr['id'] = $rating->getAttribute('id');
 346              foreach ($rating->childNodes as $node) {
 347                  if ($node->nodeType != XML_TEXT_NODE) {
 348                      $ratarr[$node->nodeName] = $node->nodeValue;
 349                  }
 350              }
 351              $this->assertEquals($DB->get_field('rating', 'userid', array('id' => $ratarr['id'])), $ratarr['userid']);
 352              $this->assertEquals($DB->get_field('rating', 'itemid', array('id' => $ratarr['id'])), $ratarr['itemid']);
 353              $this->assertEquals($DB->get_field('rating', 'rating', array('id' => $ratarr['id'])), $ratarr['post_rating']);
 354          }
 355  
 356          // Check forum has "blockeperiod" with value 0 (was declared by object instead of name)
 357          $query = '/forum[blockperiod="0"]';
 358          $result = $xpath->query($query);
 359          $this->assertEquals(1, $result->length);
 360  
 361          // Check forum is missing "completiondiscussions" (as we are using mock_skip_final_element)
 362          $query = '/forum/completiondiscussions';
 363          $result = $xpath->query($query);
 364          $this->assertEquals(0, $result->length);
 365  
 366          // Check forum has "completionreplies" with value "original was 0, now changed" (because of mock_modify_final_element)
 367          $query = '/forum[completionreplies="original was 0, now changed"]';
 368          $result = $xpath->query($query);
 369          $this->assertEquals(1, $result->length);
 370  
 371          // Check forum has "completionposts" with value "intercepted!" (because of mock_final_element_interceptor)
 372          $query = '/forum[completionposts="intercepted!"]';
 373          $result = $xpath->query($query);
 374          $this->assertEquals(1, $result->length);
 375  
 376          // Check there isn't any alternative2 tag, as far as it hasn't source defined
 377          $query = '//alternative2';
 378          $result = $xpath->query($query);
 379          $this->assertEquals(0, $result->length);
 380  
 381          // Check there are 4 "field1" elements
 382          $query = '/forum/discussions/discussion/posts/post//field1';
 383          $result = $xpath->query($query);
 384          $this->assertEquals(4, $result->length);
 385  
 386          // Check first post has one name element with value "alternative1"
 387          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid1.'"][name="alternative1"]';
 388          $result = $xpath->query($query);
 389          $this->assertEquals(1, $result->length);
 390  
 391          // Check there are two "dupetest1" elements
 392          $query = '/forum/discussions/discussion/posts/post//dupetest1';
 393          $result = $xpath->query($query);
 394          $this->assertEquals(2, $result->length);
 395  
 396          // Check second post has one name element with value "dupetest2"
 397          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid2.'"]/dupetest2';
 398          $result = $xpath->query($query);
 399          $this->assertEquals(1, $result->length);
 400  
 401          // Check element "dupetest2" of second post has one field1 element with value "2"
 402          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid2.'"]/dupetest2[field1="2"]';
 403          $result = $xpath->query($query);
 404          $this->assertEquals(1, $result->length);
 405  
 406          // Check forth post has no name element
 407          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid4.'"]/name';
 408          $result = $xpath->query($query);
 409          $this->assertEquals(0, $result->length);
 410  
 411          // Check 1st, 2nd and 3rd posts have no forumtype element
 412          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid1.'"]/forumtype';
 413          $result = $xpath->query($query);
 414          $this->assertEquals(0, $result->length);
 415          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid2.'"]/forumtype';
 416          $result = $xpath->query($query);
 417          $this->assertEquals(0, $result->length);
 418          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid3.'"]/forumtype';
 419          $result = $xpath->query($query);
 420          $this->assertEquals(0, $result->length);
 421  
 422          // Check 4th post has one forumtype element with value "general"
 423          // (because it doesn't matches alternatives 1, 2, 3, then alternative 4,
 424          // the one without conditions is being applied)
 425          $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid4.'"][forumtype="general"]';
 426          $result = $xpath->query($query);
 427          $this->assertEquals(1, $result->length);
 428  
 429          // Check annotations information against DB
 430          // Count records in original tables
 431          $c_postsid    = $DB->count_records_sql('SELECT COUNT(DISTINCT id) FROM {forum_posts}');
 432          $c_dissuserid = $DB->count_records_sql('SELECT COUNT(DISTINCT userid) FROM {forum_discussions}');
 433          $c_ratuserid  = $DB->count_records_sql('SELECT COUNT(DISTINCT userid) FROM {rating}');
 434          // Count records in backup_ids_table
 435          $f_forumpost = $DB->count_records('backup_ids_temp', array('backupid' => $backupid, 'itemname' => 'forum_post'));
 436          $f_user1     = $DB->count_records('backup_ids_temp', array('backupid' => $backupid, 'itemname' => 'user1'));
 437          $f_user2     = $DB->count_records('backup_ids_temp', array('backupid' => $backupid, 'itemname' => 'user2'));
 438          $c_notbackupid = $DB->count_records_select('backup_ids_temp', 'backupid != ?', array($backupid));
 439          // Peform tests by comparing counts
 440          $this->assertEquals($c_notbackupid, 0); // there isn't any record with incorrect backupid
 441          $this->assertEquals($c_postsid, $f_forumpost); // All posts have been registered
 442          $this->assertEquals($c_dissuserid, $f_user1); // All users coming from discussions have been registered
 443          $this->assertEquals($c_ratuserid, $f_user2); // All users coming from ratings have been registered
 444  
 445          // Check file annotations against DB
 446          $fannotations = $DB->get_records('backup_ids_temp', array('backupid' => $backupid, 'itemname' => 'file'));
 447          $ffiles       = $DB->get_records('files', array('contextid' => $this->contextid));
 448          $this->assertEquals(count($fannotations), count($ffiles)); // Same number of recs in both (all files have been annotated)
 449          foreach ($fannotations as $annotation) { // Check ids annotated
 450              $this->assertTrue($DB->record_exists('files', array('id' => $annotation->itemid)));
 451          }
 452  
 453          // Drop the backup_ids_temp table
 454          backup_controller_dbops::drop_backup_ids_temp_table('testingid');
 455      }
 456  
 457      /**
 458       * Backup structures wrong tests (trying to do things the wrong way)
 459       */
 460      function test_backup_structure_wrong() {
 461  
 462          // Instantiate the backup processor
 463          $processor = new backup_structure_processor(new xml_writer(new memory_xml_output()));
 464          $this->assertTrue($processor instanceof base_processor);
 465  
 466          // Set one var twice
 467          $processor->set_var('onenewvariable', 999);
 468          try {
 469              $processor->set_var('onenewvariable', 999);
 470              $this->assertTrue(false, 'backup_processor_exception expected');
 471          } catch (exception $e) {
 472              $this->assertTrue($e instanceof backup_processor_exception);
 473              $this->assertEquals($e->errorcode, 'processorvariablealreadyset');
 474              $this->assertEquals($e->a, 'onenewvariable');
 475          }
 476  
 477          // Get non-existing var
 478          try {
 479              $var = $processor->get_var('nonexistingvar');
 480              $this->assertTrue(false, 'backup_processor_exception expected');
 481          } catch (exception $e) {
 482              $this->assertTrue($e instanceof backup_processor_exception);
 483              $this->assertEquals($e->errorcode, 'processorvariablenotfound');
 484              $this->assertEquals($e->a, 'nonexistingvar');
 485          }
 486  
 487          // Create nested element and try ro get its parent id (doesn't exisit => exception)
 488          $ne = new backup_nested_element('test', 'one', 'two', 'three');
 489          try {
 490              $ne->set_source_table('forum', array('id' => backup::VAR_PARENTID));
 491              $ne->process($processor);
 492              $this->assertTrue(false, 'base_element_struct_exception expected');
 493          } catch (exception $e) {
 494              $this->assertTrue($e instanceof base_element_struct_exception);
 495              $this->assertEquals($e->errorcode, 'cannotfindparentidforelement');
 496          }
 497  
 498          // Try to process one nested/final/attribute elements without processor
 499          $ne = new backup_nested_element('test', 'one', 'two', 'three');
 500          try {
 501              $ne->process(new stdclass());
 502              $this->assertTrue(false, 'base_element_struct_exception expected');
 503          } catch (exception $e) {
 504              $this->assertTrue($e instanceof base_element_struct_exception);
 505              $this->assertEquals($e->errorcode, 'incorrect_processor');
 506          }
 507          $fe = new backup_final_element('test');
 508          try {
 509              $fe->process(new stdclass());
 510              $this->assertTrue(false, 'base_element_struct_exception expected');
 511          } catch (exception $e) {
 512              $this->assertTrue($e instanceof base_element_struct_exception);
 513              $this->assertEquals($e->errorcode, 'incorrect_processor');
 514          }
 515          $at = new backup_attribute('test');
 516          try {
 517              $at->process(new stdclass());
 518              $this->assertTrue(false, 'base_element_struct_exception expected');
 519          } catch (exception $e) {
 520              $this->assertTrue($e instanceof base_element_struct_exception);
 521              $this->assertEquals($e->errorcode, 'incorrect_processor');
 522          }
 523  
 524          // Try to put an incorrect alias
 525          $ne = new backup_nested_element('test', 'one', 'two', 'three');
 526          try {
 527              $ne->set_source_alias('last', 'nonexisting');
 528              $this->assertTrue(false, 'base_element_struct_exception expected');
 529          } catch (exception $e) {
 530              $this->assertTrue($e instanceof base_element_struct_exception);
 531              $this->assertEquals($e->errorcode, 'incorrectaliasfinalnamenotfound');
 532              $this->assertEquals($e->a, 'nonexisting');
 533          }
 534  
 535          // Try various incorrect paths specifying source
 536          $ne = new backup_nested_element('test', 'one', 'two', 'three');
 537          try {
 538              $ne->set_source_table('forum', array('/test/subtest'));
 539              $this->assertTrue(false, 'base_element_struct_exception expected');
 540          } catch (exception $e) {
 541              $this->assertTrue($e instanceof base_element_struct_exception);
 542              $this->assertEquals($e->errorcode, 'baseelementincorrectfinalorattribute');
 543              $this->assertEquals($e->a, 'subtest');
 544          }
 545          try {
 546              $ne->set_source_table('forum', array('/wrongtest'));
 547              $this->assertTrue(false, 'base_element_struct_exception expected');
 548          } catch (exception $e) {
 549              $this->assertTrue($e instanceof base_element_struct_exception);
 550              $this->assertEquals($e->errorcode, 'baseelementincorrectgrandparent');
 551              $this->assertEquals($e->a, 'wrongtest');
 552          }
 553          try {
 554              $ne->set_source_table('forum', array('../nonexisting'));
 555              $this->assertTrue(false, 'base_element_struct_exception expected');
 556          } catch (exception $e) {
 557              $this->assertTrue($e instanceof base_element_struct_exception);
 558              $this->assertEquals($e->errorcode, 'baseelementincorrectparent');
 559              $this->assertEquals($e->a, '..');
 560          }
 561  
 562          // Try various incorrect file annotations
 563  
 564          $ne = new backup_nested_element('test', 'one', 'two', 'three');
 565          $ne->annotate_files('test', 'filearea', null);
 566          try {
 567              $ne->annotate_files('test', 'filearea', null); // Try to add annotations twice
 568              $this->assertTrue(false, 'base_element_struct_exception expected');
 569          } catch (exception $e) {
 570              $this->assertTrue($e instanceof base_element_struct_exception);
 571              $this->assertEquals($e->errorcode, 'annotate_files_duplicate_annotation');
 572              $this->assertEquals($e->a, 'test/filearea/');
 573          }
 574  
 575          $ne = new backup_nested_element('test', 'one', 'two', 'three');
 576          try {
 577              $ne->annotate_files('test', 'filearea', 'four'); // Incorrect element
 578              $this->assertTrue(false, 'base_element_struct_exception expected');
 579          } catch (exception $e) {
 580              $this->assertTrue($e instanceof base_element_struct_exception);
 581              $this->assertEquals($e->errorcode, 'baseelementincorrectfinalorattribute');
 582              $this->assertEquals($e->a, 'four');
 583          }
 584  
 585          // Try to add incorrect element to backup_optigroup
 586          $bog = new backup_optigroup('test');
 587          try {
 588              $bog->add_child(new backup_nested_element('test2'));
 589              $this->assertTrue(false, 'base_optigroup_exception expected');
 590          } catch (exception $e) {
 591              $this->assertTrue($e instanceof base_optigroup_exception);
 592              $this->assertEquals($e->errorcode, 'optigroup_element_incorrect');
 593              $this->assertEquals($e->a, 'backup_nested_element');
 594          }
 595  
 596          $bog = new backup_optigroup('test');
 597          try {
 598              $bog->add_child('test2');
 599              $this->assertTrue(false, 'base_optigroup_exception expected');
 600          } catch (exception $e) {
 601              $this->assertTrue($e instanceof base_optigroup_exception);
 602              $this->assertEquals($e->errorcode, 'optigroup_element_incorrect');
 603              $this->assertEquals($e->a, 'non object');
 604          }
 605  
 606          try {
 607              $bog = new backup_optigroup('test', new stdclass());
 608              $this->assertTrue(false, 'base_optigroup_exception expected');
 609          } catch (exception $e) {
 610              $this->assertTrue($e instanceof base_optigroup_exception);
 611              $this->assertEquals($e->errorcode, 'optigroup_elements_incorrect');
 612          }
 613  
 614          // Try a wrong processor with backup_optigroup
 615          $bog = new backup_optigroup('test');
 616          try {
 617              $bog->process(new stdclass());
 618              $this->assertTrue(false, 'base_element_struct_exception expected');
 619          } catch (exception $e) {
 620              $this->assertTrue($e instanceof base_element_struct_exception);
 621              $this->assertEquals($e->errorcode, 'incorrect_processor');
 622          }
 623  
 624          // Try duplicating used elements with backup_optigroup
 625          // Adding top->down
 626          $bog = new backup_optigroup('test', null, true);
 627          $boge1 = new backup_optigroup_element('boge1');
 628          $boge2 = new backup_optigroup_element('boge2');
 629          $ne1 = new backup_nested_element('ne1');
 630          $ne2 = new backup_nested_element('ne1');
 631          $bog->add_child($boge1);
 632          $bog->add_child($boge2);
 633          $boge1->add_child($ne1);
 634          try {
 635              $boge2->add_child($ne2);
 636              $this->assertTrue(false, 'base_optigroup_exception expected');
 637          } catch (exception $e) {
 638              $this->assertTrue($e instanceof base_optigroup_exception);
 639              $this->assertEquals($e->errorcode, 'multiple_optigroup_duplicate_element');
 640              $this->assertEquals($e->a, 'ne1');
 641          }
 642          // Adding down->top
 643          $bog = new backup_optigroup('test', null, true);
 644          $boge1 = new backup_optigroup_element('boge1');
 645          $boge2 = new backup_optigroup_element('boge2');
 646          $ne1 = new backup_nested_element('ne1');
 647          $ne2 = new backup_nested_element('ne1');
 648          $boge1->add_child($ne1);
 649          $boge2->add_child($ne2);
 650          $bog->add_child($boge1);
 651          try {
 652              $bog->add_child($boge2);
 653              $this->assertTrue(false, 'base_element_struct_exception expected');
 654          } catch (exception $e) {
 655              $this->assertTrue($e instanceof base_element_struct_exception);
 656              $this->assertEquals($e->errorcode, 'baseelementexisting');
 657              $this->assertEquals($e->a, 'ne1');
 658          }
 659      }
 660  }