| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * This provides SimpleTests for the core file handling functionality. 6 * These include FileValidateTest and FileSaveTest. 7 */ 8 9 /** 10 * Helper validator that returns the $errors parameter. 11 */ 12 function file_test_validator($file, $errors) { 13 return $errors; 14 } 15 16 /** 17 * Helper function for testing file_scan_directory(). 18 * 19 * Each time the function is called the file is stored in a static variable. 20 * When the function is called with no $filepath parameter, the results are 21 * returned. 22 * 23 * @param $filepath 24 * File path 25 * @return 26 * If $filepath is NULL, an array of all previous $filepath parameters 27 */ 28 function file_test_file_scan_callback($filepath = NULL) { 29 $files = &drupal_static(__FUNCTION__, array()); 30 if (isset($filepath)) { 31 $files[] = $filepath; 32 } 33 else { 34 return $files; 35 } 36 } 37 38 /** 39 * Reset static variables used by file_test_file_scan_callback(). 40 */ 41 function file_test_file_scan_callback_reset() { 42 drupal_static_reset('file_test_file_scan_callback'); 43 } 44 45 /** 46 * Base class for file tests that adds some additional file specific 47 * assertions and helper functions. 48 */ 49 class FileTestCase extends DrupalWebTestCase { 50 /** 51 * Check that two files have the same values for all fields other than the 52 * timestamp. 53 * 54 * @param $before 55 * File object to compare. 56 * @param $after 57 * File object to compare. 58 */ 59 function assertFileUnchanged($before, $after) { 60 $this->assertEqual($before->fid, $after->fid, t('File id is the same: %file1 == %file2.', array('%file1' => $before->fid, '%file2' => $after->fid)), 'File unchanged'); 61 $this->assertEqual($before->uid, $after->uid, t('File owner is the same: %file1 == %file2.', array('%file1' => $before->uid, '%file2' => $after->uid)), 'File unchanged'); 62 $this->assertEqual($before->filename, $after->filename, t('File name is the same: %file1 == %file2.', array('%file1' => $before->filename, '%file2' => $after->filename)), 'File unchanged'); 63 $this->assertEqual($before->uri, $after->uri, t('File path is the same: %file1 == %file2.', array('%file1' => $before->uri, '%file2' => $after->uri)), 'File unchanged'); 64 $this->assertEqual($before->filemime, $after->filemime, t('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->filemime, '%file2' => $after->filemime)), 'File unchanged'); 65 $this->assertEqual($before->filesize, $after->filesize, t('File size is the same: %file1 == %file2.', array('%file1' => $before->filesize, '%file2' => $after->filesize)), 'File unchanged'); 66 $this->assertEqual($before->status, $after->status, t('File status is the same: %file1 == %file2.', array('%file1' => $before->status, '%file2' => $after->status)), 'File unchanged'); 67 } 68 69 /** 70 * Check that two files are not the same by comparing the fid and filepath. 71 * 72 * @param $file1 73 * File object to compare. 74 * @param $file2 75 * File object to compare. 76 */ 77 function assertDifferentFile($file1, $file2) { 78 $this->assertNotEqual($file1->fid, $file2->fid, t('Files have different ids: %file1 != %file2.', array('%file1' => $file1->fid, '%file2' => $file2->fid)), 'Different file'); 79 $this->assertNotEqual($file1->uri, $file2->uri, t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Different file'); 80 } 81 82 /** 83 * Check that two files are the same by comparing the fid and filepath. 84 * 85 * @param $file1 86 * File object to compare. 87 * @param $file2 88 * File object to compare. 89 */ 90 function assertSameFile($file1, $file2) { 91 $this->assertEqual($file1->fid, $file2->fid, t('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->fid, '%file2-fid' => $file2->fid)), 'Same file'); 92 $this->assertEqual($file1->uri, $file2->uri, t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Same file'); 93 } 94 95 /** 96 * Helper function to test the permissions of a file. 97 * 98 * @param $filepath 99 * String file path. 100 * @param $expected_mode 101 * Octal integer like 0664 or 0777. 102 * @param $message 103 * Optional message. 104 */ 105 function assertFilePermissions($filepath, $expected_mode, $message = NULL) { 106 // Clear out PHP's file stat cache to be sure we see the current value. 107 clearstatcache(); 108 109 // Mask out all but the last three octets. 110 $actual_mode = fileperms($filepath) & 0777; 111 112 // PHP on Windows has limited support for file permissions. Usually each of 113 // "user", "group" and "other" use one octal digit (3 bits) to represent the 114 // read/write/execute bits. On Windows, chmod() ignores the "group" and 115 // "other" bits, and fileperms() returns the "user" bits in all three 116 // positions. $expected_mode is updated to reflect this. 117 if (substr(PHP_OS, 0, 3) == 'WIN') { 118 // Reset the "group" and "other" bits. 119 $expected_mode = $expected_mode & 0700; 120 // Shift the "user" bits to the "group" and "other" positions also. 121 $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6; 122 } 123 124 if (!isset($message)) { 125 $message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode))); 126 } 127 $this->assertEqual($actual_mode, $expected_mode, $message); 128 } 129 130 /** 131 * Helper function to test the permissions of a directory. 132 * 133 * @param $directory 134 * String directory path. 135 * @param $expected_mode 136 * Octal integer like 0664 or 0777. 137 * @param $message 138 * Optional message. 139 */ 140 function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) { 141 // Clear out PHP's file stat cache to be sure we see the current value. 142 clearstatcache(); 143 144 // Mask out all but the last three octets. 145 $actual_mode = fileperms($directory) & 0777; 146 147 // PHP on Windows has limited support for file permissions. Usually each of 148 // "user", "group" and "other" use one octal digit (3 bits) to represent the 149 // read/write/execute bits. On Windows, chmod() ignores the "group" and 150 // "other" bits, and fileperms() returns the "user" bits in all three 151 // positions. $expected_mode is updated to reflect this. 152 if (substr(PHP_OS, 0, 3) == 'WIN') { 153 // Reset the "group" and "other" bits. 154 $expected_mode = $expected_mode & 0700; 155 // Shift the "user" bits to the "group" and "other" positions also. 156 $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6; 157 } 158 159 if (!isset($message)) { 160 $message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode))); 161 } 162 $this->assertEqual($actual_mode, $expected_mode, $message); 163 } 164 165 /** 166 * Create a directory and assert it exists. 167 * 168 * @param $path 169 * Optional string with a directory path. If none is provided, a random 170 * name in the site's files directory will be used. 171 * @return 172 * The path to the directory. 173 */ 174 function createDirectory($path = NULL) { 175 // A directory to operate on. 176 if (!isset($path)) { 177 $path = file_default_scheme() . '://' . $this->randomName(); 178 } 179 $this->assertTrue(drupal_mkdir($path) && is_dir($path), t('Directory was created successfully.')); 180 return $path; 181 } 182 183 /** 184 * Create a file and save it to the files table and assert that it occurs 185 * correctly. 186 * 187 * @param $filepath 188 * Optional string specifying the file path. If none is provided then a 189 * randomly named file will be created in the site's files directory. 190 * @param $contents 191 * Optional contents to save into the file. If a NULL value is provided an 192 * arbitrary string will be used. 193 * @param $scheme 194 * Optional string indicating the stream scheme to use. Drupal core includes 195 * public, private, and temporary. The public wrapper is the default. 196 * @return 197 * File object. 198 */ 199 function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) { 200 if (!isset($filepath)) { 201 // Prefix with non-latin characters to ensure that all file-related 202 // tests work with international filenames. 203 $filepath = 'Файл для тестирования ' . $this->randomName(); 204 } 205 if (!isset($scheme)) { 206 $scheme = file_default_scheme(); 207 } 208 $filepath = $scheme . '://' . $filepath; 209 210 if (!isset($contents)) { 211 $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data."; 212 } 213 214 file_put_contents($filepath, $contents); 215 $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file'); 216 217 $file = new stdClass(); 218 $file->uri = $filepath; 219 $file->filename = drupal_basename($file->uri); 220 $file->filemime = 'text/plain'; 221 $file->uid = 1; 222 $file->timestamp = REQUEST_TIME; 223 $file->filesize = filesize($file->uri); 224 $file->status = 0; 225 // Write the record directly rather than calling file_save() so we don't 226 // invoke the hooks. 227 $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, t('The file was added to the database.'), 'Create test file'); 228 229 return $file; 230 } 231 } 232 233 /** 234 * Base class for file tests that use the file_test module to test uploads and 235 * hooks. 236 */ 237 class FileHookTestCase extends FileTestCase { 238 function setUp() { 239 // Install file_test module 240 parent::setUp('file_test'); 241 // Clear out any hook calls. 242 file_test_reset(); 243 } 244 245 /** 246 * Assert that all of the specified hook_file_* hooks were called once, other 247 * values result in failure. 248 * 249 * @param $expected 250 * Array with string containing with the hook name, e.g. 'load', 'save', 251 * 'insert', etc. 252 */ 253 function assertFileHooksCalled($expected) { 254 // Determine which hooks were called. 255 $actual = array_keys(array_filter(file_test_get_all_calls())); 256 257 // Determine if there were any expected that were not called. 258 $uncalled = array_diff($expected, $actual); 259 if (count($uncalled)) { 260 $this->assertTrue(FALSE, t('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)))); 261 } 262 else { 263 $this->assertTrue(TRUE, t('All the expected hooks were called: %expected', array('%expected' => empty($expected) ? t('(none)') : implode(', ', $expected)))); 264 } 265 266 // Determine if there were any unexpected calls. 267 $unexpected = array_diff($actual, $expected); 268 if (count($unexpected)) { 269 $this->assertTrue(FALSE, t('Unexpected hooks were called: %unexpected.', array('%unexpected' => empty($unexpected) ? t('(none)') : implode(', ', $unexpected)))); 270 } 271 else { 272 $this->assertTrue(TRUE, t('No unexpected hooks were called.')); 273 } 274 } 275 276 /** 277 * Assert that a hook_file_* hook was called a certain number of times. 278 * 279 * @param $hook 280 * String with the hook name, e.g. 'load', 'save', 'insert', etc. 281 * @param $expected_count 282 * Optional integer count. 283 * @param $message 284 * Optional translated string message. 285 */ 286 function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) { 287 $actual_count = count(file_test_get_calls($hook)); 288 289 if (!isset($message)) { 290 if ($actual_count == $expected_count) { 291 $message = t('hook_file_@name was called correctly.', array('@name' => $hook)); 292 } 293 elseif ($expected_count == 0) { 294 $message = format_plural($actual_count, 'hook_file_@name was not expected to be called but was actually called once.', 'hook_file_@name was not expected to be called but was actually called @count times.', array('@name' => $hook, '@count' => $actual_count)); 295 } 296 else { 297 $message = t('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count)); 298 } 299 } 300 $this->assertEqual($actual_count, $expected_count, $message); 301 } 302 } 303 304 305 /** 306 * This will run tests against the file_space_used() function. 307 */ 308 class FileSpaceUsedTest extends FileTestCase { 309 public static function getInfo() { 310 return array( 311 'name' => 'File space used tests', 312 'description' => 'Tests the file_space_used() function.', 313 'group' => 'File API', 314 ); 315 } 316 317 function setUp() { 318 parent::setUp(); 319 320 // Create records for a couple of users with different sizes. 321 $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT); 322 drupal_write_record('file_managed', $file); 323 $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT); 324 drupal_write_record('file_managed', $file); 325 $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT); 326 drupal_write_record('file_managed', $file); 327 $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT); 328 drupal_write_record('file_managed', $file); 329 330 // Now create some non-permanent files. 331 $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0); 332 drupal_write_record('file_managed', $file); 333 $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0); 334 drupal_write_record('file_managed', $file); 335 } 336 337 /** 338 * Test different users with the default status. 339 */ 340 function testFileSpaceUsed() { 341 // Test different users with default status. 342 $this->assertEqual(file_space_used(2), 70); 343 $this->assertEqual(file_space_used(3), 300); 344 $this->assertEqual(file_space_used(), 370); 345 346 // Test the status fields 347 $this->assertEqual(file_space_used(NULL, 0), 4); 348 $this->assertEqual(file_space_used(NULL, FILE_STATUS_PERMANENT), 370); 349 350 // Test both the user and status. 351 $this->assertEqual(file_space_used(1, 0), 0); 352 $this->assertEqual(file_space_used(1, FILE_STATUS_PERMANENT), 0); 353 $this->assertEqual(file_space_used(2, 0), 1); 354 $this->assertEqual(file_space_used(2, FILE_STATUS_PERMANENT), 70); 355 $this->assertEqual(file_space_used(3, 0), 3); 356 $this->assertEqual(file_space_used(3, FILE_STATUS_PERMANENT), 300); 357 } 358 } 359 360 /** 361 * This will run tests against the file validation functions (file_validate_*). 362 */ 363 class FileValidatorTest extends DrupalWebTestCase { 364 public static function getInfo() { 365 return array( 366 'name' => 'File validator tests', 367 'description' => 'Tests the functions used to validate uploaded files.', 368 'group' => 'File API', 369 ); 370 } 371 372 function setUp() { 373 parent::setUp(); 374 375 $this->image = new stdClass(); 376 $this->image->uri = 'misc/druplicon.png'; 377 $this->image->filename = drupal_basename($this->image->uri); 378 379 $this->non_image = new stdClass(); 380 $this->non_image->uri = 'misc/jquery.js'; 381 $this->non_image->filename = drupal_basename($this->non_image->uri); 382 } 383 384 /** 385 * Test the file_validate_extensions() function. 386 */ 387 function testFileValidateExtensions() { 388 $file = new stdClass(); 389 $file->filename = 'asdf.txt'; 390 $errors = file_validate_extensions($file, 'asdf txt pork'); 391 $this->assertEqual(count($errors), 0, t('Valid extension accepted.'), 'File'); 392 393 $file->filename = 'asdf.txt'; 394 $errors = file_validate_extensions($file, 'exe png'); 395 $this->assertEqual(count($errors), 1, t('Invalid extension blocked.'), 'File'); 396 } 397 398 /** 399 * This ensures a specific file is actually an image. 400 */ 401 function testFileValidateIsImage() { 402 $this->assertTrue(file_exists($this->image->uri), t('The image being tested exists.'), 'File'); 403 $errors = file_validate_is_image($this->image); 404 $this->assertEqual(count($errors), 0, t('No error reported for our image file.'), 'File'); 405 406 $this->assertTrue(file_exists($this->non_image->uri), t('The non-image being tested exists.'), 'File'); 407 $errors = file_validate_is_image($this->non_image); 408 $this->assertEqual(count($errors), 1, t('An error reported for our non-image file.'), 'File'); 409 } 410 411 /** 412 * This ensures the resolution of a specific file is within bounds. 413 * The image will be resized if it's too large. 414 */ 415 function testFileValidateImageResolution() { 416 // Non-images. 417 $errors = file_validate_image_resolution($this->non_image); 418 $this->assertEqual(count($errors), 0, t("Shouldn't get any errors for a non-image file."), 'File'); 419 $errors = file_validate_image_resolution($this->non_image, '50x50', '100x100'); 420 $this->assertEqual(count($errors), 0, t("Don't check the resolution on non files."), 'File'); 421 422 // Minimum size. 423 $errors = file_validate_image_resolution($this->image); 424 $this->assertEqual(count($errors), 0, t('No errors for an image when there is no minimum or maximum resolution.'), 'File'); 425 $errors = file_validate_image_resolution($this->image, 0, '200x1'); 426 $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't wide enough."), 'File'); 427 $errors = file_validate_image_resolution($this->image, 0, '1x200'); 428 $this->assertEqual(count($errors), 1, t("Got an error for an image that wasn't tall enough."), 'File'); 429 $errors = file_validate_image_resolution($this->image, 0, '200x200'); 430 $this->assertEqual(count($errors), 1, t('Small images report an error.'), 'File'); 431 432 // Maximum size. 433 if (image_get_toolkit()) { 434 // Copy the image so that the original doesn't get resized. 435 copy('misc/druplicon.png', 'temporary://druplicon.png'); 436 $this->image->uri = 'temporary://druplicon.png'; 437 438 $errors = file_validate_image_resolution($this->image, '10x5'); 439 $this->assertEqual(count($errors), 0, t('No errors should be reported when an oversized image can be scaled down.'), 'File'); 440 441 $info = image_get_info($this->image->uri); 442 $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File'); 443 $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File'); 444 445 drupal_unlink('temporary://druplicon.png'); 446 } 447 else { 448 // TODO: should check that the error is returned if no toolkit is available. 449 $errors = file_validate_image_resolution($this->image, '5x10'); 450 $this->assertEqual(count($errors), 1, t("Oversize images that can't be scaled get an error."), 'File'); 451 } 452 } 453 454 /** 455 * This will ensure the filename length is valid. 456 */ 457 function testFileValidateNameLength() { 458 // Create a new file object. 459 $file = new stdClass(); 460 461 // Add a filename with an allowed length and test it. 462 $file->filename = str_repeat('x', 240); 463 $this->assertEqual(strlen($file->filename), 240); 464 $errors = file_validate_name_length($file); 465 $this->assertEqual(count($errors), 0, t('No errors reported for 240 length filename.'), 'File'); 466 467 // Add a filename with a length too long and test it. 468 $file->filename = str_repeat('x', 241); 469 $errors = file_validate_name_length($file); 470 $this->assertEqual(count($errors), 1, t('An error reported for 241 length filename.'), 'File'); 471 472 // Add a filename with an empty string and test it. 473 $file->filename = ''; 474 $errors = file_validate_name_length($file); 475 $this->assertEqual(count($errors), 1, t('An error reported for 0 length filename.'), 'File'); 476 } 477 478 479 /** 480 * Test file_validate_size(). 481 */ 482 function testFileValidateSize() { 483 global $user; 484 $original_user = $user; 485 drupal_save_session(FALSE); 486 487 // Run these test as uid = 1. 488 $user = user_load(1); 489 490 $file = new stdClass(); 491 $file->filesize = 999999; 492 $errors = file_validate_size($file, 1, 1); 493 $this->assertEqual(count($errors), 0, t('No size limits enforced on uid=1.'), 'File'); 494 495 // Run these tests as a regular user. 496 $user = $this->drupalCreateUser(); 497 498 // Create a file with a size of 1000 bytes, and quotas of only 1 byte. 499 $file = new stdClass(); 500 $file->filesize = 1000; 501 $errors = file_validate_size($file, 0, 0); 502 $this->assertEqual(count($errors), 0, t('No limits means no errors.'), 'File'); 503 $errors = file_validate_size($file, 1, 0); 504 $this->assertEqual(count($errors), 1, t('Error for the file being over the limit.'), 'File'); 505 $errors = file_validate_size($file, 0, 1); 506 $this->assertEqual(count($errors), 1, t('Error for the user being over their limit.'), 'File'); 507 $errors = file_validate_size($file, 1, 1); 508 $this->assertEqual(count($errors), 2, t('Errors for both the file and their limit.'), 'File'); 509 510 $user = $original_user; 511 drupal_save_session(TRUE); 512 } 513 } 514 515 516 517 /** 518 * Tests the file_unmanaged_save_data() function. 519 */ 520 class FileUnmanagedSaveDataTest extends FileTestCase { 521 public static function getInfo() { 522 return array( 523 'name' => 'Unmanaged file save data', 524 'description' => 'Tests the unmanaged file save data function.', 525 'group' => 'File API', 526 ); 527 } 528 529 /** 530 * Test the file_unmanaged_save_data() function. 531 */ 532 function testFileSaveData() { 533 $contents = $this->randomName(8); 534 535 // No filename. 536 $filepath = file_unmanaged_save_data($contents); 537 $this->assertTrue($filepath, t('Unnamed file saved correctly.')); 538 $this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), t("File was placed in Drupal's files directory.")); 539 $this->assertEqual($contents, file_get_contents($filepath), t('Contents of the file are correct.')); 540 541 // Provide a filename. 542 $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE); 543 $this->assertTrue($filepath, t('Unnamed file saved correctly.')); 544 $this->assertEqual('asdf.txt', drupal_basename($filepath), t('File was named correctly.')); 545 $this->assertEqual($contents, file_get_contents($filepath), t('Contents of the file are correct.')); 546 $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664)); 547 } 548 } 549 550 /** 551 * Tests the file_unmanaged_save_data() function on remote filesystems. 552 */ 553 class RemoteFileUnmanagedSaveDataTest extends FileUnmanagedSaveDataTest { 554 public static function getInfo() { 555 $info = parent::getInfo(); 556 $info['group'] = 'File API (remote)'; 557 return $info; 558 } 559 560 function setUp() { 561 parent::setUp('file_test'); 562 variable_set('file_default_scheme', 'dummy-remote'); 563 } 564 } 565 566 /** 567 * Test the file_save_upload() function. 568 */ 569 class FileSaveUploadTest extends FileHookTestCase { 570 /** 571 * An image file path for uploading. 572 */ 573 protected $image; 574 575 /** 576 * A PHP file path for upload security testing. 577 */ 578 protected $phpfile; 579 580 /** 581 * The largest file id when the test starts. 582 */ 583 protected $maxFidBefore; 584 585 public static function getInfo() { 586 return array( 587 'name' => 'File uploading', 588 'description' => 'Tests the file uploading functions.', 589 'group' => 'File API', 590 ); 591 } 592 593 function setUp() { 594 parent::setUp(); 595 $account = $this->drupalCreateUser(array('access content')); 596 $this->drupalLogin($account); 597 598 $image_files = $this->drupalGetTestFiles('image'); 599 $this->image = current($image_files); 600 601 list(, $this->image_extension) = explode('.', $this->image->filename); 602 $this->assertTrue(is_file($this->image->uri), t("The image file we're going to upload exists.")); 603 604 $this->phpfile = current($this->drupalGetTestFiles('php')); 605 $this->assertTrue(is_file($this->phpfile->uri), t("The PHP file we're going to upload exists.")); 606 607 $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); 608 609 // Upload with replace to guarantee there's something there. 610 $edit = array( 611 'file_test_replace' => FILE_EXISTS_REPLACE, 612 'files[file_test_upload]' => drupal_realpath($this->image->uri), 613 ); 614 $this->drupalPost('file-test/upload', $edit, t('Submit')); 615 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 616 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 617 618 // Check that the correct hooks were called then clean out the hook 619 // counters. 620 $this->assertFileHooksCalled(array('validate', 'insert')); 621 file_test_reset(); 622 } 623 624 /** 625 * Test the file_save_upload() function. 626 */ 627 function testNormal() { 628 $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); 629 $this->assertTrue($max_fid_after > $this->maxFidBefore, t('A new file was created.')); 630 $file1 = file_load($max_fid_after); 631 $this->assertTrue($file1, t('Loaded the file.')); 632 // MIME type of the uploaded image may be either image/jpeg or image/png. 633 $this->assertEqual(substr($file1->filemime, 0, 5), 'image', 'A MIME type was set.'); 634 635 // Reset the hook counters to get rid of the 'load' we just called. 636 file_test_reset(); 637 638 // Upload a second file. 639 $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); 640 $image2 = current($this->drupalGetTestFiles('image')); 641 $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri)); 642 $this->drupalPost('file-test/upload', $edit, t('Submit')); 643 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 644 $this->assertRaw(t('You WIN!')); 645 $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField(); 646 647 // Check that the correct hooks were called. 648 $this->assertFileHooksCalled(array('validate', 'insert')); 649 650 $file2 = file_load($max_fid_after); 651 $this->assertTrue($file2); 652 // MIME type of the uploaded image may be either image/jpeg or image/png. 653 $this->assertEqual(substr($file2->filemime, 0, 5), 'image', 'A MIME type was set.'); 654 655 // Load both files using file_load_multiple(). 656 $files = file_load_multiple(array($file1->fid, $file2->fid)); 657 $this->assertTrue(isset($files[$file1->fid]), t('File was loaded successfully')); 658 $this->assertTrue(isset($files[$file2->fid]), t('File was loaded successfully')); 659 660 // Upload a third file to a subdirectory. 661 $image3 = current($this->drupalGetTestFiles('image')); 662 $image3_realpath = drupal_realpath($image3->uri); 663 $dir = $this->randomName(); 664 $edit = array( 665 'files[file_test_upload]' => $image3_realpath, 666 'file_subdir' => $dir, 667 ); 668 $this->drupalPost('file-test/upload', $edit, t('Submit')); 669 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 670 $this->assertRaw(t('You WIN!')); 671 $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(drupal_basename($image3_realpath)))); 672 673 // Check that file_load_multiple() with no arguments returns FALSE. 674 $this->assertFalse(file_load_multiple(), t('No files were loaded.')); 675 } 676 677 /** 678 * Test extension handling. 679 */ 680 function testHandleExtension() { 681 // The file being tested is a .gif which is in the default safe list 682 // of extensions to allow when the extension validator isn't used. This is 683 // implicitly tested at the testNormal() test. Here we tell 684 // file_save_upload() to only allow ".foo". 685 $extensions = 'foo'; 686 $edit = array( 687 'file_test_replace' => FILE_EXISTS_REPLACE, 688 'files[file_test_upload]' => drupal_realpath($this->image->uri), 689 'extensions' => $extensions, 690 ); 691 692 $this->drupalPost('file-test/upload', $edit, t('Submit')); 693 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 694 $message = t('Only files with the following extensions are allowed:') . ' <em class="placeholder">' . $extensions . '</em>'; 695 $this->assertRaw($message, t('Can\'t upload a disallowed extension')); 696 $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.')); 697 698 // Check that the correct hooks were called. 699 $this->assertFileHooksCalled(array('validate')); 700 701 // Reset the hook counters. 702 file_test_reset(); 703 704 $extensions = 'foo ' . $this->image_extension; 705 // Now tell file_save_upload() to allow the extension of our test image. 706 $edit = array( 707 'file_test_replace' => FILE_EXISTS_REPLACE, 708 'files[file_test_upload]' => drupal_realpath($this->image->uri), 709 'extensions' => $extensions, 710 ); 711 712 $this->drupalPost('file-test/upload', $edit, t('Submit')); 713 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 714 $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload an allowed extension.')); 715 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 716 717 // Check that the correct hooks were called. 718 $this->assertFileHooksCalled(array('validate', 'load', 'update')); 719 720 // Reset the hook counters. 721 file_test_reset(); 722 723 // Now tell file_save_upload() to allow any extension. 724 $edit = array( 725 'file_test_replace' => FILE_EXISTS_REPLACE, 726 'files[file_test_upload]' => drupal_realpath($this->image->uri), 727 'allow_all_extensions' => TRUE, 728 ); 729 $this->drupalPost('file-test/upload', $edit, t('Submit')); 730 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 731 $this->assertNoRaw(t('Only files with the following extensions are allowed:'), t('Can upload any extension.')); 732 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 733 734 // Check that the correct hooks were called. 735 $this->assertFileHooksCalled(array('validate', 'load', 'update')); 736 } 737 738 /** 739 * Test dangerous file handling. 740 */ 741 function testHandleDangerousFile() { 742 // Allow the .php extension and make sure it gets renamed to .txt for 743 // safety. Also check to make sure its MIME type was changed. 744 $edit = array( 745 'file_test_replace' => FILE_EXISTS_REPLACE, 746 'files[file_test_upload]' => drupal_realpath($this->phpfile->uri), 747 'is_image_file' => FALSE, 748 'extensions' => 'php', 749 ); 750 751 $this->drupalPost('file-test/upload', $edit, t('Submit')); 752 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 753 $message = t('For security reasons, your upload has been renamed to') . ' <em class="placeholder">' . $this->phpfile->filename . '.txt' . '</em>'; 754 $this->assertRaw($message, t('Dangerous file was renamed.')); 755 $this->assertRaw(t('File MIME type is text/plain.'), t('Dangerous file\'s MIME type was changed.')); 756 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 757 758 // Check that the correct hooks were called. 759 $this->assertFileHooksCalled(array('validate', 'insert')); 760 761 // Ensure dangerous files are not renamed when insecure uploads is TRUE. 762 // Turn on insecure uploads. 763 variable_set('allow_insecure_uploads', 1); 764 // Reset the hook counters. 765 file_test_reset(); 766 767 $this->drupalPost('file-test/upload', $edit, t('Submit')); 768 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 769 $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.')); 770 $this->assertRaw(t('File name is !filename', array('!filename' => $this->phpfile->filename)), t('Dangerous file was not renamed when insecure uploads is TRUE.')); 771 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 772 773 // Check that the correct hooks were called. 774 $this->assertFileHooksCalled(array('validate', 'insert')); 775 776 // Turn off insecure uploads. 777 variable_set('allow_insecure_uploads', 0); 778 } 779 780 /** 781 * Test file munge handling. 782 */ 783 function testHandleFileMunge() { 784 // Ensure insecure uploads are disabled for this test. 785 variable_set('allow_insecure_uploads', 0); 786 $this->image = file_move($this->image, $this->image->uri . '.foo.' . $this->image_extension); 787 788 // Reset the hook counters to get rid of the 'move' we just called. 789 file_test_reset(); 790 791 $extensions = $this->image_extension; 792 $edit = array( 793 'files[file_test_upload]' => drupal_realpath($this->image->uri), 794 'extensions' => $extensions, 795 ); 796 797 $munged_filename = $this->image->filename; 798 $munged_filename = substr($munged_filename, 0, strrpos($munged_filename, '.')); 799 $munged_filename .= '_.' . $this->image_extension; 800 801 $this->drupalPost('file-test/upload', $edit, t('Submit')); 802 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 803 $this->assertRaw(t('For security reasons, your upload has been renamed'), t('Found security message.')); 804 $this->assertRaw(t('File name is !filename', array('!filename' => $munged_filename)), t('File was successfully munged.')); 805 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 806 807 // Check that the correct hooks were called. 808 $this->assertFileHooksCalled(array('validate', 'insert')); 809 810 // Ensure we don't munge files if we're allowing any extension. 811 // Reset the hook counters. 812 file_test_reset(); 813 814 $edit = array( 815 'files[file_test_upload]' => drupal_realpath($this->image->uri), 816 'allow_all_extensions' => TRUE, 817 ); 818 819 $this->drupalPost('file-test/upload', $edit, t('Submit')); 820 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 821 $this->assertNoRaw(t('For security reasons, your upload has been renamed'), t('Found no security message.')); 822 $this->assertRaw(t('File name is !filename', array('!filename' => $this->image->filename)), t('File was not munged when allowing any extension.')); 823 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 824 825 // Check that the correct hooks were called. 826 $this->assertFileHooksCalled(array('validate', 'insert')); 827 } 828 829 /** 830 * Test renaming when uploading over a file that already exists. 831 */ 832 function testExistingRename() { 833 $edit = array( 834 'file_test_replace' => FILE_EXISTS_RENAME, 835 'files[file_test_upload]' => drupal_realpath($this->image->uri) 836 ); 837 $this->drupalPost('file-test/upload', $edit, t('Submit')); 838 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 839 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 840 841 // Check that the correct hooks were called. 842 $this->assertFileHooksCalled(array('validate', 'insert')); 843 } 844 845 /** 846 * Test replacement when uploading over a file that already exists. 847 */ 848 function testExistingReplace() { 849 $edit = array( 850 'file_test_replace' => FILE_EXISTS_REPLACE, 851 'files[file_test_upload]' => drupal_realpath($this->image->uri) 852 ); 853 $this->drupalPost('file-test/upload', $edit, t('Submit')); 854 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 855 $this->assertRaw(t('You WIN!'), t('Found the success message.')); 856 857 // Check that the correct hooks were called. 858 $this->assertFileHooksCalled(array('validate', 'load', 'update')); 859 } 860 861 /** 862 * Test for failure when uploading over a file that already exists. 863 */ 864 function testExistingError() { 865 $edit = array( 866 'file_test_replace' => FILE_EXISTS_ERROR, 867 'files[file_test_upload]' => drupal_realpath($this->image->uri) 868 ); 869 $this->drupalPost('file-test/upload', $edit, t('Submit')); 870 $this->assertResponse(200, t('Received a 200 response for posted test file.')); 871 $this->assertRaw(t('Epic upload FAIL!'), t('Found the failure message.')); 872 873 // Check that the no hooks were called while failing. 874 $this->assertFileHooksCalled(array()); 875 } 876 877 /** 878 * Test for no failures when not uploading a file. 879 */ 880 function testNoUpload() { 881 $this->drupalPost('file-test/upload', array(), t('Submit')); 882 $this->assertNoRaw(t('Epic upload FAIL!'), t('Failure message not found.')); 883 } 884 } 885 886 /** 887 * Test the file_save_upload() function on remote filesystems. 888 */ 889 class RemoteFileSaveUploadTest extends FileSaveUploadTest { 890 public static function getInfo() { 891 $info = parent::getInfo(); 892 $info['group'] = 'File API (remote)'; 893 return $info; 894 } 895 896 function setUp() { 897 parent::setUp('file_test'); 898 variable_set('file_default_scheme', 'dummy-remote'); 899 } 900 } 901 902 /** 903 * Directory related tests. 904 */ 905 class FileDirectoryTest extends FileTestCase { 906 public static function getInfo() { 907 return array( 908 'name' => 'File paths and directories', 909 'description' => 'Tests operations dealing with directories.', 910 'group' => 'File API', 911 ); 912 } 913 914 /** 915 * Test directory handling functions. 916 */ 917 function testFileCheckDirectoryHandling() { 918 // A directory to operate on. 919 $directory = file_default_scheme() . '://' . $this->randomName() . '/' . $this->randomName(); 920 $this->assertFalse(is_dir($directory), t('Directory does not exist prior to testing.')); 921 922 // Non-existent directory. 923 $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for non-existing directory.'), 'File'); 924 925 // Make a directory. 926 $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), t('No error reported when creating a new directory.'), 'File'); 927 928 // Make sure directory actually exists. 929 $this->assertTrue(is_dir($directory), t('Directory actually exists.'), 'File'); 930 931 if (substr(PHP_OS, 0, 3) != 'WIN') { 932 // PHP on Windows doesn't support any kind of useful read-only mode for 933 // directories. When executing a chmod() on a directory, PHP only sets the 934 // read-only flag, which doesn't prevent files to actually be written 935 // in the directory on any recent version of Windows. 936 937 // Make directory read only. 938 @drupal_chmod($directory, 0444); 939 $this->assertFalse(file_prepare_directory($directory, 0), t('Error reported for a non-writeable directory.'), 'File'); 940 941 // Test directory permission modification. 942 $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), t('No error reported when making directory writeable.'), 'File'); 943 } 944 945 // Test that the directory has the correct permissions. 946 $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775)); 947 948 // Remove .htaccess file to then test that it gets re-created. 949 @drupal_unlink(file_default_scheme() . '://.htaccess'); 950 $this->assertFalse(is_file(file_default_scheme() . '://.htaccess'), t('Successfully removed the .htaccess file in the files directory.'), 'File'); 951 file_ensure_htaccess(); 952 $this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), t('Successfully re-created the .htaccess file in the files directory.'), 'File'); 953 // Verify contents of .htaccess file. 954 $file = file_get_contents(file_default_scheme() . '://.htaccess'); 955 $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", t('The .htaccess file contains the proper content.'), 'File'); 956 } 957 958 /** 959 * This will take a directory and path, and find a valid filepath that is not 960 * taken by another file. 961 */ 962 function testFileCreateNewFilepath() { 963 // First we test against an imaginary file that does not exist in a 964 // directory. 965 $basename = 'xyz.txt'; 966 $directory = 'misc'; 967 $original = $directory . '/' . $basename; 968 $path = file_create_filename($basename, $directory); 969 $this->assertEqual($path, $original, t('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File'); 970 971 // Then we test against a file that already exists within that directory. 972 $basename = 'druplicon.png'; 973 $original = $directory . '/' . $basename; 974 $expected = $directory . '/druplicon_0.png'; 975 $path = file_create_filename($basename, $directory); 976 $this->assertEqual($path, $expected, t('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File'); 977 978 // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix. 979 } 980 981 /** 982 * This will test the filepath for a destination based on passed flags and 983 * whether or not the file exists. 984 * 985 * If a file exists, file_destination($destination, $replace) will either 986 * return: 987 * - the existing filepath, if $replace is FILE_EXISTS_REPLACE 988 * - a new filepath if FILE_EXISTS_RENAME 989 * - an error (returning FALSE) if FILE_EXISTS_ERROR. 990 * If the file doesn't currently exist, then it will simply return the 991 * filepath. 992 */ 993 function testFileDestination() { 994 // First test for non-existent file. 995 $destination = 'misc/xyz.txt'; 996 $path = file_destination($destination, FILE_EXISTS_REPLACE); 997 $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.'), 'File'); 998 $path = file_destination($destination, FILE_EXISTS_RENAME); 999 $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_RENAME.'), 'File'); 1000 $path = file_destination($destination, FILE_EXISTS_ERROR); 1001 $this->assertEqual($path, $destination, t('Non-existing filepath destination is correct with FILE_EXISTS_ERROR.'), 'File'); 1002 1003 $destination = 'misc/druplicon.png'; 1004 $path = file_destination($destination, FILE_EXISTS_REPLACE); 1005 $this->assertEqual($path, $destination, t('Existing filepath destination remains the same with FILE_EXISTS_REPLACE.'), 'File'); 1006 $path = file_destination($destination, FILE_EXISTS_RENAME); 1007 $this->assertNotEqual($path, $destination, t('A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.'), 'File'); 1008 $path = file_destination($destination, FILE_EXISTS_ERROR); 1009 $this->assertEqual($path, FALSE, t('An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.'), 'File'); 1010 } 1011 1012 /** 1013 * Ensure that the file_directory_temp() function always returns a value. 1014 */ 1015 function testFileDirectoryTemp() { 1016 // Start with an empty variable to ensure we have a clean slate. 1017 variable_set('file_temporary_path', ''); 1018 $tmp_directory = file_directory_temp(); 1019 $this->assertEqual(empty($tmp_directory), FALSE, t('file_directory_temp() returned a non-empty value.')); 1020 $setting = variable_get('file_temporary_path', ''); 1021 $this->assertEqual($setting, $tmp_directory, t("The 'file_temporary_path' variable has the same value that file_directory_temp() returned.")); 1022 } 1023 } 1024 1025 /** 1026 * Directory related tests. 1027 */ 1028 class RemoteFileDirectoryTest extends FileDirectoryTest { 1029 public static function getInfo() { 1030 $info = parent::getInfo(); 1031 $info['group'] = 'File API (remote)'; 1032 return $info; 1033 } 1034 1035 function setUp() { 1036 parent::setUp('file_test'); 1037 variable_set('file_default_scheme', 'dummy-remote'); 1038 } 1039 } 1040 1041 /** 1042 * Tests the file_scan_directory() function. 1043 */ 1044 class FileScanDirectoryTest extends FileTestCase { 1045 public static function getInfo() { 1046 return array( 1047 'name' => 'File scan directory', 1048 'description' => 'Tests the file_scan_directory() function.', 1049 'group' => 'File API', 1050 ); 1051 } 1052 1053 function setUp() { 1054 parent::setUp(); 1055 $this->path = drupal_get_path('module', 'simpletest') . '/files'; 1056 } 1057 1058 /** 1059 * Check the format of the returned values. 1060 */ 1061 function testReturn() { 1062 // Grab a listing of all the JavaSscript files and check that they're 1063 // passed to the callback. 1064 $all_files = file_scan_directory($this->path, '/^javascript-/'); 1065 ksort($all_files); 1066 $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.')); 1067 1068 // Check the first file. 1069 $file = reset($all_files); 1070 $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the first returned file.')); 1071 $this->assertEqual($file->uri, $this->path . '/javascript-1.txt', t('First file name was set correctly.')); 1072 $this->assertEqual($file->filename, 'javascript-1.txt', t('First basename was set correctly')); 1073 $this->assertEqual($file->name, 'javascript-1', t('First name was set correctly.')); 1074 1075 // Check the second file. 1076 $file = next($all_files); 1077 $this->assertEqual(key($all_files), $file->uri, t('Correct array key was used for the second returned file.')); 1078 $this->assertEqual($file->uri, $this->path . '/javascript-2.script', t('Second file name was set correctly.')); 1079 $this->assertEqual($file->filename, 'javascript-2.script', t('Second basename was set correctly')); 1080 $this->assertEqual($file->name, 'javascript-2', t('Second name was set correctly.')); 1081 } 1082 1083 /** 1084 * Check that the callback function is called correctly. 1085 */ 1086 function testOptionCallback() { 1087 // When nothing is matched nothing should be passed to the callback. 1088 $all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback')); 1089 $this->assertEqual(0, count($all_files), t('No files were found.')); 1090 $results = file_test_file_scan_callback(); 1091 file_test_file_scan_callback_reset(); 1092 $this->assertEqual(0, count($results), t('No files were passed to the callback.')); 1093 1094 // Grab a listing of all the JavaSscript files and check that they're 1095 // passed to the callback. 1096 $all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback')); 1097 $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.')); 1098 $results = file_test_file_scan_callback(); 1099 file_test_file_scan_callback_reset(); 1100 $this->assertEqual(2, count($results), t('Files were passed to the callback.')); 1101 } 1102 1103 /** 1104 * Check that the no-mask parameter is honored. 1105 */ 1106 function testOptionNoMask() { 1107 // Grab a listing of all the JavaSscript files. 1108 $all_files = file_scan_directory($this->path, '/^javascript-/'); 1109 $this->assertEqual(2, count($all_files), t('Found two, expected javascript files.')); 1110 1111 // Now use the nomast parameter to filter out the .script file. 1112 $filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/')); 1113 $this->assertEqual(1, count($filtered_files), t('Filtered correctly.')); 1114 } 1115 1116 /** 1117 * Check that key parameter sets the return value's key. 1118 */ 1119 function testOptionKey() { 1120 // "filename", for the path starting with $dir. 1121 $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script'); 1122 $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath'))); 1123 sort($actual); 1124 $this->assertEqual($expected, $actual, t('Returned the correct values for the filename key.')); 1125 1126 // "basename", for the basename of the file. 1127 $expected = array('javascript-1.txt', 'javascript-2.script'); 1128 $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename'))); 1129 sort($actual); 1130 $this->assertEqual($expected, $actual, t('Returned the correct values for the basename key.')); 1131 1132 // "name" for the name of the file without an extension. 1133 $expected = array('javascript-1', 'javascript-2'); 1134 $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name'))); 1135 sort($actual); 1136 $this->assertEqual($expected, $actual, t('Returned the correct values for the name key.')); 1137 1138 // Invalid option that should default back to "filename". 1139 $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script'); 1140 $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID'))); 1141 sort($actual); 1142 $this->assertEqual($expected, $actual, t('An invalid key defaulted back to the default.')); 1143 } 1144 1145 /** 1146 * Check that the recurse option decends into subdirectories. 1147 */ 1148 function testOptionRecurse() { 1149 $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => FALSE)); 1150 $this->assertTrue(empty($files), t("Without recursion couldn't find javascript files.")); 1151 1152 $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => TRUE)); 1153 $this->assertEqual(2, count($files), t('With recursion we found the expected javascript files.')); 1154 } 1155 1156 1157 /** 1158 * Check that the min_depth options lets us ignore files in the starting 1159 * directory. 1160 */ 1161 function testOptionMinDepth() { 1162 $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0)); 1163 $this->assertEqual(2, count($files), t('No minimum-depth gets files in current directory.')); 1164 1165 $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1)); 1166 $this->assertTrue(empty($files), t("Minimum-depth of 1 successfully excludes files from current directory.")); 1167 } 1168 } 1169 1170 /** 1171 * Tests the file_scan_directory() function on remote filesystems. 1172 */ 1173 class RemoteFileScanDirectoryTest extends FileScanDirectoryTest { 1174 public static function getInfo() { 1175 $info = parent::getInfo(); 1176 $info['group'] = 'File API (remote)'; 1177 return $info; 1178 } 1179 1180 function setUp() { 1181 parent::setUp('file_test'); 1182 variable_set('file_default_scheme', 'dummy-remote'); 1183 } 1184 } 1185 1186 /** 1187 * Deletion related tests. 1188 */ 1189 class FileUnmanagedDeleteTest extends FileTestCase { 1190 public static function getInfo() { 1191 return array( 1192 'name' => 'Unmanaged file delete', 1193 'description' => 'Tests the unmanaged file delete function.', 1194 'group' => 'File API', 1195 ); 1196 } 1197 1198 /** 1199 * Delete a normal file. 1200 */ 1201 function testNormal() { 1202 // Create a file for testing 1203 $file = $this->createFile(); 1204 1205 // Delete a regular file 1206 $this->assertTrue(file_unmanaged_delete($file->uri), t('Deleted worked.')); 1207 $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.')); 1208 } 1209 1210 /** 1211 * Try deleting a missing file. 1212 */ 1213 function testMissing() { 1214 // Try to delete a non-existing file 1215 $this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomName()), t('Returns true when deleting a non-existent file.')); 1216 } 1217 1218 /** 1219 * Try deleting a directory. 1220 */ 1221 function testDirectory() { 1222 // A directory to operate on. 1223 $directory = $this->createDirectory(); 1224 1225 // Try to delete a directory 1226 $this->assertFalse(file_unmanaged_delete($directory), t('Could not delete the delete directory.')); 1227 $this->assertTrue(file_exists($directory), t('Directory has not been deleted.')); 1228 } 1229 } 1230 1231 /** 1232 * Deletion related tests on remote filesystems. 1233 */ 1234 class RemoteFileUnmanagedDeleteTest extends FileUnmanagedDeleteTest { 1235 public static function getInfo() { 1236 $info = parent::getInfo(); 1237 $info['group'] = 'File API (remote)'; 1238 return $info; 1239 } 1240 1241 function setUp() { 1242 parent::setUp('file_test'); 1243 variable_set('file_default_scheme', 'dummy-remote'); 1244 } 1245 } 1246 1247 /** 1248 * Deletion related tests. 1249 */ 1250 class FileUnmanagedDeleteRecursiveTest extends FileTestCase { 1251 public static function getInfo() { 1252 return array( 1253 'name' => 'Unmanaged recursive file delete', 1254 'description' => 'Tests the unmanaged file delete recursive function.', 1255 'group' => 'File API', 1256 ); 1257 } 1258 1259 /** 1260 * Delete a normal file. 1261 */ 1262 function testSingleFile() { 1263 // Create a file for testing 1264 $filepath = file_default_scheme() . '://' . $this->randomName(); 1265 file_put_contents($filepath, ''); 1266 1267 // Delete the file. 1268 $this->assertTrue(file_unmanaged_delete_recursive($filepath), t('Function reported success.')); 1269 $this->assertFalse(file_exists($filepath), t('Test file has been deleted.')); 1270 } 1271 1272 /** 1273 * Try deleting an empty directory. 1274 */ 1275 function testEmptyDirectory() { 1276 // A directory to operate on. 1277 $directory = $this->createDirectory(); 1278 1279 // Delete the directory. 1280 $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.')); 1281 $this->assertFalse(file_exists($directory), t('Directory has been deleted.')); 1282 } 1283 1284 /** 1285 * Try deleting a directory with some files. 1286 */ 1287 function testDirectory() { 1288 // A directory to operate on. 1289 $directory = $this->createDirectory(); 1290 $filepathA = $directory . '/A'; 1291 $filepathB = $directory . '/B'; 1292 file_put_contents($filepathA, ''); 1293 file_put_contents($filepathB, ''); 1294 1295 // Delete the directory. 1296 $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.')); 1297 $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.')); 1298 $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.')); 1299 $this->assertFalse(file_exists($directory), t('Directory has been deleted.')); 1300 } 1301 1302 /** 1303 * Try deleting subdirectories with some files. 1304 */ 1305 function testSubDirectory() { 1306 // A directory to operate on. 1307 $directory = $this->createDirectory(); 1308 $subdirectory = $this->createDirectory($directory . '/sub'); 1309 $filepathA = $directory . '/A'; 1310 $filepathB = $subdirectory . '/B'; 1311 file_put_contents($filepathA, ''); 1312 file_put_contents($filepathB, ''); 1313 1314 // Delete the directory. 1315 $this->assertTrue(file_unmanaged_delete_recursive($directory), t('Function reported success.')); 1316 $this->assertFalse(file_exists($filepathA), t('Test file A has been deleted.')); 1317 $this->assertFalse(file_exists($filepathB), t('Test file B has been deleted.')); 1318 $this->assertFalse(file_exists($subdirectory), t('Subdirectory has been deleted.')); 1319 $this->assertFalse(file_exists($directory), t('Directory has been deleted.')); 1320 } 1321 } 1322 1323 /** 1324 * Deletion related tests on remote filesystems. 1325 */ 1326 class RemoteFileUnmanagedDeleteRecursiveTest extends FileUnmanagedDeleteRecursiveTest { 1327 public static function getInfo() { 1328 $info = parent::getInfo(); 1329 $info['group'] = 'File API (remote)'; 1330 return $info; 1331 } 1332 1333 function setUp() { 1334 parent::setUp('file_test'); 1335 variable_set('file_default_scheme', 'dummy-remote'); 1336 } 1337 } 1338 1339 /** 1340 * Unmanaged move related tests. 1341 */ 1342 class FileUnmanagedMoveTest extends FileTestCase { 1343 public static function getInfo() { 1344 return array( 1345 'name' => 'Unmanaged file moving', 1346 'description' => 'Tests the unmanaged file move function.', 1347 'group' => 'File API', 1348 ); 1349 } 1350 1351 /** 1352 * Move a normal file. 1353 */ 1354 function testNormal() { 1355 // Create a file for testing 1356 $file = $this->createFile(); 1357 1358 // Moving to a new name. 1359 $desired_filepath = 'public://' . $this->randomName(); 1360 $new_filepath = file_unmanaged_move($file->uri, $desired_filepath, FILE_EXISTS_ERROR); 1361 $this->assertTrue($new_filepath, t('Move was successful.')); 1362 $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.')); 1363 $this->assertTrue(file_exists($new_filepath), t('File exists at the new location.')); 1364 $this->assertFalse(file_exists($file->uri), t('No file remains at the old location.')); 1365 $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); 1366 1367 // Moving with rename. 1368 $desired_filepath = 'public://' . $this->randomName(); 1369 $this->assertTrue(file_exists($new_filepath), t('File exists before moving.')); 1370 $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.')); 1371 $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME); 1372 $this->assertTrue($newer_filepath, t('Move was successful.')); 1373 $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.')); 1374 $this->assertTrue(file_exists($newer_filepath), t('File exists at the new location.')); 1375 $this->assertFalse(file_exists($new_filepath), t('No file remains at the old location.')); 1376 $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664)); 1377 1378 // TODO: test moving to a directory (rather than full directory/file path) 1379 // TODO: test creating and moving normal files (rather than streams) 1380 } 1381 1382 /** 1383 * Try to move a missing file. 1384 */ 1385 function testMissing() { 1386 // Move non-existent file. 1387 $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName()); 1388 $this->assertFalse($new_filepath, t('Moving a missing file fails.')); 1389 } 1390 1391 /** 1392 * Try to move a file onto itself. 1393 */ 1394 function testOverwriteSelf() { 1395 // Create a file for testing. 1396 $file = $this->createFile(); 1397 1398 // Move the file onto itself without renaming shouldn't make changes. 1399 $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_REPLACE); 1400 $this->assertFalse($new_filepath, t('Moving onto itself without renaming fails.')); 1401 $this->assertTrue(file_exists($file->uri), t('File exists after moving onto itself.')); 1402 1403 // Move the file onto itself with renaming will result in a new filename. 1404 $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_RENAME); 1405 $this->assertTrue($new_filepath, t('Moving onto itself with renaming works.')); 1406 $this->assertFalse(file_exists($file->uri), t('Original file has been removed.')); 1407 $this->assertTrue(file_exists($new_filepath), t('File exists after moving onto itself.')); 1408 } 1409 } 1410 1411 /** 1412 * Unmanaged move related tests on remote filesystems. 1413 */ 1414 class RemoteFileUnmanagedMoveTest extends FileUnmanagedMoveTest { 1415 public static function getInfo() { 1416 $info = parent::getInfo(); 1417 $info['group'] = 'File API (remote)'; 1418 return $info; 1419 } 1420 1421 function setUp() { 1422 parent::setUp('file_test'); 1423 variable_set('file_default_scheme', 'dummy-remote'); 1424 } 1425 } 1426 1427 /** 1428 * Unmanaged copy related tests. 1429 */ 1430 class FileUnmanagedCopyTest extends FileTestCase { 1431 public static function getInfo() { 1432 return array( 1433 'name' => 'Unmanaged file copying', 1434 'description' => 'Tests the unmanaged file copy function.', 1435 'group' => 'File API', 1436 ); 1437 } 1438 1439 /** 1440 * Copy a normal file. 1441 */ 1442 function testNormal() { 1443 // Create a file for testing 1444 $file = $this->createFile(); 1445 1446 // Copying to a new name. 1447 $desired_filepath = 'public://' . $this->randomName(); 1448 $new_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_ERROR); 1449 $this->assertTrue($new_filepath, t('Copy was successful.')); 1450 $this->assertEqual($new_filepath, $desired_filepath, t('Returned expected filepath.')); 1451 $this->assertTrue(file_exists($file->uri), t('Original file remains.')); 1452 $this->assertTrue(file_exists($new_filepath), t('New file exists.')); 1453 $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); 1454 1455 // Copying with rename. 1456 $desired_filepath = 'public://' . $this->randomName(); 1457 $this->assertTrue(file_put_contents($desired_filepath, ' '), t('Created a file so a rename will have to happen.')); 1458 $newer_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_RENAME); 1459 $this->assertTrue($newer_filepath, t('Copy was successful.')); 1460 $this->assertNotEqual($newer_filepath, $desired_filepath, t('Returned expected filepath.')); 1461 $this->assertTrue(file_exists($file->uri), t('Original file remains.')); 1462 $this->assertTrue(file_exists($newer_filepath), t('New file exists.')); 1463 $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664)); 1464 1465 // TODO: test copying to a directory (rather than full directory/file path) 1466 // TODO: test copying normal files using normal paths (rather than only streams) 1467 } 1468 1469 /** 1470 * Copy a non-existent file. 1471 */ 1472 function testNonExistent() { 1473 // Copy non-existent file 1474 $desired_filepath = $this->randomName(); 1475 $this->assertFalse(file_exists($desired_filepath), t("Randomly named file doesn't exists.")); 1476 $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomName()); 1477 $this->assertFalse($new_filepath, t('Copying a missing file fails.')); 1478 } 1479 1480 /** 1481 * Copy a file onto itself. 1482 */ 1483 function testOverwriteSelf() { 1484 // Create a file for testing 1485 $file = $this->createFile(); 1486 1487 // Copy the file onto itself with renaming works. 1488 $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_RENAME); 1489 $this->assertTrue($new_filepath, t('Copying onto itself with renaming works.')); 1490 $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.')); 1491 $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.')); 1492 $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.')); 1493 $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); 1494 1495 // Copy the file onto itself without renaming fails. 1496 $new_filepath = file_unmanaged_copy($file->uri, $file->uri, FILE_EXISTS_ERROR); 1497 $this->assertFalse($new_filepath, t('Copying onto itself without renaming fails.')); 1498 $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.')); 1499 1500 // Copy the file into same directory without renaming fails. 1501 $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_ERROR); 1502 $this->assertFalse($new_filepath, t('Copying onto itself fails.')); 1503 $this->assertTrue(file_exists($file->uri), t('File exists after copying onto itself.')); 1504 1505 // Copy the file into same directory with renaming works. 1506 $new_filepath = file_unmanaged_copy($file->uri, drupal_dirname($file->uri), FILE_EXISTS_RENAME); 1507 $this->assertTrue($new_filepath, t('Copying into same directory works.')); 1508 $this->assertNotEqual($new_filepath, $file->uri, t('Copied file has a new name.')); 1509 $this->assertTrue(file_exists($file->uri), t('Original file exists after copying onto itself.')); 1510 $this->assertTrue(file_exists($new_filepath), t('Copied file exists after copying onto itself.')); 1511 $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664)); 1512 } 1513 } 1514 1515 /** 1516 * Unmanaged copy related tests on remote filesystems. 1517 */ 1518 class RemoteFileUnmanagedCopyTest extends FileUnmanagedCopyTest { 1519 public static function getInfo() { 1520 $info = parent::getInfo(); 1521 $info['group'] = 'File API (remote)'; 1522 return $info; 1523 } 1524 1525 function setUp() { 1526 parent::setUp('file_test'); 1527 variable_set('file_default_scheme', 'dummy-remote'); 1528 } 1529 } 1530 1531 /** 1532 * Deletion related tests. 1533 */ 1534 class FileDeleteTest extends FileHookTestCase { 1535 public static function getInfo() { 1536 return array( 1537 'name' => 'File delete', 1538 'description' => 'Tests the file delete function.', 1539 'group' => 'File API', 1540 ); 1541 } 1542 1543 /** 1544 * Tries deleting a normal file (as opposed to a directory, symlink, etc). 1545 */ 1546 function testUnused() { 1547 $file = $this->createFile(); 1548 1549 // Check that deletion removes the file and database record. 1550 $this->assertTrue(is_file($file->uri), t('File exists.')); 1551 $this->assertIdentical(file_delete($file), TRUE, t('Delete worked.')); 1552 $this->assertFileHooksCalled(array('delete')); 1553 $this->assertFalse(file_exists($file->uri), t('Test file has actually been deleted.')); 1554 $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); 1555 } 1556 1557 /** 1558 * Tries deleting a file that is in use. 1559 */ 1560 function testInUse() { 1561 $file = $this->createFile(); 1562 file_usage_add($file, 'testing', 'test', 1); 1563 file_usage_add($file, 'testing', 'test', 1); 1564 1565 file_usage_delete($file, 'testing', 'test', 1); 1566 file_delete($file); 1567 $usage = file_usage_list($file); 1568 $this->assertEqual($usage['testing']['test'], array(1 => 1), t('Test file is still in use.')); 1569 $this->assertTrue(file_exists($file->uri), t('File still exists on the disk.')); 1570 $this->assertTrue(file_load($file->fid), t('File still exists in the database.')); 1571 1572 // Clear out the call to hook_file_load(). 1573 file_test_reset(); 1574 1575 file_usage_delete($file, 'testing', 'test', 1); 1576 file_delete($file); 1577 $usage = file_usage_list($file); 1578 $this->assertFileHooksCalled(array('delete')); 1579 $this->assertTrue(empty($usage), t('File usage data was removed.')); 1580 $this->assertFalse(file_exists($file->uri), t('File has been deleted after its last usage was removed.')); 1581 $this->assertFalse(file_load($file->fid), t('File was removed from the database.')); 1582 } 1583 } 1584 1585 1586 /** 1587 * Move related tests 1588 */ 1589 class FileMoveTest extends FileHookTestCase { 1590 public static function getInfo() { 1591 return array( 1592 'name' => 'File moving', 1593 'description' => 'Tests the file move function.', 1594 'group' => 'File API', 1595 ); 1596 } 1597 1598 /** 1599 * Move a normal file. 1600 */ 1601 function testNormal() { 1602 $contents = $this->randomName(10); 1603 $source = $this->createFile(NULL, $contents); 1604 $desired_filepath = 'public://' . $this->randomName(); 1605 1606 // Clone the object so we don't have to worry about the function changing 1607 // our reference copy. 1608 $result = file_move(clone $source, $desired_filepath, FILE_EXISTS_ERROR); 1609 1610 // Check the return status and that the contents changed. 1611 $this->assertTrue($result, t('File moved successfully.')); 1612 $this->assertFalse(file_exists($source->uri)); 1613 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.')); 1614 1615 // Check that the correct hooks were called. 1616 $this->assertFileHooksCalled(array('move', 'load', 'update')); 1617 1618 // Make sure we got the same file back. 1619 $this->assertEqual($source->fid, $result->fid, t("Source file id's' %fid is unchanged after move.", array('%fid' => $source->fid))); 1620 1621 // Reload the file from the database and check that the changes were 1622 // actually saved. 1623 $loaded_file = file_load($result->fid, TRUE); 1624 $this->assertTrue($loaded_file, t('File can be loaded from the database.')); 1625 $this->assertFileUnchanged($result, $loaded_file); 1626 } 1627 1628 /** 1629 * Test renaming when moving onto a file that already exists. 1630 */ 1631 function testExistingRename() { 1632 // Setup a file to overwrite. 1633 $contents = $this->randomName(10); 1634 $source = $this->createFile(NULL, $contents); 1635 $target = $this->createFile(); 1636 $this->assertDifferentFile($source, $target); 1637 1638 // Clone the object so we don't have to worry about the function changing 1639 // our reference copy. 1640 $result = file_move(clone $source, $target->uri, FILE_EXISTS_RENAME); 1641 1642 // Check the return status and that the contents changed. 1643 $this->assertTrue($result, t('File moved successfully.')); 1644 $this->assertFalse(file_exists($source->uri)); 1645 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file correctly written.')); 1646 1647 // Check that the correct hooks were called. 1648 $this->assertFileHooksCalled(array('move', 'load', 'update')); 1649 1650 // Compare the returned value to what made it into the database. 1651 $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); 1652 // The target file should not have been altered. 1653 $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); 1654 // Make sure we end up with two distinct files afterwards. 1655 $this->assertDifferentFile($target, $result); 1656 1657 // Compare the source and results. 1658 $loaded_source = file_load($source->fid, TRUE); 1659 $this->assertEqual($loaded_source->fid, $result->fid, t("Returned file's id matches the source.")); 1660 $this->assertNotEqual($loaded_source->uri, $source->uri, t("Returned file path has changed from the original.")); 1661 } 1662 1663 /** 1664 * Test replacement when moving onto a file that already exists. 1665 */ 1666 function testExistingReplace() { 1667 // Setup a file to overwrite. 1668 $contents = $this->randomName(10); 1669 $source = $this->createFile(NULL, $contents); 1670 $target = $this->createFile(); 1671 $this->assertDifferentFile($source, $target); 1672 1673 // Clone the object so we don't have to worry about the function changing 1674 // our reference copy. 1675 $result = file_move(clone $source, $target->uri, FILE_EXISTS_REPLACE); 1676 1677 // Look at the results. 1678 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.')); 1679 $this->assertFalse(file_exists($source->uri)); 1680 $this->assertTrue($result, t('File moved successfully.')); 1681 1682 // Check that the correct hooks were called. 1683 $this->assertFileHooksCalled(array('move', 'update', 'delete', 'load')); 1684 1685 // Reload the file from the database and check that the changes were 1686 // actually saved. 1687 $loaded_result = file_load($result->fid, TRUE); 1688 $this->assertFileUnchanged($result, $loaded_result); 1689 // Check that target was re-used. 1690 $this->assertSameFile($target, $loaded_result); 1691 // Source and result should be totally different. 1692 $this->assertDifferentFile($source, $loaded_result); 1693 } 1694 1695 /** 1696 * Test replacement when moving onto itself. 1697 */ 1698 function testExistingReplaceSelf() { 1699 // Setup a file to overwrite. 1700 $contents = $this->randomName(10); 1701 $source = $this->createFile(NULL, $contents); 1702 1703 // Copy the file over itself. Clone the object so we don't have to worry 1704 // about the function changing our reference copy. 1705 $result = file_move(clone $source, $source->uri, FILE_EXISTS_REPLACE); 1706 $this->assertFalse($result, t('File move failed.')); 1707 $this->assertEqual($contents, file_get_contents($source->uri), t('Contents of file were not altered.')); 1708 1709 // Check that no hooks were called while failing. 1710 $this->assertFileHooksCalled(array()); 1711 1712 // Load the file from the database and make sure it is identical to what 1713 // was returned. 1714 $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); 1715 } 1716 1717 /** 1718 * Test that moving onto an existing file fails when FILE_EXISTS_ERROR is 1719 * specified. 1720 */ 1721 function testExistingError() { 1722 $contents = $this->randomName(10); 1723 $source = $this->createFile(); 1724 $target = $this->createFile(NULL, $contents); 1725 $this->assertDifferentFile($source, $target); 1726 1727 // Clone the object so we don't have to worry about the function changing 1728 // our reference copy. 1729 $result = file_move(clone $source, $target->uri, FILE_EXISTS_ERROR); 1730 1731 // Check the return status and that the contents did not change. 1732 $this->assertFalse($result, t('File move failed.')); 1733 $this->assertTrue(file_exists($source->uri)); 1734 $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.')); 1735 1736 // Check that no hooks were called while failing. 1737 $this->assertFileHooksCalled(array()); 1738 1739 // Load the file from the database and make sure it is identical to what 1740 // was returned. 1741 $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); 1742 $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); 1743 } 1744 } 1745 1746 1747 /** 1748 * Copy related tests. 1749 */ 1750 class FileCopyTest extends FileHookTestCase { 1751 public static function getInfo() { 1752 return array( 1753 'name' => 'File copying', 1754 'description' => 'Tests the file copy function.', 1755 'group' => 'File API', 1756 ); 1757 } 1758 1759 /** 1760 * Test file copying in the normal, base case. 1761 */ 1762 function testNormal() { 1763 $contents = $this->randomName(10); 1764 $source = $this->createFile(NULL, $contents); 1765 $desired_uri = 'public://' . $this->randomName(); 1766 1767 // Clone the object so we don't have to worry about the function changing 1768 // our reference copy. 1769 $result = file_copy(clone $source, $desired_uri, FILE_EXISTS_ERROR); 1770 1771 // Check the return status and that the contents changed. 1772 $this->assertTrue($result, t('File copied successfully.')); 1773 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.')); 1774 1775 // Check that the correct hooks were called. 1776 $this->assertFileHooksCalled(array('copy', 'insert')); 1777 1778 $this->assertDifferentFile($source, $result); 1779 $this->assertEqual($result->uri, $desired_uri, t('The copied file object has the desired filepath.')); 1780 $this->assertTrue(file_exists($source->uri), t('The original file still exists.')); 1781 $this->assertTrue(file_exists($result->uri), t('The copied file exists.')); 1782 1783 // Reload the file from the database and check that the changes were 1784 // actually saved. 1785 $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); 1786 } 1787 1788 /** 1789 * Test renaming when copying over a file that already exists. 1790 */ 1791 function testExistingRename() { 1792 // Setup a file to overwrite. 1793 $contents = $this->randomName(10); 1794 $source = $this->createFile(NULL, $contents); 1795 $target = $this->createFile(); 1796 $this->assertDifferentFile($source, $target); 1797 1798 // Clone the object so we don't have to worry about the function changing 1799 // our reference copy. 1800 $result = file_copy(clone $source, $target->uri, FILE_EXISTS_RENAME); 1801 1802 // Check the return status and that the contents changed. 1803 $this->assertTrue($result, t('File copied successfully.')); 1804 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were copied correctly.')); 1805 $this->assertNotEqual($result->uri, $source->uri, t('Returned file path has changed from the original.')); 1806 1807 // Check that the correct hooks were called. 1808 $this->assertFileHooksCalled(array('copy', 'insert')); 1809 1810 // Load all the affected files to check the changes that actually made it 1811 // to the database. 1812 $loaded_source = file_load($source->fid, TRUE); 1813 $loaded_target = file_load($target->fid, TRUE); 1814 $loaded_result = file_load($result->fid, TRUE); 1815 1816 // Verify that the source file wasn't changed. 1817 $this->assertFileUnchanged($source, $loaded_source); 1818 1819 // Verify that what was returned is what's in the database. 1820 $this->assertFileUnchanged($result, $loaded_result); 1821 1822 // Make sure we end up with three distinct files afterwards. 1823 $this->assertDifferentFile($loaded_source, $loaded_target); 1824 $this->assertDifferentFile($loaded_target, $loaded_result); 1825 $this->assertDifferentFile($loaded_source, $loaded_result); 1826 } 1827 1828 /** 1829 * Test replacement when copying over a file that already exists. 1830 */ 1831 function testExistingReplace() { 1832 // Setup a file to overwrite. 1833 $contents = $this->randomName(10); 1834 $source = $this->createFile(NULL, $contents); 1835 $target = $this->createFile(); 1836 $this->assertDifferentFile($source, $target); 1837 1838 // Clone the object so we don't have to worry about the function changing 1839 // our reference copy. 1840 $result = file_copy(clone $source, $target->uri, FILE_EXISTS_REPLACE); 1841 1842 // Check the return status and that the contents changed. 1843 $this->assertTrue($result, t('File copied successfully.')); 1844 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of file were overwritten.')); 1845 $this->assertDifferentFile($source, $result); 1846 1847 // Check that the correct hooks were called. 1848 $this->assertFileHooksCalled(array('load', 'copy', 'update')); 1849 1850 // Load all the affected files to check the changes that actually made it 1851 // to the database. 1852 $loaded_source = file_load($source->fid, TRUE); 1853 $loaded_target = file_load($target->fid, TRUE); 1854 $loaded_result = file_load($result->fid, TRUE); 1855 1856 // Verify that the source file wasn't changed. 1857 $this->assertFileUnchanged($source, $loaded_source); 1858 1859 // Verify that what was returned is what's in the database. 1860 $this->assertFileUnchanged($result, $loaded_result); 1861 1862 // Target file was reused for the result. 1863 $this->assertFileUnchanged($loaded_target, $loaded_result); 1864 } 1865 1866 /** 1867 * Test that copying over an existing file fails when FILE_EXISTS_ERROR is 1868 * specified. 1869 */ 1870 function testExistingError() { 1871 $contents = $this->randomName(10); 1872 $source = $this->createFile(); 1873 $target = $this->createFile(NULL, $contents); 1874 $this->assertDifferentFile($source, $target); 1875 1876 // Clone the object so we don't have to worry about the function changing 1877 // our reference copy. 1878 $result = file_copy(clone $source, $target->uri, FILE_EXISTS_ERROR); 1879 1880 // Check the return status and that the contents were not changed. 1881 $this->assertFalse($result, t('File copy failed.')); 1882 $this->assertEqual($contents, file_get_contents($target->uri), t('Contents of file were not altered.')); 1883 1884 // Check that the correct hooks were called. 1885 $this->assertFileHooksCalled(array()); 1886 1887 $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); 1888 $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); 1889 } 1890 } 1891 1892 1893 /** 1894 * Tests the file_load() function. 1895 */ 1896 class FileLoadTest extends FileHookTestCase { 1897 public static function getInfo() { 1898 return array( 1899 'name' => 'File loading', 1900 'description' => 'Tests the file_load() function.', 1901 'group' => 'File API', 1902 ); 1903 } 1904 1905 /** 1906 * Try to load a non-existent file by fid. 1907 */ 1908 function testLoadMissingFid() { 1909 $this->assertFalse(file_load(-1), t("Try to load an invalid fid fails.")); 1910 $this->assertFileHooksCalled(array()); 1911 } 1912 1913 /** 1914 * Try to load a non-existent file by URI. 1915 */ 1916 function testLoadMissingFilepath() { 1917 $files = file_load_multiple(array(), array('uri' => 'foobar://misc/druplicon.png')); 1918 $this->assertFalse(reset($files), t("Try to load a file that doesn't exist in the database fails.")); 1919 $this->assertFileHooksCalled(array()); 1920 } 1921 1922 /** 1923 * Try to load a non-existent file by status. 1924 */ 1925 function testLoadInvalidStatus() { 1926 $files = file_load_multiple(array(), array('status' => -99)); 1927 $this->assertFalse(reset($files), t("Trying to load a file with an invalid status fails.")); 1928 $this->assertFileHooksCalled(array()); 1929 } 1930 1931 /** 1932 * Load a single file and ensure that the correct values are returned. 1933 */ 1934 function testSingleValues() { 1935 // Create a new file object from scratch so we know the values. 1936 $file = $this->createFile('druplicon.txt', NULL, 'public'); 1937 1938 $by_fid_file = file_load($file->fid); 1939 $this->assertFileHookCalled('load'); 1940 $this->assertTrue(is_object($by_fid_file), t('file_load() returned an object.')); 1941 $this->assertEqual($by_fid_file->fid, $file->fid, t("Loading by fid got the same fid."), 'File'); 1942 $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File'); 1943 $this->assertEqual($by_fid_file->filename, $file->filename, t("Loading by fid got the correct filename."), 'File'); 1944 $this->assertEqual($by_fid_file->filemime, $file->filemime, t("Loading by fid got the correct MIME type."), 'File'); 1945 $this->assertEqual($by_fid_file->status, $file->status, t("Loading by fid got the correct status."), 'File'); 1946 $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); 1947 } 1948 1949 /** 1950 * This will test loading file data from the database. 1951 */ 1952 function testMultiple() { 1953 // Create a new file object. 1954 $file = $this->createFile('druplicon.txt', NULL, 'public'); 1955 1956 // Load by path. 1957 file_test_reset(); 1958 $by_path_files = file_load_multiple(array(), array('uri' => $file->uri)); 1959 $this->assertFileHookCalled('load'); 1960 $this->assertEqual(1, count($by_path_files), t('file_load_multiple() returned an array of the correct size.')); 1961 $by_path_file = reset($by_path_files); 1962 $this->assertTrue($by_path_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); 1963 $this->assertEqual($by_path_file->fid, $file->fid, t("Loading by filepath got the correct fid."), 'File'); 1964 1965 // Load by fid. 1966 file_test_reset(); 1967 $by_fid_files = file_load_multiple(array($file->fid), array()); 1968 $this->assertFileHookCalled('load'); 1969 $this->assertEqual(1, count($by_fid_files), t('file_load_multiple() returned an array of the correct size.')); 1970 $by_fid_file = reset($by_fid_files); 1971 $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.')); 1972 $this->assertEqual($by_fid_file->uri, $file->uri, t("Loading by fid got the correct filepath."), 'File'); 1973 } 1974 } 1975 1976 /** 1977 * Tests the file_save() function. 1978 */ 1979 class FileSaveTest extends FileHookTestCase { 1980 public static function getInfo() { 1981 return array( 1982 'name' => 'File saving', 1983 'description' => 'Tests the file_save() function.', 1984 'group' => 'File API', 1985 ); 1986 } 1987 1988 function testFileSave() { 1989 // Create a new file object. 1990 $file = array( 1991 'uid' => 1, 1992 'filename' => 'druplicon.txt', 1993 'uri' => 'public://druplicon.txt', 1994 'filemime' => 'text/plain', 1995 'timestamp' => 1, 1996 'status' => FILE_STATUS_PERMANENT, 1997 ); 1998 $file = (object) $file; 1999 file_put_contents($file->uri, 'hello world'); 2000 2001 // Save it, inserting a new record. 2002 $saved_file = file_save($file); 2003 2004 // Check that the correct hooks were called. 2005 $this->assertFileHooksCalled(array('insert')); 2006 2007 $this->assertNotNull($saved_file, t("Saving the file should give us back a file object."), 'File'); 2008 $this->assertTrue($saved_file->fid > 0, t("A new file ID is set when saving a new file to the database."), 'File'); 2009 $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); 2010 $this->assertNotNull($loaded_file, t("Record exists in the database.")); 2011 $this->assertEqual($loaded_file->status, $file->status, t("Status was saved correctly.")); 2012 $this->assertEqual($saved_file->filesize, filesize($file->uri), t("File size was set correctly."), 'File'); 2013 $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File'); 2014 2015 2016 // Resave the file, updating the existing record. 2017 file_test_reset(); 2018 $saved_file->status = 7; 2019 $resaved_file = file_save($saved_file); 2020 2021 // Check that the correct hooks were called. 2022 $this->assertFileHooksCalled(array('load', 'update')); 2023 2024 $this->assertEqual($resaved_file->fid, $saved_file->fid, t("The file ID of an existing file is not changed when updating the database."), 'File'); 2025 $this->assertTrue($resaved_file->timestamp >= $saved_file->timestamp, t("Timestamp didn't go backwards."), 'File'); 2026 $loaded_file = db_query('SELECT * FROM {file_managed} f WHERE f.fid = :fid', array(':fid' => $saved_file->fid))->fetch(PDO::FETCH_OBJ); 2027 $this->assertNotNull($loaded_file, t("Record still exists in the database."), 'File'); 2028 $this->assertEqual($loaded_file->status, $saved_file->status, t("Status was saved correctly.")); 2029 2030 // Try to insert a second file with the same name apart from case insensitivity 2031 // to ensure the 'uri' index allows for filenames with different cases. 2032 $file = (object) array( 2033 'uid' => 1, 2034 'filename' => 'DRUPLICON.txt', 2035 'uri' => 'public://DRUPLICON.txt', 2036 'filemime' => 'text/plain', 2037 'timestamp' => 1, 2038 'status' => FILE_STATUS_PERMANENT, 2039 ); 2040 file_put_contents($file->uri, 'hello world'); 2041 file_save($file); 2042 } 2043 } 2044 2045 /** 2046 * Tests file usage functions. 2047 */ 2048 class FileUsageTest extends FileTestCase { 2049 public static function getInfo() { 2050 return array( 2051 'name' => 'File usage', 2052 'description' => 'Tests the file usage functions.', 2053 'group' => 'File', 2054 ); 2055 } 2056 2057 /** 2058 * Tests file_usage_list(). 2059 */ 2060 function testGetUsage() { 2061 $file = $this->createFile(); 2062 db_insert('file_usage') 2063 ->fields(array( 2064 'fid' => $file->fid, 2065 'module' => 'testing', 2066 'type' => 'foo', 2067 'id' => 1, 2068 'count' => 1 2069 )) 2070 ->execute(); 2071 db_insert('file_usage') 2072 ->fields(array( 2073 'fid' => $file->fid, 2074 'module' => 'testing', 2075 'type' => 'bar', 2076 'id' => 2, 2077 'count' => 2 2078 )) 2079 ->execute(); 2080 2081 $usage = file_usage_list($file); 2082 2083 $this->assertEqual(count($usage['testing']), 2, t('Returned the correct number of items.')); 2084 $this->assertTrue(isset($usage['testing']['foo'][1]), t('Returned the correct id.')); 2085 $this->assertTrue(isset($usage['testing']['bar'][2]), t('Returned the correct id.')); 2086 $this->assertEqual($usage['testing']['foo'][1], 1, t('Returned the correct count.')); 2087 $this->assertEqual($usage['testing']['bar'][2], 2, t('Returned the correct count.')); 2088 } 2089 2090 /** 2091 * Tests file_usage_add(). 2092 */ 2093 function testAddUsage() { 2094 $file = $this->createFile(); 2095 file_usage_add($file, 'testing', 'foo', 1); 2096 // Add the file twice to ensure that the count is incremented rather than 2097 // creating additional records. 2098 file_usage_add($file, 'testing', 'bar', 2); 2099 file_usage_add($file, 'testing', 'bar', 2); 2100 2101 $usage = db_select('file_usage', 'f') 2102 ->fields('f') 2103 ->condition('f.fid', $file->fid) 2104 ->execute() 2105 ->fetchAllAssoc('id'); 2106 $this->assertEqual(count($usage), 2, t('Created two records')); 2107 $this->assertEqual($usage[1]->module, 'testing', t('Correct module')); 2108 $this->assertEqual($usage[2]->module, 'testing', t('Correct module')); 2109 $this->assertEqual($usage[1]->type, 'foo', t('Correct type')); 2110 $this->assertEqual($usage[2]->type, 'bar', t('Correct type')); 2111 $this->assertEqual($usage[1]->count, 1, t('Correct count')); 2112 $this->assertEqual($usage[2]->count, 2, t('Correct count')); 2113 } 2114 2115 /** 2116 * Tests file_usage_delete(). 2117 */ 2118 function testRemoveUsage() { 2119 $file = $this->createFile(); 2120 db_insert('file_usage') 2121 ->fields(array( 2122 'fid' => $file->fid, 2123 'module' => 'testing', 2124 'type' => 'bar', 2125 'id' => 2, 2126 'count' => 3, 2127 )) 2128 ->execute(); 2129 2130 // Normal decrement. 2131 file_usage_delete($file, 'testing', 'bar', 2); 2132 $count = db_select('file_usage', 'f') 2133 ->fields('f', array('count')) 2134 ->condition('f.fid', $file->fid) 2135 ->execute() 2136 ->fetchField(); 2137 $this->assertEqual(2, $count, t('The count was decremented correctly.')); 2138 2139 // Multiple decrement and removal. 2140 file_usage_delete($file, 'testing', 'bar', 2, 2); 2141 $count = db_select('file_usage', 'f') 2142 ->fields('f', array('count')) 2143 ->condition('f.fid', $file->fid) 2144 ->execute() 2145 ->fetchField(); 2146 $this->assertIdentical(FALSE, $count, t('The count was removed entirely when empty.')); 2147 2148 // Non-existent decrement. 2149 file_usage_delete($file, 'testing', 'bar', 2); 2150 $count = db_select('file_usage', 'f') 2151 ->fields('f', array('count')) 2152 ->condition('f.fid', $file->fid) 2153 ->execute() 2154 ->fetchField(); 2155 $this->assertIdentical(FALSE, $count, t('Decrementing non-exist record complete.')); 2156 } 2157 } 2158 2159 /** 2160 * Tests the file_validate() function.. 2161 */ 2162 class FileValidateTest extends FileHookTestCase { 2163 public static function getInfo() { 2164 return array( 2165 'name' => 'File validate', 2166 'description' => 'Tests the file_validate() function.', 2167 'group' => 'File API', 2168 ); 2169 } 2170 2171 /** 2172 * Test that the validators passed into are checked. 2173 */ 2174 function testCallerValidation() { 2175 $file = $this->createFile(); 2176 2177 // Empty validators. 2178 $this->assertEqual(file_validate($file, array()), array(), t('Validating an empty array works successfully.')); 2179 $this->assertFileHooksCalled(array('validate')); 2180 2181 // Use the file_test.module's test validator to ensure that passing tests 2182 // return correctly. 2183 file_test_reset(); 2184 file_test_set_return('validate', array()); 2185 $passing = array('file_test_validator' => array(array())); 2186 $this->assertEqual(file_validate($file, $passing), array(), t('Validating passes.')); 2187 $this->assertFileHooksCalled(array('validate')); 2188 2189 // Now test for failures in validators passed in and by hook_validate. 2190 file_test_reset(); 2191 file_test_set_return('validate', array('Epic fail')); 2192 $failing = array('file_test_validator' => array(array('Failed', 'Badly'))); 2193 $this->assertEqual(file_validate($file, $failing), array('Failed', 'Badly', 'Epic fail'), t('Validating returns errors.')); 2194 $this->assertFileHooksCalled(array('validate')); 2195 } 2196 } 2197 2198 /** 2199 * Tests the file_save_data() function. 2200 */ 2201 class FileSaveDataTest extends FileHookTestCase { 2202 public static function getInfo() { 2203 return array( 2204 'name' => 'File save data', 2205 'description' => 'Tests the file save data function.', 2206 'group' => 'File API', 2207 ); 2208 } 2209 2210 /** 2211 * Test the file_save_data() function when no filename is provided. 2212 */ 2213 function testWithoutFilename() { 2214 $contents = $this->randomName(8); 2215 2216 $result = file_save_data($contents); 2217 $this->assertTrue($result, t('Unnamed file saved correctly.')); 2218 2219 $this->assertEqual(file_default_scheme(), file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); 2220 $this->assertEqual($result->filename, drupal_basename($result->uri), t("Filename was set to the file's basename.")); 2221 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); 2222 $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); 2223 $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); 2224 2225 // Check that the correct hooks were called. 2226 $this->assertFileHooksCalled(array('insert')); 2227 2228 // Verify that what was returned is what's in the database. 2229 $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); 2230 } 2231 2232 /** 2233 * Test the file_save_data() function when a filename is provided. 2234 */ 2235 function testWithFilename() { 2236 $contents = $this->randomName(8); 2237 2238 // Using filename with non-latin characters. 2239 $filename = 'Текстовый файл.txt'; 2240 2241 $result = file_save_data($contents, 'public://' . $filename); 2242 $this->assertTrue($result, t('Unnamed file saved correctly.')); 2243 2244 $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); 2245 $this->assertEqual($filename, drupal_basename($result->uri), t('File was named correctly.')); 2246 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); 2247 $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.')); 2248 $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); 2249 2250 // Check that the correct hooks were called. 2251 $this->assertFileHooksCalled(array('insert')); 2252 2253 // Verify that what was returned is what's in the database. 2254 $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); 2255 } 2256 2257 /** 2258 * Test file_save_data() when renaming around an existing file. 2259 */ 2260 function testExistingRename() { 2261 // Setup a file to overwrite. 2262 $existing = $this->createFile(); 2263 $contents = $this->randomName(8); 2264 2265 $result = file_save_data($contents, $existing->uri, FILE_EXISTS_RENAME); 2266 $this->assertTrue($result, t("File saved successfully.")); 2267 2268 $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); 2269 $this->assertEqual($result->filename, $existing->filename, t("Filename was set to the basename of the source, rather than that of the renamed file.")); 2270 $this->assertEqual($contents, file_get_contents($result->uri), t("Contents of the file are correct.")); 2271 $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set.")); 2272 $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); 2273 2274 // Check that the correct hooks were called. 2275 $this->assertFileHooksCalled(array('insert')); 2276 2277 // Ensure that the existing file wasn't overwritten. 2278 $this->assertDifferentFile($existing, $result); 2279 $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE)); 2280 2281 // Verify that was returned is what's in the database. 2282 $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); 2283 } 2284 2285 /** 2286 * Test file_save_data() when replacing an existing file. 2287 */ 2288 function testExistingReplace() { 2289 // Setup a file to overwrite. 2290 $existing = $this->createFile(); 2291 $contents = $this->randomName(8); 2292 2293 $result = file_save_data($contents, $existing->uri, FILE_EXISTS_REPLACE); 2294 $this->assertTrue($result, t('File saved successfully.')); 2295 2296 $this->assertEqual('public', file_uri_scheme($result->uri), t("File was placed in Drupal's files directory.")); 2297 $this->assertEqual($result->filename, $existing->filename, t('Filename was set to the basename of the existing file, rather than preserving the original name.')); 2298 $this->assertEqual($contents, file_get_contents($result->uri), t('Contents of the file are correct.')); 2299 $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); 2300 $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); 2301 2302 // Check that the correct hooks were called. 2303 $this->assertFileHooksCalled(array('load', 'update')); 2304 2305 // Verify that the existing file was re-used. 2306 $this->assertSameFile($existing, $result); 2307 2308 // Verify that what was returned is what's in the database. 2309 $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); 2310 } 2311 2312 /** 2313 * Test that file_save_data() fails overwriting an existing file. 2314 */ 2315 function testExistingError() { 2316 $contents = $this->randomName(8); 2317 $existing = $this->createFile(NULL, $contents); 2318 2319 // Check the overwrite error. 2320 $result = file_save_data('asdf', $existing->uri, FILE_EXISTS_ERROR); 2321 $this->assertFalse($result, t('Overwriting a file fails when FILE_EXISTS_ERROR is specified.')); 2322 $this->assertEqual($contents, file_get_contents($existing->uri), t('Contents of existing file were unchanged.')); 2323 2324 // Check that no hooks were called while failing. 2325 $this->assertFileHooksCalled(array()); 2326 2327 // Ensure that the existing file wasn't overwritten. 2328 $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE)); 2329 } 2330 } 2331 2332 /** 2333 * Tests for download/file transfer functions. 2334 */ 2335 class FileDownloadTest extends FileTestCase { 2336 public static function getInfo() { 2337 return array( 2338 'name' => 'File download', 2339 'description' => 'Tests for file download/transfer functions.', 2340 'group' => 'File API', 2341 ); 2342 } 2343 2344 function setUp() { 2345 parent::setUp('file_test'); 2346 // Clear out any hook calls. 2347 file_test_reset(); 2348 } 2349 2350 /** 2351 * Test the public file transfer system. 2352 */ 2353 function testPublicFileTransfer() { 2354 // Test generating an URL to a created file. 2355 $file = $this->createFile(); 2356 $url = file_create_url($file->uri); 2357 // URLs can't contain characters outside the ASCII set so $filename has to be 2358 // encoded. 2359 $filename = $GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . rawurlencode($file->filename); 2360 $this->assertEqual($filename, $url, t('Correctly generated a URL for a created file.')); 2361 $this->drupalHead($url); 2362 $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the created file.')); 2363 2364 // Test generating an URL to a shipped file (i.e. a file that is part of 2365 // Drupal core, a module or a theme, for example a JavaScript file). 2366 $filepath = 'misc/jquery.js'; 2367 $url = file_create_url($filepath); 2368 $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath, $url, t('Correctly generated a URL for a shipped file.')); 2369 $this->drupalHead($url); 2370 $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.')); 2371 } 2372 2373 /** 2374 * Test the private file transfer system. 2375 */ 2376 function testPrivateFileTransfer() { 2377 // Set file downloads to private so handler functions get called. 2378 2379 // Create a file. 2380 $contents = $this->randomName(8); 2381 $file = $this->createFile(NULL, $contents, 'private'); 2382 $url = file_create_url($file->uri); 2383 2384 // Set file_test access header to allow the download. 2385 file_test_set_return('download', array('x-foo' => 'Bar')); 2386 $this->drupalGet($url); 2387 $headers = $this->drupalGetHeaders(); 2388 $this->assertEqual($headers['x-foo'], 'Bar', t('Found header set by file_test module on private download.')); 2389 $this->assertResponse(200, t('Correctly allowed access to a file when file_test provides headers.')); 2390 2391 // Test that the file transfered correctly. 2392 $this->assertEqual($contents, $this->content, t('Contents of the file are correct.')); 2393 2394 // Deny access to all downloads via a -1 header. 2395 file_test_set_return('download', -1); 2396 $this->drupalHead($url); 2397 $this->assertResponse(403, t('Correctly denied access to a file when file_test sets the header to -1.')); 2398 2399 // Try non-existent file. 2400 $url = file_create_url('private://' . $this->randomName()); 2401 $this->drupalHead($url); 2402 $this->assertResponse(404, t('Correctly returned 404 response for a non-existent file.')); 2403 } 2404 2405 /** 2406 * Test file_create_url(). 2407 */ 2408 function testFileCreateUrl() { 2409 global $base_url; 2410 2411 // Tilde (~) is excluded from this test because it is encoded by 2412 // rawurlencode() in PHP 5.2 but not in PHP 5.3, as per RFC 3986. 2413 // @see http://www.php.net/manual/en/function.rawurlencode.php#86506 2414 $basename = " -._!$'\"()*@[]?&+%#,;=:\n\x00" . // "Special" ASCII characters. 2415 "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string. 2416 "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets. 2417 $basename_encoded = '%20-._%21%24%27%22%28%29%2A%40%5B%5D%3F%26%2B%25%23%2C%3B%3D%3A__' . 2418 '%2523%2525%2526%252B%252F%253F' . 2419 '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E'; 2420 2421 $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded); 2422 $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded); 2423 $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0'); 2424 } 2425 2426 /** 2427 * Download a file from the URL generated by file_create_url(). 2428 * 2429 * Create a file with the specified scheme, directory and filename; check that 2430 * the URL generated by file_create_url() for the specified file equals the 2431 * specified URL; fetch the URL and then compare the contents to the file. 2432 * 2433 * @param $scheme 2434 * A scheme, e.g. "public" 2435 * @param $directory 2436 * A directory, possibly "" 2437 * @param $filename 2438 * A filename 2439 * @param $expected_url 2440 * The expected URL 2441 * @param $clean_url 2442 * The value of the clean_url setting 2443 */ 2444 private function checkUrl($scheme, $directory, $filename, $expected_url, $clean_url = '1') { 2445 variable_set('clean_url', $clean_url); 2446 2447 // Convert $filename to a valid filename, i.e. strip characters not 2448 // supported by the filesystem, and create the file in the specified 2449 // directory. 2450 $filepath = file_create_filename($filename, $directory); 2451 $directory_uri = $scheme . '://' . dirname($filepath); 2452 file_prepare_directory($directory_uri, FILE_CREATE_DIRECTORY); 2453 $file = $this->createFile($filepath, NULL, $scheme); 2454 2455 $url = file_create_url($file->uri); 2456 $this->assertEqual($url, $expected_url, t('Generated URL matches expected URL.')); 2457 2458 if ($scheme == 'private') { 2459 // Tell the implementation of hook_file_download() in file_test.module 2460 // that this file may be downloaded. 2461 file_test_set_return('download', array('x-foo' => 'Bar')); 2462 } 2463 2464 $this->drupalGet($url); 2465 if ($this->assertResponse(200) == 'pass') { 2466 $this->assertRaw(file_get_contents($file->uri), t('Contents of the file are correct.')); 2467 } 2468 2469 file_delete($file); 2470 } 2471 } 2472 2473 /** 2474 * Tests for file URL rewriting. 2475 */ 2476 class FileURLRewritingTest extends FileTestCase { 2477 public static function getInfo() { 2478 return array( 2479 'name' => 'File URL rewriting', 2480 'description' => 'Tests for file URL rewriting.', 2481 'group' => 'File', 2482 ); 2483 } 2484 2485 function setUp() { 2486 parent::setUp('file_test'); 2487 } 2488 2489 /** 2490 * Test the generating of rewritten shipped file URLs. 2491 */ 2492 function testShippedFileURL() { 2493 // Test generating an URL to a shipped file (i.e. a file that is part of 2494 // Drupal core, a module or a theme, for example a JavaScript file). 2495 2496 // Test alteration of file URLs to use a CDN. 2497 variable_set('file_test_hook_file_url_alter', 'cdn'); 2498 $filepath = 'misc/jquery.js'; 2499 $url = file_create_url($filepath); 2500 $this->assertEqual(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.')); 2501 $filepath = 'misc/favicon.ico'; 2502 $url = file_create_url($filepath); 2503 $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, t('Correctly generated a CDN URL for a shipped file.')); 2504 2505 // Test alteration of file URLs to use root-relative URLs. 2506 variable_set('file_test_hook_file_url_alter', 'root-relative'); 2507 $filepath = 'misc/jquery.js'; 2508 $url = file_create_url($filepath); 2509 $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.')); 2510 $filepath = 'misc/favicon.ico'; 2511 $url = file_create_url($filepath); 2512 $this->assertEqual(base_path() . '/' . $filepath, $url, t('Correctly generated a root-relative URL for a shipped file.')); 2513 2514 // Test alteration of file URLs to use protocol-relative URLs. 2515 variable_set('file_test_hook_file_url_alter', 'protocol-relative'); 2516 $filepath = 'misc/jquery.js'; 2517 $url = file_create_url($filepath); 2518 $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.')); 2519 $filepath = 'misc/favicon.ico'; 2520 $url = file_create_url($filepath); 2521 $this->assertEqual('/' . base_path() . '/' . $filepath, $url, t('Correctly generated a protocol-relative URL for a shipped file.')); 2522 } 2523 2524 /** 2525 * Test the generating of rewritten public created file URLs. 2526 */ 2527 function testPublicCreatedFileURL() { 2528 // Test generating an URL to a created file. 2529 2530 // Test alteration of file URLs to use a CDN. 2531 variable_set('file_test_hook_file_url_alter', 'cdn'); 2532 $file = $this->createFile(); 2533 $url = file_create_url($file->uri); 2534 $public_directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(); 2535 $this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a CDN URL for a created file.')); 2536 2537 // Test alteration of file URLs to use root-relative URLs. 2538 variable_set('file_test_hook_file_url_alter', 'root-relative'); 2539 $file = $this->createFile(); 2540 $url = file_create_url($file->uri); 2541 $this->assertEqual(base_path() . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a root-relative URL for a created file.')); 2542 2543 // Test alteration of file URLs to use a protocol-relative URLs. 2544 variable_set('file_test_hook_file_url_alter', 'protocol-relative'); 2545 $file = $this->createFile(); 2546 $url = file_create_url($file->uri); 2547 $this->assertEqual('/' . base_path() . '/' . $public_directory_path . '/' . $file->filename, $url, t('Correctly generated a protocol-relative URL for a created file.')); 2548 } 2549 } 2550 2551 /** 2552 * Tests for file_munge_filename() and file_unmunge_filename(). 2553 */ 2554 class FileNameMungingTest extends FileTestCase { 2555 public static function getInfo() { 2556 return array( 2557 'name' => 'File naming', 2558 'description' => 'Test filename munging and unmunging.', 2559 'group' => 'File API', 2560 ); 2561 } 2562 2563 function setUp() { 2564 parent::setUp(); 2565 $this->bad_extension = 'php'; 2566 $this->name = $this->randomName() . '.' . $this->bad_extension . '.txt'; 2567 } 2568 2569 /** 2570 * Create a file and munge/unmunge the name. 2571 */ 2572 function testMunging() { 2573 // Disable insecure uploads. 2574 variable_set('allow_insecure_uploads', 0); 2575 $munged_name = file_munge_filename($this->name, '', TRUE); 2576 $messages = drupal_get_messages(); 2577 $this->assertTrue(in_array(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $munged_name)), $messages['status']), t('Alert properly set when a file is renamed.')); 2578 $this->assertNotEqual($munged_name, $this->name, t('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name))); 2579 } 2580 2581 /** 2582 * If the allow_insecure_uploads variable evaluates to true, the file should 2583 * come out untouched, no matter how evil the filename. 2584 */ 2585 function testMungeIgnoreInsecure() { 2586 variable_set('allow_insecure_uploads', 1); 2587 $munged_name = file_munge_filename($this->name, ''); 2588 $this->assertIdentical($munged_name, $this->name, t('The original filename (%original) matches the munged filename (%munged) when insecure uploads are enabled.', array('%munged' => $munged_name, '%original' => $this->name))); 2589 } 2590 2591 /** 2592 * White listed extensions are ignored by file_munge_filename(). 2593 */ 2594 function testMungeIgnoreWhitelisted() { 2595 // Declare our extension as whitelisted. 2596 $munged_name = file_munge_filename($this->name, $this->bad_extension); 2597 $this->assertIdentical($munged_name, $this->name, t('The new filename (%munged) matches the original (%original) once the extension has been whitelisted.', array('%munged' => $munged_name, '%original' => $this->name))); 2598 } 2599 2600 /** 2601 * Ensure that unmunge gets your name back. 2602 */ 2603 function testUnMunge() { 2604 $munged_name = file_munge_filename($this->name, '', FALSE); 2605 $unmunged_name = file_unmunge_filename($munged_name); 2606 $this->assertIdentical($unmunged_name, $this->name, t('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name))); 2607 } 2608 } 2609 2610 /** 2611 * Tests for file_get_mimetype(). 2612 */ 2613 class FileMimeTypeTest extends DrupalWebTestCase { 2614 function setUp() { 2615 parent::setUp('file_test'); 2616 } 2617 2618 public static function getInfo() { 2619 return array( 2620 'name' => 'File mimetypes', 2621 'description' => 'Test filename mimetype detection.', 2622 'group' => 'File API', 2623 ); 2624 } 2625 2626 /** 2627 * Test mapping of mimetypes from filenames. 2628 */ 2629 public function testFileMimeTypeDetection() { 2630 $prefix = 'public://'; 2631 2632 $test_case = array( 2633 'test.jar' => 'application/java-archive', 2634 'test.jpeg' => 'image/jpeg', 2635 'test.JPEG' => 'image/jpeg', 2636 'test.jpg' => 'image/jpeg', 2637 'test.jar.jpg' => 'image/jpeg', 2638 'test.jpg.jar' => 'application/java-archive', 2639 'test.pcf.Z' => 'application/x-font', 2640 'pcf.z' => 'application/octet-stream', 2641 'jar' => 'application/octet-stream', 2642 'some.junk' => 'application/octet-stream', 2643 'foo.file_test_1' => 'madeup/file_test_1', 2644 'foo.file_test_2' => 'madeup/file_test_2', 2645 'foo.doc' => 'madeup/doc', 2646 'test.ogg' => 'audio/ogg', 2647 ); 2648 2649 // Test using default mappings. 2650 foreach ($test_case as $input => $expected) { 2651 // Test stream [URI]. 2652 $output = file_get_mimetype($prefix . $input); 2653 $this->assertIdentical($output, $expected, t('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); 2654 2655 // Test normal path equivalent 2656 $output = file_get_mimetype($input); 2657 $this->assertIdentical($output, $expected, t('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); 2658 } 2659 2660 // Now test passing in the map. 2661 $mapping = array( 2662 'mimetypes' => array( 2663 0 => 'application/java-archive', 2664 1 => 'image/jpeg', 2665 ), 2666 'extensions' => array( 2667 'jar' => 0, 2668 'jpg' => 1, 2669 ) 2670 ); 2671 2672 $test_case = array( 2673 'test.jar' => 'application/java-archive', 2674 'test.jpeg' => 'application/octet-stream', 2675 'test.jpg' => 'image/jpeg', 2676 'test.jar.jpg' => 'image/jpeg', 2677 'test.jpg.jar' => 'application/java-archive', 2678 'test.pcf.z' => 'application/octet-stream', 2679 'pcf.z' => 'application/octet-stream', 2680 'jar' => 'application/octet-stream', 2681 'some.junk' => 'application/octet-stream', 2682 'foo.file_test_1' => 'application/octet-stream', 2683 'foo.file_test_2' => 'application/octet-stream', 2684 'foo.doc' => 'application/octet-stream', 2685 'test.ogg' => 'application/octet-stream', 2686 ); 2687 2688 foreach ($test_case as $input => $expected) { 2689 $output = file_get_mimetype($input, $mapping); 2690 $this->assertIdentical($output, $expected, t('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); 2691 } 2692 } 2693 } 2694 2695 /** 2696 * Tests stream wrapper functions. 2697 */ 2698 class StreamWrapperTest extends DrupalWebTestCase { 2699 2700 protected $scheme = 'dummy'; 2701 protected $classname = 'DrupalDummyStreamWrapper'; 2702 2703 public static function getInfo() { 2704 return array( 2705 'name' => 'Stream wrappers', 2706 'description' => 'Tests stream wrapper functions.', 2707 'group' => 'File API', 2708 ); 2709 } 2710 2711 function setUp() { 2712 parent::setUp('file_test'); 2713 drupal_static_reset('file_get_stream_wrappers'); 2714 } 2715 2716 function tearDown() { 2717 parent::tearDown(); 2718 stream_wrapper_unregister($this->scheme); 2719 } 2720 2721 /** 2722 * Test the getClassName() function. 2723 */ 2724 function testGetClassName() { 2725 // Check the dummy scheme. 2726 $this->assertEqual($this->classname, file_stream_wrapper_get_class($this->scheme), t('Got correct class name for dummy scheme.')); 2727 // Check core's scheme. 2728 $this->assertEqual('DrupalPublicStreamWrapper', file_stream_wrapper_get_class('public'), t('Got correct class name for public scheme.')); 2729 } 2730 2731 /** 2732 * Test the file_stream_wrapper_get_instance_by_scheme() function. 2733 */ 2734 function testGetInstanceByScheme() { 2735 $instance = file_stream_wrapper_get_instance_by_scheme($this->scheme); 2736 $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy scheme.')); 2737 2738 $instance = file_stream_wrapper_get_instance_by_scheme('public'); 2739 $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public scheme.')); 2740 } 2741 2742 /** 2743 * Test the URI and target functions. 2744 */ 2745 function testUriFunctions() { 2746 $instance = file_stream_wrapper_get_instance_by_uri($this->scheme . '://foo'); 2747 $this->assertEqual($this->classname, get_class($instance), t('Got correct class type for dummy URI.')); 2748 2749 $instance = file_stream_wrapper_get_instance_by_uri('public://foo'); 2750 $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.')); 2751 2752 // Test file_uri_target(). 2753 $this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', t('Got a valid stream target from public://foo/bar.txt.')); 2754 $this->assertFalse(file_uri_target('foo/bar.txt'), t('foo/bar.txt is not a valid stream.')); 2755 2756 // Test file_build_uri() and DrupalLocalStreamWrapper::getDirectoryPath(). 2757 $this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', t('Expected scheme was added.')); 2758 $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(), variable_get('file_public_path'), t('Expected default directory path was returned.')); 2759 $this->assertEqual(file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(), variable_get('file_temporary_path'), t('Expected temporary directory path was returned.')); 2760 2761 variable_set('file_default_scheme', 'private'); 2762 $this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', t('Got a valid URI from foo/bar.txt.')); 2763 } 2764 2765 /** 2766 * Test the scheme functions. 2767 */ 2768 function testGetValidStreamScheme() { 2769 $this->assertEqual('foo', file_uri_scheme('foo://pork//chops'), t('Got the correct scheme from foo://asdf')); 2770 $this->assertTrue(file_stream_wrapper_valid_scheme(file_uri_scheme('public://asdf')), t('Got a valid stream scheme from public://asdf')); 2771 $this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), t('Did not get a valid stream scheme from foo://asdf')); 2772 } 2773 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title