weekly release 2.3.4+
[moodle.git] / backup / util / xml / parser / tests / parser_test.php
CommitLineData
be866f9d 1<?php
be866f9d
EL
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/**
bb7898c6
PS
18 * @package core_backup
19 * @category phpunit
be866f9d
EL
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
bb7898c6 24defined('MOODLE_INTERNAL') || die();
be866f9d
EL
25
26// Include all the needed stuff
bb7898c6 27global $CFG;
be866f9d
EL
28require_once($CFG->dirroot . '/backup/util/xml/parser/progressive_parser.class.php');
29require_once($CFG->dirroot . '/backup/util/xml/parser/processors/progressive_parser_processor.class.php');
30require_once($CFG->dirroot . '/backup/util/xml/parser/processors/simplified_parser_processor.class.php');
4c7f6ac6 31require_once($CFG->dirroot . '/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
be866f9d
EL
32
33/*
34 * progressive_parser and progressive_parser_processor tests
35 */
bb7898c6 36class progressive_parser_test extends advanced_testcase {
be866f9d
EL
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);
bb7898c6 55 $this->assertEquals($e->errorcode, 'undefined_parser_processor');
be866f9d
EL
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);
bb7898c6 67 $this->assertEquals($e->errorcode, 'undefined_xml_to_parse');
be866f9d
EL
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);
bb7898c6 76 $this->assertEquals($e->errorcode, 'invalid_parser_processor');
be866f9d
EL
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);
bb7898c6 83 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
be866f9d
EL
84 $pp->process();
85 $serfromfile = serialize($pr->get_chunks()); // Get serialized results (to compare later)
86 // Set *unexisting* file from fixtures
87 try {
bb7898c6 88 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test0.xml');
be866f9d
EL
89 $this->assertTrue(false);
90 } catch (exception $e) {
91 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 92 $this->assertEquals($e->errorcode, 'invalid_file_to_parse');
be866f9d
EL
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);
bb7898c6 99 $pp->set_contents(file_get_contents($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml'));
be866f9d
EL
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);
bb7898c6 108 $this->assertEquals($e->errorcode, 'invalid_contents_to_parse');
be866f9d
EL
109 }
110
111 // Check that both results from file processing and content processing are equal
bb7898c6 112 $this->assertEquals($serfromfile, $serfrommemory);
be866f9d
EL
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);
bb7898c6 118 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
be866f9d
EL
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);
bb7898c6 129 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test2.xml');
be866f9d
EL
130 try {
131 $pp->process();
132 } catch (exception $e) {
133 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 134 $this->assertEquals($e->errorcode, 'xml_parsing_error');
be866f9d
EL
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);
bb7898c6 141 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
be866f9d
EL
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);
bb7898c6 148 $this->assertEquals($e->errorcode, 'progressive_parser_already_used');
be866f9d
EL
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
bb7898c6 166 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test3.xml');
be866f9d
EL
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
bb7898c6 174 $this->assertEquals($debug['chunks'], 10);
be866f9d
EL
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',
4c7f6ac6
EL
192 '/activity/glossary/categories/category',
193 '/activity/glossary/onetest',
194 '/activity/glossary/othertest'));
be866f9d
EL
195 $this->assertTrue($pr instanceof progressive_parser_processor);
196 // Assign processor to parser
197 $pp->set_processor($pr);
198 // Set file from fixtures
bb7898c6 199 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test4.xml');
be866f9d
EL
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
bb7898c6 208 $this->assertEquals($debug['chunks'], 12);
be866f9d
EL
209 // Get all the simplified chunks and perform various validations
210 $chunks = $pr->get_chunks();
4c7f6ac6 211 // Check we have received the correct number of chunks
bb7898c6 212 $this->assertEquals(count($chunks), 12);
be866f9d
EL
213
214 // chunk[0] (/activity) tests
bb7898c6
PS
215 $this->assertEquals(count($chunks[0]), 3);
216 $this->assertEquals($chunks[0]['path'], '/activity');
217 $this->assertEquals($chunks[0]['level'],'2');
be866f9d 218 $tags = $chunks[0]['tags'];
bb7898c6
PS
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');
be866f9d
EL
225
226 // chunk[1] (/activity/glossary) tests
bb7898c6
PS
227 $this->assertEquals(count($chunks[1]), 3);
228 $this->assertEquals($chunks[1]['path'], '/activity/glossary');
229 $this->assertEquals($chunks[1]['level'],'3');
be866f9d 230 $tags = $chunks[1]['tags'];
bb7898c6
PS
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>'.
be866f9d
EL
234 "\n".
235 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
bb7898c6 236 $this->assertEquals($tags['timemodified'], 1275639747);
be866f9d
EL
237 $this->assertTrue(!isset($tags['categories']));
238
239 // chunk[5] (second /activity/glossary/entries/entry) tests
bb7898c6
PS
240 $this->assertEquals(count($chunks[5]), 3);
241 $this->assertEquals($chunks[5]['path'], '/activity/glossary/entries/entry');
242 $this->assertEquals($chunks[5]['level'],'5');
be866f9d 243 $tags = $chunks[5]['tags'];
bb7898c6
PS
244 $this->assertEquals(count($tags), 15);
245 $this->assertEquals($tags['id'], 2);
246 $this->assertEquals($tags['concept'], 'cat');
be866f9d
EL
247 $this->assertTrue(!isset($tags['aliases']));
248 $this->assertTrue(!isset($tags['entries']));
249
250 // chunk[6] (second /activity/glossary/entries/entry/aliases/alias) tests
bb7898c6
PS
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');
be866f9d 254 $tags = $chunks[6]['tags'];
bb7898c6
PS
255 $this->assertEquals(count($tags), 2);
256 $this->assertEquals($tags['id'], 2);
257 $this->assertEquals($tags['alias_text'], 'cats');
be866f9d 258
4c7f6ac6 259 // chunk[7] (second /activity/glossary/entries/entry/aliases/alias) tests
bb7898c6
PS
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');
be866f9d 263 $tags = $chunks[7]['tags'];
bb7898c6
PS
264 $this->assertEquals(count($tags), 2);
265 $this->assertEquals($tags['id'], 3);
266 $this->assertEquals($tags['alias_text'], 'felines');
4c7f6ac6
EL
267
268 // chunk[8] (second /activity/glossary/entries/entry/ratings/rating) tests
bb7898c6
PS
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');
4c7f6ac6 272 $tags = $chunks[8]['tags'];
bb7898c6
PS
273 $this->assertEquals(count($tags), 6);
274 $this->assertEquals($tags['id'], 1);
275 $this->assertEquals($tags['timemodified'], '1275639779');
4c7f6ac6
EL
276
277 // chunk[9] (first /activity/glossary/onetest) tests
bb7898c6
PS
278 $this->assertEquals(count($chunks[9]), 3);
279 $this->assertEquals($chunks[9]['path'], '/activity/glossary/onetest');
280 $this->assertEquals($chunks[9]['level'],'4');
4c7f6ac6 281 $tags = $chunks[9]['tags'];
bb7898c6
PS
282 $this->assertEquals(count($tags), 2);
283 $this->assertEquals($tags['name'], 1);
284 $this->assertEquals($tags['value'], 1);
4c7f6ac6
EL
285
286 // chunk[10] (second /activity/glossary/onetest) tests
bb7898c6
PS
287 $this->assertEquals(count($chunks[10]), 3);
288 $this->assertEquals($chunks[10]['path'], '/activity/glossary/onetest');
289 $this->assertEquals($chunks[10]['level'],'4');
4c7f6ac6 290 $tags = $chunks[10]['tags'];
bb7898c6
PS
291 $this->assertEquals(count($tags), 2);
292 $this->assertEquals($tags['name'], 2);
293 $this->assertEquals($tags['value'], 2);
4c7f6ac6
EL
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
bb7898c6
PS
297 $this->assertEquals(count($chunks[11]), 3);
298 $this->assertEquals($chunks[11]['path'], '/activity/glossary/othertest');
299 $this->assertEquals($chunks[11]['level'],'4');
4c7f6ac6 300 $tags = $chunks[11]['tags'];
bb7898c6
PS
301 $this->assertEquals(count($tags), 2);
302 $this->assertEquals($tags['name'], 4);
303 $this->assertEquals($tags['value'], 5);
14e6710d
EL
304
305 // Now check start notifications
306 $snotifs = $pr->get_start_notifications();
307 // Check we have received the correct number of notifications
bb7898c6 308 $this->assertEquals(count($snotifs), 12);
14e6710d 309 // Check first, sixth and last notifications
bb7898c6
PS
310 $this->assertEquals($snotifs[0], '/activity');
311 $this->assertEquals($snotifs[5], '/activity/glossary/entries/entry');
312 $this->assertEquals($snotifs[11], '/activity/glossary/othertest');
14e6710d
EL
313
314 // Now check end notifications
315 $enotifs = $pr->get_end_notifications();
316 // Check we have received the correct number of notifications
bb7898c6 317 $this->assertEquals(count($snotifs), 12);
14e6710d 318 // Check first, sixth and last notifications
bb7898c6
PS
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');
14e6710d
EL
322
323 // Check start and end notifications are balanced
324 sort($snotifs);
325 sort($enotifs);
bb7898c6 326 $this->assertEquals($snotifs, $enotifs);
8298beda
EL
327
328 // Now verify that the start/process/end order is correct
329 $allnotifs = $pr->get_all_notifications();
bb7898c6 330 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
8298beda
EL
331 // Check integrity of the notifications
332 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 333 $this->assertEquals($errcount, 0); // No errors found, plz
4c7f6ac6
EL
334 }
335
54b8f334
EL
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
bb7898c6 356 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test5.xml');
54b8f334
EL
357 // Process the file
358 $pp->process();
359
360 // Get all the simplified chunks and perform various validations
361 $chunks = $pr->get_chunks();
bb7898c6 362 $this->assertEquals(count($chunks), 3); // Only 3, because 7 (COURSE, ROLES_OVERRIDES and 5 MOD) are empty, aka no chunk
54b8f334
EL
363
364 // Now check start notifications
365 $snotifs = $pr->get_start_notifications();
366 // Check we have received the correct number of notifications
bb7898c6 367 $this->assertEquals(count($snotifs), 10); // Start tags are dispatched for empties (ROLES_OVERRIDES)
54b8f334 368 // Check first and last notifications
bb7898c6
PS
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');
54b8f334
EL
376
377 // Now check end notifications
378 $enotifs = $pr->get_end_notifications();
379 // Check we have received the correct number of notifications
bb7898c6 380 $this->assertEquals(count($snotifs), 10); // End tags are dispatched for empties (ROLES_OVERRIDES)
54b8f334 381 // Check first, and last notifications
bb7898c6
PS
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');
54b8f334
EL
389
390 // Check start and end notifications are balanced
391 sort($snotifs);
392 sort($enotifs);
bb7898c6 393 $this->assertEquals($snotifs, $enotifs);
54b8f334
EL
394
395 // Now verify that the start/process/end order is correct
396 $allnotifs = $pr->get_all_notifications();
bb7898c6 397 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
54b8f334
EL
398 // Check integrity of the notifications
399 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 400 $this->assertEquals($errcount, 0); // No errors found, plz
54b8f334
EL
401 }
402
4c7f6ac6
EL
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
bb7898c6 426 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test4.xml');
4c7f6ac6
EL
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
bb7898c6 435 $this->assertEquals($debug['chunks'], 2);
4c7f6ac6
EL
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
bb7898c6 439 $this->assertEquals(count($chunks), 2);
4c7f6ac6
EL
440
441 // chunk[0] (/activity) tests
bb7898c6
PS
442 $this->assertEquals(count($chunks[0]), 3);
443 $this->assertEquals($chunks[0]['path'], '/activity');
444 $this->assertEquals($chunks[0]['level'],'2');
4c7f6ac6 445 $tags = $chunks[0]['tags'];
bb7898c6
PS
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');
4c7f6ac6
EL
452
453 // chunk[1] (grouped /activity/glossary tests)
bb7898c6
PS
454 $this->assertEquals(count($chunks[1]), 3);
455 $this->assertEquals($chunks[1]['path'], '/activity/glossary');
456 $this->assertEquals($chunks[1]['level'],'3');
4c7f6ac6 457 $tags = $chunks[1]['tags'];
bb7898c6
PS
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>'.
4c7f6ac6
EL
461 "\n".
462 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
bb7898c6 463 $this->assertEquals($tags['timemodified'], 1275639747);
4c7f6ac6
EL
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'];
bb7898c6 471 $this->assertEquals(count($entries), 2);
4c7f6ac6
EL
472
473 // First entry
474 $entry1 = $entries[0];
bb7898c6
PS
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>');
4c7f6ac6
EL
480 $this->assertTrue(isset($entry1['aliases']));
481 $this->assertTrue(isset($entry1['ratings']));
482 // aliases of first entry
483 $aliases = $entry1['aliases']['alias'];
bb7898c6 484 $this->assertEquals(count($aliases), 1);
4c7f6ac6
EL
485 // first alias
486 $alias1 = $aliases[0];
bb7898c6
PS
487 $this->assertEquals(count($alias1), 2);
488 $this->assertEquals($alias1['id'], 1);
489 $this->assertEquals($alias1['alias_text'], 'dogs');
4c7f6ac6
EL
490 // ratings of first entry
491 $ratings = $entry1['ratings']['rating'];
bb7898c6 492 $this->assertEquals(count($ratings), 1);
4c7f6ac6
EL
493 // first rating
494 $rating1 = $ratings[0];
bb7898c6
PS
495 $this->assertEquals(count($rating1), 6);
496 $this->assertEquals($rating1['id'], 2);
497 $this->assertEquals($rating1['value'], 6);
498 $this->assertEquals($rating1['timemodified'], '1275639797');
4c7f6ac6
EL
499
500 // Second entry
501 $entry2 = $entries[1];
bb7898c6
PS
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>');
4c7f6ac6
EL
507 $this->assertTrue(isset($entry2['aliases']));
508 $this->assertTrue(isset($entry2['ratings']));
509 // aliases of first entry
510 $aliases = $entry2['aliases']['alias'];
bb7898c6 511 $this->assertEquals(count($aliases), 2);
4c7f6ac6
EL
512 // first alias
513 $alias1 = $aliases[0];
bb7898c6
PS
514 $this->assertEquals(count($alias1), 2);
515 $this->assertEquals($alias1['id'], 2);
516 $this->assertEquals($alias1['alias_text'], 'cats');
4c7f6ac6
EL
517 // second alias
518 $alias2 = $aliases[1];
bb7898c6
PS
519 $this->assertEquals(count($alias2), 2);
520 $this->assertEquals($alias2['id'], 3);
521 $this->assertEquals($alias2['alias_text'], 'felines');
4c7f6ac6
EL
522 // ratings of first entry
523 $ratings = $entry2['ratings']['rating'];
bb7898c6 524 $this->assertEquals(count($ratings), 1);
4c7f6ac6
EL
525 // first rating
526 $rating1 = $ratings[0];
bb7898c6
PS
527 $this->assertEquals(count($rating1), 6);
528 $this->assertEquals($rating1['id'], 1);
529 $this->assertEquals($rating1['value'], 5);
530 $this->assertEquals($rating1['scaleid'], 10);
4c7f6ac6
EL
531
532 // Onetest test (only 1 level nested)
533 $onetest = $tags['onetest'];
bb7898c6
PS
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);
4c7f6ac6
EL
541
542 // Other test (0 level nested, only last one is retrieved)
543 $othertest = $tags['othertest'];
bb7898c6
PS
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);
14e6710d
EL
548
549 // Now check start notifications
550 $snotifs = $pr->get_start_notifications();
551 // Check we have received the correct number of notifications
bb7898c6 552 $this->assertEquals(count($snotifs), 2);
14e6710d 553 // Check first and last notifications
bb7898c6
PS
554 $this->assertEquals($snotifs[0], '/activity');
555 $this->assertEquals($snotifs[1], '/activity/glossary');
14e6710d
EL
556
557 // Now check end notifications
558 $enotifs = $pr->get_end_notifications();
559 // Check we have received the correct number of notifications
bb7898c6 560 $this->assertEquals(count($snotifs), 2);
14e6710d 561 // Check first, and last notifications
bb7898c6
PS
562 $this->assertEquals($enotifs[0], '/activity/glossary');
563 $this->assertEquals($enotifs[1], '/activity');
14e6710d
EL
564
565 // Check start and end notifications are balanced
566 sort($snotifs);
567 sort($enotifs);
bb7898c6 568 $this->assertEquals($snotifs, $enotifs);
8298beda
EL
569
570 // Now verify that the start/process/end order is correct
571 $allnotifs = $pr->get_all_notifications();
bb7898c6 572 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
8298beda
EL
573 // Check integrity of the notifications
574 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 575 $this->assertEquals($errcount, 0); // No errors found, plz
8298beda
EL
576 }
577
54b8f334
EL
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
bb7898c6 598 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test5.xml');
54b8f334
EL
599 // Process the file
600 $pp->process();
601
602 // Get all the simplified chunks and perform various validations
603 $chunks = $pr->get_chunks();
bb7898c6 604 $this->assertEquals(count($chunks), 1); // Only 1, the SECTION one
54b8f334
EL
605
606 // Now check start notifications
607 $snotifs = $pr->get_start_notifications();
608 // Check we have received the correct number of notifications
bb7898c6 609 $this->assertEquals(count($snotifs), 2);
54b8f334 610 // Check first and last notifications
bb7898c6
PS
611 $this->assertEquals($snotifs[0], '/MOODLE_BACKUP/COURSE');
612 $this->assertEquals($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
54b8f334
EL
613
614 // Now check end notifications
615 $enotifs = $pr->get_end_notifications();
616 // Check we have received the correct number of notifications
bb7898c6 617 $this->assertEquals(count($snotifs), 2); // End tags are dispatched for empties (ROLES_OVERRIDES)
54b8f334 618 // Check first, and last notifications
bb7898c6
PS
619 $this->assertEquals($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
620 $this->assertEquals($enotifs[1], '/MOODLE_BACKUP/COURSE');
54b8f334
EL
621
622 // Check start and end notifications are balanced
623 sort($snotifs);
624 sort($enotifs);
bb7898c6 625 $this->assertEquals($snotifs, $enotifs);
54b8f334
EL
626
627 // Now verify that the start/process/end order is correct
628 $allnotifs = $pr->get_all_notifications();
bb7898c6 629 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
54b8f334
EL
630 // Check integrity of the notifications
631 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 632 $this->assertEquals($errcount, 0); // No errors found, plz
54b8f334
EL
633 }
634
635
8298beda
EL
636 /**
637 * Helper function that given one array of ordered start/process/end notifications will
638 * check it of integrity like:
639 * - process only happens if start is the previous notification
640 * - end only happens if dispatch is the previous notification
641 * - start only happen with level > than last one and if there is no already started like that
642 *
643 * @param array $notifications ordered array of notifications with format [start|process|end]:path
644 * @return int number of integrity problems found (errors)
645 */
646 function helper_check_notifications_order_integrity($notifications) {
647 $numerrors = 0;
648 $notifpile = array('pilebase' => 'start');
d4dd0659 649 $lastnotif = 'start:pilebase';
8298beda 650 foreach ($notifications as $notif) {
8298beda 651
d4dd0659
EL
652 $lastpiletype = end($notifpile);
653 $lastpilepath = key($notifpile);
654 $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
655
656 $lastnotiftype = preg_replace('/:.*/', '', $lastnotif);
657 $lastnotifpath = preg_replace('/.*:/', '', $lastnotif);
658 $lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
659
8298beda
EL
660 $notiftype = preg_replace('/:.*/', '', $notif);
661 $notifpath = preg_replace('/.*:/', '', $notif);
d4dd0659 662 $notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
8298beda
EL
663
664 switch ($notiftype) {
665 case 'process':
d4dd0659
EL
666 if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
667 $numerrors++; // Only start for same path from last notification is allowed before process
8298beda
EL
668 }
669 $notifpile[$notifpath] = 'process'; // Update the status in the pile
670 break;
671 case 'end':
54b8f334 672 if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
d4dd0659 673 $numerrors++; // Only process and start for same path from last pile is allowed before end
8298beda
EL
674 }
675 unset($notifpile[$notifpath]); // Delete from the pile
676 break;
677 case 'start':
678 if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
d4dd0659 679 $numerrors++; // Only non existing in pile and with level > last pile is allowed on start
8298beda
EL
680 }
681 $notifpile[$notifpath] = 'start'; // Add to the pile
682 break;
683 default:
684 $numerrors++; // Incorrect type of notification => error
685 }
d4dd0659
EL
686 // Update lastnotif
687 $lastnotif = $notif;
8298beda
EL
688 }
689 return $numerrors;
be866f9d
EL
690 }
691}
692
693/*
694 * helper processor able to perform various auto-cheks based on attributes while processing
695 * the test1.xml file available in the fixtures dir. It performs these checks:
696 * - name equal to "name" attribute of the tag (if present)
697 * - level equal to "level" attribute of the tag (if present)
698 * - path + tagname equal to "path" attribute of the tag (if present)
699 * - cdata, if not empty is:
700 * - equal to "value" attribute of the tag (if present)
701 * - else, equal to tag name
702 *
703 * We pass the whole UnitTestCase object to the processor in order to be
704 * able to perform the tests in the straight in the process
705 */
706class mock_auto_parser_processor extends progressive_parser_processor {
707
708 private $utc = null; // To store the unit test case
709
710 public function __construct($unit_test_case) {
711 parent::__construct();
712 $this->utc = $unit_test_case;
713 }
714
715 public function process_chunk($data) {
716 // Perform auto-checks based in the rules above
717 if (isset($data['tags'])) {
718 foreach ($data['tags'] as $tag) {
719 if (isset($tag['attrs']['name'])) { // name tests
bb7898c6 720 $this->utc->assertEquals($tag['name'], $tag['attrs']['name']);
be866f9d
EL
721 }
722 if (isset($tag['attrs']['level'])) { // level tests
bb7898c6 723 $this->utc->assertEquals($data['level'], $tag['attrs']['level']);
be866f9d
EL
724 }
725 if (isset($tag['attrs']['path'])) { // path tests
bb7898c6 726 $this->utc->assertEquals(rtrim($data['path'], '/') . '/' . $tag['name'], $tag['attrs']['path']);
be866f9d
EL
727 }
728 if (!empty($tag['cdata'])) { // cdata tests
729 if (isset($tag['attrs']['value'])) {
bb7898c6 730 $this->utc->assertEquals($tag['cdata'], $tag['attrs']['value']);
be866f9d 731 } else {
bb7898c6 732 $this->utc->assertEquals($tag['cdata'], $tag['name']);
be866f9d
EL
733 }
734 }
735 }
736 }
737 }
738}
739
740/*
741 * helper processor that accumulates all the chunks, resturning them with the get_chunks() method
742 */
743class mock_parser_processor extends progressive_parser_processor {
744
745 private $chunksarr = array(); // To accumulate the found chunks
746
747 public function process_chunk($data) {
748 $this->chunksarr[] = $data;
749 }
750
751 public function get_chunks() {
752 return $this->chunksarr;
753 }
754}
755
756/*
757 * helper processor that accumulates simplified chunks, returning them with the get_chunks() method
758 */
759class mock_simplified_parser_processor extends simplified_parser_processor {
760
761 private $chunksarr = array(); // To accumulate the found chunks
14e6710d
EL
762 private $startarr = array(); // To accumulate all the notified path starts
763 private $endarr = array(); // To accumulate all the notified path ends
8298beda 764 private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
be866f9d
EL
765
766 public function dispatch_chunk($data) {
767 $this->chunksarr[] = $data;
8298beda 768 $this->allnotif[] = 'process:' . $data['path'];
be866f9d
EL
769 }
770
14e6710d
EL
771 public function notify_path_start($path) {
772 $this->startarr[] = $path;
8298beda 773 $this->allnotif[] = 'start:' . $path;
14e6710d
EL
774 }
775
776 public function notify_path_end($path) {
777 $this->endarr[] = $path;
8298beda 778 $this->allnotif[] = 'end:' . $path;
14e6710d
EL
779 }
780
be866f9d
EL
781 public function get_chunks() {
782 return $this->chunksarr;
783 }
14e6710d
EL
784
785 public function get_start_notifications() {
786 return $this->startarr;
787 }
788
789 public function get_end_notifications() {
790 return $this->endarr;
791 }
8298beda
EL
792
793 public function get_all_notifications() {
794 return $this->allnotif;
795 }
be866f9d 796}
4c7f6ac6
EL
797
798/*
799 * helper processor that accumulates grouped chunks, returning them with the get_chunks() method
800 */
801class mock_grouped_parser_processor extends grouped_parser_processor {
802
803 private $chunksarr = array(); // To accumulate the found chunks
14e6710d
EL
804 private $startarr = array(); // To accumulate all the notified path starts
805 private $endarr = array(); // To accumulate all the notified path ends
8298beda 806 private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
4c7f6ac6
EL
807
808 public function dispatch_chunk($data) {
809 $this->chunksarr[] = $data;
8298beda 810 $this->allnotif[] = 'process:' . $data['path'];
4c7f6ac6
EL
811 }
812
14e6710d
EL
813 public function notify_path_start($path) {
814 $this->startarr[] = $path;
8298beda 815 $this->allnotif[] = 'start:' . $path;
14e6710d
EL
816 }
817
818 public function notify_path_end($path) {
819 $this->endarr[] = $path;
8298beda 820 $this->allnotif[] = 'end:' . $path;
14e6710d
EL
821 }
822
4c7f6ac6
EL
823 public function get_chunks() {
824 return $this->chunksarr;
825 }
14e6710d
EL
826
827 public function get_start_notifications() {
828 return $this->startarr;
829 }
830
831 public function get_end_notifications() {
832 return $this->endarr;
833 }
8298beda
EL
834
835 public function get_all_notifications() {
836 return $this->allnotif;
837 }
4c7f6ac6 838}