MDL-41022 cleanup core_external_testcase
[moodle.git] / lib / filestorage / tests / file_storage_test.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Unit tests for /lib/filestorage/file_storage.php
20  *
21  * @package   core
22  * @category  test
23  * @copyright 2012 David Mudrak <david@moodle.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->libdir . '/filelib.php');
31 require_once($CFG->dirroot . '/repository/lib.php');
32 require_once($CFG->libdir . '/filestorage/stored_file.php');
34 class core_files_file_storage_testcase extends advanced_testcase {
36     /**
37      * Files can be created from strings.
38      */
39     public function test_create_file_from_string() {
40         global $DB;
42         $this->resetAfterTest(true);
44         $this->assertEquals(0, $DB->count_records('files', array()));
46         $content = 'abcd';
47         $syscontext = context_system::instance();
48         $filerecord = array(
49             'contextid' => $syscontext->id,
50             'component' => 'core',
51             'filearea'  => 'unittest',
52             'itemid'    => 0,
53             'filepath'  => '/images/',
54             'filename'  => 'testfile.txt',
55         );
56         $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
58         $fs = get_file_storage();
59         $file = $fs->create_file_from_string($filerecord, $content);
61         $this->assertInstanceOf('stored_file', $file);
62         $this->assertSame(sha1($content), $file->get_contenthash());
63         $this->assertSame($pathhash, $file->get_pathnamehash());
65         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
67         $location = test_stored_file_inspection::get_pretected_pathname($file);
69         $this->assertFileExists($location);
72         // Verify the dir placeholder files are created.
73         $this->assertEquals(3, $DB->count_records('files', array()));
74         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
75         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
78         // Tests that missing content file is recreated.
80         unlink($location);
81         $this->assertFileNotExists($location);
83         $filerecord['filename'] = 'testfile2.txt';
84         $file2 = $fs->create_file_from_string($filerecord, $content);
85         $this->assertInstanceOf('stored_file', $file2);
86         $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
87         $this->assertFileExists($location);
89         $this->assertEquals(4, $DB->count_records('files', array()));
92         // Test that borked content file is recreated.
94         $this->assertSame(2, file_put_contents($location, 'xx'));
96         $filerecord['filename'] = 'testfile3.txt';
97         $file3 = $fs->create_file_from_string($filerecord, $content);
98         $this->assertInstanceOf('stored_file', $file3);
99         $this->assertSame($file->get_contenthash(), $file3->get_contenthash());
100         $this->assertFileExists($location);
102         $this->assertSame($content, file_get_contents($location));
103         $this->assertDebuggingCalled();
105         $this->assertEquals(5, $DB->count_records('files', array()));
106     }
108     /**
109      * Local files can be added to the filepool
110      */
111     public function test_create_file_from_pathname() {
112         global $CFG, $DB;
114         $this->resetAfterTest(true);
116         $filecount = $DB->count_records('files', array());
117         $this->assertEquals(0, $filecount);
119         $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
120         $syscontext = context_system::instance();
121         $filerecord = array(
122             'contextid' => $syscontext->id,
123             'component' => 'core',
124             'filearea'  => 'unittest',
125             'itemid'    => 0,
126             'filepath'  => '/images/',
127             'filename'  => 'testimage.jpg',
128         );
129         $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
131         $fs = get_file_storage();
132         $file = $fs->create_file_from_pathname($filerecord, $filepath);
134         $this->assertInstanceOf('stored_file', $file);
135         $this->assertSame(sha1_file($filepath), $file->get_contenthash());
137         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
139         $location = test_stored_file_inspection::get_pretected_pathname($file);
141         $this->assertFileExists($location);
144         // Verify the dir placeholder files are created.
145         $this->assertEquals(3, $DB->count_records('files', array()));
146         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
147         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
150         // Tests that missing content file is recreated.
152         unlink($location);
153         $this->assertFileNotExists($location);
155         $filerecord['filename'] = 'testfile2.jpg';
156         $file2 = $fs->create_file_from_pathname($filerecord, $filepath);
157         $this->assertInstanceOf('stored_file', $file2);
158         $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
159         $this->assertFileExists($location);
161         $this->assertEquals(4, $DB->count_records('files', array()));
164         // Test that borked content file is recreated.
166         $this->assertSame(2, file_put_contents($location, 'xx'));
168         $filerecord['filename'] = 'testfile3.jpg';
169         $file3 = $fs->create_file_from_pathname($filerecord, $filepath);
170         $this->assertInstanceOf('stored_file', $file3);
171         $this->assertSame($file->get_contenthash(), $file3->get_contenthash());
172         $this->assertFileExists($location);
174         $this->assertSame(file_get_contents($filepath), file_get_contents($location));
175         $this->assertDebuggingCalled();
177         $this->assertEquals(5, $DB->count_records('files', array()));
179         // Test invalid file creation.
181         $filerecord['filename'] = 'testfile4.jpg';
182         try {
183             $fs->create_file_from_pathname($filerecord, $filepath.'nonexistent');
184             $this->fail('Exception expected when trying to add non-existent stored file.');
185         } catch (Exception $e) {
186             $this->assertInstanceOf('file_exception', $e);
187         }
188     }
190     /**
191      * Tests get get file.
192      */
193     public function test_get_file() {
194         global $CFG;
196         $this->resetAfterTest(false);
198         $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
199         $syscontext = context_system::instance();
200         $filerecord = array(
201             'contextid' => $syscontext->id,
202             'component' => 'core',
203             'filearea'  => 'unittest',
204             'itemid'    => 0,
205             'filepath'  => '/images/',
206             'filename'  => 'testimage.jpg',
207         );
208         $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
210         $fs = get_file_storage();
211         $file = $fs->create_file_from_pathname($filerecord, $filepath);
213         $this->assertInstanceOf('stored_file', $file);
214         $this->assertEquals($syscontext->id, $file->get_contextid());
215         $this->assertEquals('core', $file->get_component());
216         $this->assertEquals('unittest', $file->get_filearea());
217         $this->assertEquals(0, $file->get_itemid());
218         $this->assertEquals('/images/', $file->get_filepath());
219         $this->assertEquals('testimage.jpg', $file->get_filename());
220         $this->assertEquals(filesize($filepath), $file->get_filesize());
221         $this->assertEquals($pathhash, $file->get_pathnamehash());
223         return $file;
224     }
226     /**
227      * Local images can be added to the filepool and their preview can be obtained
228      *
229      * @depends test_get_file
230      */
231     public function test_get_file_preview(stored_file $file) {
232         global $CFG;
234         $this->resetAfterTest(true);
235         $fs = get_file_storage();
237         $previewtinyicon = $fs->get_file_preview($file, 'tinyicon');
238         $this->assertInstanceOf('stored_file', $previewtinyicon);
239         $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
241         $previewtinyicon = $fs->get_file_preview($file, 'thumb');
242         $this->assertInstanceOf('stored_file', $previewtinyicon);
243         $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
245         $this->setExpectedException('file_exception');
246         $fs->get_file_preview($file, 'amodewhichdoesntexist');
247     }
249     public function test_get_file_preview_nonimage() {
250         $this->resetAfterTest(true);
251         $syscontext = context_system::instance();
252         $filerecord = array(
253             'contextid' => $syscontext->id,
254             'component' => 'core',
255             'filearea'  => 'unittest',
256             'itemid'    => 0,
257             'filepath'  => '/textfiles/',
258             'filename'  => 'testtext.txt',
259         );
261         $fs = get_file_storage();
262         $fs->create_file_from_string($filerecord, 'text contents');
263         $textfile = $fs->get_file($syscontext->id, $filerecord['component'], $filerecord['filearea'],
264             $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename']);
266         $preview = $fs->get_file_preview($textfile, 'thumb');
267         $this->assertFalse($preview);
268     }
270     /**
271      * Make sure renaming is working
272      *
273      * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
274      */
275     public function test_file_renaming() {
276         global $CFG;
278         $this->resetAfterTest(true);
279         $fs = get_file_storage();
280         $syscontext = context_system::instance();
281         $component = 'core';
282         $filearea  = 'unittest';
283         $itemid    = 0;
284         $filepath  = '/';
285         $filename  = 'test.txt';
287         $filerecord = array(
288             'contextid' => $syscontext->id,
289             'component' => $component,
290             'filearea'  => $filearea,
291             'itemid'    => $itemid,
292             'filepath'  => $filepath,
293             'filename'  => $filename,
294         );
296         $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
297         $this->assertInstanceOf('stored_file', $originalfile);
298         $contenthash = $originalfile->get_contenthash();
299         $newpath = '/test/';
300         $newname = 'newtest.txt';
302         // this should work
303         $originalfile->rename($newpath, $newname);
304         $file = $fs->get_file($syscontext->id, $component, $filearea, $itemid, $newpath, $newname);
305         $this->assertInstanceOf('stored_file', $file);
306         $this->assertEquals($contenthash, $file->get_contenthash());
308         // try break it
309         $this->setExpectedException('file_exception');
310         // this shall throw exception
311         $originalfile->rename($newpath, $newname);
312     }
314     /**
315      * Create file from reference tests
316      *
317      * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
318      */
319     public function test_create_file_from_reference() {
320         global $CFG, $DB;
322         $this->resetAfterTest(true);
323         // create user
324         $generator = $this->getDataGenerator();
325         $user = $generator->create_user();
326         $this->setUser($user);
327         $usercontext = context_user::instance($user->id);
328         $syscontext = context_system::instance();
330         $fs = get_file_storage();
332         $repositorypluginname = 'user';
333         // override repository permission
334         $capability = 'repository/' . $repositorypluginname . ':view';
335         $allroles = $DB->get_records_menu('role', array(), 'id', 'archetype, id');
336         assign_capability($capability, CAP_ALLOW, $allroles['guest'], $syscontext->id, true);
339         $args = array();
340         $args['type'] = $repositorypluginname;
341         $repos = repository::get_instances($args);
342         $userrepository = reset($repos);
343         $this->assertInstanceOf('repository', $userrepository);
345         $component = 'user';
346         $filearea  = 'private';
347         $itemid    = 0;
348         $filepath  = '/';
349         $filename  = 'userfile.txt';
351         $filerecord = array(
352             'contextid' => $usercontext->id,
353             'component' => $component,
354             'filearea'  => $filearea,
355             'itemid'    => $itemid,
356             'filepath'  => $filepath,
357             'filename'  => $filename,
358         );
360         $content = 'Test content';
361         $originalfile = $fs->create_file_from_string($filerecord, $content);
362         $this->assertInstanceOf('stored_file', $originalfile);
364         $newfilerecord = array(
365             'contextid' => $syscontext->id,
366             'component' => 'core',
367             'filearea'  => 'phpunit',
368             'itemid'    => 0,
369             'filepath'  => $filepath,
370             'filename'  => $filename,
371         );
372         $ref = $fs->pack_reference($filerecord);
373         $newstoredfile = $fs->create_file_from_reference($newfilerecord, $userrepository->id, $ref);
374         $this->assertInstanceOf('stored_file', $newstoredfile);
375         $this->assertEquals($userrepository->id, $newstoredfile->get_repository_id());
376         $this->assertEquals($originalfile->get_contenthash(), $newstoredfile->get_contenthash());
377         $this->assertEquals($originalfile->get_filesize(), $newstoredfile->get_filesize());
378         $this->assertRegExp('#' . $filename. '$#', $newstoredfile->get_reference_details());
380         // Test looking for references
381         $count = $fs->get_references_count_by_storedfile($originalfile);
382         $this->assertEquals(1, $count);
383         $files = $fs->get_references_by_storedfile($originalfile);
384         $file = reset($files);
385         $this->assertEquals($file, $newstoredfile);
387         // Look for references by repository ID
388         $files = $fs->get_external_files($userrepository->id);
389         $file = reset($files);
390         $this->assertEquals($file, $newstoredfile);
392         // Try convert reference to local file
393         $importedfile = $fs->import_external_file($newstoredfile);
394         $this->assertFalse($importedfile->is_external_file());
395         $this->assertInstanceOf('stored_file', $importedfile);
396         // still readable?
397         $this->assertEquals($content, $importedfile->get_content());
398     }
400     private function setup_three_private_files() {
402         $this->resetAfterTest(true);
404         $generator = $this->getDataGenerator();
405         $user = $generator->create_user();
406         $this->setUser($user->id);
407         $usercontext = context_user::instance($user->id);
408         // create a user private file
409         $file1 = new stdClass;
410         $file1->contextid = $usercontext->id;
411         $file1->component = 'user';
412         $file1->filearea  = 'private';
413         $file1->itemid    = 0;
414         $file1->filepath  = '/';
415         $file1->filename  = '1.txt';
416         $file1->source    = 'test';
418         $fs = get_file_storage();
419         $userfile1 = $fs->create_file_from_string($file1, 'file1 content');
420         $this->assertInstanceOf('stored_file', $userfile1);
422         $file2 = clone($file1);
423         $file2->filename = '2.txt';
424         $userfile2 = $fs->create_file_from_string($file2, 'file2 content');
425         $this->assertInstanceOf('stored_file', $userfile2);
427         $file3 = clone($file1);
428         $file3->filename = '3.txt';
429         $userfile3 = $fs->create_file_from_storedfile($file3, $userfile2);
430         $this->assertInstanceOf('stored_file', $userfile3);
432         $user->ctxid = $usercontext->id;
434         return $user;
435     }
437     public function test_get_area_files() {
438         $user = $this->setup_three_private_files();
439         $fs = get_file_storage();
441         // Get area files with default options.
442         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
444         // Should be the two files we added plus the folder.
445         $this->assertEquals(4, count($areafiles));
447         // Verify structure.
448         foreach ($areafiles as $key => $file) {
449             $this->assertInstanceOf('stored_file', $file);
450             $this->assertEquals($key, $file->get_pathnamehash());
451         }
453         // Get area files without a folder.
454         $folderlessfiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
455         // Should be the two files without folder.
456         $this->assertEquals(3, count($folderlessfiles));
458         // Verify structure.
459         foreach ($folderlessfiles as $key => $file) {
460             $this->assertInstanceOf('stored_file', $file);
461             $this->assertEquals($key, $file->get_pathnamehash());
462         }
464         // Get area files ordered by id.
465         $filesbyid  = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
466         // Should be the two files without folder.
467         $this->assertEquals(3, count($filesbyid));
469         // Verify structure.
470         foreach ($filesbyid as $key => $file) {
471             $this->assertInstanceOf('stored_file', $file);
472             $this->assertEquals($key, $file->get_pathnamehash());
473         }
475         // Test with an itemid with no files
476         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', 666, 'sortorder', false);
477         // Should be none.
478         $this->assertEmpty($areafiles);
479     }
481     public function test_get_area_tree() {
482         $user = $this->setup_three_private_files();
483         $fs = get_file_storage();
486         // Get area files with default options.
487         $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
488         $this->assertEmpty($areatree['subdirs']);
489         $this->assertNotEmpty($areatree['files']);
490         $this->assertCount(3, $areatree['files']);
492         // Ensure an empty try with a fake itemid.
493         $emptytree = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
494         $this->assertEmpty($emptytree['subdirs']);
495         $this->assertEmpty($emptytree['files']);
497         // Create a subdir.
498         $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
499         $this->assertInstanceOf('stored_file', $dir);
501         // Add a file to the subdir.
502         $filerecord = array(
503             'contextid' => $user->ctxid,
504             'component' => 'user',
505             'filearea'  => 'private',
506             'itemid'    => 0,
507             'filepath'  => '/testsubdir/',
508             'filename'  => 'test-get-area-tree.txt',
509         );
511         $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
512         $this->assertInstanceOf('stored_file', $directoryfile);
514         $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
516         // At the top level there should still be 3 files.
517         $this->assertCount(3, $areatree['files']);
519         // There should now be a subdirectory.
520         $this->assertCount(1, $areatree['subdirs']);
522         // The test subdir is named testsubdir.
523         $subdir = $areatree['subdirs']['testsubdir'];
524         $this->assertNotEmpty($subdir);
525         // It should have one file we added.
526         $this->assertCount(1, $subdir['files']);
527         // And no subdirs itself.
528         $this->assertCount(0, $subdir['subdirs']);
530         // Verify the file is the one we added.
531         $subdirfile = reset($subdir['files']);
532         $this->assertInstanceOf('stored_file', $subdirfile);
533         $this->assertEquals($filerecord['filename'], $subdirfile->get_filename());
534     }
536     public function test_get_file_by_id() {
537         $user = $this->setup_three_private_files();
538         $fs = get_file_storage();
540         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
542         // Test get_file_by_id.
543         $filebyid = reset($areafiles);
544         $shouldbesame = $fs->get_file_by_id($filebyid->get_id());
545         $this->assertEquals($filebyid->get_contenthash(), $shouldbesame->get_contenthash());
547         // Test an id which doens't exist.
548         $doesntexist = $fs->get_file_by_id(99999);
549         $this->assertFalse($doesntexist);
550     }
552     public function test_get_file_by_hash() {
553         $user = $this->setup_three_private_files();
554         $fs = get_file_storage();
556         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
557         // Test get_file_by_hash
558         $filebyhash = reset($areafiles);
559         $shouldbesame = $fs->get_file_by_hash($filebyhash->get_pathnamehash());
560         $this->assertEquals($filebyhash->get_id(), $shouldbesame->get_id());
562         // Test an hash which doens't exist.
563         $doesntexist = $fs->get_file_by_hash('DOESNTEXIST');
564         $this->assertFalse($doesntexist);
565     }
567     public function test_get_external_files() {
568         $user = $this->setup_three_private_files();
569         $fs = get_file_storage();
571         $repos = repository::get_instances(array('type'=>'user'));
572         $userrepository = reset($repos);
573         $this->assertInstanceOf('repository', $userrepository);
575         // no aliases yet
576         $exfiles = $fs->get_external_files($userrepository->id, 'id');
577         $this->assertEquals(array(), $exfiles);
579         // create three aliases linking the same original: $aliasfile1 and $aliasfile2 are
580         // created via create_file_from_reference(), $aliasfile3 created from $aliasfile2
581         $originalfile = null;
582         foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
583             if (!$areafile->is_directory()) {
584                 $originalfile = $areafile;
585                 break;
586             }
587         }
588         $this->assertInstanceOf('stored_file', $originalfile);
589         $originalrecord = array(
590             'contextid' => $originalfile->get_contextid(),
591             'component' => $originalfile->get_component(),
592             'filearea'  => $originalfile->get_filearea(),
593             'itemid'    => $originalfile->get_itemid(),
594             'filepath'  => $originalfile->get_filepath(),
595             'filename'  => $originalfile->get_filename(),
596         );
598         $aliasrecord = $this->generate_file_record();
599         $aliasrecord->filepath = '/foo/';
600         $aliasrecord->filename = 'one.txt';
602         $ref = $fs->pack_reference($originalrecord);
603         $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
605         $aliasrecord->filepath = '/bar/';
606         $aliasrecord->filename = 'uno.txt';
607         // change the order of the items in the array to make sure that it does not matter
608         ksort($originalrecord);
609         $ref = $fs->pack_reference($originalrecord);
610         $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
612         $aliasrecord->filepath = '/bar/';
613         $aliasrecord->filename = 'jedna.txt';
614         $aliasfile3 = $fs->create_file_from_storedfile($aliasrecord, $aliasfile2);
616         // make sure we get three aliases now
617         $exfiles = $fs->get_external_files($userrepository->id, 'id');
618         $this->assertEquals(3, count($exfiles));
619         foreach ($exfiles as $exfile) {
620             $this->assertTrue($exfile->is_external_file());
621         }
622         // make sure they all link the same original (thence that all are linked with the same
623         // record in {files_reference})
624         $this->assertEquals($aliasfile1->get_referencefileid(), $aliasfile2->get_referencefileid());
625         $this->assertEquals($aliasfile3->get_referencefileid(), $aliasfile2->get_referencefileid());
626     }
628     public function test_create_directory_contextid_negative() {
629         $fs = get_file_storage();
631         $this->setExpectedException('file_exception');
632         $fs->create_directory(-1, 'core', 'unittest', 0, '/');
633     }
635     public function test_create_directory_contextid_invalid() {
636         $fs = get_file_storage();
638         $this->setExpectedException('file_exception');
639         $fs->create_directory('not an int', 'core', 'unittest', 0, '/');
640     }
642     public function test_create_directory_component_invalid() {
643         $fs = get_file_storage();
644         $syscontext = context_system::instance();
646         $this->setExpectedException('file_exception');
647         $fs->create_directory($syscontext->id, 'bad/component', 'unittest', 0, '/');
648     }
650     public function test_create_directory_filearea_invalid() {
651         $fs = get_file_storage();
652         $syscontext = context_system::instance();
654         $this->setExpectedException('file_exception');
655         $fs->create_directory($syscontext->id, 'core', 'bad-filearea', 0, '/');
656     }
658     public function test_create_directory_itemid_negative() {
659         $fs = get_file_storage();
660         $syscontext = context_system::instance();
662         $this->setExpectedException('file_exception');
663         $fs->create_directory($syscontext->id, 'core', 'unittest', -1, '/');
664     }
666     public function test_create_directory_itemid_invalid() {
667         $fs = get_file_storage();
668         $syscontext = context_system::instance();
670         $this->setExpectedException('file_exception');
671         $fs->create_directory($syscontext->id, 'core', 'unittest', 'notanint', '/');
672     }
674     public function test_create_directory_filepath_invalid() {
675         $fs = get_file_storage();
676         $syscontext = context_system::instance();
678         $this->setExpectedException('file_exception');
679         $fs->create_directory($syscontext->id, 'core', 'unittest', 0, '/not-with-trailing/or-leading-slash');
680     }
682     public function test_get_directory_files() {
683         $user = $this->setup_three_private_files();
684         $fs = get_file_storage();
686         $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
687         $this->assertInstanceOf('stored_file', $dir);
689         // Add a file to the subdir.
690         $filerecord = array(
691             'contextid' => $user->ctxid,
692             'component' => 'user',
693             'filearea'  => 'private',
694             'itemid'    => 0,
695             'filepath'  => '/testsubdir/',
696             'filename'  => 'test-get-area-tree.txt',
697         );
699         $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
700         $this->assertInstanceOf('stored_file', $directoryfile);
702         // Don't recurse without dirs
703         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
704         // 3 files only.
705         $this->assertCount(3, $files);
706         foreach ($files as $key => $file) {
707             $this->assertInstanceOf('stored_file', $file);
708             $this->assertEquals($key, $file->get_pathnamehash());
709         }
711         // Don't recurse with dirs.
712         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
713         // 3 files + 1 directory.
714         $this->assertCount(4, $files);
715         foreach ($files as $key => $file) {
716             $this->assertInstanceOf('stored_file', $file);
717             $this->assertEquals($key, $file->get_pathnamehash());
718         }
720         // Recurse with dirs.
721         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
722         // 3 files + 1 directory +  1 subdir file.
723         $this->assertCount(5, $files);
724         foreach ($files as $key => $file) {
725             $this->assertInstanceOf('stored_file', $file);
726             $this->assertEquals($key, $file->get_pathnamehash());
727         }
729         // Recurse without dirs.
730         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
731         // 3 files +  1 subdir file.
732         $this->assertCount(4, $files);
733         foreach ($files as $key => $file) {
734             $this->assertInstanceOf('stored_file', $file);
735             $this->assertEquals($key, $file->get_pathnamehash());
736         }
737     }
739     public function test_search_references() {
740         $user = $this->setup_three_private_files();
741         $fs = get_file_storage();
742         $repos = repository::get_instances(array('type'=>'user'));
743         $repo = reset($repos);
745         $alias1 = array(
746             'contextid' => $user->ctxid,
747             'component' => 'user',
748             'filearea'  => 'private',
749             'itemid'    => 0,
750             'filepath'  => '/aliases/',
751             'filename'  => 'alias-to-1.txt'
752         );
754         $alias2 = array(
755             'contextid' => $user->ctxid,
756             'component' => 'user',
757             'filearea'  => 'private',
758             'itemid'    => 0,
759             'filepath'  => '/aliases/',
760             'filename'  => 'another-alias-to-1.txt'
761         );
763         $reference = file_storage::pack_reference(array(
764             'contextid' => $user->ctxid,
765             'component' => 'user',
766             'filearea'  => 'private',
767             'itemid'    => 0,
768             'filepath'  => '/',
769             'filename'  => '1.txt'
770         ));
772         // There are no aliases now.
773         $result = $fs->search_references($reference);
774         $this->assertEquals(array(), $result);
776         $result = $fs->search_references_count($reference);
777         $this->assertSame($result, 0);
779         // Create two aliases and make sure they are returned.
780         $fs->create_file_from_reference($alias1, $repo->id, $reference);
781         $fs->create_file_from_reference($alias2, $repo->id, $reference);
783         $result = $fs->search_references($reference);
784         $this->assertTrue(is_array($result));
785         $this->assertEquals(count($result), 2);
786         foreach ($result as $alias) {
787             $this->assertTrue($alias instanceof stored_file);
788         }
790         $result = $fs->search_references_count($reference);
791         $this->assertSame($result, 2);
793         // The method can't be used for references to files outside the filepool
794         $exceptionthrown = false;
795         try {
796             $fs->search_references('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
797         } catch (file_reference_exception $e) {
798             $exceptionthrown = true;
799         }
800         $this->assertTrue($exceptionthrown);
802         $exceptionthrown = false;
803         try {
804             $fs->search_references_count('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
805         } catch (file_reference_exception $e) {
806             $exceptionthrown = true;
807         }
808         $this->assertTrue($exceptionthrown);
809     }
811     public function test_delete_area_files() {
812         $user = $this->setup_three_private_files();
813         $fs = get_file_storage();
815         // Get area files with default options.
816         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
817         // Should be the two files we added plus the folder.
818         $this->assertEquals(4, count($areafiles));
819         $fs->delete_area_files($user->ctxid, 'user', 'private');
821         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
822         // Should be the two files we added plus the folder.
823         $this->assertEquals(0, count($areafiles));
824     }
826     public function test_delete_area_files_itemid() {
827         $user = $this->setup_three_private_files();
828         $fs = get_file_storage();
830         // Get area files with default options.
831         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
832         // Should be the two files we added plus the folder.
833         $this->assertEquals(4, count($areafiles));
834         $fs->delete_area_files($user->ctxid, 'user', 'private', 9999);
836         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
837         $this->assertEquals(4, count($areafiles));
838     }
840     public function test_delete_area_files_select() {
841         $user = $this->setup_three_private_files();
842         $fs = get_file_storage();
844         // Get area files with default options.
845         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
846         // Should be the two files we added plus the folder.
847         $this->assertEquals(4, count($areafiles));
848         $fs->delete_area_files_select($user->ctxid, 'user', 'private', '!= :notitemid', array('notitemid'=>9999));
850         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
851         // Should be the two files we added plus the folder.
852         $this->assertEquals(0, count($areafiles));
853     }
855     public function test_delete_component_files() {
856         $user = $this->setup_three_private_files();
857         $fs = get_file_storage();
859         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
860         $this->assertEquals(4, count($areafiles));
861         $fs->delete_component_files('user');
862         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
863         $this->assertEquals(0, count($areafiles));
864     }
866     public function test_create_file_from_url() {
867         $this->resetAfterTest(true);
869         $syscontext = context_system::instance();
870         $filerecord = array(
871             'contextid' => $syscontext->id,
872             'component' => 'core',
873             'filearea'  => 'unittest',
874             'itemid'    => 0,
875             'filepath'  => '/downloadtest/',
876         );
877         $url = 'http://download.moodle.org/unittest/test.html';
879         $fs = get_file_storage();
881         // Test creating file without filename.
882         $file1 = $fs->create_file_from_url($filerecord, $url);
883         $this->assertInstanceOf('stored_file', $file1);
885         // Set filename.
886         $filerecord['filename'] = 'unit-test-filename.html';
887         $file2 = $fs->create_file_from_url($filerecord, $url);
888         $this->assertInstanceOf('stored_file', $file2);
890         // Use temporary file.
891         $filerecord['filename'] = 'unit-test-with-temp-file.html';
892         $file3 = $fs->create_file_from_url($filerecord, $url, null, true);
893         $file3 = $this->assertInstanceOf('stored_file', $file3);
894     }
896     public function test_cron() {
897         $this->resetAfterTest(true);
899         // Note: this is only testing DB compatibility atm, rather than
900         // that work is done.
901         $fs = get_file_storage();
903         $this->expectOutputRegex('/Cleaning up/');
904         $fs->cron();
905     }
907     public function test_is_area_empty() {
908         $user = $this->setup_three_private_files();
909         $fs = get_file_storage();
911         $this->assertFalse($fs->is_area_empty($user->ctxid, 'user', 'private'));
913         // File area with madeup itemid should be empty.
914         $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999));
915         // Still empty with dirs included.
916         $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999, false));
917     }
919     public function test_move_area_files_to_new_context() {
920         $this->resetAfterTest(true);
922         // Create a course with a page resource.
923         $course = $this->getDataGenerator()->create_course();
924         $page1 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
925         $page1context = context_module::instance($page1->cmid);
927         // Add a file to the page.
928         $fs = get_file_storage();
929         $filerecord = array(
930             'contextid' => $page1context->id,
931             'component' => 'mod_page',
932             'filearea'  => 'content',
933             'itemid'    => 0,
934             'filepath'  => '/',
935             'filename'  => 'unit-test-file.txt',
936         );
938         $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
939         $this->assertInstanceOf('stored_file', $originalfile);
941         $pagefiles = $fs->get_area_files($page1context->id, 'mod_page', 'content', 0, 'sortorder', false);
942         // Should be one file in filearea.
943         $this->assertFalse($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
945         // Create a new page.
946         $page2 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
947         $page2context = context_module::instance($page2->cmid);
949         // Newly created page area is empty.
950         $this->assertTrue($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
952         // Move the files.
953         $fs->move_area_files_to_new_context($page1context->id, $page2context->id, 'mod_page', 'content');
955         // Page2 filearea should no longer be empty.
956         $this->assertFalse($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
958         // Page1 filearea should now be empty.
959         $this->assertTrue($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
961         $page2files = $fs->get_area_files($page2context->id, 'mod_page', 'content', 0, 'sortorder', false);
962         $movedfile = reset($page2files);
964         // The two files should have the same content hash.
965         $this->assertEquals($movedfile->get_contenthash(), $originalfile->get_contenthash());
966     }
968     public function test_convert_image() {
969         global $CFG;
971         $this->resetAfterTest(false);
973         $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
974         $syscontext = context_system::instance();
975         $filerecord = array(
976             'contextid' => $syscontext->id,
977             'component' => 'core',
978             'filearea'  => 'unittest',
979             'itemid'    => 0,
980             'filepath'  => '/images/',
981             'filename'  => 'testimage.jpg',
982         );
984         $fs = get_file_storage();
985         $original = $fs->create_file_from_pathname($filerecord, $filepath);
987         $filerecord['filename'] = 'testimage-converted-10x10.jpg';
988         $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
989         $this->assertInstanceOf('stored_file', $converted);
991         $filerecord['filename'] = 'testimage-convereted-nosize.jpg';
992         $converted = $fs->convert_image($filerecord, $original);
993         $this->assertInstanceOf('stored_file', $converted);
994     }
996     private function generate_file_record() {
997         $syscontext = context_system::instance();
998         $filerecord = new stdClass();
999         $filerecord->contextid = $syscontext->id;
1000         $filerecord->component = 'core';
1001         $filerecord->filearea = 'phpunit';
1002         $filerecord->filepath = '/';
1003         $filerecord->filename = 'testfile.txt';
1004         $filerecord->itemid = 0;
1006         return $filerecord;
1007     }
1009     public function test_create_file_from_storedfile_file_invalid() {
1010         $this->resetAfterTest(true);
1012         $filerecord = $this->generate_file_record();
1014         $fs = get_file_storage();
1015         $this->setExpectedException('file_exception');
1016         // Create a file from a file id which doesn't exist.
1017         $fs->create_file_from_storedfile($filerecord,  9999);
1018     }
1020     public function test_create_file_from_storedfile_contextid_invalid() {
1021         $this->resetAfterTest(true);
1023         $filerecord = $this->generate_file_record();
1025         $fs = get_file_storage();
1026         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1027         $this->assertInstanceOf('stored_file', $file1);
1029         $filerecord->filename = 'invalid.txt';
1030         $filerecord->contextid = 'invalid';
1032         $this->setExpectedException('file_exception', 'Invalid contextid');
1033         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1034     }
1036     public function test_create_file_from_storedfile_component_invalid() {
1037         $this->resetAfterTest(true);
1039         $filerecord = $this->generate_file_record();
1041         $fs = get_file_storage();
1042         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1043         $this->assertInstanceOf('stored_file', $file1);
1045         $filerecord->filename = 'invalid.txt';
1046         $filerecord->component = 'bad/component';
1048         $this->setExpectedException('file_exception', 'Invalid component');
1049         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1050     }
1052     public function test_create_file_from_storedfile_filearea_invalid() {
1053         $this->resetAfterTest(true);
1055         $filerecord = $this->generate_file_record();
1057         $fs = get_file_storage();
1058         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1059         $this->assertInstanceOf('stored_file', $file1);
1061         $filerecord->filename = 'invalid.txt';
1062         $filerecord->filearea = 'bad-filearea';
1064         $this->setExpectedException('file_exception', 'Invalid filearea');
1065         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1066     }
1068     public function test_create_file_from_storedfile_itemid_invalid() {
1069         $this->resetAfterTest(true);
1071         $filerecord = $this->generate_file_record();
1073         $fs = get_file_storage();
1074         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1075         $this->assertInstanceOf('stored_file', $file1);
1077         $filerecord->filename = 'invalid.txt';
1078         $filerecord->itemid = 'bad-itemid';
1080         $this->setExpectedException('file_exception', 'Invalid itemid');
1081         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1082     }
1084     public function test_create_file_from_storedfile_filepath_invalid() {
1085         $this->resetAfterTest(true);
1087         $filerecord = $this->generate_file_record();
1089         $fs = get_file_storage();
1090         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1091         $this->assertInstanceOf('stored_file', $file1);
1093         $filerecord->filename = 'invalid.txt';
1094         $filerecord->filepath = 'a-/bad/-filepath';
1096         $this->setExpectedException('file_exception', 'Invalid file path');
1097         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1098     }
1100     public function test_create_file_from_storedfile_filename_invalid() {
1101         $this->resetAfterTest(true);
1103         $filerecord = $this->generate_file_record();
1105         $fs = get_file_storage();
1106         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1107         $this->assertInstanceOf('stored_file', $file1);
1109         $filerecord->filename = '';
1111         $this->setExpectedException('file_exception', 'Invalid file name');
1112         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1113     }
1115     public function test_create_file_from_storedfile_timecreated_invalid() {
1116         $this->resetAfterTest(true);
1118         $filerecord = $this->generate_file_record();
1120         $fs = get_file_storage();
1121         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1122         $this->assertInstanceOf('stored_file', $file1);
1124         $filerecord->filename = 'invalid.txt';
1125         $filerecord->timecreated = 'today';
1127         $this->setExpectedException('file_exception', 'Invalid file timecreated');
1128         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1129     }
1131     public function test_create_file_from_storedfile_timemodified_invalid() {
1132         $this->resetAfterTest(true);
1134         $filerecord = $this->generate_file_record();
1136         $fs = get_file_storage();
1137         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1138         $this->assertInstanceOf('stored_file', $file1);
1140         $filerecord->filename = 'invalid.txt';
1141         $filerecord->timemodified  = 'today';
1143         $this->setExpectedException('file_exception', 'Invalid file timemodified');
1144         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1145     }
1147     public function test_create_file_from_storedfile_duplicate() {
1148         $this->resetAfterTest(true);
1150         $filerecord = $this->generate_file_record();
1152         $fs = get_file_storage();
1153         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1154         $this->assertInstanceOf('stored_file', $file1);
1156         // Creating a file validating unique constraint.
1157         $this->setExpectedException('stored_file_creation_exception');
1158         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1159     }
1161     public function test_create_file_from_storedfile() {
1162         $this->resetAfterTest(true);
1164         $syscontext = context_system::instance();
1166         $filerecord = new stdClass();
1167         $filerecord->contextid = $syscontext->id;
1168         $filerecord->component = 'core';
1169         $filerecord->filearea = 'phpunit';
1170         $filerecord->filepath = '/';
1171         $filerecord->filename = 'testfile.txt';
1172         $filerecord->itemid = 0;
1174         $fs = get_file_storage();
1176         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1177         $this->assertInstanceOf('stored_file', $file1);
1179         $filerecord->filename = 'test-create-file-from-storedfile.txt';
1180         $file2 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1181         $this->assertInstanceOf('stored_file', $file2);
1183         // These will be normalised to current time..
1184         $filerecord->timecreated = -100;
1185         $filerecord->timemodified= -100;
1186         $filerecord->filename = 'test-create-file-from-storedfile-bad-dates.txt';
1188         $file3 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1189         $this->assertInstanceOf('stored_file', $file3);
1191         $this->assertNotEquals($file3->get_timemodified(), $filerecord->timemodified);
1192         $this->assertNotEquals($file3->get_timecreated(), $filerecord->timecreated);
1193     }
1195     public function test_create_file_from_string_contextid_invalid() {
1196         $this->resetAfterTest(true);
1198         $filerecord = $this->generate_file_record();
1199         $fs = get_file_storage();
1201         $filerecord->contextid = 'invalid';
1203         $this->setExpectedException('file_exception', 'Invalid contextid');
1204         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1205     }
1207     public function test_create_file_from_string_component_invalid() {
1208         $this->resetAfterTest(true);
1210         $filerecord = $this->generate_file_record();
1211         $fs = get_file_storage();
1213         $filerecord->component = 'bad/component';
1215         $this->setExpectedException('file_exception', 'Invalid component');
1216         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1217     }
1219     public function test_create_file_from_string_filearea_invalid() {
1220         $this->resetAfterTest(true);
1222         $filerecord = $this->generate_file_record();
1223         $fs = get_file_storage();
1225         $filerecord->filearea = 'bad-filearea';
1227         $this->setExpectedException('file_exception', 'Invalid filearea');
1228         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1229     }
1231     public function test_create_file_from_string_itemid_invalid() {
1232         $this->resetAfterTest(true);
1234         $filerecord = $this->generate_file_record();
1235         $fs = get_file_storage();
1237         $filerecord->itemid = 'bad-itemid';
1239         $this->setExpectedException('file_exception', 'Invalid itemid');
1240         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1241     }
1243     public function test_create_file_from_string_filepath_invalid() {
1244         $this->resetAfterTest(true);
1246         $filerecord = $this->generate_file_record();
1247         $fs = get_file_storage();
1249         $filerecord->filepath = 'a-/bad/-filepath';
1251         $this->setExpectedException('file_exception', 'Invalid file path');
1252         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1253     }
1255     public function test_create_file_from_string_filename_invalid() {
1256         $this->resetAfterTest(true);
1258         $filerecord = $this->generate_file_record();
1259         $fs = get_file_storage();
1261         $filerecord->filename = '';
1263         $this->setExpectedException('file_exception', 'Invalid file name');
1264         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1265     }
1267     public function test_create_file_from_string_timecreated_invalid() {
1268         $this->resetAfterTest(true);
1270         $filerecord = $this->generate_file_record();
1271         $fs = get_file_storage();
1273         $filerecord->timecreated = 'today';
1275         $this->setExpectedException('file_exception', 'Invalid file timecreated');
1276         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1277     }
1279     public function test_create_file_from_string_timemodified_invalid() {
1280         $this->resetAfterTest(true);
1282         $filerecord = $this->generate_file_record();
1283         $fs = get_file_storage();
1285         $filerecord->timemodified  = 'today';
1287         $this->setExpectedException('file_exception', 'Invalid file timemodified');
1288         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1289     }
1291     public function test_create_file_from_string_duplicate() {
1292         $this->resetAfterTest(true);
1294         $filerecord = $this->generate_file_record();
1295         $fs = get_file_storage();
1297         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1299         // Creating a file validating unique constraint.
1300         $this->setExpectedException('stored_file_creation_exception');
1301         $file2 = $fs->create_file_from_string($filerecord, 'text contents');
1302     }
1304     public function test_create_file_from_pathname_contextid_invalid() {
1305         global $CFG;
1306         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1308         $this->resetAfterTest(true);
1310         $filerecord = $this->generate_file_record();
1311         $fs = get_file_storage();
1313         $filerecord->contextid = 'invalid';
1315         $this->setExpectedException('file_exception', 'Invalid contextid');
1316         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1317     }
1319     public function test_create_file_from_pathname_component_invalid() {
1320         global $CFG;
1321         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1323         $this->resetAfterTest(true);
1325         $filerecord = $this->generate_file_record();
1326         $fs = get_file_storage();
1328         $filerecord->component = 'bad/component';
1330         $this->setExpectedException('file_exception', 'Invalid component');
1331         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1332     }
1334     public function test_create_file_from_pathname_filearea_invalid() {
1335         global $CFG;
1336         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1338         $this->resetAfterTest(true);
1340         $filerecord = $this->generate_file_record();
1341         $fs = get_file_storage();
1343         $filerecord->filearea = 'bad-filearea';
1345         $this->setExpectedException('file_exception', 'Invalid filearea');
1346         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1347     }
1349     public function test_create_file_from_pathname_itemid_invalid() {
1350         global $CFG;
1351         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1353         $this->resetAfterTest(true);
1355         $filerecord = $this->generate_file_record();
1356         $fs = get_file_storage();
1358         $filerecord->itemid = 'bad-itemid';
1360         $this->setExpectedException('file_exception', 'Invalid itemid');
1361         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1362     }
1364     public function test_create_file_from_pathname_filepath_invalid() {
1365         global $CFG;
1366         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1368         $this->resetAfterTest(true);
1370         $filerecord = $this->generate_file_record();
1371         $fs = get_file_storage();
1373         $filerecord->filepath = 'a-/bad/-filepath';
1375         $this->setExpectedException('file_exception', 'Invalid file path');
1376         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1377     }
1379     public function test_create_file_from_pathname_filename_invalid() {
1380         global $CFG;
1381         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1383         $this->resetAfterTest(true);
1385         $filerecord = $this->generate_file_record();
1386         $fs = get_file_storage();
1388         $filerecord->filename = '';
1390         $this->setExpectedException('file_exception', 'Invalid file name');
1391         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1392     }
1394     public function test_create_file_from_pathname_timecreated_invalid() {
1395         global $CFG;
1396         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1398         $this->resetAfterTest(true);
1400         $filerecord = $this->generate_file_record();
1401         $fs = get_file_storage();
1403         $filerecord->timecreated = 'today';
1405         $this->setExpectedException('file_exception', 'Invalid file timecreated');
1406         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1407     }
1409     public function test_create_file_from_pathname_timemodified_invalid() {
1410         global $CFG;
1411         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1413         $this->resetAfterTest(true);
1415         $filerecord = $this->generate_file_record();
1416         $fs = get_file_storage();
1418         $filerecord->timemodified  = 'today';
1420         $this->setExpectedException('file_exception', 'Invalid file timemodified');
1421         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1422     }
1424     public function test_create_file_from_pathname_duplicate_file() {
1425         global $CFG;
1426         $this->resetAfterTest(true);
1428         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1430         $filerecord = $this->generate_file_record();
1431         $fs = get_file_storage();
1433         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1434         $this->assertInstanceOf('stored_file', $file1);
1436         // Creating a file validating unique constraint.
1437         $this->setExpectedException('stored_file_creation_exception');
1438         $file2 = $fs->create_file_from_pathname($filerecord, $path);
1439     }
1441     /**
1442      * Calling stored_file::delete_reference() on a non-reference file throws coding_exception
1443      */
1444     public function test_delete_reference_on_nonreference() {
1446         $this->resetAfterTest(true);
1447         $user = $this->setup_three_private_files();
1448         $fs = get_file_storage();
1449         $repos = repository::get_instances(array('type'=>'user'));
1450         $repo = reset($repos);
1452         $file = null;
1453         foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1454             if (!$areafile->is_directory()) {
1455                 $file = $areafile;
1456                 break;
1457             }
1458         }
1459         $this->assertInstanceOf('stored_file', $file);
1460         $this->assertFalse($file->is_external_file());
1462         $this->setExpectedException('coding_exception');
1463         $file->delete_reference();
1464     }
1466     /**
1467      * Calling stored_file::delete_reference() on a reference file does not affect other
1468      * symlinks to the same original
1469      */
1470     public function test_delete_reference_one_symlink_does_not_rule_them_all() {
1472         $this->resetAfterTest(true);
1473         $user = $this->setup_three_private_files();
1474         $fs = get_file_storage();
1475         $repos = repository::get_instances(array('type'=>'user'));
1476         $repo = reset($repos);
1478         // create two aliases linking the same original
1480         $originalfile = null;
1481         foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1482             if (!$areafile->is_directory()) {
1483                 $originalfile = $areafile;
1484                 break;
1485             }
1486         }
1487         $this->assertInstanceOf('stored_file', $originalfile);
1489         // calling delete_reference() on a non-reference file
1491         $originalrecord = array(
1492             'contextid' => $originalfile->get_contextid(),
1493             'component' => $originalfile->get_component(),
1494             'filearea'  => $originalfile->get_filearea(),
1495             'itemid'    => $originalfile->get_itemid(),
1496             'filepath'  => $originalfile->get_filepath(),
1497             'filename'  => $originalfile->get_filename(),
1498         );
1500         $aliasrecord = $this->generate_file_record();
1501         $aliasrecord->filepath = '/A/';
1502         $aliasrecord->filename = 'symlink.txt';
1504         $ref = $fs->pack_reference($originalrecord);
1505         $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1507         $aliasrecord->filepath = '/B/';
1508         $aliasrecord->filename = 'symlink.txt';
1509         $ref = $fs->pack_reference($originalrecord);
1510         $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1512         // refetch A/symlink.txt
1513         $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1514             $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
1515         $this->assertTrue($symlink1->is_external_file());
1517         // unlink the A/symlink.txt
1518         $symlink1->delete_reference();
1519         $this->assertFalse($symlink1->is_external_file());
1521         // make sure that B/symlink.txt has not been affected
1522         $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1523             $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
1524         $this->assertTrue($symlink2->is_external_file());
1525     }
1527     public function test_get_unused_filename() {
1528         global $USER;
1529         $this->resetAfterTest(true);
1531         $fs = get_file_storage();
1532         $this->setAdminUser();
1533         $contextid = context_user::instance($USER->id)->id;
1534         $component = 'user';
1535         $filearea = 'private';
1536         $itemid = 0;
1537         $filepath = '/';
1539         // Create some private files.
1540         $file = new stdClass;
1541         $file->contextid = $contextid;
1542         $file->component = 'user';
1543         $file->filearea  = 'private';
1544         $file->itemid    = 0;
1545         $file->filepath  = '/';
1546         $file->source    = 'test';
1547         $filenames = array('foo.txt', 'foo (1).txt', 'foo (20).txt', 'foo (999)', 'bar.jpg', 'What (a cool file).jpg',
1548                 'Hurray! (1).php', 'Hurray! (2).php', 'Hurray! (9a).php', 'Hurray! (abc).php');
1549         foreach ($filenames as $key => $filename) {
1550             $file->filename = $filename;
1551             $userfile = $fs->create_file_from_string($file, "file $key $filename content");
1552             $this->assertInstanceOf('stored_file', $userfile);
1553         }
1555         // Asserting new generated names.
1556         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'unused.txt');
1557         $this->assertEquals('unused.txt', $newfilename);
1558         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo.txt');
1559         $this->assertEquals('foo (21).txt', $newfilename);
1560         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (1).txt');
1561         $this->assertEquals('foo (21).txt', $newfilename);
1562         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (2).txt');
1563         $this->assertEquals('foo (2).txt', $newfilename);
1564         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (20).txt');
1565         $this->assertEquals('foo (21).txt', $newfilename);
1566         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo');
1567         $this->assertEquals('foo', $newfilename);
1568         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (123)');
1569         $this->assertEquals('foo (123)', $newfilename);
1570         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (999)');
1571         $this->assertEquals('foo (1000)', $newfilename);
1572         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.png');
1573         $this->assertEquals('bar.png', $newfilename);
1574         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (12).png');
1575         $this->assertEquals('bar (12).png', $newfilename);
1576         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.jpg');
1577         $this->assertEquals('bar (1).jpg', $newfilename);
1578         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (1).jpg');
1579         $this->assertEquals('bar (1).jpg', $newfilename);
1580         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'What (a cool file).jpg');
1581         $this->assertEquals('What (a cool file) (1).jpg', $newfilename);
1582         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'Hurray! (1).php');
1583         $this->assertEquals('Hurray! (3).php', $newfilename);
1585         $this->setExpectedException('coding_exception');
1586         $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, '');
1587     }
1590 class test_stored_file_inspection extends stored_file {
1591     public static function get_pretected_pathname(stored_file $file) {
1592         return $file->get_pathname_by_contenthash();
1593     }