MDL-64506 templates: BS2 pull-x -> BS4 float-x
[moodle.git] / lib / filestorage / tests / file_storage_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 /lib/filestorage/file_storage.php
19  *
20  * @package   core_files
21  * @category  phpunit
22  * @copyright 2012 David Mudrak <david@moodle.com>
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/filelib.php');
30 require_once($CFG->dirroot . '/repository/lib.php');
31 require_once($CFG->libdir . '/filestorage/stored_file.php');
33 class core_files_file_storage_testcase extends advanced_testcase {
35     /**
36      * Files can be created from strings.
37      */
38     public function test_create_file_from_string() {
39         global $DB;
41         $this->resetAfterTest(true);
43         // Number of files installed in the database on a fresh Moodle site.
44         $installedfiles = $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->assertTrue($file->compare_to_string($content));
63         $this->assertSame($pathhash, $file->get_pathnamehash());
65         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
67         $method = new ReflectionMethod('file_system', 'get_local_path_from_storedfile');
68         $method->setAccessible(true);
69         $filesystem = $fs->get_file_system();
70         $location = $method->invokeArgs($filesystem, array($file, true));
72         $this->assertFileExists($location);
74         // Verify the dir placeholder files are created.
75         $this->assertEquals($installedfiles + 3, $DB->count_records('files', array()));
76         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
77         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
79         // Tests that missing content file is recreated.
81         unlink($location);
82         $this->assertFileNotExists($location);
84         $filerecord['filename'] = 'testfile2.txt';
85         $file2 = $fs->create_file_from_string($filerecord, $content);
86         $this->assertInstanceOf('stored_file', $file2);
87         $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
88         $this->assertFileExists($location);
90         $this->assertEquals($installedfiles + 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($installedfiles + 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         // Number of files installed in the database on a fresh Moodle site.
117         $installedfiles = $DB->count_records('files', array());
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->assertTrue($file->compare_to_path($filepath));
137         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
139         $method = new ReflectionMethod('file_system', 'get_local_path_from_storedfile');
140         $method->setAccessible(true);
141         $filesystem = $fs->get_file_system();
142         $location = $method->invokeArgs($filesystem, array($file, true));
144         $this->assertFileExists($location);
146         // Verify the dir placeholder files are created.
147         $this->assertEquals($installedfiles + 3, $DB->count_records('files', array()));
148         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
149         $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
151         // Tests that missing content file is recreated.
153         unlink($location);
154         $this->assertFileNotExists($location);
156         $filerecord['filename'] = 'testfile2.jpg';
157         $file2 = $fs->create_file_from_pathname($filerecord, $filepath);
158         $this->assertInstanceOf('stored_file', $file2);
159         $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
160         $this->assertFileExists($location);
162         $this->assertEquals($installedfiles + 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($installedfiles + 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();
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->expectException('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();
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->expectException('file_exception');
310         $this->expectExceptionMessage('Can not create file "1/core/unittest/0/test/newtest.txt" (file exists, cannot rename)');
311         // This shall throw exception.
312         $originalfile->rename($newpath, $newname);
313     }
315     /**
316      * Create file from reference tests
317      *
318      * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
319      */
320     public function test_create_file_from_reference() {
321         global $CFG, $DB;
323         $this->resetAfterTest();
324         // Create user.
325         $generator = $this->getDataGenerator();
326         $user = $generator->create_user();
327         $this->setUser($user);
328         $usercontext = context_user::instance($user->id);
329         $syscontext = context_system::instance();
331         $fs = get_file_storage();
333         $repositorypluginname = 'user';
334         // Override repository permission.
335         $capability = 'repository/' . $repositorypluginname . ':view';
336         $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest'));
337         assign_capability($capability, CAP_ALLOW, $guestroleid, $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     /**
401      * Create file from reference tests
402      *
403      * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
404      */
405     public function test_create_file_from_reference_with_content_hash() {
406         global $CFG, $DB;
408         $this->resetAfterTest();
409         // Create user.
410         $generator = $this->getDataGenerator();
411         $user = $generator->create_user();
412         $this->setUser($user);
413         $usercontext = context_user::instance($user->id);
414         $syscontext = context_system::instance();
416         $fs = get_file_storage();
418         $repositorypluginname = 'user';
419         // Override repository permission.
420         $capability = 'repository/' . $repositorypluginname . ':view';
421         $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest'));
422         assign_capability($capability, CAP_ALLOW, $guestroleid, $syscontext->id, true);
424         $args = array();
425         $args['type'] = $repositorypluginname;
426         $repos = repository::get_instances($args);
427         $userrepository = reset($repos);
428         $this->assertInstanceOf('repository', $userrepository);
430         $component = 'user';
431         $filearea = 'private';
432         $itemid = 0;
433         $filepath = '/';
434         $filename = 'userfile.txt';
436         $filerecord = array(
437                 'contextid' => $usercontext->id,
438                 'component' => $component,
439                 'filearea' => $filearea,
440                 'itemid' => $itemid,
441                 'filepath' => $filepath,
442                 'filename' => $filename,
443         );
445         $content = 'Test content';
446         $originalfile = $fs->create_file_from_string($filerecord, $content);
447         $this->assertInstanceOf('stored_file', $originalfile);
449         $otherfilerecord = $filerecord;
450         $otherfilerecord['filename'] = 'other-filename.txt';
451         $otherfilewithsamecontents = $fs->create_file_from_string($otherfilerecord, $content);
452         $this->assertInstanceOf('stored_file', $otherfilewithsamecontents);
454         $newfilerecord = array(
455                 'contextid' => $syscontext->id,
456                 'component' => 'core',
457                 'filearea' => 'phpunit',
458                 'itemid' => 0,
459                 'filepath' => $filepath,
460                 'filename' => $filename,
461                 'contenthash' => $originalfile->get_contenthash(),
462         );
463         $ref = $fs->pack_reference($filerecord);
464         $newstoredfile = $fs->create_file_from_reference($newfilerecord, $userrepository->id, $ref);
465         $this->assertInstanceOf('stored_file', $newstoredfile);
466         $this->assertEquals($userrepository->id, $newstoredfile->get_repository_id());
467         $this->assertEquals($originalfile->get_contenthash(), $newstoredfile->get_contenthash());
468         $this->assertEquals($originalfile->get_filesize(), $newstoredfile->get_filesize());
469         $this->assertRegExp('#' . $filename . '$#', $newstoredfile->get_reference_details());
470     }
472     private function setup_three_private_files() {
474         $this->resetAfterTest();
476         $generator = $this->getDataGenerator();
477         $user = $generator->create_user();
478         $this->setUser($user->id);
479         $usercontext = context_user::instance($user->id);
480         // Create a user private file.
481         $file1 = new stdClass;
482         $file1->contextid = $usercontext->id;
483         $file1->component = 'user';
484         $file1->filearea  = 'private';
485         $file1->itemid    = 0;
486         $file1->filepath  = '/';
487         $file1->filename  = '1.txt';
488         $file1->source    = 'test';
490         $fs = get_file_storage();
491         $userfile1 = $fs->create_file_from_string($file1, 'file1 content');
492         $this->assertInstanceOf('stored_file', $userfile1);
494         $file2 = clone($file1);
495         $file2->filename = '2.txt';
496         $userfile2 = $fs->create_file_from_string($file2, 'file2 content longer');
497         $this->assertInstanceOf('stored_file', $userfile2);
499         $file3 = clone($file1);
500         $file3->filename = '3.txt';
501         $userfile3 = $fs->create_file_from_storedfile($file3, $userfile2);
502         $this->assertInstanceOf('stored_file', $userfile3);
504         $user->ctxid = $usercontext->id;
506         return $user;
507     }
509     public function test_get_area_files() {
510         $user = $this->setup_three_private_files();
511         $fs = get_file_storage();
513         // Get area files with default options.
514         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
516         // Should be the two files we added plus the folder.
517         $this->assertEquals(4, count($areafiles));
519         // Verify structure.
520         foreach ($areafiles as $key => $file) {
521             $this->assertInstanceOf('stored_file', $file);
522             $this->assertEquals($key, $file->get_pathnamehash());
523         }
525         // Get area files without a folder.
526         $folderlessfiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
527         // Should be the two files without folder.
528         $this->assertEquals(3, count($folderlessfiles));
530         // Verify structure.
531         foreach ($folderlessfiles as $key => $file) {
532             $this->assertInstanceOf('stored_file', $file);
533             $this->assertEquals($key, $file->get_pathnamehash());
534         }
536         // Get area files ordered by id.
537         $filesbyid  = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
538         // Should be the two files without folder.
539         $this->assertEquals(3, count($filesbyid));
541         // Verify structure.
542         foreach ($filesbyid as $key => $file) {
543             $this->assertInstanceOf('stored_file', $file);
544             $this->assertEquals($key, $file->get_pathnamehash());
545         }
547         // Test the limit feature to retrieve each individual file.
548         $limited = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false,
549                 0, 0, 1);
550         $mapfunc = function($f) {
551             return $f->get_filename();
552         };
553         $this->assertEquals(array('1.txt'), array_values(array_map($mapfunc, $limited)));
554         $limited = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false,
555                 0, 1, 50);
556         $this->assertEquals(array('2.txt', '3.txt'), array_values(array_map($mapfunc, $limited)));
558         // Test with an itemid with no files.
559         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', 666, 'sortorder', false);
560         // Should be none.
561         $this->assertEmpty($areafiles);
562     }
564     public function test_get_area_tree() {
565         $user = $this->setup_three_private_files();
566         $fs = get_file_storage();
568         // Get area files with default options.
569         $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
570         $this->assertEmpty($areatree['subdirs']);
571         $this->assertNotEmpty($areatree['files']);
572         $this->assertCount(3, $areatree['files']);
574         // Ensure an empty try with a fake itemid.
575         $emptytree = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
576         $this->assertEmpty($emptytree['subdirs']);
577         $this->assertEmpty($emptytree['files']);
579         // Create a subdir.
580         $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
581         $this->assertInstanceOf('stored_file', $dir);
583         // Add a file to the subdir.
584         $filerecord = array(
585             'contextid' => $user->ctxid,
586             'component' => 'user',
587             'filearea'  => 'private',
588             'itemid'    => 0,
589             'filepath'  => '/testsubdir/',
590             'filename'  => 'test-get-area-tree.txt',
591         );
593         $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
594         $this->assertInstanceOf('stored_file', $directoryfile);
596         $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
598         // At the top level there should still be 3 files.
599         $this->assertCount(3, $areatree['files']);
601         // There should now be a subdirectory.
602         $this->assertCount(1, $areatree['subdirs']);
604         // The test subdir is named testsubdir.
605         $subdir = $areatree['subdirs']['testsubdir'];
606         $this->assertNotEmpty($subdir);
607         // It should have one file we added.
608         $this->assertCount(1, $subdir['files']);
609         // And no subdirs itself.
610         $this->assertCount(0, $subdir['subdirs']);
612         // Verify the file is the one we added.
613         $subdirfile = reset($subdir['files']);
614         $this->assertInstanceOf('stored_file', $subdirfile);
615         $this->assertEquals($filerecord['filename'], $subdirfile->get_filename());
616     }
618     public function test_get_file_by_id() {
619         $user = $this->setup_three_private_files();
620         $fs = get_file_storage();
622         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
624         // Test get_file_by_id.
625         $filebyid = reset($areafiles);
626         $shouldbesame = $fs->get_file_by_id($filebyid->get_id());
627         $this->assertEquals($filebyid->get_contenthash(), $shouldbesame->get_contenthash());
629         // Test an id which doens't exist.
630         $doesntexist = $fs->get_file_by_id(99999);
631         $this->assertFalse($doesntexist);
632     }
634     public function test_get_file_by_hash() {
635         $user = $this->setup_three_private_files();
636         $fs = get_file_storage();
638         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
639         // Test get_file_by_hash.
640         $filebyhash = reset($areafiles);
641         $shouldbesame = $fs->get_file_by_hash($filebyhash->get_pathnamehash());
642         $this->assertEquals($filebyhash->get_id(), $shouldbesame->get_id());
644         // Test an hash which doens't exist.
645         $doesntexist = $fs->get_file_by_hash('DOESNTEXIST');
646         $this->assertFalse($doesntexist);
647     }
649     public function test_get_external_files() {
650         $user = $this->setup_three_private_files();
651         $fs = get_file_storage();
653         $repos = repository::get_instances(array('type'=>'user'));
654         $userrepository = reset($repos);
655         $this->assertInstanceOf('repository', $userrepository);
657         // No aliases yet.
658         $exfiles = $fs->get_external_files($userrepository->id, 'id');
659         $this->assertEquals(array(), $exfiles);
661         // Create three aliases linking the same original: $aliasfile1 and $aliasfile2 are
662         // created via create_file_from_reference(), $aliasfile3 created from $aliasfile2.
663         $originalfile = null;
664         foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
665             if (!$areafile->is_directory()) {
666                 $originalfile = $areafile;
667                 break;
668             }
669         }
670         $this->assertInstanceOf('stored_file', $originalfile);
671         $originalrecord = array(
672             'contextid' => $originalfile->get_contextid(),
673             'component' => $originalfile->get_component(),
674             'filearea'  => $originalfile->get_filearea(),
675             'itemid'    => $originalfile->get_itemid(),
676             'filepath'  => $originalfile->get_filepath(),
677             'filename'  => $originalfile->get_filename(),
678         );
680         $aliasrecord = $this->generate_file_record();
681         $aliasrecord->filepath = '/foo/';
682         $aliasrecord->filename = 'one.txt';
684         $ref = $fs->pack_reference($originalrecord);
685         $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
687         $aliasrecord->filepath = '/bar/';
688         $aliasrecord->filename = 'uno.txt';
689         // Change the order of the items in the array to make sure that it does not matter.
690         ksort($originalrecord);
691         $ref = $fs->pack_reference($originalrecord);
692         $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
694         $aliasrecord->filepath = '/bar/';
695         $aliasrecord->filename = 'jedna.txt';
696         $aliasfile3 = $fs->create_file_from_storedfile($aliasrecord, $aliasfile2);
698         // Make sure we get three aliases now.
699         $exfiles = $fs->get_external_files($userrepository->id, 'id');
700         $this->assertEquals(3, count($exfiles));
701         foreach ($exfiles as $exfile) {
702             $this->assertTrue($exfile->is_external_file());
703         }
704         // Make sure they all link the same original (thence that all are linked with the same
705         // record in {files_reference}).
706         $this->assertEquals($aliasfile1->get_referencefileid(), $aliasfile2->get_referencefileid());
707         $this->assertEquals($aliasfile3->get_referencefileid(), $aliasfile2->get_referencefileid());
708     }
710     public function test_create_directory_contextid_negative() {
711         $fs = get_file_storage();
713         $this->expectException('file_exception');
714         $fs->create_directory(-1, 'core', 'unittest', 0, '/');
715     }
717     public function test_create_directory_contextid_invalid() {
718         $fs = get_file_storage();
720         $this->expectException('file_exception');
721         $fs->create_directory('not an int', 'core', 'unittest', 0, '/');
722     }
724     public function test_create_directory_component_invalid() {
725         $fs = get_file_storage();
726         $syscontext = context_system::instance();
728         $this->expectException('file_exception');
729         $fs->create_directory($syscontext->id, 'bad/component', 'unittest', 0, '/');
730     }
732     public function test_create_directory_filearea_invalid() {
733         $fs = get_file_storage();
734         $syscontext = context_system::instance();
736         $this->expectException('file_exception');
737         $fs->create_directory($syscontext->id, 'core', 'bad-filearea', 0, '/');
738     }
740     public function test_create_directory_itemid_negative() {
741         $fs = get_file_storage();
742         $syscontext = context_system::instance();
744         $this->expectException('file_exception');
745         $fs->create_directory($syscontext->id, 'core', 'unittest', -1, '/');
746     }
748     public function test_create_directory_itemid_invalid() {
749         $fs = get_file_storage();
750         $syscontext = context_system::instance();
752         $this->expectException('file_exception');
753         $fs->create_directory($syscontext->id, 'core', 'unittest', 'notanint', '/');
754     }
756     public function test_create_directory_filepath_invalid() {
757         $fs = get_file_storage();
758         $syscontext = context_system::instance();
760         $this->expectException('file_exception');
761         $fs->create_directory($syscontext->id, 'core', 'unittest', 0, '/not-with-trailing/or-leading-slash');
762     }
764     public function test_get_directory_files() {
765         $user = $this->setup_three_private_files();
766         $fs = get_file_storage();
768         $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
769         $this->assertInstanceOf('stored_file', $dir);
771         // Add a file to the subdir.
772         $filerecord = array(
773             'contextid' => $user->ctxid,
774             'component' => 'user',
775             'filearea'  => 'private',
776             'itemid'    => 0,
777             'filepath'  => '/testsubdir/',
778             'filename'  => 'test-get-area-tree.txt',
779         );
781         $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
782         $this->assertInstanceOf('stored_file', $directoryfile);
784         // Don't recurse without dirs.
785         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
786         // 3 files only.
787         $this->assertCount(3, $files);
788         foreach ($files as $key => $file) {
789             $this->assertInstanceOf('stored_file', $file);
790             $this->assertEquals($key, $file->get_pathnamehash());
791         }
793         // Don't recurse with dirs.
794         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
795         // 3 files + 1 directory.
796         $this->assertCount(4, $files);
797         foreach ($files as $key => $file) {
798             $this->assertInstanceOf('stored_file', $file);
799             $this->assertEquals($key, $file->get_pathnamehash());
800         }
802         // Recurse with dirs.
803         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
804         // 3 files + 1 directory +  1 subdir file.
805         $this->assertCount(5, $files);
806         foreach ($files as $key => $file) {
807             $this->assertInstanceOf('stored_file', $file);
808             $this->assertEquals($key, $file->get_pathnamehash());
809         }
811         // Recurse without dirs.
812         $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
813         // 3 files +  1 subdir file.
814         $this->assertCount(4, $files);
815         foreach ($files as $key => $file) {
816             $this->assertInstanceOf('stored_file', $file);
817             $this->assertEquals($key, $file->get_pathnamehash());
818         }
819     }
821     public function test_search_references() {
822         $user = $this->setup_three_private_files();
823         $fs = get_file_storage();
824         $repos = repository::get_instances(array('type'=>'user'));
825         $repo = reset($repos);
827         $alias1 = array(
828             'contextid' => $user->ctxid,
829             'component' => 'user',
830             'filearea'  => 'private',
831             'itemid'    => 0,
832             'filepath'  => '/aliases/',
833             'filename'  => 'alias-to-1.txt'
834         );
836         $alias2 = array(
837             'contextid' => $user->ctxid,
838             'component' => 'user',
839             'filearea'  => 'private',
840             'itemid'    => 0,
841             'filepath'  => '/aliases/',
842             'filename'  => 'another-alias-to-1.txt'
843         );
845         $reference = file_storage::pack_reference(array(
846             'contextid' => $user->ctxid,
847             'component' => 'user',
848             'filearea'  => 'private',
849             'itemid'    => 0,
850             'filepath'  => '/',
851             'filename'  => '1.txt'
852         ));
854         // There are no aliases now.
855         $result = $fs->search_references($reference);
856         $this->assertEquals(array(), $result);
858         $result = $fs->search_references_count($reference);
859         $this->assertSame($result, 0);
861         // Create two aliases and make sure they are returned.
862         $fs->create_file_from_reference($alias1, $repo->id, $reference);
863         $fs->create_file_from_reference($alias2, $repo->id, $reference);
865         $result = $fs->search_references($reference);
866         $this->assertTrue(is_array($result));
867         $this->assertEquals(count($result), 2);
868         foreach ($result as $alias) {
869             $this->assertTrue($alias instanceof stored_file);
870         }
872         $result = $fs->search_references_count($reference);
873         $this->assertSame($result, 2);
875         // The method can't be used for references to files outside the filepool.
876         $exceptionthrown = false;
877         try {
878             $fs->search_references('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
879         } catch (file_reference_exception $e) {
880             $exceptionthrown = true;
881         }
882         $this->assertTrue($exceptionthrown);
884         $exceptionthrown = false;
885         try {
886             $fs->search_references_count('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
887         } catch (file_reference_exception $e) {
888             $exceptionthrown = true;
889         }
890         $this->assertTrue($exceptionthrown);
891     }
893     public function test_delete_area_files() {
894         $user = $this->setup_three_private_files();
895         $fs = get_file_storage();
897         // Get area files with default options.
898         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
899         // Should be the two files we added plus the folder.
900         $this->assertEquals(4, count($areafiles));
901         $fs->delete_area_files($user->ctxid, 'user', 'private');
903         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
904         // Should be the two files we added plus the folder.
905         $this->assertEquals(0, count($areafiles));
906     }
908     public function test_delete_area_files_itemid() {
909         $user = $this->setup_three_private_files();
910         $fs = get_file_storage();
912         // Get area files with default options.
913         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
914         // Should be the two files we added plus the folder.
915         $this->assertEquals(4, count($areafiles));
916         $fs->delete_area_files($user->ctxid, 'user', 'private', 9999);
918         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
919         $this->assertEquals(4, count($areafiles));
920     }
922     public function test_delete_area_files_select() {
923         $user = $this->setup_three_private_files();
924         $fs = get_file_storage();
926         // Get area files with default options.
927         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
928         // Should be the two files we added plus the folder.
929         $this->assertEquals(4, count($areafiles));
930         $fs->delete_area_files_select($user->ctxid, 'user', 'private', '!= :notitemid', array('notitemid'=>9999));
932         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
933         // Should be the two files we added plus the folder.
934         $this->assertEquals(0, count($areafiles));
935     }
937     public function test_delete_component_files() {
938         $user = $this->setup_three_private_files();
939         $fs = get_file_storage();
941         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
942         $this->assertEquals(4, count($areafiles));
943         $fs->delete_component_files('user');
944         $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
945         $this->assertEquals(0, count($areafiles));
946     }
948     public function test_create_file_from_url() {
949         $this->resetAfterTest(true);
951         $syscontext = context_system::instance();
952         $filerecord = array(
953             'contextid' => $syscontext->id,
954             'component' => 'core',
955             'filearea'  => 'unittest',
956             'itemid'    => 0,
957             'filepath'  => '/downloadtest/',
958         );
959         $url = $this->getExternalTestFileUrl('/test.html');
961         $fs = get_file_storage();
963         // Test creating file without filename.
964         $file1 = $fs->create_file_from_url($filerecord, $url);
965         $this->assertInstanceOf('stored_file', $file1);
967         // Set filename.
968         $filerecord['filename'] = 'unit-test-filename.html';
969         $file2 = $fs->create_file_from_url($filerecord, $url);
970         $this->assertInstanceOf('stored_file', $file2);
972         // Use temporary file.
973         $filerecord['filename'] = 'unit-test-with-temp-file.html';
974         $file3 = $fs->create_file_from_url($filerecord, $url, null, true);
975         $file3 = $this->assertInstanceOf('stored_file', $file3);
976     }
978     public function test_cron() {
979         $this->resetAfterTest(true);
981         // Note: this is only testing DB compatibility atm, rather than
982         // that work is done.
983         $fs = get_file_storage();
985         $this->expectOutputRegex('/Cleaning up/');
986         $fs->cron();
987     }
989     public function test_is_area_empty() {
990         $user = $this->setup_three_private_files();
991         $fs = get_file_storage();
993         $this->assertFalse($fs->is_area_empty($user->ctxid, 'user', 'private'));
995         // File area with madeup itemid should be empty.
996         $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999));
997         // Still empty with dirs included.
998         $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999, false));
999     }
1001     public function test_move_area_files_to_new_context() {
1002         $this->resetAfterTest(true);
1004         // Create a course with a page resource.
1005         $course = $this->getDataGenerator()->create_course();
1006         $page1 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
1007         $page1context = context_module::instance($page1->cmid);
1009         // Add a file to the page.
1010         $fs = get_file_storage();
1011         $filerecord = array(
1012             'contextid' => $page1context->id,
1013             'component' => 'mod_page',
1014             'filearea'  => 'content',
1015             'itemid'    => 0,
1016             'filepath'  => '/',
1017             'filename'  => 'unit-test-file.txt',
1018         );
1020         $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
1021         $this->assertInstanceOf('stored_file', $originalfile);
1023         $pagefiles = $fs->get_area_files($page1context->id, 'mod_page', 'content', 0, 'sortorder', false);
1024         // Should be one file in filearea.
1025         $this->assertFalse($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
1027         // Create a new page.
1028         $page2 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
1029         $page2context = context_module::instance($page2->cmid);
1031         // Newly created page area is empty.
1032         $this->assertTrue($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
1034         // Move the files.
1035         $fs->move_area_files_to_new_context($page1context->id, $page2context->id, 'mod_page', 'content');
1037         // Page2 filearea should no longer be empty.
1038         $this->assertFalse($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
1040         // Page1 filearea should now be empty.
1041         $this->assertTrue($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
1043         $page2files = $fs->get_area_files($page2context->id, 'mod_page', 'content', 0, 'sortorder', false);
1044         $movedfile = reset($page2files);
1046         // The two files should have the same content hash.
1047         $this->assertEquals($movedfile->get_contenthash(), $originalfile->get_contenthash());
1048     }
1050     public function test_convert_image() {
1051         global $CFG;
1053         $this->resetAfterTest(false);
1055         $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1056         $syscontext = context_system::instance();
1057         $filerecord = array(
1058             'contextid' => $syscontext->id,
1059             'component' => 'core',
1060             'filearea'  => 'unittest',
1061             'itemid'    => 0,
1062             'filepath'  => '/images/',
1063             'filename'  => 'testimage.jpg',
1064         );
1066         $fs = get_file_storage();
1067         $original = $fs->create_file_from_pathname($filerecord, $filepath);
1069         $filerecord['filename'] = 'testimage-converted-10x10.jpg';
1070         $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
1071         $this->assertInstanceOf('stored_file', $converted);
1073         $filerecord['filename'] = 'testimage-convereted-nosize.jpg';
1074         $converted = $fs->convert_image($filerecord, $original);
1075         $this->assertInstanceOf('stored_file', $converted);
1076     }
1078     public function test_convert_image_png() {
1079         global $CFG;
1081         $this->resetAfterTest(false);
1083         $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.png';
1084         $syscontext = context_system::instance();
1085         $filerecord = array(
1086             'contextid' => $syscontext->id,
1087             'component' => 'core',
1088             'filearea'  => 'unittest',
1089             'itemid'    => 0,
1090             'filepath'  => '/images/',
1091             'filename'  => 'testimage.png',
1092         );
1094         $fs = get_file_storage();
1095         $original = $fs->create_file_from_pathname($filerecord, $filepath);
1097         // Vanilla test.
1098         $filerecord['filename'] = 'testimage-converted-nosize.png';
1099         $vanilla = $fs->convert_image($filerecord, $original);
1100         $this->assertInstanceOf('stored_file', $vanilla);
1101         // Assert that byte 25 has the ascii value 6 for PNG-24.
1102         $this->assertTrue(ord(substr($vanilla->get_content(), 25, 1)) == 6);
1104         // 10x10 resize test; also testing for a ridiculous quality setting, which
1105         // we should if necessary scale to the 0 - 9 range.
1106         $filerecord['filename'] = 'testimage-converted-10x10.png';
1107         $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
1108         $this->assertInstanceOf('stored_file', $converted);
1109         // Assert that byte 25 has the ascii value 6 for PNG-24.
1110         $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
1112         // Transparency test.
1113         $filerecord['filename'] = 'testimage-converted-102x31.png';
1114         $converted = $fs->convert_image($filerecord, $original, 102, 31, true, 9);
1115         $this->assertInstanceOf('stored_file', $converted);
1116         // Assert that byte 25 has the ascii value 6 for PNG-24.
1117         $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
1119         $originalfile = imagecreatefromstring($original->get_content());
1120         $convertedfile = imagecreatefromstring($converted->get_content());
1121         $vanillafile = imagecreatefromstring($vanilla->get_content());
1123         $originalcolors = imagecolorsforindex($originalfile, imagecolorat($originalfile, 0, 0));
1124         $convertedcolors = imagecolorsforindex($convertedfile, imagecolorat($convertedfile, 0, 0));
1125         $vanillacolors = imagecolorsforindex($vanillafile, imagecolorat($vanillafile, 0, 0));
1126         $this->assertEquals(count($originalcolors), 4);
1127         $this->assertEquals(count($convertedcolors), 4);
1128         $this->assertEquals(count($vanillacolors), 4);
1129         $this->assertEquals($originalcolors['red'], $convertedcolors['red']);
1130         $this->assertEquals($originalcolors['green'], $convertedcolors['green']);
1131         $this->assertEquals($originalcolors['blue'], $convertedcolors['blue']);
1132         $this->assertEquals($originalcolors['alpha'], $convertedcolors['alpha']);
1133         $this->assertEquals($originalcolors['red'], $vanillacolors['red']);
1134         $this->assertEquals($originalcolors['green'], $vanillacolors['green']);
1135         $this->assertEquals($originalcolors['blue'], $vanillacolors['blue']);
1136         $this->assertEquals($originalcolors['alpha'], $vanillacolors['alpha']);
1137         $this->assertEquals($originalcolors['alpha'], 127);
1139     }
1141     private function generate_file_record() {
1142         $syscontext = context_system::instance();
1143         $filerecord = new stdClass();
1144         $filerecord->contextid = $syscontext->id;
1145         $filerecord->component = 'core';
1146         $filerecord->filearea = 'phpunit';
1147         $filerecord->filepath = '/';
1148         $filerecord->filename = 'testfile.txt';
1149         $filerecord->itemid = 0;
1151         return $filerecord;
1152     }
1154     /**
1155      * @expectedException        file_exception
1156      */
1157     public function test_create_file_from_storedfile_file_invalid() {
1158         $this->resetAfterTest(true);
1160         $filerecord = $this->generate_file_record();
1162         $fs = get_file_storage();
1164         // Create a file from a file id which doesn't exist.
1165         $fs->create_file_from_storedfile($filerecord,  9999);
1166     }
1168     /**
1169      * @expectedException        file_exception
1170      * @expectedExceptionMessage Invalid contextid
1171      */
1172     public function test_create_file_from_storedfile_contextid_invalid() {
1173         $this->resetAfterTest(true);
1175         $filerecord = $this->generate_file_record();
1177         $fs = get_file_storage();
1178         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1179         $this->assertInstanceOf('stored_file', $file1);
1181         $filerecord->filename = 'invalid.txt';
1182         $filerecord->contextid = 'invalid';
1184         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1185     }
1187     /**
1188      * @expectedException        file_exception
1189      * @expectedExceptionMessage Invalid component
1190      */
1191     public function test_create_file_from_storedfile_component_invalid() {
1192         $this->resetAfterTest(true);
1194         $filerecord = $this->generate_file_record();
1196         $fs = get_file_storage();
1197         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1198         $this->assertInstanceOf('stored_file', $file1);
1200         $filerecord->filename = 'invalid.txt';
1201         $filerecord->component = 'bad/component';
1203         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1204     }
1206     /**
1207      * @expectedException        file_exception
1208      * @expectedExceptionMessage Invalid filearea
1209      */
1210     public function test_create_file_from_storedfile_filearea_invalid() {
1211         $this->resetAfterTest(true);
1213         $filerecord = $this->generate_file_record();
1215         $fs = get_file_storage();
1216         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1217         $this->assertInstanceOf('stored_file', $file1);
1219         $filerecord->filename = 'invalid.txt';
1220         $filerecord->filearea = 'bad-filearea';
1222         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1223     }
1225     /**
1226      * @expectedException        file_exception
1227      * @expectedExceptionMessage Invalid itemid
1228      */
1229     public function test_create_file_from_storedfile_itemid_invalid() {
1230         $this->resetAfterTest(true);
1232         $filerecord = $this->generate_file_record();
1234         $fs = get_file_storage();
1235         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1236         $this->assertInstanceOf('stored_file', $file1);
1238         $filerecord->filename = 'invalid.txt';
1239         $filerecord->itemid = 'bad-itemid';
1241         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1242     }
1244     /**
1245      * @expectedException        file_exception
1246      * @expectedExceptionMessage Invalid file path
1247      */
1248     public function test_create_file_from_storedfile_filepath_invalid() {
1249         $this->resetAfterTest(true);
1251         $filerecord = $this->generate_file_record();
1253         $fs = get_file_storage();
1254         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1255         $this->assertInstanceOf('stored_file', $file1);
1257         $filerecord->filename = 'invalid.txt';
1258         $filerecord->filepath = 'a-/bad/-filepath';
1260         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1261     }
1263     /**
1264      * @expectedException        file_exception
1265      * @expectedExceptionMessage Invalid file name
1266      */
1267     public function test_create_file_from_storedfile_filename_invalid() {
1268         $this->resetAfterTest(true);
1270         $filerecord = $this->generate_file_record();
1272         $fs = get_file_storage();
1273         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1274         $this->assertInstanceOf('stored_file', $file1);
1276         $filerecord->filename = '';
1278         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1279     }
1281     /**
1282      * @expectedException        file_exception
1283      * @expectedExceptionMessage Invalid file timecreated
1284      */
1285     public function test_create_file_from_storedfile_timecreated_invalid() {
1286         $this->resetAfterTest(true);
1288         $filerecord = $this->generate_file_record();
1290         $fs = get_file_storage();
1291         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1292         $this->assertInstanceOf('stored_file', $file1);
1294         $filerecord->filename = 'invalid.txt';
1295         $filerecord->timecreated = 'today';
1297         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1298     }
1300     /**
1301      * @expectedException        file_exception
1302      * @expectedExceptionMessage Invalid file timemodified
1303      */
1304     public function test_create_file_from_storedfile_timemodified_invalid() {
1305         $this->resetAfterTest(true);
1307         $filerecord = $this->generate_file_record();
1309         $fs = get_file_storage();
1310         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1311         $this->assertInstanceOf('stored_file', $file1);
1313         $filerecord->filename = 'invalid.txt';
1314         $filerecord->timemodified  = 'today';
1316         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1317     }
1319     /**
1320      * @expectedException        stored_file_creation_exception
1321      * @expectedExceptionMessage Can not create file "1/core/phpunit/0/testfile.txt"
1322      */
1323     public function test_create_file_from_storedfile_duplicate() {
1324         $this->resetAfterTest(true);
1326         $filerecord = $this->generate_file_record();
1328         $fs = get_file_storage();
1329         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1330         $this->assertInstanceOf('stored_file', $file1);
1332         // Creating a file validating unique constraint.
1333         $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1334     }
1336     public function test_create_file_from_storedfile() {
1337         $this->resetAfterTest(true);
1339         $syscontext = context_system::instance();
1341         $filerecord = new stdClass();
1342         $filerecord->contextid = $syscontext->id;
1343         $filerecord->component = 'core';
1344         $filerecord->filearea = 'phpunit';
1345         $filerecord->filepath = '/';
1346         $filerecord->filename = 'testfile.txt';
1347         $filerecord->itemid = 0;
1349         $fs = get_file_storage();
1351         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1352         $this->assertInstanceOf('stored_file', $file1);
1354         $filerecord->filename = 'test-create-file-from-storedfile.txt';
1355         $file2 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1356         $this->assertInstanceOf('stored_file', $file2);
1358         // These will be normalised to current time..
1359         $filerecord->timecreated = -100;
1360         $filerecord->timemodified= -100;
1361         $filerecord->filename = 'test-create-file-from-storedfile-bad-dates.txt';
1363         $file3 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1364         $this->assertInstanceOf('stored_file', $file3);
1366         $this->assertNotEquals($file3->get_timemodified(), $filerecord->timemodified);
1367         $this->assertNotEquals($file3->get_timecreated(), $filerecord->timecreated);
1368     }
1370     /**
1371      * @expectedException        file_exception
1372      * @expectedExceptionMessage Invalid contextid
1373      */
1374     public function test_create_file_from_string_contextid_invalid() {
1375         $this->resetAfterTest(true);
1377         $filerecord = $this->generate_file_record();
1378         $fs = get_file_storage();
1380         $filerecord->contextid = 'invalid';
1382         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1383     }
1385     /**
1386      * @expectedException        file_exception
1387      * @expectedExceptionMessage Invalid component
1388      */
1389     public function test_create_file_from_string_component_invalid() {
1390         $this->resetAfterTest(true);
1392         $filerecord = $this->generate_file_record();
1393         $fs = get_file_storage();
1395         $filerecord->component = 'bad/component';
1397         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1398     }
1400     /**
1401      * @expectedException        file_exception
1402      * @expectedExceptionMessage Invalid filearea
1403      */
1404     public function test_create_file_from_string_filearea_invalid() {
1405         $this->resetAfterTest(true);
1407         $filerecord = $this->generate_file_record();
1408         $fs = get_file_storage();
1410         $filerecord->filearea = 'bad-filearea';
1412         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1413     }
1415     /**
1416      * @expectedException        file_exception
1417      * @expectedExceptionMessage Invalid itemid
1418      */
1419     public function test_create_file_from_string_itemid_invalid() {
1420         $this->resetAfterTest(true);
1422         $filerecord = $this->generate_file_record();
1423         $fs = get_file_storage();
1425         $filerecord->itemid = 'bad-itemid';
1427         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1428     }
1430     /**
1431      * @expectedException        file_exception
1432      * @expectedExceptionMessage Invalid file path
1433      */
1434     public function test_create_file_from_string_filepath_invalid() {
1435         $this->resetAfterTest(true);
1437         $filerecord = $this->generate_file_record();
1438         $fs = get_file_storage();
1440         $filerecord->filepath = 'a-/bad/-filepath';
1442         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1443     }
1445     /**
1446      * @expectedException        file_exception
1447      * @expectedExceptionMessage Invalid file name
1448      */
1449     public function test_create_file_from_string_filename_invalid() {
1450         $this->resetAfterTest(true);
1452         $filerecord = $this->generate_file_record();
1453         $fs = get_file_storage();
1455         $filerecord->filename = '';
1457         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1458     }
1460     /**
1461      * @expectedException        file_exception
1462      * @expectedExceptionMessage Invalid file timecreated
1463      */
1464     public function test_create_file_from_string_timecreated_invalid() {
1465         $this->resetAfterTest(true);
1467         $filerecord = $this->generate_file_record();
1468         $fs = get_file_storage();
1470         $filerecord->timecreated = 'today';
1472         $this->expectException('file_exception');
1473         $this->expectExceptionMessage('Invalid file timecreated');
1474         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1475     }
1477     /**
1478      * @expectedException        file_exception
1479      * @expectedExceptionMessage Invalid file timemodified
1480      */
1481     public function test_create_file_from_string_timemodified_invalid() {
1482         $this->resetAfterTest(true);
1484         $filerecord = $this->generate_file_record();
1485         $fs = get_file_storage();
1487         $filerecord->timemodified  = 'today';
1489         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1490     }
1492     public function test_create_file_from_string_duplicate() {
1493         $this->resetAfterTest(true);
1495         $filerecord = $this->generate_file_record();
1496         $fs = get_file_storage();
1498         $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1500         // Creating a file validating unique constraint.
1501         $this->expectException('stored_file_creation_exception');
1502         $file2 = $fs->create_file_from_string($filerecord, 'text contents');
1503     }
1505     /**
1506      * @expectedException        file_exception
1507      * @expectedExceptionMessage Invalid contextid
1508      */
1509     public function test_create_file_from_pathname_contextid_invalid() {
1510         global $CFG;
1511         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1513         $this->resetAfterTest(true);
1515         $filerecord = $this->generate_file_record();
1516         $fs = get_file_storage();
1518         $filerecord->contextid = 'invalid';
1520         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1521     }
1523     /**
1524      * @expectedException        file_exception
1525      * @expectedExceptionMessage Invalid component
1526      */
1527     public function test_create_file_from_pathname_component_invalid() {
1528         global $CFG;
1529         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1531         $this->resetAfterTest(true);
1533         $filerecord = $this->generate_file_record();
1534         $fs = get_file_storage();
1536         $filerecord->component = 'bad/component';
1538         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1539     }
1541     /**
1542      * @expectedException        file_exception
1543      * @expectedExceptionMessage Invalid filearea
1544      */
1545     public function test_create_file_from_pathname_filearea_invalid() {
1546         global $CFG;
1547         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1549         $this->resetAfterTest(true);
1551         $filerecord = $this->generate_file_record();
1552         $fs = get_file_storage();
1554         $filerecord->filearea = 'bad-filearea';
1556         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1557     }
1559     /**
1560      * @expectedException        file_exception
1561      * @expectedExceptionMessage Invalid itemid
1562      */
1563     public function test_create_file_from_pathname_itemid_invalid() {
1564         global $CFG;
1565         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1567         $this->resetAfterTest(true);
1569         $filerecord = $this->generate_file_record();
1570         $fs = get_file_storage();
1572         $filerecord->itemid = 'bad-itemid';
1574          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1575     }
1577     /**
1578      * @expectedException        file_exception
1579      * @expectedExceptionMessage Invalid file path
1580      */
1581     public function test_create_file_from_pathname_filepath_invalid() {
1582         global $CFG;
1583         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1585         $this->resetAfterTest(true);
1587         $filerecord = $this->generate_file_record();
1588         $fs = get_file_storage();
1590         $filerecord->filepath = 'a-/bad/-filepath';
1592         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1593     }
1595     /**
1596      * @expectedException        file_exception
1597      * @expectedExceptionMessage Invalid file name
1598      */
1599     public function test_create_file_from_pathname_filename_invalid() {
1600         global $CFG;
1601         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1603         $this->resetAfterTest(true);
1605         $filerecord = $this->generate_file_record();
1606         $fs = get_file_storage();
1608         $filerecord->filename = '';
1610         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1611     }
1613     /**
1614      * @expectedException        file_exception
1615      * @expectedExceptionMessage Invalid file timecreated
1616      */
1617     public function test_create_file_from_pathname_timecreated_invalid() {
1618         global $CFG;
1619         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1621         $this->resetAfterTest(true);
1623         $filerecord = $this->generate_file_record();
1624         $fs = get_file_storage();
1626         $filerecord->timecreated = 'today';
1628         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1629     }
1631     /**
1632      * @expectedException        file_exception
1633      * @expectedExceptionMessage Invalid file timemodified
1634      */
1635     public function test_create_file_from_pathname_timemodified_invalid() {
1636         global $CFG;
1637         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1639         $this->resetAfterTest(true);
1641         $filerecord = $this->generate_file_record();
1642         $fs = get_file_storage();
1644         $filerecord->timemodified  = 'today';
1646         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1647     }
1649     /**
1650      * @expectedException        stored_file_creation_exception
1651      * @expectedExceptionMessage Can not create file "1/core/phpunit/0/testfile.txt"
1652      */
1653     public function test_create_file_from_pathname_duplicate_file() {
1654         global $CFG;
1655         $this->resetAfterTest(true);
1657         $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1659         $filerecord = $this->generate_file_record();
1660         $fs = get_file_storage();
1662         $file1 = $fs->create_file_from_pathname($filerecord, $path);
1663         $this->assertInstanceOf('stored_file', $file1);
1665         // Creating a file validating unique constraint.
1666         $file2 = $fs->create_file_from_pathname($filerecord, $path);
1667     }
1669     /**
1670      * Calling stored_file::delete_reference() on a non-reference file throws coding_exception
1671      */
1672     public function test_delete_reference_on_nonreference() {
1674         $this->resetAfterTest(true);
1675         $user = $this->setup_three_private_files();
1676         $fs = get_file_storage();
1677         $repos = repository::get_instances(array('type'=>'user'));
1678         $repo = reset($repos);
1680         $file = null;
1681         foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1682             if (!$areafile->is_directory()) {
1683                 $file = $areafile;
1684                 break;
1685             }
1686         }
1687         $this->assertInstanceOf('stored_file', $file);
1688         $this->assertFalse($file->is_external_file());
1690         $this->expectException('coding_exception');
1691         $file->delete_reference();
1692     }
1694     /**
1695      * Calling stored_file::delete_reference() on a reference file does not affect other
1696      * symlinks to the same original
1697      */
1698     public function test_delete_reference_one_symlink_does_not_rule_them_all() {
1700         $this->resetAfterTest(true);
1701         $user = $this->setup_three_private_files();
1702         $fs = get_file_storage();
1703         $repos = repository::get_instances(array('type'=>'user'));
1704         $repo = reset($repos);
1706         // Create two aliases linking the same original.
1708         $originalfile = null;
1709         foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1710             if (!$areafile->is_directory()) {
1711                 $originalfile = $areafile;
1712                 break;
1713             }
1714         }
1715         $this->assertInstanceOf('stored_file', $originalfile);
1717         // Calling delete_reference() on a non-reference file.
1719         $originalrecord = array(
1720             'contextid' => $originalfile->get_contextid(),
1721             'component' => $originalfile->get_component(),
1722             'filearea'  => $originalfile->get_filearea(),
1723             'itemid'    => $originalfile->get_itemid(),
1724             'filepath'  => $originalfile->get_filepath(),
1725             'filename'  => $originalfile->get_filename(),
1726         );
1728         $aliasrecord = $this->generate_file_record();
1729         $aliasrecord->filepath = '/A/';
1730         $aliasrecord->filename = 'symlink.txt';
1732         $ref = $fs->pack_reference($originalrecord);
1733         $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1735         $aliasrecord->filepath = '/B/';
1736         $aliasrecord->filename = 'symlink.txt';
1737         $ref = $fs->pack_reference($originalrecord);
1738         $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1740         // Refetch A/symlink.txt file.
1741         $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1742             $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
1743         $this->assertTrue($symlink1->is_external_file());
1745         // Unlink the A/symlink.txt file.
1746         $symlink1->delete_reference();
1747         $this->assertFalse($symlink1->is_external_file());
1749         // Make sure that B/symlink.txt has not been affected.
1750         $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1751             $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
1752         $this->assertTrue($symlink2->is_external_file());
1753     }
1755     /**
1756      * Make sure that when internal file is updated all references to it are
1757      * updated immediately. When it is deleted, the references are converted
1758      * to true copies.
1759      */
1760     public function test_update_reference_internal() {
1761         purge_all_caches();
1762         $this->resetAfterTest(true);
1763         $user = $this->setup_three_private_files();
1764         $fs = get_file_storage();
1765         $repos = repository::get_instances(array('type' => 'user'));
1766         $repo = reset($repos);
1768         // Create two aliases linking the same original.
1770         $areafiles = array_values($fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false));
1772         $originalfile = $areafiles[0];
1773         $this->assertInstanceOf('stored_file', $originalfile);
1774         $contenthash = $originalfile->get_contenthash();
1775         $filesize = $originalfile->get_filesize();
1777         $substitutefile = $areafiles[1];
1778         $this->assertInstanceOf('stored_file', $substitutefile);
1779         $newcontenthash = $substitutefile->get_contenthash();
1780         $newfilesize = $substitutefile->get_filesize();
1782         $originalrecord = array(
1783             'contextid' => $originalfile->get_contextid(),
1784             'component' => $originalfile->get_component(),
1785             'filearea'  => $originalfile->get_filearea(),
1786             'itemid'    => $originalfile->get_itemid(),
1787             'filepath'  => $originalfile->get_filepath(),
1788             'filename'  => $originalfile->get_filename(),
1789         );
1791         $aliasrecord = $this->generate_file_record();
1792         $aliasrecord->filepath = '/A/';
1793         $aliasrecord->filename = 'symlink.txt';
1795         $ref = $fs->pack_reference($originalrecord);
1796         $symlink1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1797         // Make sure created alias is a reference and has the same size and contenthash as source.
1798         $this->assertEquals($contenthash, $symlink1->get_contenthash());
1799         $this->assertEquals($filesize, $symlink1->get_filesize());
1800         $this->assertEquals($repo->id, $symlink1->get_repository_id());
1801         $this->assertNotEmpty($symlink1->get_referencefileid());
1802         $referenceid = $symlink1->get_referencefileid();
1804         $aliasrecord->filepath = '/B/';
1805         $aliasrecord->filename = 'symlink.txt';
1806         $ref = $fs->pack_reference($originalrecord);
1807         $symlink2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1808         // Make sure created alias is a reference and has the same size and contenthash as source.
1809         $this->assertEquals($contenthash, $symlink2->get_contenthash());
1810         $this->assertEquals($filesize, $symlink2->get_filesize());
1811         $this->assertEquals($repo->id, $symlink2->get_repository_id());
1812         // Make sure both aliases have the same reference id.
1813         $this->assertEquals($referenceid, $symlink2->get_referencefileid());
1815         // Overwrite ofiginal file.
1816         $originalfile->replace_file_with($substitutefile);
1817         $this->assertEquals($newcontenthash, $originalfile->get_contenthash());
1818         $this->assertEquals($newfilesize, $originalfile->get_filesize());
1820         // References to the internal files must be synchronised immediately.
1821         // Refetch A/symlink.txt file.
1822         $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1823             $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
1824         $this->assertTrue($symlink1->is_external_file());
1825         $this->assertEquals($newcontenthash, $symlink1->get_contenthash());
1826         $this->assertEquals($newfilesize, $symlink1->get_filesize());
1827         $this->assertEquals($repo->id, $symlink1->get_repository_id());
1828         $this->assertEquals($referenceid, $symlink1->get_referencefileid());
1830         // Refetch B/symlink.txt file.
1831         $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1832             $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
1833         $this->assertTrue($symlink2->is_external_file());
1834         $this->assertEquals($newcontenthash, $symlink2->get_contenthash());
1835         $this->assertEquals($newfilesize, $symlink2->get_filesize());
1836         $this->assertEquals($repo->id, $symlink2->get_repository_id());
1837         $this->assertEquals($referenceid, $symlink2->get_referencefileid());
1839         // Remove original file.
1840         $originalfile->delete();
1842         // References must be converted to independend files.
1843         // Refetch A/symlink.txt file.
1844         $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1845             $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
1846         $this->assertFalse($symlink1->is_external_file());
1847         $this->assertEquals($newcontenthash, $symlink1->get_contenthash());
1848         $this->assertEquals($newfilesize, $symlink1->get_filesize());
1849         $this->assertNull($symlink1->get_repository_id());
1850         $this->assertNull($symlink1->get_referencefileid());
1852         // Refetch B/symlink.txt file.
1853         $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1854             $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
1855         $this->assertFalse($symlink2->is_external_file());
1856         $this->assertEquals($newcontenthash, $symlink2->get_contenthash());
1857         $this->assertEquals($newfilesize, $symlink2->get_filesize());
1858         $this->assertNull($symlink2->get_repository_id());
1859         $this->assertNull($symlink2->get_referencefileid());
1860     }
1862     public function test_get_unused_filename() {
1863         global $USER;
1864         $this->resetAfterTest(true);
1866         $fs = get_file_storage();
1867         $this->setAdminUser();
1868         $contextid = context_user::instance($USER->id)->id;
1869         $component = 'user';
1870         $filearea = 'private';
1871         $itemid = 0;
1872         $filepath = '/';
1874         // Create some private files.
1875         $file = new stdClass;
1876         $file->contextid = $contextid;
1877         $file->component = 'user';
1878         $file->filearea  = 'private';
1879         $file->itemid    = 0;
1880         $file->filepath  = '/';
1881         $file->source    = 'test';
1882         $filenames = array('foo.txt', 'foo (1).txt', 'foo (20).txt', 'foo (999)', 'bar.jpg', 'What (a cool file).jpg',
1883                 'Hurray! (1).php', 'Hurray! (2).php', 'Hurray! (9a).php', 'Hurray! (abc).php');
1884         foreach ($filenames as $key => $filename) {
1885             $file->filename = $filename;
1886             $userfile = $fs->create_file_from_string($file, "file $key $filename content");
1887             $this->assertInstanceOf('stored_file', $userfile);
1888         }
1890         // Asserting new generated names.
1891         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'unused.txt');
1892         $this->assertEquals('unused.txt', $newfilename);
1893         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo.txt');
1894         $this->assertEquals('foo (21).txt', $newfilename);
1895         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (1).txt');
1896         $this->assertEquals('foo (21).txt', $newfilename);
1897         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (2).txt');
1898         $this->assertEquals('foo (2).txt', $newfilename);
1899         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (20).txt');
1900         $this->assertEquals('foo (21).txt', $newfilename);
1901         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo');
1902         $this->assertEquals('foo', $newfilename);
1903         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (123)');
1904         $this->assertEquals('foo (123)', $newfilename);
1905         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (999)');
1906         $this->assertEquals('foo (1000)', $newfilename);
1907         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.png');
1908         $this->assertEquals('bar.png', $newfilename);
1909         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (12).png');
1910         $this->assertEquals('bar (12).png', $newfilename);
1911         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.jpg');
1912         $this->assertEquals('bar (1).jpg', $newfilename);
1913         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (1).jpg');
1914         $this->assertEquals('bar (1).jpg', $newfilename);
1915         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'What (a cool file).jpg');
1916         $this->assertEquals('What (a cool file) (1).jpg', $newfilename);
1917         $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'Hurray! (1).php');
1918         $this->assertEquals('Hurray! (3).php', $newfilename);
1920         $this->expectException('coding_exception');
1921         $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, '');
1922     }
1924     /**
1925      * Test that mimetype_from_file returns appropriate output when the
1926      * file could not be found.
1927      */
1928     public function test_mimetype_not_found() {
1929         $mimetype = file_storage::mimetype('/path/to/nonexistent/file');
1930         $this->assertEquals('document/unknown', $mimetype);
1931     }
1933     /**
1934      * Test that mimetype_from_file returns appropriate output for a known
1935      * file.
1936      *
1937      * Note: this is not intended to check that functions outside of this
1938      * file works. It is intended to validate the codepath contains no
1939      * errors and behaves as expected.
1940      */
1941     public function test_mimetype_known() {
1942         $filepath = __DIR__ . '/fixtures/testimage.jpg';
1943         $mimetype = file_storage::mimetype_from_file($filepath);
1944         $this->assertEquals('image/jpeg', $mimetype);
1945     }
1947     /**
1948      * Test that mimetype_from_file returns appropriate output when the
1949      * file could not be found.
1950      */
1951     public function test_mimetype_from_file_not_found() {
1952         $mimetype = file_storage::mimetype_from_file('/path/to/nonexistent/file');
1953         $this->assertEquals('document/unknown', $mimetype);
1954     }
1956     /**
1957      * Test that mimetype_from_file returns appropriate output for a known
1958      * file.
1959      *
1960      * Note: this is not intended to check that functions outside of this
1961      * file works. It is intended to validate the codepath contains no
1962      * errors and behaves as expected.
1963      */
1964     public function test_mimetype_from_file_known() {
1965         $filepath = __DIR__ . '/fixtures/testimage.jpg';
1966         $mimetype = file_storage::mimetype_from_file($filepath);
1967         $this->assertEquals('image/jpeg', $mimetype);
1968     }
1972 class test_stored_file_inspection extends stored_file {
1973     public static function get_pretected_pathname(stored_file $file) {
1974         return $file->get_pathname_by_contenthash();
1975     }