MDL-71116 core_badges: Backpack URLs with more than 50 chars
[moodle.git] / lib / filestorage / tests / file_system_test.php
blobc85f572c24fb4b7daeec9735d266d003cda8b67a
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.
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 file_system.
20 * @package core_files
21 * @category phpunit
22 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/filestorage/file_system.php');
31 /**
32 * Unit tests for file_system.
34 * @package core_files
35 * @category phpunit
36 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 * @coversDefaultClass file_system
40 class core_files_file_system_testcase extends advanced_testcase {
42 public function setUp(): void {
43 get_file_storage(true);
46 public function tearDown(): void {
47 get_file_storage(true);
50 /**
51 * Helper function to help setup and configure the virtual file system stream.
53 * @param array $filedir Directory structure and content of the filedir
54 * @param array $trashdir Directory structure and content of the sourcedir
55 * @param array $sourcedir Directory structure and content of a directory used for source files for tests
56 * @return \org\bovigo\vfs\vfsStream
58 protected function setup_vfile_root($content = []) {
59 $vfileroot = \org\bovigo\vfs\vfsStream::setup('root', null, $content);
61 return $vfileroot;
64 /**
65 * Helper to create a stored file objectw with the given supplied content.
67 * @param string $filecontent The content of the mocked file
68 * @param string $filename The file name to use in the stored_file
69 * @param array $mockedmethods A list of methods you intend to override
70 * If no methods are specified, only abstract functions are mocked.
71 * @return stored_file
73 protected function get_stored_file($filecontent, $filename = null, $mockedmethods = []) {
74 $contenthash = file_storage::hash_from_string($filecontent);
75 if (empty($filename)) {
76 $filename = $contenthash;
79 $file = $this->getMockBuilder(stored_file::class)
80 ->onlyMethods($mockedmethods)
81 ->setConstructorArgs([
82 get_file_storage(),
83 (object) [
84 'contenthash' => $contenthash,
85 'filesize' => strlen($filecontent),
86 'filename' => $filename,
89 ->getMock();
91 return $file;
94 /**
95 * Get a testable mock of the abstract file_system class.
97 * @param array $mockedmethods A list of methods you intend to override
98 * If no methods are specified, only abstract functions are mocked.
99 * @return file_system
101 protected function get_testable_mock($mockedmethods = []) {
102 $fs = $this->getMockBuilder(file_system::class)
103 ->onlyMethods($mockedmethods)
104 ->getMockForAbstractClass();
106 return $fs;
110 * Ensure that the file system is not clonable.
113 public function test_not_cloneable() {
114 $reflection = new ReflectionClass('file_system');
115 $this->assertFalse($reflection->isCloneable());
119 * Ensure that the filedir file_system extension is used by default.
122 public function test_default_class() {
123 $this->resetAfterTest();
125 // Ensure that the alternative_file_system_class is null.
126 global $CFG;
127 $CFG->alternative_file_system_class = null;
129 $storage = get_file_storage();
130 $fs = $storage->get_file_system();
131 $this->assertInstanceOf(file_system::class, $fs);
132 $this->assertEquals(file_system_filedir::class, get_class($fs));
136 * Ensure that the specified file_system extension class is used.
139 public function test_supplied_class() {
140 global $CFG;
141 $this->resetAfterTest();
143 // Mock the file_system.
144 // Mocks create a new child of the mocked class which is perfect for this test.
145 $filesystem = $this->getMockBuilder('file_system')
146 ->disableOriginalConstructor()
147 ->getMock();
148 $CFG->alternative_file_system_class = get_class($filesystem);
150 $storage = get_file_storage();
151 $fs = $storage->get_file_system();
152 $this->assertInstanceOf(file_system::class, $fs);
153 $this->assertEquals(get_class($filesystem), get_class($fs));
157 * Test that the readfile function outputs content to disk.
159 * @covers ::readfile
161 public function test_readfile_remote() {
162 global $CFG;
164 // Mock the filesystem.
165 $filecontent = 'example content';
166 $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]);
167 $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile');
169 $file = $this->get_stored_file($filecontent);
171 // Mock the file_system class.
172 // We need to override the get_remote_path_from_storedfile function.
173 $fs = $this->get_testable_mock([
174 'get_remote_path_from_storedfile',
175 'is_file_readable_locally_by_storedfile',
176 'get_local_path_from_storedfile',
178 $fs->method('get_remote_path_from_storedfile')->willReturn($filepath);
179 $fs->method('is_file_readable_locally_by_storedfile')->willReturn(false);
180 $fs->expects($this->never())->method('get_local_path_from_storedfile');
182 // Note: It is currently not possible to mock readfile_allow_large
183 // because file_system is in the global namespace.
184 // We must therefore check for expected output. This is not ideal.
185 $this->expectOutputString($filecontent);
186 $fs->readfile($file);
190 * Test that the readfile function outputs content to disk.
192 * @covers ::readfile
194 public function test_readfile_local() {
195 global $CFG;
197 // Mock the filesystem.
198 $filecontent = 'example content';
199 $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]);
200 $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile');
202 $file = $this->get_stored_file($filecontent);
204 // Mock the file_system class.
205 // We need to override the get_remote_path_from_storedfile function.
206 $fs = $this->get_testable_mock([
207 'get_remote_path_from_storedfile',
208 'is_file_readable_locally_by_storedfile',
209 'get_local_path_from_storedfile',
211 $fs->method('is_file_readable_locally_by_storedfile')->willReturn(true);
212 $fs->expects($this->never())->method('get_remote_path_from_storedfile');
213 $fs->expects($this->once())->method('get_local_path_from_storedfile')->willReturn($filepath);
215 // Note: It is currently not possible to mock readfile_allow_large
216 // because file_system is in the global namespace.
217 // We must therefore check for expected output. This is not ideal.
218 $this->expectOutputString($filecontent);
219 $fs->readfile($file);
223 * Test that the get_local_path_from_storedfile function functions
224 * correctly when called with various args.
226 * @dataProvider get_local_path_from_storedfile_provider
227 * @param array $args The additional args to pass to get_local_path_from_storedfile
228 * @param bool $fetch Whether the combination of args should have caused a fetch
230 * @covers ::get_local_path_from_storedfile
232 public function test_get_local_path_from_storedfile($args, $fetch) {
233 $filepath = '/path/to/file';
234 $filecontent = 'example content';
236 // Get the filesystem mock.
237 $fs = $this->get_testable_mock([
238 'get_local_path_from_hash',
240 $fs->expects($this->once())
241 ->method('get_local_path_from_hash')
242 ->with($this->equalTo(file_storage::hash_from_string($filecontent)), $this->equalTo($fetch))
243 ->willReturn($filepath);
245 $file = $this->get_stored_file($filecontent);
247 $result = $fs->get_local_path_from_storedfile($file, $fetch);
249 $this->assertEquals($filepath, $result);
253 * Ensure that the default implementation of get_remote_path_from_storedfile
254 * simply calls get_local_path_from_storedfile without requiring a
255 * fetch.
257 * @covers ::get_remote_path_from_storedfile
259 public function test_get_remote_path_from_storedfile() {
260 $filepath = '/path/to/file';
261 $filecontent = 'example content';
263 $fs = $this->get_testable_mock([
264 'get_remote_path_from_hash',
267 $fs->expects($this->once())
268 ->method('get_remote_path_from_hash')
269 ->with($this->equalTo(file_storage::hash_from_string($filecontent)), $this->equalTo(false))
270 ->willReturn($filepath);
272 $file = $this->get_stored_file($filecontent);
274 $result = $fs->get_remote_path_from_storedfile($file);
276 $this->assertEquals($filepath, $result);
280 * Test the stock implementation of is_file_readable_locally_by_hash with a valid file.
282 * This should call get_local_path_from_hash and check the readability
283 * of the file.
285 * Fetching the file is optional.
287 * @covers ::is_file_readable_locally_by_hash
289 public function test_is_file_readable_locally_by_hash() {
290 $filecontent = 'example content';
291 $contenthash = file_storage::hash_from_string($filecontent);
292 $filepath = __FILE__;
294 $fs = $this->get_testable_mock([
295 'get_local_path_from_hash',
298 $fs->method('get_local_path_from_hash')
299 ->with($this->equalTo($contenthash), $this->equalTo(false))
300 ->willReturn($filepath);
302 $this->assertTrue($fs->is_file_readable_locally_by_hash($contenthash));
306 * Test the stock implementation of is_file_readable_locally_by_hash with an empty file.
308 * @covers ::is_file_readable_locally_by_hash
310 public function test_is_file_readable_locally_by_hash_empty() {
311 $filecontent = '';
312 $contenthash = file_storage::hash_from_string($filecontent);
314 $fs = $this->get_testable_mock([
315 'get_local_path_from_hash',
318 $fs->expects($this->never())
319 ->method('get_local_path_from_hash');
321 $this->assertTrue($fs->is_file_readable_locally_by_hash($contenthash));
325 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file.
327 * @covers ::is_file_readable_remotely_by_hash
329 public function test_is_file_readable_remotely_by_hash() {
330 $filecontent = 'example content';
331 $contenthash = file_storage::hash_from_string($filecontent);
333 $fs = $this->get_testable_mock([
334 'get_remote_path_from_hash',
337 $fs->method('get_remote_path_from_hash')
338 ->with($this->equalTo($contenthash), $this->equalTo(false))
339 ->willReturn(__FILE__);
341 $this->assertTrue($fs->is_file_readable_remotely_by_hash($contenthash));
345 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file.
347 * @covers ::is_file_readable_remotely_by_hash
349 public function test_is_file_readable_remotely_by_hash_empty() {
350 $filecontent = '';
351 $contenthash = file_storage::hash_from_string($filecontent);
353 $fs = $this->get_testable_mock([
354 'get_remote_path_from_hash',
357 $fs->expects($this->never())
358 ->method('get_remote_path_from_hash');
360 $this->assertTrue($fs->is_file_readable_remotely_by_hash($contenthash));
364 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file.
366 * @covers ::is_file_readable_remotely_by_hash
368 public function test_is_file_readable_remotely_by_hash_not_found() {
369 $filecontent = 'example content';
370 $contenthash = file_storage::hash_from_string($filecontent);
372 $fs = $this->get_testable_mock([
373 'get_remote_path_from_hash',
376 $fs->method('get_remote_path_from_hash')
377 ->with($this->equalTo($contenthash), $this->equalTo(false))
378 ->willReturn('/path/to/nonexistent/file');
380 $this->assertFalse($fs->is_file_readable_remotely_by_hash($contenthash));
384 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file.
386 * @covers ::is_file_readable_remotely_by_storedfile
388 public function test_is_file_readable_remotely_by_storedfile() {
389 $file = $this->get_stored_file('example content');
391 $fs = $this->get_testable_mock([
392 'get_remote_path_from_storedfile',
395 $fs->method('get_remote_path_from_storedfile')
396 ->willReturn(__FILE__);
398 $this->assertTrue($fs->is_file_readable_remotely_by_storedfile($file));
402 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file.
404 * @covers ::is_file_readable_remotely_by_storedfile
406 public function test_is_file_readable_remotely_by_storedfile_empty() {
407 $fs = $this->get_testable_mock([
408 'get_remote_path_from_storedfile',
411 $fs->expects($this->never())
412 ->method('get_remote_path_from_storedfile');
414 $file = $this->get_stored_file('');
415 $this->assertTrue($fs->is_file_readable_remotely_by_storedfile($file));
419 * Test the stock implementation of is_file_readable_locally_by_storedfile with an empty file.
421 * @covers ::is_file_readable_locally_by_storedfile
423 public function test_is_file_readable_locally_by_storedfile_empty() {
424 $fs = $this->get_testable_mock([
425 'get_local_path_from_storedfile',
428 $fs->expects($this->never())
429 ->method('get_local_path_from_storedfile');
431 $file = $this->get_stored_file('');
432 $this->assertTrue($fs->is_file_readable_locally_by_storedfile($file));
436 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file.
438 * @covers ::is_file_readable_locally_by_storedfile
440 public function test_is_file_readable_remotely_by_storedfile_not_found() {
441 $file = $this->get_stored_file('example content');
443 $fs = $this->get_testable_mock([
444 'get_remote_path_from_storedfile',
447 $fs->method('get_remote_path_from_storedfile')
448 ->willReturn(__LINE__);
450 $this->assertFalse($fs->is_file_readable_remotely_by_storedfile($file));
454 * Test the stock implementation of is_file_readable_locally_by_storedfile with a valid file.
456 * @covers ::is_file_readable_locally_by_storedfile
458 public function test_is_file_readable_locally_by_storedfile_unreadable() {
459 $fs = $this->get_testable_mock([
460 'get_local_path_from_storedfile',
462 $file = $this->get_stored_file('example content');
464 $fs->method('get_local_path_from_storedfile')
465 ->with($this->equalTo($file), $this->equalTo(false))
466 ->willReturn('/path/to/nonexistent/file');
468 $this->assertFalse($fs->is_file_readable_locally_by_storedfile($file));
472 * Test the stock implementation of is_file_readable_locally_by_storedfile with a valid file should pass fetch.
474 * @covers ::is_file_readable_locally_by_storedfile
476 public function test_is_file_readable_locally_by_storedfile_passes_fetch() {
477 $fs = $this->get_testable_mock([
478 'get_local_path_from_storedfile',
480 $file = $this->get_stored_file('example content');
482 $fs->method('get_local_path_from_storedfile')
483 ->with($this->equalTo($file), $this->equalTo(true))
484 ->willReturn('/path/to/nonexistent/file');
486 $this->assertFalse($fs->is_file_readable_locally_by_storedfile($file, true));
490 * Ensure that is_file_removable returns correctly for an empty file.
492 * @covers ::is_file_removable
494 public function test_is_file_removable_empty() {
495 $filecontent = '';
496 $contenthash = file_storage::hash_from_string($filecontent);
498 $method = new ReflectionMethod(file_system::class, 'is_file_removable');
499 $method->setAccessible(true);
500 $result = $method->invokeArgs(null, [$contenthash]);
501 $this->assertFalse($result);
505 * Ensure that is_file_removable returns false if the file is still in use.
507 * @covers ::is_file_removable
509 public function test_is_file_removable_in_use() {
510 $this->resetAfterTest();
511 global $DB;
513 $filecontent = 'example content';
514 $contenthash = file_storage::hash_from_string($filecontent);
516 $DB = $this->getMockBuilder(\moodle_database::class)
517 ->onlyMethods(['record_exists'])
518 ->getMockForAbstractClass();
519 $DB->method('record_exists')->willReturn(true);
521 $method = new ReflectionMethod(file_system::class, 'is_file_removable');
522 $method->setAccessible(true);
523 $result = $method->invokeArgs(null, [$contenthash]);
525 $this->assertFalse($result);
529 * Ensure that is_file_removable returns false if the file is not in use.
531 * @covers ::is_file_removable
533 public function test_is_file_removable_not_in_use() {
534 $this->resetAfterTest();
535 global $DB;
537 $filecontent = 'example content';
538 $contenthash = file_storage::hash_from_string($filecontent);
540 $DB = $this->getMockBuilder(\moodle_database::class)
541 ->onlyMethods(['record_exists'])
542 ->getMockForAbstractClass();
543 $DB->method('record_exists')->willReturn(false);
545 $method = new ReflectionMethod(file_system::class, 'is_file_removable');
546 $method->setAccessible(true);
547 $result = $method->invokeArgs(null, [$contenthash]);
549 $this->assertTrue($result);
553 * Test the stock implementation of get_content.
555 * @covers ::get_content
557 public function test_get_content() {
558 global $CFG;
560 // Mock the filesystem.
561 $filecontent = 'example content';
562 $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]);
563 $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile');
565 $file = $this->get_stored_file($filecontent);
567 // Mock the file_system class.
568 // We need to override the get_remote_path_from_storedfile function.
569 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']);
570 $fs->method('get_remote_path_from_storedfile')->willReturn($filepath);
572 $result = $fs->get_content($file);
574 $this->assertEquals($filecontent, $result);
578 * Test the stock implementation of get_content.
580 * @covers ::get_content
582 public function test_get_content_empty() {
583 global $CFG;
585 $filecontent = '';
586 $file = $this->get_stored_file($filecontent);
588 // Mock the file_system class.
589 // We need to override the get_remote_path_from_storedfile function.
590 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']);
591 $fs->expects($this->never())
592 ->method('get_remote_path_from_storedfile');
594 $result = $fs->get_content($file);
596 $this->assertEquals($filecontent, $result);
600 * Ensure that the list_files function requires a local copy of the
601 * file, and passes the path to the packer.
603 * @covers ::list_files
605 public function test_list_files() {
606 $filecontent = 'example content';
607 $file = $this->get_stored_file($filecontent);
608 $filepath = __FILE__;
609 $expectedresult = (object) [];
611 // Mock the file_system class.
612 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
613 $fs->method('get_local_path_from_storedfile')
614 ->with($this->equalTo($file), $this->equalTo(true))
615 ->willReturn(__FILE__);
617 $packer = $this->getMockBuilder(file_packer::class)
618 ->onlyMethods(['list_files'])
619 ->getMockForAbstractClass();
621 $packer->expects($this->once())
622 ->method('list_files')
623 ->with($this->equalTo($filepath))
624 ->willReturn($expectedresult);
626 $result = $fs->list_files($file, $packer);
628 $this->assertEquals($expectedresult, $result);
632 * Ensure that the extract_to_pathname function requires a local copy of the
633 * file, and passes the path to the packer.
635 * @covers ::extract_to_pathname
637 public function test_extract_to_pathname() {
638 $filecontent = 'example content';
639 $file = $this->get_stored_file($filecontent);
640 $filepath = __FILE__;
641 $expectedresult = (object) [];
642 $outputpath = '/path/to/output';
644 // Mock the file_system class.
645 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
646 $fs->method('get_local_path_from_storedfile')
647 ->with($this->equalTo($file), $this->equalTo(true))
648 ->willReturn(__FILE__);
650 $packer = $this->getMockBuilder(file_packer::class)
651 ->onlyMethods(['extract_to_pathname'])
652 ->getMockForAbstractClass();
654 $packer->expects($this->once())
655 ->method('extract_to_pathname')
656 ->with($this->equalTo($filepath), $this->equalTo($outputpath), $this->equalTo(null), $this->equalTo(null))
657 ->willReturn($expectedresult);
659 $result = $fs->extract_to_pathname($file, $packer, $outputpath);
661 $this->assertEquals($expectedresult, $result);
665 * Ensure that the extract_to_storage function requires a local copy of the
666 * file, and passes the path to the packer.
668 * @covers ::extract_to_storage
670 public function test_extract_to_storage() {
671 $filecontent = 'example content';
672 $file = $this->get_stored_file($filecontent);
673 $filepath = __FILE__;
674 $expectedresult = (object) [];
675 $outputpath = '/path/to/output';
677 // Mock the file_system class.
678 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
679 $fs->method('get_local_path_from_storedfile')
680 ->with($this->equalTo($file), $this->equalTo(true))
681 ->willReturn(__FILE__);
683 $packer = $this->getMockBuilder(file_packer::class)
684 ->onlyMethods(['extract_to_storage'])
685 ->getMockForAbstractClass();
687 $packer->expects($this->once())
688 ->method('extract_to_storage')
689 ->with(
690 $this->equalTo($filepath),
691 $this->equalTo(42),
692 $this->equalTo('component'),
693 $this->equalTo('filearea'),
694 $this->equalTo('itemid'),
695 $this->equalTo('pathbase'),
696 $this->equalTo('userid'),
697 $this->equalTo(null)
699 ->willReturn($expectedresult);
701 $result = $fs->extract_to_storage($file, $packer, 42, 'component','filearea', 'itemid', 'pathbase', 'userid');
703 $this->assertEquals($expectedresult, $result);
707 * Ensure that the add_storedfile_to_archive function requires a local copy of the
708 * file, and passes the path to the archive.
711 public function test_add_storedfile_to_archive_directory() {
712 $file = $this->get_stored_file('', '.');
713 $archivepath = 'example';
714 $expectedresult = (object) [];
716 // Mock the file_system class.
717 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
718 $fs->method('get_local_path_from_storedfile')
719 ->with($this->equalTo($file), $this->equalTo(true))
720 ->willReturn(__FILE__);
722 $archive = $this->getMockBuilder(file_archive::class)
723 ->onlyMethods([
724 'add_directory',
725 'add_file_from_pathname',
727 ->getMockForAbstractClass();
729 $archive->expects($this->once())
730 ->method('add_directory')
731 ->with($this->equalTo($archivepath))
732 ->willReturn($expectedresult);
734 $archive->expects($this->never())
735 ->method('add_file_from_pathname');
737 $result = $fs->add_storedfile_to_archive($file, $archive, $archivepath);
739 $this->assertEquals($expectedresult, $result);
743 * Ensure that the add_storedfile_to_archive function requires a local copy of the
744 * file, and passes the path to the archive.
747 public function test_add_storedfile_to_archive_file() {
748 $file = $this->get_stored_file('example content');
749 $filepath = __LINE__;
750 $archivepath = 'example';
751 $expectedresult = (object) [];
753 // Mock the file_system class.
754 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
755 $fs->method('get_local_path_from_storedfile')
756 ->with($this->equalTo($file), $this->equalTo(true))
757 ->willReturn($filepath);
759 $archive = $this->getMockBuilder(file_archive::class)
760 ->onlyMethods([
761 'add_directory',
762 'add_file_from_pathname',
764 ->getMockForAbstractClass();
766 $archive->expects($this->never())
767 ->method('add_directory');
769 $archive->expects($this->once())
770 ->method('add_file_from_pathname')
771 ->with(
772 $this->equalTo($archivepath),
773 $this->equalTo($filepath)
775 ->willReturn($expectedresult);
777 $result = $fs->add_storedfile_to_archive($file, $archive, $archivepath);
779 $this->assertEquals($expectedresult, $result);
783 * Ensure that the add_to_curl_request function requires a local copy of the
784 * file, and passes the path to curl_file_create.
786 * @covers ::add_to_curl_request
788 public function test_add_to_curl_request() {
789 $file = $this->get_stored_file('example content');
790 $filepath = __FILE__;
791 $archivepath = 'example';
792 $key = 'myfile';
794 // Mock the file_system class.
795 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
796 $fs->method('get_local_path_from_storedfile')
797 ->with($this->equalTo($file), $this->equalTo(true))
798 ->willReturn($filepath);
800 $request = (object) ['_tmp_file_post_params' => []];
801 $fs->add_to_curl_request($file, $request, $key);
802 $this->assertArrayHasKey($key, $request->_tmp_file_post_params);
803 $this->assertEquals($filepath, $request->_tmp_file_post_params[$key]->name);
807 * Ensure that test_get_imageinfo_not_image returns false if the file
808 * passed was deemed to not be an image.
810 * @covers ::get_imageinfo
812 public function test_get_imageinfo_not_image() {
813 $filecontent = 'example content';
814 $file = $this->get_stored_file($filecontent);
816 $fs = $this->get_testable_mock([
817 'is_image_from_storedfile',
820 $fs->expects($this->once())
821 ->method('is_image_from_storedfile')
822 ->with($this->equalTo($file))
823 ->willReturn(false);
825 $this->assertFalse($fs->get_imageinfo($file));
829 * Ensure that test_get_imageinfo_not_image returns imageinfo.
831 * @covers ::get_imageinfo
833 public function test_get_imageinfo() {
834 $filepath = '/path/to/file';
835 $filecontent = 'example content';
836 $expectedresult = (object) [];
837 $file = $this->get_stored_file($filecontent);
839 $fs = $this->get_testable_mock([
840 'is_image_from_storedfile',
841 'get_local_path_from_storedfile',
842 'get_imageinfo_from_path',
845 $fs->expects($this->once())
846 ->method('is_image_from_storedfile')
847 ->with($this->equalTo($file))
848 ->willReturn(true);
850 $fs->expects($this->once())
851 ->method('get_local_path_from_storedfile')
852 ->with($this->equalTo($file), $this->equalTo(true))
853 ->willReturn($filepath);
855 $fs->expects($this->once())
856 ->method('get_imageinfo_from_path')
857 ->with($this->equalTo($filepath))
858 ->willReturn($expectedresult);
860 $this->assertEquals($expectedresult, $fs->get_imageinfo($file));
864 * Ensure that is_image_from_storedfile always returns false for an
865 * empty file size.
867 * @covers ::is_image_from_storedfile
869 public function test_is_image_empty_filesize() {
870 $filecontent = 'example content';
871 $file = $this->get_stored_file($filecontent, null, ['get_filesize']);
873 $file->expects($this->once())
874 ->method('get_filesize')
875 ->willReturn(0);
877 $fs = $this->get_testable_mock();
878 $this->assertFalse($fs->is_image_from_storedfile($file));
882 * Ensure that is_image_from_storedfile behaves correctly based on
883 * mimetype.
885 * @dataProvider is_image_from_storedfile_provider
886 * @param string $mimetype Mimetype to test
887 * @param bool $isimage Whether this mimetype should be detected as an image
888 * @covers ::is_image_from_storedfile
890 public function test_is_image_from_storedfile_mimetype($mimetype, $isimage) {
891 $filecontent = 'example content';
892 $file = $this->get_stored_file($filecontent, null, ['get_mimetype']);
894 $file->expects($this->once())
895 ->method('get_mimetype')
896 ->willReturn($mimetype);
898 $fs = $this->get_testable_mock();
899 $this->assertEquals($isimage, $fs->is_image_from_storedfile($file));
903 * Test that get_imageinfo_from_path returns an appropriate response
904 * for an image.
906 * @covers ::get_imageinfo_from_path
908 public function test_get_imageinfo_from_path() {
909 $filepath = __DIR__ . "/fixtures/testimage.jpg";
911 // Get the filesystem mock.
912 $fs = $this->get_testable_mock();
914 $method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
915 $method->setAccessible(true);
916 $result = $method->invokeArgs($fs, [$filepath]);
918 $this->assertArrayHasKey('width', $result);
919 $this->assertArrayHasKey('height', $result);
920 $this->assertArrayHasKey('mimetype', $result);
921 $this->assertEquals('image/jpeg', $result['mimetype']);
925 * Test that get_imageinfo_from_path returns an appropriate response
926 * for a file which is not an image.
928 * @covers ::get_imageinfo_from_path
930 public function test_get_imageinfo_from_path_no_image() {
931 $filepath = __FILE__;
933 // Get the filesystem mock.
934 $fs = $this->get_testable_mock();
936 $method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
937 $method->setAccessible(true);
938 $result = $method->invokeArgs($fs, [$filepath]);
940 $this->assertFalse($result);
944 * Ensure that get_content_file_handle returns a valid file handle.
946 * @covers ::get_content_file_handle
948 public function test_get_content_file_handle_default() {
949 $filecontent = 'example content';
950 $file = $this->get_stored_file($filecontent);
952 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']);
953 $fs->method('get_remote_path_from_storedfile')
954 ->willReturn(__FILE__);
956 // Note: We are unable to determine the mode in which the $fh was opened.
957 $fh = $fs->get_content_file_handle($file);
958 $this->assertTrue(is_resource($fh));
959 $this->assertEquals('stream', get_resource_type($fh));
960 fclose($fh);
964 * Ensure that get_content_file_handle returns a valid file handle for a gz file.
966 * @covers ::get_content_file_handle
968 public function test_get_content_file_handle_gz() {
969 $filecontent = 'example content';
970 $file = $this->get_stored_file($filecontent);
972 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
973 $fs->method('get_local_path_from_storedfile')
974 ->willReturn(__DIR__ . "/fixtures/test.tgz");
976 // Note: We are unable to determine the mode in which the $fh was opened.
977 $fh = $fs->get_content_file_handle($file, stored_file::FILE_HANDLE_GZOPEN);
978 $this->assertTrue(is_resource($fh));
979 gzclose($fh);
983 * Ensure that get_content_file_handle returns an exception when calling for a invalid file handle type.
985 * @covers ::get_content_file_handle
987 public function test_get_content_file_handle_invalid() {
988 $filecontent = 'example content';
989 $file = $this->get_stored_file($filecontent);
991 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']);
992 $fs->method('get_remote_path_from_storedfile')
993 ->willReturn(__FILE__);
995 $this->expectException('coding_exception', 'Unexpected file handle type');
996 $fs->get_content_file_handle($file, -1);
1000 * Test that mimetype_from_hash returns the correct mimetype with
1001 * a file whose filename suggests mimetype.
1003 * @covers ::mimetype_from_hash
1005 public function test_mimetype_from_hash_using_filename() {
1006 $filepath = '/path/to/file/not/currently/on/disk';
1007 $filecontent = 'example content';
1008 $filename = 'test.jpg';
1009 $contenthash = file_storage::hash_from_string($filecontent);
1011 $fs = $this->get_testable_mock(['get_remote_path_from_hash']);
1012 $fs->method('get_remote_path_from_hash')->willReturn($filepath);
1014 $result = $fs->mimetype_from_hash($contenthash, $filename);
1015 $this->assertEquals('image/jpeg', $result);
1019 * Test that mimetype_from_hash returns the correct mimetype with
1020 * a locally available file whose filename does not suggest mimetype.
1022 * @covers ::mimetype_from_hash
1024 public function test_mimetype_from_hash_using_file_content() {
1025 $filecontent = 'example content';
1026 $contenthash = file_storage::hash_from_string($filecontent);
1027 $filename = 'example';
1029 $filepath = __DIR__ . "/fixtures/testimage.jpg";
1030 $fs = $this->get_testable_mock(['get_local_path_from_hash']);
1031 $fs->method('get_local_path_from_hash')->willReturn($filepath);
1033 $result = $fs->mimetype_from_hash($contenthash, $filename);
1034 $this->assertEquals('image/jpeg', $result);
1038 * Test that mimetype_from_hash returns the correct mimetype with
1039 * a remotely available file whose filename does not suggest mimetype.
1041 * @covers ::mimetype_from_hash
1043 public function test_mimetype_from_hash_using_file_content_remote() {
1044 $filepath = '/path/to/file/not/currently/on/disk';
1045 $filecontent = 'example content';
1046 $contenthash = file_storage::hash_from_string($filecontent);
1047 $filename = 'example';
1049 $filepath = __DIR__ . "/fixtures/testimage.jpg";
1051 $fs = $this->get_testable_mock([
1052 'get_remote_path_from_hash',
1053 'is_file_readable_locally_by_hash',
1054 'get_local_path_from_hash',
1057 $fs->method('get_remote_path_from_hash')->willReturn('/path/to/remote/file');
1058 $fs->method('is_file_readable_locally_by_hash')->willReturn(false);
1059 $fs->method('get_local_path_from_hash')->willReturn($filepath);
1061 $result = $fs->mimetype_from_hash($contenthash, $filename);
1062 $this->assertEquals('image/jpeg', $result);
1066 * Test that mimetype_from_storedfile returns the correct mimetype with
1067 * a file whose filename suggests mimetype.
1069 * @covers ::mimetype_from_storedfile
1071 public function test_mimetype_from_storedfile_empty() {
1072 $file = $this->get_stored_file('');
1074 $fs = $this->get_testable_mock();
1075 $result = $fs->mimetype_from_storedfile($file);
1076 $this->assertNull($result);
1080 * Test that mimetype_from_storedfile returns the correct mimetype with
1081 * a file whose filename suggests mimetype.
1083 * @covers ::mimetype_from_storedfile
1085 public function test_mimetype_from_storedfile_using_filename() {
1086 $filepath = '/path/to/file/not/currently/on/disk';
1087 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']);
1088 $fs->method('get_remote_path_from_storedfile')->willReturn($filepath);
1090 $file = $this->get_stored_file('example content', 'test.jpg');
1092 $result = $fs->mimetype_from_storedfile($file);
1093 $this->assertEquals('image/jpeg', $result);
1097 * Test that mimetype_from_storedfile returns the correct mimetype with
1098 * a locally available file whose filename does not suggest mimetype.
1100 * @covers ::mimetype_from_storedfile
1102 public function test_mimetype_from_storedfile_using_file_content() {
1103 $filepath = __DIR__ . "/fixtures/testimage.jpg";
1104 $fs = $this->get_testable_mock(['get_local_path_from_hash']);
1105 $fs->method('get_local_path_from_hash')->willReturn($filepath);
1107 $file = $this->get_stored_file('example content');
1109 $result = $fs->mimetype_from_storedfile($file);
1110 $this->assertEquals('image/jpeg', $result);
1114 * Test that mimetype_from_storedfile returns the correct mimetype with
1115 * a remotely available file whose filename does not suggest mimetype.
1117 * @covers ::mimetype_from_storedfile
1119 public function test_mimetype_from_storedfile_using_file_content_remote() {
1120 $filepath = __DIR__ . "/fixtures/testimage.jpg";
1122 $fs = $this->get_testable_mock([
1123 'is_file_readable_locally_by_hash',
1124 'get_local_path_from_hash',
1127 $fs->method('is_file_readable_locally_by_hash')->willReturn(false);
1128 $fs->method('get_local_path_from_hash')->will($this->onConsecutiveCalls('/path/to/remote/file', $filepath));
1130 $file = $this->get_stored_file('example content');
1132 $result = $fs->mimetype_from_storedfile($file);
1133 $this->assertEquals('image/jpeg', $result);
1137 * Data Provider for is_image_from_storedfile tests.
1139 * @return array
1141 public function is_image_from_storedfile_provider() {
1142 return array(
1143 'Standard image' => array('image/png', true),
1144 'Made up document/image' => array('document/image', false),
1149 * Data provider for get_local_path_from_storedfile tests.
1151 * @return array
1153 public function get_local_path_from_storedfile_provider() {
1154 return [
1155 'default args (nofetch)' => [
1156 'args' => [],
1157 'fetch' => 0,
1159 'explicit: nofetch' => [
1160 'args' => [false],
1161 'fetch' => 0,
1163 'explicit: fetch' => [
1164 'args' => [true],
1165 'fetch' => 1,