diff --git a/core/composer.json b/core/composer.json
index 4fee977c23..757a60774e 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -22,6 +22,7 @@
         "symfony/console": "~3.4.0",
         "symfony/dependency-injection": "~3.4.26",
         "symfony/event-dispatcher": "~3.4.0",
+        "symfony/finder": "~3.4.0",
         "symfony/http-foundation": "~3.4.27",
         "symfony/http-kernel": "~3.4.14",
         "symfony/routing": "~3.4.0",
diff --git a/core/core.console.services.yml b/core/core.console.services.yml
new file mode 100644
index 0000000000..c559a425e2
--- /dev/null
+++ b/core/core.console.services.yml
@@ -0,0 +1,24 @@
+parameters:
+  # Make the class a property so we can change it for testing.
+  console.app.class: Drupal\Core\Console\Application
+  # console.classloader is added in the console script.
+
+services:
+  console.app:
+    class: "%console.app.class%"
+
+  console.bootstrap:
+    class: Drupal\Core\Console\Bootstrap
+    arguments: ['%console.classloader%']
+
+  console.command.cache_clear:
+    class: Drupal\Core\Console\Command\ClearCache
+    arguments: ['@console.bootstrap']
+    tags:
+      - { name: console.command }
+
+  console.command.run_cron:
+    class: Drupal\Core\Console\Command\RunCron
+    arguments: ['@console.bootstrap']
+    tags:
+      - { name: console.command }
diff --git a/core/core.services.yml b/core/core.services.yml
index 04f75f64bc..606cdf0c56 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1724,3 +1724,27 @@ services:
     arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%']
     tags:
       - { name: backend_overridable }
