Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
     */
11 efrain 56
    function test_parser_public_api(): void {
1 efrain 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
     */
11 efrain 171
    function test_parser_results(): void {
1 efrain 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
     */
11 efrain 196
    function test_simplified_parser_results(): void {
1 efrain 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 &amp; 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
     */
11 efrain 356
    function test_simplified_david_backup19_file_fragment(): void {
1 efrain 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
     */
11 efrain 422
    function test_grouped_parser_results(): void {
1 efrain 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 &amp; 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
     */
11 efrain 598
    function test_grouped_david_backup19_file_fragment(): void {
1 efrain 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
     */
11 efrain 652
    function test_grouped_at_empty_node(): void {
1 efrain 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
}