Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace core;use file_system_filedir;defined('MOODLE_INTERNAL') || die();global $CFG;require_once($CFG->libdir . '/filestorage/file_system.php');require_once($CFG->libdir . '/filestorage/file_system_filedir.php');/*** Unit tests for file_system_filedir.** @package core* @category test* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later* @coversDefaultClass \file_system_filedir*/class file_system_filedir_test extends \advanced_testcase {/*** Shared test setUp.*/public function setUp(): void {// Reset the file storage so that subsequent fetches to get_file_storage are called after// configuration is prepared.get_file_storage(true);}/*** Shared teset tearDown.*/public function tearDown(): void {// Reset the file storage so that subsequent tests will use the standard file storage.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($filedir = [], $trashdir = [], $sourcedir = null) {global $CFG;$this->resetAfterTest();$content = [];if ($filedir !== null) {$content['filedir'] = $filedir;}if ($trashdir !== null) {$content['trashdir'] = $trashdir;}if ($sourcedir !== null) {$content['sourcedir'] = $sourcedir;}$vfileroot = \org\bovigo\vfs\vfsStream::setup('root', null, $content);$CFG->filedir = \org\bovigo\vfs\vfsStream::url('root/filedir');$CFG->trashdir = \org\bovigo\vfs\vfsStream::url('root/trashdir');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 = []) {$contenthash = \file_storage::hash_from_string($filecontent);if (empty($filename)) {$filename = $contenthash;}$file = $this->getMockBuilder(\stored_file::class)->onlyMethods($mockedmethods)->setConstructorArgs([get_file_storage(),(object) ['contenthash' => $contenthash,'filesize' => strlen($filecontent),'filename' => $filename,]])->getMock();return $file;}/*** Get a testable mock of the file_system_filedir 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_filedir::class)->onlyMethods($mockedmethods)->getMock();return $fs;}/*** Ensure that an appropriate error is shown when the filedir directory* is not writable.** @covers ::__construct*/public function test_readonly_filesystem_filedir(): void {$this->resetAfterTest();// Setup the filedir but remove permissions.$vfileroot = $this->setup_vfile_root(null);// Make the target path readonly.$vfileroot->chmod(0444)->chown(\org\bovigo\vfs\vfsStream::OWNER_USER_2);// This should generate an exception.$this->expectException('file_exception');$this->expectExceptionMessageMatches('/Cannot create local file pool directories. Please verify permissions in dataroot./');new file_system_filedir();}/*** Ensure that an appropriate error is shown when the trash directory* is not writable.** @covers ::__construct*/public function test_readonly_filesystem_trashdir(): void {$this->resetAfterTest();// Setup the trashdir but remove permissions.$vfileroot = $this->setup_vfile_root([], null);// Make the target path readonly.$vfileroot->chmod(0444)->chown(\org\bovigo\vfs\vfsStream::OWNER_USER_2);// This should generate an exception.$this->expectException('file_exception');$this->expectExceptionMessageMatches('/Cannot create local file pool directories. Please verify permissions in dataroot./');new file_system_filedir();}/*** Test that the standard Moodle warning message is put into the filedir.** @covers ::__construct*/public function test_warnings_put_in_place(): void {$this->resetAfterTest();$vfileroot = $this->setup_vfile_root(null);new file_system_filedir();$this->assertTrue($vfileroot->hasChild('filedir/warning.txt'));$this->assertEquals('This directory contains the content of uploaded files and is controlled by Moodle code. ' .'Do not manually move, change or rename any of the files and subdirectories here.',$vfileroot->getChild('filedir/warning.txt')->getContent());}/*** Ensure that the default implementation of get_remote_path_from_hash* simply calls get_local_path_from_hash.** @covers ::get_remote_path_from_hash*/public function test_get_remote_path_from_hash(): void {$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$expectedresult = (object) [];$fs = $this->get_testable_mock(['get_local_path_from_hash',]);$fs->expects($this->once())->method('get_local_path_from_hash')->with($this->equalTo($contenthash), $this->equalTo(false))->willReturn($expectedresult);$method = new \ReflectionMethod(file_system_filedir::class, 'get_remote_path_from_hash');$result = $method->invokeArgs($fs, [$contenthash]);$this->assertEquals($expectedresult, $result);}/*** Test the stock implementation of get_local_path_from_storedfile_with_recovery with no file found and* a failed recovery.** @covers ::get_local_path_from_storedfile*/public function test_get_local_path_from_storedfile_with_recovery(): void {$filecontent = 'example content';$file = $this->get_stored_file($filecontent);$fs = $this->get_testable_mock(['get_local_path_from_hash','recover_file',]);$filepath = '/path/to/nonexistent/file';$fs->method('get_local_path_from_hash')->willReturn($filepath);$fs->expects($this->once())->method('recover_file')->with($this->equalTo($file));$file = $this->get_stored_file('example content');$result = $fs->get_local_path_from_storedfile($file, true);$this->assertEquals($filepath, $result);}/*** Test the stock implementation of get_local_path_from_storedfile_with_recovery with no file found and* a failed recovery.** @covers ::get_local_path_from_storedfile*/public function test_get_local_path_from_storedfile_without_recovery(): void {$filecontent = 'example content';$file = $this->get_stored_file($filecontent);$fs = $this->get_testable_mock(['get_local_path_from_hash','recover_file',]);$filepath = '/path/to/nonexistent/file';$fs->method('get_local_path_from_hash')->willReturn($filepath);$fs->expects($this->never())->method('recover_file');$file = $this->get_stored_file('example content');$result = $fs->get_local_path_from_storedfile($file, false);$this->assertEquals($filepath, $result);}/*** Test that the correct path is generated for the supplied content* hashes.** @dataProvider contenthash_dataprovider* @param string $hash contenthash to test* @param string $hashdir Expected format of content directory** @covers ::get_fulldir_from_hash*/public function test_get_fulldir_from_hash($hash, $hashdir): void {global $CFG;$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'get_fulldir_from_hash');$result = $method->invokeArgs($fs, array($hash));$expectedpath = sprintf('%s/filedir/%s', $CFG->dataroot, $hashdir);$this->assertEquals($expectedpath, $result);}/*** Test that the correct path is generated for the supplied content* hashes when used with a stored_file.** @dataProvider contenthash_dataprovider* @param string $hash contenthash to test* @param string $hashdir Expected format of content directory** @covers ::get_fulldir_from_storedfile*/public function test_get_fulldir_from_storedfile($hash, $hashdir): void {global $CFG;$file = $this->getMockBuilder('stored_file')->disableOriginalConstructor()->onlyMethods(['sync_external_file','get_contenthash',])->getMock();$file->method('get_contenthash')->willReturn($hash);$fs = new file_system_filedir();$method = new \ReflectionMethod('file_system_filedir', 'get_fulldir_from_storedfile');$result = $method->invokeArgs($fs, array($file));$expectedpath = sprintf('%s/filedir/%s', $CFG->dataroot, $hashdir);$this->assertEquals($expectedpath, $result);}/*** Test that the correct content directory is generated for the supplied* content hashes.** @dataProvider contenthash_dataprovider* @param string $hash contenthash to test* @param string $hashdir Expected format of content directory** @covers ::get_contentdir_from_hash*/public function test_get_contentdir_from_hash($hash, $hashdir): void {$method = new \ReflectionMethod(file_system_filedir::class, 'get_contentdir_from_hash');$fs = new file_system_filedir();$result = $method->invokeArgs($fs, array($hash));$this->assertEquals($hashdir, $result);}/*** Test that the correct content path is generated for the supplied* content hashes.** @dataProvider contenthash_dataprovider* @param string $hash contenthash to test* @param string $hashdir Expected format of content directory** @covers ::get_contentpath_from_hash*/public function test_get_contentpath_from_hash($hash, $hashdir): void {$method = new \ReflectionMethod(file_system_filedir::class, 'get_contentpath_from_hash');$fs = new file_system_filedir();$result = $method->invokeArgs($fs, array($hash));$expectedpath = sprintf('%s/%s', $hashdir, $hash);$this->assertEquals($expectedpath, $result);}/*** Test that the correct trash path is generated for the supplied* content hashes.** @dataProvider contenthash_dataprovider* @param string $hash contenthash to test* @param string $hashdir Expected format of content directory** @covers ::get_trash_fullpath_from_hash*/public function test_get_trash_fullpath_from_hash($hash, $hashdir): void {global $CFG;$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'get_trash_fullpath_from_hash');$result = $method->invokeArgs($fs, array($hash));$expectedpath = sprintf('%s/trashdir/%s/%s', $CFG->dataroot, $hashdir, $hash);$this->assertEquals($expectedpath, $result);}/*** Test that the correct trash directory is generated for the supplied* content hashes.** @dataProvider contenthash_dataprovider* @param string $hash contenthash to test* @param string $hashdir Expected format of content directory** @covers ::get_trash_fulldir_from_hash*/public function test_get_trash_fulldir_from_hash($hash, $hashdir): void {global $CFG;$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'get_trash_fulldir_from_hash');$result = $method->invokeArgs($fs, array($hash));$expectedpath = sprintf('%s/trashdir/%s', $CFG->dataroot, $hashdir);$this->assertEquals($expectedpath, $result);}/*** Ensure that copying a file to a target from a stored_file works as anticipated.** @covers ::copy_content_from_storedfile*/public function test_copy_content_from_storedfile(): void {$this->resetAfterTest();global $CFG;$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = [$contenthash => $filecontent,];$vfileroot = $this->setup_vfile_root($filedircontent, [], []);$fs = $this->getMockBuilder(file_system_filedir::class)->disableOriginalConstructor()->onlyMethods(['get_local_path_from_storedfile',])->getMock();$file = $this->getMockBuilder(\stored_file::class)->disableOriginalConstructor()->getMock();$sourcefile = \org\bovigo\vfs\vfsStream::url('root/filedir/' . $contenthash);$fs->method('get_local_path_from_storedfile')->willReturn($sourcefile);$targetfile = \org\bovigo\vfs\vfsStream::url('root/targetfile');$CFG->preventfilelocking = true;$result = $fs->copy_content_from_storedfile($file, $targetfile);$this->assertTrue($result);$this->assertEquals($filecontent, $vfileroot->getChild('targetfile')->getContent());}/*** Ensure that content recovery works.** @covers ::recover_file*/public function test_recover_file(): void {$this->resetAfterTest();// Setup the filedir.// This contains a virtual file which has a cache mismatch.$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$trashdircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root([], $trashdircontent);$file = new \stored_file(get_file_storage(), (object) ['contenthash' => $contenthash,'filesize' => strlen($filecontent),]);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'recover_file');$result = $method->invokeArgs($fs, array($file));// Test the output.$this->assertTrue($result);$this->assertEquals($filecontent, $vfileroot->getChild('filedir/0f/f3/' . $contenthash)->getContent());}/*** Ensure that content recovery works.** @covers ::recover_file*/public function test_recover_file_already_present(): void {$this->resetAfterTest();// Setup the filedir.// This contains a virtual file which has a cache mismatch.$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = $trashdircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root($filedircontent, $trashdircontent);$file = new \stored_file(get_file_storage(), (object) ['contenthash' => $contenthash,'filesize' => strlen($filecontent),]);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'recover_file');$result = $method->invokeArgs($fs, array($file));// Test the output.$this->assertTrue($result);$this->assertEquals($filecontent, $vfileroot->getChild('filedir/0f/f3/' . $contenthash)->getContent());}/*** Ensure that content recovery works.** @covers ::recover_file*/public function test_recover_file_size_mismatch(): void {$this->resetAfterTest();// Setup the filedir.// This contains a virtual file which has a cache mismatch.$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$trashdircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root([], $trashdircontent);$file = new \stored_file(get_file_storage(), (object) ['contenthash' => $contenthash,'filesize' => strlen($filecontent) + 1,]);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'recover_file');$result = $method->invokeArgs($fs, array($file));// Test the output.$this->assertFalse($result);$this->assertFalse($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));}/*** Ensure that content recovery works.** @covers ::recover_file*/public function test_recover_file_has_mismatch(): void {$this->resetAfterTest();// Setup the filedir.// This contains a virtual file which has a cache mismatch.$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$trashdircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root([], $trashdircontent);$file = new \stored_file(get_file_storage(), (object) ['contenthash' => $contenthash . " different",'filesize' => strlen($filecontent),]);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'recover_file');$result = $method->invokeArgs($fs, array($file));// Test the output.$this->assertFalse($result);$this->assertFalse($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));}/*** Ensure that content recovery works when the content file is in the* alt trash directory.** @covers ::recover_file*/public function test_recover_file_alttrash(): void {$this->resetAfterTest();// Setup the filedir.// This contains a virtual file which has a cache mismatch.$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$trashdircontent = [$contenthash => $filecontent,];$vfileroot = $this->setup_vfile_root([], $trashdircontent);$file = new \stored_file(get_file_storage(), (object) ['contenthash' => $contenthash,'filesize' => strlen($filecontent),]);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'recover_file');$result = $method->invokeArgs($fs, array($file));// Test the output.$this->assertTrue($result);$this->assertEquals($filecontent, $vfileroot->getChild('filedir/0f/f3/' . $contenthash)->getContent());}/*** Test that an appropriate error message is generated when adding a* file to the pool when the pool directory structure is not writable.** @covers ::recover_file*/public function test_recover_file_contentdir_readonly(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => [],];$trashdircontent = [$contenthash => $filecontent,];$vfileroot = $this->setup_vfile_root($filedircontent, $trashdircontent);// Make the target path readonly.$vfileroot->getChild('filedir/0f')->chmod(0444)->chown(\org\bovigo\vfs\vfsStream::OWNER_USER_2);$file = new \stored_file(get_file_storage(), (object) ['contenthash' => $contenthash,'filesize' => strlen($filecontent),]);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'recover_file');$result = $method->invokeArgs($fs, array($file));// Test the output.$this->assertFalse($result);}/*** Test adding a file to the pool.** @covers ::add_file_from_path*/public function test_add_file_from_path(): void {$this->resetAfterTest();global $CFG;// Setup the filedir.// This contains a virtual file which has a cache mismatch.$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$sourcedircontent = ['file' => $filecontent,];$vfileroot = $this->setup_vfile_root([], [], $sourcedircontent);// Note, the vfs file system does not support locks - prevent file locking here.$CFG->preventfilelocking = true;// Attempt to add the file to the file pool.$fs = new file_system_filedir();$sourcefile = \org\bovigo\vfs\vfsStream::url('root/sourcedir/file');$result = $fs->add_file_from_path($sourcefile);// Test the output.$this->assertEquals($contenthash, $result[0]);$this->assertEquals(\core_text::strlen($filecontent), $result[1]);$this->assertTrue($result[2]);$this->assertEquals($filecontent, $vfileroot->getChild('filedir/0f/f3/' . $contenthash)->getContent());}/*** Test that an appropriate error message is generated when adding an* unavailable file to the pool is attempted.** @covers ::add_file_from_path*/public function test_add_file_from_path_file_unavailable(): void {$this->resetAfterTest();// Setup the filedir.$vfileroot = $this->setup_vfile_root();$this->expectException('file_exception');$this->expectExceptionMessageMatches('/Cannot read file\. Either the file does not exist or there is a permission problem\./');$fs = new file_system_filedir();$fs->add_file_from_path(\org\bovigo\vfs\vfsStream::url('filedir/file'));}/*** Test that an appropriate error message is generated when specifying* the wrong contenthash when adding a file to the pool.** @covers ::add_file_from_path*/public function test_add_file_from_path_mismatched_hash(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$sourcedir = ['file' => $filecontent,];$vfileroot = $this->setup_vfile_root([], [], $sourcedir);$fs = new file_system_filedir();$filepath = \org\bovigo\vfs\vfsStream::url('root/sourcedir/file');$fs->add_file_from_path($filepath, 'eee4943847a35a4b6942c6f96daafde06bcfdfab');$this->assertDebuggingCalled("Invalid contenthash submitted for file $filepath");}/*** Test that an appropriate error message is generated when an existing* file in the pool has the wrong contenthash** @covers ::add_file_from_path*/public function test_add_file_from_path_existing_content_invalid(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => ['f3' => [// This contains a virtual file which has a cache mismatch.'0ff30941ca5acd879fd809e8c937d9f9e6dd1615' => 'different example content',],],];$sourcedir = ['file' => $filecontent,];$vfileroot = $this->setup_vfile_root($filedircontent, [], $sourcedir);// Check that we hit the jackpot.$fs = new file_system_filedir();$filepath = \org\bovigo\vfs\vfsStream::url('root/sourcedir/file');$result = $fs->add_file_from_path($filepath);// We provided a bad hash. Check that the file was replaced.$this->assertDebuggingCalled("Replacing invalid content file $contenthash");// Test the output.$this->assertEquals($contenthash, $result[0]);$this->assertEquals(\core_text::strlen($filecontent), $result[1]);$this->assertFalse($result[2]);// Fetch the new file structure.$structure = \org\bovigo\vfs\vfsStream::inspect(new \org\bovigo\vfs\visitor\vfsStreamStructureVisitor())->getStructure();$this->assertEquals($filecontent, $structure['root']['filedir']['0f']['f3'][$contenthash]);}/*** Test that an appropriate error message is generated when adding a* file to the pool when the pool directory structure is not writable.** @covers ::add_file_from_path*/public function test_add_file_from_path_existing_cannot_write_hashpath(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => [],];$sourcedir = ['file' => $filecontent,];$vfileroot = $this->setup_vfile_root($filedircontent, [], $sourcedir);// Make the target path readonly.$vfileroot->getChild('filedir/0f')->chmod(0444)->chown(\org\bovigo\vfs\vfsStream::OWNER_USER_2);$this->expectException('file_exception');$this->expectExceptionMessageMatches("/Cannot create local file pool directories. Please verify permissions in dataroot./");// Attempt to add the file to the file pool.$fs = new file_system_filedir();$sourcefile = \org\bovigo\vfs\vfsStream::url('root/sourcedir/file');$fs->add_file_from_path($sourcefile);}/*** Test adding a string to the pool.** @covers ::add_file_from_string*/public function test_add_file_from_string(): void {$this->resetAfterTest();global $CFG;$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$vfileroot = $this->setup_vfile_root();// Note, the vfs file system does not support locks - prevent file locking here.$CFG->preventfilelocking = true;// Attempt to add the file to the file pool.$fs = new file_system_filedir();$result = $fs->add_file_from_string($filecontent);// Test the output.$this->assertEquals($contenthash, $result[0]);$this->assertEquals(\core_text::strlen($filecontent), $result[1]);$this->assertTrue($result[2]);}/*** Test that an appropriate error message is generated when adding a* string to the pool when the pool directory structure is not writable.** @covers ::add_file_from_string*/public function test_add_file_from_string_existing_cannot_write_hashpath(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => [],];$vfileroot = $this->setup_vfile_root($filedircontent);// Make the target path readonly.$vfileroot->getChild('filedir/0f')->chmod(0444)->chown(\org\bovigo\vfs\vfsStream::OWNER_USER_2);$this->expectException('file_exception');$this->expectExceptionMessageMatches("/Cannot create local file pool directories. Please verify permissions in dataroot./");// Attempt to add the file to the file pool.$fs = new file_system_filedir();$fs->add_file_from_string($filecontent);}/*** Test adding a string to the pool when an item with the same* contenthash is already present.** @covers ::add_file_from_string*/public function test_add_file_from_string_existing_matches(): void {$this->resetAfterTest();global $CFG;$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root($filedircontent);// Note, the vfs file system does not support locks - prevent file locking here.$CFG->preventfilelocking = true;// Attempt to add the file to the file pool.$fs = new file_system_filedir();$result = $fs->add_file_from_string($filecontent);// Test the output.$this->assertEquals($contenthash, $result[0]);$this->assertEquals(\core_text::strlen($filecontent), $result[1]);$this->assertFalse($result[2]);}/*** Test the cleanup of deleted files when there are no files to delete.** @covers ::remove_file*/public function test_remove_file_missing(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$vfileroot = $this->setup_vfile_root();$fs = new file_system_filedir();$fs->remove_file($contenthash);$this->assertFalse($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));// No file to move to trash, so the trash path will also be empty.$this->assertFalse($vfileroot->hasChild('trashdir/0f'));$this->assertFalse($vfileroot->hasChild('trashdir/0f/f3'));$this->assertFalse($vfileroot->hasChild('trashdir/0f/f3/' . $contenthash));}/*** Test the cleanup of deleted files when a file already exists in the* trash for that path.** @covers ::remove_file*/public function test_remove_file_existing_trash(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = $trashdircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$trashdircontent['0f']['f3'][$contenthash] .= 'different';$vfileroot = $this->setup_vfile_root($filedircontent, $trashdircontent);$fs = new file_system_filedir();$fs->remove_file($contenthash);$this->assertFalse($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));$this->assertTrue($vfileroot->hasChild('trashdir/0f/f3/' . $contenthash));$this->assertNotEquals($filecontent, $vfileroot->getChild('trashdir/0f/f3/' . $contenthash)->getContent());}/*** Ensure that remove_file does nothing with an empty file.** @covers ::remove_file*/public function test_remove_file_empty(): void {$this->resetAfterTest();global $DB;$DB = $this->getMockBuilder(\moodle_database::class)->onlyMethods(['record_exists'])->getMockForAbstractClass();$DB->expects($this->never())->method('record_exists');$fs = new file_system_filedir();$result = $fs->remove_file(\file_storage::hash_from_string(''));$this->assertNull($result);}/*** Ensure that remove_file does nothing when a file is still* in use.** @covers ::remove_file*/public function test_remove_file_in_use(): void {$this->resetAfterTest();global $DB;$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root($filedircontent);$DB = $this->getMockBuilder(\moodle_database::class)->onlyMethods(['record_exists'])->getMockForAbstractClass();$DB->method('record_exists')->willReturn(true);$fs = new file_system_filedir();$result = $fs->remove_file($contenthash);$this->assertTrue($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));$this->assertFalse($vfileroot->hasChild('trashdir/0f/f3/' . $contenthash));}/*** Ensure that remove_file removes the file when it is no* longer in use.** @covers ::remove_file*/public function test_remove_file_expired(): void {$this->resetAfterTest();global $DB;$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root($filedircontent);$DB = $this->getMockBuilder(\moodle_database::class)->onlyMethods(['record_exists'])->getMockForAbstractClass();$DB->method('record_exists')->willReturn(false);$fs = new file_system_filedir();$result = $fs->remove_file($contenthash);$this->assertFalse($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));$this->assertTrue($vfileroot->hasChild('trashdir/0f/f3/' . $contenthash));}/*** Test purging the cache.** @covers ::empty_trash*/public function test_empty_trash(): void {$this->resetAfterTest();$filecontent = 'example content';$contenthash = \file_storage::hash_from_string($filecontent);$filedircontent = $trashdircontent = ['0f' => ['f3' => [$contenthash => $filecontent,],],];$vfileroot = $this->setup_vfile_root($filedircontent, $trashdircontent);$fs = new file_system_filedir();$method = new \ReflectionMethod(file_system_filedir::class, 'empty_trash');$result = $method->invoke($fs);$this->assertTrue($vfileroot->hasChild('filedir/0f/f3/' . $contenthash));$this->assertFalse($vfileroot->hasChild('trashdir'));$this->assertFalse($vfileroot->hasChild('trashdir/0f'));$this->assertFalse($vfileroot->hasChild('trashdir/0f/f3'));$this->assertFalse($vfileroot->hasChild('trashdir/0f/f3/' . $contenthash));}/*** Data Provider for contenthash to contendir conversion.** @return array*/public function contenthash_dataprovider() {return array(array('contenthash' => 'eee4943847a35a4b6942c6f96daafde06bcfdfab','contentdir' => 'ee/e4',),array('contenthash' => 'aef05a62ae81ca0005d2569447779af062b7cda0','contentdir' => 'ae/f0',),);}}