. /** * Unit tests for file_system. * * @package core_files * @category phpunit * @copyright 2017 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->libdir . '/filestorage/file_system.php'); /** * Unit tests for file_system. * * @package core_files * @category phpunit * @copyright 2017 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @coversDefaultClass file_system */ class core_files_file_system_testcase extends advanced_testcase { public function setUp() { get_file_storage(true); } public function tearDown() { get_file_storage(true); } /** * Helper function to help setup and configure the virtual file system stream. * * @param array $filedir Directory structure and content of the filedir * @param array $trashdir Directory structure and content of the sourcedir * @param array $sourcedir Directory structure and content of a directory used for source files for tests * @return \org\bovigo\vfs\vfsStream */ protected function setup_vfile_root($content = []) { $vfileroot = \org\bovigo\vfs\vfsStream::setup('root', null, $content); return $vfileroot; } /** * Helper to create a stored file objectw with the given supplied content. * * @param string $filecontent The content of the mocked file * @param string $filename The file name to use in the stored_file * @param array $mockedmethods A list of methods you intend to override * If no methods are specified, only abstract functions are mocked. * @return stored_file */ protected function get_stored_file($filecontent, $filename = null, $mockedmethods = null) { $contenthash = file_storage::hash_from_string($filecontent); if (empty($filename)) { $filename = $contenthash; } $file = $this->getMockBuilder(stored_file::class) ->setMethods($mockedmethods) ->setConstructorArgs([ get_file_storage(), (object) [ 'contenthash' => $contenthash, 'filesize' => strlen($filecontent), 'filename' => $filename, ] ]) ->getMock(); return $file; } /** * Get a testable mock of the abstract file_system class. * * @param array $mockedmethods A list of methods you intend to override * If no methods are specified, only abstract functions are mocked. * @return file_system */ protected function get_testable_mock($mockedmethods = []) { $fs = $this->getMockBuilder(file_system::class) ->setMethods($mockedmethods) ->getMockForAbstractClass(); return $fs; } /** * Ensure that the file system is not clonable. * * @covers :: */ public function test_not_cloneable() { $reflection = new ReflectionClass('file_system'); $this->assertFalse($reflection->isCloneable()); } /** * Ensure that the filedir file_system extension is used by default. * * @covers :: */ public function test_default_class() { $this->resetAfterTest(); // Ensure that the alternative_file_system_class is null. global $CFG; $CFG->alternative_file_system_class = null; $storage = get_file_storage(); $fs = $storage->get_file_system(); $this->assertInstanceOf(file_system::class, $fs); $this->assertEquals(file_system_filedir::class, get_class($fs)); } /** * Ensure that the specified file_system extension class is used. * * @covers :: */ public function test_supplied_class() { global $CFG; $this->resetAfterTest(); // Mock the file_system. // Mocks create a new child of the mocked class which is perfect for this test. $filesystem = $this->getMockBuilder('file_system') ->disableOriginalConstructor() ->getMock(); $CFG->alternative_file_system_class = get_class($filesystem); $storage = get_file_storage(); $fs = $storage->get_file_system(); $this->assertInstanceOf(file_system::class, $fs); $this->assertEquals(get_class($filesystem), get_class($fs)); } /** * Test that the readfile function outputs content to disk. * * @covers ::readfile * @covers :: */ public function test_readfile_remote() { global $CFG; // Mock the filesystem. $filecontent = 'example content'; $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]); $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile'); $file = $this->get_stored_file($filecontent); // Mock the file_system class. // We need to override the get_remote_path_from_storedfile function. $fs = $this->get_testable_mock([ 'get_remote_path_from_storedfile', 'is_file_readable_locally_by_storedfile', 'get_local_path_from_storedfile', ]); $fs->method('get_remote_path_from_storedfile')->willReturn($filepath); $fs->method('is_file_readable_locally_by_storedfile')->willReturn(false); $fs->expects($this->never())->method('get_local_path_from_storedfile'); // Note: It is currently not possible to mock readfile_allow_large // because file_system is in the global namespace. // We must therefore check for expected output. This is not ideal. $this->expectOutputString($filecontent); $fs->readfile($file); } /** * Test that the readfile function outputs content to disk. * * @covers ::readfile * @covers :: */ public function test_readfile_local() { global $CFG; // Mock the filesystem. $filecontent = 'example content'; $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]); $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile'); $file = $this->get_stored_file($filecontent); // Mock the file_system class. // We need to override the get_remote_path_from_storedfile function. $fs = $this->get_testable_mock([ 'get_remote_path_from_storedfile', 'is_file_readable_locally_by_storedfile', 'get_local_path_from_storedfile', ]); $fs->method('is_file_readable_locally_by_storedfile')->willReturn(true); $fs->expects($this->never())->method('get_remote_path_from_storedfile'); $fs->expects($this->once())->method('get_local_path_from_storedfile')->willReturn($filepath); // Note: It is currently not possible to mock readfile_allow_large // because file_system is in the global namespace. // We must therefore check for expected output. This is not ideal. $this->expectOutputString($filecontent); $fs->readfile($file); } /** * Test that the get_local_path_from_storedfile function functions * correctly when called with various args. * * @dataProvider get_local_path_from_storedfile_provider * @param array $args The additional args to pass to get_local_path_from_storedfile * @param bool $fetch Whether the combination of args should have caused a fetch * * @covers ::get_local_path_from_storedfile * @covers :: */ public function test_get_local_path_from_storedfile($args, $fetch) { $filepath = '/path/to/file'; $filecontent = 'example content'; // Get the filesystem mock. $fs = $this->get_testable_mock([ 'get_local_path_from_hash', ]); $fs->expects($this->once()) ->method('get_local_path_from_hash') ->with($this->equalTo(file_storage::hash_from_string($filecontent)), $this->equalTo($fetch)) ->willReturn($filepath); $file = $this->get_stored_file($filecontent); $method = new ReflectionMethod(file_system::class, 'get_local_path_from_storedfile'); $method->setAccessible(true); $result = $method->invokeArgs($fs, array_merge([$file], $args)); $this->assertEquals($filepath, $result); } /** * Ensure that the default implementation of get_remote_path_from_storedfile * simply calls get_local_path_from_storedfile without requiring a * fetch. * * @covers ::get_remote_path_from_storedfile * @covers :: */ public function test_get_remote_path_from_storedfile() { $filepath = '/path/to/file'; $filecontent = 'example content'; $fs = $this->get_testable_mock([ 'get_remote_path_from_hash', ]); $fs->expects($this->once()) ->method('get_remote_path_from_hash') ->with($this->equalTo(file_storage::hash_from_string($filecontent)), $this->equalTo(false)) ->willReturn($filepath); $file = $this->get_stored_file($filecontent); $method = new ReflectionMethod(file_system::class, 'get_remote_path_from_storedfile'); $method->setAccessible(true); $result = $method->invokeArgs($fs, [$file]); $this->assertEquals($filepath, $result); } /** * Test the stock implementation of is_file_readable_locally_by_hash with a valid file. * * This should call get_local_path_from_hash and check the readability * of the file. * * Fetching the file is optional. * * @covers ::is_file_readable_locally_by_hash * @covers :: */ public function test_is_file_readable_locally_by_hash() { $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $filepath = __FILE__; $fs = $this->get_testable_mock([ 'get_local_path_from_hash', ]); $fs->method('get_local_path_from_hash') ->with($this->equalTo($contenthash), $this->equalTo(false)) ->willReturn($filepath); $this->assertTrue($fs->is_file_readable_locally_by_hash($contenthash)); } /** * Test the stock implementation of is_file_readable_locally_by_hash with an empty file. * * @covers ::is_file_readable_locally_by_hash * @covers :: */ public function test_is_file_readable_locally_by_hash_empty() { $filecontent = ''; $contenthash = file_storage::hash_from_string($filecontent); $fs = $this->get_testable_mock([ 'get_local_path_from_hash', ]); $fs->expects($this->never()) ->method('get_local_path_from_hash'); $this->assertTrue($fs->is_file_readable_locally_by_hash($contenthash)); } /** * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. * * @covers ::is_file_readable_remotely_by_hash * @covers :: */ public function test_is_file_readable_remotely_by_hash() { $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $fs = $this->get_testable_mock([ 'get_remote_path_from_hash', ]); $fs->method('get_remote_path_from_hash') ->with($this->equalTo($contenthash), $this->equalTo(false)) ->willReturn(__FILE__); $this->assertTrue($fs->is_file_readable_remotely_by_hash($contenthash)); } /** * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. * * @covers ::is_file_readable_remotely_by_hash * @covers :: */ public function test_is_file_readable_remotely_by_hash_empty() { $filecontent = ''; $contenthash = file_storage::hash_from_string($filecontent); $fs = $this->get_testable_mock([ 'get_remote_path_from_hash', ]); $fs->expects($this->never()) ->method('get_remote_path_from_hash'); $this->assertTrue($fs->is_file_readable_remotely_by_hash($contenthash)); } /** * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. * * @covers ::is_file_readable_remotely_by_hash * @covers :: */ public function test_is_file_readable_remotely_by_hash_not_found() { $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $fs = $this->get_testable_mock([ 'get_remote_path_from_hash', ]); $fs->method('get_remote_path_from_hash') ->with($this->equalTo($contenthash), $this->equalTo(false)) ->willReturn('/path/to/nonexistent/file'); $this->assertFalse($fs->is_file_readable_remotely_by_hash($contenthash)); } /** * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. * * @covers ::is_file_readable_remotely_by_storedfile * @covers :: */ public function test_is_file_readable_remotely_by_storedfile() { $file = $this->get_stored_file('example content'); $fs = $this->get_testable_mock([ 'get_remote_path_from_storedfile', ]); $fs->method('get_remote_path_from_storedfile') ->willReturn(__FILE__); $this->assertTrue($fs->is_file_readable_remotely_by_storedfile($file)); } /** * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. * * @covers ::is_file_readable_remotely_by_storedfile * @covers :: */ public function test_is_file_readable_remotely_by_storedfile_empty() { $fs = $this->get_testable_mock([ 'get_remote_path_from_storedfile', ]); $fs->expects($this->never()) ->method('get_remote_path_from_storedfile'); $file = $this->get_stored_file(''); $this->assertTrue($fs->is_file_readable_remotely_by_storedfile($file)); } /** * Test the stock implementation of is_file_readable_locally_by_storedfile with an empty file. * * @covers ::is_file_readable_locally_by_storedfile * @covers :: */ public function test_is_file_readable_locally_by_storedfile_empty() { $fs = $this->get_testable_mock([ 'get_local_path_from_storedfile', ]); $fs->expects($this->never()) ->method('get_local_path_from_storedfile'); $file = $this->get_stored_file(''); $this->assertTrue($fs->is_file_readable_locally_by_storedfile($file)); } /** * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. * * @covers ::is_file_readable_locally_by_storedfile * @covers :: */ public function test_is_file_readable_remotely_by_storedfile_not_found() { $file = $this->get_stored_file('example content'); $fs = $this->get_testable_mock([ 'get_remote_path_from_storedfile', ]); $fs->method('get_remote_path_from_storedfile') ->willReturn(__LINE__); $this->assertFalse($fs->is_file_readable_remotely_by_storedfile($file)); } /** * Test the stock implementation of is_file_readable_locally_by_storedfile with a valid file. * * @covers ::is_file_readable_locally_by_storedfile * @covers :: */ public function test_is_file_readable_locally_by_storedfile_unreadable() { $fs = $this->get_testable_mock([ 'get_local_path_from_storedfile', ]); $file = $this->get_stored_file('example content'); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(false)) ->willReturn('/path/to/nonexistent/file'); $this->assertFalse($fs->is_file_readable_locally_by_storedfile($file)); } /** * Test the stock implementation of is_file_readable_locally_by_storedfile with a valid file should pass fetch. * * @covers ::is_file_readable_locally_by_storedfile * @covers :: */ public function test_is_file_readable_locally_by_storedfile_passes_fetch() { $fs = $this->get_testable_mock([ 'get_local_path_from_storedfile', ]); $file = $this->get_stored_file('example content'); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn('/path/to/nonexistent/file'); $this->assertFalse($fs->is_file_readable_locally_by_storedfile($file, true)); } /** * Ensure that is_file_removable returns correctly for an empty file. * * @covers ::is_file_removable * @covers :: */ public function test_is_file_removable_empty() { $filecontent = ''; $contenthash = file_storage::hash_from_string($filecontent); $method = new ReflectionMethod(file_system::class, 'is_file_removable'); $method->setAccessible(true); $result = $method->invokeArgs(null, [$contenthash]); $this->assertFalse($result); } /** * Ensure that is_file_removable returns false if the file is still in use. * * @covers ::is_file_removable * @covers :: */ public function test_is_file_removable_in_use() { $this->resetAfterTest(); global $DB; $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $DB = $this->getMockBuilder(\moodle_database::class) ->setMethods(['record_exists']) ->getMockForAbstractClass(); $DB->method('record_exists')->willReturn(true); $method = new ReflectionMethod(file_system::class, 'is_file_removable'); $method->setAccessible(true); $result = $method->invokeArgs(null, [$contenthash]); $this->assertFalse($result); } /** * Ensure that is_file_removable returns false if the file is not in use. * * @covers ::is_file_removable * @covers :: */ public function test_is_file_removable_not_in_use() { $this->resetAfterTest(); global $DB; $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $DB = $this->getMockBuilder(\moodle_database::class) ->setMethods(['record_exists']) ->getMockForAbstractClass(); $DB->method('record_exists')->willReturn(false); $method = new ReflectionMethod(file_system::class, 'is_file_removable'); $method->setAccessible(true); $result = $method->invokeArgs(null, [$contenthash]); $this->assertTrue($result); } /** * Test the stock implementation of get_content. * * @covers ::get_content * @covers :: */ public function test_get_content() { global $CFG; // Mock the filesystem. $filecontent = 'example content'; $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]); $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile'); $file = $this->get_stored_file($filecontent); // Mock the file_system class. // We need to override the get_remote_path_from_storedfile function. $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); $fs->method('get_remote_path_from_storedfile')->willReturn($filepath); $result = $fs->get_content($file); $this->assertEquals($filecontent, $result); } /** * Test the stock implementation of get_content. * * @covers ::get_content * @covers :: */ public function test_get_content_empty() { global $CFG; $filecontent = ''; $file = $this->get_stored_file($filecontent); // Mock the file_system class. // We need to override the get_remote_path_from_storedfile function. $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); $fs->expects($this->never()) ->method('get_remote_path_from_storedfile'); $result = $fs->get_content($file); $this->assertEquals($filecontent, $result); } /** * Ensure that the list_files function requires a local copy of the * file, and passes the path to the packer. * * @covers ::list_files * @covers :: */ public function test_list_files() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $filepath = __FILE__; $expectedresult = (object) []; // Mock the file_system class. $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn(__FILE__); $packer = $this->getMockBuilder(file_packer::class) ->setMethods(['list_files']) ->getMockForAbstractClass(); $packer->expects($this->once()) ->method('list_files') ->with($this->equalTo($filepath)) ->willReturn($expectedresult); $result = $fs->list_files($file, $packer); $this->assertEquals($expectedresult, $result); } /** * Ensure that the extract_to_pathname function requires a local copy of the * file, and passes the path to the packer. * * @covers ::extract_to_pathname * @covers :: */ public function test_extract_to_pathname() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $filepath = __FILE__; $expectedresult = (object) []; $outputpath = '/path/to/output'; // Mock the file_system class. $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn(__FILE__); $packer = $this->getMockBuilder(file_packer::class) ->setMethods(['extract_to_pathname']) ->getMockForAbstractClass(); $packer->expects($this->once()) ->method('extract_to_pathname') ->with($this->equalTo($filepath), $this->equalTo($outputpath), $this->equalTo(null), $this->equalTo(null)) ->willReturn($expectedresult); $result = $fs->extract_to_pathname($file, $packer, $outputpath); $this->assertEquals($expectedresult, $result); } /** * Ensure that the extract_to_storage function requires a local copy of the * file, and passes the path to the packer. * * @covers ::extract_to_storage * @covers :: */ public function test_extract_to_storage() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $filepath = __FILE__; $expectedresult = (object) []; $outputpath = '/path/to/output'; // Mock the file_system class. $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn(__FILE__); $packer = $this->getMockBuilder(file_packer::class) ->setMethods(['extract_to_storage']) ->getMockForAbstractClass(); $packer->expects($this->once()) ->method('extract_to_storage') ->with( $this->equalTo($filepath), $this->equalTo(42), $this->equalTo('component'), $this->equalTo('filearea'), $this->equalTo('itemid'), $this->equalTo('pathbase'), $this->equalTo('userid'), $this->equalTo(null) ) ->willReturn($expectedresult); $result = $fs->extract_to_storage($file, $packer, 42, 'component','filearea', 'itemid', 'pathbase', 'userid'); $this->assertEquals($expectedresult, $result); } /** * Ensure that the add_storedfile_to_archive function requires a local copy of the * file, and passes the path to the archive. * * @covers :: */ public function test_add_storedfile_to_archive_directory() { $file = $this->get_stored_file('', '.'); $archivepath = 'example'; $expectedresult = (object) []; // Mock the file_system class. $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn(__FILE__); $archive = $this->getMockBuilder(file_archive::class) ->setMethods([ 'add_directory', 'add_file_from_pathname', ]) ->getMockForAbstractClass(); $archive->expects($this->once()) ->method('add_directory') ->with($this->equalTo($archivepath)) ->willReturn($expectedresult); $archive->expects($this->never()) ->method('add_file_from_pathname'); $result = $fs->add_storedfile_to_archive($file, $archive, $archivepath); $this->assertEquals($expectedresult, $result); } /** * Ensure that the add_storedfile_to_archive function requires a local copy of the * file, and passes the path to the archive. * * @covers :: */ public function test_add_storedfile_to_archive_file() { $file = $this->get_stored_file('example content'); $filepath = __LINE__; $archivepath = 'example'; $expectedresult = (object) []; // Mock the file_system class. $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn($filepath); $archive = $this->getMockBuilder(file_archive::class) ->setMethods([ 'add_directory', 'add_file_from_pathname', ]) ->getMockForAbstractClass(); $archive->expects($this->never()) ->method('add_directory'); $archive->expects($this->once()) ->method('add_file_from_pathname') ->with( $this->equalTo($archivepath), $this->equalTo($filepath) ) ->willReturn($expectedresult); $result = $fs->add_storedfile_to_archive($file, $archive, $archivepath); $this->assertEquals($expectedresult, $result); } /** * Ensure that the add_to_curl_request function requires a local copy of the * file, and passes the path to curl_file_create. * * @covers ::add_to_curl_request * @covers :: */ public function test_add_to_curl_request() { $file = $this->get_stored_file('example content'); $filepath = __FILE__; $archivepath = 'example'; $key = 'myfile'; // Mock the file_system class. $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn($filepath); $request = (object) ['_tmp_file_post_params' => []]; $fs->add_to_curl_request($file, $request, $key); $this->assertArrayHasKey($key, $request->_tmp_file_post_params); $this->assertEquals($filepath, $request->_tmp_file_post_params[$key]->name); } /** * Ensure that test_get_imageinfo_not_image returns false if the file * passed was deemed to not be an image. * * @covers ::get_imageinfo * @covers :: */ public function test_get_imageinfo_not_image() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $fs = $this->get_testable_mock([ 'is_image_from_storedfile', ]); $fs->expects($this->once()) ->method('is_image_from_storedfile') ->with($this->equalTo($file)) ->willReturn(false); $this->assertFalse($fs->get_imageinfo($file)); } /** * Ensure that test_get_imageinfo_not_image returns imageinfo. * * @covers ::get_imageinfo * @covers :: */ public function test_get_imageinfo() { $filepath = '/path/to/file'; $filecontent = 'example content'; $expectedresult = (object) []; $file = $this->get_stored_file($filecontent); $fs = $this->get_testable_mock([ 'is_image_from_storedfile', 'get_local_path_from_storedfile', 'get_imageinfo_from_path', ]); $fs->expects($this->once()) ->method('is_image_from_storedfile') ->with($this->equalTo($file)) ->willReturn(true); $fs->expects($this->once()) ->method('get_local_path_from_storedfile') ->with($this->equalTo($file), $this->equalTo(true)) ->willReturn($filepath); $fs->expects($this->once()) ->method('get_imageinfo_from_path') ->with($this->equalTo($filepath)) ->willReturn($expectedresult); $this->assertEquals($expectedresult, $fs->get_imageinfo($file)); } /** * Ensure that is_image_from_storedfile always returns false for an * empty file size. * * @covers ::is_image_from_storedfile * @covers :: */ public function test_is_image_empty_filesize() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent, null, ['get_filesize']); $file->expects($this->once()) ->method('get_filesize') ->willReturn(0); $fs = $this->get_testable_mock(); $this->assertFalse($fs->is_image_from_storedfile($file)); } /** * Ensure that is_image_from_storedfile behaves correctly based on * mimetype. * * @dataProvider is_image_from_storedfile_provider * @param string $mimetype Mimetype to test * @param bool $isimage Whether this mimetype should be detected as an image * @covers ::is_image_from_storedfile * @covers :: */ public function test_is_image_from_storedfile_mimetype($mimetype, $isimage) { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent, null, ['get_mimetype']); $file->expects($this->once()) ->method('get_mimetype') ->willReturn($mimetype); $fs = $this->get_testable_mock(); $this->assertEquals($isimage, $fs->is_image_from_storedfile($file)); } /** * Test that get_imageinfo_from_path returns an appropriate response * for an image. * * @covers ::get_imageinfo_from_path * @covers :: */ public function test_get_imageinfo_from_path() { $filepath = __DIR__ . "/fixtures/testimage.jpg"; // Get the filesystem mock. $fs = $this->get_testable_mock(); $method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); $method->setAccessible(true); $result = $method->invokeArgs($fs, [$filepath]); $this->assertArrayHasKey('width', $result); $this->assertArrayHasKey('height', $result); $this->assertArrayHasKey('mimetype', $result); $this->assertEquals('image/jpeg', $result['mimetype']); } /** * Test that get_imageinfo_from_path returns an appropriate response * for a file which is not an image. * * @covers ::get_imageinfo_from_path * @covers :: */ public function test_get_imageinfo_from_path_no_image() { $filepath = __FILE__; // Get the filesystem mock. $fs = $this->get_testable_mock(); $method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); $method->setAccessible(true); $result = $method->invokeArgs($fs, [$filepath]); $this->assertFalse($result); } /** * Ensure that get_content_file_handle returns a valid file handle. * * @covers ::get_content_file_handle * @covers :: */ public function test_get_content_file_handle_default() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); $fs->method('get_remote_path_from_storedfile') ->willReturn(__FILE__); // Note: We are unable to determine the mode in which the $fh was opened. $fh = $fs->get_content_file_handle($file); $this->assertTrue(is_resource($fh)); $this->assertEquals('stream', get_resource_type($fh)); fclose($fh); } /** * Ensure that get_content_file_handle returns a valid file handle for a gz file. * * @covers ::get_content_file_handle * @covers :: */ public function test_get_content_file_handle_gz() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); $fs->method('get_local_path_from_storedfile') ->willReturn(__DIR__ . "/fixtures/test.tgz"); // Note: We are unable to determine the mode in which the $fh was opened. $fh = $fs->get_content_file_handle($file, stored_file::FILE_HANDLE_GZOPEN); $this->assertTrue(is_resource($fh)); gzclose($fh); } /** * Ensure that get_content_file_handle returns an exception when calling for a invalid file handle type. * * @covers ::get_content_file_handle * @covers :: */ public function test_get_content_file_handle_invalid() { $filecontent = 'example content'; $file = $this->get_stored_file($filecontent); $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); $fs->method('get_remote_path_from_storedfile') ->willReturn(__FILE__); $this->expectException('coding_exception', 'Unexpected file handle type'); $fs->get_content_file_handle($file, -1); } /** * Test that mimetype_from_hash returns the correct mimetype with * a file whose filename suggests mimetype. * * @covers ::mimetype_from_hash * @covers :: */ public function test_mimetype_from_hash_using_filename() { $filepath = '/path/to/file/not/currently/on/disk'; $filecontent = 'example content'; $filename = 'test.jpg'; $contenthash = file_storage::hash_from_string($filecontent); $fs = $this->get_testable_mock(['get_remote_path_from_hash']); $fs->method('get_remote_path_from_hash')->willReturn($filepath); $result = $fs->mimetype_from_hash($contenthash, $filename); $this->assertEquals('image/jpeg', $result); } /** * Test that mimetype_from_hash returns the correct mimetype with * a locally available file whose filename does not suggest mimetype. * * @covers ::mimetype_from_hash * @covers :: */ public function test_mimetype_from_hash_using_file_content() { $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $filename = 'example'; $filepath = __DIR__ . "/fixtures/testimage.jpg"; $fs = $this->get_testable_mock(['get_local_path_from_hash']); $fs->method('get_local_path_from_hash')->willReturn($filepath); $result = $fs->mimetype_from_hash($contenthash, $filename); $this->assertEquals('image/jpeg', $result); } /** * Test that mimetype_from_hash returns the correct mimetype with * a remotely available file whose filename does not suggest mimetype. * * @covers ::mimetype_from_hash * @covers :: */ public function test_mimetype_from_hash_using_file_content_remote() { $filepath = '/path/to/file/not/currently/on/disk'; $filecontent = 'example content'; $contenthash = file_storage::hash_from_string($filecontent); $filename = 'example'; $filepath = __DIR__ . "/fixtures/testimage.jpg"; $fs = $this->get_testable_mock([ 'get_remote_path_from_hash', 'is_file_readable_locally_by_hash', 'get_local_path_from_hash', ]); $fs->method('get_remote_path_from_hash')->willReturn('/path/to/remote/file'); $fs->method('is_file_readable_locally_by_hash')->willReturn(false); $fs->method('get_local_path_from_hash')->willReturn($filepath); $result = $fs->mimetype_from_hash($contenthash, $filename); $this->assertEquals('image/jpeg', $result); } /** * Test that mimetype_from_storedfile returns the correct mimetype with * a file whose filename suggests mimetype. * * @covers ::mimetype_from_storedfile * @covers :: */ public function test_mimetype_from_storedfile_empty() { $file = $this->get_stored_file(''); $fs = $this->get_testable_mock(); $result = $fs->mimetype_from_storedfile($file); $this->assertNull($result); } /** * Test that mimetype_from_storedfile returns the correct mimetype with * a file whose filename suggests mimetype. * * @covers ::mimetype_from_storedfile * @covers :: */ public function test_mimetype_from_storedfile_using_filename() { $filepath = '/path/to/file/not/currently/on/disk'; $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); $fs->method('get_remote_path_from_storedfile')->willReturn($filepath); $file = $this->get_stored_file('example content', 'test.jpg'); $result = $fs->mimetype_from_storedfile($file); $this->assertEquals('image/jpeg', $result); } /** * Test that mimetype_from_storedfile returns the correct mimetype with * a locally available file whose filename does not suggest mimetype. * * @covers ::mimetype_from_storedfile * @covers :: */ public function test_mimetype_from_storedfile_using_file_content() { $filepath = __DIR__ . "/fixtures/testimage.jpg"; $fs = $this->get_testable_mock(['get_local_path_from_hash']); $fs->method('get_local_path_from_hash')->willReturn($filepath); $file = $this->get_stored_file('example content'); $result = $fs->mimetype_from_storedfile($file); $this->assertEquals('image/jpeg', $result); } /** * Test that mimetype_from_storedfile returns the correct mimetype with * a remotely available file whose filename does not suggest mimetype. * * @covers ::mimetype_from_storedfile * @covers :: */ public function test_mimetype_from_storedfile_using_file_content_remote() { $filepath = __DIR__ . "/fixtures/testimage.jpg"; $fs = $this->get_testable_mock([ 'is_file_readable_locally_by_hash', 'get_local_path_from_hash', ]); $fs->method('is_file_readable_locally_by_hash')->willReturn(false); $fs->method('get_local_path_from_hash')->will($this->onConsecutiveCalls('/path/to/remote/file', $filepath)); $file = $this->get_stored_file('example content'); $result = $fs->mimetype_from_storedfile($file); $this->assertEquals('image/jpeg', $result); } /** * Data Provider for is_image_from_storedfile tests. * * @return array */ public function is_image_from_storedfile_provider() { return array( 'Standard image' => array('image/png', true), 'Made up document/image' => array('document/image', false), ); } /** * Data provider for get_local_path_from_storedfile tests. * * @return array */ public function get_local_path_from_storedfile_provider() { return [ 'default args (nofetch)' => [ 'args' => [], 'fetch' => 0, ], 'explicit: nofetch' => [ 'args' => [false], 'fetch' => 0, ], 'explicit: fetch' => [ 'args' => [true], 'fetch' => 1, ], ]; } }