diff --git a/core/modules/field/modules/number/number.module b/core/modules/field/modules/number/number.module
index 1b9a300..23725f5 100644
--- a/core/modules/field/modules/number/number.module
+++ b/core/modules/field/modules/number/number.module
@@ -33,7 +33,7 @@ function number_field_info() {
     'number_decimal' => array(
       'label' => t('Decimal'),
       'description' => t('This field stores a number in the database in a fixed decimal format.'),
-      'settings' => array('precision' => 10, 'scale' => 2),
+      'settings' => array('precision' => 10, 'scale' => 2, 'decimal_separator' => '.'),
       'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
       'default_widget' => 'number',
       'default_formatter' => 'number_decimal',
@@ -41,6 +41,7 @@ function number_field_info() {
     'number_float' => array(
       'label' => t('Float'),
       'description' => t('This field stores a number in the database in a floating point format.'),
+      'settings' => array('decimal_separator' => '.'),
       'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
       'default_widget' => 'number',
       'default_formatter' => 'number_decimal',
@@ -73,6 +74,15 @@ function number_field_settings_form($field, $instance, $has_data) {
       '#disabled' => $has_data,
     );
   }
+  if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') {
+    $form['decimal_separator'] = array(
+      '#type' => 'select',
+      '#title' => t('Decimal marker'),
+      '#options' => array('.' => t('Decimal point'), ',' => t('Comma')),
+      '#default_value' => $settings['decimal_separator'],
+      '#description' => t('The character users will input to mark the decimal point in forms.'),
+    );
+  }
 
   return $form;
 }
@@ -320,36 +330,23 @@ function number_field_widget_info() {
  */
 function number_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
   $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : '';
+  // Substitute the decimal separator.
+  if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') {
+    $value = strtr($value, '.', $field['settings']['decimal_separator']);
+  }
 
   $element += array(
-    '#type' => 'number',
+    '#type' => 'textfield',
     '#default_value' => $value,
     // Allow a slightly larger size that the field length to allow for some
     // configurations where all characters won't fit in input field.
     '#size' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 4 : 12,
     // Allow two extra characters for signed values and decimal separator.
     '#maxlength' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 2 : 10,
+    // Extract the number type from the field type name for easier validation.
+    '#number_type' => str_replace('number_', '', $field['type']),
   );
 
-  // Set the step for floating point and decimal numbers.
-  switch ($field['type']) {
-    case 'number_decimal':
-      $element['#step'] = pow(0.1, $field['settings']['scale']);
-      break;
-
-    case 'number_float':
-      $element['#step'] = 'any';
-      break;
-  }
-
-  // Set minimum and maximum.
-  if (is_numeric($instance['settings']['min'])) {
-    $element['#min'] = $instance['settings']['min'];
-  }
-  if (is_numeric($instance['settings']['max'])) {
-    $element['#max'] = $instance['settings']['max'];
-  }
-
   // Add prefix and suffix.
   if (!empty($instance['settings']['prefix'])) {
     $prefixes = explode('|', $instance['settings']['prefix']);
@@ -360,10 +357,61 @@ function number_field_widget_form(&$form, &$form_state, $field, $instance, $lang
     $element['#field_suffix'] = field_filter_xss(array_pop($suffixes));
   }
 
+  $element['#element_validate'][] = 'number_field_widget_validate';
+
   return array('value' => $element);
 }
 
 /**
+ * FAPI validation of an individual number element.
+ */
+function number_field_widget_validate($element, &$form_state) {
+  $field = field_widget_field($element, $form_state);
+  $instance = field_widget_instance($element, $form_state);
+
+  $type = $element['#number_type'];
+  $value = $element['#value'];
+
+  // Reject invalid characters.
+  if (!empty($value)) {
+    switch ($type) {
+      case 'float':
+      case 'decimal':
+        $regexp = '@([^-0-9\\' . $field['settings']['decimal_separator'] . '])|(.-)@';
+        $message = t('Only numbers and the decimal separator (@separator) allowed in %field.', array('%field' => $instance['label'], '@separator' => $field['settings']['decimal_separator']));
+        break;
+
+      case 'integer':
+        $regexp = '@([^-0-9])|(.-)@';
+        $message = t('Only numbers are allowed in %field.', array('%field' => $instance['label']));
+        break;
+    }
+    if ($value != preg_replace($regexp, '', $value)) {
+      form_error($element, $message);
+    }
+    else {
+      if ($type == 'decimal' || $type == 'float') {
+        // Verify that only one decimal separator exists in the field.
+        if (substr_count($value, $field['settings']['decimal_separator']) > 1) {
+          $message = t('%field: There should only be one decimal separator (@separator).',
+            array(
+              '%field' => t($instance['label']),
+              '@separator' => $field['settings']['decimal_separator'],
+            )
+          );
+          form_error($element, $message);
+        }
+        else {
+          // Substitute the decimal separator; things should be fine.
+          $value = strtr($value, $field['settings']['decimal_separator'], '.');
+        }
+      }
+      form_set_value($element, $value, $form_state);
+    }
+  }
+}
+
+/**
  * Implements hook_field_widget_error().
  */
 function number_field_widget_error($element, $error, $form, &$form_state) {
diff --git a/core/modules/field/modules/number/number.test b/core/modules/field/modules/number/number.test
index 99ec6f0..4a4f05f 100644
--- a/core/modules/field/modules/number/number.test
+++ b/core/modules/field/modules/number/number.test
@@ -86,7 +86,11 @@ class NumberFieldTestCase extends DrupalWebTestCase {
         "{$this->field['field_name']}[$langcode][0][value]" => $wrong_entry,
       );
       $this->drupalPost(NULL, $edit, t('Save'));
-      $this->assertRaw(t('%name must be a number.', array('%name' => $this->field['field_name'])), 'Correctly failed to save decimal value with more than one decimal point.');
+      $this->assertText(
+        t('There should only be one decimal separator (@separator)',
+          array('@separator' => $this->field['settings']['decimal_separator'])),
+        t('Correctly failed to save decimal value with more than one decimal point.')
+      );
     }
 
     // Try to create entries with minus sign not in the first position.
@@ -104,7 +108,11 @@ class NumberFieldTestCase extends DrupalWebTestCase {
         "{$this->field['field_name']}[$langcode][0][value]" => $wrong_entry,
       );
       $this->drupalPost(NULL, $edit, t('Save'));
-      $this->assertRaw(t('%name must be a number.', array('%name' => $this->field['field_name'])), 'Correctly failed to save decimal value with minus sign in the wrong position.');
+      $this->assertText(
+        t('Only numbers and the decimal separator (@separator) allowed in ',
+          array('@separator' => $this->field['settings']['decimal_separator'])),
+        'Correctly failed to save decimal value with minus sign in the wrong position.'
+      );
     }
   }
 
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index cbc946c..9afcd7c 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -8,6 +8,13 @@ use Drupal\Core\Database\Database;
  */
 
 /**
+ * Implements hook_init().
+ */
+function simpletest_init() {
+  simpletest_register_test_namespaces();
+}
+
+/**
  * Implements hook_help().
  */
 function simpletest_help($path, $arg) {
@@ -159,6 +166,7 @@ function simpletest_run_tests($test_list, $reporter = 'drupal') {
  * Batch operation callback.
  */
 function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
+  simpletest_register_test_namespaces();
   // Get working values.
   if (!isset($context['sandbox']['max'])) {
     // First iteration: initialize working values.
@@ -291,6 +299,9 @@ function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALS
  * a static variable. In order to list tests provided by disabled modules
  * hook_registry_files_alter() is used to forcefully add them to the registry.
  *
+ * PSR-0 classes are found by searching the designated directory for each module
+ * for files matching the PSR-0 standard.
+ *
  * @return
  *   An array of tests keyed with the groups specified in each of the tests
  *   getInfo() method and then keyed by the test class. An example of the array
@@ -315,11 +326,45 @@ function simpletest_test_get_all() {
     // information from each tests getInfo() method.
     if ($cache = cache()->get('simpletest')) {
       $groups = $cache->data;
+      simpletest_register_test_namespaces();
     }
     else {
       // Select all clases in files ending with .test.
       $classes = db_query("SELECT name FROM {registry} WHERE type = :type AND filename LIKE :name", array(':type' => 'class', ':name' => '%.test'))->fetchCol();
 
+      // Select all PSR-0 classes in the tests folder of all available modules.
+      // @todo: can profiles/themes have tests as well?
+      $module_list = db_query("SELECT name, filename FROM {system} WHERE type = 'module' ORDER BY weight ASC, name ASC")->fetchAllKeyed();
+
+      // Prepare a list of modules which provide tests.
+      $test_modules = array();
+      foreach ($module_list as $name => $filename) {
+        // Build directory in which the test files would reside.
+        $tests_dir = DRUPAL_ROOT . '/' . dirname($filename) . '/tests/Drupal/' . $name . '/Test';
+        // Scan it for test files if it exists.
+        if (is_dir($tests_dir)) {
+          $files = file_scan_directory($tests_dir, '/.*\.php/');
+          if (!empty($files)) {
+            $basedir = DRUPAL_ROOT . '/' . dirname($filename) . '/tests/';
+            $test_modules[] = $name;
+            foreach ($files as $file) {
+              // Convert the file name into the namespaced class name.
+              $replacements = array(
+                '/' => '\\',
+                $basedir => '',
+                '.php' => '',
+              );
+              $classes[] = strtr($file->uri, $replacements);
+            }
+          }
+        }
+      }
+
+      // Ensure that namespaces for newly found modules are registered so that
+      // the checks below work.
+      cache()->set('simpletest_modules', $test_modules);
+      simpletest_register_test_namespaces(TRUE);
+
       // Check that each class has a getInfo() method and store the information
       // in an array keyed with the group specified in the test information.
       $groups = array();
@@ -356,6 +401,30 @@ function simpletest_test_get_all() {
 }
 
 /**
+ * Registers test namespaces for all modules which provide tests.
+ */
+function simpletest_register_test_namespaces($force_register = FALSE) {
+  static $called = FALSE;
+
+  // Prevent multiple namespace registrations unless explicitly forced to do so.
+  if (!$called || $force_register) {
+    // Get the cached test modules list and register a test namespace for each.
+    if ($cache = cache()->get('simpletest_modules')) {
+      $loader = drupal_classloader();
+      foreach ($cache->data as $module) {
+        $loader->registerNamespace('Drupal\\' . $module . '\\Test', DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . '/tests');
+      }
+    }
+    $called = TRUE;
+  }
+  else {
+    // If the cache failed to load, rebuild the test information. This will also
+    // lead to a new call to this function.
+    simpletest_test_get_all();
+  }
+}
+
+/**
  * Implements hook_registry_files_alter().
  *
  * Add the test files for disabled modules so that we get a list containing
diff --git a/core/modules/simpletest/simpletest.pages.inc b/core/modules/simpletest/simpletest.pages.inc
index 8ac1ee2..481e4fc 100644
--- a/core/modules/simpletest/simpletest.pages.inc
+++ b/core/modules/simpletest/simpletest.pages.inc
@@ -183,6 +183,7 @@ function theme_simpletest_test_table($variables) {
 function simpletest_test_form_submit($form, &$form_state) {
   // Get list of tests.
   $tests_list = array();
+  simpletest_register_test_namespaces();
   foreach ($form_state['values'] as $class_name => $value) {
     // Since class_exists() will likely trigger an autoload lookup,
     // we do the fast check first.
@@ -233,6 +234,8 @@ function simpletest_result_form($form, &$form_state, $test_id) {
     '#debug' => 0,
   );
 
+  simpletest_register_test_namespaces();
+
   // Cycle through each test group.
   $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status')));
   $form['result']['results'] = array();
diff --git a/core/modules/tracker/tests/Drupal/tracker/Test/TrackerTest.php b/core/modules/tracker/tests/Drupal/tracker/Test/TrackerTest.php
new file mode 100644
index 0000000..302e4a3
--- /dev/null
+++ b/core/modules/tracker/tests/Drupal/tracker/Test/TrackerTest.php
@@ -0,0 +1,274 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\tracker\Test\TrackerTests
+ */
+
+namespace Drupal\tracker\Test;
+
+use DrupalWebTestCase;
+
+/**
+ * Defines a base class for testing tracker.module.
+ */
+class TrackerTest extends DrupalWebTestCase {
+
+  /**
+   * The main user for testing.
+   *
+   * @var object
+   */
+  protected $user;
+
+  /**
+   * A second user that will 'create' comments and nodes.
+   *
+   * @var object
+   */
+  protected $other_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Tracker',
+      'description' => 'Create and delete nodes and check for their display in the tracker listings.',
+      'group' => 'Tracker'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('comment', 'tracker');
+
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+
+    $permissions = array('access comments', 'create page content', 'post comments', 'skip comment approval');
+    $this->user = $this->drupalCreateUser($permissions);
+    $this->other_user = $this->drupalCreateUser($permissions);
+
+    // Make node preview optional.
+    variable_set('comment_preview_page', 0);
+  }
+
+  /**
+   * Tests for the presence of nodes on the global tracker listing.
+   */
+  function testTrackerAll() {
+    $this->drupalLogin($this->user);
+
+    $unpublished = $this->drupalCreateNode(array(
+      'title' => $this->randomName(8),
+      'status' => 0,
+    ));
+    $published = $this->drupalCreateNode(array(
+      'title' => $this->randomName(8),
+      'status' => 1,
+    ));
+
+    $this->drupalGet('tracker');
+    $this->assertNoText($unpublished->title, t('Unpublished node do not show up in the tracker listing.'));
+    $this->assertText($published->title, t('Published node show up in the tracker listing.'));
+    $this->assertLink(t('My recent content'), 0, t('User tab shows up on the global tracker page.'));
+
+    // Delete a node and ensure it no longer appears on the tracker.
+    node_delete($published->nid);
+    $this->drupalGet('tracker');
+    $this->assertNoText($published->title, t('Deleted node do not show up in the tracker listing.'));
+  }
+
+  /**
+   * Tests for the presence of nodes on a user's tracker listing.
+   */
+  function testTrackerUser() {
+    $this->drupalLogin($this->user);
+
+    $unpublished = $this->drupalCreateNode(array(
+      'title' => $this->randomName(8),
+      'uid' => $this->user->uid,
+      'status' => 0,
+    ));
+    $my_published = $this->drupalCreateNode(array(
+      'title' => $this->randomName(8),
+      'uid' => $this->user->uid,
+      'status' => 1,
+    ));
+    $other_published_no_comment = $this->drupalCreateNode(array(
+      'title' => $this->randomName(8),
+      'uid' => $this->other_user->uid,
+      'status' => 1,
+    ));
+    $other_published_my_comment = $this->drupalCreateNode(array(
+      'title' => $this->randomName(8),
+      'uid' => $this->other_user->uid,
+      'status' => 1,
+    ));
+    $comment = array(
+      'subject' => $this->randomName(),
+      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
+    );
+    $this->drupalPost('comment/reply/' . $other_published_my_comment->nid, $comment, t('Save'));
+
+    $this->drupalGet('user/' . $this->user->uid . '/track');
+    $this->assertNoText($unpublished->title, t("Unpublished nodes do not show up in the users's tracker listing."));
+    $this->assertText($my_published->title, t("Published nodes show up in the user's tracker listing."));
+    $this->assertNoText($other_published_no_comment->title, t("Other user's nodes do not show up in the user's tracker listing."));
+    $this->assertText($other_published_my_comment->title, t("Nodes that the user has commented on appear in the user's tracker listing."));
+
+    // Verify that unpublished comments are removed from the tracker.
+    $admin_user = $this->drupalCreateUser(array('post comments', 'administer comments', 'access user profiles'));
+    $this->drupalLogin($admin_user);
+    $this->drupalPost('comment/1/edit', array('status' => COMMENT_NOT_PUBLISHED), t('Save'));
+    $this->drupalGet('user/' . $this->user->uid . '/track');
+    $this->assertNoText($other_published_my_comment->title, 'Unpublished comments are not counted on the tracker listing.');
+  }
+
+  /**
+   * Tests for the presence of the "new" flag for nodes.
+   */
+  function testTrackerNewNodes() {
+    $this->drupalLogin($this->user);
+
+    $edit = array(
+      'title' => $this->randomName(8),
+    );
+
+    $node = $this->drupalCreateNode($edit);
+    $title = $edit['title'];
+    $this->drupalGet('tracker');
+    $this->assertPattern('/' . $title . '.*new/', t('New nodes are flagged as such in the tracker listing.'));
+
+    $this->drupalGet('node/' . $node->nid);
+    $this->drupalGet('tracker');
+    $this->assertNoPattern('/' . $title . '.*new/', t('Visited nodes are not flagged as new.'));
+
+    $this->drupalLogin($this->other_user);
+    $this->drupalGet('tracker');
+    $this->assertPattern('/' . $title . '.*new/', t('For another user, new nodes are flagged as such in the tracker listing.'));
+
+    $this->drupalGet('node/' . $node->nid);
+    $this->drupalGet('tracker');
+    $this->assertNoPattern('/' . $title . '.*new/', t('For another user, visited nodes are not flagged as new.'));
+  }
+
+  /**
+   * Tests for comment counters on the tracker listing.
+   */
+  function testTrackerNewComments() {
+    $this->drupalLogin($this->user);
+
+    $node = $this->drupalCreateNode(array(
+      'comment' => 2,
+      'title' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => $this->randomName(8)))),
+    ));
+
+    // Add a comment to the page.
+    $comment = array(
+      'subject' => $this->randomName(),
+      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
+    );
+    // The new comment is automatically viewed by the current user.
+    $this->drupalPost('comment/reply/' . $node->nid, $comment, t('Save'));
+
+    $this->drupalLogin($this->other_user);
+    $this->drupalGet('tracker');
+    $this->assertText('1 new', t('New comments are counted on the tracker listing pages.'));
+    $this->drupalGet('node/' . $node->nid);
+
+    // Add another comment as other_user.
+    $comment = array(
+      'subject' => $this->randomName(),
+      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
+    );
+    // If the comment is posted in the same second as the last one then Drupal
+    // can't tell the difference, so we wait one second here.
+    sleep(1);
+    $this->drupalPost('comment/reply/' . $node->nid, $comment, t('Save'));
+
+    $this->drupalLogin($this->user);
+    $this->drupalGet('tracker');
+    $this->assertText('1 new', t('New comments are counted on the tracker listing pages.'));
+  }
+
+  /**
+   * Tests that existing nodes are indexed by cron.
+   */
+  function testTrackerCronIndexing() {
+    $this->drupalLogin($this->user);
+
+    // Create 3 nodes.
+    $edits = array();
+    $nodes = array();
+    for ($i = 1; $i <= 3; $i++) {
+      $edits[$i] = array(
+        'comment' => 2,
+        'title' => $this->randomName(),
+      );
+      $nodes[$i] = $this->drupalCreateNode($edits[$i]);
+    }
+
+    // Add a comment to the last node as other user.
+    $this->drupalLogin($this->other_user);
+    $comment = array(
+      'subject' => $this->randomName(),
+      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
+    );
+    $this->drupalPost('comment/reply/' . $nodes[3]->nid, $comment, t('Save'));
+
+    // Start indexing backwards from node 3.
+    variable_set('tracker_index_nid', 3);
+
+    // Clear the current tracker tables and rebuild them.
+    db_delete('tracker_node')
+      ->execute();
+    db_delete('tracker_user')
+      ->execute();
+    tracker_cron();
+
+    $this->drupalLogin($this->user);
+
+    // Fetch the user's tracker.
+    $this->drupalGet('tracker/' . $this->user->uid);
+
+    // Assert that all node titles are displayed.
+    foreach ($nodes as $i => $node) {
+      $this->assertText($node->title, t('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
+    }
+    $this->assertText('1 new', t('New comment is counted on the tracker listing pages.'));
+    $this->assertText('updated', t('Node is listed as updated'));
+
+    // Fetch the site-wide tracker.
+    $this->drupalGet('tracker');
+
+    // Assert that all node titles are displayed.
+    foreach ($nodes as $i => $node) {
+      $this->assertText($node->title, t('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
+    }
+    $this->assertText('1 new', t('New comment is counted on the tracker listing pages.'));
+  }
+
+  /**
+   * Tests that publish/unpublish works at admin/content/node.
+   */
+  function testTrackerAdminUnpublish() {
+    $admin_user = $this->drupalCreateUser(array('access content overview', 'administer nodes', 'bypass node access'));
+    $this->drupalLogin($admin_user);
+
+    $node = $this->drupalCreateNode(array(
+      'comment' => 2,
+      'title' => $this->randomName(),
+    ));
+
+    // Assert that the node is displayed.
+    $this->drupalGet('tracker');
+    $this->assertText($node->title, t('Node is displayed on the tracker listing pages.'));
+
+    // Unpublish the node and ensure that it's no longer displayed.
+    $edit = array(
+      'operation' => 'unpublish',
+      'nodes[' . $node->nid . ']' => $node->nid,
+    );
+    $this->drupalPost('admin/content', $edit, t('Update'));
+
+    $this->drupalGet('tracker');
+    $this->assertText(t('No content available.'), t('Node is displayed on the tracker listing pages.'));
+  }
+}
diff --git a/core/modules/tracker/tracker.info b/core/modules/tracker/tracker.info
index a765504..b39eb3f 100644
--- a/core/modules/tracker/tracker.info
+++ b/core/modules/tracker/tracker.info
@@ -4,4 +4,3 @@ dependencies[] = comment
 package = Core
 version = VERSION
 core = 8.x
-files[] = tracker.test
diff --git a/core/modules/tracker/tracker.test b/core/modules/tracker/tracker.test
deleted file mode 100644
index c6d4a29..0000000
--- a/core/modules/tracker/tracker.test
+++ /dev/null
@@ -1,270 +0,0 @@
-<?php
-
-/**
- * @file
- * Tests for tracker.module.
- */
-
-/**
- * Defines a base class for testing tracker.module.
- */
-class TrackerTest extends DrupalWebTestCase {
-
-  /**
-   * The main user for testing.
-   *
-   * @var object
-   */
-  protected $user;
-
-  /**
-   * A second user that will 'create' comments and nodes.
-   *
-   * @var object
-   */
-  protected $other_user;
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Tracker',
-      'description' => 'Create and delete nodes and check for their display in the tracker listings.',
-      'group' => 'Tracker'
-    );
-  }
-
-  function setUp() {
-    parent::setUp('comment', 'tracker');
-
-    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
-
-    $permissions = array('access comments', 'create page content', 'post comments', 'skip comment approval');
-    $this->user = $this->drupalCreateUser($permissions);
-    $this->other_user = $this->drupalCreateUser($permissions);
-
-    // Make node preview optional.
-    variable_set('comment_preview_page', 0);
-  }
-
-  /**
-   * Tests for the presence of nodes on the global tracker listing.
-   */
-  function testTrackerAll() {
-    $this->drupalLogin($this->user);
-
-    $unpublished = $this->drupalCreateNode(array(
-      'title' => $this->randomName(8),
-      'status' => 0,
-    ));
-    $published = $this->drupalCreateNode(array(
-      'title' => $this->randomName(8),
-      'status' => 1,
-    ));
-
-    $this->drupalGet('tracker');
-    $this->assertNoText($unpublished->title, t('Unpublished node do not show up in the tracker listing.'));
-    $this->assertText($published->title, t('Published node show up in the tracker listing.'));
-    $this->assertLink(t('My recent content'), 0, t('User tab shows up on the global tracker page.'));
-
-    // Delete a node and ensure it no longer appears on the tracker.
-    node_delete($published->nid);
-    $this->drupalGet('tracker');
-    $this->assertNoText($published->title, t('Deleted node do not show up in the tracker listing.'));
-  }
-
-  /**
-   * Tests for the presence of nodes on a user's tracker listing.
-   */
-  function testTrackerUser() {
-    $this->drupalLogin($this->user);
-
-    $unpublished = $this->drupalCreateNode(array(
-      'title' => $this->randomName(8),
-      'uid' => $this->user->uid,
-      'status' => 0,
-    ));
-    $my_published = $this->drupalCreateNode(array(
-      'title' => $this->randomName(8),
-      'uid' => $this->user->uid,
-      'status' => 1,
-    ));
-    $other_published_no_comment = $this->drupalCreateNode(array(
-      'title' => $this->randomName(8),
-      'uid' => $this->other_user->uid,
-      'status' => 1,
-    ));
-    $other_published_my_comment = $this->drupalCreateNode(array(
-      'title' => $this->randomName(8),
-      'uid' => $this->other_user->uid,
-      'status' => 1,
-    ));
-    $comment = array(
-      'subject' => $this->randomName(),
-      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
-    );
-    $this->drupalPost('comment/reply/' . $other_published_my_comment->nid, $comment, t('Save'));
-
-    $this->drupalGet('user/' . $this->user->uid . '/track');
-    $this->assertNoText($unpublished->title, t("Unpublished nodes do not show up in the users's tracker listing."));
-    $this->assertText($my_published->title, t("Published nodes show up in the user's tracker listing."));
-    $this->assertNoText($other_published_no_comment->title, t("Other user's nodes do not show up in the user's tracker listing."));
-    $this->assertText($other_published_my_comment->title, t("Nodes that the user has commented on appear in the user's tracker listing."));
-
-    // Verify that unpublished comments are removed from the tracker.
-    $admin_user = $this->drupalCreateUser(array('post comments', 'administer comments', 'access user profiles'));
-    $this->drupalLogin($admin_user);
-    $this->drupalPost('comment/1/edit', array('status' => COMMENT_NOT_PUBLISHED), t('Save'));
-    $this->drupalGet('user/' . $this->user->uid . '/track');
-    $this->assertNoText($other_published_my_comment->title, 'Unpublished comments are not counted on the tracker listing.');
-  }
-
-  /**
-   * Tests for the presence of the "new" flag for nodes.
-   */
-  function testTrackerNewNodes() {
-    $this->drupalLogin($this->user);
-
-    $edit = array(
-      'title' => $this->randomName(8),
-    );
-
-    $node = $this->drupalCreateNode($edit);
-    $title = $edit['title'];
-    $this->drupalGet('tracker');
-    $this->assertPattern('/' . $title . '.*new/', t('New nodes are flagged as such in the tracker listing.'));
-
-    $this->drupalGet('node/' . $node->nid);
-    $this->drupalGet('tracker');
-    $this->assertNoPattern('/' . $title . '.*new/', t('Visited nodes are not flagged as new.'));
-
-    $this->drupalLogin($this->other_user);
-    $this->drupalGet('tracker');
-    $this->assertPattern('/' . $title . '.*new/', t('For another user, new nodes are flagged as such in the tracker listing.'));
-
-    $this->drupalGet('node/' . $node->nid);
-    $this->drupalGet('tracker');
-    $this->assertNoPattern('/' . $title . '.*new/', t('For another user, visited nodes are not flagged as new.'));
-  }
-
-  /**
-   * Tests for comment counters on the tracker listing.
-   */
-  function testTrackerNewComments() {
-    $this->drupalLogin($this->user);
-
-    $node = $this->drupalCreateNode(array(
-      'comment' => 2,
-      'title' => array(LANGUAGE_NOT_SPECIFIED => array(array('value' => $this->randomName(8)))),
-    ));
-
-    // Add a comment to the page.
-    $comment = array(
-      'subject' => $this->randomName(),
-      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
-    );
-    // The new comment is automatically viewed by the current user.
-    $this->drupalPost('comment/reply/' . $node->nid, $comment, t('Save'));
-
-    $this->drupalLogin($this->other_user);
-    $this->drupalGet('tracker');
-    $this->assertText('1 new', t('New comments are counted on the tracker listing pages.'));
-    $this->drupalGet('node/' . $node->nid);
-
-    // Add another comment as other_user.
-    $comment = array(
-      'subject' => $this->randomName(),
-      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
-    );
-    // If the comment is posted in the same second as the last one then Drupal
-    // can't tell the difference, so we wait one second here.
-    sleep(1);
-    $this->drupalPost('comment/reply/' . $node->nid, $comment, t('Save'));
-
-    $this->drupalLogin($this->user);
-    $this->drupalGet('tracker');
-    $this->assertText('1 new', t('New comments are counted on the tracker listing pages.'));
-  }
-
-  /**
-   * Tests that existing nodes are indexed by cron.
-   */
-  function testTrackerCronIndexing() {
-    $this->drupalLogin($this->user);
-
-    // Create 3 nodes.
-    $edits = array();
-    $nodes = array();
-    for ($i = 1; $i <= 3; $i++) {
-      $edits[$i] = array(
-        'comment' => 2,
-        'title' => $this->randomName(),
-      );
-      $nodes[$i] = $this->drupalCreateNode($edits[$i]);
-    }
-
-    // Add a comment to the last node as other user.
-    $this->drupalLogin($this->other_user);
-    $comment = array(
-      'subject' => $this->randomName(),
-      'comment_body[' . LANGUAGE_NOT_SPECIFIED . '][0][value]' => $this->randomName(20),
-    );
-    $this->drupalPost('comment/reply/' . $nodes[3]->nid, $comment, t('Save'));
-
-    // Start indexing backwards from node 3.
-    variable_set('tracker_index_nid', 3);
-
-    // Clear the current tracker tables and rebuild them.
-    db_delete('tracker_node')
-      ->execute();
-    db_delete('tracker_user')
-      ->execute();
-    tracker_cron();
-
-    $this->drupalLogin($this->user);
-
-    // Fetch the user's tracker.
-    $this->drupalGet('tracker/' . $this->user->uid);
-
-    // Assert that all node titles are displayed.
-    foreach ($nodes as $i => $node) {
-      $this->assertText($node->title, t('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
-    }
-    $this->assertText('1 new', t('New comment is counted on the tracker listing pages.'));
-    $this->assertText('updated', t('Node is listed as updated'));
-
-    // Fetch the site-wide tracker.
-    $this->drupalGet('tracker');
-
-    // Assert that all node titles are displayed.
-    foreach ($nodes as $i => $node) {
-      $this->assertText($node->title, t('Node @i is displayed on the tracker listing pages.', array('@i' => $i)));
-    }
-    $this->assertText('1 new', t('New comment is counted on the tracker listing pages.'));
-  }
-
-  /**
-   * Tests that publish/unpublish works at admin/content/node.
-   */
-  function testTrackerAdminUnpublish() {
-    $admin_user = $this->drupalCreateUser(array('access content overview', 'administer nodes', 'bypass node access'));
-    $this->drupalLogin($admin_user);
-
-    $node = $this->drupalCreateNode(array(
-      'comment' => 2,
-      'title' => $this->randomName(),
-    ));
-
-    // Assert that the node is displayed.
-    $this->drupalGet('tracker');
-    $this->assertText($node->title, t('Node is displayed on the tracker listing pages.'));
-
-    // Unpublish the node and ensure that it's no longer displayed.
-    $edit = array(
-      'operation' => 'unpublish',
-      'nodes[' . $node->nid . ']' => $node->nid,
-    );
-    $this->drupalPost('admin/content', $edit, t('Update'));
-
-    $this->drupalGet('tracker');
-    $this->assertText(t('No content available.'), t('Node is displayed on the tracker listing pages.'));
-  }
-}
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 5bf95fb..a3a9b80 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -363,6 +363,8 @@ function simpletest_script_run_one_test($test_id, $test_class) {
     // Bootstrap Drupal.
     drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
+    simpletest_register_test_namespaces();
+
     $test = new $test_class($test_id);
     $test->run();
     $info = $test->getInfo();
@@ -396,7 +398,7 @@ function simpletest_script_command($test_id, $test_class) {
   if ($args['color']) {
     $command .= ' --color';
   }
-  $command .= " --php " . escapeshellarg($php) . " --test-id $test_id --execute-test $test_class";
+  $command .= " --php " . escapeshellarg($php) . " --test-id $test_id --execute-test \"$test_class\"";
   return $command;
 }
 
