Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * @package core_backup 19 * @category phpunit 20 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 // Include all the needed stuff 27 global $CFG; 28 require_once($CFG->dirroot . '/backup/util/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 & 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 & 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 advanced_testcase 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body