diff --git a/pift.admin.inc b/pift.admin.inc
index ca73d8f..f254b66 100644
--- a/pift.admin.inc
+++ b/pift.admin.inc
@@ -79,6 +79,25 @@ function pift_admin_settings_form() {
     '#required' => TRUE,
   );
 
+  $project_types = array();
+  foreach (project_get_project_types() as $key => $project_type) {
+    $project_types[$key] = $project_type->name;
+  }
+  $form['results'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Results Display'),
+    '#weight' => 0,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['results']['pift_results_display'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Project types'),
+    '#description' => t('Project types for which the "Testing Status" tab should be displayed on the project page.'),
+    '#options' => $project_types,
+    '#default_value' => variable_get('pift_results_display', array()),
+  );
+
   $form['criteria'] = array(
     '#type' => 'fieldset',
     '#title' => t('Criteria'),
diff --git a/pift.module b/pift.module
index 9a1d4f1..688d560 100644
--- a/pift.module
+++ b/pift.module
@@ -91,11 +91,44 @@ function pift_menu() {
     'file' => 'pift.pages.inc',
     'type' => MENU_CALLBACK,
   );
+  $items['node/%node/testing-status'] = array(
+    'title' => t('Automated Testing'),
+    'access callback' => 'pift_results_visibility',
+    'access arguments' => array(1),
+    'page callback' => 'pift_results_project_tab',
+    'page arguments' => array(1),
+    'file' => 'pift.results.inc',
+    'weight' => '5',
+    'type' => MENU_LOCAL_TASK,
+  );
 
   return $items;
 }
 
 /**
+ * Access callback to determine whether the Automated Testing tab is visible.
+ */
+function pift_results_visibility($node) {
+  // Check if we have a module node which has a Git repository
+  // Do not display the tab if the project does not have any release nodes
+  // TODO: Once PIFT/PIFR has been refactored to use label_ids instead of
+  // release nids, the last condition can be changed to enable reporting
+  // of tests for any tag/branch within a project.
+
+  // Determine if node represents a valid project type for results display
+  $valid_type = array_intersect(variable_get('pift_results_display', array()), array_keys($node->taxonomy));
+  if ($node->type == 'project_project'
+    && !empty($valid_type)
+    && !empty($node->versioncontrol_project['repo']->vcs)
+    && $node->versioncontrol_project['repo']->vcs == 'git'
+    && user_is_logged_in()
+    && pift_get_releases($node, TRUE) != array()) {
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/**
  * Implementation of hook_perm().
  */
 function pift_perm() {
@@ -204,6 +237,64 @@ function pift_versioncontrol_git_refs_updated($repository, $refs) {
 }
 
 /**
+ * Helper fuction to build a list of releases for this project's repository.
+ *
+ * @param $node The project node object
+ * @param $quiet Silence error messages
+ * @return array List of available labels with corresponding release nodes
+ */
+function pift_get_releases($node, $quiet = FALSE) {
+  $branches = array();
+  if ($node->type == 'project_project') {
+    $result = db_query("Select b.name from {versioncontrol_release_labels} a
+       join {versioncontrol_labels} b on a.label_id = b.label_id
+       where a.project_nid = %s", $node->nid);
+    while ($data = db_fetch_object($result)) {
+      // Filter out any branches < 6 (not accepted by PIFT)
+      // Move '.x' releases to the top
+      if (in_array(substr($data->name, 0, 1), array('6', '7', '8'))) {
+        if (substr($data->name, strlen($data->name) - 2, 2) == '.x') {
+          $topbranches[$data->name] = $data->name;
+        }
+        else {
+          $branches[$data->name] = $data->name;
+        }
+      }
+    }
+    // Sort to get the highest branch on top
+    uasort($branches, 'version_compare');
+    uasort($topbranches, 'version_compare');
+    $branches = array_merge($branches, $topbranches);
+    $branches = pift_array_reverse($branches);
+
+    if (empty($branches) && !$quiet) {
+      drupal_set_message(t('No releases found for the given project.'), 'error', FALSE);
+    }
+  }
+  return $branches;
+}
+
+/**
+ * Alternative function for the php standard array_reverse()
+ *
+ * As array_reverse() would damage numerical array keys, from
+ * http://drupal.org/node/1074220.  Code copied from project_git_instructions
+ *
+ * Borrowed from http://php.net/manual/en/function.array-reverse.php#102492
+ */
+function pift_array_reverse($array) {
+  $array_key = array_keys($array);
+  $array_value = array_values($array);
+
+  $array_return = array();
+  for ($i = 1, $size_of_array = sizeof($array_key); $i <= $size_of_array; $i++) {
+    $array_return[$array_key[$size_of_array-$i]] = $array_value[$size_of_array-$i];
+  }
+
+  return $array_return;
+}
+
+/**
  * Implementation of hook_form_alter(). Must use generic form_alter() due to
  * comment_upload implementation.
  */
@@ -226,7 +317,19 @@ function pift_form_project_issue_project_edit_form_alter(&$form, $form_state) {
  * Implementation of hook_nodeapi().
  */
 function pift_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
-  if ($node->type != 'project_issue' || empty($node->files)) {
+  if ($node->type == 'project_project') {
+    // Used to maintain 'nice' url aliases for testing status pages
+    $src = 'node/' . $node->nid . '/testing-status';
+    if ($op == 'delete') {
+      db_query("DELETE FROM {url_alias} WHERE src = '%s'", $src);
+    }
+    elseif (($op == 'update' || $op == 'insert') && !empty($node->project['uri'])) {
+      $dst = 'project/' . drupal_urlencode($node->project['uri']) . '/testing-status';
+      db_query("DELETE FROM {url_alias} WHERE src = '%s'", $src);
+      db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $src, $dst);
+    }
+  }
+  elseif ($node->type != 'project_issue' || empty($node->files)) {
     return;
   }
 
diff --git a/pift.pages.inc b/pift.pages.inc
index 99a1781..305679d 100644
--- a/pift.pages.inc
+++ b/pift.pages.inc
@@ -94,19 +94,38 @@ function pift_pages_retest_confirm_form(&$form_state, $test_id) {
 
   if ($test) {
     $form = array();
-    $form['test'] = array(
-      '#type' => 'value',
-      '#value' => array(
-        'test_id' => $test_id,
-        'fid' => $test['fid'],
-        'nid' => ($test['fid'] ? $test['nid'] : $test['rid']),
-        'cid' => ($test['cid'] ? $test['cid'] : 0),
-        'status' => $test['status'],
-        'title' => ($test['fid'] ? $test['filename'] : project_release_get_version((object) $test))
-      ),
-    );
-    $form['#redirect'] = 'node/' . $form['test']['#value']['nid'];
-
+    // Key off type to determine if this is a file retest or branch retest
+    if ($test['type'] == PIFT_TYPE_FILE) {
+      // File test
+      $form['test'] = array(
+        '#type' => 'value',
+        '#value' => array(
+          'test_id' => $test_id,
+          'fid' => $test['fid'],
+          'nid' => ($test['fid'] ? $test['nid'] : $test['rid']),
+          'cid' => ($test['cid'] ? $test['cid'] : 0),
+          'status' => $test['status'],
+          'title' => ($test['fid'] ? $test['filename'] : project_release_get_version((object) $test)),
+          'type' => PIFT_TYPE_FILE,
+        ),
+      );
+      $form['#redirect'] = 'node/' . $form['test']['#value']['nid'];
+    }
+    elseif ($test['type'] == PIFT_TYPE_RELEASE) {
+      // Branch test
+      $form['test'] = array(
+        '#type' => 'value',
+        '#value' => array(
+          'test_id' => $test_id,
+          'status' => $test['status'],
+          'title' => "Test " . check_plain($test['test_id']),
+          'type' => PIFT_TYPE_RELEASE,
+        ),
+      );
+      // Get the project id
+      $pid = db_result(db_query_range("SELECT project_nid from {versioncontrol_release_labels} where release_nid = %d", $test['id'], 0, 1));
+      $form['#redirect'] = 'node/' . $pid . '/testing-status';
+    }
     return confirm_form(
       $form,
       t('Are you sure you want to request that %title be re-tested?', array('%title' => $form['test']['#value']['title'])),
@@ -130,22 +149,24 @@ function pift_pages_retest_confirm_form_submit($form, &$form_state) {
   $test = $form_state['values']['test'];
 
   if ($test['status'] > PIFT_STATUS_SENT) {
-    // Base changes to be made in followup comment.
-    $changes = array(
-      'nid' => $test['nid'],
-      'uid' => $user->uid,
-      'comment' => theme('pift_auto_followup', 'retest', $test['nid'], $test['cid'], $test['title']),
-    );
+    if ($test['type'] == PIFT_TYPE_FILE) {
+      // Base changes to be made in followup comment.
+      $changes = array(
+        'nid' => $test['nid'],
+        'uid' => $user->uid,
+        'comment' => theme('pift_auto_followup', 'retest', $test['nid'], $test['cid'], $test['title']),
+      );
 
-    // If node issue status is not already a valid status then set to default
-    // retest status, otherwise leave status alone.
-    $node = node_load($test['nid']);
-    if (!in_array($node->project_issue['sid'], variable_get('pift_status', array()))) {
-      $changes['sid'] = PIFT_FOLLOWUP_RETEST;
-    }
+      // If node issue status is not already a valid status then set to default
+      // retest status, otherwise leave status alone.
+      $node = node_load($test['nid']);
+      if (!in_array($node->project_issue['sid'], variable_get('pift_status', array()))) {
+        $changes['sid'] = PIFT_FOLLOWUP_RETEST;
+      }
 
-    // Add followup to issue.
-    project_issue_add_auto_followup($changes);
+      // Add followup to issue.
+      project_issue_add_auto_followup($changes);
+    }
 
     // Update test record to reflect change.
     db_query('UPDATE {pift_test}
diff --git a/pift.results.inc b/pift.results.inc
new file mode 100644
index 0000000..be09573
--- /dev/null
+++ b/pift.results.inc
@@ -0,0 +1,301 @@
+<?php
+
+/**
+ * @file
+ * Provide the Automated Testing tab and UI functions.
+ *
+ * @author Jeremy Thorson ("jthorson", http://drupal.org/user/148199)
+ */
+
+/**
+ * Produce html code for the Automated Testing tab.
+ */
+function pift_results_project_tab($node, $version = NULL) {
+  // Load array of release branches in this projects repo.
+  $versions = pift_get_releases($node);
+
+  if (empty($version) && !empty($versions)) {
+    $version = current($versions);
+  }
+
+  // Retrieve project test results for the current branch/tag from {PIFT_TEST}
+  $tests = pift_results_testresult_load($node, $versions);
+
+  // Determine list of releases without tests
+  $noresults = $versions;
+
+  if (!empty($tests)) {
+    foreach ($tests as $testdata) {
+      foreach ($testdata as $test) {
+        unset($noresults[$test['version']]);
+      }
+    }
+    // Format test results
+    $statusoutput = pift_results_format_testresult_table($tests);
+  }
+  else {
+    $statusoutput = pift_results_empty_text();
+  }
+
+  // Okay we have the variable data let's build the page.
+  $content = array();
+  $content[] = '<p>' . t('This page provides information regarding automated testing status for this project\'s releases.') . '</p>';
+
+  // Add the 'Testing Status' table
+  $content[] = '<h3>' . t('Branch Test Results') . '</h3>';
+  $content[] =  $statusoutput;
+
+  // If there are any untested branches, provide a form which can be
+  // used to initiate an 'on-demand' branch test.
+  if (!empty($noresults)) {
+    $content[] = '<br /><br />' . drupal_get_form('pift_results_test_branch_form', $node, $noresults);
+  }
+
+  // TODO:  (Future development) If test has been queued or sent for too
+  // long, provide a form which will allow the user to reset the test (by
+  // updating status for tests with id = branch_nid in the pift_test table)
+
+  return implode("\n", $content);
+}
+
+/**
+ * Prepare a form API array to mark a specific module version for testing
+ *
+ * @param $form_state
+ * @param $project object The project node object
+ * @param version string The currently selected version
+ * @param versions array List of versions
+ * @return array
+ */
+function pift_results_test_branch_form(&$form_state, $project, $versions = array()) {
+  $form = array(
+    '#prefix' => '<div class="container-inline">',
+    '#suffix' => '</div>',
+  );
+
+  $description = '<strong>' . t('Note: Automated tests are typically run on ".x" branches.') . '</strong><br />';
+  $description .= t('Core examples: 6.x, 7.x, 8.x') . '<br />';
+  $description .= t('Contrib examples: 6.x-1.x, 7.x-2.x, 8.x-1.x') . '<br />';
+  $description = t('Marking a branch for testing will add it to the list of branch/patch requests sent to the automated testing infrastructure. ');
+  $description .= t('Once the tests complete, the results will be passed back to drupal.org and reflected on this page. ');
+  $description .= '<br />';
+
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $project->nid,
+  );
+
+  $form['additional'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Test Additional Branches'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#prefix' => '<div>',
+    '#suffix' => '</div>',
+    '#description' => $description,
+  );
+
+  $form['additional']['version'] = array(
+    '#type' => 'select',
+    '#title' => t('Select a branch/tag to queue for testing'),
+    '#options' => $versions,
+    '#required' => TRUE,
+  );
+
+  $form['additional'][] = array(
+    '#type' => 'submit',
+    '#value' => t('Queue Test'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validate the submitted pift_results_test_branch_form form
+ *
+ * @param $form form to validate
+ * @param $form_state A keyed array containing the current state of the form.
+ */
+function pift_results_test_branch_form_validate($form, &$form_state) {
+  // Ensure required values have been passed
+  if (!$form_state['values']['nid'] || !$form_state['values']['version']) {
+    form_set_error('form', t('Error: Unable to determine correct project tag/branch.'));
+  }
+}
+
+/**
+ * Process the submitted pift_results_test_branch_form form
+ *
+ * @param $form form to submit
+ * @param $form_state A keyed array containing the current state of the form.
+ */
+function pift_results_test_branch_form_submit($form, &$form_state) {
+  pift_results_test_add($form_state['values']['nid'], $form_state['values']['version']);
+}
+
+/**
+ * Queues a particular project branch/tag/release for testing
+ *
+ * @param $nid The node id for the given project to be tested
+ * @param $version A label representing a particular project branch/tag/release
+ */
+function pift_results_test_add($nid, $version) {
+
+  // Get the repo_id for the project
+  $repo_id = db_result(db_query("Select repo_id from {versioncontrol_project_projects} where nid = %d", $nid));
+
+  // Get the label_id for the passed version
+  $label_id = db_result(db_query("Select label_id from {versioncontrol_labels} where repo_id = %d and name = '%s'", $repo_id, $version));
+
+  /********************* Version Control Label ID code ********************
+  // For use if/when PIFT/PIFR are refactored to use label_id instead of release_nid.
+  if ($label_id) {
+    db_query('INSERT INTO {pift_test} (type, id, status) VALUES (%d, %d, %d)', PIFT_TYPE_RELEASE, $label_id, PIFT_STATUS_QUEUE);
+    drupal_set_message(t("This project branch/tag has been queued for testing."));
+  }
+  else {
+    drupal_set_message("Unable to queue this project branch/tag for testing, due to an error in resolving the corresponding label ID.", 'error');
+  }
+  */
+  /********************* Release Nid code ********************/
+  $release_nid = db_result(db_query("Select release_nid from {versioncontrol_release_labels} where label_id = %d and project_nid = %d", $label_id, $nid));
+  if ($release_nid) {
+    db_query('INSERT INTO {pift_test} (type, id, status) VALUES (%d, %d, %d)', PIFT_TYPE_RELEASE, $release_nid, PIFT_STATUS_QUEUE);
+    drupal_set_message("This project branch/tag has been queued for testing.");
+  }
+  else {
+    drupal_set_message("This branch does not have an associated release node.  Unable to queue branch for testing.", 'error');
+  }
+}
+
+/**
+ * Retrieves recent testing status for a given project branch/tag/release
+ *
+ * @param $node The node id for the given project
+ * @param $versions An array of project branch/tag/release labels
+ *
+ * @return $status Array of test results, or FALSE if no results available
+ */
+function pift_results_testresult_load($node, $versions) {
+  // Ensure we've been passed an actual project node
+  if ($node->type != 'project_project') {
+    return FALSE;
+  }
+  // Initialize placeholder for test results return value
+  $tests = array();
+
+  // Get the repo_id for the project
+  $repo_id = db_result(db_query("Select repo_id from {versioncontrol_project_projects} where nid = %d", $node->nid));
+
+  // Get the label_ids for the passed versions
+  $label_ids = array();
+  $result = db_query("Select label_id, name from {versioncontrol_labels}
+          where repo_id = %d and name IN (" . db_placeholders($versions, 'text') . ")",
+          array_merge( (array) $repo_id, $versions));
+  while ($data = db_fetch_object($result)) {
+    $label_ids[$data->name] = $data->label_id;
+  }
+
+  /********************* Version Control Label ID code ********************
+  // Get the branch test results for this label_id
+  $sql = "Select * from {pift_test} where type = 1 and id IN (" . db_placeholders($label_ids, 'int') . ")";
+  $result = db_query($sql, $label_ids);
+  */
+  /********************* Release Nid code ********************/
+  // Retrieve corresponding release nids for the requested labels
+  $release_nids = array();
+  $result = db_query("Select release_nid, label_id from {versioncontrol_release_labels}
+          where project_nid = %d and label_id IN (" . db_placeholders($label_ids, 'int') . ")",
+          array_merge( (array) $node->nid, $label_ids));
+  while ($data = db_fetch_object($result)) {
+    $release_nids[$data->label_id] = $data->release_nid;
+  }
+
+  // Obtain the actual test results
+  $sql = "Select test_id, status, message, id, last_tested from {pift_test}
+          where type = 1 and id IN (" . db_placeholders($release_nids, 'int') . ")
+          order by last_tested desc";
+  $result = db_query($sql, $release_nids);
+  while ($data = db_fetch_array($result)) {
+    $data['version'] = array_search(array_search($data['id'], $release_nids), $label_ids);
+    unset($data['id']);
+    $tests[$data['version']][] = $data;
+  }
+  if (!empty($tests)) {
+    // Re-order tests array
+    $sorted = array();
+    foreach ($versions as $version) {
+      if (array_key_exists($version, $tests)) {
+        $sorted[] = $tests[$version];
+      }
+    }
+    return $sorted;
+  }
+  return FALSE;
+}
+
+/**
+ * Formats the testing results table for output
+ *
+ * @param $tests An array of test statuses to be rendered
+ *
+ * @return Themed table containing the testing status for the given branch
+ */
+function pift_results_format_testresult_table($tests = array()) {
+  if (!empty($tests)) {
+    $header = array('Version', 'Status', 'Result', 'Last Tested', 'Operations');
+    foreach ($tests as $testdata) {
+      foreach ($testdata as $test) {
+        $rows[] = pift_results_format_testresult_row($test);
+      }
+    }
+  }
+  return theme('table', $header, $rows);
+}
+
+/**
+ * Formats a row of the testing results table for output
+ *
+ * @param $test An array of test status information to render
+ *
+ * @return Themed table containing the testing status for the given branch
+ */
+function pift_results_format_testresult_row($test = array()) {
+  if (!empty($test)) {
+    $options = array('1' => 'Queued', '2' => 'Sent', '3' => 'Failed', '4' => 'Passed');
+    $rowclass = array('1' => 'pift-retest', '2' => 'pift-retest', '3' => 'pift-fail', '4' => 'pift-pass');
+    $operations = array();
+    // A first-time queued test will not have a test_id, so hide links
+    if ($test['test_id'] > 0) {
+      $operations = array(
+        l('View Test', variable_get('pift_server', 'http://qa.drupal.org/') . 'pifr/test/' . $test['test_id']),
+      );
+      if (user_access('pift re-test files') && $test['status'] > 1) {
+        $operations[] = l('Re-test', 'pift/retest/' . $test['test_id']);
+      }
+    }
+    // Hide old results if new test queued/sent
+    if ($test['status'] < 3) {
+      $test['message'] = t('Waiting for results.');
+    }
+    // Format 'Last Tested' value
+    $test['last_tested'] = $test['last_tested'] > 0 ? format_date($test['last_tested'], 'small') : 'never';
+    $row = array(
+      'data' => array($test['version'], $options[$test['status']], $test['message'], $test['last_tested'], implode(" | ", $operations)),
+      'class' => $rowclass[$test['status']],
+    );
+    return $row;
+  }
+  return array();
+}
+
+/**
+ * Returns the default 'no test results available' text
+ */
+function pift_results_empty_text() {
+  $default = <<<EOT
+<p>No Test Results Available.</p>
+<p>Please use the 'queue test' form below to trigger a new test.</p>
+EOT;
+  return $default;
+}
