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