+  console.app:
+    class: Drupal\Core\Console\Application
+  console.bootstrapper:
+    class: Drupal\Core\Console\Bootstrap
+    arguments: ['@class_loader']
+  console.install.command:
+   class: Drupal\Core\Command\InstallCommand
+   arguments: ['@class_loader']
+   tags:
+     - { name: console.command }
+  console.quickstart.command:
+   class: Drupal\Core\Command\QuickStartCommand
+   tags:
+     - { name: console.command }
+  console.server.command:
+   class: Drupal\Core\Command\ServerCommand
+   arguments: ['@class_loader']
+   tags:
+     - { name: console.command }
+  console.run_cron.command:
+   class: Drupal\Core\Console\Command\RunCron
+   arguments: ['@console.bootstrapper']
+   tags:
+     - { name: console.command }
diff --git a/core/lib/Drupal/Core/Console/Application.php b/core/lib/Drupal/Core/Console/Application.php
new file mode 100644
index 0000000000..24dcc53ffc
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Application.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Symfony\Component\Console\Application as GenericApplication;
+
+/**
+ * Console application for Drupal.
+ */
+class Application extends GenericApplication {
+
+  /**
+   * Constructs a new class instance.
+   */
+  public function __construct() {
+    parent::__construct('Drupal', \Drupal::VERSION);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/Bootstrap.php b/core/lib/Drupal/Core/Console/Bootstrap.php
new file mode 100644
index 0000000000..cfc38da920
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Bootstrap.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Composer\Autoload\ClassLoader;
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Bootstrap Drupal enough so that we can run a console command.
+ */
+class Bootstrap implements BootstrapInterface {
+
+  /**
+   * The class loader.
+   *
+   * @var \Composer\Autoload\ClassLoader
+   */
+  protected $classLoader;
+
+  /**
+   * Construct a Bootstrap object.
+   *
+   * @param \Composer\Autoload\ClassLoader $class_loader
+   *   The class loader.
+   */
+  public function __construct(ClassLoader $class_loader) {
+    $this->classLoader = $class_loader;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function bootstrap(Command $command, InputInterface $input, OutputInterface $output, Request $request = NULL) {
+    $env = $input->getOption('environment');
+    if (!$request) {
+      // Create a meaningful request object. We shouldn't really need this but
+      // Drupal complains if it's not present.
+      $request = Request::createFromGlobals();
+    }
+    try {
+      $kernel = DrupalKernel::createFromRequest($request, $this->classLoader, $env);
+      $kernel->boot();
+
+      $container = $kernel->getContainer();
+      $container->set('request', $request);
+      $container->get('request_stack')->push($request);
+
+      // @todo: Change this when we're not using includes any more.
+      require_once dirname(dirname(dirname(dirname(__DIR__)))) . '/includes/common.inc';
+      return $kernel;
+    }
+    catch (\Exception $e) {
+      /** @var \Symfony\Component\Console\Helper\FormatterHelper $formatter */
+      $formatter = $command->getHelperSet()->get('formatter');
+      $error_messages = [
+        'Insufficient Drupal to proceed.',
+        'This command must be run within an installed Drupal.',
+        $e->getMessage(),
+      ];
+      $formatted_block = $formatter->formatBlock($error_messages, 'error', TRUE);
+      $output->writeln($formatted_block);
+    }
+    return FALSE;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/BootstrapInterface.php b/core/lib/Drupal/Core/Console/BootstrapInterface.php
new file mode 100644
index 0000000000..a9de5f8dd4
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/BootstrapInterface.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines a bootstrap service.
+ */
+interface BootstrapInterface {
+
+  /**
+   * Bootstraps the Drupal we're working on.
+   *
+   * Callers should avoid calling this method until it's actually time to
+   * perform some action on the installed site.
+   *
+   * @param \Symfony\Component\Console\Command\Command $command
+   *   Console command object.
+   * @param \Symfony\Component\Console\Input\InputInterface $input
+   *   Console input object.
+   * @param \Symfony\Component\Console\Output\OutputInterface $output
+   *   Console output object.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   Request object for the kernel. If NULL, one will be generated from
+   *   globals.
+   *
+   * @returns \Drupal\Core\DrupalKernelInterface|false
+   *   The booted kernel, or FALSE if the bootstrap failed.
+   */
+  public function bootstrap(Command $command, InputInterface $input, OutputInterface $output, Request $request = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/Console/Command/CommandBootstrapBase.php b/core/lib/Drupal/Core/Console/Command/CommandBootstrapBase.php
new file mode 100644
index 0000000000..d4a1c82004
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Command/CommandBootstrapBase.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\Core\Console\Command;
+
+use Drupal\Core\Console\BootstrapInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputOption;
+
+/**
+ * Bootstraps Drupal so we can run a console command.
+ *
+ * Subclass this so you can write a command which requires a booted Drupal. If
+ * your command does not require a booted Drupal, don't subclass this.
+ */
+abstract class CommandBootstrapBase extends Command {
+
+  /**
+   * The bootstrap service.
+   *
+   * @var \Drupal\Core\Console\BootstrapInterface
+   */
+  protected $bootstrap;
+
+  /**
+   * Constructs a new class instance.
+   *
+   * @param \Drupal\Core\Console\BootstrapInterface $bootstrap
+   *   A bootstrap builder object.
+   */
+  public function __construct(BootstrapInterface $bootstrap) {
+    parent::__construct();
+    $this->bootstrap = $bootstrap;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    parent::configure();
+    $this->addOption(
+      'environment', 'e', InputOption::VALUE_REQUIRED, 'Kernel environment.', 'prod'
+    );
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/Command/RunCron.php b/core/lib/Drupal/Core/Console/Command/RunCron.php
new file mode 100644
index 0000000000..2261afee10
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/Command/RunCron.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\Core\Console\Command;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Runs cron from the console.
+ */
+class RunCron extends CommandBootstrapBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    parent::configure();
+    $this
+      ->setName('drupal:cron')
+      ->setAliases(['cron'])
+      ->setDescription('Performs a cron run.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $kernel = $this->bootstrap->bootstrap($this, $input, $output);
+    if ($kernel) {
+      $output->writeln('<info>Running cron...</info>');
+      /** @var \Drupal\Core\CronInterface $cron */
+      $cron = $kernel->getContainer()->get('cron');
+      $cron->run();
+      $output->writeln('<info>Done.</info>');
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/ConsoleServiceProvider.php b/core/lib/Drupal/Core/Console/ConsoleServiceProvider.php
new file mode 100644
index 0000000000..982bf05b54
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/ConsoleServiceProvider.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder as DrupalContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Compiles dependencies between the console app and commands we've discovered.
+ *
+ * If you are making a console command, you must define it as a service with the
+ * console.command tag.
+ *
+ * @code
+ *   console.name.command:
+ *     class: Name\Space\To\YourCommand
+ *     tags:
+ *       - { name: console.command }
+ * @endcode
+ */
+class ConsoleServiceProvider implements ServiceProviderInterface, CompilerPassInterface {
+
+  public function register(DrupalContainerBuilder $container) {
+    $container->addCompilerPass($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ContainerBuilder $container) {
+    $tagged_services = $container->findTaggedServiceIds('console.command');
+    $definition = $container->getDefinition('console.app');
+    foreach (array_keys($tagged_services) as $id) {
+      $definition->addMethodCall(
+        'add', [new Reference($id)]
+      );
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/ServicesFinder.php b/core/lib/Drupal/Core/Console/ServicesFinder.php
new file mode 100644
index 0000000000..baa32dee2b
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/ServicesFinder.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Drupal\Core\Discovery\DrupalFinder;
+
+class ServicesFinder extends DrupalFinder {
+
+  /**
+   * Constructs a new class instance.
+   */
+  public function __construct() {
+    parent::__construct();
+    $this
+      ->inDrupalCore()
+      ->inContrib()
+      ->excludeVendor()
+      ->files()
+      ->ignoreUnreadableDirs()
+      ->ignoreVCS(TRUE)
+      ->name('*.console.services.yml');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Discovery/DrupalFinder.php b/core/lib/Drupal/Core/Discovery/DrupalFinder.php
new file mode 100644
index 0000000000..41e365872a
--- /dev/null
+++ b/core/lib/Drupal/Core/Discovery/DrupalFinder.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Drupal\Core\Discovery;
+
+use Symfony\Component\Finder\Finder;
+
+/**
+ * Provides a finder with helpers for searching Drupal directories.
+ */
+class DrupalFinder extends Finder {
+
+  /**
+   * The path to the Drupal root directory.
+   *
+   * @var string
+   */
+  protected $drupalRoot = '';
+
+  /**
+   * Constructs a new class instance.
+   *
+   * @param string $drupal_root
+   *   (optional) Path to initialize as the root of the Drupal installation.
+   */
+  public function __construct($drupal_root = NULL) {
+    parent::__construct();
+    // Guess where the root of Drupal is.
+    if (!$drupal_root) {
+      $drupal_root = __DIR__ . '/../../../../..';
+    }
+    $this->drupalRoot = $drupal_root;
+  }
+
+  /**
+   * Gets the path to the root of the Drupal installation.
+   *
+   * @return string
+   *   The path to the root of the Drupal installation.
+   */
+  public function getDrupalRootPath() {
+    return $this->drupalRoot;
+  }
+
+  /**
+   * Gets the path to the Drupal installation's ./core directory.
+   *
+   * @return string
+   *   The path to the Drupal installation's core directory.
+   */
+  public function getDrupalCorePath() {
+    return $this->getDrupalRootPath() . '/core';
+  }
+
+  /**
+   * Sets the iterator to search the Drupal root directory.
+   *
+   * @return $this
+   */
+  public function inDrupalRoot() {
+    return $this->in($this->getDrupalRootPath());
+  }
+
+  /**
+   * Sets the iterator to search the Drupal core directory.
+   *
+   * @return $this
+   */
+  public function inDrupalCore() {
+    return $this->in($this->getDrupalCorePath());
+  }
+
+  /**
+   * Sets the iterator to search within Drupal's contrib directories.
+   *
+   * @return $this
+   */
+  public function inContrib() {
+    $this->in($this->getDrupalRootPath() . '/modules');
+    return $this->in($this->getDrupalRootPath() . '/sites');
+  }
+
+  /**
+   * Sets the iterator to exclude Composer's vendor directory.
+   *
+   * @return $this
+   */
+  public function excludeVendor() {
+    return $this->exclude($this->getDrupalCorePath() . '/vendor');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index e3607d237b..030b288611 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -617,6 +617,10 @@ public function discoverServiceProviders() {
     $this->serviceYamls['app']['core'] = 'core/core.services.yml';
     $this->serviceProviderClasses['app']['core'] = 'Drupal\Core\CoreServiceProvider';
 
+    if ($this->environment === 'console') {
+      $this->serviceProviderClasses['app']['console'] = 'Drupal\Core\Console\ConsoleServiceProvider';
+    }
+
     // Retrieve enabled modules and register their namespaces.
     if (!isset($this->moduleList)) {
       $extensions = $this->getConfigStorage()->read('core.extension');
@@ -900,7 +904,8 @@ protected function initializeContainer() {
 
     // If the module list hasn't already been set in updateModules and we are
     // not forcing a rebuild, then try and load the container from the cache.
-    if (empty($this->moduleList) && !$this->containerNeedsRebuild) {
+    // @todo Make the cached definition available for the console environment.
+    if (empty($this->moduleList) && !$this->containerNeedsRebuild && $this->environment !== 'console') {
       $container_definition = $this->getCachedContainerDefinition();
     }
 
@@ -949,6 +954,9 @@ protected function initializeContainer() {
       $this->container->get('current_user')->setInitialAccountId($current_user_id);
     }
 
+    // Some services need the class loader.
+    $this->container->set('class_loader', $this->classLoader);
+
     \Drupal::setContainer($this->container);
 
     // Allow other parts of the codebase to react on container initialization in
diff --git a/core/modules/system/src/Command/ClearCache.php b/core/modules/system/src/Command/ClearCache.php
new file mode 100644
index 0000000000..82aef678e2
--- /dev/null
+++ b/core/modules/system/src/Command/ClearCache.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\system\Command;
+
+use Drupal\Core\Console\Command\CommandBootstrapBase;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Clears all caches.
+ */
+class ClearCache extends CommandBootstrapBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    parent::configure();
+    // @todo Rename this to be cache-rebuild.
+    $this
+      ->setName('drupal:cache-clear')
+      ->setAliases(['cc'])
+      ->setDescription('Clears all caches.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    /* @var $kernel \Drupal\Core\DrupalKernelInterface */
+    $kernel = $this->bootstrap->bootstrap($this, $input, $output);
+    if ($kernel) {
+      $this->doRebuild($kernel->getContainer());
+      $output->writeln('<info>Caches cleared.</info>');
+    }
+    else {
+      // Attempts to bootstrap should fail before we see this error, but let's
+      // emit one anyway.
+      $output->write('<error>Unable to clear caches.</error>');
+    }
+  }
+
+  protected function doRebuild(ContainerInterface $container) {
+    // The drupal_rebuild() function isn't autoloaded, so we have to do things
+    // the old-fashioned way.
+    // @todo Change this once
+    //   https://www.drupal.org/project/drupal/issues/3014752 is in.
+    include_once $container->get('app.root') . '/core/includes/utility.inc';
+    drupal_rebuild($container->get('class_loader'), $container->get('request'));
+  }
+
+}
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index 0728e0da86..702f084ce9 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -43,3 +43,8 @@ services:
     arguments: ['@theme_handler', '@cache_tags.invalidator']
     tags:
       - { name: event_subscriber }
+  console.clear_cache.command:
+   class: Drupal\system\Command\ClearCache
+   arguments: ['@console.bootstrapper']
+   tags:
+     - { name: console.command }
diff --git a/core/scripts/console b/core/scripts/console
new file mode 100755
index 0000000000..b9f48b029d
--- /dev/null
+++ b/core/scripts/console
@@ -0,0 +1,26 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * @file
+ * Contains the Drupal core console.
+ */
+
+$class_loader = require_once __DIR__ . '/../../autoload.php';
+
+use Drupal\Component\FileCache\FileCacheFactory;
+use Drupal\Core\Console\CompilerPass;
+use Drupal\Core\Console\ServicesFinder;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\YamlFileLoader;
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $class_loader, 'console');
+$kernel->boot();
+
+$container = $kernel->getContainer();
+
+/* @var $app \Symfony\Component\Console\Application */
+$app = $container->get('console.app');
+$app->run();
diff --git a/core/tests/Drupal/Tests/Core/Console/ApplicationTest.php b/core/tests/Drupal/Tests/Core/Console/ApplicationTest.php
new file mode 100644
index 0000000000..437f81b824
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/ApplicationTest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Tests\Core\Console;
+
+use Drupal\Core\Console\Application;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Console\Application
+ *
+ * @group Console
+ */
+class ApplicationTest extends UnitTestCase {
+
+  /**
+   * @covers ::__construct
+   */
+  public function testConstruct() {
+    $app = new Application();
+    $this->assertEquals('Drupal', $app->getName());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Console/Command/ClearCacheTest.php b/core/tests/Drupal/Tests/Core/Console/Command/ClearCacheTest.php
new file mode 100644
index 0000000000..837bd9090f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/Command/ClearCacheTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Drupal\Tests\Core\Console\Command;
+
+use Drupal\Core\Console\BootstrapInterface;
+use Drupal\Core\Console\Command\ClearCache;
+use Drupal\Core\DrupalKernelInterface;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Test the clear cache command.
+ *
+ * @coversDefaultClass \Drupal\Core\Console\Command\ClearCache
+ *
+ * @group Console
+ * @group Command
+ */
+class ClearCacheTest extends UnitTestCase {
+
+  /**
+   * Test that we can load and reach the drupal:cache-clear command.
+   * @covers ::execute
+   */
+  public function testExecute() {
+    $bootstrap = $this->getMockBuilder(BootstrapInterface::class)
+      ->setMethods(['bootstrap'])
+      ->getMockForAbstractClass();
+
+    $kernel = $this->getMockBuilder(DrupalKernelInterface::class)
+      ->setMethods(['getContainer'])
+      ->getMockForAbstractClass();
+
+    $bootstrap->expects($this->once())
+      ->method('bootstrap')
+      ->will($this->returnValue($kernel));
+
+    $container = $this->getMockBuilder(ContainerInterface::class)
+      ->getMockForAbstractClass();
+
+    $kernel->expects($this->once())
+      ->method('getContainer')
+      ->willReturn($container);
+
+    $command = $this->getMockBuilder(ClearCache::class)
+      ->setMethods(['doRebuild'])
+      ->setConstructorArgs([$bootstrap])
+      ->getMock();
+    $command->expects($this->once())
+      ->method('doRebuild');
+
+    $input = $this->getMockForAbstractClass(InputInterface::class);
+
+    $output = $this->getMockBuilder(OutputInterface::class)
+      ->setMethods(['writeln'])
+      ->getMockForAbstractClass();
+    $output->expects($this->atLeastOnce())
+      ->method('writeln');
+
+    $method = new \ReflectionMethod($command, 'execute');
+    $method->setAccessible(TRUE);
+    $method->invokeArgs($command, [$input, $output]);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Console/Command/RunCronTest.php b/core/tests/Drupal/Tests/Core/Console/Command/RunCronTest.php
new file mode 100644
index 0000000000..926f9fe87f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/Command/RunCronTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Drupal\Tests\Core\Console\Command;
+
+use Drupal\Core\Console\BootstrapInterface;
+use Drupal\Core\Console\Command\RunCron;
+use Drupal\Core\CronInterface;
+use Drupal\Core\DrupalKernelInterface;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Test the console cron run command.
+ *
+ * @coversDefaultClass \Drupal\Core\Console\Command\RunCron
+ *
+ * @group Console
+ * @group Command
+ */
+class RunCronTest extends UnitTestCase {
+
+  /**
+   * The bootstrap service.
+   *
+   * @var \Drupal\Core\Console\BootstrapInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $bootstrap;
+
+  /**
+   * The command under test.
+   *
+   * @var \Drupal\Core\Console\Command\RunCron
+   */
+  protected $command;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @covers ::__construct
+   * @covers ::configure
+   */
+  public function setUp() {
+    $this->bootstrap = $this->getMockBuilder(BootstrapInterface::class)
+      ->getMockForAbstractClass();
+
+    $this->command = new RunCron($this->bootstrap);
+  }
+
+  /**
+   * @covers ::execute
+   */
+  public function testExecute() {
+    $input = $this->getMockBuilder(InputInterface::class)
+      ->getMockForAbstractClass();
+
+    $output = $this->getMockBuilder(OutputInterface::class)
+      ->getMockForAbstractClass();
+    $output->expects($this->atLeastOnce())
+      ->method('writeln');
+
+    $cron = $this->getMockBuilder(CronInterface::class)
+      ->getMockForAbstractClass();
+    $cron->expects($this->once())
+      ->method('run');
+
+    $container = $this->getMockBuilder(ContainerInterface::class)
+      ->getMockForAbstractClass();
+    $container->expects($this->once())
+      ->method('get')
+      ->with('cron')
+      ->will($this->returnValue($cron));
+
+    $kernel = $this->getMockBuilder(DrupalKernelInterface::class)
+      ->getMockForAbstractClass();
+    $kernel->expects($this->atLeastOnce())
+      ->method('getContainer')
+      ->will($this->returnValue($container));
+
+    $this->bootstrap->expects($this->once())
+      ->method('bootstrap')
+      ->with($this->command, $input, $output)
+      ->will($this->returnValue($kernel));
+
+    $method = new \ReflectionMethod($this->command, 'execute');
+    $method->setAccessible(TRUE);
+    $method->invoke($this->command, $input, $output);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Console/ConsoleServiceProviderTest.php b/core/tests/Drupal/Tests/Core/Console/ConsoleServiceProviderTest.php
new file mode 100644
index 0000000000..9d0bcb871f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/ConsoleServiceProviderTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\Tests\Core\Console;
+
+use Drupal\Core\Console\ConsoleServiceProvider;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Test console services compiler.
+ *
+ * @coversDefaultClass Drupal\Core\Console\ConsoleServiceProvider
+ *
+ * @group Console
+ */
+class ConsoleServiceProviderTest extends UnitTestCase {
+
+  /**
+   * Test the process() method.
+   *
+   * @covers ::process
+   */
+  public function testProcess() {
+    $service_ids = [$this->randomMachineName(), $this->randomMachineName()];
+    $references[] = new Reference($service_ids[0]);
+    $references[] = new Reference($service_ids[1]);
+
+    $definition = $this->getMockBuilder(Definition::class)
+      ->disableOriginalConstructor()
+      ->getMock();
+    $definition->expects($this->at(0))
+      ->method('addMethodCall')
+      ->with('add', [$references[0]]);
+    $definition->expects($this->at(1))
+      ->method('addMethodCall')
+      ->with('add', [$references[1]]);
+
+    $container_builder = $this->getMockBuilder(ContainerBuilder::class)
+      ->setMethods([
+        'get',
+        'findTaggedServiceIds',
+        'getDefinition',
+      ])
+      ->disableOriginalConstructor()
+      ->getMock();
+    $container_builder->expects($this->once())
+      ->method('findTaggedServiceIds')
+      ->with('console.command')
+      ->will($this->returnValue(array_fill_keys($service_ids, [])));
+    $container_builder->expects($this->once())
+      ->method('getDefinition')
+      ->with('console.app')
+      ->will($this->returnValue($definition));
+
+    $command = new ConsoleServiceProvider();
+    $command->process($container_builder);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Console/ServicesFinderTest.php b/core/tests/Drupal/Tests/Core/Console/ServicesFinderTest.php
new file mode 100644
index 0000000000..65ea115c6c
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Console/ServicesFinderTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\Tests\Core\Console;
+
+use Drupal\Core\Console\ServicesFinder;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Test the console services finder.
+ *
+ * @coversDefaultClass \Drupal\Core\Console\ServicesFinder
+ *
+ * @group Console
+ */
+class ServicesFinderTest extends UnitTestCase {
+
+  /**
+   * Basic test of constructing a new services finder.
+   *
+   * @covers ::__construct
+   */
+  public function testConstruct() {
+    $finder = new ServicesFinder();
+    $files = [];
+    foreach ($finder as $file) {
+      /** @var \Symfony\Component\Finder\SplFileInfo $file */
+      $files[] = $file->getRealPath();
+      $this->assertFileExists($file->getRealPath());
+    }
+    $this->assertNotCount(0, $files);
+  }
+
+}
