diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 48ee728..cd3b334 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -8,7 +8,6 @@
 use \InvalidArgumentException;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Entity\EntityFieldQuery;
-use Drupal\Core\Entity\EntityMalformedException;
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Entity\EntityInterface;
 
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index 77ffa8c..dc2c025 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -32,8 +32,9 @@ public function __construct(array $values, $entity_type) {
     parent::__construct($values, $entity_type);
 
     // Backup the original ID, if any.
-    if ($original_id = $this->id()) {
-      $this->originalID = $original_id;
+    $original_id = $this->id();
+    if ($original_id !== NULL && $original_id !== '') {
+      $this->setOriginalID($original_id);
     }
   }
 
@@ -45,23 +46,21 @@ public function getOriginalID() {
   }
 
   /**
-   * Overrides Entity::isNew().
-   *
-   * EntityInterface::enforceIsNew() is not supported by configuration entities,
-   * since each configuration entity is unique.
+   * Implements ConfigEntityInterface::setOriginalID().
    */
-  final public function isNew() {
-    return !$this->id();
+  public function setOriginalID($id) {
+    $this->originalID = $id;
   }
 
   /**
-   * Overrides Entity::bundle().
+   * Overrides Entity::isNew().
    *
-   * EntityInterface::bundle() is not supported by configuration entities, since
-   * a configuration entity is a bundle.
+   * EntityInterface::enforceIsNew() is only supported for newly created
+   * configuration entities but has no effect after saving, since each
+   * configuration entity is unique.
    */
-  final public function bundle() {
-    return $this->entityType;
+  final public function isNew() {
+    return !empty($this->enforceIsNew) || $this->id() === NULL || $this->id() === '';
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
index a8b78e1..15ef4dd 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
@@ -22,4 +22,14 @@
    */
   public function getOriginalID();
 
+  /**
+   * Sets the original ID.
+   *
+   * @param string $id
+   *   The new ID to set as original ID.
+   *
+   * @return void
+   */
+  public function setOriginalID($id);
+
 }
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 07849c6..cb93873 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityMalformedException;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 
 /**
@@ -222,6 +223,12 @@ public function create(array $values) {
     $class = isset($this->entityInfo['entity class']) ? $this->entityInfo['entity class'] : 'Drupal\Core\Entity\Entity';
 
     $entity = new $class($values, $this->entityType);
+    // Mark this entity as new, so isNew() returns TRUE. This does not check
+    // whether a configuration entity with the same ID (if any) already exists.
+    // @todo If we expect isNew() to return FALSE when given an ID that exists
+    //   already, wrap this in !config($prefix . $id)->getStorage()->exists().
+    //   In that case, do we expect existing values to be merged into $values?
+    $entity->enforceIsNew();
 
     // Assign a new UUID if there is none yet.
     if (!isset($entity->{$this->uuidKey})) {
@@ -260,22 +267,29 @@ public function delete($ids) {
 
   /**
    * Implements Drupal\Core\Entity\EntityStorageControllerInterface::save().
+   *
+   * @throws EntityMalformedException
+   *   When attempting to save a configuration entity that has no ID.
    */
   public function save(EntityInterface $entity) {
     $prefix = $this->entityInfo['config prefix'] . '.';
 
+    $id = $entity->id();
+    if ($id === NULL || $id === '') {
+      throw new EntityMalformedException('The entity does not have an ID.');
+    }
+
     // Load the stored entity, if any.
-    if ($entity->getOriginalID()) {
+    if ($entity->getOriginalID() !== NULL) {
       $id = $entity->getOriginalID();
     }
-    else {
-      $id = $entity->id();
-    }
     $config = config($prefix . $id);
     $config->setName($prefix . $entity->id());
 
     if (!$config->isNew() && !isset($entity->original)) {
-      $entity->original = entity_load_unchanged($this->entityType, $id);
+      $this->resetCache(array($id));
+      $result = $this->load(array($id));
+      $entity->original = reset($result);
     }
 
     $this->preSave($entity);
@@ -295,6 +309,9 @@ public function save(EntityInterface $entity) {
       $config->save();
       $this->postSave($entity, TRUE);
       $this->invokeHook('update', $entity);
+
+      // Immediately update the original ID.
+      $entity->setOriginalID($entity->id());
     }
     else {
       $return = SAVED_NEW;
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
index cd48f71..0cca791 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
@@ -145,7 +145,7 @@ function testListUI() {
     $this->assertLink('Edit');
     $this->clickLink('Edit');
     $this->assertResponse(200);
-    $this->assertTitle('Edit test configuration | Drupal');
+    $this->assertTitle('Edit Antelope | Drupal');
     $edit = array('label' => 'Albatross', 'id' => 'albatross');
     $this->drupalPost(NULL, $edit, t('Save'));
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php
index 532979c..bb98ac0 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\config\Tests;
 
+use Drupal\Core\Entity\EntityMalformedException;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -30,13 +31,163 @@ public static function getInfo() {
   }
 
   /**
-   * Tests basic CRUD operations through the UI.
+   * Tests CRUD operations.
    */
   function testCRUD() {
+    // Verify default properties on a newly created empty entity.
+    $empty = entity_create('config_test', array());
+    $this->assertIdentical($empty->id, NULL);
+    $this->assertTrue($empty->uuid);
+    $this->assertIdentical($empty->label, NULL);
+    $this->assertIdentical($empty->style, NULL);
+    $this->assertIdentical($empty->langcode, LANGUAGE_NOT_SPECIFIED);
+
+    // Verify ConfigEntity properties/methods on the newly created empty entity.
+    $this->assertIdentical($empty->isNew(), TRUE);
+    $this->assertIdentical($empty->getOriginalID(), NULL);
+    $this->assertIdentical($empty->bundle(), 'config_test');
+    $this->assertIdentical($empty->id(), NULL);
+    $this->assertTrue($empty->uuid());
+    $this->assertIdentical($empty->label(), NULL);
+
+    $this->assertIdentical($empty->get('id'), NULL);
+    $this->assertTrue($empty->get('uuid'));
+    $this->assertIdentical($empty->get('label'), NULL);
+    $this->assertIdentical($empty->get('style'), NULL);
+    $this->assertIdentical($empty->get('langcode'), LANGUAGE_NOT_SPECIFIED);
+
+    // Verify Entity properties/methods on the newly created empty entity.
+    $this->assertIdentical($empty->isNewRevision(), FALSE);
+    $this->assertIdentical($empty->entityType(), 'config_test');
+    $uri = $empty->uri();
+    $this->assertIdentical($uri['path'], 'admin/structure/config_test/manage/');
+    $this->assertIdentical($empty->isDefaultRevision(), TRUE);
+
+    // Verify that an empty entity cannot be saved.
+    try {
+      $empty->save();
+      $this->fail('EntityMalformedException was thrown.');
+    }
+    catch (EntityMalformedException $e) {
+      $this->pass('EntityMalformedException was thrown.');
+    }
+
+    // Verify that an entity with an empty ID string is considered empty, too.
+    $empty_id = entity_create('config_test', array(
+      'id' => '',
+    ));
+    $this->assertIdentical($empty_id->isNew(), TRUE);
+    try {
+      $empty_id->save();
+      $this->fail('EntityMalformedException was thrown.');
+    }
+    catch (EntityMalformedException $e) {
+      $this->pass('EntityMalformedException was thrown.');
+    }
+
+    // Verify properties on a newly created entity.
+    $config_test = entity_create('config_test', $expected = array(
+      'id' => $this->randomName(),
+      'label' => $this->randomString(),
+      'style' => $this->randomName(),
+    ));
+    $this->assertIdentical($config_test->id, $expected['id']);
+    $this->assertTrue($config_test->uuid);
+    $this->assertNotEqual($config_test->uuid, $empty->uuid);
+    $this->assertIdentical($config_test->label, $expected['label']);
+    $this->assertIdentical($config_test->style, $expected['style']);
+    $this->assertIdentical($config_test->langcode, LANGUAGE_NOT_SPECIFIED);
+
+    // Verify methods on the newly created entity.
+    $this->assertIdentical($config_test->isNew(), TRUE);
+    $this->assertIdentical($config_test->getOriginalID(), $expected['id']);
+    $this->assertIdentical($config_test->id(), $expected['id']);
+    $this->assertTrue($config_test->uuid());
+    $expected['uuid'] = $config_test->uuid();
+    $this->assertIdentical($config_test->label(), $expected['label']);
+
+    $this->assertIdentical($config_test->isNewRevision(), FALSE);
+    $uri = $config_test->uri();
+    $this->assertIdentical($uri['path'], 'admin/structure/config_test/manage/' . $expected['id']);
+    $this->assertIdentical($config_test->isDefaultRevision(), TRUE);
+
+    // Verify that the entity can be saved.
+    try {
+      $status = $config_test->save();
+      $this->pass('EntityMalformedException was not thrown.');
+    }
+    catch (EntityMalformedException $e) {
+      $this->fail('EntityMalformedException was not thrown.');
+    }
+
+    // Verify that the correct status is returned and properties did not change.
+    $this->assertIdentical($status, SAVED_NEW);
+    $this->assertIdentical($config_test->id(), $expected['id']);
+    $this->assertIdentical($config_test->uuid(), $expected['uuid']);
+    $this->assertIdentical($config_test->label(), $expected['label']);
+    $this->assertIdentical($config_test->isNew(), FALSE);
+    $this->assertIdentical($config_test->getOriginalID(), $expected['id']);
+
+    // Save again, and verify correct status and properties again.
+    $status = $config_test->save();
+    $this->assertIdentical($status, SAVED_UPDATED);
+    $this->assertIdentical($config_test->id(), $expected['id']);
+    $this->assertIdentical($config_test->uuid(), $expected['uuid']);
+    $this->assertIdentical($config_test->label(), $expected['label']);
+    $this->assertIdentical($config_test->isNew(), FALSE);
+    $this->assertIdentical($config_test->getOriginalID(), $expected['id']);
+
+    // Re-create the entity with the same ID and verify updated status.
+    $same_id = entity_create('config_test', array(
+      'id' => $config_test->id(),
+    ));
+    $this->assertIdentical($same_id->isNew(), TRUE);
+    $status = $same_id->save();
+    $this->assertIdentical($status, SAVED_UPDATED);
+
+    // Verify that the entity was overwritten.
+    $same_id = entity_load('config_test', $config_test->id());
+    $this->assertIdentical($same_id->id(), $config_test->id());
+    // Note: Reloading loads from FileStorage, and FileStorage enforces strings.
+    $this->assertIdentical($same_id->label(), '');
+    $this->assertNotEqual($same_id->uuid(), $config_test->uuid());
+
+    // Revert to previous state.
+    $config_test->save();
+
+    // Verify that renaming the ID returns correct status and properties.
+    $ids = array($expected['id'], 'second_' . $this->randomName(4), 'third_' . $this->randomName(4));
+    for ($i = 1; $i < 3; $i++) {
+      $old_id = $ids[$i - 1];
+      $new_id = $ids[$i];
+      // Before renaming, everything should point to the current ID.
+      $this->assertIdentical($config_test->id(), $old_id);
+      $this->assertIdentical($config_test->getOriginalID(), $old_id);
+
+      // Rename.
+      $config_test->id = $new_id;
+      $this->assertIdentical($config_test->id(), $new_id);
+      $status = $config_test->save();
+      $this->assertIdentical($status, SAVED_UPDATED);
+      $this->assertIdentical($config_test->isNew(), FALSE);
+
+      // Verify that originalID points to new ID directly after renaming.
+      $this->assertIdentical($config_test->id(), $new_id);
+      $this->assertIdentical($config_test->getOriginalID(), $new_id);
+    }
+  }
+
+  /**
+   * Tests CRUD operations through the UI.
+   */
+  function testCRUDUI() {
     $id = strtolower($this->randomName());
     $label1 = $this->randomName();
     $label2 = $this->randomName();
     $label3 = $this->randomName();
+    $message_insert = format_string('%label configuration has been created.', array('%label' => $label1));
+    $message_update = format_string('%label configuration has been updated.', array('%label' => $label2));
+    $message_delete = format_string('%label configuration has been deleted.', array('%label' => $label2));
 
     // Create a configuration entity.
     $edit = array(
@@ -44,27 +195,36 @@ function testCRUD() {
       'label' => $label1,
     );
     $this->drupalPost('admin/structure/config_test/add', $edit, 'Save');
+    $this->assertUrl('admin/structure/config_test');
     $this->assertResponse(200);
-    $message_insert = format_string('%label configuration has been created.', array('%label' => $label1));
     $this->assertRaw($message_insert);
+    $this->assertNoRaw($message_update);
+    $this->assertLinkByHref("admin/structure/config_test/manage/$id/edit");
 
     // Update the configuration entity.
-    $this->assertLinkByHref('admin/structure/config_test/manage/' . $id);
     $edit = array(
       'label' => $label2,
     );
-    $this->drupalPost('admin/structure/config_test/manage/' . $id, $edit, 'Save');
+    $this->drupalPost("admin/structure/config_test/manage/$id", $edit, 'Save');
+    $this->assertUrl('admin/structure/config_test');
     $this->assertResponse(200);
-    $message_update = format_string('%label configuration has been updated.', array('%label' => $label2));
     $this->assertNoRaw($message_insert);
     $this->assertRaw($message_update);
+    $this->assertLinkByHref("admin/structure/config_test/manage/$id/edit");
+    $this->assertLinkByHref("admin/structure/config_test/manage/$id/delete");
 
     // Delete the configuration entity.
-    $this->assertLinkByHref('admin/structure/config_test/manage/' . $id . '/delete');
-    $this->drupalPost('admin/structure/config_test/manage/' . $id . '/delete', array(), 'Delete');
+    $this->drupalGet("admin/structure/config_test/manage/$id/edit");
+    $this->drupalPost(NULL, array(), 'Delete');
+    $this->assertUrl("admin/structure/config_test/manage/$id/delete");
+    $this->drupalPost(NULL, array(), 'Delete');
+    $this->assertUrl('admin/structure/config_test');
     $this->assertResponse(200);
+    $this->assertNoRaw($message_update);
+    $this->assertRaw($message_delete);
     $this->assertNoText($label1);
-    $this->assertNoText($label2);
+    $this->assertNoLinkByHref("admin/structure/config_test/manage/$id");
+    $this->assertNoLinkByHref("admin/structure/config_test/manage/$id/edit");
 
     // Re-create a configuration entity.
     $edit = array(
@@ -72,19 +232,26 @@ function testCRUD() {
       'label' => $label1,
     );
     $this->drupalPost('admin/structure/config_test/add', $edit, 'Save');
+    $this->assertUrl('admin/structure/config_test');
     $this->assertResponse(200);
     $this->assertText($label1);
+    $this->assertLinkByHref("admin/structure/config_test/manage/$id/edit");
 
     // Rename the configuration entity's ID/machine name.
-    $this->assertLinkByHref('admin/structure/config_test/manage/' . $id);
     $edit = array(
       'id' => strtolower($this->randomName()),
       'label' => $label3,
     );
-    $this->drupalPost('admin/structure/config_test/manage/' . $id, $edit, 'Save');
+    $this->drupalPost("admin/structure/config_test/manage/$id", $edit, 'Save');
+    $this->assertUrl('admin/structure/config_test');
     $this->assertResponse(200);
     $this->assertNoText($label1);
+    $this->assertNoText($label2);
     $this->assertText($label3);
+    $this->assertNoLinkByHref("admin/structure/config_test/manage/$id");
+    $this->assertNoLinkByHref("admin/structure/config_test/manage/$id/edit");
+    $id = $edit['id'];
+    $this->assertLinkByHref("admin/structure/config_test/manage/$id/edit");
   }
 
 }
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index 44df4da..d73f64a 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -35,9 +35,7 @@ function config_test_config_import_change($name, $new_config, $old_config) {
   $GLOBALS['hook_config_import'] = __FUNCTION__;
 
   // @todo Make this less ugly.
-  list($entity_type) = explode('.', $name);
-  $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config prefix']) + 1);
+  list(, , $id) = explode('.', $name);
   $config_test = entity_load('config_test', $id);
 
   // Store the original config, and iterate through each property to store it.
@@ -67,10 +65,9 @@ function config_test_config_import_delete($name, $new_config, $old_config) {
   $GLOBALS['hook_config_import'] = __FUNCTION__;
 
   // @todo Make this less ugly.
-  list($entity_type) = explode('.', $name);
-  $entity_info = entity_get_info($entity_type);
-  $id = substr($name, strlen($entity_info['config prefix']) + 1);
-  config_test_delete($id);
+  list(, , $id) = explode('.', $name);
+  $config_test = entity_load('config_test', $id);
+  $config_test->delete();
   return TRUE;
 }
 
@@ -83,6 +80,9 @@ function config_test_entity_info() {
     'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
     'entity class' => 'Drupal\config_test\ConfigTest',
     'list controller class' => 'Drupal\Core\Config\Entity\ConfigEntityListController',
+    'form controller class' => array(
+      'default' => 'Drupal\config_test\ConfigTestFormController',
+    ),
     'uri callback' => 'config_test_uri',
     'config prefix' => 'config_test.dynamic',
     'entity keys' => array(
@@ -117,15 +117,14 @@ function config_test_menu() {
   );
   $items['admin/structure/config_test/add'] = array(
     'title' => 'Add test configuration',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('config_test_form'),
+    'page callback' => 'config_test_add_page',
     'access callback' => TRUE,
     'type' => MENU_LOCAL_ACTION,
   );
   $items['admin/structure/config_test/manage/%config_test'] = array(
     'title' => 'Edit test configuration',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('config_test_form', 4),
+    'page callback' => 'config_test_edit_page',
+    'page arguments' => array(4),
     'access callback' => TRUE,
   );
   $items['admin/structure/config_test/manage/%config_test/edit'] = array(
@@ -154,26 +153,6 @@ function config_test_load($id) {
 }
 
 /**
- * Saves a ConfigTest object.
- *
- * @param Drupal\config_test\ConfigTest $config_test
- *   The ConfigTest object to save.
- */
-function config_test_save(ConfigTest $config_test) {
-  return $config_test->save();
-}
-
-/**
- * Deletes a ConfigTest object.
- *
- * @param string $id
- *   The ID of the ConfigTest object to delete.
- */
-function config_test_delete($id) {
-  entity_delete_multiple('config_test', array($id));
-}
-
-/**
  * Page callback; Lists available ConfigTest objects.
  */
 function config_test_list_page() {
@@ -182,79 +161,28 @@ function config_test_list_page() {
 }
 
 /**
- * Form constructor to add or edit a ConfigTest object.
+ * Page callback: Presents the ConfigTest creation form.
  *
- * @param Drupal\config_test\ConfigTest $config_test
- *   (optional) An existing ConfigTest object to edit. If omitted, the form
- *   creates a new ConfigTest.
+ * @return array
+ *   A form array as expected by drupal_render().
  */
-function config_test_form($form, &$form_state, ConfigTest $config_test = NULL) {
-  // Standard procedure for handling the entity argument in entity forms, taking
-  // potential form caching and rebuilds properly into account.
-  // @see http://drupal.org/node/1499596
-  if (!isset($form_state['config_test'])) {
-    if (!isset($config_test)) {
-      $config_test = entity_create('config_test', array());
-    }
-    $form_state['config_test'] = $config_test;
-  }
-  else {
-    $config_test = $form_state['config_test'];
-  }
-
-  $form['label'] = array(
-    '#type' => 'textfield',
-    '#title' => 'Label',
-    '#default_value' => $config_test->label(),
-    '#required' => TRUE,
-  );
-  $form['id'] = array(
-    '#type' => 'machine_name',
-    '#default_value' => $config_test->id(),
-    '#required' => TRUE,
-    '#machine_name' => array(
-      'exists' => 'config_test_load',
-      // @todo Update form_process_machine_name() to use 'label' by default.
-      'source' => array('label'),
-    ),
-  );
-  $form['style'] = array(
-    '#type' => 'select',
-    '#title' => 'Image style',
-    '#options' => array(),
-    '#default_value' => $config_test->get('style'),
-    '#access' => FALSE,
-  );
-  if (module_exists('image')) {
-    $form['style']['#access'] = TRUE;
-    $form['style']['#options'] = image_style_options();
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Save');
-
-  return $form;
+function config_test_add_page() {
+  $entity = entity_create('config_test', array());
+  return entity_get_form($entity);
 }
 
 /**
- * Form submission handler for config_test_form().
+ * Page callback: Presents the ConfigTest edit form.
+ *
+ * @param Drupal\config_test\ConfigTest $config_test
+ *   The ConfigTest object to edit.
+ *
+ * @return array
+ *   A form array as expected by drupal_render().
  */
-function config_test_form_submit($form, &$form_state) {
-  form_state_values_clean($form_state);
-
-  $config_test = $form_state['config_test'];
-  entity_form_submit_build_entity('config_test', $config_test, $form, $form_state);
-
-  $status = $config_test->save();
-
-  if ($status == SAVED_UPDATED) {
-    drupal_set_message(format_string('%label configuration has been updated.', array('%label' => $config_test->label())));
-  }
-  else {
-    drupal_set_message(format_string('%label configuration has been created.', array('%label' => $config_test->label())));
-  }
-
-  $form_state['redirect'] = 'admin/structure/config_test';
+function config_test_edit_page(ConfigTest $config_test) {
+  drupal_set_title(format_string('Edit %label', array('%label' => $config_test->label())), PASS_THROUGH);
+  return entity_get_form($config_test);
 }
 
 /**
@@ -280,5 +208,6 @@ function config_test_delete_form($form, &$form_state, ConfigTest $config_test) {
  */
 function config_test_delete_form_submit($form, &$form_state) {
   $form_state['config_test']->delete();
+  drupal_set_message(format_string('%label configuration has been deleted.', array('%label' => $form_state['config_test']->label())));
   $form_state['redirect'] = 'admin/structure/config_test';
 }
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php
new file mode 100644
index 0000000..ee66bbc
--- /dev/null
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestFormController.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\config_test\ConfigTestFormController.
+ */
+
+namespace Drupal\config_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+
+/**
+ * Form controller for the test config edit forms.
+ */
+class ConfigTestFormController extends EntityFormController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $entity) {
+    $form = parent::form($form, $form_state, $entity);
+
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Label',
+      '#default_value' => $entity->label(),
+      '#required' => TRUE,
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $entity->id(),
+      '#required' => TRUE,
+      '#machine_name' => array(
+        'exists' => 'config_test_load',
+        // @todo Update form_process_machine_name() to use 'label' by default.
+        'source' => array('label'),
+      ),
+    );
+    $form['style'] = array(
+      '#type' => 'select',
+      '#title' => 'Image style',
+      '#options' => array(),
+      '#default_value' => $entity->get('style'),
+      '#access' => FALSE,
+    );
+    if (module_exists('image')) {
+      $form['style']['#access'] = TRUE;
+      $form['style']['#options'] = image_style_options();
+    }
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => 'Save',
+    );
+    $form['actions']['delete'] = array(
+      '#type' => 'submit',
+      '#value' => 'Delete',
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $status = $entity->save();
+
+    if ($status === SAVED_UPDATED) {
+      drupal_set_message(format_string('%label configuration has been updated.', array('%label' => $entity->label())));
+    }
+    else {
+      drupal_set_message(format_string('%label configuration has been created.', array('%label' => $entity->label())));
+    }
+
+    $form_state['redirect'] = 'admin/structure/config_test';
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::delete().
+   */
+  public function delete(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $form_state['redirect'] = 'admin/structure/config_test/manage/' . $entity->id() . '/delete';
+  }
+
+}
