Search moodle.org's
Developer Documentation


  • Bug fixes for general core bugs in 2.8.x ended 9 November 2015 (12 months).
  • Bug fixes for security issues in 2.8.x ended 9 May 2016 (18 months).
  • minimum PHP 5.4.4 (always use latest PHP 5.4.x or 5.5.x on Windows - http://windows.php.net/download/), PHP 7 is NOT supported
  • Differences Between: [Versions 28 and 32] [Versions 28 and 33] [Versions 28 and 34] [Versions 28 and 35] [Versions 28 and 36] [Versions 28 and 37]

       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/xml/parser/progressive_parser.class.php');
      29  require_once($CFG->dirroot . '/backup/util/xml/parser/processors/progressive_parser_processor.class.php');
      30  require_once($CFG->dirroot . '/backup/util/xml/parser/processors/simplified_parser_processor.class.php');
      31  require_once($CFG->dirroot . '/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
      32  
      33  /*
      34   * progressive_parser and progressive_parser_processor tests
      35   */
      36  class progressive_parser_test extends advanced_testcase {
      37  
      38      /*
      39       * test progressive_parser public methods
      40       */
      41      function test_parser_public_api() {
      42          global $CFG;
      43          // Instantiate progressive_parser
      44          $pp = new progressive_parser();
      45          $this->assertTrue($pp instanceof progressive_parser);
      46          $pr = new mock_parser_processor();
      47          $this->assertTrue($pr instanceof progressive_parser_processor);
      48  
      49          // Try to process without processor
      50          try {
      51              $pp->process();
      52              $this->assertTrue(false);
      53          } catch (exception $e) {
      54              $this->assertTrue($e instanceof progressive_parser_exception);
      55              $this->assertEquals($e->errorcode, 'undefined_parser_processor');
      56          }
      57  
      58          // Assign processor to parser
      59          $pp->set_processor($pr);
      60  
      61          // Try to process without file and contents
      62          try {
      63              $pp->process();
      64              $this->assertTrue(false);
      65          } catch (exception $e) {
      66              $this->assertTrue($e instanceof progressive_parser_exception);
      67              $this->assertEquals($e->errorcode, 'undefined_xml_to_parse');
      68          }
      69  
      70          // Assign *invalid* processor to parser
      71          try {
      72              $pp->set_processor(new stdClass());
      73              $this->assertTrue(false);
      74          } catch (exception $e) {
      75              $this->assertTrue($e instanceof progressive_parser_exception);
      76              $this->assertEquals($e->errorcode, 'invalid_parser_processor');
      77          }
      78  
      79          // Set file from fixtures (test1.xml) and process it
      80          $pp = new progressive_parser();
      81          $pr = new mock_parser_processor();
      82          $pp->set_processor($pr);
      83          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
      84          $pp->process();
      85          $serfromfile = serialize($pr->get_chunks()); // Get serialized results (to compare later)
      86          // Set *unexisting* file from fixtures
      87          try {
      88              $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test0.xml');
      89              $this->assertTrue(false);
      90          } catch (exception $e) {
      91              $this->assertTrue($e instanceof progressive_parser_exception);
      92              $this->assertEquals($e->errorcode, 'invalid_file_to_parse');
      93          }
      94  
      95          // Set contents from fixtures (test1.xml) and process it
      96          $pp = new progressive_parser();
      97          $pr = new mock_parser_processor();
      98          $pp->set_processor($pr);
      99          $pp->set_contents(file_get_contents($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml'));
     100          $pp->process();
     101          $serfrommemory = serialize($pr->get_chunks()); // Get serialized results (to compare later)
     102          // Set *empty* contents
     103          try {
     104              $pp->set_contents('');
     105              $this->assertTrue(false);
     106          } catch (exception $e) {
     107              $this->assertTrue($e instanceof progressive_parser_exception);
     108              $this->assertEquals($e->errorcode, 'invalid_contents_to_parse');
     109          }
     110  
     111          // Check that both results from file processing and content processing are equal
     112          $this->assertEquals($serfromfile, $serfrommemory);
     113  
     114          // Check case_folding is working ok
     115          $pp = new progressive_parser(true);
     116          $pr = new mock_parser_processor();
     117          $pp->set_processor($pr);
     118          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
     119          $pp->process();
     120          $chunks = $pr->get_chunks();
     121          $this->assertTrue($chunks[0]['path'] === '/FIRSTTAG');
     122          $this->assertTrue($chunks[0]['tags']['SECONDTAG']['name'] === 'SECONDTAG');
     123          $this->assertTrue($chunks[0]['tags']['SECONDTAG']['attrs']['NAME'] === 'secondtag');
     124  
     125          // Check invalid XML exception is working ok
     126          $pp = new progressive_parser(true);
     127          $pr = new mock_parser_processor();
     128          $pp->set_processor($pr);
     129          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test2.xml');
     130          try {
     131              $pp->process();
     132          } catch (exception $e) {
     133              $this->assertTrue($e instanceof progressive_parser_exception);
     134              $this->assertEquals($e->errorcode, 'xml_parsing_error');
     135          }
     136  
     137          // Check double process throws exception
     138          $pp = new progressive_parser(true);
     139          $pr = new mock_parser_processor();
     140          $pp->set_processor($pr);
     141          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
     142          $pp->process();
     143          try { // Second process, will throw exception
     144              $pp->process();
     145              $this->assertTrue(false);
     146          } catch (exception $e) {
     147              $this->assertTrue($e instanceof progressive_parser_exception);
     148              $this->assertEquals($e->errorcode, 'progressive_parser_already_used');
     149          }
     150      }
     151  
     152      /*
     153       * test progressive_parser parsing results using testing_parser_processor and test1.xml
     154       * auto-described file from fixtures
     155       */
     156      function test_parser_results() {
     157          global $CFG;
     158          // Instantiate progressive_parser
     159          $pp = new progressive_parser();
     160          // Instantiate processor, passing the unit test as param
     161          $pr = new mock_auto_parser_processor($this);
     162          $this->assertTrue($pr instanceof progressive_parser_processor);
     163          // Assign processor to parser
     164          $pp->set_processor($pr);
     165          // Set file from fixtures
     166          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test3.xml');
     167          // Process the file, the autotest processor will perform a bunch of automatic tests
     168          $pp->process();
     169          // Get processor debug info
     170          $debug = $pr->debug_info();
     171          $this->assertTrue(is_array($debug));
     172          $this->assertTrue(array_key_exists('chunks', $debug));
     173          // Check the number of chunks is correct for the file
     174          $this->assertEquals($debug['chunks'], 10);
     175      }
     176  
     177      /*
     178       * test progressive_parser parsing results using simplified_parser_processor and test4.xml
     179       * (one simple glossary backup file example)
     180       */
     181      function test_simplified_parser_results() {
     182          global $CFG;
     183          // Instantiate progressive_parser
     184          $pp =  new progressive_parser();
     185          // Instantiate simplified_parser_processor declaring the interesting paths
     186          $pr = new mock_simplified_parser_processor(array(
     187              '/activity',
     188              '/activity/glossary',
     189              '/activity/glossary/entries/entry',
     190              '/activity/glossary/entries/entry/aliases/alias',
     191              '/activity/glossary/entries/entry/ratings/rating',
     192              '/activity/glossary/categories/category',
     193              '/activity/glossary/onetest',
     194              '/activity/glossary/othertest'));
     195          $this->assertTrue($pr instanceof progressive_parser_processor);
     196          // Assign processor to parser
     197          $pp->set_processor($pr);
     198          // Set file from fixtures
     199          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test4.xml');
     200          // Process the file
     201          $pp->process();
     202          // Get processor debug info
     203          $debug = $pr->debug_info();
     204          $this->assertTrue(is_array($debug));
     205          $this->assertTrue(array_key_exists('chunks', $debug));
     206  
     207          // Check the number of chunks is correct for the file
     208          $this->assertEquals($debug['chunks'], 12);
     209          // Get all the simplified chunks and perform various validations
     210          $chunks = $pr->get_chunks();
     211          // Check we have received the correct number of chunks
     212          $this->assertEquals(count($chunks), 12);
     213  
     214          // chunk[0] (/activity) tests
     215          $this->assertEquals(count($chunks[0]), 3);
     216          $this->assertEquals($chunks[0]['path'], '/activity');
     217          $this->assertEquals($chunks[0]['level'],'2');
     218          $tags = $chunks[0]['tags'];
     219          $this->assertEquals(count($tags), 4);
     220          $this->assertEquals($tags['id'], 1);
     221          $this->assertEquals($tags['moduleid'], 5);
     222          $this->assertEquals($tags['modulename'], 'glossary');
     223          $this->assertEquals($tags['contextid'], 26);
     224          $this->assertEquals($chunks[0]['level'],'2');
     225  
     226          // chunk[1] (/activity/glossary) tests
     227          $this->assertEquals(count($chunks[1]), 3);
     228          $this->assertEquals($chunks[1]['path'], '/activity/glossary');
     229          $this->assertEquals($chunks[1]['level'],'3');
     230          $tags = $chunks[1]['tags'];
     231          $this->assertEquals(count($tags), 24);
     232          $this->assertEquals($tags['id'], 1);
     233          $this->assertEquals($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
     234                                             "\n".
     235                                             '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
     236          $this->assertEquals($tags['timemodified'], 1275639747);
     237          $this->assertTrue(!isset($tags['categories']));
     238  
     239          // chunk[5] (second /activity/glossary/entries/entry) tests
     240          $this->assertEquals(count($chunks[5]), 3);
     241          $this->assertEquals($chunks[5]['path'], '/activity/glossary/entries/entry');
     242          $this->assertEquals($chunks[5]['level'],'5');
     243          $tags = $chunks[5]['tags'];
     244          $this->assertEquals(count($tags), 15);
     245          $this->assertEquals($tags['id'], 2);
     246          $this->assertEquals($tags['concept'], 'cat');
     247          $this->assertTrue(!isset($tags['aliases']));
     248          $this->assertTrue(!isset($tags['entries']));
     249  
     250          // chunk[6] (second /activity/glossary/entries/entry/aliases/alias) tests
     251          $this->assertEquals(count($chunks[6]), 3);
     252          $this->assertEquals($chunks[6]['path'], '/activity/glossary/entries/entry/aliases/alias');
     253          $this->assertEquals($chunks[6]['level'],'7');
     254          $tags = $chunks[6]['tags'];
     255          $this->assertEquals(count($tags), 2);
     256          $this->assertEquals($tags['id'], 2);
     257          $this->assertEquals($tags['alias_text'], 'cats');
     258  
     259          // chunk[7] (second /activity/glossary/entries/entry/aliases/alias) tests
     260          $this->assertEquals(count($chunks[7]), 3);
     261          $this->assertEquals($chunks[7]['path'], '/activity/glossary/entries/entry/aliases/alias');
     262          $this->assertEquals($chunks[7]['level'],'7');
     263          $tags = $chunks[7]['tags'];
     264          $this->assertEquals(count($tags), 2);
     265          $this->assertEquals($tags['id'], 3);
     266          $this->assertEquals($tags['alias_text'], 'felines');
     267  
     268          // chunk[8] (second /activity/glossary/entries/entry/ratings/rating) tests
     269          $this->assertEquals(count($chunks[8]), 3);
     270          $this->assertEquals($chunks[8]['path'], '/activity/glossary/entries/entry/ratings/rating');
     271          $this->assertEquals($chunks[8]['level'],'7');
     272          $tags = $chunks[8]['tags'];
     273          $this->assertEquals(count($tags), 6);
     274          $this->assertEquals($tags['id'], 1);
     275          $this->assertEquals($tags['timemodified'], '1275639779');
     276  
     277          // chunk[9] (first /activity/glossary/onetest) tests
     278          $this->assertEquals(count($chunks[9]), 3);
     279          $this->assertEquals($chunks[9]['path'], '/activity/glossary/onetest');
     280          $this->assertEquals($chunks[9]['level'],'4');
     281          $tags = $chunks[9]['tags'];
     282          $this->assertEquals(count($tags), 2);
     283          $this->assertEquals($tags['name'], 1);
     284          $this->assertEquals($tags['value'], 1);
     285  
     286          // chunk[10] (second /activity/glossary/onetest) tests
     287          $this->assertEquals(count($chunks[10]), 3);
     288          $this->assertEquals($chunks[10]['path'], '/activity/glossary/onetest');
     289          $this->assertEquals($chunks[10]['level'],'4');
     290          $tags = $chunks[10]['tags'];
     291          $this->assertEquals(count($tags), 2);
     292          $this->assertEquals($tags['name'], 2);
     293          $this->assertEquals($tags['value'], 2);
     294  
     295          // chunk[11] (first /activity/glossary/othertest) tests
     296          // note we don't allow repeated "final" element, so we only return the last one
     297          $this->assertEquals(count($chunks[11]), 3);
     298          $this->assertEquals($chunks[11]['path'], '/activity/glossary/othertest');
     299          $this->assertEquals($chunks[11]['level'],'4');
     300          $tags = $chunks[11]['tags'];
     301          $this->assertEquals(count($tags), 2);
     302          $this->assertEquals($tags['name'], 4);
     303          $this->assertEquals($tags['value'], 5);
     304  
     305          // Now check start notifications
     306          $snotifs = $pr->get_start_notifications();
     307          // Check we have received the correct number of notifications
     308          $this->assertEquals(count($snotifs), 12);
     309          // Check first, sixth and last notifications
     310          $this->assertEquals($snotifs[0], '/activity');
     311          $this->assertEquals($snotifs[5], '/activity/glossary/entries/entry');
     312          $this->assertEquals($snotifs[11], '/activity/glossary/othertest');
     313  
     314          // Now check end notifications
     315          $enotifs = $pr->get_end_notifications();
     316          // Check we have received the correct number of notifications
     317          $this->assertEquals(count($snotifs), 12);
     318          // Check first, sixth and last notifications
     319          $this->assertEquals($enotifs[0], '/activity/glossary/entries/entry/aliases/alias');
     320          $this->assertEquals($enotifs[5], '/activity/glossary/entries/entry/ratings/rating');
     321          $this->assertEquals($enotifs[11], '/activity');
     322  
     323          // Check start and end notifications are balanced
     324          sort($snotifs);
     325          sort($enotifs);
     326          $this->assertEquals($snotifs, $enotifs);
     327  
     328          // Now verify that the start/process/end order is correct
     329          $allnotifs = $pr->get_all_notifications();
     330          $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
     331          // Check integrity of the notifications
     332          $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
     333          $this->assertEquals($errcount, 0); // No errors found, plz
     334      }
     335  
     336      /**
     337       * test how the simplified processor and the order of start/process/end events happens
     338       * with one real fragment of one backup 1.9 file, where some problems
     339       * were found by David, hence we honor him in the name of the test ;-)
     340       */
     341      function test_simplified_david_backup19_file_fragment() {
     342          global $CFG;
     343          // Instantiate progressive_parser
     344          $pp =  new progressive_parser();
     345          // Instantiate grouped_parser_processor
     346          $pr = new mock_simplified_parser_processor();
     347          // Add interesting paths
     348          $pr->add_path('/MOODLE_BACKUP/COURSE');
     349          $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
     350          $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     351          $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
     352          $this->assertTrue($pr instanceof progressive_parser_processor);
     353          // Assign processor to parser
     354          $pp->set_processor($pr);
     355          // Set file from fixtures
     356          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test5.xml');
     357          // Process the file
     358          $pp->process();
     359  
     360          // Get all the simplified chunks and perform various validations
     361          $chunks = $pr->get_chunks();
     362          $this->assertEquals(count($chunks), 3); // Only 3, because 7 (COURSE, ROLES_OVERRIDES and 5 MOD) are empty, aka no chunk
     363  
     364          // Now check start notifications
     365          $snotifs = $pr->get_start_notifications();
     366          // Check we have received the correct number of notifications
     367          $this->assertEquals(count($snotifs), 10); // Start tags are dispatched for empties (ROLES_OVERRIDES)
     368          // Check first and last notifications
     369          $this->assertEquals($snotifs[0], '/MOODLE_BACKUP/COURSE');
     370          $this->assertEquals($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
     371          $this->assertEquals($snotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     372          $this->assertEquals($snotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
     373          $this->assertEquals($snotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     374          $this->assertEquals($snotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     375          $this->assertEquals($snotifs[9], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     376  
     377          // Now check end notifications
     378          $enotifs = $pr->get_end_notifications();
     379          // Check we have received the correct number of notifications
     380          $this->assertEquals(count($snotifs), 10); // End tags are dispatched for empties (ROLES_OVERRIDES)
     381          // Check first, and last notifications
     382          $this->assertEquals($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
     383          $this->assertEquals($enotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     384          $this->assertEquals($enotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     385          $this->assertEquals($enotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     386          $this->assertEquals($enotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     387          $this->assertEquals($enotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
     388          $this->assertEquals($enotifs[9], '/MOODLE_BACKUP/COURSE');
     389  
     390          // Check start and end notifications are balanced
     391          sort($snotifs);
     392          sort($enotifs);
     393          $this->assertEquals($snotifs, $enotifs);
     394  
     395          // Now verify that the start/process/end order is correct
     396          $allnotifs = $pr->get_all_notifications();
     397          $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
     398          // Check integrity of the notifications
     399          $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
     400          $this->assertEquals($errcount, 0); // No errors found, plz
     401      }
     402  
     403      /*
     404       * test progressive_parser parsing results using grouped_parser_processor and test4.xml
     405       * (one simple glossary backup file example)
     406       */
     407      function test_grouped_parser_results() {
     408          global $CFG;
     409          // Instantiate progressive_parser
     410          $pp =  new progressive_parser();
     411          // Instantiate grouped_parser_processor
     412          $pr = new mock_grouped_parser_processor();
     413          // Add interesting paths
     414          $pr->add_path('/activity');
     415          $pr->add_path('/activity/glossary', true);
     416          $pr->add_path('/activity/glossary/entries/entry');
     417          $pr->add_path('/activity/glossary/entries/entry/aliases/alias');
     418          $pr->add_path('/activity/glossary/entries/entry/ratings/rating');
     419          $pr->add_path('/activity/glossary/categories/category');
     420          $pr->add_path('/activity/glossary/onetest');
     421          $pr->add_path('/activity/glossary/othertest');
     422          $this->assertTrue($pr instanceof progressive_parser_processor);
     423          // Assign processor to parser
     424          $pp->set_processor($pr);
     425          // Set file from fixtures
     426          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test4.xml');
     427          // Process the file
     428          $pp->process();
     429          // Get processor debug info
     430          $debug = $pr->debug_info();
     431          $this->assertTrue(is_array($debug));
     432          $this->assertTrue(array_key_exists('chunks', $debug));
     433  
     434          // Check the number of chunks is correct for the file
     435          $this->assertEquals($debug['chunks'], 2);
     436          // Get all the simplified chunks and perform various validations
     437          $chunks = $pr->get_chunks();
     438          // Check we have received the correct number of chunks
     439          $this->assertEquals(count($chunks), 2);
     440  
     441          // chunk[0] (/activity) tests
     442          $this->assertEquals(count($chunks[0]), 3);
     443          $this->assertEquals($chunks[0]['path'], '/activity');
     444          $this->assertEquals($chunks[0]['level'],'2');
     445          $tags = $chunks[0]['tags'];
     446          $this->assertEquals(count($tags), 4);
     447          $this->assertEquals($tags['id'], 1);
     448          $this->assertEquals($tags['moduleid'], 5);
     449          $this->assertEquals($tags['modulename'], 'glossary');
     450          $this->assertEquals($tags['contextid'], 26);
     451          $this->assertEquals($chunks[0]['level'],'2');
     452  
     453          // chunk[1] (grouped /activity/glossary tests)
     454          $this->assertEquals(count($chunks[1]), 3);
     455          $this->assertEquals($chunks[1]['path'], '/activity/glossary');
     456          $this->assertEquals($chunks[1]['level'],'3');
     457          $tags = $chunks[1]['tags'];
     458          $this->assertEquals(count($tags), 27);
     459          $this->assertEquals($tags['id'], 1);
     460          $this->assertEquals($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
     461                                             "\n".
     462                                             '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
     463          $this->assertEquals($tags['timemodified'], 1275639747);
     464          $this->assertTrue(!isset($tags['categories']));
     465          $this->assertTrue(isset($tags['entries']));
     466          $this->assertTrue(isset($tags['onetest']));
     467          $this->assertTrue(isset($tags['othertest']));
     468  
     469          // Various tests under the entries
     470          $entries = $chunks[1]['tags']['entries']['entry'];
     471          $this->assertEquals(count($entries), 2);
     472  
     473          // First entry
     474          $entry1 = $entries[0];
     475          $this->assertEquals(count($entry1), 17);
     476          $this->assertEquals($entry1['id'], 1);
     477          $this->assertEquals($entry1['userid'], 2);
     478          $this->assertEquals($entry1['concept'], 'dog');
     479          $this->assertEquals($entry1['definition'], '<p>Traditional enemies of cats</p>');
     480          $this->assertTrue(isset($entry1['aliases']));
     481          $this->assertTrue(isset($entry1['ratings']));
     482          // aliases of first entry
     483          $aliases = $entry1['aliases']['alias'];
     484          $this->assertEquals(count($aliases), 1);
     485          // first alias
     486          $alias1 = $aliases[0];
     487          $this->assertEquals(count($alias1), 2);
     488          $this->assertEquals($alias1['id'], 1);
     489          $this->assertEquals($alias1['alias_text'], 'dogs');
     490          // ratings of first entry
     491          $ratings = $entry1['ratings']['rating'];
     492          $this->assertEquals(count($ratings), 1);
     493          // first rating
     494          $rating1 = $ratings[0];
     495          $this->assertEquals(count($rating1), 6);
     496          $this->assertEquals($rating1['id'], 2);
     497          $this->assertEquals($rating1['value'], 6);
     498          $this->assertEquals($rating1['timemodified'], '1275639797');
     499  
     500          // Second entry
     501          $entry2 = $entries[1];
     502          $this->assertEquals(count($entry2), 17);
     503          $this->assertEquals($entry2['id'], 2);
     504          $this->assertEquals($entry2['userid'], 2);
     505          $this->assertEquals($entry2['concept'], 'cat');
     506          $this->assertEquals($entry2['definition'], '<p>traditional enemies of dogs</p>');
     507          $this->assertTrue(isset($entry2['aliases']));
     508          $this->assertTrue(isset($entry2['ratings']));
     509          // aliases of first entry
     510          $aliases = $entry2['aliases']['alias'];
     511          $this->assertEquals(count($aliases), 2);
     512          // first alias
     513          $alias1 = $aliases[0];
     514          $this->assertEquals(count($alias1), 2);
     515          $this->assertEquals($alias1['id'], 2);
     516          $this->assertEquals($alias1['alias_text'], 'cats');
     517          // second alias
     518          $alias2 = $aliases[1];
     519          $this->assertEquals(count($alias2), 2);
     520          $this->assertEquals($alias2['id'], 3);
     521          $this->assertEquals($alias2['alias_text'], 'felines');
     522          // ratings of first entry
     523          $ratings = $entry2['ratings']['rating'];
     524          $this->assertEquals(count($ratings), 1);
     525          // first rating
     526          $rating1 = $ratings[0];
     527          $this->assertEquals(count($rating1), 6);
     528          $this->assertEquals($rating1['id'], 1);
     529          $this->assertEquals($rating1['value'], 5);
     530          $this->assertEquals($rating1['scaleid'], 10);
     531  
     532          // Onetest test (only 1 level nested)
     533          $onetest = $tags['onetest'];
     534          $this->assertEquals(count($onetest), 2);
     535          $this->assertEquals(count($onetest[0]), 2);
     536          $this->assertEquals($onetest[0]['name'], 1);
     537          $this->assertEquals($onetest[0]['value'], 1);
     538          $this->assertEquals(count($onetest[1]), 2);
     539          $this->assertEquals($onetest[1]['name'], 2);
     540          $this->assertEquals($onetest[1]['value'], 2);
     541  
     542          // Other test (0 level nested, only last one is retrieved)
     543          $othertest = $tags['othertest'];
     544          $this->assertEquals(count($othertest), 1);
     545          $this->assertEquals(count($othertest[0]), 2);
     546          $this->assertEquals($othertest[0]['name'], 4);
     547          $this->assertEquals($othertest[0]['value'], 5);
     548  
     549          // Now check start notifications
     550          $snotifs = $pr->get_start_notifications();
     551          // Check we have received the correct number of notifications
     552          $this->assertEquals(count($snotifs), 2);
     553          // Check first and last notifications
     554          $this->assertEquals($snotifs[0], '/activity');
     555          $this->assertEquals($snotifs[1], '/activity/glossary');
     556  
     557          // Now check end notifications
     558          $enotifs = $pr->get_end_notifications();
     559          // Check we have received the correct number of notifications
     560          $this->assertEquals(count($snotifs), 2);
     561          // Check first, and last notifications
     562          $this->assertEquals($enotifs[0], '/activity/glossary');
     563          $this->assertEquals($enotifs[1], '/activity');
     564  
     565          // Check start and end notifications are balanced
     566          sort($snotifs);
     567          sort($enotifs);
     568          $this->assertEquals($snotifs, $enotifs);
     569  
     570          // Now verify that the start/process/end order is correct
     571          $allnotifs = $pr->get_all_notifications();
     572          $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
     573          // Check integrity of the notifications
     574          $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
     575          $this->assertEquals($errcount, 0); // No errors found, plz
     576      }
     577  
     578      /**
     579       * test how the grouped processor and the order of start/process/end events happens
     580       * with one real fragment of one backup 1.9 file, where some problems
     581       * were found by David, hence we honor him in the name of the test ;-)
     582       */
     583      function test_grouped_david_backup19_file_fragment() {
     584          global $CFG;
     585          // Instantiate progressive_parser
     586          $pp =  new progressive_parser();
     587          // Instantiate grouped_parser_processor
     588          $pr = new mock_grouped_parser_processor();
     589          // Add interesting paths
     590          $pr->add_path('/MOODLE_BACKUP/COURSE');
     591          $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION', true);
     592          $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
     593          $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
     594          $this->assertTrue($pr instanceof progressive_parser_processor);
     595          // Assign processor to parser
     596          $pp->set_processor($pr);
     597          // Set file from fixtures
     598          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test5.xml');
     599          // Process the file
     600          $pp->process();
     601  
     602          // Get all the simplified chunks and perform various validations
     603          $chunks = $pr->get_chunks();
     604          $this->assertEquals(count($chunks), 1); // Only 1, the SECTION one
     605  
     606          // Now check start notifications
     607          $snotifs = $pr->get_start_notifications();
     608          // Check we have received the correct number of notifications
     609          $this->assertEquals(count($snotifs), 2);
     610          // Check first and last notifications
     611          $this->assertEquals($snotifs[0], '/MOODLE_BACKUP/COURSE');
     612          $this->assertEquals($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
     613  
     614          // Now check end notifications
     615          $enotifs = $pr->get_end_notifications();
     616          // Check we have received the correct number of notifications
     617          $this->assertEquals(count($snotifs), 2); // End tags are dispatched for empties (ROLES_OVERRIDES)
     618          // Check first, and last notifications
     619          $this->assertEquals($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
     620          $this->assertEquals($enotifs[1], '/MOODLE_BACKUP/COURSE');
     621  
     622          // Check start and end notifications are balanced
     623          sort($snotifs);
     624          sort($enotifs);
     625          $this->assertEquals($snotifs, $enotifs);
     626  
     627          // Now verify that the start/process/end order is correct
     628          $allnotifs = $pr->get_all_notifications();
     629          $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
     630          // Check integrity of the notifications
     631          $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
     632          $this->assertEquals($errcount, 0); // No errors found, plz
     633      }
     634  
     635      /**
     636       */
     637      function test_grouped_at_empty_node() {
     638          global $CFG;
     639          // Instantiate progressive_parser.
     640          $pp =  new progressive_parser();
     641          // Instantiate grouped_parser_processor.
     642          $pr = new mock_grouped_parser_processor();
     643          $this->assertTrue($pr instanceof progressive_parser_processor);
     644          // Add interesting paths - moodle1 style.
     645          $pr->add_path('/test/MOODLE_BACKUP/COURSE/FORMATDATA', true);
     646          $pr->add_path('/test/MOODLE_BACKUP/COURSE/FORMATDATA/WEEKS/WEEK');
     647          $pr->add_path('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', true);
     648          $pr->add_path('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', true);
     649          $pr->add_path('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED/SUBS/SUB');
     650          // Add interesting paths - moodle2 style.
     651          $pr->add_path('/test/moodle2/grouped', true);
     652          $pr->add_path('/test/moodle2/grouped/subs/sub');
     653          $pr->add_path('/test/moodle2/groupedemptywithattr', true);
     654          $pr->add_path('/test/moodle2/groupednonemptywithattr', true);
     655          $pr->add_path('/test/moodle2/groupednonemptywithattr/subs/sub');
     656          // Assign processor to parser.
     657          $pp->set_processor($pr);
     658          // Set file from fixtures.
     659          $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test6.xml');
     660          // Process the file.
     661          $pp->process();
     662  
     663          // Get all the simplified chunks and perform various validations.
     664          $chunks = $pr->get_chunks();
     665          $this->assertEquals(count($chunks), 6); // All grouped elements.
     666  
     667          // Check some random data.
     668          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/FORMATDATA', $chunks[0]['path']);
     669          $this->assertEquals(2, $chunks[0]['tags']['WEEKS']['WEEK'][1]['SECTION']);
     670  
     671          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', $chunks[1]['path']);
     672          $this->assertEquals(array(), $chunks[1]['tags']);
     673  
     674          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', $chunks[2]['path']);
     675          $this->assertEquals('Unit tests rock!', $chunks[2]['tags']['SUBS']['SUB'][0]['PROP']);
     676  
     677          $this->assertEquals('/test/moodle2/grouped', $chunks[3]['path']);
     678          $this->assertFalse(isset($chunks[3]['tags']['id'])); // No final elements, this should be fixed one day.
     679          $this->assertEquals(34, $chunks[3]['tags']['subs']['sub'][0]['id']); // We have final element so this is parsed.
     680          $this->assertEquals('Oh yeah', $chunks[3]['tags']['subs']['sub'][0]['prop']);
     681  
     682          $this->assertEquals('/test/moodle2/groupednonemptywithattr', $chunks[4]['path']);
     683          $this->assertEquals(78, $chunks[4]['tags']['id']); // We have final element so this is parsed.
     684          $this->assertEquals('Go baby go', $chunks[4]['tags']['prop']);
     685          $this->assertEquals(89, $chunks[4]['tags']['subs']['sub'][0]['id']);
     686          $this->assertEquals('http://moodle.org', $chunks[4]['tags']['subs']['sub'][0]['prop']);
     687  
     688          $this->assertEquals('/test/moodle2/groupedemptywithattr', $chunks[5]['path']);
     689          $this->assertFalse(isset($chunks[5]['tags']['attr'])); // No final elements, this should be fixed one day.
     690  
     691          // Now check start notifications.
     692          $snotifs = $pr->get_start_notifications();
     693          // Check we have received the correct number of notifications.
     694          $this->assertEquals(count($snotifs), 6);
     695          // Check the order of notifications (in order they appear in test6.xml).
     696          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/FORMATDATA', $snotifs[0]);
     697          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', $snotifs[1]);
     698          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', $snotifs[2]);
     699          $this->assertEquals('/test/moodle2/grouped', $snotifs[3]);
     700          $this->assertEquals('/test/moodle2/groupednonemptywithattr', $snotifs[4]);
     701          $this->assertEquals('/test/moodle2/groupedemptywithattr', $snotifs[5]);
     702  
     703          // Now check end notifications.
     704          $enotifs = $pr->get_end_notifications();
     705          // Check we have received the correct number of notifications.
     706          $this->assertEquals(count($enotifs), 6);
     707          // Check the order of notifications (in order they appear in test6.xml).
     708          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/FORMATDATA', $enotifs[0]);
     709          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', $enotifs[1]);
     710          $this->assertEquals('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', $enotifs[2]);
     711          $this->assertEquals('/test/moodle2/grouped', $enotifs[3]);
     712          $this->assertEquals('/test/moodle2/groupednonemptywithattr', $enotifs[4]);
     713          $this->assertEquals('/test/moodle2/groupedemptywithattr', $enotifs[5]);
     714  
     715          // Now verify that the start/process/end order is correct.
     716          $allnotifs = $pr->get_all_notifications();
     717          $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks));
     718          // Check integrity of the notifications.
     719          $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
     720          $this->assertEquals(0, $errcount);
     721      }
     722  
     723      /**
     724       * Helper function that given one array of ordered start/process/end notifications will
     725       * check it of integrity like:
     726       *    - process only happens if start is the previous notification
     727       *    - end only happens if dispatch is the previous notification
     728       *    - start only happen with level > than last one and if there is no already started like that
     729       *
     730       * @param array $notifications ordered array of notifications with format [start|process|end]:path
     731       * @return int number of integrity problems found (errors)
     732       */
     733      function helper_check_notifications_order_integrity($notifications) {
     734          $numerrors = 0;
     735          $notifpile = array('pilebase' => 'start');
     736          $lastnotif = 'start:pilebase';
     737          foreach ($notifications as $notif) {
     738  
     739              $lastpiletype = end($notifpile);
     740              $lastpilepath = key($notifpile);
     741              $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
     742  
     743              $lastnotiftype  = preg_replace('/:.*/', '', $lastnotif);
     744              $lastnotifpath  = preg_replace('/.*:/', '', $lastnotif);
     745              $lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
     746  
     747              $notiftype  = preg_replace('/:.*/', '', $notif);
     748              $notifpath  = preg_replace('/.*:/', '', $notif);
     749              $notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
     750  
     751              switch ($notiftype) {
     752                  case 'process':
     753                      if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
     754                          $numerrors++; // Only start for same path from last notification is allowed before process
     755                      }
     756                      $notifpile[$notifpath] = 'process'; // Update the status in the pile
     757                      break;
     758                  case 'end':
     759                      if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
     760                          $numerrors++; // Only process and start for same path from last pile is allowed before end
     761                      }
     762                      unset($notifpile[$notifpath]); // Delete from the pile
     763                      break;
     764                  case 'start':
     765                      if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
     766                          $numerrors++; // Only non existing in pile and with level > last pile is allowed on start
     767                      }
     768                      $notifpile[$notifpath] = 'start'; // Add to the pile
     769                      break;
     770                  default:
     771                      $numerrors++; // Incorrect type of notification => error
     772              }
     773              // Update lastnotif
     774              $lastnotif = $notif;
     775          }
     776          return $numerrors;
     777      }
     778  }
     779  
     780  /*
     781   * helper processor able to perform various auto-cheks based on attributes while processing
     782   * the test1.xml file available in the fixtures dir. It performs these checks:
     783   *    - name equal to "name" attribute of the tag (if present)
     784   *    - level equal to "level" attribute of the tag (if present)
     785   *    - path + tagname equal to "path" attribute of the tag (if present)
     786   *    - cdata, if not empty is:
     787   *        - equal to "value" attribute of the tag (if present)
     788   *        - else, equal to tag name
     789   *
     790   * We pass the whole UnitTestCase object to the processor in order to be
     791   * able to perform the tests in the straight in the process
     792   */
     793  class mock_auto_parser_processor extends progressive_parser_processor {
     794  
     795      private $utc = null; // To store the unit test case
     796  
     797      public function __construct($unit_test_case) {
     798          parent::__construct();
     799          $this->utc = $unit_test_case;
     800      }
     801  
     802      public function process_chunk($data) {
     803          // Perform auto-checks based in the rules above
     804          if (isset($data['tags'])) {
     805              foreach ($data['tags'] as $tag) {
     806                  if (isset($tag['attrs']['name'])) { // name tests
     807                      $this->utc->assertEquals($tag['name'], $tag['attrs']['name']);
     808                  }
     809                  if (isset($tag['attrs']['level'])) { // level tests
     810                      $this->utc->assertEquals($data['level'], $tag['attrs']['level']);
     811                  }
     812                  if (isset($tag['attrs']['path'])) { // path tests
     813                      $this->utc->assertEquals(rtrim($data['path'], '/') . '/' . $tag['name'], $tag['attrs']['path']);
     814                  }
     815                  if (!empty($tag['cdata'])) { // cdata tests
     816                      if (isset($tag['attrs']['value'])) {
     817                          $this->utc->assertEquals($tag['cdata'], $tag['attrs']['value']);
     818                      } else {
     819                          $this->utc->assertEquals($tag['cdata'], $tag['name']);
     820                      }
     821                  }
     822              }
     823          }
     824      }
     825  }
     826  
     827  /*
     828   * helper processor that accumulates all the chunks, resturning them with the get_chunks() method
     829   */
     830  class mock_parser_processor extends progressive_parser_processor {
     831  
     832      private $chunksarr = array(); // To accumulate the found chunks
     833  
     834      public function process_chunk($data) {
     835          $this->chunksarr[] = $data;
     836      }
     837  
     838      public function get_chunks() {
     839          return $this->chunksarr;
     840      }
     841  }
     842  
     843  /*
     844   * helper processor that accumulates simplified chunks, returning them with the get_chunks() method
     845   */
     846  class mock_simplified_parser_processor extends simplified_parser_processor {
     847  
     848      private $chunksarr = array(); // To accumulate the found chunks
     849      private $startarr  = array(); // To accumulate all the notified path starts
     850      private $endarr    = array(); // To accumulate all the notified path ends
     851      private $allnotif  = array(); // To accumulate all the notified and dispatched events in an ordered way
     852  
     853      public function dispatch_chunk($data) {
     854          $this->chunksarr[] = $data;
     855          $this->allnotif[] = 'process:' . $data['path'];
     856      }
     857  
     858      public function notify_path_start($path) {
     859          $this->startarr[] = $path;
     860          $this->allnotif[] = 'start:' . $path;
     861      }
     862  
     863      public function notify_path_end($path) {
     864          $this->endarr[] = $path;
     865          $this->allnotif[] = 'end:' . $path;
     866      }
     867  
     868      public function get_chunks() {
     869          return $this->chunksarr;
     870      }
     871  
     872      public function get_start_notifications() {
     873          return $this->startarr;
     874      }
     875  
     876      public function get_end_notifications() {
     877          return $this->endarr;
     878      }
     879  
     880      public function get_all_notifications() {
     881          return $this->allnotif;
     882      }
     883  }
     884  
     885  /*
     886   * helper processor that accumulates grouped chunks, returning them with the get_chunks() method
     887   */
     888  class mock_grouped_parser_processor extends grouped_parser_processor {
     889  
     890      private $chunksarr = array(); // To accumulate the found chunks
     891      private $startarr  = array(); // To accumulate all the notified path starts
     892      private $endarr    = array(); // To accumulate all the notified path ends
     893      private $allnotif  = array(); // To accumulate all the notified and dispatched events in an ordered way
     894  
     895      public function dispatch_chunk($data) {
     896          $this->chunksarr[] = $data;
     897          $this->allnotif[] = 'process:' . $data['path'];
     898      }
     899  
     900      public function notify_path_start($path) {
     901          $this->startarr[] = $path;
     902          $this->allnotif[] = 'start:' . $path;
     903      }
     904  
     905      public function notify_path_end($path) {
     906          $this->endarr[] = $path;
     907          $this->allnotif[] = 'end:' . $path;
     908      }
     909  
     910      public function get_chunks() {
     911          return $this->chunksarr;
     912      }
     913  
     914      public function get_start_notifications() {
     915          return $this->startarr;
     916      }
     917  
     918      public function get_end_notifications() {
     919          return $this->endarr;
     920      }
     921  
     922      public function get_all_notifications() {
     923          return $this->allnotif;
     924      }
     925  }
    

    Search This Site: