Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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 /lib/filestorage/file_storage.php
  19   *
  20   * @package   core_files
  21   * @category  phpunit
  22   * @copyright 2012 David Mudrak <david@moodle.com>
  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 . '/filelib.php');
  30  require_once($CFG->dirroot . '/repository/lib.php');
  31  require_once($CFG->libdir . '/filestorage/stored_file.php');
  32  
  33  /**
  34   * Unit tests for /lib/filestorage/file_storage.php
  35   *
  36   * @copyright 2012 David Mudrak <david@moodle.com>
  37   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   * @coversDefaultClass \file_storage
  39   */
  40  class core_files_file_storage_testcase extends advanced_testcase {
  41  
  42      /**
  43       * Files can be created from strings.
  44       *
  45       * @covers ::create_file_from_string
  46       */
  47      public function test_create_file_from_string() {
  48          global $DB;
  49  
  50          $this->resetAfterTest(true);
  51  
  52          // Number of files installed in the database on a fresh Moodle site.
  53          $installedfiles = $DB->count_records('files', array());
  54  
  55          $content = 'abcd';
  56          $syscontext = context_system::instance();
  57          $filerecord = array(
  58              'contextid' => $syscontext->id,
  59              'component' => 'core',
  60              'filearea'  => 'unittest',
  61              'itemid'    => 0,
  62              'filepath'  => '/images/',
  63              'filename'  => 'testfile.txt',
  64          );
  65          $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
  66  
  67          $fs = get_file_storage();
  68          $file = $fs->create_file_from_string($filerecord, $content);
  69  
  70          $this->assertInstanceOf('stored_file', $file);
  71          $this->assertTrue($file->compare_to_string($content));
  72          $this->assertSame($pathhash, $file->get_pathnamehash());
  73  
  74          $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
  75  
  76          $filesystem = $fs->get_file_system();
  77          $location = $filesystem->get_local_path_from_storedfile($file, true);
  78  
  79          $this->assertFileExists($location);
  80  
  81          // Verify the dir placeholder files are created.
  82          $this->assertEquals($installedfiles + 3, $DB->count_records('files', array()));
  83          $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
  84          $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
  85  
  86          // Tests that missing content file is recreated.
  87  
  88          unlink($location);
  89          $this->assertFileNotExists($location);
  90  
  91          $filerecord['filename'] = 'testfile2.txt';
  92          $file2 = $fs->create_file_from_string($filerecord, $content);
  93          $this->assertInstanceOf('stored_file', $file2);
  94          $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
  95          $this->assertFileExists($location);
  96  
  97          $this->assertEquals($installedfiles + 4, $DB->count_records('files', array()));
  98  
  99          // Test that borked content file is recreated.
 100  
 101          $this->assertSame(2, file_put_contents($location, 'xx'));
 102  
 103          $filerecord['filename'] = 'testfile3.txt';
 104          $file3 = $fs->create_file_from_string($filerecord, $content);
 105          $this->assertInstanceOf('stored_file', $file3);
 106          $this->assertSame($file->get_contenthash(), $file3->get_contenthash());
 107          $this->assertFileExists($location);
 108  
 109          $this->assertSame($content, file_get_contents($location));
 110          $this->assertDebuggingCalled();
 111  
 112          $this->assertEquals($installedfiles + 5, $DB->count_records('files', array()));
 113      }
 114  
 115      /**
 116       * Local files can be added to the filepool
 117       *
 118       * @covers ::create_file_from_pathname
 119       */
 120      public function test_create_file_from_pathname() {
 121          global $CFG, $DB;
 122  
 123          $this->resetAfterTest(true);
 124  
 125          // Number of files installed in the database on a fresh Moodle site.
 126          $installedfiles = $DB->count_records('files', array());
 127  
 128          $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
 129          $syscontext = context_system::instance();
 130          $filerecord = array(
 131              'contextid' => $syscontext->id,
 132              'component' => 'core',
 133              'filearea'  => 'unittest',
 134              'itemid'    => 0,
 135              'filepath'  => '/images/',
 136              'filename'  => 'testimage.jpg',
 137          );
 138          $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
 139  
 140          $fs = get_file_storage();
 141          $file = $fs->create_file_from_pathname($filerecord, $filepath);
 142  
 143          $this->assertInstanceOf('stored_file', $file);
 144          $this->assertTrue($file->compare_to_path($filepath));
 145  
 146          $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>$pathhash)));
 147  
 148          $filesystem = $fs->get_file_system();
 149          $location = $filesystem->get_local_path_from_storedfile($file, true);
 150  
 151          $this->assertFileExists($location);
 152  
 153          // Verify the dir placeholder files are created.
 154          $this->assertEquals($installedfiles + 3, $DB->count_records('files', array()));
 155          $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].'/.'))));
 156          $this->assertTrue($DB->record_exists('files', array('pathnamehash'=>sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].'.'))));
 157  
 158          // Tests that missing content file is recreated.
 159  
 160          unlink($location);
 161          $this->assertFileNotExists($location);
 162  
 163          $filerecord['filename'] = 'testfile2.jpg';
 164          $file2 = $fs->create_file_from_pathname($filerecord, $filepath);
 165          $this->assertInstanceOf('stored_file', $file2);
 166          $this->assertSame($file->get_contenthash(), $file2->get_contenthash());
 167          $this->assertFileExists($location);
 168  
 169          $this->assertEquals($installedfiles + 4, $DB->count_records('files', array()));
 170  
 171          // Test that borked content file is recreated.
 172  
 173          $this->assertSame(2, file_put_contents($location, 'xx'));
 174  
 175          $filerecord['filename'] = 'testfile3.jpg';
 176          $file3 = $fs->create_file_from_pathname($filerecord, $filepath);
 177          $this->assertInstanceOf('stored_file', $file3);
 178          $this->assertSame($file->get_contenthash(), $file3->get_contenthash());
 179          $this->assertFileExists($location);
 180  
 181          $this->assertSame(file_get_contents($filepath), file_get_contents($location));
 182          $this->assertDebuggingCalled();
 183  
 184          $this->assertEquals($installedfiles + 5, $DB->count_records('files', array()));
 185  
 186          // Test invalid file creation.
 187  
 188          $filerecord['filename'] = 'testfile4.jpg';
 189          try {
 190              $fs->create_file_from_pathname($filerecord, $filepath.'nonexistent');
 191              $this->fail('Exception expected when trying to add non-existent stored file.');
 192          } catch (Exception $e) {
 193              $this->assertInstanceOf('file_exception', $e);
 194          }
 195      }
 196  
 197      /**
 198       * Tests get get file.
 199       *
 200       * @covers ::get_file
 201       */
 202      public function test_get_file() {
 203          global $CFG;
 204  
 205          $this->resetAfterTest(false);
 206  
 207          $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
 208          $syscontext = context_system::instance();
 209          $filerecord = array(
 210              'contextid' => $syscontext->id,
 211              'component' => 'core',
 212              'filearea'  => 'unittest',
 213              'itemid'    => 0,
 214              'filepath'  => '/images/',
 215              'filename'  => 'testimage.jpg',
 216          );
 217          $pathhash = sha1('/'.$filerecord['contextid'].'/'.$filerecord['component'].'/'.$filerecord['filearea'].'/'.$filerecord['itemid'].$filerecord['filepath'].$filerecord['filename']);
 218  
 219          $fs = get_file_storage();
 220          $file = $fs->create_file_from_pathname($filerecord, $filepath);
 221  
 222          $this->assertInstanceOf('stored_file', $file);
 223          $this->assertEquals($syscontext->id, $file->get_contextid());
 224          $this->assertEquals('core', $file->get_component());
 225          $this->assertEquals('unittest', $file->get_filearea());
 226          $this->assertEquals(0, $file->get_itemid());
 227          $this->assertEquals('/images/', $file->get_filepath());
 228          $this->assertEquals('testimage.jpg', $file->get_filename());
 229          $this->assertEquals(filesize($filepath), $file->get_filesize());
 230          $this->assertEquals($pathhash, $file->get_pathnamehash());
 231  
 232          return $file;
 233      }
 234  
 235      /**
 236       * Local images can be added to the filepool and their preview can be obtained
 237       *
 238       * @param stored_file $file
 239       * @depends test_get_file
 240       * @covers ::get_file_preview
 241       */
 242      public function test_get_file_preview(stored_file $file) {
 243          global $CFG;
 244  
 245          $this->resetAfterTest();
 246          $fs = get_file_storage();
 247  
 248          $previewtinyicon = $fs->get_file_preview($file, 'tinyicon');
 249          $this->assertInstanceOf('stored_file', $previewtinyicon);
 250          $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
 251  
 252          $previewtinyicon = $fs->get_file_preview($file, 'thumb');
 253          $this->assertInstanceOf('stored_file', $previewtinyicon);
 254          $this->assertEquals('6b9864ae1536a8eeef54e097319175a8be12f07c', $previewtinyicon->get_filename());
 255  
 256          $this->expectException('file_exception');
 257          $fs->get_file_preview($file, 'amodewhichdoesntexist');
 258      }
 259  
 260      /**
 261       * Tests for get_file_preview without an image.
 262       *
 263       * @covers ::get_file_preview
 264       */
 265      public function test_get_file_preview_nonimage() {
 266          $this->resetAfterTest(true);
 267          $syscontext = context_system::instance();
 268          $filerecord = array(
 269              'contextid' => $syscontext->id,
 270              'component' => 'core',
 271              'filearea'  => 'unittest',
 272              'itemid'    => 0,
 273              'filepath'  => '/textfiles/',
 274              'filename'  => 'testtext.txt',
 275          );
 276  
 277          $fs = get_file_storage();
 278          $fs->create_file_from_string($filerecord, 'text contents');
 279          $textfile = $fs->get_file($syscontext->id, $filerecord['component'], $filerecord['filearea'],
 280              $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename']);
 281  
 282          $preview = $fs->get_file_preview($textfile, 'thumb');
 283          $this->assertFalse($preview);
 284      }
 285  
 286      /**
 287       * Make sure renaming is working
 288       *
 289       * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
 290       * @covers \stored_file::rename
 291       */
 292      public function test_file_renaming() {
 293          global $CFG;
 294  
 295          $this->resetAfterTest();
 296          $fs = get_file_storage();
 297          $syscontext = context_system::instance();
 298          $component = 'core';
 299          $filearea  = 'unittest';
 300          $itemid    = 0;
 301          $filepath  = '/';
 302          $filename  = 'test.txt';
 303  
 304          $filerecord = array(
 305              'contextid' => $syscontext->id,
 306              'component' => $component,
 307              'filearea'  => $filearea,
 308              'itemid'    => $itemid,
 309              'filepath'  => $filepath,
 310              'filename'  => $filename,
 311          );
 312  
 313          $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
 314          $this->assertInstanceOf('stored_file', $originalfile);
 315          $contenthash = $originalfile->get_contenthash();
 316          $newpath = '/test/';
 317          $newname = 'newtest.txt';
 318  
 319          // This should work.
 320          $originalfile->rename($newpath, $newname);
 321          $file = $fs->get_file($syscontext->id, $component, $filearea, $itemid, $newpath, $newname);
 322          $this->assertInstanceOf('stored_file', $file);
 323          $this->assertEquals($contenthash, $file->get_contenthash());
 324  
 325          // Try break it.
 326          $this->expectException('file_exception');
 327          $this->expectExceptionMessage('Cannot create file 1/core/unittest/0/test/newtest.txt (file exists, cannot rename)');
 328          // This shall throw exception.
 329          $originalfile->rename($newpath, $newname);
 330      }
 331  
 332      /**
 333       * Create file from reference tests
 334       *
 335       * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
 336       * @covers ::create_file_from_reference
 337       */
 338      public function test_create_file_from_reference() {
 339          global $CFG, $DB;
 340  
 341          $this->resetAfterTest();
 342          // Create user.
 343          $generator = $this->getDataGenerator();
 344          $user = $generator->create_user();
 345          $this->setUser($user);
 346          $usercontext = context_user::instance($user->id);
 347          $syscontext = context_system::instance();
 348  
 349          $fs = get_file_storage();
 350  
 351          $repositorypluginname = 'user';
 352          // Override repository permission.
 353          $capability = 'repository/' . $repositorypluginname . ':view';
 354          $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest'));
 355          assign_capability($capability, CAP_ALLOW, $guestroleid, $syscontext->id, true);
 356  
 357          $args = array();
 358          $args['type'] = $repositorypluginname;
 359          $repos = repository::get_instances($args);
 360          $userrepository = reset($repos);
 361          $this->assertInstanceOf('repository', $userrepository);
 362  
 363          $component = 'user';
 364          $filearea  = 'private';
 365          $itemid    = 0;
 366          $filepath  = '/';
 367          $filename  = 'userfile.txt';
 368  
 369          $filerecord = array(
 370              'contextid' => $usercontext->id,
 371              'component' => $component,
 372              'filearea'  => $filearea,
 373              'itemid'    => $itemid,
 374              'filepath'  => $filepath,
 375              'filename'  => $filename,
 376          );
 377  
 378          $content = 'Test content';
 379          $originalfile = $fs->create_file_from_string($filerecord, $content);
 380          $this->assertInstanceOf('stored_file', $originalfile);
 381  
 382          $newfilerecord = array(
 383              'contextid' => $syscontext->id,
 384              'component' => 'core',
 385              'filearea'  => 'phpunit',
 386              'itemid'    => 0,
 387              'filepath'  => $filepath,
 388              'filename'  => $filename,
 389          );
 390          $ref = $fs->pack_reference($filerecord);
 391          $newstoredfile = $fs->create_file_from_reference($newfilerecord, $userrepository->id, $ref);
 392          $this->assertInstanceOf('stored_file', $newstoredfile);
 393          $this->assertEquals($userrepository->id, $newstoredfile->get_repository_id());
 394          $this->assertEquals($originalfile->get_contenthash(), $newstoredfile->get_contenthash());
 395          $this->assertEquals($originalfile->get_filesize(), $newstoredfile->get_filesize());
 396          $this->assertRegExp('#' . $filename. '$#', $newstoredfile->get_reference_details());
 397  
 398          // Test looking for references.
 399          $count = $fs->get_references_count_by_storedfile($originalfile);
 400          $this->assertEquals(1, $count);
 401          $files = $fs->get_references_by_storedfile($originalfile);
 402          $file = reset($files);
 403          $this->assertEquals($file, $newstoredfile);
 404  
 405          // Look for references by repository ID.
 406          $files = $fs->get_external_files($userrepository->id);
 407          $file = reset($files);
 408          $this->assertEquals($file, $newstoredfile);
 409  
 410          // Try convert reference to local file.
 411          $importedfile = $fs->import_external_file($newstoredfile);
 412          $this->assertFalse($importedfile->is_external_file());
 413          $this->assertInstanceOf('stored_file', $importedfile);
 414          // Still readable?
 415          $this->assertEquals($content, $importedfile->get_content());
 416      }
 417  
 418      /**
 419       * Create file from reference tests
 420       *
 421       * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
 422       * @covers ::create_file_from_reference
 423       */
 424      public function test_create_file_from_reference_with_content_hash() {
 425          global $CFG, $DB;
 426  
 427          $this->resetAfterTest();
 428          // Create user.
 429          $generator = $this->getDataGenerator();
 430          $user = $generator->create_user();
 431          $this->setUser($user);
 432          $usercontext = context_user::instance($user->id);
 433          $syscontext = context_system::instance();
 434  
 435          $fs = get_file_storage();
 436  
 437          $repositorypluginname = 'user';
 438          // Override repository permission.
 439          $capability = 'repository/' . $repositorypluginname . ':view';
 440          $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest'));
 441          assign_capability($capability, CAP_ALLOW, $guestroleid, $syscontext->id, true);
 442  
 443          $args = array();
 444          $args['type'] = $repositorypluginname;
 445          $repos = repository::get_instances($args);
 446          $userrepository = reset($repos);
 447          $this->assertInstanceOf('repository', $userrepository);
 448  
 449          $component = 'user';
 450          $filearea = 'private';
 451          $itemid = 0;
 452          $filepath = '/';
 453          $filename = 'userfile.txt';
 454  
 455          $filerecord = array(
 456                  'contextid' => $usercontext->id,
 457                  'component' => $component,
 458                  'filearea' => $filearea,
 459                  'itemid' => $itemid,
 460                  'filepath' => $filepath,
 461                  'filename' => $filename,
 462          );
 463  
 464          $content = 'Test content';
 465          $originalfile = $fs->create_file_from_string($filerecord, $content);
 466          $this->assertInstanceOf('stored_file', $originalfile);
 467  
 468          $otherfilerecord = $filerecord;
 469          $otherfilerecord['filename'] = 'other-filename.txt';
 470          $otherfilewithsamecontents = $fs->create_file_from_string($otherfilerecord, $content);
 471          $this->assertInstanceOf('stored_file', $otherfilewithsamecontents);
 472  
 473          $newfilerecord = array(
 474                  'contextid' => $syscontext->id,
 475                  'component' => 'core',
 476                  'filearea' => 'phpunit',
 477                  'itemid' => 0,
 478                  'filepath' => $filepath,
 479                  'filename' => $filename,
 480                  'contenthash' => $originalfile->get_contenthash(),
 481          );
 482          $ref = $fs->pack_reference($filerecord);
 483          $newstoredfile = $fs->create_file_from_reference($newfilerecord, $userrepository->id, $ref);
 484          $this->assertInstanceOf('stored_file', $newstoredfile);
 485          $this->assertEquals($userrepository->id, $newstoredfile->get_repository_id());
 486          $this->assertEquals($originalfile->get_contenthash(), $newstoredfile->get_contenthash());
 487          $this->assertEquals($originalfile->get_filesize(), $newstoredfile->get_filesize());
 488          $this->assertRegExp('#' . $filename . '$#', $newstoredfile->get_reference_details());
 489      }
 490  
 491      private function setup_three_private_files() {
 492  
 493          $this->resetAfterTest();
 494  
 495          $generator = $this->getDataGenerator();
 496          $user = $generator->create_user();
 497          $this->setUser($user->id);
 498          $usercontext = context_user::instance($user->id);
 499          // Create a user private file.
 500          $file1 = new stdClass;
 501          $file1->contextid = $usercontext->id;
 502          $file1->component = 'user';
 503          $file1->filearea  = 'private';
 504          $file1->itemid    = 0;
 505          $file1->filepath  = '/';
 506          $file1->filename  = '1.txt';
 507          $file1->source    = 'test';
 508  
 509          $fs = get_file_storage();
 510          $userfile1 = $fs->create_file_from_string($file1, 'file1 content');
 511          $this->assertInstanceOf('stored_file', $userfile1);
 512  
 513          $file2 = clone($file1);
 514          $file2->filename = '2.txt';
 515          $userfile2 = $fs->create_file_from_string($file2, 'file2 content longer');
 516          $this->assertInstanceOf('stored_file', $userfile2);
 517  
 518          $file3 = clone($file1);
 519          $file3->filename = '3.txt';
 520          $userfile3 = $fs->create_file_from_storedfile($file3, $userfile2);
 521          $this->assertInstanceOf('stored_file', $userfile3);
 522  
 523          $user->ctxid = $usercontext->id;
 524  
 525          return $user;
 526      }
 527  
 528      /**
 529       * Tests for get_area_files
 530       *
 531       * @covers ::get_area_files
 532       */
 533      public function test_get_area_files() {
 534          $user = $this->setup_three_private_files();
 535          $fs = get_file_storage();
 536  
 537          // Get area files with default options.
 538          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
 539  
 540          // Should be the two files we added plus the folder.
 541          $this->assertEquals(4, count($areafiles));
 542  
 543          // Verify structure.
 544          foreach ($areafiles as $key => $file) {
 545              $this->assertInstanceOf('stored_file', $file);
 546              $this->assertEquals($key, $file->get_pathnamehash());
 547          }
 548  
 549          // Get area files without a folder.
 550          $folderlessfiles = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'sortorder', false);
 551          // Should be the two files without folder.
 552          $this->assertEquals(3, count($folderlessfiles));
 553  
 554          // Verify structure.
 555          foreach ($folderlessfiles as $key => $file) {
 556              $this->assertInstanceOf('stored_file', $file);
 557              $this->assertEquals($key, $file->get_pathnamehash());
 558          }
 559  
 560          // Get area files ordered by id.
 561          $filesbyid  = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'id', false);
 562          // Should be the two files without folder.
 563          $this->assertEquals(3, count($filesbyid));
 564  
 565          // Verify structure.
 566          foreach ($filesbyid as $key => $file) {
 567              $this->assertInstanceOf('stored_file', $file);
 568              $this->assertEquals($key, $file->get_pathnamehash());
 569          }
 570  
 571          // Test the limit feature to retrieve each individual file.
 572          $limited = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false,
 573                  0, 0, 1);
 574          $mapfunc = function($f) {
 575              return $f->get_filename();
 576          };
 577          $this->assertEquals(array('1.txt'), array_values(array_map($mapfunc, $limited)));
 578          $limited = $fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false,
 579                  0, 1, 50);
 580          $this->assertEquals(array('2.txt', '3.txt'), array_values(array_map($mapfunc, $limited)));
 581  
 582          // Test with an itemid with no files.
 583          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private', 666, 'sortorder', false);
 584          // Should be none.
 585          $this->assertEmpty($areafiles);
 586      }
 587  
 588      /**
 589       * Tests for get_area_tree
 590       *
 591       * @covers ::get_area_tree
 592       */
 593      public function test_get_area_tree() {
 594          $user = $this->setup_three_private_files();
 595          $fs = get_file_storage();
 596  
 597          // Get area files with default options.
 598          $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
 599          $this->assertEmpty($areatree['subdirs']);
 600          $this->assertNotEmpty($areatree['files']);
 601          $this->assertCount(3, $areatree['files']);
 602  
 603          // Ensure an empty try with a fake itemid.
 604          $emptytree = $fs->get_area_tree($user->ctxid, 'user', 'private', 666);
 605          $this->assertEmpty($emptytree['subdirs']);
 606          $this->assertEmpty($emptytree['files']);
 607  
 608          // Create a subdir.
 609          $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
 610          $this->assertInstanceOf('stored_file', $dir);
 611  
 612          // Add a file to the subdir.
 613          $filerecord = array(
 614              'contextid' => $user->ctxid,
 615              'component' => 'user',
 616              'filearea'  => 'private',
 617              'itemid'    => 0,
 618              'filepath'  => '/testsubdir/',
 619              'filename'  => 'test-get-area-tree.txt',
 620          );
 621  
 622          $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
 623          $this->assertInstanceOf('stored_file', $directoryfile);
 624  
 625          $areatree = $fs->get_area_tree($user->ctxid, 'user', 'private', 0);
 626  
 627          // At the top level there should still be 3 files.
 628          $this->assertCount(3, $areatree['files']);
 629  
 630          // There should now be a subdirectory.
 631          $this->assertCount(1, $areatree['subdirs']);
 632  
 633          // The test subdir is named testsubdir.
 634          $subdir = $areatree['subdirs']['testsubdir'];
 635          $this->assertNotEmpty($subdir);
 636          // It should have one file we added.
 637          $this->assertCount(1, $subdir['files']);
 638          // And no subdirs itself.
 639          $this->assertCount(0, $subdir['subdirs']);
 640  
 641          // Verify the file is the one we added.
 642          $subdirfile = reset($subdir['files']);
 643          $this->assertInstanceOf('stored_file', $subdirfile);
 644          $this->assertEquals($filerecord['filename'], $subdirfile->get_filename());
 645      }
 646  
 647      /**
 648       * Tests for get_file_by_id
 649       *
 650       * @covers ::get_file_by_id
 651       */
 652      public function test_get_file_by_id() {
 653          $user = $this->setup_three_private_files();
 654          $fs = get_file_storage();
 655  
 656          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
 657  
 658          // Test get_file_by_id.
 659          $filebyid = reset($areafiles);
 660          $shouldbesame = $fs->get_file_by_id($filebyid->get_id());
 661          $this->assertEquals($filebyid->get_contenthash(), $shouldbesame->get_contenthash());
 662  
 663          // Test an id which doens't exist.
 664          $doesntexist = $fs->get_file_by_id(99999);
 665          $this->assertFalse($doesntexist);
 666      }
 667  
 668      /**
 669       * Tests for get_file_by_hash
 670       *
 671       * @covers ::get_file_by_hash
 672       */
 673      public function test_get_file_by_hash() {
 674          $user = $this->setup_three_private_files();
 675          $fs = get_file_storage();
 676  
 677          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
 678          // Test get_file_by_hash.
 679          $filebyhash = reset($areafiles);
 680          $shouldbesame = $fs->get_file_by_hash($filebyhash->get_pathnamehash());
 681          $this->assertEquals($filebyhash->get_id(), $shouldbesame->get_id());
 682  
 683          // Test an hash which doens't exist.
 684          $doesntexist = $fs->get_file_by_hash('DOESNTEXIST');
 685          $this->assertFalse($doesntexist);
 686      }
 687  
 688      /**
 689       * Tests for get_external_files
 690       *
 691       * @covers ::get_external_files
 692       */
 693      public function test_get_external_files() {
 694          $user = $this->setup_three_private_files();
 695          $fs = get_file_storage();
 696  
 697          $repos = repository::get_instances(array('type'=>'user'));
 698          $userrepository = reset($repos);
 699          $this->assertInstanceOf('repository', $userrepository);
 700  
 701          // No aliases yet.
 702          $exfiles = $fs->get_external_files($userrepository->id, 'id');
 703          $this->assertEquals(array(), $exfiles);
 704  
 705          // Create three aliases linking the same original: $aliasfile1 and $aliasfile2 are
 706          // created via create_file_from_reference(), $aliasfile3 created from $aliasfile2.
 707          $originalfile = null;
 708          foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
 709              if (!$areafile->is_directory()) {
 710                  $originalfile = $areafile;
 711                  break;
 712              }
 713          }
 714          $this->assertInstanceOf('stored_file', $originalfile);
 715          $originalrecord = array(
 716              'contextid' => $originalfile->get_contextid(),
 717              'component' => $originalfile->get_component(),
 718              'filearea'  => $originalfile->get_filearea(),
 719              'itemid'    => $originalfile->get_itemid(),
 720              'filepath'  => $originalfile->get_filepath(),
 721              'filename'  => $originalfile->get_filename(),
 722          );
 723  
 724          $aliasrecord = $this->generate_file_record();
 725          $aliasrecord->filepath = '/foo/';
 726          $aliasrecord->filename = 'one.txt';
 727  
 728          $ref = $fs->pack_reference($originalrecord);
 729          $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
 730  
 731          $aliasrecord->filepath = '/bar/';
 732          $aliasrecord->filename = 'uno.txt';
 733          // Change the order of the items in the array to make sure that it does not matter.
 734          ksort($originalrecord);
 735          $ref = $fs->pack_reference($originalrecord);
 736          $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $userrepository->id, $ref);
 737  
 738          $aliasrecord->filepath = '/bar/';
 739          $aliasrecord->filename = 'jedna.txt';
 740          $aliasfile3 = $fs->create_file_from_storedfile($aliasrecord, $aliasfile2);
 741  
 742          // Make sure we get three aliases now.
 743          $exfiles = $fs->get_external_files($userrepository->id, 'id');
 744          $this->assertEquals(3, count($exfiles));
 745          foreach ($exfiles as $exfile) {
 746              $this->assertTrue($exfile->is_external_file());
 747          }
 748          // Make sure they all link the same original (thence that all are linked with the same
 749          // record in {files_reference}).
 750          $this->assertEquals($aliasfile1->get_referencefileid(), $aliasfile2->get_referencefileid());
 751          $this->assertEquals($aliasfile3->get_referencefileid(), $aliasfile2->get_referencefileid());
 752      }
 753  
 754      /**
 755       * Tests for create_directory with a negative contextid.
 756       *
 757       * @covers ::create_directory
 758       */
 759      public function test_create_directory_contextid_negative() {
 760          $fs = get_file_storage();
 761  
 762          $this->expectException('file_exception');
 763          $fs->create_directory(-1, 'core', 'unittest', 0, '/');
 764      }
 765  
 766      /**
 767       * Tests for create_directory with an invalid contextid.
 768       *
 769       * @covers ::create_directory
 770       */
 771      public function test_create_directory_contextid_invalid() {
 772          $fs = get_file_storage();
 773  
 774          $this->expectException('file_exception');
 775          $fs->create_directory('not an int', 'core', 'unittest', 0, '/');
 776      }
 777  
 778      /**
 779       * Tests for create_directory with an invalid component.
 780       *
 781       * @covers ::create_directory
 782       */
 783      public function test_create_directory_component_invalid() {
 784          $fs = get_file_storage();
 785          $syscontext = context_system::instance();
 786  
 787          $this->expectException('file_exception');
 788          $fs->create_directory($syscontext->id, 'bad/component', 'unittest', 0, '/');
 789      }
 790  
 791      /**
 792       * Tests for create_directory with an invalid filearea.
 793       *
 794       * @covers ::create_directory
 795       */
 796      public function test_create_directory_filearea_invalid() {
 797          $fs = get_file_storage();
 798          $syscontext = context_system::instance();
 799  
 800          $this->expectException('file_exception');
 801          $fs->create_directory($syscontext->id, 'core', 'bad-filearea', 0, '/');
 802      }
 803  
 804      /**
 805       * Tests for create_directory with a negative itemid
 806       *
 807       * @covers ::create_directory
 808       */
 809      public function test_create_directory_itemid_negative() {
 810          $fs = get_file_storage();
 811          $syscontext = context_system::instance();
 812  
 813          $this->expectException('file_exception');
 814          $fs->create_directory($syscontext->id, 'core', 'unittest', -1, '/');
 815      }
 816  
 817      /**
 818       * Tests for create_directory with an invalid itemid
 819       *
 820       * @covers ::create_directory
 821       */
 822      public function test_create_directory_itemid_invalid() {
 823          $fs = get_file_storage();
 824          $syscontext = context_system::instance();
 825  
 826          $this->expectException('file_exception');
 827          $fs->create_directory($syscontext->id, 'core', 'unittest', 'notanint', '/');
 828      }
 829  
 830      /**
 831       * Tests for create_directory with an invalid filepath
 832       *
 833       * @covers ::create_directory
 834       */
 835      public function test_create_directory_filepath_invalid() {
 836          $fs = get_file_storage();
 837          $syscontext = context_system::instance();
 838  
 839          $this->expectException('file_exception');
 840          $fs->create_directory($syscontext->id, 'core', 'unittest', 0, '/not-with-trailing/or-leading-slash');
 841      }
 842  
 843      /**
 844       * Tests for get_directory_files.
 845       *
 846       * @covers ::get_directory_files
 847       */
 848      public function test_get_directory_files() {
 849          $user = $this->setup_three_private_files();
 850          $fs = get_file_storage();
 851  
 852          $dir = $fs->create_directory($user->ctxid, 'user', 'private', 0, '/testsubdir/');
 853          $this->assertInstanceOf('stored_file', $dir);
 854  
 855          // Add a file to the subdir.
 856          $filerecord = array(
 857              'contextid' => $user->ctxid,
 858              'component' => 'user',
 859              'filearea'  => 'private',
 860              'itemid'    => 0,
 861              'filepath'  => '/testsubdir/',
 862              'filename'  => 'test-get-area-tree.txt',
 863          );
 864  
 865          $directoryfile = $fs->create_file_from_string($filerecord, 'Test content');
 866          $this->assertInstanceOf('stored_file', $directoryfile);
 867  
 868          // Don't recurse without dirs.
 869          $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, false, 'id');
 870          // 3 files only.
 871          $this->assertCount(3, $files);
 872          foreach ($files as $key => $file) {
 873              $this->assertInstanceOf('stored_file', $file);
 874              $this->assertEquals($key, $file->get_pathnamehash());
 875          }
 876  
 877          // Don't recurse with dirs.
 878          $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', false, true, 'id');
 879          // 3 files + 1 directory.
 880          $this->assertCount(4, $files);
 881          foreach ($files as $key => $file) {
 882              $this->assertInstanceOf('stored_file', $file);
 883              $this->assertEquals($key, $file->get_pathnamehash());
 884          }
 885  
 886          // Recurse with dirs.
 887          $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, true, 'id');
 888          // 3 files + 1 directory +  1 subdir file.
 889          $this->assertCount(5, $files);
 890          foreach ($files as $key => $file) {
 891              $this->assertInstanceOf('stored_file', $file);
 892              $this->assertEquals($key, $file->get_pathnamehash());
 893          }
 894  
 895          // Recurse without dirs.
 896          $files = $fs->get_directory_files($user->ctxid, 'user', 'private', 0, '/', true, false, 'id');
 897          // 3 files +  1 subdir file.
 898          $this->assertCount(4, $files);
 899          foreach ($files as $key => $file) {
 900              $this->assertInstanceOf('stored_file', $file);
 901              $this->assertEquals($key, $file->get_pathnamehash());
 902          }
 903      }
 904  
 905      /**
 906       * Tests for search_references.
 907       *
 908       * @covers ::search_references
 909       */
 910      public function test_search_references() {
 911          $user = $this->setup_three_private_files();
 912          $fs = get_file_storage();
 913          $repos = repository::get_instances(array('type'=>'user'));
 914          $repo = reset($repos);
 915  
 916          $alias1 = array(
 917              'contextid' => $user->ctxid,
 918              'component' => 'user',
 919              'filearea'  => 'private',
 920              'itemid'    => 0,
 921              'filepath'  => '/aliases/',
 922              'filename'  => 'alias-to-1.txt'
 923          );
 924  
 925          $alias2 = array(
 926              'contextid' => $user->ctxid,
 927              'component' => 'user',
 928              'filearea'  => 'private',
 929              'itemid'    => 0,
 930              'filepath'  => '/aliases/',
 931              'filename'  => 'another-alias-to-1.txt'
 932          );
 933  
 934          $reference = file_storage::pack_reference(array(
 935              'contextid' => $user->ctxid,
 936              'component' => 'user',
 937              'filearea'  => 'private',
 938              'itemid'    => 0,
 939              'filepath'  => '/',
 940              'filename'  => '1.txt'
 941          ));
 942  
 943          // There are no aliases now.
 944          $result = $fs->search_references($reference);
 945          $this->assertEquals(array(), $result);
 946  
 947          $result = $fs->search_references_count($reference);
 948          $this->assertSame($result, 0);
 949  
 950          // Create two aliases and make sure they are returned.
 951          $fs->create_file_from_reference($alias1, $repo->id, $reference);
 952          $fs->create_file_from_reference($alias2, $repo->id, $reference);
 953  
 954          $result = $fs->search_references($reference);
 955          $this->assertTrue(is_array($result));
 956          $this->assertEquals(count($result), 2);
 957          foreach ($result as $alias) {
 958              $this->assertTrue($alias instanceof stored_file);
 959          }
 960  
 961          $result = $fs->search_references_count($reference);
 962          $this->assertSame($result, 2);
 963  
 964          // The method can't be used for references to files outside the filepool.
 965          $exceptionthrown = false;
 966          try {
 967              $fs->search_references('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
 968          } catch (file_reference_exception $e) {
 969              $exceptionthrown = true;
 970          }
 971          $this->assertTrue($exceptionthrown);
 972  
 973          $exceptionthrown = false;
 974          try {
 975              $fs->search_references_count('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
 976          } catch (file_reference_exception $e) {
 977              $exceptionthrown = true;
 978          }
 979          $this->assertTrue($exceptionthrown);
 980      }
 981  
 982      /**
 983       * Tests for delete_area_files.
 984       *
 985       * @covers ::delete_area_files
 986       */
 987      public function test_delete_area_files() {
 988          $user = $this->setup_three_private_files();
 989          $fs = get_file_storage();
 990  
 991          // Get area files with default options.
 992          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
 993          // Should be the two files we added plus the folder.
 994          $this->assertEquals(4, count($areafiles));
 995          $fs->delete_area_files($user->ctxid, 'user', 'private');
 996  
 997          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
 998          // Should be the two files we added plus the folder.
 999          $this->assertEquals(0, count($areafiles));
1000      }
1001  
1002      /**
1003       * Tests for delete_area_files using an itemid.
1004       *
1005       * @covers ::delete_area_files
1006       */
1007      public function test_delete_area_files_itemid() {
1008          $user = $this->setup_three_private_files();
1009          $fs = get_file_storage();
1010  
1011          // Get area files with default options.
1012          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1013          // Should be the two files we added plus the folder.
1014          $this->assertEquals(4, count($areafiles));
1015          $fs->delete_area_files($user->ctxid, 'user', 'private', 9999);
1016  
1017          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1018          $this->assertEquals(4, count($areafiles));
1019      }
1020  
1021      /**
1022       * Tests for delete_area_files_select.
1023       *
1024       * @covers ::delete_area_files_select
1025       */
1026      public function test_delete_area_files_select() {
1027          $user = $this->setup_three_private_files();
1028          $fs = get_file_storage();
1029  
1030          // Get area files with default options.
1031          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1032          // Should be the two files we added plus the folder.
1033          $this->assertEquals(4, count($areafiles));
1034          $fs->delete_area_files_select($user->ctxid, 'user', 'private', '!= :notitemid', array('notitemid'=>9999));
1035  
1036          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1037          // Should be the two files we added plus the folder.
1038          $this->assertEquals(0, count($areafiles));
1039      }
1040  
1041      /**
1042       * Tests for delete_component_files.
1043       *
1044       * @covers ::delete_component_files
1045       */
1046      public function test_delete_component_files() {
1047          $user = $this->setup_three_private_files();
1048          $fs = get_file_storage();
1049  
1050          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1051          $this->assertEquals(4, count($areafiles));
1052          $fs->delete_component_files('user');
1053          $areafiles = $fs->get_area_files($user->ctxid, 'user', 'private');
1054          $this->assertEquals(0, count($areafiles));
1055      }
1056  
1057      /**
1058       * Tests for create_file_from_url.
1059       *
1060       * @covers ::create_file_from_url
1061       */
1062      public function test_create_file_from_url() {
1063          $this->resetAfterTest(true);
1064  
1065          $syscontext = context_system::instance();
1066          $filerecord = array(
1067              'contextid' => $syscontext->id,
1068              'component' => 'core',
1069              'filearea'  => 'unittest',
1070              'itemid'    => 0,
1071              'filepath'  => '/downloadtest/',
1072          );
1073          $url = $this->getExternalTestFileUrl('/test.html');
1074  
1075          $fs = get_file_storage();
1076  
1077          // Test creating file without filename.
1078          $file1 = $fs->create_file_from_url($filerecord, $url);
1079          $this->assertInstanceOf('stored_file', $file1);
1080  
1081          // Set filename.
1082          $filerecord['filename'] = 'unit-test-filename.html';
1083          $file2 = $fs->create_file_from_url($filerecord, $url);
1084          $this->assertInstanceOf('stored_file', $file2);
1085  
1086          // Use temporary file.
1087          $filerecord['filename'] = 'unit-test-with-temp-file.html';
1088          $file3 = $fs->create_file_from_url($filerecord, $url, null, true);
1089          $file3 = $this->assertInstanceOf('stored_file', $file3);
1090      }
1091  
1092      /**
1093       * Tests for cron.
1094       *
1095       * @covers ::cron
1096       */
1097      public function test_cron() {
1098          $this->resetAfterTest(true);
1099  
1100          // Note: this is only testing DB compatibility atm, rather than
1101          // that work is done.
1102          $fs = get_file_storage();
1103  
1104          $this->expectOutputRegex('/Cleaning up/');
1105          $fs->cron();
1106      }
1107  
1108      /**
1109       * Tests for is_area_empty.
1110       *
1111       * @covers ::is_area_empty
1112       */
1113      public function test_is_area_empty() {
1114          $user = $this->setup_three_private_files();
1115          $fs = get_file_storage();
1116  
1117          $this->assertFalse($fs->is_area_empty($user->ctxid, 'user', 'private'));
1118  
1119          // File area with madeup itemid should be empty.
1120          $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999));
1121          // Still empty with dirs included.
1122          $this->assertTrue($fs->is_area_empty($user->ctxid, 'user', 'private', 9999, false));
1123      }
1124  
1125      /**
1126       * Tests for move_area_files_to_new_context.
1127       *
1128       * @covers ::move_area_files_to_new_context
1129       */
1130      public function test_move_area_files_to_new_context() {
1131          $this->resetAfterTest(true);
1132  
1133          // Create a course with a page resource.
1134          $course = $this->getDataGenerator()->create_course();
1135          $page1 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
1136          $page1context = context_module::instance($page1->cmid);
1137  
1138          // Add a file to the page.
1139          $fs = get_file_storage();
1140          $filerecord = array(
1141              'contextid' => $page1context->id,
1142              'component' => 'mod_page',
1143              'filearea'  => 'content',
1144              'itemid'    => 0,
1145              'filepath'  => '/',
1146              'filename'  => 'unit-test-file.txt',
1147          );
1148  
1149          $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
1150          $this->assertInstanceOf('stored_file', $originalfile);
1151  
1152          $pagefiles = $fs->get_area_files($page1context->id, 'mod_page', 'content', 0, 'sortorder', false);
1153          // Should be one file in filearea.
1154          $this->assertFalse($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
1155  
1156          // Create a new page.
1157          $page2 = $this->getDataGenerator()->create_module('page', array('course'=>$course->id));
1158          $page2context = context_module::instance($page2->cmid);
1159  
1160          // Newly created page area is empty.
1161          $this->assertTrue($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
1162  
1163          // Move the files.
1164          $fs->move_area_files_to_new_context($page1context->id, $page2context->id, 'mod_page', 'content');
1165  
1166          // Page2 filearea should no longer be empty.
1167          $this->assertFalse($fs->is_area_empty($page2context->id, 'mod_page', 'content'));
1168  
1169          // Page1 filearea should now be empty.
1170          $this->assertTrue($fs->is_area_empty($page1context->id, 'mod_page', 'content'));
1171  
1172          $page2files = $fs->get_area_files($page2context->id, 'mod_page', 'content', 0, 'sortorder', false);
1173          $movedfile = reset($page2files);
1174  
1175          // The two files should have the same content hash.
1176          $this->assertEquals($movedfile->get_contenthash(), $originalfile->get_contenthash());
1177      }
1178  
1179      /**
1180       * Tests for convert_image.
1181       *
1182       * @covers ::convert_image
1183       */
1184      public function test_convert_image() {
1185          global $CFG;
1186  
1187          $this->resetAfterTest(false);
1188  
1189          $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1190          $syscontext = context_system::instance();
1191          $filerecord = array(
1192              'contextid' => $syscontext->id,
1193              'component' => 'core',
1194              'filearea'  => 'unittest',
1195              'itemid'    => 0,
1196              'filepath'  => '/images/',
1197              'filename'  => 'testimage.jpg',
1198          );
1199  
1200          $fs = get_file_storage();
1201          $original = $fs->create_file_from_pathname($filerecord, $filepath);
1202  
1203          $filerecord['filename'] = 'testimage-converted-10x10.jpg';
1204          $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
1205          $this->assertInstanceOf('stored_file', $converted);
1206  
1207          $filerecord['filename'] = 'testimage-convereted-nosize.jpg';
1208          $converted = $fs->convert_image($filerecord, $original);
1209          $this->assertInstanceOf('stored_file', $converted);
1210      }
1211  
1212      /**
1213       * Tests for convert_image with a PNG.
1214       *
1215       * @covers ::convert_image
1216       */
1217      public function test_convert_image_png() {
1218          global $CFG;
1219  
1220          $this->resetAfterTest(false);
1221  
1222          $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.png';
1223          $syscontext = context_system::instance();
1224          $filerecord = array(
1225              'contextid' => $syscontext->id,
1226              'component' => 'core',
1227              'filearea'  => 'unittest',
1228              'itemid'    => 0,
1229              'filepath'  => '/images/',
1230              'filename'  => 'testimage.png',
1231          );
1232  
1233          $fs = get_file_storage();
1234          $original = $fs->create_file_from_pathname($filerecord, $filepath);
1235  
1236          // Vanilla test.
1237          $filerecord['filename'] = 'testimage-converted-nosize.png';
1238          $vanilla = $fs->convert_image($filerecord, $original);
1239          $this->assertInstanceOf('stored_file', $vanilla);
1240          // Assert that byte 25 has the ascii value 6 for PNG-24.
1241          $this->assertTrue(ord(substr($vanilla->get_content(), 25, 1)) == 6);
1242  
1243          // 10x10 resize test; also testing for a ridiculous quality setting, which
1244          // we should if necessary scale to the 0 - 9 range.
1245          $filerecord['filename'] = 'testimage-converted-10x10.png';
1246          $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
1247          $this->assertInstanceOf('stored_file', $converted);
1248          // Assert that byte 25 has the ascii value 6 for PNG-24.
1249          $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
1250  
1251          // Transparency test.
1252          $filerecord['filename'] = 'testimage-converted-102x31.png';
1253          $converted = $fs->convert_image($filerecord, $original, 102, 31, true, 9);
1254          $this->assertInstanceOf('stored_file', $converted);
1255          // Assert that byte 25 has the ascii value 6 for PNG-24.
1256          $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
1257  
1258          $originalfile = imagecreatefromstring($original->get_content());
1259          $convertedfile = imagecreatefromstring($converted->get_content());
1260          $vanillafile = imagecreatefromstring($vanilla->get_content());
1261  
1262          $originalcolors = imagecolorsforindex($originalfile, imagecolorat($originalfile, 0, 0));
1263          $convertedcolors = imagecolorsforindex($convertedfile, imagecolorat($convertedfile, 0, 0));
1264          $vanillacolors = imagecolorsforindex($vanillafile, imagecolorat($vanillafile, 0, 0));
1265          $this->assertEquals(count($originalcolors), 4);
1266          $this->assertEquals(count($convertedcolors), 4);
1267          $this->assertEquals(count($vanillacolors), 4);
1268          $this->assertEquals($originalcolors['red'], $convertedcolors['red']);
1269          $this->assertEquals($originalcolors['green'], $convertedcolors['green']);
1270          $this->assertEquals($originalcolors['blue'], $convertedcolors['blue']);
1271          $this->assertEquals($originalcolors['alpha'], $convertedcolors['alpha']);
1272          $this->assertEquals($originalcolors['red'], $vanillacolors['red']);
1273          $this->assertEquals($originalcolors['green'], $vanillacolors['green']);
1274          $this->assertEquals($originalcolors['blue'], $vanillacolors['blue']);
1275          $this->assertEquals($originalcolors['alpha'], $vanillacolors['alpha']);
1276          $this->assertEquals($originalcolors['alpha'], 127);
1277  
1278      }
1279  
1280      private function generate_file_record() {
1281          $syscontext = context_system::instance();
1282          $filerecord = new stdClass();
1283          $filerecord->contextid = $syscontext->id;
1284          $filerecord->component = 'core';
1285          $filerecord->filearea = 'phpunit';
1286          $filerecord->filepath = '/';
1287          $filerecord->filename = 'testfile.txt';
1288          $filerecord->itemid = 0;
1289  
1290          return $filerecord;
1291      }
1292  
1293      /**
1294       * @expectedException        file_exception
1295       * @covers ::create_file_from_storedfile
1296       */
1297      public function test_create_file_from_storedfile_file_invalid() {
1298          $this->resetAfterTest(true);
1299  
1300          $filerecord = $this->generate_file_record();
1301  
1302          $fs = get_file_storage();
1303  
1304          // Create a file from a file id which doesn't exist.
1305          $fs->create_file_from_storedfile($filerecord,  9999);
1306      }
1307  
1308      /**
1309       * @expectedException        file_exception
1310       * @expectedExceptionMessage Invalid contextid
1311       * @covers ::create_file_from_storedfile
1312       */
1313      public function test_create_file_from_storedfile_contextid_invalid() {
1314          $this->resetAfterTest(true);
1315  
1316          $filerecord = $this->generate_file_record();
1317  
1318          $fs = get_file_storage();
1319          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1320          $this->assertInstanceOf('stored_file', $file1);
1321  
1322          $filerecord->filename = 'invalid.txt';
1323          $filerecord->contextid = 'invalid';
1324  
1325          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1326      }
1327  
1328      /**
1329       * @expectedException        file_exception
1330       * @expectedExceptionMessage Invalid component
1331       * @covers ::create_file_from_storedfile
1332       */
1333      public function test_create_file_from_storedfile_component_invalid() {
1334          $this->resetAfterTest(true);
1335  
1336          $filerecord = $this->generate_file_record();
1337  
1338          $fs = get_file_storage();
1339          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1340          $this->assertInstanceOf('stored_file', $file1);
1341  
1342          $filerecord->filename = 'invalid.txt';
1343          $filerecord->component = 'bad/component';
1344  
1345          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1346      }
1347  
1348      /**
1349       * @expectedException        file_exception
1350       * @expectedExceptionMessage Invalid filearea
1351       * @covers ::create_file_from_storedfile
1352       */
1353      public function test_create_file_from_storedfile_filearea_invalid() {
1354          $this->resetAfterTest(true);
1355  
1356          $filerecord = $this->generate_file_record();
1357  
1358          $fs = get_file_storage();
1359          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1360          $this->assertInstanceOf('stored_file', $file1);
1361  
1362          $filerecord->filename = 'invalid.txt';
1363          $filerecord->filearea = 'bad-filearea';
1364  
1365          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1366      }
1367  
1368      /**
1369       * @expectedException        file_exception
1370       * @expectedExceptionMessage Invalid itemid
1371       * @covers ::create_file_from_storedfile
1372       */
1373      public function test_create_file_from_storedfile_itemid_invalid() {
1374          $this->resetAfterTest(true);
1375  
1376          $filerecord = $this->generate_file_record();
1377  
1378          $fs = get_file_storage();
1379          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1380          $this->assertInstanceOf('stored_file', $file1);
1381  
1382          $filerecord->filename = 'invalid.txt';
1383          $filerecord->itemid = 'bad-itemid';
1384  
1385          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1386      }
1387  
1388      /**
1389       * @expectedException        file_exception
1390       * @expectedExceptionMessage Invalid file path
1391       * @covers ::create_file_from_storedfile
1392       */
1393      public function test_create_file_from_storedfile_filepath_invalid() {
1394          $this->resetAfterTest(true);
1395  
1396          $filerecord = $this->generate_file_record();
1397  
1398          $fs = get_file_storage();
1399          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1400          $this->assertInstanceOf('stored_file', $file1);
1401  
1402          $filerecord->filename = 'invalid.txt';
1403          $filerecord->filepath = 'a-/bad/-filepath';
1404  
1405          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1406      }
1407  
1408      /**
1409       * @expectedException        file_exception
1410       * @expectedExceptionMessage Invalid file name
1411       * @covers ::create_file_from_storedfile
1412       */
1413      public function test_create_file_from_storedfile_filename_invalid() {
1414          $this->resetAfterTest(true);
1415  
1416          $filerecord = $this->generate_file_record();
1417  
1418          $fs = get_file_storage();
1419          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1420          $this->assertInstanceOf('stored_file', $file1);
1421  
1422          $filerecord->filename = '';
1423  
1424          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1425      }
1426  
1427      /**
1428       * @expectedException        file_exception
1429       * @expectedExceptionMessage Invalid file timecreated
1430       * @covers ::create_file_from_storedfile
1431       */
1432      public function test_create_file_from_storedfile_timecreated_invalid() {
1433          $this->resetAfterTest(true);
1434  
1435          $filerecord = $this->generate_file_record();
1436  
1437          $fs = get_file_storage();
1438          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1439          $this->assertInstanceOf('stored_file', $file1);
1440  
1441          $filerecord->filename = 'invalid.txt';
1442          $filerecord->timecreated = 'today';
1443  
1444          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1445      }
1446  
1447      /**
1448       * @expectedException        file_exception
1449       * @expectedExceptionMessage Invalid file timemodified
1450       * @covers ::create_file_from_storedfile
1451       */
1452      public function test_create_file_from_storedfile_timemodified_invalid() {
1453          $this->resetAfterTest(true);
1454  
1455          $filerecord = $this->generate_file_record();
1456  
1457          $fs = get_file_storage();
1458          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1459          $this->assertInstanceOf('stored_file', $file1);
1460  
1461          $filerecord->filename = 'invalid.txt';
1462          $filerecord->timemodified  = 'today';
1463  
1464          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1465      }
1466  
1467      /**
1468       * @expectedException        stored_file_creation_exception
1469       * @expectedExceptionMessage Cannot create file 1/core/phpunit/0/testfile.txt
1470       * @covers ::create_file_from_storedfile
1471       */
1472      public function test_create_file_from_storedfile_duplicate() {
1473          $this->resetAfterTest(true);
1474  
1475          $filerecord = $this->generate_file_record();
1476  
1477          $fs = get_file_storage();
1478          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1479          $this->assertInstanceOf('stored_file', $file1);
1480  
1481          // Creating a file validating unique constraint.
1482          $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1483      }
1484  
1485      /**
1486       * Tests for create_file_from_storedfile.
1487       *
1488       * @covers ::create_file_from_storedfile
1489       */
1490      public function test_create_file_from_storedfile() {
1491          $this->resetAfterTest(true);
1492  
1493          $syscontext = context_system::instance();
1494  
1495          $filerecord = new stdClass();
1496          $filerecord->contextid = $syscontext->id;
1497          $filerecord->component = 'core';
1498          $filerecord->filearea = 'phpunit';
1499          $filerecord->filepath = '/';
1500          $filerecord->filename = 'testfile.txt';
1501          $filerecord->itemid = 0;
1502  
1503          $fs = get_file_storage();
1504  
1505          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1506          $this->assertInstanceOf('stored_file', $file1);
1507  
1508          $filerecord->filename = 'test-create-file-from-storedfile.txt';
1509          $file2 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1510          $this->assertInstanceOf('stored_file', $file2);
1511  
1512          // These will be normalised to current time..
1513          $filerecord->timecreated = -100;
1514          $filerecord->timemodified= -100;
1515          $filerecord->filename = 'test-create-file-from-storedfile-bad-dates.txt';
1516  
1517          $file3 = $fs->create_file_from_storedfile($filerecord, $file1->get_id());
1518          $this->assertInstanceOf('stored_file', $file3);
1519  
1520          $this->assertNotEquals($file3->get_timemodified(), $filerecord->timemodified);
1521          $this->assertNotEquals($file3->get_timecreated(), $filerecord->timecreated);
1522      }
1523  
1524      /**
1525       * @expectedException        file_exception
1526       * @expectedExceptionMessage Invalid contextid
1527       * @covers ::create_file_from_string
1528       */
1529      public function test_create_file_from_string_contextid_invalid() {
1530          $this->resetAfterTest(true);
1531  
1532          $filerecord = $this->generate_file_record();
1533          $fs = get_file_storage();
1534  
1535          $filerecord->contextid = 'invalid';
1536  
1537          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1538      }
1539  
1540      /**
1541       * @expectedException        file_exception
1542       * @expectedExceptionMessage Invalid component
1543       * @covers ::create_file_from_string
1544       */
1545      public function test_create_file_from_string_component_invalid() {
1546          $this->resetAfterTest(true);
1547  
1548          $filerecord = $this->generate_file_record();
1549          $fs = get_file_storage();
1550  
1551          $filerecord->component = 'bad/component';
1552  
1553          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1554      }
1555  
1556      /**
1557       * @expectedException        file_exception
1558       * @expectedExceptionMessage Invalid filearea
1559       * @covers ::create_file_from_string
1560       */
1561      public function test_create_file_from_string_filearea_invalid() {
1562          $this->resetAfterTest(true);
1563  
1564          $filerecord = $this->generate_file_record();
1565          $fs = get_file_storage();
1566  
1567          $filerecord->filearea = 'bad-filearea';
1568  
1569          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1570      }
1571  
1572      /**
1573       * @expectedException        file_exception
1574       * @expectedExceptionMessage Invalid itemid
1575       * @covers ::create_file_from_string
1576       */
1577      public function test_create_file_from_string_itemid_invalid() {
1578          $this->resetAfterTest(true);
1579  
1580          $filerecord = $this->generate_file_record();
1581          $fs = get_file_storage();
1582  
1583          $filerecord->itemid = 'bad-itemid';
1584  
1585          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1586      }
1587  
1588      /**
1589       * @expectedException        file_exception
1590       * @expectedExceptionMessage Invalid file path
1591       * @covers ::create_file_from_string
1592       */
1593      public function test_create_file_from_string_filepath_invalid() {
1594          $this->resetAfterTest(true);
1595  
1596          $filerecord = $this->generate_file_record();
1597          $fs = get_file_storage();
1598  
1599          $filerecord->filepath = 'a-/bad/-filepath';
1600  
1601          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1602      }
1603  
1604      /**
1605       * @expectedException        file_exception
1606       * @expectedExceptionMessage Invalid file name
1607       * @covers ::create_file_from_string
1608       */
1609      public function test_create_file_from_string_filename_invalid() {
1610          $this->resetAfterTest(true);
1611  
1612          $filerecord = $this->generate_file_record();
1613          $fs = get_file_storage();
1614  
1615          $filerecord->filename = '';
1616  
1617          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1618      }
1619  
1620      /**
1621       * @expectedException        file_exception
1622       * @expectedExceptionMessage Invalid file timecreated
1623       * @covers ::create_file_from_string
1624       */
1625      public function test_create_file_from_string_timecreated_invalid() {
1626          $this->resetAfterTest(true);
1627  
1628          $filerecord = $this->generate_file_record();
1629          $fs = get_file_storage();
1630  
1631          $filerecord->timecreated = 'today';
1632  
1633          $this->expectException('file_exception');
1634          $this->expectExceptionMessage('Invalid file timecreated');
1635          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1636      }
1637  
1638      /**
1639       * @expectedException        file_exception
1640       * @expectedExceptionMessage Invalid file timemodified
1641       * @covers ::create_file_from_string
1642       */
1643      public function test_create_file_from_string_timemodified_invalid() {
1644          $this->resetAfterTest(true);
1645  
1646          $filerecord = $this->generate_file_record();
1647          $fs = get_file_storage();
1648  
1649          $filerecord->timemodified  = 'today';
1650  
1651          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1652      }
1653  
1654      /**
1655       * Tests for create_file_from_string with a duplicate string.
1656       * @covers ::create_file_from_string
1657       */
1658      public function test_create_file_from_string_duplicate() {
1659          $this->resetAfterTest(true);
1660  
1661          $filerecord = $this->generate_file_record();
1662          $fs = get_file_storage();
1663  
1664          $file1 = $fs->create_file_from_string($filerecord, 'text contents');
1665  
1666          // Creating a file validating unique constraint.
1667          $this->expectException('stored_file_creation_exception');
1668          $file2 = $fs->create_file_from_string($filerecord, 'text contents');
1669      }
1670  
1671      /**
1672       * @expectedException        file_exception
1673       * @expectedExceptionMessage Invalid contextid
1674       * @covers ::create_file_from_pathname
1675       */
1676      public function test_create_file_from_pathname_contextid_invalid() {
1677          global $CFG;
1678          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1679  
1680          $this->resetAfterTest(true);
1681  
1682          $filerecord = $this->generate_file_record();
1683          $fs = get_file_storage();
1684  
1685          $filerecord->contextid = 'invalid';
1686  
1687          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1688      }
1689  
1690      /**
1691       * @expectedException        file_exception
1692       * @expectedExceptionMessage Invalid component
1693       * @covers ::create_file_from_pathname
1694       */
1695      public function test_create_file_from_pathname_component_invalid() {
1696          global $CFG;
1697          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1698  
1699          $this->resetAfterTest(true);
1700  
1701          $filerecord = $this->generate_file_record();
1702          $fs = get_file_storage();
1703  
1704          $filerecord->component = 'bad/component';
1705  
1706          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1707      }
1708  
1709      /**
1710       * @expectedException        file_exception
1711       * @expectedExceptionMessage Invalid filearea
1712       * @covers ::create_file_from_pathname
1713       */
1714      public function test_create_file_from_pathname_filearea_invalid() {
1715          global $CFG;
1716          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1717  
1718          $this->resetAfterTest(true);
1719  
1720          $filerecord = $this->generate_file_record();
1721          $fs = get_file_storage();
1722  
1723          $filerecord->filearea = 'bad-filearea';
1724  
1725          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1726      }
1727  
1728      /**
1729       * @expectedException        file_exception
1730       * @expectedExceptionMessage Invalid itemid
1731       * @covers ::create_file_from_pathname
1732       */
1733      public function test_create_file_from_pathname_itemid_invalid() {
1734          global $CFG;
1735          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1736  
1737          $this->resetAfterTest(true);
1738  
1739          $filerecord = $this->generate_file_record();
1740          $fs = get_file_storage();
1741  
1742          $filerecord->itemid = 'bad-itemid';
1743  
1744           $file1 = $fs->create_file_from_pathname($filerecord, $path);
1745      }
1746  
1747      /**
1748       * @expectedException        file_exception
1749       * @expectedExceptionMessage Invalid file path
1750       * @covers ::create_file_from_pathname
1751       */
1752      public function test_create_file_from_pathname_filepath_invalid() {
1753          global $CFG;
1754          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1755  
1756          $this->resetAfterTest(true);
1757  
1758          $filerecord = $this->generate_file_record();
1759          $fs = get_file_storage();
1760  
1761          $filerecord->filepath = 'a-/bad/-filepath';
1762  
1763          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1764      }
1765  
1766      /**
1767       * @expectedException        file_exception
1768       * @expectedExceptionMessage Invalid file name
1769       * @covers ::create_file_from_pathname
1770       */
1771      public function test_create_file_from_pathname_filename_invalid() {
1772          global $CFG;
1773          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1774  
1775          $this->resetAfterTest(true);
1776  
1777          $filerecord = $this->generate_file_record();
1778          $fs = get_file_storage();
1779  
1780          $filerecord->filename = '';
1781  
1782          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1783      }
1784  
1785      /**
1786       * @expectedException        file_exception
1787       * @expectedExceptionMessage Invalid file timecreated
1788       * @covers ::create_file_from_pathname
1789       */
1790      public function test_create_file_from_pathname_timecreated_invalid() {
1791          global $CFG;
1792          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1793  
1794          $this->resetAfterTest(true);
1795  
1796          $filerecord = $this->generate_file_record();
1797          $fs = get_file_storage();
1798  
1799          $filerecord->timecreated = 'today';
1800  
1801          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1802      }
1803  
1804      /**
1805       * @expectedException        file_exception
1806       * @expectedExceptionMessage Invalid file timemodified
1807       * @covers ::create_file_from_pathname
1808       */
1809      public function test_create_file_from_pathname_timemodified_invalid() {
1810          global $CFG;
1811          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1812  
1813          $this->resetAfterTest(true);
1814  
1815          $filerecord = $this->generate_file_record();
1816          $fs = get_file_storage();
1817  
1818          $filerecord->timemodified  = 'today';
1819  
1820          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1821      }
1822  
1823      /**
1824       * @expectedException        stored_file_creation_exception
1825       * @expectedExceptionMessage Cannot create file 1/core/phpunit/0/testfile.txt
1826       * @covers ::create_file_from_pathname
1827       */
1828      public function test_create_file_from_pathname_duplicate_file() {
1829          global $CFG;
1830          $this->resetAfterTest(true);
1831  
1832          $path = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.jpg';
1833  
1834          $filerecord = $this->generate_file_record();
1835          $fs = get_file_storage();
1836  
1837          $file1 = $fs->create_file_from_pathname($filerecord, $path);
1838          $this->assertInstanceOf('stored_file', $file1);
1839  
1840          // Creating a file validating unique constraint.
1841          $file2 = $fs->create_file_from_pathname($filerecord, $path);
1842      }
1843  
1844      /**
1845       * Calling stored_file::delete_reference() on a non-reference file throws coding_exception
1846       *
1847       * @covers \stored_file::delete_reference
1848       */
1849      public function test_delete_reference_on_nonreference() {
1850  
1851          $this->resetAfterTest(true);
1852          $user = $this->setup_three_private_files();
1853          $fs = get_file_storage();
1854          $repos = repository::get_instances(array('type'=>'user'));
1855          $repo = reset($repos);
1856  
1857          $file = null;
1858          foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1859              if (!$areafile->is_directory()) {
1860                  $file = $areafile;
1861                  break;
1862              }
1863          }
1864          $this->assertInstanceOf('stored_file', $file);
1865          $this->assertFalse($file->is_external_file());
1866  
1867          $this->expectException('coding_exception');
1868          $file->delete_reference();
1869      }
1870  
1871      /**
1872       * Calling stored_file::delete_reference() on a reference file does not affect other
1873       * symlinks to the same original
1874       *
1875       * @covers \stored_file::delete_reference
1876       */
1877      public function test_delete_reference_one_symlink_does_not_rule_them_all() {
1878  
1879          $this->resetAfterTest(true);
1880          $user = $this->setup_three_private_files();
1881          $fs = get_file_storage();
1882          $repos = repository::get_instances(array('type'=>'user'));
1883          $repo = reset($repos);
1884  
1885          // Create two aliases linking the same original.
1886  
1887          $originalfile = null;
1888          foreach ($fs->get_area_files($user->ctxid, 'user', 'private') as $areafile) {
1889              if (!$areafile->is_directory()) {
1890                  $originalfile = $areafile;
1891                  break;
1892              }
1893          }
1894          $this->assertInstanceOf('stored_file', $originalfile);
1895  
1896          // Calling delete_reference() on a non-reference file.
1897  
1898          $originalrecord = array(
1899              'contextid' => $originalfile->get_contextid(),
1900              'component' => $originalfile->get_component(),
1901              'filearea'  => $originalfile->get_filearea(),
1902              'itemid'    => $originalfile->get_itemid(),
1903              'filepath'  => $originalfile->get_filepath(),
1904              'filename'  => $originalfile->get_filename(),
1905          );
1906  
1907          $aliasrecord = $this->generate_file_record();
1908          $aliasrecord->filepath = '/A/';
1909          $aliasrecord->filename = 'symlink.txt';
1910  
1911          $ref = $fs->pack_reference($originalrecord);
1912          $aliasfile1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1913  
1914          $aliasrecord->filepath = '/B/';
1915          $aliasrecord->filename = 'symlink.txt';
1916          $ref = $fs->pack_reference($originalrecord);
1917          $aliasfile2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1918  
1919          // Refetch A/symlink.txt file.
1920          $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1921              $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
1922          $this->assertTrue($symlink1->is_external_file());
1923  
1924          // Unlink the A/symlink.txt file.
1925          $symlink1->delete_reference();
1926          $this->assertFalse($symlink1->is_external_file());
1927  
1928          // Make sure that B/symlink.txt has not been affected.
1929          $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
1930              $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
1931          $this->assertTrue($symlink2->is_external_file());
1932      }
1933  
1934      /**
1935       * Make sure that when internal file is updated all references to it are
1936       * updated immediately. When it is deleted, the references are converted
1937       * to true copies.
1938       */
1939      public function test_update_reference_internal() {
1940          purge_all_caches();
1941          $this->resetAfterTest(true);
1942          $user = $this->setup_three_private_files();
1943          $fs = get_file_storage();
1944          $repos = repository::get_instances(array('type' => 'user'));
1945          $repo = reset($repos);
1946  
1947          // Create two aliases linking the same original.
1948  
1949          $areafiles = array_values($fs->get_area_files($user->ctxid, 'user', 'private', false, 'filename', false));
1950  
1951          $originalfile = $areafiles[0];
1952          $this->assertInstanceOf('stored_file', $originalfile);
1953          $contenthash = $originalfile->get_contenthash();
1954          $filesize = $originalfile->get_filesize();
1955  
1956          $substitutefile = $areafiles[1];
1957          $this->assertInstanceOf('stored_file', $substitutefile);
1958          $newcontenthash = $substitutefile->get_contenthash();
1959          $newfilesize = $substitutefile->get_filesize();
1960  
1961          $originalrecord = array(
1962              'contextid' => $originalfile->get_contextid(),
1963              'component' => $originalfile->get_component(),
1964              'filearea'  => $originalfile->get_filearea(),
1965              'itemid'    => $originalfile->get_itemid(),
1966              'filepath'  => $originalfile->get_filepath(),
1967              'filename'  => $originalfile->get_filename(),
1968          );
1969  
1970          $aliasrecord = $this->generate_file_record();
1971          $aliasrecord->filepath = '/A/';
1972          $aliasrecord->filename = 'symlink.txt';
1973  
1974          $ref = $fs->pack_reference($originalrecord);
1975          $symlink1 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1976          // Make sure created alias is a reference and has the same size and contenthash as source.
1977          $this->assertEquals($contenthash, $symlink1->get_contenthash());
1978          $this->assertEquals($filesize, $symlink1->get_filesize());
1979          $this->assertEquals($repo->id, $symlink1->get_repository_id());
1980          $this->assertNotEmpty($symlink1->get_referencefileid());
1981          $referenceid = $symlink1->get_referencefileid();
1982  
1983          $aliasrecord->filepath = '/B/';
1984          $aliasrecord->filename = 'symlink.txt';
1985          $ref = $fs->pack_reference($originalrecord);
1986          $symlink2 = $fs->create_file_from_reference($aliasrecord, $repo->id, $ref);
1987          // Make sure created alias is a reference and has the same size and contenthash as source.
1988          $this->assertEquals($contenthash, $symlink2->get_contenthash());
1989          $this->assertEquals($filesize, $symlink2->get_filesize());
1990          $this->assertEquals($repo->id, $symlink2->get_repository_id());
1991          // Make sure both aliases have the same reference id.
1992          $this->assertEquals($referenceid, $symlink2->get_referencefileid());
1993  
1994          // Overwrite ofiginal file.
1995          $originalfile->replace_file_with($substitutefile);
1996          $this->assertEquals($newcontenthash, $originalfile->get_contenthash());
1997          $this->assertEquals($newfilesize, $originalfile->get_filesize());
1998  
1999          // References to the internal files must be synchronised immediately.
2000          // Refetch A/symlink.txt file.
2001          $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2002              $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
2003          $this->assertTrue($symlink1->is_external_file());
2004          $this->assertEquals($newcontenthash, $symlink1->get_contenthash());
2005          $this->assertEquals($newfilesize, $symlink1->get_filesize());
2006          $this->assertEquals($repo->id, $symlink1->get_repository_id());
2007          $this->assertEquals($referenceid, $symlink1->get_referencefileid());
2008  
2009          // Refetch B/symlink.txt file.
2010          $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2011              $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
2012          $this->assertTrue($symlink2->is_external_file());
2013          $this->assertEquals($newcontenthash, $symlink2->get_contenthash());
2014          $this->assertEquals($newfilesize, $symlink2->get_filesize());
2015          $this->assertEquals($repo->id, $symlink2->get_repository_id());
2016          $this->assertEquals($referenceid, $symlink2->get_referencefileid());
2017  
2018          // Remove original file.
2019          $originalfile->delete();
2020  
2021          // References must be converted to independend files.
2022          // Refetch A/symlink.txt file.
2023          $symlink1 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2024              $aliasrecord->filearea, $aliasrecord->itemid, '/A/', 'symlink.txt');
2025          $this->assertFalse($symlink1->is_external_file());
2026          $this->assertEquals($newcontenthash, $symlink1->get_contenthash());
2027          $this->assertEquals($newfilesize, $symlink1->get_filesize());
2028          $this->assertNull($symlink1->get_repository_id());
2029          $this->assertNull($symlink1->get_referencefileid());
2030  
2031          // Refetch B/symlink.txt file.
2032          $symlink2 = $fs->get_file($aliasrecord->contextid, $aliasrecord->component,
2033              $aliasrecord->filearea, $aliasrecord->itemid, '/B/', 'symlink.txt');
2034          $this->assertFalse($symlink2->is_external_file());
2035          $this->assertEquals($newcontenthash, $symlink2->get_contenthash());
2036          $this->assertEquals($newfilesize, $symlink2->get_filesize());
2037          $this->assertNull($symlink2->get_repository_id());
2038          $this->assertNull($symlink2->get_referencefileid());
2039      }
2040  
2041      /**
2042       * Tests for get_unused_filename.
2043       *
2044       * @covers ::get_unused_filename
2045       */
2046      public function test_get_unused_filename() {
2047          global $USER;
2048          $this->resetAfterTest(true);
2049  
2050          $fs = get_file_storage();
2051          $this->setAdminUser();
2052          $contextid = context_user::instance($USER->id)->id;
2053          $component = 'user';
2054          $filearea = 'private';
2055          $itemid = 0;
2056          $filepath = '/';
2057  
2058          // Create some private files.
2059          $file = new stdClass;
2060          $file->contextid = $contextid;
2061          $file->component = 'user';
2062          $file->filearea  = 'private';
2063          $file->itemid    = 0;
2064          $file->filepath  = '/';
2065          $file->source    = 'test';
2066          $filenames = array('foo.txt', 'foo (1).txt', 'foo (20).txt', 'foo (999)', 'bar.jpg', 'What (a cool file).jpg',
2067                  'Hurray! (1).php', 'Hurray! (2).php', 'Hurray! (9a).php', 'Hurray! (abc).php');
2068          foreach ($filenames as $key => $filename) {
2069              $file->filename = $filename;
2070              $userfile = $fs->create_file_from_string($file, "file $key $filename content");
2071              $this->assertInstanceOf('stored_file', $userfile);
2072          }
2073  
2074          // Asserting new generated names.
2075          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'unused.txt');
2076          $this->assertEquals('unused.txt', $newfilename);
2077          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo.txt');
2078          $this->assertEquals('foo (21).txt', $newfilename);
2079          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (1).txt');
2080          $this->assertEquals('foo (21).txt', $newfilename);
2081          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (2).txt');
2082          $this->assertEquals('foo (2).txt', $newfilename);
2083          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (20).txt');
2084          $this->assertEquals('foo (21).txt', $newfilename);
2085          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo');
2086          $this->assertEquals('foo', $newfilename);
2087          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (123)');
2088          $this->assertEquals('foo (123)', $newfilename);
2089          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'foo (999)');
2090          $this->assertEquals('foo (1000)', $newfilename);
2091          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.png');
2092          $this->assertEquals('bar.png', $newfilename);
2093          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (12).png');
2094          $this->assertEquals('bar (12).png', $newfilename);
2095          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar.jpg');
2096          $this->assertEquals('bar (1).jpg', $newfilename);
2097          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'bar (1).jpg');
2098          $this->assertEquals('bar (1).jpg', $newfilename);
2099          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'What (a cool file).jpg');
2100          $this->assertEquals('What (a cool file) (1).jpg', $newfilename);
2101          $newfilename = $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, 'Hurray! (1).php');
2102          $this->assertEquals('Hurray! (3).php', $newfilename);
2103  
2104          $this->expectException('coding_exception');
2105          $fs->get_unused_filename($contextid, $component, $filearea, $itemid, $filepath, '');
2106      }
2107  
2108      /**
2109       * Test that mimetype_from_file returns appropriate output when the
2110       * file could not be found.
2111       *
2112       * @covers ::mimetype
2113       */
2114      public function test_mimetype_not_found() {
2115          $mimetype = file_storage::mimetype('/path/to/nonexistent/file');
2116          $this->assertEquals('document/unknown', $mimetype);
2117      }
2118  
2119      /**
2120       * Test that mimetype_from_file returns appropriate output for a known
2121       * file.
2122       *
2123       * Note: this is not intended to check that functions outside of this
2124       * file works. It is intended to validate the codepath contains no
2125       * errors and behaves as expected.
2126       *
2127       * @covers ::mimetype
2128       */
2129      public function test_mimetype_known() {
2130          $filepath = __DIR__ . '/fixtures/testimage.jpg';
2131          $mimetype = file_storage::mimetype_from_file($filepath);
2132          $this->assertEquals('image/jpeg', $mimetype);
2133      }
2134  
2135      /**
2136       * Test that mimetype_from_file returns appropriate output when the
2137       * file could not be found.
2138       *
2139       * @covers ::mimetype
2140       */
2141      public function test_mimetype_from_file_not_found() {
2142          $mimetype = file_storage::mimetype_from_file('/path/to/nonexistent/file');
2143          $this->assertEquals('document/unknown', $mimetype);
2144      }
2145  
2146      /**
2147       * Test that mimetype_from_file returns appropriate output for a known
2148       * file.
2149       *
2150       * Note: this is not intended to check that functions outside of this
2151       * file works. It is intended to validate the codepath contains no
2152       * errors and behaves as expected.
2153       *
2154       * @covers ::mimetype
2155       */
2156      public function test_mimetype_from_file_known() {
2157          $filepath = __DIR__ . '/fixtures/testimage.jpg';
2158          $mimetype = file_storage::mimetype_from_file($filepath);
2159          $this->assertEquals('image/jpeg', $mimetype);
2160      }
2161  
2162  }
2163  
2164  class test_stored_file_inspection extends stored_file {
2165      public static function get_pretected_pathname(stored_file $file) {
2166          return $file->get_pathname_by_contenthash();
2167      }
2168  }