bf4935cdd5fd174aeb0492da350d44358a550e2c
[moodle.git] / backup / converter / moodle1 / tests / lib_test.php
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/>.
17 /**
18  * Unit tests for the moodle1 converter
19  *
20  * @package    core_backup
21  * @subpackage backup-convert
22  * @category   phpunit
23  * @copyright  2011 Mark Nielsen <mark@moodlerooms.com>
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/backup/converter/moodle1/lib.php');
33 class moodle1_converter_testcase extends advanced_testcase {
35     /** @var string the name of the directory containing the unpacked Moodle 1.9 backup */
36     protected $tempdir;
38     /** @var string saved hash of an icon file used during testing */
39     protected $iconhash;
41     protected function setUp() {
42         global $CFG;
44         $this->tempdir = convert_helper::generate_id('unittest');
45         check_dir_exists("$CFG->tempdir/backup/$this->tempdir/course_files/sub1");
46         check_dir_exists("$CFG->tempdir/backup/$this->tempdir/moddata/unittest/4/7");
47         copy(
48             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/moodle.xml",
49             "$CFG->tempdir/backup/$this->tempdir/moodle.xml"
50         );
51         copy(
52             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
53             "$CFG->tempdir/backup/$this->tempdir/course_files/file1.gif"
54         );
55         copy(
56             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
57             "$CFG->tempdir/backup/$this->tempdir/course_files/sub1/file2.gif"
58         );
59         copy(
60             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
61             "$CFG->tempdir/backup/$this->tempdir/moddata/unittest/4/file1.gif"
62         );
63         copy(
64             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
65             "$CFG->tempdir/backup/$this->tempdir/moddata/unittest/4/icon.gif"
66         );
67         $this->iconhash = sha1_file($CFG->tempdir.'/backup/'.$this->tempdir.'/moddata/unittest/4/icon.gif');
68         copy(
69             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
70             "$CFG->tempdir/backup/$this->tempdir/moddata/unittest/4/7/icon.gif"
71         );
72     }
74     protected function tearDown() {
75         global $CFG;
76         if (empty($CFG->keeptempdirectoriesonbackup)) {
77             fulldelete("$CFG->tempdir/backup/$this->tempdir");
78         }
79     }
81     public function test_detect_format() {
82         $detected = moodle1_converter::detect_format($this->tempdir);
83         $this->assertEquals(backup::FORMAT_MOODLE1, $detected);
84     }
86     public function test_convert_factory() {
87         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
88         $this->assertInstanceOf('moodle1_converter', $converter);
89     }
91     public function test_stash_storage_not_created() {
92         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
93         $this->setExpectedException('moodle1_convert_storage_exception');
94         $converter->set_stash('tempinfo', 12);
95     }
97     public function test_stash_requiring_empty_stash() {
98         $this->resetAfterTest(true);
99         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
100         $converter->create_stash_storage();
101         $converter->set_stash('tempinfo', 12);
102         $this->setExpectedException('moodle1_convert_empty_storage_exception');
103         try {
104             $converter->get_stash('anothertempinfo');
106         } catch (moodle1_convert_empty_storage_exception $e) {
107             // we must drop the storage here so we are able to re-create it in the next test
108             $converter->drop_stash_storage();
109             throw new moodle1_convert_empty_storage_exception('rethrowing');
110         }
111     }
113     public function test_stash_storage() {
114         $this->resetAfterTest(true);
115         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
116         $converter->create_stash_storage();
118         // no implicit stashes
119         $stashes = $converter->get_stash_names();
120         $this->assertEquals(gettype($stashes), 'array');
121         $this->assertTrue(empty($stashes));
123         // test stashes without itemid
124         $converter->set_stash('tempinfo1', 12);
125         $converter->set_stash('tempinfo2', array('a' => 2, 'b' => 3));
126         $stashes = $converter->get_stash_names();
127         $this->assertEquals('array', gettype($stashes));
128         $this->assertEquals(2, count($stashes));
129         $this->assertTrue(in_array('tempinfo1', $stashes));
130         $this->assertTrue(in_array('tempinfo2', $stashes));
131         $this->assertEquals(12, $converter->get_stash('tempinfo1'));
132         $this->assertEquals(array('a' => 2, 'b' => 3), $converter->get_stash('tempinfo2'));
134         // overwriting a stashed value is allowed
135         $converter->set_stash('tempinfo1', '13');
136         $this->assertNotSame(13, $converter->get_stash('tempinfo1'));
137         $this->assertSame('13', $converter->get_stash('tempinfo1'));
139         // repeated reading is allowed
140         $this->assertEquals('13', $converter->get_stash('tempinfo1'));
142         // storing empty array
143         $converter->set_stash('empty_array_stash', array());
144         $restored = $converter->get_stash('empty_array_stash');
145         //$this->assertEquals(gettype($restored), 'array'); // todo return null now, this needs MDL-27713 to be fixed, then uncomment
146         $this->assertTrue(empty($restored));
148         // test stashes with itemid
149         $converter->set_stash('tempinfo', 'Hello', 1);
150         $converter->set_stash('tempinfo', 'World', 2);
151         $this->assertSame('Hello', $converter->get_stash('tempinfo', 1));
152         $this->assertSame('World', $converter->get_stash('tempinfo', 2));
154         // test get_stash_itemids()
155         $ids = $converter->get_stash_itemids('course_fileref');
156         $this->assertEquals(gettype($ids), 'array');
157         $this->assertTrue(empty($ids));
159         $converter->set_stash('course_fileref', null, 34);
160         $converter->set_stash('course_fileref', null, 52);
161         $ids = $converter->get_stash_itemids('course_fileref');
162         $this->assertEquals(2, count($ids));
163         $this->assertTrue(in_array(34, $ids));
164         $this->assertTrue(in_array(52, $ids));
166         $converter->drop_stash_storage();
167     }
169     public function test_get_stash_or_default() {
170         $this->resetAfterTest(true);
171         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
172         $converter->create_stash_storage();
174         $this->assertTrue(is_null($converter->get_stash_or_default('stashname')));
175         $this->assertTrue(is_null($converter->get_stash_or_default('stashname', 7)));
176         $this->assertTrue('default' === $converter->get_stash_or_default('stashname', 0, 'default'));
177         $this->assertTrue(array('foo', 'bar') === $converter->get_stash_or_default('stashname', 42, array('foo', 'bar')));
179         //$converter->set_stash('stashname', 0);
180         //$this->assertFalse(is_null($converter->get_stash_or_default('stashname'))); // todo returns true now, this needs MDL-27713 to be fixed
182         //$converter->set_stash('stashname', '');
183         //$this->assertFalse(is_null($converter->get_stash_or_default('stashname'))); // todo returns true now, this needs MDL-27713 to be fixed
185         //$converter->set_stash('stashname', array());
186         //$this->assertFalse(is_null($converter->get_stash_or_default('stashname'))); // todo returns true now, this needs MDL-27713 to be fixed
188         $converter->set_stash('stashname', 42);
189         $this->assertTrue(42 === $converter->get_stash_or_default('stashname'));
190         $this->assertTrue(is_null($converter->get_stash_or_default('stashname', 1)));
191         $this->assertTrue(42 === $converter->get_stash_or_default('stashname', 0, 61));
193         $converter->set_stash('stashname', array(42 => (object)array('id' => 42)), 18);
194         $stashed = $converter->get_stash_or_default('stashname', 18, 1984);
195         $this->assertEquals(gettype($stashed), 'array');
196         $this->assertTrue(is_object($stashed[42]));
197         $this->assertTrue($stashed[42]->id === 42);
199         $converter->drop_stash_storage();
200     }
202     public function test_get_contextid() {
203         $this->resetAfterTest(true);
205         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
207         // stash storage must be created in advance
208         $converter->create_stash_storage();
210         // ids are generated on the first call
211         $id1 = $converter->get_contextid(CONTEXT_BLOCK, 10);
212         $id2 = $converter->get_contextid(CONTEXT_BLOCK, 11);
213         $id3 = $converter->get_contextid(CONTEXT_MODULE, 10);
215         $this->assertNotEquals($id1, $id2);
216         $this->assertNotEquals($id1, $id3);
217         $this->assertNotEquals($id2, $id3);
219         // and then re-used if called with the same params
220         $this->assertEquals($id1, $converter->get_contextid(CONTEXT_BLOCK, 10));
221         $this->assertEquals($id2, $converter->get_contextid(CONTEXT_BLOCK, 11));
222         $this->assertEquals($id3, $converter->get_contextid(CONTEXT_MODULE, 10));
224         // for system and course level, the instance is irrelevant
225         // as we need only one system and one course
226         $id1 = $converter->get_contextid(CONTEXT_COURSE);
227         $id2 = $converter->get_contextid(CONTEXT_COURSE, 10);
228         $id3 = $converter->get_contextid(CONTEXT_COURSE, 14);
230         $this->assertEquals($id1, $id2);
231         $this->assertEquals($id1, $id3);
233         $id1 = $converter->get_contextid(CONTEXT_SYSTEM);
234         $id2 = $converter->get_contextid(CONTEXT_SYSTEM, 11);
235         $id3 = $converter->get_contextid(CONTEXT_SYSTEM, 15);
237         $this->assertEquals($id1, $id2);
238         $this->assertEquals($id1, $id3);
240         $converter->drop_stash_storage();
241     }
243     public function test_get_nextid() {
244         $this->resetAfterTest(true);
246         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
248         $id1 = $converter->get_nextid();
249         $id2 = $converter->get_nextid();
250         $id3 = $converter->get_nextid();
252         $this->assertTrue(0 < $id1);
253         $this->assertTrue($id1 < $id2);
254         $this->assertTrue($id2 < $id3);
255     }
257     public function test_migrate_file() {
258         $this->resetAfterTest(true);
260         // set-up the file manager
261         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
262         $converter->create_stash_storage();
263         $contextid = $converter->get_contextid(CONTEXT_MODULE, 32);
264         $fileman   = $converter->get_file_manager($contextid, 'mod_unittest', 'testarea');
265         // this fileman has not converted anything yet
266         $fileids = $fileman->get_fileids();
267         $this->assertEquals(gettype($fileids), 'array');
268         $this->assertEquals(0, count($fileids));
269         // try to migrate a non-existing directory
270         $returned = $fileman->migrate_directory('not/existing/directory');
271         $this->assertEquals(gettype($returned), 'array');
272         $this->assertEquals(0, count($returned));
273         $fileids = $fileman->get_fileids();
274         $this->assertEquals(gettype($fileids), 'array');
275         $this->assertEquals(0, count($fileids));
276         // migrate a single file
277         $fileman->itemid = 4;
278         $fileman->migrate_file('moddata/unittest/4/icon.gif');
279         $subdir = substr($this->iconhash, 0, 2);
280         $this->assertTrue(is_file($converter->get_workdir_path().'/files/'.$subdir.'/'.$this->iconhash));
281         // get the file id
282         $fileids = $fileman->get_fileids();
283         $this->assertEquals(gettype($fileids), 'array');
284         $this->assertEquals(1, count($fileids));
285         // migrate another single file into another file area
286         $fileman->filearea = 'anotherarea';
287         $fileman->itemid = 7;
288         $fileman->migrate_file('moddata/unittest/4/7/icon.gif', '/', 'renamed.gif');
289         // get the file records
290         $filerecordids = $converter->get_stash_itemids('files');
291         foreach ($filerecordids as $filerecordid) {
292             $filerecord = $converter->get_stash('files', $filerecordid);
293             $this->assertEquals($this->iconhash, $filerecord['contenthash']);
294             $this->assertEquals($contextid, $filerecord['contextid']);
295             $this->assertEquals('mod_unittest', $filerecord['component']);
296             if ($filerecord['filearea'] === 'testarea') {
297                 $this->assertEquals(4, $filerecord['itemid']);
298                 $this->assertEquals('icon.gif', $filerecord['filename']);
299             }
300         }
301         // explicitly clear the list of migrated files
302         $this->assertTrue(count($fileman->get_fileids()) > 0);
303         $fileman->reset_fileids();
304         $this->assertTrue(count($fileman->get_fileids()) == 0);
305         $converter->drop_stash_storage();
306     }
308     public function test_convert_path() {
309         $path = new convert_path('foo_bar', '/ROOT/THINGS/FOO/BAR');
310         $this->assertEquals('foo_bar', $path->get_name());
311         $this->assertEquals('/ROOT/THINGS/FOO/BAR', $path->get_path());
312         $this->assertEquals('process_foo_bar', $path->get_processing_method());
313         $this->assertEquals('on_foo_bar_start', $path->get_start_method());
314         $this->assertEquals('on_foo_bar_end', $path->get_end_method());
315     }
317     public function test_convert_path_implicit_recipes() {
318         $path = new convert_path('foo_bar', '/ROOT/THINGS/FOO/BAR');
319         $data = array(
320             'ID' => 76,
321             'ELOY' => 'stronk7',
322             'MARTIN' => 'moodler',
323             'EMPTY' => null,
324         );
325         // apply default recipes (converting keys to lowercase)
326         $data = $path->apply_recipes($data);
327         $this->assertEquals(4, count($data));
328         $this->assertEquals(76, $data['id']);
329         $this->assertEquals('stronk7', $data['eloy']);
330         $this->assertEquals('moodler', $data['martin']);
331         $this->assertSame(null, $data['empty']);
332     }
334     public function test_convert_path_explicit_recipes() {
335         $path = new convert_path(
336             'foo_bar', '/ROOT/THINGS/FOO/BAR',
337             array(
338                 'newfields' => array(
339                     'david' => 'mudrd8mz',
340                     'petr'  => 'skodak',
341                 ),
342                 'renamefields' => array(
343                     'empty' => 'nothing',
344                 ),
345                 'dropfields' => array(
346                     'id'
347                 ),
348             )
349         );
350         $data = array(
351             'ID' => 76,
352             'ELOY' => 'stronk7',
353             'MARTIN' => 'moodler',
354             'EMPTY' => null,
355         );
356         $data = $path->apply_recipes($data);
358         $this->assertEquals(5, count($data));
359         $this->assertFalse(array_key_exists('id', $data));
360         $this->assertEquals('stronk7', $data['eloy']);
361         $this->assertEquals('moodler', $data['martin']);
362         $this->assertEquals('mudrd8mz', $data['david']);
363         $this->assertEquals('skodak', $data['petr']);
364         $this->assertSame(null, $data['nothing']);
365     }
367     public function test_grouped_data_on_nongrouped_convert_path() {
368         // prepare some grouped data
369         $data = array(
370             'ID' => 77,
371             'NAME' => 'Pale lagers',
372             'BEERS' => array(
373                 array(
374                     'BEER' => array(
375                         'ID' => 67,
376                         'NAME' => 'Pilsner Urquell',
377                     )
378                 ),
379                 array(
380                     'BEER' => array(
381                         'ID' => 34,
382                         'NAME' => 'Heineken',
383                     )
384                 ),
385             )
386         );
388         // declare a non-grouped path
389         $path = new convert_path('beer_style', '/ROOT/BEER_STYLES/BEER_STYLE');
391         // an attempt to apply recipes throws exception because we do not expect grouped data
392         $this->setExpectedException('convert_path_exception');
393         $data = $path->apply_recipes($data);
394     }
396     public function test_grouped_convert_path_with_recipes() {
397         // prepare some grouped data
398         $data = array(
399             'ID' => 77,
400             'NAME' => 'Pale lagers',
401             'BEERS' => array(
402                 array(
403                     'BEER' => array(
404                         'ID' => 67,
405                         'NAME' => 'Pilsner Urquell',
406                     )
407                 ),
408                 array(
409                     'BEER' => array(
410                         'ID' => 34,
411                         'NAME' => 'Heineken',
412                     )
413                 ),
414             )
415         );
417         // implict recipes work for grouped data if the path is declared as grouped
418         $path = new convert_path('beer_style', '/ROOT/BEER_STYLES/BEER_STYLE', array(), true);
419         $data = $path->apply_recipes($data);
420         $this->assertEquals('Heineken', $data['beers'][1]['beer']['name']);
422         // an attempt to provide explicit recipes on grouped elements throws exception
423         $this->setExpectedException('convert_path_exception');
424         $path = new convert_path(
425             'beer_style', '/ROOT/BEER_STYLES/BEER_STYLE',
426             array(
427                 'renamefields' => array(
428                     'name' => 'beername',   // note this is confusing recipe because the 'name' is used for both
429                     // beer-style name ('Pale lagers') and beer name ('Pilsner Urquell')
430                 )
431             ), true);
432     }
434     public function test_referenced_course_files() {
436         $text = 'This is a text containing links to file.php
437 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif" /><a href="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif$@FORCEDOWNLOAD@$">download image</a><br />
438     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />';
440         $files = moodle1_converter::find_referenced_files($text);
441         $this->assertEquals(gettype($files), 'array');
442         $this->assertEquals(2, count($files));
443         $this->assertTrue(in_array('/pics/news.gif', $files));
444         $this->assertTrue(in_array('/MANUAL.DOC', $files));
446         $text = moodle1_converter::rewrite_filephp_usage($text, array('/pics/news.gif', '/another/file/notused.txt'));
447         $this->assertEquals($text, 'This is a text containing links to file.php
448 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="@@PLUGINFILE@@/pics/news.gif" /><a href="@@PLUGINFILE@@/pics/news.gif?forcedownload=1">download image</a><br />
449     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />');
450     }
452     public function test_referenced_files_urlencoded() {
454         $text = 'This is a text containing links to file.php
455 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif" /><a href="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif$@FORCEDOWNLOAD@$">no space</a><br />
456     <br /><a href=\'$@FILEPHP@$$@SLASH@$pics$@SLASH@$news%20with%20spaces.gif$@FORCEDOWNLOAD@$\'>with urlencoded spaces</a><br />
457 <a href="$@FILEPHP@$$@SLASH@$illegal%20pics%2Bmovies$@SLASH@$romeo%2Bjuliet.avi">Download the full AVI for free! (space and plus encoded)</a>
458 <a href="$@FILEPHP@$$@SLASH@$illegal pics+movies$@SLASH@$romeo+juliet.avi">Download the full AVI for free! (none encoded)</a>
459 <a href="$@FILEPHP@$$@SLASH@$illegal%20pics+movies$@SLASH@$romeo+juliet.avi">Download the full AVI for free! (only space encoded)</a>
460 <a href="$@FILEPHP@$$@SLASH@$illegal pics%2Bmovies$@SLASH@$romeo%2Bjuliet.avi">Download the full AVI for free! (only plus)</a>';
462         $files = moodle1_converter::find_referenced_files($text);
463         $this->assertEquals(gettype($files), 'array');
464         $this->assertEquals(3, count($files));
465         $this->assertTrue(in_array('/pics/news.gif', $files));
466         $this->assertTrue(in_array('/pics/news with spaces.gif', $files));
467         $this->assertTrue(in_array('/illegal pics+movies/romeo+juliet.avi', $files));
469         $text = moodle1_converter::rewrite_filephp_usage($text, $files);
470         $this->assertEquals('This is a text containing links to file.php
471 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="@@PLUGINFILE@@/pics/news.gif" /><a href="@@PLUGINFILE@@/pics/news.gif?forcedownload=1">no space</a><br />
472     <br /><a href=\'@@PLUGINFILE@@/pics/news%20with%20spaces.gif?forcedownload=1\'>with urlencoded spaces</a><br />
473 <a href="@@PLUGINFILE@@/illegal%20pics%2Bmovies/romeo%2Bjuliet.avi">Download the full AVI for free! (space and plus encoded)</a>
474 <a href="@@PLUGINFILE@@/illegal%20pics%2Bmovies/romeo%2Bjuliet.avi">Download the full AVI for free! (none encoded)</a>
475 <a href="$@FILEPHP@$$@SLASH@$illegal%20pics+movies$@SLASH@$romeo+juliet.avi">Download the full AVI for free! (only space encoded)</a>
476 <a href="$@FILEPHP@$$@SLASH@$illegal pics%2Bmovies$@SLASH@$romeo%2Bjuliet.avi">Download the full AVI for free! (only plus)</a>', $text);
477     }
479     public function test_question_bank_conversion() {
480         global $CFG;
482         $this->resetAfterTest(true);
484         copy(
485             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/questions.xml",
486             "$CFG->tempdir/backup/$this->tempdir/moodle.xml"
487         );
488         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
489         $converter->convert();
490     }
492     public function test_convert_run_convert() {
493         $this->resetAfterTest(true);
494         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
495         $converter->convert();
496     }
498     public function test_inforef_manager() {
499         $converter = convert_factory::get_converter('moodle1', $this->tempdir);
500         $inforef = $converter->get_inforef_manager('unittest');
501         $inforef->add_ref('file', 45);
502         $inforef->add_refs('file', array(46, 47));
503         // todo test the write_refs() via some dummy xml_writer
504         $this->setExpectedException('coding_exception');
505         $inforef->add_ref('unknown_referenced_item_name', 76);
506     }