? .DS_Store ? .cache ? .git ? .project ? .settings ? INSTALL.sqlite.txt ? file_334303_4.patch ? file_334303_5.patch ? file_341910.patch ? junk ? logs ? user_load_multiple_5.patch ? sites/.DS_Store ? sites/all/modules ? sites/default/.DS_Store ? sites/default/files ? sites/default/settings.php ? sites/default/test Index: includes/file.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/file.inc,v retrieving revision 1.148 diff -u -p -r1.148 file.inc --- includes/file.inc 31 Dec 2008 11:08:47 -0000 1.148 +++ includes/file.inc 1 Jan 2009 00:53:53 -0000 @@ -369,18 +369,21 @@ function file_save($file) { * replace the file or rename the file based on the $replace parameter. * - Adds the new file to the files database. If the source file is a * temporary file, the resulting file will also be a temporary file. - * @see file_save_upload about temporary files. + * @see file_save_upload() for details on temporary files. * * @param $source * A file object. * @param $destination - * A string containing the directory $source should be copied to. If this - * value is omitted, Drupal's 'files' directory will be used. + * A string containing the destination that $source should be copied to. This + * can be a complete file path, a directory path or, if this value is omitted, + * Drupal's 'files' directory will be used. * @param $replace * Replace behavior when the destination file already exists: - * - FILE_EXISTS_REPLACE - Replace the existing file. + * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with + * the destination name exists then its database entry will be updated. If + * no database entry is found then a new one will be created. * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. + * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return * File object if the copy is successful, or FALSE in the event of an error. @@ -393,14 +396,30 @@ function file_copy($source, $destination if ($filepath = file_unmanaged_copy($source->filepath, $destination, $replace)) { $file = clone $source; - $file->fid = NULL; - $file->filename = basename($filepath); + $file->fid = NULL; $file->filepath = $filepath; - if ($file = file_save($file)) { - // Inform modules that the file has been copied. - module_invoke_all('file_copy', $file, $source); - return $file; + $file->filename = basename($filepath); + // If we're replacing an existing file re-use its database record. + if ($replace == FILE_EXISTS_REPLACE) { + $existing_files = file_load_multiple(array(), array('filepath' => $filepath)); + if (count($existing_files)) { + $existing = reset($existing_files); + $file->fid = $existing->fid; + $file->filename = $existing->filename; + } } + // If we're renaming around an existing file (rather than a directory), + // use its basename for the filename. + else if ($replace == FILE_EXISTS_RENAME && is_file(file_create_path($destination))) { + $file->filename = basename($destination); + } + + $file = file_save($file); + + // Inform modules that the file has been copied. + module_invoke_all('file_copy', $file, $source); + + return $file; } return FALSE; } @@ -420,13 +439,14 @@ function file_copy($source, $destination * @param $source * A string specifying the file location of the original file. * @param $destination - * A string containing the directory $source should be copied to. If this - * value is omitted, Drupal's 'files' directory will be used. + * A string containing the destination that $source should be copied to. This + * can be a complete file path, a directory path or, if this value is omitted, + * Drupal's 'files' directory will be used. * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. + * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return * The path to the new file, or FALSE in the event of an error. @@ -490,7 +510,7 @@ function file_unmanaged_copy($source, $d * Replace behavior when the destination file already exists. * - FILE_EXISTS_REPLACE - Replace the existing file. * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. + * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return * The destination file path or FALSE if the file already exists and @@ -531,13 +551,18 @@ function file_destination($destination, * @param $source * A file object. * @param $destination - * A string containing the directory $source should be copied to. If this - * value is omitted, Drupal's 'files' directory will be used. + * A string containing the destination that $source should be moved to. This + * can be a complete file path, a directory path or, if this value is omitted, + * Drupal's 'files' directory will be used. * @param $replace * Replace behavior when the destination file already exists: - * - FILE_EXISTS_REPLACE - Replace the existing file. + * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with + * the destination name exists then its database entry will be updated and + * file_delete() called on the source file after hook_file_move is called. + * If no database entry is found then the source files record will be + * updated. * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. + * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return * Resulting file object for success, or FALSE in the event of an error. @@ -549,15 +574,36 @@ function file_move($source, $destination $source = (object)$source; if ($filepath = file_unmanaged_move($source->filepath, $destination, $replace)) { + $delete_source = FALSE; + $file = clone $source; - $file->filename = basename($filepath); $file->filepath = $filepath; - if ($file = file_save($file)) { - // Inform modules that the file has been moved. - module_invoke_all('file_move', $file, $source); - return $file; + // If we're replacing an existing file re-use its database record. + if ($replace == FILE_EXISTS_REPLACE) { + $existing_files = file_load_multiple(array(), array('filepath' => $filepath)); + if (count($existing_files)) { + $existing = reset($existing_files); + $delete_source = TRUE; + $file->fid = $existing->fid; + } } - drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $source->filepath)), 'error'); + // If we're renaming around an existing file (rather than a directory), + // use its basename for the filename. + else if ($replace == FILE_EXISTS_RENAME && is_file(file_create_path($destination))) { + $file->filename = basename($destination); + } + + $file = file_save($file); + + // Inform modules that the file has been moved. + module_invoke_all('file_move', $file, $source); + + if ($delete_source) { + // Try a soft delete to remove original if it's not in use elsewhere. + file_delete($source); + } + + return $file; } return FALSE; } @@ -569,13 +615,14 @@ function file_move($source, $destination * @param $source * A string specifying the file location of the original file. * @param $destination - * A string containing the directory $source should be copied to. If this - * value is omitted, Drupal's 'files' directory will be used. + * A string containing the destination that $source should be moved to. This + * can be a complete file path, a directory name or, if this value is omitted, + * Drupal's 'files' directory will be used. * @param $replace * Replace behavior when the destination file already exists: * - FILE_EXISTS_REPLACE - Replace the existing file. * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. + * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return * The filepath of the moved file, or FALSE in the event of an error. @@ -1119,9 +1166,11 @@ function file_validate_image_resolution( * files directory. * @param $replace * Replace behavior when the destination file already exists: - * - FILE_EXISTS_REPLACE - Replace the existing file. + * - FILE_EXISTS_REPLACE - Replace the existing file. If a managed file with + * the destination name exists then its database entry will be updated. If + * no database entry is found then a new one will be created. * - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is - * unique. + * unique. * - FILE_EXISTS_ERROR - Do nothing and return FALSE. * @return * A file object, or FALSE on error. @@ -1134,11 +1183,27 @@ function file_save_data($data, $destinat if ($filepath = file_unmanaged_save_data($data, $destination, $replace)) { // Create a file object. $file = new stdClass(); + $file->fid = NULL; $file->filepath = $filepath; - $file->filename = basename($file->filepath); + $file->filename = basename($filepath); $file->filemime = file_get_mimetype($file->filepath); $file->uid = $user->uid; $file->status = FILE_STATUS_PERMANENT; + // If we're replacing an existing file re-use its database record. + if ($replace == FILE_EXISTS_REPLACE) { + $existing_files = file_load_multiple(array(), array('filepath' => $filepath)); + if (count($existing_files)) { + $existing = reset($existing_files); + $file->fid = $existing->fid; + $file->filename = $existing->filename; + } + } + // If we're renaming around an existing file (rather than a directory), + // use its basename for the filename. + else if ($replace == FILE_EXISTS_RENAME && is_file(file_create_path($destination))) { + $file->filename = basename($destination); + } + return file_save($file); } return FALSE; Index: modules/simpletest/tests/file.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file.test,v retrieving revision 1.16 diff -u -p -r1.16 file.test --- modules/simpletest/tests/file.test 31 Dec 2008 11:08:47 -0000 1.16 +++ modules/simpletest/tests/file.test 1 Jan 2009 00:53:55 -0000 @@ -20,6 +20,51 @@ function file_test_validator($file, $err */ class FileTestCase extends DrupalWebTestCase { /** + * Check that two files have the same values for all fields other than the + * timestamp. + * + * @param $before + * File object to compare. + * @param $after + * File object to compare. + */ + function assertFileUnchanged($before, $after) { + $this->assertEqual($before->fid, $after->fid, t('File id is the same.'), 'File unchanged'); + $this->assertEqual($before->uid, $after->uid, t('File owner is the same.'), 'File unchanged'); + $this->assertEqual($before->filename, $after->filename, t('File name is the same.'), 'File unchanged'); + $this->assertEqual($before->filepath, $after->filepath, t('File path is the same.'), 'File unchanged'); + $this->assertEqual($before->filemime, $after->filemime, t('File MIME type is the same.'), 'File unchanged'); + $this->assertEqual($before->filesize, $after->filesize, t('File size is the same.'), 'File unchanged'); + $this->assertEqual($before->status, $after->status, t('File status is the same.'), 'File unchanged'); + } + + /** + * Check that two files are not the same by comparing the fid and filepath. + * + * @param $a + * File object to compare. + * @param $b + * File object to compare. + */ + function assertDifferentFile($a, $b) { + $this->assertNotEqual($a->fid, $b->fid, t('Files have different ids.'), 'Different file'); + $this->assertNotEqual($a->filepath, $b->filepath, t('Files have different paths.'), 'Different file'); + } + + /** + * Check that two files are the same by comparing the fid and filepath. + * + * @param $a + * File object to compare. + * @param $b + * File object to compare. + */ + function assertSameFile($a, $b) { + $this->assertEqual($a->fid, $b->fid, t('Files have the same ids %a-fid == %b-fid.', array('%a-fid' => $a->fid, '%b-fid' => $b->fid)), 'Same file'); + $this->assertEqual($a->filepath, $b->filepath, t('Files have the same path %a-filepath == %b-filepath', array('%a-filepath' => $a->filepath, '%b-filepath' => $b->filepath)), 'Same file'); + } + + /** * Helper function to test the permissions of a file. * * @param $filepath @@ -68,16 +113,23 @@ class FileTestCase extends DrupalWebTest * @param $filepath * Optional string specifying the file path. If none is provided then a * randomly named file will be created in the site's files directory. + * @param $contents + * Optional contents to save into the file. If a NULL value is provided an + * arbitrary string will be used. * @return * File object. */ - function createFile($filepath = NULL) { + function createFile($filepath = NULL, $contents = NULL) { if (is_null($filepath)) { $filepath = file_directory_path() . '/' . $this->randomName(); } - file_put_contents($filepath, 'File put contents does not seem to appreciate empty strings so lets put in some data.'); - $this->assertTrue(is_file($filepath), t('The test file exists on the disk.')); + if (is_null($contents)) { + $contents = "file_put_contents() doesn't seem to appreciate empty strings so lets put in some data."; + } + + file_put_contents($filepath, $contents); + $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file'); $file = new stdClass(); $file->filepath = $filepath; @@ -87,7 +139,9 @@ class FileTestCase extends DrupalWebTest $file->timestamp = REQUEST_TIME; $file->filesize = filesize($file->filepath); $file->status = FILE_STATUS_TEMPORARY; - $this->assertNotIdentical(drupal_write_record('files', $file), FALSE, t('The file was added to the database.')); + // Write the record directly rather than calling file_save() so we don't + // invoke the hooks. + $this->assertNotIdentical(drupal_write_record('files', $file), FALSE, t('The file was added to the database.'), 'Create test file'); return $file; } @@ -122,6 +176,9 @@ class FileHookTestCase extends FileTestC if ($actual_count == $expected_count) { $message = t('hook_file_@name was called correctly.', array('@name' => $hook)); } + elseif ($expected_count == 0) { + $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)); + } else { $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)); } @@ -821,19 +878,122 @@ class FileMoveTest extends FileHookTestC * Move a normal file. */ function testNormal() { - $file = $this->createFile(); + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); $desired_filepath = file_directory_path() . '/' . $this->randomName(); - $file = file_move(clone $file, $desired_filepath, FILE_EXISTS_ERROR); - $this->assertTrue($file, t("File moved sucessfully.")); + $result = file_move(clone $source, $desired_filepath, FILE_EXISTS_ERROR); + + // Look at the results. + $this->assertTrue($result, t('File moved sucessfully.')); + $this->assertFalse(file_exists($source->filepath)); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file correctly written.')); $this->assertFileHookCalled('move'); $this->assertFileHookCalled('update'); - $this->assertEqual($file->fid, $file->fid, t("File id $file->fid is unchanged after move.")); + $this->assertEqual($source->fid, $result->fid, t("Source file id's' %fid is unchanged after move.", array('%fid' => $source->fid))); + + $loaded_file = file_load($result->fid, TRUE); + $this->assertTrue($loaded_file, t('File can be loaded from the database.')); + $this->assertFileUnchanged($result, $loaded_file); + } + + /** + * Test renaming when moveing onto a file that already exists. + */ + function testExistingRename() { + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); + $target = $this->createFile(); + $this->assertDifferentFile($source, $target); + + $result = file_move(clone $source, $target->filepath, FILE_EXISTS_RENAME); + + // Look at the results. + $this->assertTrue($result, t('File moved sucessfully.')); + $this->assertFalse(file_exists($source->filepath)); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file correctly written.')); + $this->assertFileHookCalled('move'); + $this->assertFileHookCalled('update', 1); + $this->assertFileHookCalled('insert', 0); + + // Compare the returned value to what made it into the database. + $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); + // The target file should not have been altered. + $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); + // Make sure we end up with two distinct files afterwards. + $this->assertDifferentFile($target, $result); - $loaded_file = file_load($file->fid); - $this->assertTrue($loaded_file, t("File can be loaded from the database.")); - $this->assertEqual($file->filename, $loaded_file->filename, t("File name was updated correctly in the database.")); - $this->assertEqual($file->filepath, $loaded_file->filepath, t("File path was updated correctly in the database.")); + // Compare the source and results. + $loaded_source = file_load($source->fid, TRUE); + $this->assertEqual($loaded_source->fid, $result->fid, t("Returned file's id matches the source.")); + $this->assertNotEqual($loaded_source->filepath, $source->filepath, t("Returned file path has changed from the original.")); + } + + /** + * Test replacement when moving onto a file that already exists. + */ + function testExistingReplace() { + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); + $target = $this->createFile(); + $this->assertDifferentFile($source, $target); + + $result = file_move(clone $source, $target->filepath, FILE_EXISTS_REPLACE); + + // Look at the results + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were overwritten.')); + $this->assertFalse(file_exists($source->filepath)); + $this->assertTrue($result, t('File moved sucessfully.')); + $this->assertFileHookCalled('move'); + $this->assertFileHookCalled('update'); + $this->assertFileHookCalled('delete'); + $this->assertFileHookCalled('insert', 0); + + $loaded_result = file_load($result->fid, TRUE); + // Check that the changes were actually saved to the database. + $this->assertFileUnchanged($result, $loaded_result); + // Check that target was re-used. + $this->assertSameFile($target, $loaded_result); + // Source and result should be totally different. + $this->assertDifferentFile($source, $loaded_result); + } + + /** + * Test replacement when moving onto a file that already exists. + */ + function testExistingReplaceSelf() { + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); + + $result = file_move(clone $source, $source->filepath, FILE_EXISTS_REPLACE); + $this->assertFalse($result, t('File move failed.')); + $this->assertEqual($contents, file_get_contents($source->filepath), t('Contents of file were not altered.')); + $this->assertFileHookCalled('move', 0); + $this->assertFileHookCalled('insert', 0); + $this->assertFileHookCalled('update', 0); + $this->assertFileHookCalled('delete', 0); + $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); + } + + /** + * Test that moving onto an existing file fails when FILE_EXISTS_ERROR is + * specified. + */ + function testExistingError() { + $contents = $this->randomName(10); + $source = $this->createFile(); + $target = $this->createFile(NULL, $contents); + $this->assertDifferentFile($source, $target); + + $result = file_move(clone $source, $target->filepath, FILE_EXISTS_ERROR); + $this->assertFalse($result, t('File move failed.')); + $this->assertTrue(file_exists($source->filepath)); + $this->assertEqual($contents, file_get_contents($target->filepath), t('Contents of file were not altered.')); + $this->assertFileHookCalled('move', 0); + $this->assertFileHookCalled('insert', 0); + $this->assertFileHookCalled('update', 0); + $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); + $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); } } @@ -850,28 +1010,111 @@ class FileCopyTest extends FileHookTestC ); } - /** - * Test copying a normal file. - */ function testNormal() { - $source_file = $this->createFile(); + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); $desired_filepath = file_directory_path() . '/' . $this->randomName(); - $file = file_copy(clone $source_file, $desired_filepath, FILE_EXISTS_ERROR); - $this->assertTrue($file, t("File copied sucessfully.")); + $result = file_copy(clone $source, $desired_filepath, FILE_EXISTS_ERROR); + + // Look at the results. + $this->assertTrue($result, t('File copied sucessfully.')); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were copied correctly.')); $this->assertFileHookCalled('copy'); $this->assertFileHookCalled('insert'); - $this->assertNotEqual($source_file->fid, $file->fid, t("A new file id was created.")); - $this->assertNotEqual($source_file->filepath, $file->filepath, t("A new filepath was created.")); - $this->assertEqual($file->filepath, $desired_filepath, t('The copied file object has the desired filepath.')); - $this->assertTrue(file_exists($source_file->filepath), t('The original file still exists.')); - $this->assertTrue(file_exists($file->filepath), t('The copied file exists.')); + $this->assertDifferentFile($source, $result); + $this->assertEqual($result->filepath, $desired_filepath, t('The copied file object has the desired filepath.')); + $this->assertTrue(file_exists($source->filepath), t('The original file still exists.')); + $this->assertTrue(file_exists($result->filepath), t('The copied file exists.')); // Check that the changes were actually saved to the database. - $loaded_file = file_load($file->fid); - $this->assertTrue($loaded_file, t("File can be loaded from the database.")); - $this->assertEqual($file->filename, $loaded_file->filename, t("File name was updated correctly in the database.")); - $this->assertEqual($file->filepath, $loaded_file->filepath, t("File path was updated correctly in the database.")); + $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); + } + + /** + * Test renaming when copying over a file that already exists. + */ + function testExistingRename() { + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); + $target = $this->createFile(); + $this->assertDifferentFile($source, $target); + + $result = file_copy(clone $source, $target->filepath, FILE_EXISTS_RENAME); + + // Look at the results. + $this->assertTrue($result, t('File copied sucessfully.')); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were copied correctly.')); + $this->assertNotEqual($result->filepath, $source->filepath, t('Returned file path has changed from the original.')); + $this->assertFileHookCalled('copy'); + $this->assertFileHookCalled('insert'); + $this->assertFileHookCalled('update', 0); + + $loaded_source = file_load($source->fid, TRUE); + $loaded_target = file_load($target->fid, TRUE); + $loaded_result = file_load($result->fid, TRUE); + // Verify that the source file wasn't changed. + $this->assertFileUnchanged($source, $loaded_source); + // Verify that what was returned is what's in the database. + $this->assertFileUnchanged($result, $loaded_result); + // Make sure we end up with three distinct files afterwards. + $this->assertDifferentFile($loaded_source, $loaded_target); + $this->assertDifferentFile($loaded_target, $loaded_result); + $this->assertDifferentFile($loaded_source, $loaded_result); + } + + /** + * Test replacement when copying over a file that already exists. + */ + function testExistingReplace() { + $contents = $this->randomName(10); + $source = $this->createFile(NULL, $contents); + $target = $this->createFile(); + $this->assertDifferentFile($source, $target); + + $result = file_copy(clone $source, $target->filepath, FILE_EXISTS_REPLACE); + + // Look at the results. + $this->assertTrue($result, t('File copied sucessfully.')); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of file were overwritten.')); + $this->assertFileHookCalled('copy'); + $this->assertFileHookCalled('insert', 0); + $this->assertFileHookCalled('update'); + $this->assertDifferentFile($source, $result); + + // Now examine what actually made it into the database. + $loaded_source = file_load($source->fid, TRUE); + $loaded_target = file_load($target->fid, TRUE); + $loaded_result = file_load($result->fid, TRUE); + + // Verify that the source file wasn't changed. + $this->assertFileUnchanged($source, $loaded_source); + // Verify that what was returned is what's in the database. + $this->assertFileUnchanged($result, $loaded_result); + // Target file was reused for the result. + $this->assertFileUnchanged($loaded_target, $loaded_result); + } + + /** + * Test that copying over an existing file fails when FILE_EXISTS_ERROR is + * specified. + */ + function testExistingError() { + $contents = $this->randomName(10); + $source = $this->createFile(); + $target = $this->createFile(NULL, $contents); + $this->assertDifferentFile($source, $target); + + $result = file_copy(clone $source, $target->filepath, FILE_EXISTS_ERROR); + + // Look at the results. + $this->assertFalse($result, t('File copy failed.')); + $this->assertEqual($contents, file_get_contents($target->filepath), t('Contents of file were not altered.')); + $this->assertFileHookCalled('copy', 0); + $this->assertFileHookCalled('insert', 0); + $this->assertFileHookCalled('update', 0); + $this->assertFileUnchanged($source, file_load($source->fid, TRUE)); + $this->assertFileUnchanged($target, file_load($target->fid, TRUE)); } } @@ -1074,32 +1317,116 @@ class FileSaveDataTest extends FileHookT } /** - * Test the file_save_data() function. + * Test the file_save_data() function when no filename is provided. */ - function testFileSaveData() { + function testWithoutFilename() { $contents = $this->randomName(8); - // No filename. - $file = file_save_data($contents); - $this->assertTrue($file, t("Unnamed file saved correctly.")); - $this->assertEqual(file_directory_path(), dirname($file->filepath), t("File was placed in Drupal's files directory.")); - $this->assertEqual($contents, file_get_contents(realpath($file->filepath)), t("Contents of the file are correct.")); - $this->assertEqual($file->filemime, 'application/octet-stream', t("A MIME type was set.")); - $this->assertEqual($file->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); - - // Try loading the file. - $loaded_file = file_load($file->fid); - $this->assertTrue($loaded_file, t("File loaded from database.")); + $result = file_save_data($contents); - // Provide a filename. - $file = file_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE); - $this->assertTrue($file, t("Unnamed file saved correctly.")); - $this->assertEqual(file_directory_path(), dirname($file->filepath), t("File was placed in Drupal's files directory.")); - $this->assertEqual('asdf.txt', basename($file->filepath), t("File was named correctly.")); - $this->assertEqual($contents, file_get_contents(realpath($file->filepath)), t("Contents of the file are correct.")); + $this->assertTrue($result, t('Unnamed file saved correctly.')); + $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual($result->filename, basename($result->filepath), t("Filename was was set to the file's basename.")); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of the file are correct.')); + $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); + $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); + $this->assertFileHookCalled('insert'); + $this->assertFileHookCalled('update', 0); + $this->assertFileHookCalled('delete', 0); + + // Verify that what was returned is what's in the database. + $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); + } + + /** + * Test the file_save_data() function when a filename is provided. + */ + function testWithFilename() { + $contents = $this->randomName(8); + + $result = file_save_data($contents, 'asdf.txt'); + + $this->assertTrue($result, t('Unnamed file saved correctly.')); + $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual('asdf.txt', basename($result->filepath), t('File was named correctly.')); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of the file are correct.')); + $this->assertEqual($result->filemime, 'text/plain', t('A MIME type was set.')); + $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); + $this->assertFileHookCalled('insert'); + $this->assertFileHookCalled('update', 0); + $this->assertFileHookCalled('delete', 0); + + // Verify that what was returned is what's in the database. + $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); + } + + /** + * Test file_save_data() when renaming around an existing file. + */ + function testExistingRename() { + $existing = $this->createFile(); + $contents = $this->randomName(8); + + $result = file_save_data($contents, $existing->filepath, FILE_EXISTS_RENAME); + + $this->assertTrue($result, t("File saved sucessfully.")); + $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual($result->filename, $existing->filename, t("Filename was was set to the basename of the source.")); + $this->assertEqual($contents, file_get_contents($result->filepath), t("Contents of the file are correct.")); + $this->assertEqual($result->filemime, 'application/octet-stream', t("A MIME type was set.")); + $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); + $this->assertFileHookCalled('insert'); + + // Ensure that the existing file wasn't ovewritten. + $this->assertDifferentFile($existing, $result); + $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE)); + + // Verify that was was returned is what's in the database. + $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); + } + + /** + * Test file_save_data() when replacing an existing file. + */ + function testExistingReplace() { + $existing = $this->createFile(); + $contents = $this->randomName(8); + + $result = file_save_data($contents, $existing->filepath, FILE_EXISTS_REPLACE); + + $this->assertTrue($result, t('File saved sucessfully.')); + $this->assertEqual(file_directory_path(), dirname($result->filepath), t("File was placed in Drupal's files directory.")); + $this->assertEqual($result->filename, $existing->filename, t('Filename was was set to the basename of the source.')); + $this->assertEqual($contents, file_get_contents($result->filepath), t('Contents of the file are correct.')); + $this->assertEqual($result->filemime, 'application/octet-stream', t('A MIME type was set.')); + $this->assertEqual($result->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent.")); + $this->assertFileHookCalled('update'); + $this->assertFileHookCalled('insert', 0); + $this->assertFileHookCalled('delete', 0); + + // Verify that the existing file was re-used. + $this->assertSameFile($existing, $result); + + // Verify that what was returned is what's in the database. + $this->assertFileUnchanged($result, file_load($result->fid, TRUE)); + } + + /** + * Test that file_save_data() fails overwriting an existing file. + */ + function testOverwriteError() { + $contents = $this->randomName(8); + $existing = $this->createFile(NULL, $contents); // Check the overwrite error. - $file = file_save_data($contents, 'asdf.txt', FILE_EXISTS_ERROR); - $this->assertFalse($file, t("Overwriting a file fails when FILE_EXISTS_ERROR is specified.")); + $result = file_save_data('asdf', $existing->filepath, FILE_EXISTS_ERROR); + $this->assertFalse($result, t('Overwriting a file fails when FILE_EXISTS_ERROR is specified.')); + $this->assertEqual($contents, file_get_contents($existing->filepath), t('Contents of existing file were unchagned.')); + $this->assertFileHookCalled('insert', 0); + $this->assertFileHookCalled('update', 0); + $this->assertFileHookCalled('delete', 0); + + // Ensure that the existing file wasn't overwritten. + $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE)); } }