diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 2152725..334c957 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -14,6 +14,7 @@
  * @see batch_get()
  */
 
+use \Drupal\Component\Batch\Batch;
 use \Symfony\Component\HttpFoundation\JsonResponse;
 
 /**
@@ -329,27 +330,7 @@ function _batch_process() {
  * @see _batch_process()
  */
 function _batch_api_percentage($total, $current) {
-  if (!$total || $total == $current) {
-    // If $total doesn't evaluate as true or is equal to the current set, then
-    // we're finished, and we can return "100".
-    $percentage = "100";
-  }
-  else {
-    // We add a new digit at 200, 2000, etc. (since, for example, 199/200
-    // would round up to 100% if we didn't).
-    $decimal_places = max(0, floor(log10($total / 2.0)) - 1);
-    do {
-      // Calculate the percentage to the specified number of decimal places.
-      $percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places));
-      // When $current is an integer, the above calculation will always be
-      // correct. However, if $current is a floating point number (in the case
-      // of a multi-step batch operation that is not yet complete), $percentage
-      // may be erroneously rounded up to 100%. To prevent that, we add one
-      // more decimal place and try again.
-      $decimal_places++;
-    } while ($percentage == '100');
-  }
-  return $percentage;
+  return Batch::percentage($total, $current);
 }
 
 /**
diff --git a/core/lib/Drupal/Component/Batch/Batch.php b/core/lib/Drupal/Component/Batch/Batch.php
new file mode 100644
index 0000000..7d863cb
--- /dev/null
+++ b/core/lib/Drupal/Component/Batch/Batch.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Component\Batch\Batch.
+ */
+
+namespace Drupal\Component\Batch;
+
+/**
+ * Helper methods for the batch system.
+ */
+class Batch {
+  /**
+   * Formats the percent completion for a batch set.
+   *
+   * @param $total
+   *   The total number of operations.
+   * @param $current
+   *   The number of the current operation. This may be a floating point number
+   *   rather than an integer in the case of a multi-step operation that is not
+   *   yet complete; in that case, the fractional part of $current represents the
+   *   fraction of the operation that has been completed.
+   *
+   * @return
+   *   The properly formatted percentage, as a string. We output percentages
+   *   using the correct number of decimal places so that we never print "100%"
+   *   until we are finished, but we also never print more decimal places than
+   *   are meaningful.
+   *
+   * @see _batch_process()
+   */
+  public static function percentage($total, $current) {
+    if (!$total || $total == $current) {
+      // If $total doesn't evaluate as true or is equal to the current set, then
+      // we're finished, and we can return "100".
+      $percentage = "100";
+    }
+    else {
+      // We add a new digit at 200, 2000, etc. (since, for example, 199/200
+      // would round up to 100% if we didn't).
+      $decimal_places = max(0, floor(log10($total / 2.0)) - 1);
+      do {
+        // Calculate the percentage to the specified number of decimal places.
+        $percentage = sprintf('%01.' . $decimal_places . 'f', round($current / $total * 100, $decimal_places));
+        // When $current is an integer, the above calculation will always be
+        // correct. However, if $current is a floating point number (in the case
+        // of a multi-step batch operation that is not yet complete), $percentage
+        // may be erroneously rounded up to 100%. To prevent that, we add one
+        // more decimal place and try again.
+        $decimal_places++;
+      } while ($percentage == '100');
+    }
+    return $percentage;
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php
deleted file mode 100644
index 5b92b19..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Batch/PercentagesUnitTest.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Batch\PercentagesUnitTest.
- */
-
-namespace Drupal\system\Tests\Batch;
-
-use Drupal\simpletest\UnitTestBase;
-
-/**
- * Unit tests of progress percentage rounding.
- *
- * Tests the function _batch_api_percentage() to make sure that the rounding
- * works properly in all cases.
- */
-class PercentagesUnitTest extends UnitTestBase {
-  protected $testCases = array();
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Batch percentages',
-      'description' => 'Unit tests of progress percentage rounding.',
-      'group' => 'Batch API',
-    );
-  }
-
-  function setUp() {
-    // Set up an array of test cases, where the expected values are the keys,
-    // and the values are arrays with the keys 'total' and 'current',
-    // corresponding with the function parameters of _batch_api_percentage().
-    $this->testCases = array(
-      // 1/2 is 50%.
-      '50' => array('total' => 2, 'current' => 1),
-      // Though we should never encounter a case where the current set is set
-      // 0, if we did, we should get 0%.
-      '0' => array('total' => 3, 'current' => 0),
-      // 1/3 is closer to 33% than to 34%.
-      '33' => array('total' => 3, 'current' => 1),
-      // 2/3 is closer to 67% than to 66%.
-      '67' => array('total' => 3, 'current' => 2),
-      // 1/199 should round up to 1%.
-      '1' => array('total' => 199, 'current' => 1),
-      // 198/199 should round down to 99%.
-      '99' => array('total' => 199, 'current' => 198),
-      // 199/200 would have rounded up to 100%, which would give the false
-      // impression of being finished, so we add another digit and should get
-      // 99.5%.
-      '99.5' => array('total' => 200, 'current' => 199),
-      // The same logic holds for 1/200: we should get 0.5%.
-      '0.5' => array('total' => 200, 'current' => 1),
-      // Numbers that come out evenly, such as 50/200, should be forced to have
-      // extra digits for consistancy.
-      '25.0' => array('total' => 200, 'current' => 50),
-      // Regardless of number of digits we're using, 100% should always just be
-      // 100%.
-      '100' => array('total' => 200, 'current' => 200),
-      // 1998/1999 should similarly round down to 99.9%.
-      '99.9' => array('total' => 1999, 'current' => 1998),
-      // 1999/2000 should add another digit and go to 99.95%.
-      '99.95' => array('total' => 2000, 'current' => 1999),
-      // 19999/20000 should add yet another digit and go to 99.995%.
-      '99.995' => array('total' => 20000, 'current' => 19999),
-      // The next five test cases simulate a batch with a single operation
-      // ('total' equals 1) that takes several steps to complete. Within the
-      // operation, we imagine that there are 501 items to process, and 100 are
-      // completed during each step. The percentages we get back should be
-      // rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
-      // but for the last pass through, when 500 out of 501 items have been
-      // processed, we do not want to round up to 100%, since that would
-      // erroneously indicate that the processing is complete.
-      '20' => array('total' => 1, 'current' => 100/501),
-      '40' => array('total' => 1, 'current' => 200/501),
-      '60' => array('total' => 1, 'current' => 300/501),
-      '80' => array('total' => 1, 'current' => 400/501),
-      '99.8' => array('total' => 1, 'current' => 500/501),
-    );
-    require_once DRUPAL_ROOT . '/core/includes/batch.inc';
-    parent::setUp();
-  }
-
-  /**
-   * Tests the _batch_api_percentage() function.
-   */
-  function testBatchPercentages() {
-    foreach ($this->testCases as $expected_result => $arguments) {
-      // PHP sometimes casts numeric strings that are array keys to integers,
-      // cast them back here.
-      $expected_result = (string) $expected_result;
-      $total = $arguments['total'];
-      $current = $arguments['current'];
-      $actual_result = _batch_api_percentage($total, $current);
-      if ($actual_result === $expected_result) {
-        $this->pass(format_string('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
-      }
-      else {
-        $this->fail(format_string('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
-      }
-    }
-  }
-}
diff --git a/core/tests/Drupal/Tests/Component/Batch/PercentagesTest.php b/core/tests/Drupal/Tests/Component/Batch/PercentagesTest.php
new file mode 100644
index 0000000..d533ee3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Batch/PercentagesTest.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Batch\PercentagesTest.
+ */
+
+namespace Drupal\Tests\Component\Batch;
+
+use Drupal\Component\Batch\Batch;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Unit tests of progress percentage rounding.
+ *
+ * Tests the function _batch_api_percentage() to make sure that the rounding
+ * works properly in all cases.
+ */
+class PercentagesTest extends UnitTestCase {
+  protected $testCases = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Batch percentages',
+      'description' => 'Unit tests of progress percentage rounding.',
+      'group' => 'Batch API',
+    );
+  }
+
+  /**
+   * Tests the _batch_api_percentage() function.
+   *
+   * @dataProvider provider
+   */
+  function testBatchPercentages($total, $current, $expected_result) {
+    $actual_result = Batch::percentage($total, $current);
+    $this->assertEquals($actual_result, $expected_result, sprintf('The expected the batch api percentage at the state %s/%s is %s%% and got %s%%.', $current, $total, $expected_result, $actual_result));
+  }
+
+  /**
+   * Provide data for batch unit tests.
+   */
+  public function provider() {
+    // Set up an array of test cases.
+    return array(
+      // array(total, current, expected).
+      // 1/2 is 50%.
+      array(2, 1, '50'),
+      // Though we should never encounter a case where the current set is set
+      // 0, if we did, we should get 0%.
+      array(3, 0, '0'),
+      // 1/3 is closer to 33% than to 34%.
+      array(3, 1, '33'),
+      // 2/3 is closer to 67% than to 66%.
+      array(3, 2, '67'),
+      // 1/199 should round up to 1%.
+      array(199, 1, '1'),
+      // 198/199 should round down to 99%.
+      array(199, 198, '99'),
+      // 199/200 would have rounded up to 100%, which would give the false
+      // impression of being finished, so we add another digit and should get
+      // 99.5%.
+      array(200, 199, '99.5'),
+      // The same logic holds for 1/200: we should get 0.5%.
+      array(200, 1, '0.5'),
+      // Numbers that come out evenly, such as 50/200, should be forced to have
+      // extra digits for consistancy.
+      array(200, 50, '25.0'),
+      // Regardless of number of digits we're using, 100% should always just be
+      // 100%.
+      array(200, 200, '100'),
+      // 1998/1999 should similarly round down to 99.9%.
+      array(1999, 1998, '99.9'),
+      // 1999/2000 should add another digit and go to 99.95%.
+      array(2000, 1999, '99.95'),
+      // 19999/20000 should add yet another digit and go to 99.995%.
+      array(20000, 19999, '99.995'),
+      // The next five test cases simulate a batch with a single operation
+      // ('total' equals 1) that takes several steps to complete. Within the
+      // operation, we imagine that there are 501 items to process, and 100 are
+      // completed during each step. The percentages we get back should be
+      // rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
+      // but for the last pass through, when 500 out of 501 items have been
+      // processed, we do not want to round up to 100%, since that would
+      // erroneously indicate that the processing is complete.
+      array('total' => 1, 'current' => 100/501, '20'),
+      array('total' => 1, 'current' => 200/501, '40'),
+      array('total' => 1, 'current' => 300/501, '60'),
+      array('total' => 1, 'current' => 400/501, '80'),
+      array('total' => 1, 'current' => 500/501, '99.8'),
+    );
+  }
+}
