diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 67dbd6c..bf77964 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -85,174 +85,69 @@ function filter_theme() {
 /**
  * Retrieves a list of enabled text formats, ordered by weight.
  *
- * @param \Drupal\Core\Session\AccountInterface|null $account
- *   (optional) If provided, only those formats that are allowed for this user
- *   account will be returned. All enabled formats will be returned otherwise.
- *   Defaults to NULL.
- *
- * @return \Drupal\filter\FilterFormatInterface[]
- *   An array of text format objects, keyed by the format ID and ordered by
- *   weight.
- *
- * @see filter_formats_reset()
+ * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
+ *   either \Drupal\filter\FilterFormatRepositoryInterface::getAllFormats() or
+ *   \Drupal\filter\FilterFormatRepositoryInterface::getFormatsForAccount().
  */
 function filter_formats(AccountInterface $account = NULL) {
-  $formats = &drupal_static(__FUNCTION__, array());
-
-  // All available formats are cached for performance.
-  if (!isset($formats['all'])) {
-    $language_interface = \Drupal::languageManager()->getCurrentLanguage();
-    if ($cache = \Drupal::cache()->get("filter_formats:{$language_interface->getId()}")) {
-      $formats['all'] = $cache->data;
-    }
-    else {
-      $formats['all'] = \Drupal::entityManager()->getStorage('filter_format')->loadByProperties(array('status' => TRUE));
-      uasort($formats['all'], 'Drupal\Core\Config\Entity\ConfigEntityBase::sort');
-      \Drupal::cache()->set("filter_formats:{$language_interface->getId()}", $formats['all'], Cache::PERMANENT, \Drupal::entityManager()->getDefinition('filter_format')->getListCacheTags());
-    }
-  }
+  $filter_format_repository = \Drupal::service('filter.filter_format_repository');
 
   // If no user was specified, return all formats.
   if (!isset($account)) {
-    return $formats['all'];
+    return $filter_format_repository->getAllFormats();
   }
 
-  // Build a list of user-specific formats.
-  $account_id = $account->id();
-  if (!isset($formats['user'][$account_id])) {
-    $formats['user'][$account_id] = array();
-    foreach ($formats['all'] as $format) {
-      if ($format->access('use', $account)) {
-        $formats['user'][$account_id][$format->id()] = $format;
-      }
-    }
-  }
-
-  return $formats['user'][$account_id];
+  return $filter_format_repository->getFormatsForAccount($account);
 }
 
 /**
  * Resets the text format caches.
  *
- * @see filter_formats()
+ * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\filter\FilterFormatRepositoryInterface::resetFormats().
  */
 function filter_formats_reset() {
-  drupal_static_reset('filter_formats');
+  \Drupal::service('filter.filter_format_repository')->resetFormats();
 }
 
 /**
  * Retrieves a list of roles that are allowed to use a given text format.
  *
- * @param \Drupal\filter\FilterFormatInterface $format
- *   An object representing the text format.
- *
- * @return array
- *   An array of role names, keyed by role ID.
+ * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\filter\FilterFormatInterface::getRoles().
  */
 function filter_get_roles_by_format(FilterFormatInterface $format) {
-  // Handle the fallback format upfront (all roles have access to this format).
-  if ($format->isFallbackFormat()) {
-    return user_role_names();
-  }
-  // Do not list any roles if the permission does not exist.
-  $permission = $format->getPermissionName();
-  return !empty($permission) ? user_role_names(FALSE, $permission) : array();
+  return $format->getRoles();
 }
 
 /**
  * Retrieves a list of text formats that are allowed for a given role.
  *
- * @param $rid
- *   The user role ID to retrieve text formats for.
- *
- * @return \Drupal\filter\FilterFormatInterface[]
- *   An array of text format objects that are allowed for the role, keyed by
- *   the text format ID and ordered by weight.
+ * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\filter\FilterFormatRepositoryInterface::getFormatsByRole().
  */
 function filter_get_formats_by_role($rid) {
-  $formats = array();
-  foreach (filter_formats() as $format) {
-    $roles = filter_get_roles_by_format($format);
-    if (isset($roles[$rid])) {
-      $formats[$format->id()] = $format;
-    }
-  }
-  return $formats;
+  return \Drupal::service('filter.filter_format_repository')->getFormatsByRole($rid);
 }
 
 /**
  * Returns the ID of the default text format for a particular user.
  *
- * The default text format is the first available format that the user is
- * allowed to access, when the formats are ordered by weight. It should
- * generally be used as a default choice when presenting the user with a list
- * of possible text formats (for example, in a node creation form).
- *
- * Conversely, when existing content that does not have an assigned text format
- * needs to be filtered for display, the default text format is the wrong
- * choice, because it is not guaranteed to be consistent from user to user, and
- * some trusted users may have an unsafe text format set by default, which
- * should not be used on text of unknown origin. Instead, the fallback format
- * returned by filter_fallback_format() should be used, since that is intended
- * to be a safe, consistent format that is always available to all users.
- *
- * @param \Drupal\Core\Session\AccountInterface|null $account
- *   (optional) The user account to check. Defaults to the currently logged-in
- *   user. Defaults to NULL.
- *
- * @return string
- *   The ID of the user's default text format.
- *
- * @see filter_fallback_format()
+ * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\filter\FilterFormatRepositoryInterface::getDefaultFormat().
  */
 function filter_default_format(AccountInterface $account = NULL) {
-  if (!isset($account)) {
-    $account = \Drupal::currentUser();
-  }
-  // Get a list of formats for this user, ordered by weight. The first one
-  // available is the user's default format.
-  $formats = filter_formats($account);
-  $format = reset($formats);
-  return $format->id();
+  return \Drupal::service('filter.filter_format_repository')->getDefaultFormat($account)->id();
 }
 
 /**
  * Returns the ID of the fallback text format that all users have access to.
  *
- * The fallback text format is a regular text format in every respect, except
- * it does not participate in the filter permission system and cannot be
- * disabled. It needs to exist because any user who has permission to create
- * formatted content must always have at least one text format they can use.
- *
- * Because the fallback format is available to all users, it should always be
- * configured securely. For example, when the Filter module is installed, this
- * format is initialized to output plain text. Installation profiles and site
- * administrators have the freedom to configure it further.
- *
- * Note that the fallback format is completely distinct from the default format,
- * which differs per user and is simply the first format which that user has
- * access to. The default and fallback formats are only guaranteed to be the
- * same for users who do not have access to any other format; otherwise, the
- * fallback format's weight determines its placement with respect to the user's
- * other formats.
- *
- * Any modules implementing a format deletion functionality must not delete this
- * format.
- *
- * @return
- *   The ID of the fallback text format.
- *
- * @see hook_filter_format_disable()
- * @see filter_default_format()
+ * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. Use
+ *   \Drupal\filter\FilterFormatRepositoryInterface::getFallbackFormatId().
  */
 function filter_fallback_format() {
-  // This variable is automatically set in the database for all installations
-  // of Drupal. In the event that it gets disabled or deleted somehow, there
-  // is no safe default to return, since we do not want to risk making an
-  // existing (and potentially unsafe) text format on the site automatically
-  // available to all users. Returning NULL at least guarantees that this
-  // cannot happen.
-  return \Drupal::config('filter.settings')->get('fallback_format');
+  return \Drupal::service('filter.filter_format_repository')->getFallbackFormatId();
 }
 
 /**
diff --git a/core/modules/filter/filter.services.yml b/core/modules/filter/filter.services.yml
index 996304a..9e9bea4 100644
--- a/core/modules/filter/filter.services.yml
+++ b/core/modules/filter/filter.services.yml
@@ -9,3 +9,8 @@ services:
       - { name: module_install.uninstall_validator }
     arguments: ['@plugin.manager.filter', '@entity.manager', '@string_translation']
     lazy: true
+
+  filter.filter_format_repository:
+    class: Drupal\filter\FilterFormatRepository
+    arguments: ['@entity.manager', '@language_manager', '@cache.default', '@config.factory', '@current_user']
+    lazy: true
diff --git a/core/modules/filter/src/Entity/FilterFormat.php b/core/modules/filter/src/Entity/FilterFormat.php
index cf61f7f..f79bb4c 100644
--- a/core/modules/filter/src/Entity/FilterFormat.php
+++ b/core/modules/filter/src/Entity/FilterFormat.php
@@ -406,6 +406,19 @@ public function removeFilter($instance_id) {
   /**
    * {@inheritdoc}
    */
+  public function getRoles() {
+    // Handle the fallback format upfront (all roles have access to this format).
+    if ($this->isFallbackFormat()) {
+      return user_role_names();
+    }
+    // Do not list any roles if the permission does not exist.
+    $permission = $this->getPermissionName();
+    return !empty($permission) ? user_role_names(FALSE, $permission) : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function onDependencyRemoval(array $dependencies) {
     $changed = parent::onDependencyRemoval($dependencies);
     $filters = $this->filters();
diff --git a/core/modules/filter/src/FilterFormatInterface.php b/core/modules/filter/src/FilterFormatInterface.php
index b9c558b..c637a31 100644
--- a/core/modules/filter/src/FilterFormatInterface.php
+++ b/core/modules/filter/src/FilterFormatInterface.php
@@ -92,4 +92,12 @@ public function getHtmlRestrictions();
    */
   public function removeFilter($instance_id);
 
+  /**
+   * Retrieves a list of roles that are allowed to use this format.
+   *
+   * @return string[]
+   *   An array of role names, keyed by role ID.
+   */
+  public function getRoles();
+
 }
diff --git a/core/modules/filter/src/FilterFormatRepository.php b/core/modules/filter/src/FilterFormatRepository.php
new file mode 100644
index 0000000..0a4a7a7
--- /dev/null
+++ b/core/modules/filter/src/FilterFormatRepository.php
@@ -0,0 +1,194 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\FilterFormatRepository.
+ */
+
+namespace Drupal\filter;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides a repository for filter formats.
+ */
+class FilterFormatRepository implements FilterFormatRepositoryInterface {
+
+  /**
+   * Stores all of the filter formats.
+   *
+   * @var \Drupal\filter\FilterFormatInterface[]
+   *   The array is keyed by format ID.
+   */
+  protected static $formatsAll = [];
+
+  /**
+   * Stores all of the filter formats for each user.
+   *
+   * @var \Drupal\filter\FilterFormatInterface[][]
+   *   The array is first keyed by user ID and then by format ID.
+   */
+  protected static $formatsUser = [];
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The current user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * Constructs a new FilterFormatRepository.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   The cache backend.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user account.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, CacheBackendInterface $cache_backend, ConfigFactoryInterface $config_factory, AccountInterface $account) {
+    $this->entityManager = $entity_manager;
+    $this->languageManager = $language_manager;
+    $this->cacheBackend = $cache_backend;
+    $this->configFactory = $config_factory;
+    $this->account = $account;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllFormats() {
+    $this->loadFormats();
+    return static::$formatsAll;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormatsForAccount(AccountInterface $account) {
+    $this->loadFormats();
+
+    // Build a list of user-specific formats.
+    $account_id = $account->id();
+    if (!isset(static::$formatsUser[$account_id])) {
+      static::$formatsUser[$account_id] = [];
+      foreach (static::$formatsAll as $format) {
+        if ($format->access('use', $account)) {
+          static::$formatsUser[$account_id][$format->id()] = $format;
+        }
+      }
+    }
+
+    return static::$formatsUser[$account_id];
+  }
+
+  /**
+   * Loads filter formats either from cache or from storage.
+   */
+  protected function loadFormats() {
+    // All available formats are cached for performance.
+    if (empty(static::$formatsAll)) {
+      $cache_key = 'filter_formats:' . $this->languageManager->getCurrentLanguage()->getId();
+
+      if ($cache = $this->cacheBackend->get($cache_key)) {
+        static::$formatsAll = $cache->data;
+      }
+      else {
+        static::$formatsAll = $this->entityManager->getStorage('filter_format')->loadByProperties(['status' => TRUE]);
+        $filter_format_definition = $this->entityManager->getDefinition('filter_format');
+        uasort(static::$formatsAll, [$filter_format_definition->getClass(), 'sort']);
+
+        $this->cacheBackend->set($cache_key, static::$formatsAll, Cache::PERMANENT, $filter_format_definition->getListCacheTags());
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormatsByRole($rid) {
+    $formats = [];
+    foreach ($this->getAllFormats() as $format) {
+      $roles = $format->getRoles();
+      if (isset($roles[$rid])) {
+        $formats[$format->id()] = $format;
+      }
+    }
+    return $formats;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultFormat(AccountInterface $account = NULL) {
+    if (!isset($account)) {
+      $account = $this->account;
+    }
+
+    // Get a list of formats for this user, ordered by weight. The first one
+    // available is the user's default format.
+    $formats = $this->getFormatsForAccount($account);
+    return reset($formats);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFallbackFormatId() {
+    // This variable is automatically set in the database for all installations
+    // of Drupal. In the event that it gets disabled or deleted somehow, there
+    // is no safe default to return, since we do not want to risk making an
+    // existing (and potentially unsafe) text format on the site automatically
+    // available to all users. Returning NULL at least guarantees that this
+    // cannot happen.
+    return $this->configFactory->get('filter.settings')->get('fallback_format');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetFormats() {
+    static::$formatsAll = [];
+    static::$formatsUser = [];
+    return $this;
+  }
+
+}
diff --git a/core/modules/filter/src/FilterFormatRepositoryInterface.php b/core/modules/filter/src/FilterFormatRepositoryInterface.php
new file mode 100644
index 0000000..8fedc9f
--- /dev/null
+++ b/core/modules/filter/src/FilterFormatRepositoryInterface.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\FilterFormatRepositoryInterface.
+ */
+
+namespace Drupal\filter;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides an interface for a repository for filter formats.
+ */
+interface FilterFormatRepositoryInterface {
+
+  /**
+   * Gets all enabled formats.
+   *
+   * @return \Drupal\filter\FilterFormatInterface[]
+   *   An array of text format objects, keyed by the format ID and ordered by
+   *   weight.
+   */
+  public function getAllFormats();
+
+  /**
+   * Gets only those formats which the specified account can use.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user account to check.
+   *
+   * @return \Drupal\filter\FilterFormatInterface[]
+   *   An array of text format objects, keyed by the format ID and ordered by
+   *   weight.
+   */
+  public function getFormatsForAccount(AccountInterface $account);
+
+  /**
+   * Gets a list of text formats that are allowed for a given role.
+   *
+   * @param string $rid
+   *   The user role ID to retrieve text formats for.
+   *
+   * @return \Drupal\filter\FilterFormatInterface[]
+   *   An array of text format objects that are allowed for the role, keyed by
+   *   the text format ID and ordered by weight.
+   */
+  public function getFormatsByRole($rid);
+
+  /**
+   * Gets the default text format for a particular user.
+   *
+   * The default text format is the first available format that the user is
+   * allowed to access, when the formats are ordered by weight. It should
+   * generally be used as a default choice when presenting the user with a list
+   * of possible text formats (for example, in a node creation form).
+   *
+   * Conversely, when existing content that does not have an assigned text
+   * format needs to be filtered for display, the default text format is the
+   * wrong choice, because it is not guaranteed to be consistent from user to
+   * user, and some trusted users may have an unsafe text format set by default,
+   * which should not be used on text of unknown origin. Instead, the fallback
+   * format returned by filter_fallback_format() should be used, since that is
+   * intended to be a safe, consistent format that is always available to all
+   * users.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   (optional) The user account to check. Defaults to the currently logged-in
+   *   user. Defaults to NULL.
+   *
+   * @return \Drupal\filter\FilterFormatInterface
+   */
+  public function getDefaultFormat(AccountInterface $account = NULL);
+
+  /**
+   * Gets the ID of the fallback text format that all users have access to.
+   *
+   * The fallback text format is a regular text format in every respect, except
+   * it does not participate in the filter permission system and cannot be
+   * disabled. It needs to exist because any user who has permission to create
+   * formatted content must always have at least one text format they can use.
+   *
+   * Because the fallback format is available to all users, it should always be
+   * configured securely. For example, when the Filter module is installed, this
+   * format is initialized to output plain text. Installation profiles and site
+   * administrators have the freedom to configure it further.
+   *
+   * Note that the fallback format is completely distinct from the default
+   * format, which differs per user and is simply the first format which that
+   * user has access to. The default and fallback formats are only guaranteed to
+   * be the same for users who do not have access to any other format;
+   * otherwise, the fallback format's weight determines its placement with
+   * respect to the user's other formats.
+   *
+   * Any modules implementing a format deletion functionality must not delete
+   * this format.
+   *
+   * @return string
+   *   The ID of the fallback text format.
+   *
+   * @see ::getDefaultFormat()
+   * @see hook_filter_format_disable()
+   */
+  public function getFallbackFormatId();
+
+  /**
+   * Resets the text format caches.
+   *
+   * @return $this
+   */
+  public function resetFormats();
+
+}
diff --git a/core/modules/filter/src/ProxyClass/FilterFormatRepository.php b/core/modules/filter/src/ProxyClass/FilterFormatRepository.php
new file mode 100644
index 0000000..5ecc937
--- /dev/null
+++ b/core/modules/filter/src/ProxyClass/FilterFormatRepository.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\ProxyClass\FilterFormatRepository.
+ */
+
+/**
+ * This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\filter\FilterFormatRepository' "core/modules/filter/src".
+ */
+
+namespace Drupal\filter\ProxyClass {
+
+    /**
+     * Provides a proxy class for \Drupal\filter\FilterFormatRepository.
+     *
+     * @see \Drupal\Component\ProxyBuilder
+     */
+    class FilterFormatRepository implements \Drupal\filter\FilterFormatRepositoryInterface
+    {
+
+        use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
+
+        /**
+         * The id of the original proxied service.
+         *
+         * @var string
+         */
+        protected $drupalProxyOriginalServiceId;
+
+        /**
+         * The real proxied service, after it was lazy loaded.
+         *
+         * @var \Drupal\filter\FilterFormatRepository
+         */
+        protected $service;
+
+        /**
+         * The service container.
+         *
+         * @var \Symfony\Component\DependencyInjection\ContainerInterface
+         */
+        protected $container;
+
+        /**
+         * Constructs a ProxyClass Drupal proxy object.
+         *
+         * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+         *   The container.
+         * @param string $drupal_proxy_original_service_id
+         *   The service ID of the original service.
+         */
+        public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
+        {
+            $this->container = $container;
+            $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+        }
+
+        /**
+         * Lazy loads the real service from the container.
+         *
+         * @return object
+         *   Returns the constructed real service.
+         */
+        protected function lazyLoadItself()
+        {
+            if (!isset($this->service)) {
+                $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+            }
+
+            return $this->service;
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function getAllFormats()
+        {
+            return $this->lazyLoadItself()->getAllFormats();
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function getFormatsForAccount(\Drupal\Core\Session\AccountInterface $account)
+        {
+            return $this->lazyLoadItself()->getFormatsForAccount($account);
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function getFormatsByRole($rid)
+        {
+            return $this->lazyLoadItself()->getFormatsByRole($rid);
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function getDefaultFormat(\Drupal\Core\Session\AccountInterface $account = NULL)
+        {
+            return $this->lazyLoadItself()->getDefaultFormat($account);
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function getFallbackFormatId()
+        {
+            return $this->lazyLoadItself()->getFallbackFormatId();
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function resetFormats()
+        {
+            return $this->lazyLoadItself()->resetFormats();
+        }
+
+    }
+
+}
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index 263b31e..0fac33d 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -438,7 +438,7 @@ public function testDependencyRemoval() {
     $filters = $filter_format->get('filters');
     $this->assertTrue(isset($filters['filter_test_restrict_tags_and_attributes']), 'The filter plugin filter_test_restrict_tags_and_attributes is configured by the filtered_html filter format.');
 
-    drupal_static_reset('filter_formats');
+    \Drupal::service('filter.filter_format_repository')->resetFormats();
     \Drupal::entityManager()->getStorage('filter_format')->resetCache();
     $module_data = _system_rebuild_module_data();
     $this->assertFalse(isset($module_data['filter_test']->info['required']), 'The filter_test module is required.');
diff --git a/core/modules/filter/tests/src/Unit/FilterFormatRepositoryTest.php b/core/modules/filter/tests/src/Unit/FilterFormatRepositoryTest.php
new file mode 100644
index 0000000..5a501ba
--- /dev/null
+++ b/core/modules/filter/tests/src/Unit/FilterFormatRepositoryTest.php
@@ -0,0 +1,274 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\filter\Unit\FilterFormatRepositoryTest.
+ */
+
+namespace Drupal\Tests\filter\Unit;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Entity\EntityType;
+use Drupal\filter\FilterFormatRepository;
+use Drupal\Tests\UnitTestCase;
+use Prophecy\Argument;
+
+/**
+ * @coversDefaultClass \Drupal\filter\FilterFormatRepository
+ * @group filter
+ */
+class FilterFormatRepositoryTest extends UnitTestCase {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $entityManager;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $languageManager;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $cacheBackend;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $configFactory;
+
+  /**
+   * The current user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $account;
+
+  /**
+   * The filter format repository under test.
+   *
+   * @var \Drupal\filter\FilterFormatRepository
+   */
+  protected $filterFormatRepository;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $current_language = $this->prophesize('\Drupal\Core\Language\LanguageInterface');
+    $current_language->getId()->willReturn('en');
+
+    $this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface');
+    $this->languageManager = $this->prophesize('\Drupal\Core\Language\LanguageManagerInterface');
+    $this->languageManager->getCurrentLanguage()->willReturn($current_language->reveal());
+
+    $this->cacheBackend = $this->prophesize('\Drupal\Core\Cache\CacheBackendInterface');
+    $this->configFactory = $this->prophesize('\Drupal\Core\Config\ConfigFactoryInterface');
+    $this->account = $this->prophesize('\Drupal\Core\Session\AccountInterface');
+
+    $this->filterFormatRepository = new FilterFormatRepository($this->entityManager->reveal(), $this->languageManager->reveal(), $this->cacheBackend->reveal(), $this->configFactory->reveal(), $this->account->reveal());
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @coversNothing
+   */
+  protected function tearDown() {
+    parent::tearDown();
+    $this->filterFormatRepository->resetFormats();
+  }
+
+  /**
+   * @covers ::getAllFormats
+   * @covers ::loadFormats
+   */
+  public function testGetAllFormatsFromStorage() {
+    $this->cacheBackend->get('filter_formats:en')->willReturn(NULL);
+
+    $filter_format_prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $filter_format = $filter_format_prophecy->reveal();
+
+    $entity_storage_prophecy = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface');
+    $entity_storage_prophecy->loadByProperties(['status' => TRUE])
+      ->willReturn([$filter_format]);
+
+    $this->entityManager->getStorage('filter_format')
+      ->willReturn($entity_storage_prophecy->reveal());
+
+    $this->entityManager->getDefinition('filter_format')
+      ->willReturn(new EntityType([
+        'id' => 'entity_type',
+        'class' => '\Drupal\filter\Entity\FilterFormat',
+      ]));
+
+    $this->cacheBackend->set('filter_formats:en', $filter_format, Cache::PERMANENT, ['entity_type_list']);
+    $this->cacheBackend->get('filter_formats:en')
+      ->willReturn((object) ['data' => [$filter_format]]);
+
+    $expected = [$filter_format];
+    $formats = $this->filterFormatRepository->getAllFormats();
+    $this->assertSame($expected, $formats);
+
+    // Ensure a second call doesn't load from storage or the cache backend.
+    $formats = $this->filterFormatRepository->getAllFormats();
+    $this->assertSame($expected, $formats);
+  }
+
+  /**
+   * @covers ::getAllFormats
+   * @covers ::loadFormats
+   */
+  public function testGetAllFormatsFromCache() {
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $filter_format = $prophecy->reveal();
+
+    $this->cacheBackend->get('filter_formats:en')
+      ->willReturn((object) ['data' => [$filter_format]]);
+    $this->entityManager->getStorage(Argument::any())->shouldNotBeCalled();
+
+    $expected = [$filter_format];
+    $formats = $this->filterFormatRepository->getAllFormats();
+    $this->assertSame($expected, $formats);
+    // Ensure a second call doesn't load from storage or the cache backend.
+    $formats = $this->filterFormatRepository->getAllFormats();
+    $this->assertSame($expected, $formats);
+  }
+
+  /**
+   * @covers ::getFormatsForAccount
+   */
+  public function testGetFormatsForAccount() {
+    $prophecy = $this->prophesize('\Drupal\Core\Session\AccountInterface');
+    $prophecy->id()->willReturn(5);
+    $account = $prophecy->reveal();
+
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_1');
+    $prophecy->access('use', $account)->willReturn(TRUE);
+    $filter_format1 = $prophecy->reveal();
+
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_2');
+    $prophecy->access('use', $account)->willReturn(FALSE);
+    $filter_format2 = $prophecy->reveal();
+
+    $this->cacheBackend->get('filter_formats:en')
+      ->willReturn((object) ['data' => [
+        'filter_format_1' => $filter_format1,
+        'filter_format_2' => $filter_format2,
+      ]]);
+
+    $formats = $this->filterFormatRepository->getFormatsForAccount($account);
+    $this->assertSame(['filter_format_1'], array_keys($formats));
+    $this->assertSame('filter_format_1', $formats['filter_format_1']->id());
+
+    $formats = $this->filterFormatRepository->getFormatsForAccount($account);
+    $this->assertSame(['filter_format_1'], array_keys($formats));
+    $this->assertSame('filter_format_1', $formats['filter_format_1']->id());
+  }
+
+  /**
+   * @covers ::getFormatsByRole
+   */
+  public function testGetFormatsByRole() {
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_1');
+    $prophecy->getRoles()->willReturn(['anonymous' => 'anonymous', 'admin' => 'admin']);
+    $filter_format1 = $prophecy->reveal();
+
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->shouldNotBeCalled();
+    $prophecy->getRoles()->willReturn(['anonymous' => 'anonymous']);
+    $filter_format2 = $prophecy->reveal();
+
+    $this->cacheBackend->get('filter_formats:en')
+      ->willReturn((object) ['data' => [
+        'filter_format_1' => $filter_format1,
+        'filter_format_2' => $filter_format2,
+      ]]);
+
+    $formats = $this->filterFormatRepository->getFormatsByRole('admin');
+    $this->assertSame(['filter_format_1'], array_keys($formats));
+    $this->assertSame('filter_format_1', $formats['filter_format_1']->id());
+  }
+
+  /**
+   * @covers ::getDefaultFormat
+   */
+  public function testGetDefaultFormatWithAccount() {
+    $prophecy = $this->prophesize('\Drupal\Core\Session\AccountInterface');
+    $prophecy->id()->willReturn(5);
+    $account = $prophecy->reveal();
+
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_1');
+    $prophecy->access('use', $account)->willReturn(TRUE);
+    $filter_format1 = $prophecy->reveal();
+
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_2');
+    $prophecy->access('use', $account)->willReturn(TRUE);
+    $filter_format2 = $prophecy->reveal();
+
+    $this->cacheBackend->get('filter_formats:en')
+      ->willReturn((object) ['data' => [
+        'filter_format_1' => $filter_format1,
+        'filter_format_2' => $filter_format2,
+      ]]);
+
+    $format = $this->filterFormatRepository->getDefaultFormat($account);
+    $this->assertSame($filter_format1, $format);
+  }
+
+  /**
+   * @covers ::getDefaultFormat
+   */
+  public function testGetDefaultFormatWithoutAccount() {
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_1');
+    $prophecy->access('use', $this->account)->willReturn(TRUE);
+    $filter_format1 = $prophecy->reveal();
+
+    $prophecy = $this->prophesize('\Drupal\filter\FilterFormatInterface');
+    $prophecy->id()->willReturn('filter_format_2');
+    $prophecy->access('use', $this->account)->willReturn(TRUE);
+    $filter_format2 = $prophecy->reveal();
+
+    $this->cacheBackend->get('filter_formats:en')
+      ->willReturn((object) ['data' => [
+        'filter_format_2' => $filter_format2,
+        'filter_format_1' => $filter_format1,
+      ]]);
+
+    $this->account->id()->willReturn(5);
+
+    $format = $this->filterFormatRepository->getDefaultFormat();
+    $this->assertSame($filter_format2, $format);
+  }
+
+  /**
+   * @covers ::getFallbackFormatId
+   */
+  public function testGetFallbackFormatId() {
+    $config_object = $this->prophesize('\Drupal\Core\Config\Config');
+    $config_object->get('fallback_format')->willReturn('the_filter_format_id');
+    $this->configFactory->get('filter.settings')->willReturn($config_object->reveal());
+
+    $expected = 'the_filter_format_id';
+    $format_id = $this->filterFormatRepository->getFallbackFormatId();
+    $this->assertSame($expected, $format_id);
+  }
+
+}
