diff --git a/core/modules/file/file.info b/core/modules/file/file.info
index 39daffc..dc93ab0 100644
--- a/core/modules/file/file.info
+++ b/core/modules/file/file.info
@@ -4,4 +4,3 @@ package = Core
 version = VERSION
 core = 8.x
 dependencies[] = field
-files[] = tests/file.test
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
new file mode 100644
index 0000000..d5fd817
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldDisplayTest.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileFieldDisplayTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests that formatters are working properly.
+ */
+class FileFieldDisplayTest extends FileFieldTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field display tests',
+      'description' => 'Test the display of file fields in node and views.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Tests normal formatter display on node display.
+   */
+  function testNodeDisplay() {
+    $field_name = strtolower($this->randomName());
+    $type_name = 'article';
+    $field_settings = array(
+      'display_field' => '1',
+      'display_default' => '1',
+    );
+    $instance_settings = array(
+      'description_field' => '1',
+    );
+    $widget_settings = array();
+    $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    // Create a new node *without* the file field set, and check that the field
+    // is not shown for each node display.
+    $node = $this->drupalCreateNode(array('type' => $type_name));
+    $file_formatters = array('file_default', 'file_table', 'file_url_plain', 'hidden');
+    foreach ($file_formatters as $formatter) {
+      $edit = array(
+        "fields[$field_name][type]" => $formatter,
+      );
+      $this->drupalPost("admin/structure/types/manage/$type_name/display", $edit, t('Save'));
+      $this->drupalGet('node/' . $node->nid);
+      $this->assertNoText($field_name, t('Field label is hidden when no file attached for formatter %formatter', array('%formatter' => $formatter)));
+    }
+
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $this->drupalGet('node/' . $nid . '/edit');
+
+    // Check that the default formatter is displaying with the file name.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $default_output = theme('file_link', array('file' => $node_file));
+    $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.'));
+
+    // Turn the "display" option off and check that the file is no longer displayed.
+    $edit = array($field_name . '[' . LANGUAGE_NOT_SPECIFIED . '][0][display]' => FALSE);
+    $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
+
+    $this->assertNoRaw($default_output, t('Field is hidden when "display" option is unchecked.'));
+
+  }
+}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
new file mode 100644
index 0000000..dd58f12
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldPathTest.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileFieldPathTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests that files are uploaded to proper locations.
+ */
+class FileFieldPathTest extends FileFieldTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field file path tests',
+      'description' => 'Test that files are uploaded to the proper location with token support.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Tests the normal formatter display on node display.
+   */
+  function testUploadPath() {
+    $field_name = strtolower($this->randomName());
+    $type_name = 'article';
+    $field = $this->createFileField($field_name, $type_name);
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file was uploaded to the file root.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
+
+    // Change the path to contain multiple subdirectories.
+    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => 'foo/bar/baz'));
+
+    // Upload a new file into the subdirectories.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file was uploaded into the subdirectory.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
+
+    // Check the path when used with tokens.
+    // Change the path to contain multiple token directories.
+    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => '[current-user:uid]/[current-user:name]'));
+
+    // Upload a new file into the token subdirectories.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file was uploaded into the subdirectory.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    // Do token replacement using the same user which uploaded the file, not
+    // the user running the test case.
+    $data = array('user' => $this->admin_user);
+    $subdirectory = token_replace('[user:uid]/[user:name]', $data);
+    $this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
+  }
+
+  /**
+   * Asserts that a file is uploaded to the right location.
+   *
+   * @param $expected_path
+   *   The location where the file is expected to be uploaded. Duplicate file
+   *   names to not need to be taken into account.
+   * @param $actual_path
+   *   Where the file was actually uploaded.
+   * @param $message
+   *   The message to display with this assertion.
+   */
+  function assertPathMatch($expected_path, $actual_path, $message) {
+    // Strip off the extension of the expected path to allow for _0, _1, etc.
+    // suffixes when the file hits a duplicate name.
+    $pos = strrpos($expected_path, '.');
+    $base_path = substr($expected_path, 0, $pos);
+    $extension = substr($expected_path, $pos + 1);
+
+    $result = preg_match('/' . preg_quote($base_path, '/') . '(_[0-9]+)?\.' . preg_quote($extension, '/') . '/', $actual_path);
+    $this->assertTrue($result, $message);
+  }
+}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
new file mode 100644
index 0000000..9f9448f
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldRevisionTest.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileFieldRevisionTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests file handling with node revisions.
+ */
+class FileFieldRevisionTest extends FileFieldTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File field revision test',
+      'description' => 'Test creating and deleting revisions with files attached.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Tests creating multiple revisions of a node and managing attached files.
+   *
+   * Expected behaviors:
+   *  - Adding a new revision will make another entry in the field table, but
+   *    the original file will not be duplicated.
+   *  - Deleting a revision should not delete the original file if the file
+   *    is in use by another revision.
+   *  - When the last revision that uses a file is deleted, the original file
+   *    should be deleted also.
+   */
+  function testRevisions() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    // Attach the same fields to users.
+    $this->attachFileField($field_name, 'user', 'user');
+
+    $test_file = $this->getTestFile('text');
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Check that the file exists on disk and in the database.
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r1 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $node_vid_r1 = $node->vid;
+    $this->assertFileExists($node_file_r1, t('New file saved to disk on node creation.'));
+    $this->assertFileEntryExists($node_file_r1, t('File entry exists in database on node creation.'));
+    $this->assertFileIsPermanent($node_file_r1, t('File is permanent.'));
+
+    // Upload another file to the same node in a new revision.
+    $this->replaceNodeFile($test_file, $field_name, $nid);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r2 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $node_vid_r2 = $node->vid;
+    $this->assertFileExists($node_file_r2, t('Replacement file exists on disk after creating new revision.'));
+    $this->assertFileEntryExists($node_file_r2, t('Replacement file entry exists in database after creating new revision.'));
+    $this->assertFileIsPermanent($node_file_r2, t('Replacement file is permanent.'));
+
+    // Check that the original file is still in place on the first revision.
+    $node = node_load($nid, $node_vid_r1, TRUE);
+    $this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0], t('Original file still in place after replacing file in new revision.'));
+    $this->assertFileExists($node_file_r1, t('Original file still in place after replacing file in new revision.'));
+    $this->assertFileEntryExists($node_file_r1, t('Original file entry still in place after replacing file in new revision'));
+    $this->assertFileIsPermanent($node_file_r1, t('Original file is still permanent.'));
+
+    // Save a new version of the node without any changes.
+    // Check that the file is still the same as the previous revision.
+    $this->drupalPost('node/' . $nid . '/edit', array('revision' => '1'), t('Save'));
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r3 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $node_vid_r3 = $node->vid;
+    $this->assertEqual($node_file_r2, $node_file_r3, t('Previous revision file still in place after creating a new revision without a new file.'));
+    $this->assertFileIsPermanent($node_file_r3, t('New revision file is permanent.'));
+
+    // Revert to the first revision and check that the original file is active.
+    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
+    $node = node_load($nid, NULL, TRUE);
+    $node_file_r4 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $node_vid_r4 = $node->vid;
+    $this->assertEqual($node_file_r1, $node_file_r4, t('Original revision file still in place after reverting to the original revision.'));
+    $this->assertFileIsPermanent($node_file_r4, t('Original revision file still permanent after reverting to the original revision.'));
+
+    // Delete the second revision and check that the file is kept (since it is
+    // still being used by the third revision).
+    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', array(), t('Delete'));
+    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting second revision, since it is being used by the third revision.'));
+    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting second revision, since it is being used by the third revision.'));
+    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting second revision, since it is being used by the third revision.'));
+
+    // Attach the second file to a user.
+    $user = $this->drupalCreateUser();
+    $user->{$field_name}[LANGUAGE_NOT_SPECIFIED][0] = (array) $node_file_r3;
+    $user->save();
+    $this->drupalGet('user/' . $user->uid . '/edit');
+
+    // Delete the third revision and check that the file is not deleted yet.
+    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', array(), t('Delete'));
+    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting third revision, since it is being used by the user.'));
+    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting third revision, since it is being used by the user.'));
+    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting third revision, since it is being used by the user.'));
+
+    // Delete the user and check that the file is also deleted.
+    user_delete($user->uid);
+    // TODO: This seems like a bug in File API. Clearing the stat cache should
+    // not be necessary here. The file really is deleted, but stream wrappers
+    // doesn't seem to think so unless we clear the PHP file stat() cache.
+    clearstatcache();
+
+    // Call system_cron() to clean up the file. Make sure the timestamp
+    // of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
+    db_update('file_managed')
+      ->fields(array(
+        'timestamp' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
+      ))
+      ->condition('fid', $node_file_r3->fid)
+      ->execute();
+    drupal_cron_run();
+
+    $this->assertFileNotExists($node_file_r3, t('Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
+    $this->assertFileEntryNotExists($node_file_r3, t('Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
+
+    // Delete the entire node and check that the original file is deleted.
+    $this->drupalPost('node/' . $nid . '/delete', array(), t('Delete'));
+    // Call system_cron() to clean up the file. Make sure the timestamp
+    // of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
+    db_update('file_managed')
+      ->fields(array(
+        'timestamp' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
+      ))
+      ->condition('fid', $node_file_r1->fid)
+      ->execute();
+    drupal_cron_run();
+    $this->assertFileNotExists($node_file_r1, t('Original file is deleted after deleting the entire node with two revisions remaining.'));
+    $this->assertFileEntryNotExists($node_file_r1, t('Original file entry is deleted after deleting the entire node with two revisions remaining.'));
+  }
+}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
new file mode 100644
index 0000000..d95cf86
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldTestBase.php
@@ -0,0 +1,228 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileFieldTestBase.
+ */
+
+namespace Drupal\file\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Provides methods specifically for testing File module's field handling.
+ */
+class FileFieldTestBase extends WebTestBase {
+  protected $profile = 'standard';
+
+  protected $admin_user;
+
+  function setUp() {
+    // Since this is a base class for many test cases, support the same
+    // flexibility that Drupal\simpletest\WebTestBase::setUp() has for the
+    // modules to be passed in as either an array or a variable number of string
+    // arguments.
+    $modules = func_get_args();
+    if (isset($modules[0]) && is_array($modules[0])) {
+      $modules = $modules[0];
+    }
+    $modules[] = 'file';
+    $modules[] = 'file_module_test';
+    parent::setUp($modules);
+    $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access'));
+    $this->drupalLogin($this->admin_user);
+  }
+
+  /**
+   * Retrieves a sample file of the specified type.
+   */
+  function getTestFile($type_name, $size = NULL) {
+    // Get a file to upload.
+    $file = current($this->drupalGetTestFiles($type_name, $size));
+
+    // Add a filesize property to files as would be read by file_load().
+    $file->filesize = filesize($file->uri);
+
+    return entity_create('file', (array) $file);
+  }
+
+  /**
+   * Retrieves the fid of the last inserted file.
+   */
+  function getLastFileId() {
+    return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
+  }
+
+  /**
+   * Creates a new file field.
+   *
+   * @param $name
+   *   The name of the new field (all lowercase), exclude the "field_" prefix.
+   * @param $type_name
+   *   The node type that this field will be added to.
+   * @param $field_settings
+   *   A list of field settings that will be added to the defaults.
+   * @param $instance_settings
+   *   A list of instance settings that will be added to the instance defaults.
+   * @param $widget_settings
+   *   A list of widget settings that will be added to the widget defaults.
+   */
+  function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
+    $field = array(
+      'field_name' => $name,
+      'type' => 'file',
+      'settings' => array(),
+      'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
+    );
+    $field['settings'] = array_merge($field['settings'], $field_settings);
+    field_create_field($field);
+
+    $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings);
+  }
+
+  /**
+   * Attaches a file field to an entity.
+   *
+   * @param $name
+   *   The name of the new field (all lowercase), exclude the "field_" prefix.
+   * @param $entity_type
+   *   The entity type this field will be added to.
+   * @param $bundle
+   *   The bundle this field will be added to.
+   * @param $field_settings
+   *   A list of field settings that will be added to the defaults.
+   * @param $instance_settings
+   *   A list of instance settings that will be added to the instance defaults.
+   * @param $widget_settings
+   *   A list of widget settings that will be added to the widget defaults.
+   */
+  function attachFileField($name, $entity_type, $bundle, $instance_settings = array(), $widget_settings = array()) {
+    $instance = array(
+      'field_name' => $name,
+      'label' => $name,
+      'entity_type' => $entity_type,
+      'bundle' => $bundle,
+      'required' => !empty($instance_settings['required']),
+      'settings' => array(),
+      'widget' => array(
+        'type' => 'file_generic',
+        'settings' => array(),
+      ),
+    );
+    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
+    field_create_instance($instance);
+  }
+
+  /**
+   * Updates an existing file field with new settings.
+   */
+  function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) {
+    $instance = field_info_instance('node', $name, $type_name);
+    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
+    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
+
+    field_update_instance($instance);
+  }
+
+  /**
+   * Uploads a file to a node.
+   */
+  function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE, $extras = array()) {
+    $langcode = LANGUAGE_NOT_SPECIFIED;
+    $edit = array(
+      "title" => $this->randomName(),
+      'revision' => (string) (int) $new_revision,
+    );
+
+    if (is_numeric($nid_or_type)) {
+      $nid = $nid_or_type;
+    }
+    else {
+      // Add a new node.
+      $extras['type'] = $nid_or_type;
+      $node = $this->drupalCreateNode($extras);
+      $nid = $node->nid;
+      // Save at least one revision to better simulate a real site.
+      $this->drupalCreateNode(get_object_vars($node));
+      $node = node_load($nid, NULL, TRUE);
+      $this->assertNotEqual($nid, $node->vid, t('Node revision exists.'));
+    }
+
+    // Attach a file to the node.
+    $edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($file->uri);
+    $this->drupalPost("node/$nid/edit", $edit, t('Save'));
+
+    return $nid;
+  }
+
+  /**
+   * Removes a file from a node.
+   *
+   * Note that if replacing a file, it must first be removed then added again.
+   */
+  function removeNodeFile($nid, $new_revision = TRUE) {
+    $edit = array(
+      'revision' => (string) (int) $new_revision,
+    );
+
+    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
+    $this->drupalPost(NULL, $edit, t('Save'));
+  }
+
+  /**
+   * Replaces a file within a node.
+   */
+  function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) {
+    $edit = array(
+      'files[' . $field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_0]' => drupal_realpath($file->uri),
+      'revision' => (string) (int) $new_revision,
+    );
+
+    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
+    $this->drupalPost(NULL, $edit, t('Save'));
+  }
+
+  /**
+   * Asserts that a file exists physically on disk.
+   */
+  function assertFileExists($file, $message = NULL) {
+    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
+    $this->assertTrue(is_file($file->uri), $message);
+  }
+
+  /**
+   * Asserts that a file exists in the database.
+   */
+  function assertFileEntryExists($file, $message = NULL) {
+    entity_get_controller('file')->resetCache();
+    $db_file = file_load($file->fid);
+    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
+    $this->assertEqual($db_file->uri, $file->uri, $message);
+  }
+
+  /**
+   * Asserts that a file does not exist on disk.
+   */
+  function assertFileNotExists($file, $message = NULL) {
+    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
+    $this->assertFalse(is_file($file->uri), $message);
+  }
+
+  /**
+   * Asserts that a file does not exist in the database.
+   */
+  function assertFileEntryNotExists($file, $message) {
+    entity_get_controller('file')->resetCache();
+    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
+    $this->assertFalse(file_load($file->fid), $message);
+  }
+
+  /**
+   * Asserts that a file's status is set to permanent in the database.
+   */
+  function assertFileIsPermanent($file, $message = NULL) {
+    $message = isset($message) ? $message : t('File %file is permanent.', array('%file' => $file->uri));
+    $this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
+  }
+}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
new file mode 100644
index 0000000..be37295
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileFieldValidateTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests various validations.
+ */
+class FileFieldValidateTest extends FileFieldTestBase {
+  protected $field;
+  protected $node_type;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'File field validation tests',
+      'description' => 'Tests validation functions such as file type, max file size, max size per node, and required.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Tests the required property on file fields.
+   */
+  function testRequired() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+
+    // Try to post a new node without uploading a file.
+    $langcode = LANGUAGE_NOT_SPECIFIED;
+    $edit = array("title" => $this->randomName());
+    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required file field was empty.'));
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $this->assertTrue($nid !== FALSE, t('uploadNodeFile(@test_file, @field_name, @type_name) succeeded', array('@test_file' => $test_file->uri, '@field_name' => $field_name, '@type_name' => $type_name)));
+
+    $node = node_load($nid, NULL, TRUE);
+
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertFileExists($node_file, t('File exists after uploading to the required field.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required field.'));
+
+    // Try again with a multiple value field.
+    field_delete_field($field_name);
+    $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
+
+    // Try to post a new node without uploading a file in the multivalue field.
+    $edit = array('title' => $this->randomName());
+    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
+    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required multiple value file field was empty.'));
+
+    // Create a new node with the uploaded file into the multivalue field.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertFileExists($node_file, t('File exists after uploading to the required multiple value field.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required multipel value field.'));
+
+    // Remove our file field.
+    field_delete_field($field_name);
+  }
+
+  /**
+   * Tests the max file size validator.
+   */
+  function testFileMaxSize() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $small_file = $this->getTestFile('text', 131072); // 128KB.
+    $large_file = $this->getTestFile('text', 1310720); // 1.2MB
+
+    // Test uploading both a large and small file with different increments.
+    $sizes = array(
+      '1M' => 1048576,
+      '1024K' => 1048576,
+      '1048576' => 1048576,
+    );
+
+    foreach ($sizes as $max_filesize => $file_limit) {
+      // Set the max file upload size.
+      $this->updateFileField($field_name, $type_name, array('max_filesize' => $max_filesize));
+      $instance = field_info_instance('node', $field_name, $type_name);
+
+      // Create a new node with the small file, which should pass.
+      $nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
+      $node = node_load($nid, NULL, TRUE);
+      $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+      $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
+      $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
+
+      // Check that uploading the large file fails (1M limit).
+      $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
+      $error_message = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($large_file->filesize), '%maxsize' => format_size($file_limit)));
+      $this->assertRaw($error_message, t('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
+    }
+
+    // Turn off the max filesize.
+    $this->updateFileField($field_name, $type_name, array('max_filesize' => ''));
+
+    // Upload the big file successfully.
+    $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
+
+    // Remove our file field.
+    field_delete_field($field_name);
+  }
+
+  /**
+   * Tests file extension checking.
+   */
+  function testFileExtension() {
+    $type_name = 'article';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('image');
+    list(, $test_file_extension) = explode('.', $test_file->filename);
+
+    // Disable extension checking.
+    $this->updateFileField($field_name, $type_name, array('file_extensions' => ''));
+
+    // Check that the file can be uploaded with no extension checking.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file with no extension checking.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with no extension checking.'));
+
+    // Enable extension checking for text files.
+    $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
+
+    // Check that the file with the wrong extension cannot be uploaded.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
+    $this->assertRaw($error_message, t('Node save failed when file uploaded with the wrong extension.'));
+
+    // Enable extension checking for text and image files.
+    $this->updateFileField($field_name, $type_name, array('file_extensions' => "txt $test_file_extension"));
+
+    // Check that the file can be uploaded with extension checking.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file with extension checking.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with extension checking.'));
+
+    // Remove our file field.
+    field_delete_field($field_name);
+  }
+}
diff --git a/core/modules/file/tests/file.test b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
similarity index 28%
rename from core/modules/file/tests/file.test
rename to core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
index 0f70aa6..a924874 100644
--- a/core/modules/file/tests/file.test
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldWidgetTest.php
@@ -2,327 +2,15 @@
 
 /**
  * @file
- * Tests for file.module.
+ * Definition of Drupal\file\Tests\FileFieldWidgetTest.
  */
 
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Provides methods specifically for testing File module's field handling.
- */
-class FileFieldTestCase extends WebTestBase {
-  protected $profile = 'standard';
-
-  protected $admin_user;
-
-  function setUp() {
-    // Since this is a base class for many test cases, support the same
-    // flexibility that Drupal\simpletest\WebTestBase::setUp() has for the
-    // modules to be passed in as either an array or a variable number of string
-    // arguments.
-    $modules = func_get_args();
-    if (isset($modules[0]) && is_array($modules[0])) {
-      $modules = $modules[0];
-    }
-    $modules[] = 'file';
-    $modules[] = 'file_module_test';
-    parent::setUp($modules);
-    $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer users', 'administer permissions', 'administer content types', 'administer nodes', 'bypass node access'));
-    $this->drupalLogin($this->admin_user);
-  }
-
-  /**
-   * Retrieves a sample file of the specified type.
-   */
-  function getTestFile($type_name, $size = NULL) {
-    // Get a file to upload.
-    $file = current($this->drupalGetTestFiles($type_name, $size));
-
-    // Add a filesize property to files as would be read by file_load().
-    $file->filesize = filesize($file->uri);
-
-    return entity_create('file', (array) $file);
-  }
-
-  /**
-   * Retrieves the fid of the last inserted file.
-   */
-  function getLastFileId() {
-    return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
-  }
-
-  /**
-   * Creates a new file field.
-   *
-   * @param $name
-   *   The name of the new field (all lowercase), exclude the "field_" prefix.
-   * @param $type_name
-   *   The node type that this field will be added to.
-   * @param $field_settings
-   *   A list of field settings that will be added to the defaults.
-   * @param $instance_settings
-   *   A list of instance settings that will be added to the instance defaults.
-   * @param $widget_settings
-   *   A list of widget settings that will be added to the widget defaults.
-   */
-  function createFileField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) {
-    $field = array(
-      'field_name' => $name,
-      'type' => 'file',
-      'settings' => array(),
-      'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1,
-    );
-    $field['settings'] = array_merge($field['settings'], $field_settings);
-    field_create_field($field);
-
-    $this->attachFileField($name, 'node', $type_name, $instance_settings, $widget_settings);
-  }
-
-  /**
-   * Attaches a file field to an entity.
-   *
-   * @param $name
-   *   The name of the new field (all lowercase), exclude the "field_" prefix.
-   * @param $entity_type
-   *   The entity type this field will be added to.
-   * @param $bundle
-   *   The bundle this field will be added to.
-   * @param $field_settings
-   *   A list of field settings that will be added to the defaults.
-   * @param $instance_settings
-   *   A list of instance settings that will be added to the instance defaults.
-   * @param $widget_settings
-   *   A list of widget settings that will be added to the widget defaults.
-   */
-  function attachFileField($name, $entity_type, $bundle, $instance_settings = array(), $widget_settings = array()) {
-    $instance = array(
-      'field_name' => $name,
-      'label' => $name,
-      'entity_type' => $entity_type,
-      'bundle' => $bundle,
-      'required' => !empty($instance_settings['required']),
-      'settings' => array(),
-      'widget' => array(
-        'type' => 'file_generic',
-        'settings' => array(),
-      ),
-    );
-    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
-    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
-    field_create_instance($instance);
-  }
-
-  /**
-   * Updates an existing file field with new settings.
-   */
-  function updateFileField($name, $type_name, $instance_settings = array(), $widget_settings = array()) {
-    $instance = field_info_instance('node', $name, $type_name);
-    $instance['settings'] = array_merge($instance['settings'], $instance_settings);
-    $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings);
-
-    field_update_instance($instance);
-  }
-
-  /**
-   * Uploads a file to a node.
-   */
-  function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE, $extras = array()) {
-    $langcode = LANGUAGE_NOT_SPECIFIED;
-    $edit = array(
-      "title" => $this->randomName(),
-      'revision' => (string) (int) $new_revision,
-    );
-
-    if (is_numeric($nid_or_type)) {
-      $nid = $nid_or_type;
-    }
-    else {
-      // Add a new node.
-      $extras['type'] = $nid_or_type;
-      $node = $this->drupalCreateNode($extras);
-      $nid = $node->nid;
-      // Save at least one revision to better simulate a real site.
-      $this->drupalCreateNode(get_object_vars($node));
-      $node = node_load($nid, NULL, TRUE);
-      $this->assertNotEqual($nid, $node->vid, t('Node revision exists.'));
-    }
-
-    // Attach a file to the node.
-    $edit['files[' . $field_name . '_' . $langcode . '_0]'] = drupal_realpath($file->uri);
-    $this->drupalPost("node/$nid/edit", $edit, t('Save'));
-
-    return $nid;
-  }
-
-  /**
-   * Removes a file from a node.
-   *
-   * Note that if replacing a file, it must first be removed then added again.
-   */
-  function removeNodeFile($nid, $new_revision = TRUE) {
-    $edit = array(
-      'revision' => (string) (int) $new_revision,
-    );
-
-    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
-    $this->drupalPost(NULL, $edit, t('Save'));
-  }
-
-  /**
-   * Replaces a file within a node.
-   */
-  function replaceNodeFile($file, $field_name, $nid, $new_revision = TRUE) {
-    $edit = array(
-      'files[' . $field_name . '_' . LANGUAGE_NOT_SPECIFIED . '_0]' => drupal_realpath($file->uri),
-      'revision' => (string) (int) $new_revision,
-    );
-
-    $this->drupalPost('node/' . $nid . '/edit', array(), t('Remove'));
-    $this->drupalPost(NULL, $edit, t('Save'));
-  }
-
-  /**
-   * Asserts that a file exists physically on disk.
-   */
-  function assertFileExists($file, $message = NULL) {
-    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
-    $this->assertTrue(is_file($file->uri), $message);
-  }
-
-  /**
-   * Asserts that a file exists in the database.
-   */
-  function assertFileEntryExists($file, $message = NULL) {
-    entity_get_controller('file')->resetCache();
-    $db_file = file_load($file->fid);
-    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
-    $this->assertEqual($db_file->uri, $file->uri, $message);
-  }
-
-  /**
-   * Asserts that a file does not exist on disk.
-   */
-  function assertFileNotExists($file, $message = NULL) {
-    $message = isset($message) ? $message : t('File %file exists on the disk.', array('%file' => $file->uri));
-    $this->assertFalse(is_file($file->uri), $message);
-  }
-
-  /**
-   * Asserts that a file does not exist in the database.
-   */
-  function assertFileEntryNotExists($file, $message) {
-    entity_get_controller('file')->resetCache();
-    $message = isset($message) ? $message : t('File %file exists in database at the correct path.', array('%file' => $file->uri));
-    $this->assertFalse(file_load($file->fid), $message);
-  }
-
-  /**
-   * Asserts that a file's status is set to permanent in the database.
-   */
-  function assertFileIsPermanent($file, $message = NULL) {
-    $message = isset($message) ? $message : t('File %file is permanent.', array('%file' => $file->uri));
-    $this->assertTrue($file->status == FILE_STATUS_PERMANENT, $message);
-  }
-}
-
-/**
- * Tests the 'managed_file' element type.
- *
- * @todo Create a FileTestCase base class and move FileFieldTestCase methods
- *   that aren't related to fields into it.
- */
-class FileManagedFileElementTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Managed file element test',
-      'description' => 'Tests the managed_file element type.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Tests the managed_file element type.
-   */
-  function testManagedFile() {
-    // Check that $element['#size'] is passed to the child upload element.
-    $this->drupalGet('file/test');
-    $this->assertFieldByXpath('//input[@name="files[nested_file]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.');
-
-    // Perform the tests with all permutations of $form['#tree'] and
-    // $element['#extended'].
-    foreach (array(0, 1) as $tree) {
-      foreach (array(0, 1) as $extended) {
-        $test_file = $this->getTestFile('text');
-        $path = 'file/test/' . $tree . '/' . $extended;
-        $input_base_name = $tree ? 'nested_file' : 'file';
-
-        // Submit without a file.
-        $this->drupalPost($path, array(), t('Save'));
-        $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submitted without a file.'));
-
-        // Submit a new file, without using the Upload button.
-        $last_fid_prior = $this->getLastFileId();
-        $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
-        $this->drupalPost($path, $edit, t('Save'));
-        $last_fid = $this->getLastFileId();
-        $this->assertTrue($last_fid > $last_fid_prior, t('New file got saved.'));
-        $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
-
-        // Submit no new input, but with a default file.
-        $this->drupalPost($path . '/' . $last_fid, array(), t('Save'));
-        $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Empty submission did not change an existing file.'));
-
-        // Now, test the Upload and Remove buttons, with and without Ajax.
-        foreach (array(FALSE, TRUE) as $ajax) {
-          // Upload, then Submit.
-          $last_fid_prior = $this->getLastFileId();
-          $this->drupalGet($path);
-          $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
-          if ($ajax) {
-            $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
-          }
-          else {
-            $this->drupalPost(NULL, $edit, t('Upload'));
-          }
-          $last_fid = $this->getLastFileId();
-          $this->assertTrue($last_fid > $last_fid_prior, t('New file got uploaded.'));
-          $this->drupalPost(NULL, array(), t('Save'));
-          $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
-
-          // Remove, then Submit.
-          $this->drupalGet($path . '/' . $last_fid);
-          if ($ajax) {
-            $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
-          }
-          else {
-            $this->drupalPost(NULL, array(), t('Remove'));
-          }
-          $this->drupalPost(NULL, array(), t('Save'));
-          $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file removal was successful.'));
-
-          // Upload, then Remove, then Submit.
-          $this->drupalGet($path);
-          $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
-          if ($ajax) {
-            $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
-            $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
-          }
-          else {
-            $this->drupalPost(NULL, $edit, t('Upload'));
-            $this->drupalPost(NULL, array(), t('Remove'));
-          }
-          $this->drupalPost(NULL, array(), t('Save'));
-          $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file upload and removal was successful.'));
-        }
-      }
-    }
-  }
-}
+namespace Drupal\file\Tests;
 
 /**
  * Tests file field widget.
  */
