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