Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core; 18 19 use file_archive; 20 use file_packer; 21 use file_system; 22 use file_system_filedir; 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 global $CFG; 27 require_once($CFG->libdir . '/filestorage/file_system.php'); 28 29 /** 30 * Unit tests for file_system. 31 * 32 * @package core 33 * @category test 34 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk> 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 * @coversDefaultClass \file_system 37 */ 38 class file_system_test extends \advanced_testcase { 39 40 public function setUp(): void { 41 get_file_storage(true); 42 } 43 44 public function tearDown(): void { 45 get_file_storage(true); 46 } 47 48 /** 49 * Helper function to help setup and configure the virtual file system stream. 50 * 51 * @param array $filedir Directory structure and content of the filedir 52 * @param array $trashdir Directory structure and content of the sourcedir 53 * @param array $sourcedir Directory structure and content of a directory used for source files for tests 54 * @return \org\bovigo\vfs\vfsStream 55 */ 56 protected function setup_vfile_root($content = []) { 57 $vfileroot = \org\bovigo\vfs\vfsStream::setup('root', null, $content); 58 59 return $vfileroot; 60 } 61 62 /** 63 * Helper to create a stored file objectw with the given supplied content. 64 * 65 * @param string $filecontent The content of the mocked file 66 * @param string $filename The file name to use in the stored_file 67 * @param array $mockedmethods A list of methods you intend to override 68 * If no methods are specified, only abstract functions are mocked. 69 * @return \stored_file 70 */ 71 protected function get_stored_file($filecontent, $filename = null, $mockedmethods = []) { 72 $contenthash = \file_storage::hash_from_string($filecontent); 73 if (empty($filename)) { 74 $filename = $contenthash; 75 } 76 77 $file = $this->getMockBuilder(\stored_file::class) 78 ->onlyMethods($mockedmethods) 79 ->setConstructorArgs([ 80 get_file_storage(), 81 (object) [ 82 'contenthash' => $contenthash, 83 'filesize' => strlen($filecontent), 84 'filename' => $filename, 85 ] 86 ]) 87 ->getMock(); 88 89 return $file; 90 } 91 92 /** 93 * Get a testable mock of the abstract file_system class. 94 * 95 * @param array $mockedmethods A list of methods you intend to override 96 * If no methods are specified, only abstract functions are mocked. 97 * @return file_system 98 */ 99 protected function get_testable_mock($mockedmethods = []) { 100 $fs = $this->getMockBuilder(file_system::class) 101 ->onlyMethods($mockedmethods) 102 ->getMockForAbstractClass(); 103 104 return $fs; 105 } 106 107 /** 108 * Ensure that the file system is not clonable. 109 * 110 */ 111 public function test_not_cloneable() { 112 $reflection = new \ReflectionClass('file_system'); 113 $this->assertFalse($reflection->isCloneable()); 114 } 115 116 /** 117 * Ensure that the filedir file_system extension is used by default. 118 * 119 */ 120 public function test_default_class() { 121 $this->resetAfterTest(); 122 123 // Ensure that the alternative_file_system_class is null. 124 global $CFG; 125 $CFG->alternative_file_system_class = null; 126 127 $storage = get_file_storage(); 128 $fs = $storage->get_file_system(); 129 $this->assertInstanceOf(file_system::class, $fs); 130 $this->assertEquals(file_system_filedir::class, get_class($fs)); 131 } 132 133 /** 134 * Ensure that the specified file_system extension class is used. 135 * 136 */ 137 public function test_supplied_class() { 138 global $CFG; 139 $this->resetAfterTest(); 140 141 // Mock the file_system. 142 // Mocks create a new child of the mocked class which is perfect for this test. 143 $filesystem = $this->getMockBuilder('file_system') 144 ->disableOriginalConstructor() 145 ->getMock(); 146 $CFG->alternative_file_system_class = get_class($filesystem); 147 148 $storage = get_file_storage(); 149 $fs = $storage->get_file_system(); 150 $this->assertInstanceOf(file_system::class, $fs); 151 $this->assertEquals(get_class($filesystem), get_class($fs)); 152 } 153 154 /** 155 * Test that the readfile function outputs content to disk. 156 * 157 * @covers ::readfile 158 */ 159 public function test_readfile_remote() { 160 global $CFG; 161 162 // Mock the filesystem. 163 $filecontent = 'example content'; 164 $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]); 165 $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile'); 166 167 $file = $this->get_stored_file($filecontent); 168 169 // Mock the file_system class. 170 // We need to override the get_remote_path_from_storedfile function. 171 $fs = $this->get_testable_mock([ 172 'get_remote_path_from_storedfile', 173 'is_file_readable_locally_by_storedfile', 174 'get_local_path_from_storedfile', 175 ]); 176 $fs->method('get_remote_path_from_storedfile')->willReturn($filepath); 177 $fs->method('is_file_readable_locally_by_storedfile')->willReturn(false); 178 $fs->expects($this->never())->method('get_local_path_from_storedfile'); 179 180 // Note: It is currently not possible to mock readfile_allow_large 181 // because file_system is in the global namespace. 182 // We must therefore check for expected output. This is not ideal. 183 $this->expectOutputString($filecontent); 184 $fs->readfile($file); 185 } 186 187 /** 188 * Test that the readfile function outputs content to disk. 189 * 190 * @covers ::readfile 191 */ 192 public function test_readfile_local() { 193 global $CFG; 194 195 // Mock the filesystem. 196 $filecontent = 'example content'; 197 $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]); 198 $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile'); 199 200 $file = $this->get_stored_file($filecontent); 201 202 // Mock the file_system class. 203 // We need to override the get_remote_path_from_storedfile function. 204 $fs = $this->get_testable_mock([ 205 'get_remote_path_from_storedfile', 206 'is_file_readable_locally_by_storedfile', 207 'get_local_path_from_storedfile', 208 ]); 209 $fs->method('is_file_readable_locally_by_storedfile')->willReturn(true); 210 $fs->expects($this->never())->method('get_remote_path_from_storedfile'); 211 $fs->expects($this->once())->method('get_local_path_from_storedfile')->willReturn($filepath); 212 213 // Note: It is currently not possible to mock readfile_allow_large 214 // because file_system is in the global namespace. 215 // We must therefore check for expected output. This is not ideal. 216 $this->expectOutputString($filecontent); 217 $fs->readfile($file); 218 } 219 220 /** 221 * Test that the get_local_path_from_storedfile function functions 222 * correctly when called with various args. 223 * 224 * @dataProvider get_local_path_from_storedfile_provider 225 * @param array $args The additional args to pass to get_local_path_from_storedfile 226 * @param bool $fetch Whether the combination of args should have caused a fetch 227 * 228 * @covers ::get_local_path_from_storedfile 229 */ 230 public function test_get_local_path_from_storedfile($args, $fetch) { 231 $filepath = '/path/to/file'; 232 $filecontent = 'example content'; 233 234 // Get the filesystem mock. 235 $fs = $this->get_testable_mock([ 236 'get_local_path_from_hash', 237 ]); 238 $fs->expects($this->once()) 239 ->method('get_local_path_from_hash') 240 ->with($this->equalTo(\file_storage::hash_from_string($filecontent)), $this->equalTo($fetch)) 241 ->willReturn($filepath); 242 243 $file = $this->get_stored_file($filecontent); 244 245 $result = $fs->get_local_path_from_storedfile($file, $fetch); 246 247 $this->assertEquals($filepath, $result); 248 } 249 250 /** 251 * Ensure that the default implementation of get_remote_path_from_storedfile 252 * simply calls get_local_path_from_storedfile without requiring a 253 * fetch. 254 * 255 * @covers ::get_remote_path_from_storedfile 256 */ 257 public function test_get_remote_path_from_storedfile() { 258 $filepath = '/path/to/file'; 259 $filecontent = 'example content'; 260 261 $fs = $this->get_testable_mock([ 262 'get_remote_path_from_hash', 263 ]); 264 265 $fs->expects($this->once()) 266 ->method('get_remote_path_from_hash') 267 ->with($this->equalTo(\file_storage::hash_from_string($filecontent)), $this->equalTo(false)) 268 ->willReturn($filepath); 269 270 $file = $this->get_stored_file($filecontent); 271 272 $result = $fs->get_remote_path_from_storedfile($file); 273 274 $this->assertEquals($filepath, $result); 275 } 276 277 /** 278 * Test the stock implementation of is_file_readable_locally_by_hash with a valid file. 279 * 280 * This should call get_local_path_from_hash and check the readability 281 * of the file. 282 * 283 * Fetching the file is optional. 284 * 285 * @covers ::is_file_readable_locally_by_hash 286 */ 287 public function test_is_file_readable_locally_by_hash() { 288 $filecontent = 'example content'; 289 $contenthash = \file_storage::hash_from_string($filecontent); 290 $filepath = __FILE__; 291 292 $fs = $this->get_testable_mock([ 293 'get_local_path_from_hash', 294 ]); 295 296 $fs->method('get_local_path_from_hash') 297 ->with($this->equalTo($contenthash), $this->equalTo(false)) 298 ->willReturn($filepath); 299 300 $this->assertTrue($fs->is_file_readable_locally_by_hash($contenthash)); 301 } 302 303 /** 304 * Test the stock implementation of is_file_readable_locally_by_hash with an empty file. 305 * 306 * @covers ::is_file_readable_locally_by_hash 307 */ 308 public function test_is_file_readable_locally_by_hash_empty() { 309 $filecontent = ''; 310 $contenthash = \file_storage::hash_from_string($filecontent); 311 312 $fs = $this->get_testable_mock([ 313 'get_local_path_from_hash', 314 ]); 315 316 $fs->expects($this->never()) 317 ->method('get_local_path_from_hash'); 318 319 $this->assertTrue($fs->is_file_readable_locally_by_hash($contenthash)); 320 } 321 322 /** 323 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. 324 * 325 * @covers ::is_file_readable_remotely_by_hash 326 */ 327 public function test_is_file_readable_remotely_by_hash() { 328 $filecontent = 'example content'; 329 $contenthash = \file_storage::hash_from_string($filecontent); 330 331 $fs = $this->get_testable_mock([ 332 'get_remote_path_from_hash', 333 ]); 334 335 $fs->method('get_remote_path_from_hash') 336 ->with($this->equalTo($contenthash), $this->equalTo(false)) 337 ->willReturn(__FILE__); 338 339 $this->assertTrue($fs->is_file_readable_remotely_by_hash($contenthash)); 340 } 341 342 /** 343 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. 344 * 345 * @covers ::is_file_readable_remotely_by_hash 346 */ 347 public function test_is_file_readable_remotely_by_hash_empty() { 348 $filecontent = ''; 349 $contenthash = \file_storage::hash_from_string($filecontent); 350 351 $fs = $this->get_testable_mock([ 352 'get_remote_path_from_hash', 353 ]); 354 355 $fs->expects($this->never()) 356 ->method('get_remote_path_from_hash'); 357 358 $this->assertTrue($fs->is_file_readable_remotely_by_hash($contenthash)); 359 } 360 361 /** 362 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. 363 * 364 * @covers ::is_file_readable_remotely_by_hash 365 */ 366 public function test_is_file_readable_remotely_by_hash_not_found() { 367 $filecontent = 'example content'; 368 $contenthash = \file_storage::hash_from_string($filecontent); 369 370 $fs = $this->get_testable_mock([ 371 'get_remote_path_from_hash', 372 ]); 373 374 $fs->method('get_remote_path_from_hash') 375 ->with($this->equalTo($contenthash), $this->equalTo(false)) 376 ->willReturn('/path/to/nonexistent/file'); 377 378 $this->assertFalse($fs->is_file_readable_remotely_by_hash($contenthash)); 379 } 380 381 /** 382 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. 383 * 384 * @covers ::is_file_readable_remotely_by_storedfile 385 */ 386 public function test_is_file_readable_remotely_by_storedfile() { 387 $file = $this->get_stored_file('example content'); 388 389 $fs = $this->get_testable_mock([ 390 'get_remote_path_from_storedfile', 391 ]); 392 393 $fs->method('get_remote_path_from_storedfile') 394 ->willReturn(__FILE__); 395 396 $this->assertTrue($fs->is_file_readable_remotely_by_storedfile($file)); 397 } 398 399 /** 400 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. 401 * 402 * @covers ::is_file_readable_remotely_by_storedfile 403 */ 404 public function test_is_file_readable_remotely_by_storedfile_empty() { 405 $fs = $this->get_testable_mock([ 406 'get_remote_path_from_storedfile', 407 ]); 408 409 $fs->expects($this->never()) 410 ->method('get_remote_path_from_storedfile'); 411 412 $file = $this->get_stored_file(''); 413 $this->assertTrue($fs->is_file_readable_remotely_by_storedfile($file)); 414 } 415 416 /** 417 * Test the stock implementation of is_file_readable_locally_by_storedfile with an empty file. 418 * 419 * @covers ::is_file_readable_locally_by_storedfile 420 */ 421 public function test_is_file_readable_locally_by_storedfile_empty() { 422 $fs = $this->get_testable_mock([ 423 'get_local_path_from_storedfile', 424 ]); 425 426 $fs->expects($this->never()) 427 ->method('get_local_path_from_storedfile'); 428 429 $file = $this->get_stored_file(''); 430 $this->assertTrue($fs->is_file_readable_locally_by_storedfile($file)); 431 } 432 433 /** 434 * Test the stock implementation of is_file_readable_remotely_by_storedfile with a valid file. 435 * 436 * @covers ::is_file_readable_locally_by_storedfile 437 */ 438 public function test_is_file_readable_remotely_by_storedfile_not_found() { 439 $file = $this->get_stored_file('example content'); 440 441 $fs = $this->get_testable_mock([ 442 'get_remote_path_from_storedfile', 443 ]); 444 445 $fs->method('get_remote_path_from_storedfile') 446 ->willReturn(__LINE__); 447 448 $this->assertFalse($fs->is_file_readable_remotely_by_storedfile($file)); 449 } 450 451 /** 452 * Test the stock implementation of is_file_readable_locally_by_storedfile with a valid file. 453 * 454 * @covers ::is_file_readable_locally_by_storedfile 455 */ 456 public function test_is_file_readable_locally_by_storedfile_unreadable() { 457 $fs = $this->get_testable_mock([ 458 'get_local_path_from_storedfile', 459 ]); 460 $file = $this->get_stored_file('example content'); 461 462 $fs->method('get_local_path_from_storedfile') 463 ->with($this->equalTo($file), $this->equalTo(false)) 464 ->willReturn('/path/to/nonexistent/file'); 465 466 $this->assertFalse($fs->is_file_readable_locally_by_storedfile($file)); 467 } 468 469 /** 470 * Test the stock implementation of is_file_readable_locally_by_storedfile with a valid file should pass fetch. 471 * 472 * @covers ::is_file_readable_locally_by_storedfile 473 */ 474 public function test_is_file_readable_locally_by_storedfile_passes_fetch() { 475 $fs = $this->get_testable_mock([ 476 'get_local_path_from_storedfile', 477 ]); 478 $file = $this->get_stored_file('example content'); 479 480 $fs->method('get_local_path_from_storedfile') 481 ->with($this->equalTo($file), $this->equalTo(true)) 482 ->willReturn('/path/to/nonexistent/file'); 483 484 $this->assertFalse($fs->is_file_readable_locally_by_storedfile($file, true)); 485 } 486 487 /** 488 * Ensure that is_file_removable returns correctly for an empty file. 489 * 490 * @covers ::is_file_removable 491 */ 492 public function test_is_file_removable_empty() { 493 $filecontent = ''; 494 $contenthash = \file_storage::hash_from_string($filecontent); 495 496 $method = new \ReflectionMethod(file_system::class, 'is_file_removable'); 497 $method->setAccessible(true); 498 $result = $method->invokeArgs(null, [$contenthash]); 499 $this->assertFalse($result); 500 } 501 502 /** 503 * Ensure that is_file_removable returns false if the file is still in use. 504 * 505 * @covers ::is_file_removable 506 */ 507 public function test_is_file_removable_in_use() { 508 $this->resetAfterTest(); 509 global $DB; 510 511 $filecontent = 'example content'; 512 $contenthash = \file_storage::hash_from_string($filecontent); 513 514 $DB = $this->getMockBuilder(\moodle_database::class) 515 ->onlyMethods(['record_exists']) 516 ->getMockForAbstractClass(); 517 $DB->method('record_exists')->willReturn(true); 518 519 $method = new \ReflectionMethod(file_system::class, 'is_file_removable'); 520 $method->setAccessible(true); 521 $result = $method->invokeArgs(null, [$contenthash]); 522 523 $this->assertFalse($result); 524 } 525 526 /** 527 * Ensure that is_file_removable returns false if the file is not in use. 528 * 529 * @covers ::is_file_removable 530 */ 531 public function test_is_file_removable_not_in_use() { 532 $this->resetAfterTest(); 533 global $DB; 534 535 $filecontent = 'example content'; 536 $contenthash = \file_storage::hash_from_string($filecontent); 537 538 $DB = $this->getMockBuilder(\moodle_database::class) 539 ->onlyMethods(['record_exists']) 540 ->getMockForAbstractClass(); 541 $DB->method('record_exists')->willReturn(false); 542 543 $method = new \ReflectionMethod(file_system::class, 'is_file_removable'); 544 $method->setAccessible(true); 545 $result = $method->invokeArgs(null, [$contenthash]); 546 547 $this->assertTrue($result); 548 } 549 550 /** 551 * Test the stock implementation of get_content. 552 * 553 * @covers ::get_content 554 */ 555 public function test_get_content() { 556 global $CFG; 557 558 // Mock the filesystem. 559 $filecontent = 'example content'; 560 $vfileroot = $this->setup_vfile_root(['sourcefile' => $filecontent]); 561 $filepath = \org\bovigo\vfs\vfsStream::url('root/sourcefile'); 562 563 $file = $this->get_stored_file($filecontent); 564 565 // Mock the file_system class. 566 // We need to override the get_remote_path_from_storedfile function. 567 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); 568 $fs->method('get_remote_path_from_storedfile')->willReturn($filepath); 569 570 $result = $fs->get_content($file); 571 572 $this->assertEquals($filecontent, $result); 573 } 574 575 /** 576 * Test the stock implementation of get_content. 577 * 578 * @covers ::get_content 579 */ 580 public function test_get_content_empty() { 581 global $CFG; 582 583 $filecontent = ''; 584 $file = $this->get_stored_file($filecontent); 585 586 // Mock the file_system class. 587 // We need to override the get_remote_path_from_storedfile function. 588 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); 589 $fs->expects($this->never()) 590 ->method('get_remote_path_from_storedfile'); 591 592 $result = $fs->get_content($file); 593 594 $this->assertEquals($filecontent, $result); 595 } 596 597 /** 598 * Ensure that the list_files function requires a local copy of the 599 * file, and passes the path to the packer. 600 * 601 * @covers ::list_files 602 */ 603 public function test_list_files() { 604 $filecontent = 'example content'; 605 $file = $this->get_stored_file($filecontent); 606 $filepath = __FILE__; 607 $expectedresult = (object) []; 608 609 // Mock the file_system class. 610 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 611 $fs->method('get_local_path_from_storedfile') 612 ->with($this->equalTo($file), $this->equalTo(true)) 613 ->willReturn(__FILE__); 614 615 $packer = $this->getMockBuilder(file_packer::class) 616 ->onlyMethods(['list_files']) 617 ->getMockForAbstractClass(); 618 619 $packer->expects($this->once()) 620 ->method('list_files') 621 ->with($this->equalTo($filepath)) 622 ->willReturn($expectedresult); 623 624 $result = $fs->list_files($file, $packer); 625 626 $this->assertEquals($expectedresult, $result); 627 } 628 629 /** 630 * Ensure that the extract_to_pathname function requires a local copy of the 631 * file, and passes the path to the packer. 632 * 633 * @covers ::extract_to_pathname 634 */ 635 public function test_extract_to_pathname() { 636 $filecontent = 'example content'; 637 $file = $this->get_stored_file($filecontent); 638 $filepath = __FILE__; 639 $expectedresult = (object) []; 640 $outputpath = '/path/to/output'; 641 642 // Mock the file_system class. 643 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 644 $fs->method('get_local_path_from_storedfile') 645 ->with($this->equalTo($file), $this->equalTo(true)) 646 ->willReturn(__FILE__); 647 648 $packer = $this->getMockBuilder(file_packer::class) 649 ->onlyMethods(['extract_to_pathname']) 650 ->getMockForAbstractClass(); 651 652 $packer->expects($this->once()) 653 ->method('extract_to_pathname') 654 ->with($this->equalTo($filepath), $this->equalTo($outputpath), $this->equalTo(null), $this->equalTo(null)) 655 ->willReturn($expectedresult); 656 657 $result = $fs->extract_to_pathname($file, $packer, $outputpath); 658 659 $this->assertEquals($expectedresult, $result); 660 } 661 662 /** 663 * Ensure that the extract_to_storage function requires a local copy of the 664 * file, and passes the path to the packer. 665 * 666 * @covers ::extract_to_storage 667 */ 668 public function test_extract_to_storage() { 669 $filecontent = 'example content'; 670 $file = $this->get_stored_file($filecontent); 671 $filepath = __FILE__; 672 $expectedresult = (object) []; 673 $outputpath = '/path/to/output'; 674 675 // Mock the file_system class. 676 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 677 $fs->method('get_local_path_from_storedfile') 678 ->with($this->equalTo($file), $this->equalTo(true)) 679 ->willReturn(__FILE__); 680 681 $packer = $this->getMockBuilder(file_packer::class) 682 ->onlyMethods(['extract_to_storage']) 683 ->getMockForAbstractClass(); 684 685 $packer->expects($this->once()) 686 ->method('extract_to_storage') 687 ->with( 688 $this->equalTo($filepath), 689 $this->equalTo(42), 690 $this->equalTo('component'), 691 $this->equalTo('filearea'), 692 $this->equalTo('itemid'), 693 $this->equalTo('pathbase'), 694 $this->equalTo('userid'), 695 $this->equalTo(null) 696 ) 697 ->willReturn($expectedresult); 698 699 $result = $fs->extract_to_storage($file, $packer, 42, 'component','filearea', 'itemid', 'pathbase', 'userid'); 700 701 $this->assertEquals($expectedresult, $result); 702 } 703 704 /** 705 * Ensure that the add_storedfile_to_archive function requires a local copy of the 706 * file, and passes the path to the archive. 707 * 708 */ 709 public function test_add_storedfile_to_archive_directory() { 710 $file = $this->get_stored_file('', '.'); 711 $archivepath = 'example'; 712 $expectedresult = (object) []; 713 714 // Mock the file_system class. 715 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 716 $fs->method('get_local_path_from_storedfile') 717 ->with($this->equalTo($file), $this->equalTo(true)) 718 ->willReturn(__FILE__); 719 720 $archive = $this->getMockBuilder(file_archive::class) 721 ->onlyMethods([ 722 'add_directory', 723 'add_file_from_pathname', 724 ]) 725 ->getMockForAbstractClass(); 726 727 $archive->expects($this->once()) 728 ->method('add_directory') 729 ->with($this->equalTo($archivepath)) 730 ->willReturn($expectedresult); 731 732 $archive->expects($this->never()) 733 ->method('add_file_from_pathname'); 734 735 $result = $fs->add_storedfile_to_archive($file, $archive, $archivepath); 736 737 $this->assertEquals($expectedresult, $result); 738 } 739 740 /** 741 * Ensure that the add_storedfile_to_archive function requires a local copy of the 742 * file, and passes the path to the archive. 743 * 744 */ 745 public function test_add_storedfile_to_archive_file() { 746 $file = $this->get_stored_file('example content'); 747 $filepath = __LINE__; 748 $archivepath = 'example'; 749 $expectedresult = (object) []; 750 751 // Mock the file_system class. 752 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 753 $fs->method('get_local_path_from_storedfile') 754 ->with($this->equalTo($file), $this->equalTo(true)) 755 ->willReturn($filepath); 756 757 $archive = $this->getMockBuilder(file_archive::class) 758 ->onlyMethods([ 759 'add_directory', 760 'add_file_from_pathname', 761 ]) 762 ->getMockForAbstractClass(); 763 764 $archive->expects($this->never()) 765 ->method('add_directory'); 766 767 $archive->expects($this->once()) 768 ->method('add_file_from_pathname') 769 ->with( 770 $this->equalTo($archivepath), 771 $this->equalTo($filepath) 772 ) 773 ->willReturn($expectedresult); 774 775 $result = $fs->add_storedfile_to_archive($file, $archive, $archivepath); 776 777 $this->assertEquals($expectedresult, $result); 778 } 779 780 /** 781 * Ensure that the add_to_curl_request function requires a local copy of the 782 * file, and passes the path to curl_file_create. 783 * 784 * @covers ::add_to_curl_request 785 */ 786 public function test_add_to_curl_request() { 787 $file = $this->get_stored_file('example content'); 788 $filepath = __FILE__; 789 $archivepath = 'example'; 790 $key = 'myfile'; 791 792 // Mock the file_system class. 793 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 794 $fs->method('get_local_path_from_storedfile') 795 ->with($this->equalTo($file), $this->equalTo(true)) 796 ->willReturn($filepath); 797 798 $request = (object) ['_tmp_file_post_params' => []]; 799 $fs->add_to_curl_request($file, $request, $key); 800 $this->assertArrayHasKey($key, $request->_tmp_file_post_params); 801 $this->assertEquals($filepath, $request->_tmp_file_post_params[$key]->name); 802 } 803 804 /** 805 * Ensure that test_get_imageinfo_not_image returns false if the file 806 * passed was deemed to not be an image. 807 * 808 * @covers ::get_imageinfo 809 */ 810 public function test_get_imageinfo_not_image() { 811 $filecontent = 'example content'; 812 $file = $this->get_stored_file($filecontent); 813 814 $fs = $this->get_testable_mock([ 815 'is_image_from_storedfile', 816 ]); 817 818 $fs->expects($this->once()) 819 ->method('is_image_from_storedfile') 820 ->with($this->equalTo($file)) 821 ->willReturn(false); 822 823 $this->assertFalse($fs->get_imageinfo($file)); 824 } 825 826 /** 827 * Ensure that test_get_imageinfo_not_image returns imageinfo. 828 * 829 * @covers ::get_imageinfo 830 */ 831 public function test_get_imageinfo() { 832 $filepath = '/path/to/file'; 833 $filecontent = 'example content'; 834 $expectedresult = (object) []; 835 $file = $this->get_stored_file($filecontent); 836 837 $fs = $this->get_testable_mock([ 838 'is_image_from_storedfile', 839 'get_local_path_from_storedfile', 840 'get_imageinfo_from_path', 841 ]); 842 843 $fs->expects($this->once()) 844 ->method('is_image_from_storedfile') 845 ->with($this->equalTo($file)) 846 ->willReturn(true); 847 848 $fs->expects($this->once()) 849 ->method('get_local_path_from_storedfile') 850 ->with($this->equalTo($file), $this->equalTo(true)) 851 ->willReturn($filepath); 852 853 $fs->expects($this->once()) 854 ->method('get_imageinfo_from_path') 855 ->with($this->equalTo($filepath)) 856 ->willReturn($expectedresult); 857 858 $this->assertEquals($expectedresult, $fs->get_imageinfo($file)); 859 } 860 861 /** 862 * Ensure that is_image_from_storedfile always returns false for an 863 * empty file size. 864 * 865 * @covers ::is_image_from_storedfile 866 */ 867 public function test_is_image_empty_filesize() { 868 $filecontent = 'example content'; 869 $file = $this->get_stored_file($filecontent, null, ['get_filesize']); 870 871 $file->expects($this->once()) 872 ->method('get_filesize') 873 ->willReturn(0); 874 875 $fs = $this->get_testable_mock(); 876 $this->assertFalse($fs->is_image_from_storedfile($file)); 877 } 878 879 /** 880 * Ensure that is_image_from_storedfile behaves correctly based on 881 * mimetype. 882 * 883 * @dataProvider is_image_from_storedfile_provider 884 * @param string $mimetype Mimetype to test 885 * @param bool $isimage Whether this mimetype should be detected as an image 886 * @covers ::is_image_from_storedfile 887 */ 888 public function test_is_image_from_storedfile_mimetype($mimetype, $isimage) { 889 $filecontent = 'example content'; 890 $file = $this->get_stored_file($filecontent, null, ['get_mimetype']); 891 892 $file->expects($this->once()) 893 ->method('get_mimetype') 894 ->willReturn($mimetype); 895 896 $fs = $this->get_testable_mock(); 897 $this->assertEquals($isimage, $fs->is_image_from_storedfile($file)); 898 } 899 900 /** 901 * Test that get_imageinfo_from_path returns an appropriate response 902 * for an image. 903 * 904 * @covers ::get_imageinfo_from_path 905 */ 906 public function test_get_imageinfo_from_path() { 907 $filepath = __DIR__ . "/fixtures/testimage.jpg"; 908 909 // Get the filesystem mock. 910 $fs = $this->get_testable_mock(); 911 912 $method = new \ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); 913 $method->setAccessible(true); 914 $result = $method->invokeArgs($fs, [$filepath]); 915 916 $this->assertArrayHasKey('width', $result); 917 $this->assertArrayHasKey('height', $result); 918 $this->assertArrayHasKey('mimetype', $result); 919 $this->assertEquals('image/jpeg', $result['mimetype']); 920 } 921 922 /** 923 * Test that get_imageinfo_from_path returns an appropriate response 924 * for a file which is not an image. 925 * 926 * @covers ::get_imageinfo_from_path 927 */ 928 public function test_get_imageinfo_from_path_no_image() { 929 $filepath = __FILE__; 930 931 // Get the filesystem mock. 932 $fs = $this->get_testable_mock(); 933 934 $method = new \ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); 935 $method->setAccessible(true); 936 $result = $method->invokeArgs($fs, [$filepath]); 937 938 $this->assertFalse($result); 939 } 940 941 /** 942 * Test that get_imageinfo_from_path returns an appropriate response 943 * for an svg image with viewbox attribute. 944 */ 945 public function test_get_imageinfo_from_path_svg_viewbox() { 946 $filepath = __DIR__ . '/fixtures/testimage_viewbox.svg'; 947 948 // Get the filesystem mock. 949 $fs = $this->get_testable_mock(); 950 951 $method = new \ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); 952 $method->setAccessible(true); 953 $result = $method->invokeArgs($fs, [$filepath]); 954 955 $this->assertArrayHasKey('width', $result); 956 $this->assertArrayHasKey('height', $result); 957 $this->assertArrayHasKey('mimetype', $result); 958 $this->assertEquals(100, $result['width']); 959 $this->assertEquals(100, $result['height']); 960 $this->assertStringContainsString('image/svg', $result['mimetype']); 961 } 962 963 /** 964 * Test that get_imageinfo_from_path returns an appropriate response 965 * for an svg image with width and height attributes. 966 */ 967 public function test_get_imageinfo_from_path_svg_with_width_height() { 968 $filepath = __DIR__ . '/fixtures/testimage_width_height.svg'; 969 970 // Get the filesystem mock. 971 $fs = $this->get_testable_mock(); 972 973 $method = new \ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); 974 $method->setAccessible(true); 975 $result = $method->invokeArgs($fs, [$filepath]); 976 977 $this->assertArrayHasKey('width', $result); 978 $this->assertArrayHasKey('height', $result); 979 $this->assertArrayHasKey('mimetype', $result); 980 $this->assertEquals(100, $result['width']); 981 $this->assertEquals(100, $result['height']); 982 $this->assertStringContainsString('image/svg', $result['mimetype']); 983 } 984 985 /** 986 * Test that get_imageinfo_from_path returns an appropriate response 987 * for an svg image without attributes. 988 */ 989 public function test_get_imageinfo_from_path_svg_without_attribute() { 990 $filepath = __DIR__ . '/fixtures/testimage.svg'; 991 992 // Get the filesystem mock. 993 $fs = $this->get_testable_mock(); 994 995 $method = new \ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); 996 $method->setAccessible(true); 997 $result = $method->invokeArgs($fs, [$filepath]); 998 999 $this->assertArrayHasKey('width', $result); 1000 $this->assertArrayHasKey('height', $result); 1001 $this->assertArrayHasKey('mimetype', $result); 1002 $this->assertEquals(800, $result['width']); 1003 $this->assertEquals(600, $result['height']); 1004 $this->assertStringContainsString('image/svg', $result['mimetype']); 1005 } 1006 1007 /** 1008 * Test that get_imageinfo_from_path returns an appropriate response 1009 * for a file which is not an correct svg. 1010 */ 1011 public function test_get_imageinfo_from_path_svg_invalid() { 1012 $filepath = __DIR__ . '/fixtures/testimage_error.svg'; 1013 1014 // Get the filesystem mock. 1015 $fs = $this->get_testable_mock(); 1016 1017 $method = new \ReflectionMethod(file_system::class, 'get_imageinfo_from_path'); 1018 $method->setAccessible(true); 1019 $result = $method->invokeArgs($fs, [$filepath]); 1020 1021 $this->assertFalse($result); 1022 } 1023 1024 /** 1025 * Ensure that get_content_file_handle returns a valid file handle. 1026 * 1027 * @covers ::get_content_file_handle 1028 */ 1029 public function test_get_content_file_handle_default() { 1030 $filecontent = 'example content'; 1031 $file = $this->get_stored_file($filecontent); 1032 1033 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); 1034 $fs->method('get_remote_path_from_storedfile') 1035 ->willReturn(__FILE__); 1036 1037 // Note: We are unable to determine the mode in which the $fh was opened. 1038 $fh = $fs->get_content_file_handle($file); 1039 $this->assertTrue(is_resource($fh)); 1040 $this->assertEquals('stream', get_resource_type($fh)); 1041 fclose($fh); 1042 } 1043 1044 /** 1045 * Ensure that get_content_file_handle returns a valid file handle for a gz file. 1046 * 1047 * @covers ::get_content_file_handle 1048 */ 1049 public function test_get_content_file_handle_gz() { 1050 $filecontent = 'example content'; 1051 $file = $this->get_stored_file($filecontent); 1052 1053 $fs = $this->get_testable_mock(['get_local_path_from_storedfile']); 1054 $fs->method('get_local_path_from_storedfile') 1055 ->willReturn(__DIR__ . "/fixtures/test.tgz"); 1056 1057 // Note: We are unable to determine the mode in which the $fh was opened. 1058 $fh = $fs->get_content_file_handle($file, \stored_file::FILE_HANDLE_GZOPEN); 1059 $this->assertTrue(is_resource($fh)); 1060 gzclose($fh); 1061 } 1062 1063 /** 1064 * Ensure that get_content_file_handle returns an exception when calling for a invalid file handle type. 1065 * 1066 * @covers ::get_content_file_handle 1067 */ 1068 public function test_get_content_file_handle_invalid() { 1069 $filecontent = 'example content'; 1070 $file = $this->get_stored_file($filecontent); 1071 1072 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); 1073 $fs->method('get_remote_path_from_storedfile') 1074 ->willReturn(__FILE__); 1075 1076 $this->expectException('coding_exception', 'Unexpected file handle type'); 1077 $fs->get_content_file_handle($file, -1); 1078 } 1079 1080 /** 1081 * Ensure that get_content_file_handle returns a valid file handle. 1082 * 1083 * @covers ::get_psr_stream 1084 */ 1085 public function test_get_psr_stream(): void { 1086 $file = $this->get_stored_file(''); 1087 1088 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); 1089 $fs->method('get_remote_path_from_storedfile') 1090 ->willReturn(__FILE__); 1091 1092 $stream = $fs->get_psr_stream($file); 1093 $this->assertInstanceOf(\Psr\Http\Message\StreamInterface::class, $stream); 1094 $this->assertEquals(file_get_contents(__FILE__), $stream->getContents()); 1095 $this->assertFalse($stream->isWritable()); 1096 $stream->close(); 1097 } 1098 1099 /** 1100 * Test that mimetype_from_hash returns the correct mimetype with 1101 * a file whose filename suggests mimetype. 1102 * 1103 * @covers ::mimetype_from_hash 1104 */ 1105 public function test_mimetype_from_hash_using_filename() { 1106 $filepath = '/path/to/file/not/currently/on/disk'; 1107 $filecontent = 'example content'; 1108 $filename = 'test.jpg'; 1109 $contenthash = \file_storage::hash_from_string($filecontent); 1110 1111 $fs = $this->get_testable_mock(['get_remote_path_from_hash']); 1112 $fs->method('get_remote_path_from_hash')->willReturn($filepath); 1113 1114 $result = $fs->mimetype_from_hash($contenthash, $filename); 1115 $this->assertEquals('image/jpeg', $result); 1116 } 1117 1118 /** 1119 * Test that mimetype_from_hash returns the correct mimetype with 1120 * a locally available file whose filename does not suggest mimetype. 1121 * 1122 * @covers ::mimetype_from_hash 1123 */ 1124 public function test_mimetype_from_hash_using_file_content() { 1125 $filecontent = 'example content'; 1126 $contenthash = \file_storage::hash_from_string($filecontent); 1127 $filename = 'example'; 1128 1129 $filepath = __DIR__ . "/fixtures/testimage.jpg"; 1130 $fs = $this->get_testable_mock(['get_local_path_from_hash']); 1131 $fs->method('get_local_path_from_hash')->willReturn($filepath); 1132 1133 $result = $fs->mimetype_from_hash($contenthash, $filename); 1134 $this->assertEquals('image/jpeg', $result); 1135 } 1136 1137 /** 1138 * Test that mimetype_from_hash returns the correct mimetype with 1139 * a remotely available file whose filename does not suggest mimetype. 1140 * 1141 * @covers ::mimetype_from_hash 1142 */ 1143 public function test_mimetype_from_hash_using_file_content_remote() { 1144 $filepath = '/path/to/file/not/currently/on/disk'; 1145 $filecontent = 'example content'; 1146 $contenthash = \file_storage::hash_from_string($filecontent); 1147 $filename = 'example'; 1148 1149 $filepath = __DIR__ . "/fixtures/testimage.jpg"; 1150 1151 $fs = $this->get_testable_mock([ 1152 'get_remote_path_from_hash', 1153 'is_file_readable_locally_by_hash', 1154 'get_local_path_from_hash', 1155 ]); 1156 1157 $fs->method('get_remote_path_from_hash')->willReturn('/path/to/remote/file'); 1158 $fs->method('is_file_readable_locally_by_hash')->willReturn(false); 1159 $fs->method('get_local_path_from_hash')->willReturn($filepath); 1160 1161 $result = $fs->mimetype_from_hash($contenthash, $filename); 1162 $this->assertEquals('image/jpeg', $result); 1163 } 1164 1165 /** 1166 * Test that mimetype_from_storedfile returns the correct mimetype with 1167 * a file whose filename suggests mimetype. 1168 * 1169 * @covers ::mimetype_from_storedfile 1170 */ 1171 public function test_mimetype_from_storedfile_empty() { 1172 $file = $this->get_stored_file(''); 1173 1174 $fs = $this->get_testable_mock(); 1175 $result = $fs->mimetype_from_storedfile($file); 1176 $this->assertNull($result); 1177 } 1178 1179 /** 1180 * Test that mimetype_from_storedfile returns the correct mimetype with 1181 * a file whose filename suggests mimetype. 1182 * 1183 * @covers ::mimetype_from_storedfile 1184 */ 1185 public function test_mimetype_from_storedfile_using_filename() { 1186 $filepath = '/path/to/file/not/currently/on/disk'; 1187 $fs = $this->get_testable_mock(['get_remote_path_from_storedfile']); 1188 $fs->method('get_remote_path_from_storedfile')->willReturn($filepath); 1189 1190 $file = $this->get_stored_file('example content', 'test.jpg'); 1191 1192 $result = $fs->mimetype_from_storedfile($file); 1193 $this->assertEquals('image/jpeg', $result); 1194 } 1195 1196 /** 1197 * Test that mimetype_from_storedfile returns the correct mimetype with 1198 * a locally available file whose filename does not suggest mimetype. 1199 * 1200 * @covers ::mimetype_from_storedfile 1201 */ 1202 public function test_mimetype_from_storedfile_using_file_content() { 1203 $filepath = __DIR__ . "/fixtures/testimage.jpg"; 1204 $fs = $this->get_testable_mock(['get_local_path_from_hash']); 1205 $fs->method('get_local_path_from_hash')->willReturn($filepath); 1206 1207 $file = $this->get_stored_file('example content'); 1208 1209 $result = $fs->mimetype_from_storedfile($file); 1210 $this->assertEquals('image/jpeg', $result); 1211 } 1212 1213 /** 1214 * Test that mimetype_from_storedfile returns the correct mimetype with 1215 * a remotely available file whose filename does not suggest mimetype. 1216 * 1217 * @covers ::mimetype_from_storedfile 1218 */ 1219 public function test_mimetype_from_storedfile_using_file_content_remote() { 1220 $filepath = __DIR__ . "/fixtures/testimage.jpg"; 1221 1222 $fs = $this->get_testable_mock([ 1223 'is_file_readable_locally_by_hash', 1224 'get_local_path_from_hash', 1225 ]); 1226 1227 $fs->method('is_file_readable_locally_by_hash')->willReturn(false); 1228 $fs->method('get_local_path_from_hash')->will($this->onConsecutiveCalls('/path/to/remote/file', $filepath)); 1229 1230 $file = $this->get_stored_file('example content'); 1231 1232 $result = $fs->mimetype_from_storedfile($file); 1233 $this->assertEquals('image/jpeg', $result); 1234 } 1235 1236 /** 1237 * Data Provider for is_image_from_storedfile tests. 1238 * 1239 * @return array 1240 */ 1241 public function is_image_from_storedfile_provider() { 1242 return array( 1243 'Standard image' => array('image/png', true), 1244 'Made up document/image' => array('document/image', false), 1245 ); 1246 } 1247 1248 /** 1249 * Data provider for get_local_path_from_storedfile tests. 1250 * 1251 * @return array 1252 */ 1253 public function get_local_path_from_storedfile_provider() { 1254 return [ 1255 'default args (nofetch)' => [ 1256 'args' => [], 1257 'fetch' => 0, 1258 ], 1259 'explicit: nofetch' => [ 1260 'args' => [false], 1261 'fetch' => 0, 1262 ], 1263 'explicit: fetch' => [ 1264 'args' => [true], 1265 'fetch' => 1, 1266 ], 1267 ]; 1268 } 1269 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body