? .cache
? .git
? .project
? .settings
? empty
? file_74645_4.patch
? logs
? modules/file
? sites/all/modules
? 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.145
diff -u -p -r1.145 file.inc
--- includes/file.inc	16 Nov 2008 19:41:14 -0000	1.145
+++ includes/file.inc	17 Nov 2008 08:15:54 -0000
@@ -364,7 +364,7 @@ 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.
@@ -373,9 +373,11 @@ function file_save($file) {
  *   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.
@@ -387,15 +389,21 @@ function file_copy($source, $destination
   $source = (object)$source;
 
   if ($filepath = file_unmanaged_copy($source->filepath, $destination, $replace)) {
+    // If we're replacing an existing file re-use it's database record.
     $file = clone $source;
-    $file->fid      = NULL;
-    $file->filename = basename($filepath);
-    $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;
+    if ($replace == FILE_EXISTS_REPLACE && ($existing = file_load(array('filepath' => $filepath)))) {
+      $file->fid = $existing->fid;
+    }
+    else {
+      $file->fid = NULL;
     }
+    $file->filepath = $filepath;
+    $file = file_save($file);
+
+    // Inform modules that the file has been copied.
+    module_invoke_all('file_copy', $file, $source);
+
+    return $file;
   }
   return FALSE;
 }
@@ -421,7 +429,7 @@ function file_copy($source, $destination
  *   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.
@@ -485,7 +493,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
@@ -530,9 +538,13 @@ function file_destination($destination, 
  *   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.
@@ -544,15 +556,25 @@ function file_move($source, $destination
   $source = (object)$source;
 
   if ($filepath = file_unmanaged_move($source->filepath, $destination, $replace)) {
+    $delete_original = FALSE;
+    // If we're replacing an existing file re-use it's database record.
     $file = clone $source;
-    $file->filename = basename($filepath);
+    if ($replace == FILE_EXISTS_REPLACE && ($existing = file_load(array('filepath' => $filepath)))) {
+      $delete_original = TRUE;
+      $file->fid = $existing->fid;
+    }
     $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;
+    $file = file_save($file);
+
+    // Inform modules that the file has been moved.
+    module_invoke_all('file_move', $file, $source);
+
+    if ($delete_original) {
+      // Try to delete to remove original.
+      file_delete($source);
     }
-    drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $source->filepath)), 'error');
+
+    return $file;
   }
   return FALSE;
 }
@@ -570,7 +592,7 @@ function file_move($source, $destination
  *   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.
@@ -1114,9 +1136,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.
@@ -1129,6 +1153,11 @@ 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;
+    // If we're replacing an existing file re-use it's database record.
+    if ($replace == FILE_EXISTS_REPLACE && ($existing = file_load(array('filepath' => $filepath)))) {
+      $file->fid = $existing->fid;
+    }
     $file->filepath = $filepath;
     $file->filename = basename($file->filepath);
     $file->filemime = file_get_mimetype($file->filepath);
Index: modules/simpletest/tests/file.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file.test,v
retrieving revision 1.12
diff -u -p -r1.12 file.test
--- modules/simpletest/tests/file.test	16 Nov 2008 19:41:14 -0000	1.12
+++ modules/simpletest/tests/file.test	17 Nov 2008 08:15:59 -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 besides the
+   * timestamp.
+   *
+   * @param $a
+   *   File object to compare.
+   * @param $b
+   *   File object to compare.
+   */
+  function assertFileUnchanged($a, $b) {
+    $this->assertEqual($a->fid, $b->fid, t("File's id is the same."), 'File Unchanged');
+    $this->assertEqual($a->uid, $b->uid, t("File's owner is the same."), 'File Unchanged');
+    $this->assertEqual($a->filename, $b->filename, t("File's name is the same."), 'File Unchanged');
+    $this->assertEqual($a->filepath, $b->filepath, t("File's path is the same."), 'File Unchanged');
+    $this->assertEqual($a->filemime, $b->filemime, t("File's MIME type is the same."), 'File Unchanged');
+    $this->assertEqual($a->filesize, $b->filesize, t("File's size is the same."), 'File Unchanged');
+    $this->assertEqual($a->status, $b->status, t("File's 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;
   }
@@ -125,6 +179,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));
       }
@@ -841,19 +898,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($file->fid, TRUE);
+    $loaded_file = file_load($result->fid, TRUE);
     $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, $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);
+
+    // 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));
   }
 }
 
@@ -873,28 +1033,116 @@ 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->assertNotEqual($source->fid, $result->fid, t("A new file id was created."));
+    $this->assertNotEqual($source->filepath, $result->filepath, t("A new filepath was created."));
+    $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, TRUE);
-    $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 was 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 exampine 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 was 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));
   }
 }
 
@@ -1084,32 +1332,106 @@ 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($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');
+
+    // Verify that was 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');
+
+    // Verify that was 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($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($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');
+
+    // Verify that the existing file was re-used.
+    $this->assertSameFile($existing, $result);
+
+    // Verify that was 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);
+
+    // Ensure that the existing file wasn't ovewritten.
+    $this->assertFileUnchanged($existing, file_load($existing->fid, TRUE));
   }
 }
\ No newline at end of file