-class FileFieldWidgetTestCase extends FileFieldTestCase {
+class FileFieldWidgetTest extends FileFieldTestBase {
   public static function getInfo() {
     return array(
       'name' => 'File field widget test',
@@ -623,560 +311,3 @@ class FileFieldWidgetTestCase extends FileFieldTestCase {
   }
 
 }
-
-/**
- * Tests file handling with node revisions.
- */
-class FileFieldRevisionTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field revision test',
-      'description' => 'Test creating and deleting revisions with files attached.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Tests creating multiple revisions of a node and managing attached files.
-   *
-   * Expected behaviors:
-   *  - Adding a new revision will make another entry in the field table, but
-   *    the original file will not be duplicated.
-   *  - Deleting a revision should not delete the original file if the file
-   *    is in use by another revision.
-   *  - When the last revision that uses a file is deleted, the original file
-   *    should be deleted also.
-   */
-  function testRevisions() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    // Attach the same fields to users.
-    $this->attachFileField($field_name, 'user', 'user');
-
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file exists on disk and in the database.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r1 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $node_vid_r1 = $node->vid;
-    $this->assertFileExists($node_file_r1, t('New file saved to disk on node creation.'));
-    $this->assertFileEntryExists($node_file_r1, t('File entry exists in database on node creation.'));
-    $this->assertFileIsPermanent($node_file_r1, t('File is permanent.'));
-
-    // Upload another file to the same node in a new revision.
-    $this->replaceNodeFile($test_file, $field_name, $nid);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r2 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $node_vid_r2 = $node->vid;
-    $this->assertFileExists($node_file_r2, t('Replacement file exists on disk after creating new revision.'));
-    $this->assertFileEntryExists($node_file_r2, t('Replacement file entry exists in database after creating new revision.'));
-    $this->assertFileIsPermanent($node_file_r2, t('Replacement file is permanent.'));
-
-    // Check that the original file is still in place on the first revision.
-    $node = node_load($nid, $node_vid_r1, TRUE);
-    $this->assertEqual($node_file_r1, (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0], t('Original file still in place after replacing file in new revision.'));
-    $this->assertFileExists($node_file_r1, t('Original file still in place after replacing file in new revision.'));
-    $this->assertFileEntryExists($node_file_r1, t('Original file entry still in place after replacing file in new revision'));
-    $this->assertFileIsPermanent($node_file_r1, t('Original file is still permanent.'));
-
-    // Save a new version of the node without any changes.
-    // Check that the file is still the same as the previous revision.
-    $this->drupalPost('node/' . $nid . '/edit', array('revision' => '1'), t('Save'));
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r3 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $node_vid_r3 = $node->vid;
-    $this->assertEqual($node_file_r2, $node_file_r3, t('Previous revision file still in place after creating a new revision without a new file.'));
-    $this->assertFileIsPermanent($node_file_r3, t('New revision file is permanent.'));
-
-    // Revert to the first revision and check that the original file is active.
-    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
-    $node = node_load($nid, NULL, TRUE);
-    $node_file_r4 = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $node_vid_r4 = $node->vid;
-    $this->assertEqual($node_file_r1, $node_file_r4, t('Original revision file still in place after reverting to the original revision.'));
-    $this->assertFileIsPermanent($node_file_r4, t('Original revision file still permanent after reverting to the original revision.'));
-
-    // Delete the second revision and check that the file is kept (since it is
-    // still being used by the third revision).
-    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', array(), t('Delete'));
-    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting second revision, since it is being used by the third revision.'));
-    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting second revision, since it is being used by the third revision.'));
-    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting second revision, since it is being used by the third revision.'));
-
-    // Attach the second file to a user.
-    $user = $this->drupalCreateUser();
-    $user->{$field_name}[LANGUAGE_NOT_SPECIFIED][0] = (array) $node_file_r3;
-    $user->save();
-    $this->drupalGet('user/' . $user->uid . '/edit');
-
-    // Delete the third revision and check that the file is not deleted yet.
-    $this->drupalPost('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', array(), t('Delete'));
-    $this->assertFileExists($node_file_r3, t('Second file is still available after deleting third revision, since it is being used by the user.'));
-    $this->assertFileEntryExists($node_file_r3, t('Second file entry is still available after deleting third revision, since it is being used by the user.'));
-    $this->assertFileIsPermanent($node_file_r3, t('Second file entry is still permanent after deleting third revision, since it is being used by the user.'));
-
-    // Delete the user and check that the file is also deleted.
-    user_delete($user->uid);
-    // TODO: This seems like a bug in File API. Clearing the stat cache should
-    // not be necessary here. The file really is deleted, but stream wrappers
-    // doesn't seem to think so unless we clear the PHP file stat() cache.
-    clearstatcache();
-
-    // Call system_cron() to clean up the file. Make sure the timestamp
-    // of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
-    db_update('file_managed')
-      ->fields(array(
-        'timestamp' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
-      ))
-      ->condition('fid', $node_file_r3->fid)
-      ->execute();
-    drupal_cron_run();
-
-    $this->assertFileNotExists($node_file_r3, t('Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
-    $this->assertFileEntryNotExists($node_file_r3, t('Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.'));
-
-    // Delete the entire node and check that the original file is deleted.
-    $this->drupalPost('node/' . $nid . '/delete', array(), t('Delete'));
-    // Call system_cron() to clean up the file. Make sure the timestamp
-    // of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
-    db_update('file_managed')
-      ->fields(array(
-        'timestamp' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
-      ))
-      ->condition('fid', $node_file_r1->fid)
-      ->execute();
-    drupal_cron_run();
-    $this->assertFileNotExists($node_file_r1, t('Original file is deleted after deleting the entire node with two revisions remaining.'));
-    $this->assertFileEntryNotExists($node_file_r1, t('Original file entry is deleted after deleting the entire node with two revisions remaining.'));
-  }
-}
-
-/**
- * Tests that formatters are working properly.
- */
-class FileFieldDisplayTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field display tests',
-      'description' => 'Test the display of file fields in node and views.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Tests normal formatter display on node display.
-   */
-  function testNodeDisplay() {
-    $field_name = strtolower($this->randomName());
-    $type_name = 'article';
-    $field_settings = array(
-      'display_field' => '1',
-      'display_default' => '1',
-    );
-    $instance_settings = array(
-      'description_field' => '1',
-    );
-    $widget_settings = array();
-    $this->createFileField($field_name, $type_name, $field_settings, $instance_settings, $widget_settings);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    // Create a new node *without* the file field set, and check that the field
-    // is not shown for each node display.
-    $node = $this->drupalCreateNode(array('type' => $type_name));
-    $file_formatters = array('file_default', 'file_table', 'file_url_plain', 'hidden');
-    foreach ($file_formatters as $formatter) {
-      $edit = array(
-        "fields[$field_name][type]" => $formatter,
-      );
-      $this->drupalPost("admin/structure/types/manage/$type_name/display", $edit, t('Save'));
-      $this->drupalGet('node/' . $node->nid);
-      $this->assertNoText($field_name, t('Field label is hidden when no file attached for formatter %formatter', array('%formatter' => $formatter)));
-    }
-
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $this->drupalGet('node/' . $nid . '/edit');
-
-    // Check that the default formatter is displaying with the file name.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $default_output = theme('file_link', array('file' => $node_file));
-    $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.'));
-
-    // Turn the "display" option off and check that the file is no longer displayed.
-    $edit = array($field_name . '[' . LANGUAGE_NOT_SPECIFIED . '][0][display]' => FALSE);
-    $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save'));
-
-    $this->assertNoRaw($default_output, t('Field is hidden when "display" option is unchecked.'));
-
-  }
-}
-
-/**
- * Tests various validations.
- */
-class FileFieldValidateTestCase extends FileFieldTestCase {
-  protected $field;
-  protected $node_type;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'File field validation tests',
-      'description' => 'Tests validation functions such as file type, max file size, max size per node, and required.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Tests the required property on file fields.
-   */
-  function testRequired() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('text');
-
-    // Try to post a new node without uploading a file.
-    $langcode = LANGUAGE_NOT_SPECIFIED;
-    $edit = array("title" => $this->randomName());
-    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
-    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required file field was empty.'));
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $this->assertTrue($nid !== FALSE, t('uploadNodeFile(@test_file, @field_name, @type_name) succeeded', array('@test_file' => $test_file->uri, '@field_name' => $field_name, '@type_name' => $type_name)));
-
-    $node = node_load($nid, NULL, TRUE);
-
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertFileExists($node_file, t('File exists after uploading to the required field.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required field.'));
-
-    // Try again with a multiple value field.
-    field_delete_field($field_name);
-    $this->createFileField($field_name, $type_name, array('cardinality' => FIELD_CARDINALITY_UNLIMITED), array('required' => '1'));
-
-    // Try to post a new node without uploading a file in the multivalue field.
-    $edit = array('title' => $this->randomName());
-    $this->drupalPost('node/add/' . $type_name, $edit, t('Save'));
-    $this->assertRaw(t('!title field is required.', array('!title' => $instance['label'])), t('Node save failed when required multiple value file field was empty.'));
-
-    // Create a new node with the uploaded file into the multivalue field.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertFileExists($node_file, t('File exists after uploading to the required multiple value field.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading to the required multipel value field.'));
-
-    // Remove our file field.
-    field_delete_field($field_name);
-  }
-
-  /**
-   * Tests the max file size validator.
-   */
-  function testFileMaxSize() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array(), array('required' => '1'));
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $small_file = $this->getTestFile('text', 131072); // 128KB.
-    $large_file = $this->getTestFile('text', 1310720); // 1.2MB
-
-    // Test uploading both a large and small file with different increments.
-    $sizes = array(
-      '1M' => 1048576,
-      '1024K' => 1048576,
-      '1048576' => 1048576,
-    );
-
-    foreach ($sizes as $max_filesize => $file_limit) {
-      // Set the max file upload size.
-      $this->updateFileField($field_name, $type_name, array('max_filesize' => $max_filesize));
-      $instance = field_info_instance('node', $field_name, $type_name);
-
-      // Create a new node with the small file, which should pass.
-      $nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
-      $node = node_load($nid, NULL, TRUE);
-      $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-      $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
-      $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->filesize), '%maxsize' => $max_filesize)));
-
-      // Check that uploading the large file fails (1M limit).
-      $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
-      $error_message = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($large_file->filesize), '%maxsize' => format_size($file_limit)));
-      $this->assertRaw($error_message, t('Node save failed when file (%filesize) exceeded the max upload size (%maxsize).', array('%filesize' => format_size($large_file->filesize), '%maxsize' => $max_filesize)));
-    }
-
-    // Turn off the max filesize.
-    $this->updateFileField($field_name, $type_name, array('max_filesize' => ''));
-
-    // Upload the big file successfully.
-    $nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertFileExists($node_file, t('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->filesize))));
-
-    // Remove our file field.
-    field_delete_field($field_name);
-  }
-
-  /**
-   * Tests file extension checking.
-   */
-  function testFileExtension() {
-    $type_name = 'article';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('image');
-    list(, $test_file_extension) = explode('.', $test_file->filename);
-
-    // Disable extension checking.
-    $this->updateFileField($field_name, $type_name, array('file_extensions' => ''));
-
-    // Check that the file can be uploaded with no extension checking.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertFileExists($node_file, t('File exists after uploading a file with no extension checking.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with no extension checking.'));
-
-    // Enable extension checking for text files.
-    $this->updateFileField($field_name, $type_name, array('file_extensions' => 'txt'));
-
-    // Check that the file with the wrong extension cannot be uploaded.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $error_message = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => 'txt'));
-    $this->assertRaw($error_message, t('Node save failed when file uploaded with the wrong extension.'));
-
-    // Enable extension checking for text and image files.
-    $this->updateFileField($field_name, $type_name, array('file_extensions' => "txt $test_file_extension"));
-
-    // Check that the file can be uploaded with extension checking.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertFileExists($node_file, t('File exists after uploading a file with extension checking.'));
-    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with extension checking.'));
-
-    // Remove our file field.
-    field_delete_field($field_name);
-  }
-}
-
-/**
- * Tests that files are uploaded to proper locations.
- */
-class FileFieldPathTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File field file path tests',
-      'description' => 'Test that files are uploaded to the proper location with token support.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Tests the normal formatter display on node display.
-   */
-  function testUploadPath() {
-    $field_name = strtolower($this->randomName());
-    $type_name = 'article';
-    $field = $this->createFileField($field_name, $type_name);
-    $test_file = $this->getTestFile('text');
-
-    // Create a new node.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file was uploaded to the file root.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertPathMatch('public://' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
-
-    // Change the path to contain multiple subdirectories.
-    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => 'foo/bar/baz'));
-
-    // Upload a new file into the subdirectories.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file was uploaded into the subdirectory.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    $this->assertPathMatch('public://foo/bar/baz/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path.', array('%file' => $node_file->uri)));
-
-    // Check the path when used with tokens.
-    // Change the path to contain multiple token directories.
-    $field = $this->updateFileField($field_name, $type_name, array('file_directory' => '[current-user:uid]/[current-user:name]'));
-
-    // Upload a new file into the token subdirectories.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Check that the file was uploaded into the subdirectory.
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    // Do token replacement using the same user which uploaded the file, not
-    // the user running the test case.
-    $data = array('user' => $this->admin_user);
-    $subdirectory = token_replace('[user:uid]/[user:name]', $data);
-    $this->assertPathMatch('public://' . $subdirectory . '/' . $test_file->filename, $node_file->uri, t('The file %file was uploaded to the correct path with token replacements.', array('%file' => $node_file->uri)));
-  }
-
-  /**
-   * Asserts that a file is uploaded to the right location.
-   *
-   * @param $expected_path
-   *   The location where the file is expected to be uploaded. Duplicate file
-   *   names to not need to be taken into account.
-   * @param $actual_path
-   *   Where the file was actually uploaded.
-   * @param $message
-   *   The message to display with this assertion.
-   */
-  function assertPathMatch($expected_path, $actual_path, $message) {
-    // Strip off the extension of the expected path to allow for _0, _1, etc.
-    // suffixes when the file hits a duplicate name.
-    $pos = strrpos($expected_path, '.');
-    $base_path = substr($expected_path, 0, $pos);
-    $extension = substr($expected_path, $pos + 1);
-
-    $result = preg_match('/' . preg_quote($base_path, '/') . '(_[0-9]+)?\.' . preg_quote($extension, '/') . '/', $actual_path);
-    $this->assertTrue($result, $message);
-  }
-}
-
-/**
- * Tests the file token replacement in strings.
- */
-class FileTokenReplaceTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'File token replacement',
-      'description' => 'Generates text using placeholders for dummy content to check file token replacement.',
-      'group' => 'File',
-    );
-  }
-
-  /**
-   * Creates a file, then tests the tokens generated from it.
-   */
-  function testFileTokenReplacement() {
-    $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE);
-    $url_options = array(
-      'absolute' => TRUE,
-      'language' => $language_interface,
-    );
-
-    // Create file field.
-    $type_name = 'article';
-    $field_name = 'field_' . strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name);
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('node', $field_name, $type_name);
-
-    $test_file = $this->getTestFile('text');
-    // Coping a file to test uploads with non-latin filenames.
-    $filename = drupal_dirname($test_file->uri) . '/текстовый файл.txt';
-    $test_file = file_copy($test_file, $filename);
-
-    // Create a new node with the uploaded file.
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
-
-    // Load the node and the file.
-    $node = node_load($nid, NULL, TRUE);
-    $file = file_load($node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0]['fid']);
-
-    // Generate and test sanitized tokens.
-    $tests = array();
-    $tests['[file:fid]'] = $file->fid;
-    $tests['[file:name]'] = check_plain($file->filename);
-    $tests['[file:path]'] = check_plain($file->uri);
-    $tests['[file:mime]'] = check_plain($file->filemime);
-    $tests['[file:size]'] = format_size($file->filesize);
-    $tests['[file:url]'] = check_plain(file_create_url($file->uri));
-    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language_interface->langcode);
-    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language_interface->langcode);
-    $tests['[file:owner]'] = check_plain(user_format_name($this->admin_user));
-    $tests['[file:owner:uid]'] = $file->uid;
-
-    // Test to make sure that we generated something for each token.
-    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('file' => $file), array('language' => $language_interface));
-      $this->assertEqual($output, $expected, t('Sanitized file token %token replaced.', array('%token' => $input)));
-    }
-
-    // Generate and test unsanitized tokens.
-    $tests['[file:name]'] = $file->filename;
-    $tests['[file:path]'] = $file->uri;
-    $tests['[file:mime]'] = $file->filemime;
-    $tests['[file:size]'] = format_size($file->filesize);
-
-    foreach ($tests as $input => $expected) {
-      $output = token_replace($input, array('file' => $file), array('language' => $language_interface, 'sanitize' => FALSE));
-      $this->assertEqual($output, $expected, t('Unsanitized file token %token replaced.', array('%token' => $input)));
-    }
-  }
-}
-
-/**
- * Tests file access on private nodes.
- */
-class FilePrivateTestCase extends FileFieldTestCase {
-  public static function getInfo() {
-    return array(
-      'name' => 'Private file test',
-      'description' => 'Uploads a test to a private node and checks access.',
-      'group' => 'File',
-    );
-  }
-
-  function setUp() {
-    parent::setUp('node_access_test');
-    node_access_rebuild();
-    variable_set('node_access_test_private', TRUE);
-  }
-
-  /**
-   * Tests file access for file uploaded to a private node.
-   */
-  function testPrivateFile() {
-    // Use 'page' instead of 'article', so that the 'article' image field does
-    // not conflict with this test. If in the future the 'page' type gets its
-    // own default file or image field, this test can be made more robust by
-    // using a custom node type.
-    $type_name = 'page';
-    $field_name = strtolower($this->randomName());
-    $this->createFileField($field_name, $type_name, array('uri_scheme' => 'private'));
-
-    $test_file = $this->getTestFile('text');
-    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE));
-    $node = node_load($nid, NULL, TRUE);
-    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
-    // Ensure the file can be downloaded.
-    $this->drupalGet(file_create_url($node_file->uri));
-    $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
-    $this->drupalLogOut();
-    $this->drupalGet(file_create_url($node_file->uri));
-    $this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.'));
-  }
-}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php
new file mode 100644
index 0000000..9865314
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedFileElementTest.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileManagedFileElementTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests the 'managed_file' element type.
+ *
+ * @todo Create a FileTestBase class and move FileFieldTestBase methods
+ *   that aren't related to fields into it.
+ */
+class FileManagedFileElementTest extends FileFieldTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Managed file element test',
+      'description' => 'Tests the managed_file element type.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Tests the managed_file element type.
+   */
+  function testManagedFile() {
+    // Check that $element['#size'] is passed to the child upload element.
+    $this->drupalGet('file/test');
+    $this->assertFieldByXpath('//input[@name="files[nested_file]" and @size="13"]', NULL, 'The custom #size attribute is passed to the child upload element.');
+
+    // Perform the tests with all permutations of $form['#tree'] and
+    // $element['#extended'].
+    foreach (array(0, 1) as $tree) {
+      foreach (array(0, 1) as $extended) {
+        $test_file = $this->getTestFile('text');
+        $path = 'file/test/' . $tree . '/' . $extended;
+        $input_base_name = $tree ? 'nested_file' : 'file';
+
+        // Submit without a file.
+        $this->drupalPost($path, array(), t('Save'));
+        $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submitted without a file.'));
+
+        // Submit a new file, without using the Upload button.
+        $last_fid_prior = $this->getLastFileId();
+        $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+        $this->drupalPost($path, $edit, t('Save'));
+        $last_fid = $this->getLastFileId();
+        $this->assertTrue($last_fid > $last_fid_prior, t('New file got saved.'));
+        $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
+
+        // Submit no new input, but with a default file.
+        $this->drupalPost($path . '/' . $last_fid, array(), t('Save'));
+        $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Empty submission did not change an existing file.'));
+
+        // Now, test the Upload and Remove buttons, with and without Ajax.
+        foreach (array(FALSE, TRUE) as $ajax) {
+          // Upload, then Submit.
+          $last_fid_prior = $this->getLastFileId();
+          $this->drupalGet($path);
+          $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+          if ($ajax) {
+            $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
+          }
+          else {
+            $this->drupalPost(NULL, $edit, t('Upload'));
+          }
+          $last_fid = $this->getLastFileId();
+          $this->assertTrue($last_fid > $last_fid_prior, t('New file got uploaded.'));
+          $this->drupalPost(NULL, array(), t('Save'));
+          $this->assertRaw(t('The file id is %fid.', array('%fid' => $last_fid)), t('Submit handler has correct file info.'));
+
+          // Remove, then Submit.
+          $this->drupalGet($path . '/' . $last_fid);
+          if ($ajax) {
+            $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
+          }
+          else {
+            $this->drupalPost(NULL, array(), t('Remove'));
+          }
+          $this->drupalPost(NULL, array(), t('Save'));
+          $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file removal was successful.'));
+
+          // Upload, then Remove, then Submit.
+          $this->drupalGet($path);
+          $edit = array('files[' . $input_base_name . ']' => drupal_realpath($test_file->uri));
+          if ($ajax) {
+            $this->drupalPostAJAX(NULL, $edit, $input_base_name . '_upload_button');
+            $this->drupalPostAJAX(NULL, array(), $input_base_name . '_remove_button');
+          }
+          else {
+            $this->drupalPost(NULL, $edit, t('Upload'));
+            $this->drupalPost(NULL, array(), t('Remove'));
+          }
+          $this->drupalPost(NULL, array(), t('Save'));
+          $this->assertRaw(t('The file id is %fid.', array('%fid' => 0)), t('Submission after file upload and removal was successful.'));
+        }
+      }
+    }
+  }
+}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
new file mode 100644
index 0000000..42ee933
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FilePrivateTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests file access on private nodes.
+ */
+class FilePrivateTest extends FileFieldTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Private file test',
+      'description' => 'Uploads a test to a private node and checks access.',
+      'group' => 'File',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('node_access_test');
+    node_access_rebuild();
+    variable_set('node_access_test_private', TRUE);
+  }
+
+  /**
+   * Tests file access for file uploaded to a private node.
+   */
+  function testPrivateFile() {
+    // Use 'page' instead of 'article', so that the 'article' image field does
+    // not conflict with this test. If in the future the 'page' type gets its
+    // own default file or image field, this test can be made more robust by
+    // using a custom node type.
+    $type_name = 'page';
+    $field_name = strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name, array('uri_scheme' => 'private'));
+
+    $test_file = $this->getTestFile('text');
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE));
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    // Ensure the file can be downloaded.
+    $this->drupalGet(file_create_url($node_file->uri));
+    $this->assertResponse(200, t('Confirmed that the generated URL is correct by downloading the shipped file.'));
+    $this->drupalLogOut();
+    $this->drupalGet(file_create_url($node_file->uri));
+    $this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.'));
+  }
+}
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
new file mode 100644
index 0000000..57e9a1e
--- /dev/null
+++ b/core/modules/file/lib/Drupal/file/Tests/FileTokenReplaceTest.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\file\Tests\FileTokenReplaceTest.
+ */
+
+namespace Drupal\file\Tests;
+
+/**
+ * Tests the file token replacement in strings.
+ */
+class FileTokenReplaceTest extends FileFieldTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'File token replacement',
+      'description' => 'Generates text using placeholders for dummy content to check file token replacement.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * Creates a file, then tests the tokens generated from it.
+   */
+  function testFileTokenReplacement() {
+    $language_interface = drupal_container()->get(LANGUAGE_TYPE_INTERFACE);
+    $url_options = array(
+      'absolute' => TRUE,
+      'language' => $language_interface,
+    );
+
+    // Create file field.
+    $type_name = 'article';
+    $field_name = 'field_' . strtolower($this->randomName());
+    $this->createFileField($field_name, $type_name);
+    $field = field_info_field($field_name);
+    $instance = field_info_instance('node', $field_name, $type_name);
+
+    $test_file = $this->getTestFile('text');
+    // Coping a file to test uploads with non-latin filenames.
+    $filename = drupal_dirname($test_file->uri) . '/текстовый файл.txt';
+    $test_file = file_copy($test_file, $filename);
+
+    // Create a new node with the uploaded file.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+
+    // Load the node and the file.
+    $node = node_load($nid, NULL, TRUE);
+    $file = file_load($node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0]['fid']);
+
+    // Generate and test sanitized tokens.
+    $tests = array();
+    $tests['[file:fid]'] = $file->fid;
+    $tests['[file:name]'] = check_plain($file->filename);
+    $tests['[file:path]'] = check_plain($file->uri);
+    $tests['[file:mime]'] = check_plain($file->filemime);
+    $tests['[file:size]'] = format_size($file->filesize);
+    $tests['[file:url]'] = check_plain(file_create_url($file->uri));
+    $tests['[file:timestamp]'] = format_date($file->timestamp, 'medium', '', NULL, $language_interface->langcode);
+    $tests['[file:timestamp:short]'] = format_date($file->timestamp, 'short', '', NULL, $language_interface->langcode);
+    $tests['[file:owner]'] = check_plain(user_format_name($this->admin_user));
+    $tests['[file:owner:uid]'] = $file->uid;
+
+    // Test to make sure that we generated something for each token.
+    $this->assertFalse(in_array(0, array_map('strlen', $tests)), t('No empty tokens generated.'));
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('file' => $file), array('language' => $language_interface));
+      $this->assertEqual($output, $expected, t('Sanitized file token %token replaced.', array('%token' => $input)));
+    }
+
+    // Generate and test unsanitized tokens.
+    $tests['[file:name]'] = $file->filename;
+    $tests['[file:path]'] = $file->uri;
+    $tests['[file:mime]'] = $file->filemime;
+    $tests['[file:size]'] = format_size($file->filesize);
+
+    foreach ($tests as $input => $expected) {
+      $output = token_replace($input, array('file' => $file), array('language' => $language_interface, 'sanitize' => FALSE));
+      $this->assertEqual($output, $expected, t('Unsanitized file token %token replaced.', array('%token' => $input)));
+    }
+  }
+}
