Index: review/simpletest/pifr_simpletest.server.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue_file_review/review/simpletest/pifr_simpletest.server.inc,v
retrieving revision 1.8
diff -u -r1.8 pifr_simpletest.server.inc
--- review/simpletest/pifr_simpletest.server.inc	8 Dec 2009 19:33:59 -0000	1.8
+++ review/simpletest/pifr_simpletest.server.inc	9 Dec 2009 23:35:06 -0000
@@ -3,374 +3,62 @@
 
 /**
  * @file
- * Provide server review implementation.
+ * Provide SimpleTest server review implementation.
  *
  * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
  */
 
-module_load_include('server.inc', 'pifr_assertion');
+module_load_include('server.inc', 'pifr_drupal');
 
-class pifr_server_review_pifr_simpletest extends pifr_server_review_pifr_assertion {
-
-  protected $confirmation_finish = PIFR_SERVER_TEST_RESULT_PASS;
+/**
+ * SimpleTest server review implementation.
+ */
+class pifr_server_review_pifr_simpletest extends pifr_server_review_pifr_drupal {
 
-  protected function argument_default() {
-    return array(
-      'core' => 7,
-      'database' => 'mysql-5.0-isam',
-      'tests' => array(),
-      'modules' => array(),
+  protected $argument_default = array(
+    'core' => 7,
+    'database' => 'mysql-5.0-isam',
+    'tests' => array(),
+    'modules' => array(),
+  );
+
+  /**
+   * Modify step information.
+   */
+  public function __construct() {
+    $this->steps['review'] = array(
+      'title' => 'detect a test run failure',
+      'description' => 'Ensure that you can install SimpleTest locally and invoke tests using the command line script.',
+      'summary' => 'Failed to run tests on @environment: @reason.',
+      'confirmation' => 'pifr_simpletest',
     );
-  }
-
-  public function confirmation_test(array $client, array $environment, $stage) {
-    $changes = $this->confirmation_test_change($stage);
-    return $this->confirmation_test_merge($client, $environment, $changes);
-  }
-
-  /**
-   * Get the changes to the default client confirmation test array.
-   *
-   * The changes should then be merged with the default client confirmation test
-   * array before being sent as the XML-RPC response.
-   *
-   * @param integer $stage Confirmation stage.
-   * @return array Changes to default client confirmation test array.
-   */
-  protected function confirmation_test_change($stage) {
-    // Incrament stage by one since PIFR_SERVER_TEST_RESULT_SETUP is ignored.
-    $stage++;
-
-    if ($stage == PIFR_SERVER_TEST_RESULT_FETCH) {
-      return array(
-        'files' => array(
-          'http://example.com/pifr_server/some.patch',
-        ),
-      );
-    }
-    elseif ($stage == PIFR_SERVER_TEST_RESULT_CVS) {
-      return array(
-        'vcs' => array(
-          'main' => array(
-            'repository' => array(
-              'type' => 'cvs',
-              'url' => 'example.com/cvs',
-            ),
-          ),
-        ),
-      );
-    }
-    else {
-      // Stages: PIFR_SERVER_TEST_RESULT_PATCH through PIFR_SERVER_TEST_RESULT_PASS.
-      $patch = $this->confirmation_test_patch($stage);
-      $changes = array(
-        'files' => array(
-          url(file_directory_path() . '/pifr_simpletest/' . $patch . '.patch', array('absolute' => TRUE)),
-        ),
-      );
-
-      // Only run the BlockTestCase to detect failed test results. The entire
-      // test suite will be run in the next step anyway.
-      if ($stage == PIFR_SERVER_TEST_RESULT_FAIL) {
-        $changes['review'] = array(
-          'argument' => array(
-            'tests' => array('NonDefaultBlockAdmin'),
-          )
-        );
-      }
-
-      return $changes;
-    }
-  }
-
-  /**
-   * Get the patch related to the stage.
-   *
-   * @param integer $stage Confirmation stage.
-   * @return string Patch name without the '.patch' suffix.
-   */
-  protected function confirmation_test_patch($stage) {
-    $patches = array('patch', 'apply', 'syntax', 'install', 'test', 'fail', 'pass');
-    return $patches[$stage - PIFR_SERVER_TEST_RESULT_PATCH];
-  }
-
-  /**
-   * Merge an array of changes with the default client confirmation test array.
-   *
-   * @param array $client Client information
-   * @param array $environment Environment information.
-   * @param array $changes Changes to default client confirmation test array.
-   * @return array XML-RPC reponse containing test definition.
-   */
-  protected function confirmation_test_merge($client, array $environment, array $changes) {
-    $default = array(
-      'test_id' => $client['test_id'],
-      'review' => array(
-        'plugin' => $environment['plugin'],
-        'argument' => pifr_server_review_argument_get($environment),
-      ),
-      'vcs' => array(
-        'main' => array(
-          'repository' => array(
-            'type' => 'cvs',
-            'url' => ':pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal/drupal',
-          ),
-          'vcs_identifier' => 'HEAD',
-        ),
-        'dependencies' => array(),
-      ),
-      'files' => array(),
+    $this->steps['fail'] = array(
+      'title' => 'detect a failing test',
+      'description' => 'Review the test failures below then if necessary run them locally until the problem has been fixed.',
+      'confirmation' => 'pifr_simpletest',
+    );
+    $this->steps['pass'] = array(
+      'title' => 'complete test suite with all tests passing',
+      'confirmation' => 'pifr_simpletest',
     );
-    return array_replace_recursive($default, $changes);
-  }
-
-  public function confirmation_check(array $client, array $result, array $status) {
-    // Incrament stage by one since PIFR_SERVER_TEST_RESULT_SETUP is ignored.
-    $status['stage'] = $status['stage'] + 1;
-
-    return parent::confirmation_check($client, $result, $status);
-  }
-
-  public function check(array $result) {
-    return $result['code'] == PIFR_SERVER_TEST_RESULT_PASS;
-  }
-
-  public function summary(array $result) {
-    switch ($result['code']) {
-      case PIFR_SERVER_TEST_RESULT_SETUP:
-        return 'Setup environment: @reason.';
-      case PIFR_SERVER_TEST_RESULT_FETCH:
-        return 'Fetch test file: @reason.';
-      case PIFR_SERVER_TEST_RESULT_CVS:
-        return 'Repository checkout: @reason.';
-      case PIFR_SERVER_TEST_RESULT_PATCH:
-        return 'Invalid patch format in @filename.';
-      case PIFR_SERVER_TEST_RESULT_APPLY:
-        return 'Unable to apply patch @filename';
-      case PIFR_SERVER_TEST_RESULT_SYNTAX:
-        return 'Invalid PHP syntax in @filename.';
-      case PIFR_SERVER_TEST_RESULT_INSTALL:
-        return 'Failed to install Drupal on @environment.';
-      case PIFR_SERVER_TEST_RESULT_TEST:
-        return 'Failed to run tests on @environment: @reason.';
-      case PIFR_SERVER_TEST_RESULT_FAIL:
-        return 'Failed on @environment, with: @pass pass(es), @fail fail(s), and @exception exception(es).';
-      default:
-        return 'Unknown';
-    }
-  }
-
-  /**
-   * Details will include steps, test summaries, and assertions as applicable.
-   */
-  public function detail(array $test, array $result) {
-    return $this->detail_step($test, $result) . $this->detail_table($result);
   }
 
   /**
-   * Generate a list of steps.
-   *
-   * The list will contain classes and additional details based on the test,
-   * environment, and results.
-   *
-   * @param array $test Test information.
-   * @param array $result Test results.
-   * @return string HTML output.
+   * Add the 'tests' argument when performing the fail operation.
    */
-  protected function detail_step(array $test, array $result) {
-    $is_client = $test['type'] == PIFR_SERVER_TEST_TYPE_CLIENT;
-    if ($is_client) {
-      $client = pifr_server_client_get_test($test['test_id']);
-      $status = pifr_server_client_confirmation_status($client);
-
-      // Since there is a result for this environment then it has been tested. If
-      // the client is enabled then the confirmation test passed and thus this
-      // environment must have passed. If the client is not enabled and the last
-      // environment the confirmation test ran in is not this one then it must
-      // have passed, otherwise the last environment to run is this one and it
-      // should be evaluated based on the stage it got to.
-      if ($client['status'] == PIFR_SERVER_CLIENT_STATUS_ENABLED || $status['environment_id'] != $result['environment_id']) {
-        $code = PIFR_SERVER_TEST_RESULT_PASS;
-      }
-      else {
-        // $status['environment_id'] == $result['environment_id']
-        $code = $status['stage'] + 1;
-      }
-    }
-    else {
-      // Since the test is not a client test it can be evaluated solely on the
-      // basis of the result code.
-      $code = $result['code'];
-    }
+  protected function confirmation_test_change($key, $module) {
+    $changes = parent::confirmation_test_change($key, $module);
 
-    // Generate a list of the steps.
-    $steps = array();
-    for ($i = PIFR_SERVER_TEST_RESULT_SETUP; $i <= PIFR_SERVER_TEST_RESULT_PASS; $i++) {
-      $step = array(
-        'data' => ucfirst($this->detail_step_title($i)),
-        'class' => array(),
+    // Only run the NonDefaultBlockAdmin to detect failed test results. The
+    // entire test suite will be run in the next step anyway.
+    if ($key == 'fail') {
+      $changes['review'] = array(
+        'argument' => array(
+          'tests' => array('NonDefaultBlockAdmin'),
+        )
       );
-
-      // Add classes used to style the first three steps since they are unrelated
-      // to the item being tested.
-      if ($i <= PIFR_SERVER_TEST_RESULT_CVS) {
-        $step['class'][] = 'pifr-init';
-        if ($i == PIFR_SERVER_TEST_RESULT_CVS) {
-          $step['class'][] = 'pifr-last';
-        }
-      }
-
-      // If the step is less then the code then it has passed. If the step is
-      // equal to the code then it must be checked, otherwise ignore the step.
-      if ($i < $code) {
-        $step['class'][] = 'pifr-pass';
-      }
-      elseif ($i == $code) {
-        // If the step is the final step and the result has passed then mark as
-        // passed, otherwise the step is the one that failed.
-        if ($i == PIFR_SERVER_TEST_RESULT_PASS && $result['code'] == PIFR_SERVER_TEST_RESULT_PASS) {
-          $step['class'][] = 'pifr-pass';
-        }
-        else {
-          $step['class'][] = 'pifr-fail';
-
-          if ($is_client) {
-            $step['data'] .= '<div>' . t($this->detail_step_confirmation_description($i)) . '</div>';
-          }
-          else {
-            $step['data'] .= '<div>' . t($this->detail_step_description($i)) . '</div>';
-          }
-        }
-      }
-
-      $step['class'] = implode(' ', $step['class']);
-      $steps[] = $step;
     }
-    return theme('item_list', $steps, NULL, 'ol', array('id' => 'pifr-steps'));
-  }
 
-  /**
-   * Get the client confirmation test step title.
-   *
-   * @param integer $step Step of test.
-   * @return string Title of step.
-   */
-  protected function detail_step_title($step) {
-     switch ($step) {
-      case PIFR_SERVER_TEST_RESULT_SETUP:
-        return t('setup environment');
-      case PIFR_SERVER_TEST_RESULT_FETCH:
-        return t('detect an invalid file URL');
-      case PIFR_SERVER_TEST_RESULT_CVS:
-        return t('detect invalid repository information');
-      case PIFR_SERVER_TEST_RESULT_PATCH:
-        return t('detect invalid patch format');
-      case PIFR_SERVER_TEST_RESULT_APPLY:
-        return t('detect a non-applicable patch');
-      case PIFR_SERVER_TEST_RESULT_SYNTAX:
-        return t('detect invalid PHP syntax');
-      case PIFR_SERVER_TEST_RESULT_INSTALL:
-        return t('detect a Drupal installation failure');
-      case PIFR_SERVER_TEST_RESULT_TEST:
-        return t('detect a test run failure');
-      case PIFR_SERVER_TEST_RESULT_FAIL:
-        return t('detect a failing test');
-      case PIFR_SERVER_TEST_RESULT_PASS:
-        return t('complete test suite with all tests passing');
-      default:
-        return 'Unknown';
-    }
-  }
-
-  /**
-   * Get the test step description.
-   *
-   * @param integer $step Step of test.
-   * @return string Description of step.
-   */
-  protected function detail_step_description($step) {
-    $base_url = url(file_directory_path() . '/pifr_simpletest/', array('absolute' => TRUE));
-    switch ($step) {
-      case PIFR_SERVER_TEST_RESULT_SETUP:
-        return t('The client is most likely malfunctioning.');
-      case PIFR_SERVER_TEST_RESULT_FETCH:
-        return t('Ensure the file server is functioning properly.');
-      case PIFR_SERVER_TEST_RESULT_CVS:
-        return t('Ensure the version control system is functioning properly.');
-      case PIFR_SERVER_TEST_RESULT_PATCH:
-        return t('Ensure the patch only contains unix-style line endings.');
-      case PIFR_SERVER_TEST_RESULT_APPLY:
-        return t('Ensure the patch applies to the lastest checkout.');
-      case PIFR_SERVER_TEST_RESULT_SYNTAX:
-        return t('Check the syntax of your PHP files');
-      case PIFR_SERVER_TEST_RESULT_INSTALL:
-        return t('Ensure that you can perform a fresh Drupal install.');
-      case PIFR_SERVER_TEST_RESULT_TEST:
-        return t('Ensure that you can install SimpleTest locally and invoke tests using the command line script.');
-      case PIFR_SERVER_TEST_RESULT_FAIL:
-        return t('Review the test failures below then if necessary run them locally until the problem has been fixed.');
-      case PIFR_SERVER_TEST_RESULT_PASS:
-        return '';
-      default:
-        return 'Unknown';
-    }
-  }
-
-  /**
-   * Get the client confirmation test step description.
-   *
-   * @param integer $step Step of test.
-   * @return string Description of step.
-   */
-  protected function detail_step_confirmation_description($type) {
-    $base_url = url(file_directory_path() . '/pifr_simpletest/', array('absolute' => TRUE));
-    switch ($type) {
-      case PIFR_SERVER_TEST_RESULT_SETUP:
-        return t('The client failed to setup the testing environment. Check the pifr_client INSTALL.txt.');
-      case PIFR_SERVER_TEST_RESULT_FETCH:
-        return t('The client failed to detect an invalid file URL that pointed to example.com.');
-      case PIFR_SERVER_TEST_RESULT_CVS:
-        return t('The client failed to detect invalid repository information that pointed to example.com.');
-      case PIFR_SERVER_TEST_RESULT_PATCH:
-      case PIFR_SERVER_TEST_RESULT_APPLY:
-      case PIFR_SERVER_TEST_RESULT_SYNTAX:
-      case PIFR_SERVER_TEST_RESULT_INSTALL:
-      case PIFR_SERVER_TEST_RESULT_TEST:
-      case PIFR_SERVER_TEST_RESULT_FAIL:
-        $patch = $this->confirmation_test_patch($type);
-        return t('The client failed to @type which is induced using the !patch.',
-          array('@type' => $this->detail_step_title($type), '!patch' => l($patch . '.patch', $base_url . $patch . '.patch')));
-      case PIFR_SERVER_TEST_RESULT_PASS:
-        return t('Client failed to run the test suite with all tests passing.');
-      default:
-        return 'Unknown';
-    }
-  }
-
-  public function code_title($code) {
-     switch ($code) {
-      case PIFR_SERVER_TEST_RESULT_SETUP:
-        return t('setup environment');
-      case PIFR_SERVER_TEST_RESULT_FETCH:
-        return t('invalid file URL');
-      case PIFR_SERVER_TEST_RESULT_CVS:
-        return t('invalid repository information');
-      case PIFR_SERVER_TEST_RESULT_PATCH:
-        return t('invalid patch format');
-      case PIFR_SERVER_TEST_RESULT_APPLY:
-        return t('non-applicable patch');
-      case PIFR_SERVER_TEST_RESULT_SYNTAX:
-        return t('invalid PHP syntax');
-      case PIFR_SERVER_TEST_RESULT_INSTALL:
-        return t('Drupal installation failure');
-      case PIFR_SERVER_TEST_RESULT_TEST:
-        return t('test run failure');
-      case PIFR_SERVER_TEST_RESULT_FAIL:
-        return t('failing test');
-      case PIFR_SERVER_TEST_RESULT_PASS:
-        return t('all tests passing');
-      default:
-        return 'Unknown';
-    }
+    return $changes;
   }
 }
Index: review/server.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue_file_review/review/server.inc,v
retrieving revision 1.8
diff -u -r1.8 server.inc
--- review/server.inc	8 Dec 2009 19:33:59 -0000	1.8
+++ review/server.inc	9 Dec 2009 23:35:06 -0000
@@ -9,11 +9,222 @@
  */
 
 /**
- * Provide base server review class.
+ * Base server review class.
  */
 abstract class pifr_server_review {
 
   /**
+   * Associative array of default arguments.
+   *
+   * @var array
+   */
+  protected $argument_default = array();
+
+  /**
+   * The operations which make up a review mapped to integer codes.
+   *
+   * @var array
+   */
+  protected $map = array();
+
+  /**
+   * The operations which make up a confirmation test mapped to integer codes.
+   *
+   * @var array
+   */
+  protected $confirmation_map = array();
+
+  /**
+   * The default confirmation test array.
+   *
+   * @var array
+   */
+  protected $confirmation_default = array(
+    'test_id' => 17,
+    'review' => array(
+      'plugin' => '',
+      'argument' => array(),
+    ),
+    'vcs' => array(),
+    'files' => array(),
+  );
+
+  /**
+   * Detailed step information.
+   *
+   * @var array
+   */
+  protected $steps = array(
+    'setup' => array(
+      'title' => 'setup environment',
+      'description' => 'The client is most likely malfunctioning.',
+      'help' => 'The client failed to setup the testing environment. Check the pifr_client the !install.',
+      'summary' => 'Setup environment: @reason.',
+      'confirmation' => FALSE,
+    ),
+    'fetch' => array(
+      'title' => 'detect an invalid file URL',
+      'description' => 'Ensure the file server is functioning properly.',
+      'help' => 'The client failed to detect an invalid file URL that pointed to example.com.',
+      'summary' => 'Fetch test file: @reason.',
+      'confirmation' => 'no-patch',
+    ),
+    'checkout' => array(
+      'title' => 'detect invalid repository information',
+      'description' => 'Ensure the version control system is functioning properly.',
+      'help' => 'The client failed to detect invalid repository information that pointed to example.com.',
+      'summary' => 'Repository checkout: @reason.',
+      'confirmation' => 'no-patch',
+    ),
+    'check' => array(
+      'title' => 'detect invalid patch format',
+      'description' => 'Ensure the patch only contains unix-style line endings.',
+      'help' => 'The client failed to @type which is induced using the !patch.',
+      'summary' => 'Invalid patch format in @filename.',
+      'confirmation' => 'pifr_server',
+    ),
+    'apply' => array(
+      'title' => 'detect a non-applicable patch',
+      'description' => 'Ensure the patch applies to the lastest checkotu of the code-base.',
+      'help' => 'The client failed to @type which is induced using the !patch.',
+      'summary' => 'Unable to apply patch @filename.',
+      'confirmation' => 'pifr_server',
+    ),
+    'syntax' => array(
+      'title' => 'detect invalid syntax',
+      'description' => 'Check the syntax of the files.',
+      'help' => 'The client failed to @type which is induced using the !patch.',
+      'summary' => 'Invalid syntax in @filename.',
+      'confirmation' => FALSE,
+    ),
+    'install' => array(
+      'title' => 'detect an installation failure',
+      'description' => 'Ensure that you can perform a fresh installation.',
+      'help' => 'The client failed to @type which is induced using the !patch.',
+      'summary' => 'Failed to install on @environment.',
+      'confirmation' => FALSE,
+    ),
+    'review' => array(
+      'title' => 'detect a review run failure',
+      'description' => 'Ensure that you can perform the review manually.',
+      'help' => 'The client failed to @type which is induced using the !patch.',
+      'summary' => 'Failed to review on @environment: @reason.',
+      'confirmation' => FALSE,
+    ),
+    'fail' => array(
+      'title' => 'detect a failing review',
+      'description' => 'Look over the review failures below.',
+      'help' => 'The client failed to @type which is induced using the !patch.',
+      'summary' => 'Failed review on @environment.',
+      'confirmation' => FALSE,
+    ),
+    'pass' => array(
+      'title' => 'complete test suite with all tests passing',
+      'description' => 'Congratulation everything looks good!',
+      'help' => 'Client failed to run the test suite with all tests passing.',
+      'summary' => 'Passed review on @environment.',
+      'confirmation' => FALSE,
+    ),
+  );
+
+  /**
+   * Generate step map and confirmation step map.
+   */
+  public function __construct() {
+    $i = $j = 1;
+    foreach ($this->steps as $step => $info) {
+      $this->map[$i++] = $step;
+      if ($info['confirmation'] !== FALSE) {
+        $this->confirmation_map[$j++] = $step;
+      }
+    }
+  }
+
+  /**
+   * Get all the operations.
+   *
+   * @return array List of operation keys.
+   */
+  public function operation_get_all() {
+    return array_keys($this->steps);
+  }
+
+  /**
+   * Get the operation related to the code.
+   *
+   * @param integer $code Operation code.
+   * @return string Operation related to the code.
+   */
+  public function operation_get($code) {
+    return $this->map[$code];
+  }
+
+  /**
+   * Get the operation related to the confirmation stage code.
+   *
+   * @param integer $code Confirmation stage operation code.
+   * @return string Confirmation operation related to the code.
+   */
+  public function operation_confirmation_get($code) {
+    return $this->confirmation_map[$code];
+  }
+
+  /**
+   * Get all the steps.
+   *
+   * @return array List of steps.
+   * @see self::step_get()
+   */
+  public function step_get_all() {
+    return $this->steps;
+  }
+
+  /**
+   * Get the step related to the code.
+   *
+   * @param integer $code Step code.
+   * @return array Step information, with keys: key, title, description, help,
+   *   and summary, confirmation.
+   */
+  public function step_get($code) {
+    $key = $this->operation_get($code);
+    $step = $this->steps[$key];
+    $step['key'] = $key;
+
+    return $step;
+  }
+
+  /**
+   * Get the confirmation step related to the code.
+   *
+   * @param integer $code Confirmation stage code.
+   * @return array Step information.
+   * @see self::step_get()
+   */
+  public function step_confirmation_get($code) {
+    $key = $this->operation_confirmation_get($code);
+    $step = $this->steps[$key];
+    $step['key'] = $key;
+
+    return $step;
+  }
+
+  /**
+   * Get the code related to the operation.
+   *
+   * @param string $operation Operation key.
+   * @return integer Code related to operation.
+   */
+  public function code_get($operation) {
+    foreach ($this->map as $code => $key) {
+      if ($operation == $key) {
+        return $code;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
    * Merge arguments from the various parts that makeup the test.
    *
    * The default behavior is to merge the dependency arguments in reverse order
@@ -28,8 +239,6 @@
    * @see self::argument_default()
    */
   public function argument(array $environment_argument, array $dependency_argument, array $branch_argument) {
-    $default = $this->argument_default();
-
     // Merge dependency arguments in reverse order so that first dependencies
     // have priority.
     $merged = array();
@@ -37,54 +246,306 @@
       $merged = array_replace_recursive($argument, $merged);
     }
 
-    return array_replace_recursive($default, $environment_argument, $merged, $branch_argument);
+    return array_replace_recursive($this->argument_default, $environment_argument, $merged, $branch_argument);
   }
 
   /**
-   * Provides the default arguments.
+   * Get a client confirmation test for the stage.
    *
-   * @return array
-   *   Associative array of default arguments.
+   * @param array Client information.
+   * @param array Environment information.
+   * @param integer $stage Confirmation stage.
+   * @return array XML-RPC reponse containing test definition.
+   * @see pifr_server_xmlrpc_next()
    */
-  protected function argument_default() {
+  public function confirmation_test_get(array $client, array $environment, $stage) {
+    $step = $this->step_confirmation_get($stage);
+    $changes = $this->confirmation_test_change($step['key'], $step['confirmation']);
+    return $this->confirmation_test_merge($client, $environment, $changes);
+  }
+
+  /**
+   * Get the changes to the default client confirmation test array.
+   *
+   * The changes should then be merged with the default client confirmation test
+   * array before being sent as the XML-RPC response.
+   *
+   * @param string $key Confirmation step key.
+   * @param string $module Module providing the confirmation patch, or
+   *   'no-patch' if a patch is not required for confirmation stage.
+   * @return array Changes to default client confirmation test array.
+   */
+  protected function confirmation_test_change($key, $module) {
+    if ($key == 'fetch') {
+      return array(
+        'files' => array(
+          'http://example.com/pifr_server/some.patch',
+        ),
+      );
+    }
+    elseif ($key == 'checkout') {
+      return array(
+        'vcs' => array(
+          'main' => array(
+            'repository' => array(
+              'type' => 'cvs',
+              'url' => 'example.com/cvs',
+            ),
+          ),
+        ),
+      );
+    }
+    elseif ($module != 'no-patch') {
+      // The stages require a patch to cause the client to fail in such a way
+      // as to cause the desired result code.
+      return array(
+        'files' => array(
+          $this->confirmation_test_patch($key, $module),
+        ),
+      );
+    }
     return array();
   }
 
   /**
-   * Get a test that will ensure that the specified result code is triggered.
+   * Get a confirmation patch related to the stage.
    *
-   * @param array Client information.
-   * @param array Environment information.
-   * @param integer $stage Confirmation stage.
-   * @return array XML-RPC reponse containing test definition, see
-   *   pifr_server_xmlrpc_next().
-   * @see pifr_server_xmlrpc_next()
+   * @param string $key Confirmation step key.
+   * @param string $module Module providing the confirmation patch.
+   * @return string Absolute URL to confirmation patch.
+   */
+  protected function confirmation_test_patch($key, $module) {
+    $base = url('', array('absolute' => TRUE));
+    return $base . drupal_get_path('module', $module) . '/confirmation/' . $key . '.patch';
+  }
+
+  /**
+   * Merge an array of changes with the default client confirmation test array.
+   *
+   * @param array $client Client information.
+   * @param array $environment Environment information.
+   * @param array $changes Changes to default client confirmation test array.
+   * @return array XML-RPC reponse containing test definition.
+   */
+  protected function confirmation_test_merge($client, array $environment, array $changes) {
+    $test = array(
+      'test_id' => $client['test_id'],
+      'review' => array(
+        'plugin' => $environment['plugin'],
+        'argument' => pifr_server_review_argument_get($environment),
+      ),
+    );
+    return array_replace_recursive($this->confirmation_default, $test, $changes);
+  }
+
+  /**
+   * Check the result of a client confirmation test.
+   *
+   * @param array $client Client information.
+   * @param array $result Result information.
+   * @param array $status Client confirmation status information.
+   * @return string Status of client confirmation, either: pass, fail, or
+   *   continue.
+   */
+  public function confirmation_check(array $client, array $result, array $status) {
+    $step = $this->step_confirmation_get($status['stage']);
+    $operation = $this->operation_get($result['code']);
+
+    if ($status['stage'] == count($this->confirmation_map) && $step['key'] == $operation) {
+      // Client confirmation has reached the last stage and is now complete.
+      return 'pass';
+    }
+    else {
+      // Check the result against the current stage.
+      if ($operation != $key) {
+        return 'fail';
+      }
+      else {
+        // Result is fine and the test is not complete so continue to next stage.
+        return 'continue';
+      }
+    }
+  }
+
+  /**
+   * Check a result.
+   *
+   * @param array $result Result information.
+   * @return boolean TRUE if pass, otherwise FALSE.
+   */
+  public function check(array $result) {
+    return $this->operation_get($result['code']) == 'pass';
+  }
+
+  /**
+   * Get the unformatted summary message for a review result.
+   *
+   * @param array $result Result information.
+   * @return string Unformatted summary message.
    */
-  public function confirmation_test(array $client, array $environment, $stage);
+  public function summary(array $result) {
+    $step = $this->step_get($result['code']);
+    return $step['summary'];
+  }
 
-  public function confirmation_check(array $client, array $result, array $status);
+  /**
+   * Format the result details.
+   *
+   * @param array $details Result detail array.
+   * @return array Formated result details.
+   */
+  public function summary_format(array $details) {
+    return $details;
+  }
 
-  public function check(array $result);
+  /**
+   * Generate a detailed display of test a test result.
+   *
+   * @param array $test Test information.
+   * @param array $result Test result information.
+   * @return stirng HTML output.
+   */
+  public function detail(array $test, array $result) {
+    return $this->detail_step($test, $result) . $this->detail_display($test, $result);
+  }
 
-  public function summary(array $result);
+  /**
+   * Generate a list of steps.
+   *
+   * The list will contain classes and additional details based on the test,
+   * environment, and results.
+   *
+   * @param array $test Test information.
+   * @param array $result Test result information.
+   * @return string HTML output.
+   */
+  protected function detail_step(array $test, array $result) {
+    $is_client = $test['type'] == PIFR_SERVER_TEST_TYPE_CLIENT;
+    if ($is_client) {
+      $client = pifr_server_client_get_test($test['test_id']);
+      $status = pifr_server_client_confirmation_status($client);
+
+      // Since there is a result for this environment then it has been tested. If
+      // the client is enabled then the confirmation test passed and thus this
+      // environment must have passed. If the client is not enabled and the last
+      // environment the confirmation test ran in is not this one then it must
+      // have passed, otherwise the last environment to run is this one and it
+      // should be evaluated based on the stage it got to.
+      if ($client['status'] == PIFR_SERVER_CLIENT_STATUS_ENABLED || $status['environment_id'] != $result['environment_id']) {
+        $code = $this->code_get('pass');
+      }
+      else {
+        // $status['environment_id'] == $result['environment_id']
+        $code = $status['stage'] + 1;
+      }
+    }
+    else {
+      // Since the test is not a client test it can be evaluated solely on the
+      // basis of the result code.
+      $code = $result['code'];
+    }
 
-  public function summary_format(array $details);
+    // Generate a list of the steps.
+    $steps = array();
+    foreach ($this->steps as $operation => $info) {
+      $step_code = $this->code_get($operation);
+
+      $step = array(
+        'data' => ucfirst($info['title']),
+        'class' => array(),
+      );
+
+      // Add classes used to style the first three steps since they are unrelated
+      // to the item being tested.
+      if ($step_code <= $this->code_get('checkout')) {
+        $step['class'][] = 'pifr-init';
+        if ($operation == 'checkout') {
+          $step['class'][] = 'pifr-last';
+        }
+      }
+
+      // If the step is less then the code then it has passed. If the step is
+      // equal to the code then it must be checked, otherwise ignore the step.
+      if ($step_code < $code) {
+        $step['class'][] = 'pifr-pass';
+      }
+      elseif ($step_code == $code) {
+        // If the step is the final step and the result has passed then mark as
+        // passed, otherwise the step is the one that failed.
+        if ($operation == 'pass' && $this->check($result['code'])) {
+          $step['class'][] = 'pifr-pass';
+        }
+        else {
+          $step['class'][] = 'pifr-fail';
+
+          if ($is_client) {
+            $args = array('@type' => $info['title']);
+            if ($info['confirmation'] !== FALSE && $info['confirmation'] != 'no-patch') {
+              $args['!patch'] = l($operation . '.patch', $this->confirmation_test_patch($operation, $info['confirmation']));
+            }
+            $step['data'] .= '<div>' . t($info['help'], $args) . '</div>';
+          }
+          else {
+            $step['data'] .= '<div>' . t($step['description']) . '</div>';
+          }
+        }
+      }
+
+      $step['class'] = implode(' ', $step['class']);
+      $steps[] = $step;
+    }
+    return theme('item_list', $steps, NULL, 'ol', array('id' => 'pifr-steps'));
+  }
 
-  public function detail(array $test, array $result);
+  /**
+   * Display detailed information about a test result.
+   *
+   * @param array $test Test information.
+   * @param array $result Test result information.
+   * @return string HTML output.
+   */
+  protected abstract function detail_display(array $test, array $result);
 
-  public function detail_summary(array $test, array $result);
+  /**
+   * Generate detailed summary display of test results.
+   *
+   * @param array $test Test information.
+   * @param array $result Test result information.
+   * @return string HTML output.
+   */
+  public abstract function detail_summary(array $test, array $result);
 
   /**
-   * Get the code title.
+   * Get the title for a review code.
    *
-   * @param integer $code Result code.
+   * @param integer $code Review code.
    * @return string Title representing code.
    */
-  public function code_title($code);
+  public function code_title($code) {
+    return t($this->code_title[$code - 1]);
+  }
 
+  /**
+   * Load plugin specific data for a result.
+   *
+   * @param integer $result_id Result ID.
+   * @return array Array of plugin specific data.
+   */
   public function load($result_id);
 
+  /**
+   * Record plugin specific data for a result.
+   *
+   * @param integer $result_id Result ID.
+   * @param array $data Array of plugin specific data.
+   */
   public function record($result_id, array $data);
 
+  /**
+   * Delete plugin specific data for a result.
+   *
+   * @param integer $result_id Result ID.
+   * @param array $data Array of plugin specific data.
+   */
   public function delete($result_id, array $data);
 }
Index: review/simpletest/confirmation/syntax.patch
===================================================================
RCS file: review/simpletest/confirmation/syntax.patch
diff -N review/simpletest/confirmation/syntax.patch
--- review/simpletest/confirmation/syntax.patch	10 Oct 2009 21:39:04 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,16 +0,0 @@
-Index: install.php
-===================================================================
-RCS file: /cvs/drupal/drupal/install.php,v
-retrieving revision 1.152
-diff -u -r1.152 install.php
---- install.php	4 Feb 2009 04:42:26 -0000	1.152
-+++ install.php	5 Feb 2009 07:44:00 -0000
-@@ -5,7 +5,7 @@
-  * Root directory of Drupal installation.
-  */
- define('DRUPAL_ROOT', getcwd());
--
-+syntax error
- require_once DRUPAL_ROOT . '/includes/install.inc';
- 
- /**
Index: review/simpletest/confirmation/install.patch
===================================================================
RCS file: review/simpletest/confirmation/install.patch
diff -N review/simpletest/confirmation/install.patch
--- review/simpletest/confirmation/install.patch	10 Oct 2009 21:39:04 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,16 +0,0 @@
-Index: install.php
-===================================================================
-RCS file: /cvs/drupal/drupal/install.php,v
-retrieving revision 1.152
-diff -u -r1.152 install.php
---- install.php	4 Feb 2009 04:42:26 -0000	1.152
-+++ install.php	5 Feb 2009 07:42:26 -0000
-@@ -5,7 +5,7 @@
-  * Root directory of Drupal installation.
-  */
- define('DRUPAL_ROOT', getcwd());
--
-+break_me();
- require_once DRUPAL_ROOT . '/includes/install.inc';
- 
- /**
Index: review/simpletest/confirmation/test.patch
===================================================================
RCS file: review/simpletest/confirmation/test.patch
diff -N review/simpletest/confirmation/test.patch
--- review/simpletest/confirmation/test.patch	10 Oct 2009 21:39:04 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,15 +0,0 @@
-Index: scripts/run-tests.sh
-===================================================================
-RCS file: /cvs/drupal/drupal/scripts/run-tests.sh,v
-retrieving revision 1.28
-diff -u -r1.28 run-tests.sh
---- scripts/run-tests.sh  10 Jun 2009 16:17:02 -0000  1.28
-+++ scripts/run-tests.sh  25 Jun 2009 00:35:00 -0000
-@@ -5,6 +5,7 @@
-  * This script runs Drupal tests from command line.
-  */
- 
-+break_me();
- define('SIMPLETEST_SCRIPT_COLOR_PASS', 32); // Green.
- define('SIMPLETEST_SCRIPT_COLOR_FAIL', 31); // Red.
- define('SIMPLETEST_SCRIPT_COLOR_EXCEPTION', 33); // Brown.
Index: review/simpletest/confirmation/apply.patch
===================================================================
RCS file: review/simpletest/confirmation/apply.patch
diff -N review/simpletest/confirmation/apply.patch
--- review/simpletest/confirmation/apply.patch	10 Oct 2009 21:39:04 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1 +0,0 @@
-fail to apply
\ No newline at end of file
Index: review/assertion/pifr_assertion.server.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/project_issue_file_review/review/assertion/pifr_assertion.server.inc,v
retrieving revision 1.17
diff -u -r1.17 pifr_assertion.server.inc
--- review/assertion/pifr_assertion.server.inc	8 Dec 2009 19:33:59 -0000	1.17
+++ review/assertion/pifr_assertion.server.inc	9 Dec 2009 23:35:06 -0000
@@ -3,7 +3,7 @@
 
 /**
  * @file
- * Provide server review implementation.
+ * Provide general assertion server review implementation.
  *
  * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
  */
@@ -13,27 +13,65 @@
  */
 define('PIFR_CLIENT_MAX_ASSERTIONS', 500);
 
+/**
+ * General assertion server review implementation.
+ */
 abstract class pifr_server_review_pifr_assertion extends pifr_server_review {
 
-  protected $confirmation_finish = 0;
+  /**
+   * Modify step information.
+   */
+  public function __construct() {
+    $this->steps['fail'] = array(
+      'description' => 'Review the test failures below then if necessary run them locally until the problem has been fixed.',
+      'summary' => 'Failed on @environment, with: @pass pass(es), @fail fail(s), and @exception exception(es).',
+    );
+    $this->steps['pass'] = array(
+      'summary' => 'Passed on @environment, with: @pass pass(es).',
+    );
+  }
 
-  public function confirmation_check(array $client, array $result, array $status) {
-    if ($result['code'] == $this->confirmation_finish && $status['stage'] == $this->confirmation_finish) {
-      // Client confirmation has reached the last stage and is now complete.
-      return 'pass';
-    }
-    else {
-      // Check the result against the current stage.
-      if ($result['code'] != $status['stage']) {
-        return 'fail';
+  /**
+   * Format the result detail numbers.
+   *
+   * Performs a number format on each of the three assertion summary counts:
+   * pass, fail, exception (with @ in front of each).
+   */
+  public function summary_format(array $details) {
+    foreach (array('@pass', '@fail', '@exception') as $key) {
+      if (!empty($details[$key])) {
+        $details[$key] = number_format($details[$key]);
       }
-      else {
-        // Result is fine and the test is not complete so continue to next stage.
-        return 'continue';
+    }
+    return $details;
+  }
+
+  /**
+   * The display will provide a list of non-pass tests.
+   */
+  public function detail_summary(array $test, array $result) {
+    $failed_tests = array();
+    foreach ($result['data'] as $detail) {
+      if ($detail['fail'] + $detail['exception'] > 0) {
+        $details = array(
+          '@pass' => $detail['pass'],
+          '@fail' => $detail['fail'],
+          '@exception' => $detail['exception'],
+        );
+        $details = t('@pass pass(es), @fail fail(s), and @exception exception(s)', $details);
+        $failed_tests[] = $detail['test_name'] . ' (' . $details . ')';
       }
     }
+
+    if (!$failed_tests) {
+      $failed_tests[] = t('No relevant data');
+    }
+    return theme('item_list', $failed_tests);
   }
 
+  /**
+   * Load tree of assertion information.
+   */
   public function load($result_id) {
     $data = array();
 
@@ -56,6 +94,9 @@
     return $data;
   }
 
+  /**
+   * Record tree of assertion information.
+   */
   public function record($result_id, array $data) {
     foreach ($data as $detail) {
       $detail['result_id'] = $result_id;
@@ -68,6 +109,9 @@
     }
   }
 
+  /**
+   * Delete tree of assertion information.
+   */
   public function delete($result_id, array $data) {
     foreach ($data as $detail) {
       db_query('DELETE FROM {pifr_result_detail_assertion}
@@ -76,162 +120,4 @@
     db_query('DELETE FROM {pifr_result_detail}
               WHERE result_id = %d', $result_id);
   }
-
-  /**
-   * Format the result detail numbers.
-   *
-   * Performs a number format on each of the three assertion summary counts:
-   * pass, fail, exception (with @ in front of each).
-   *
-   * @param array $details Result detail array.
-   * @return array Formated result details.
-   */
-  public function summary_format(array $details) {
-    foreach (array('@pass', '@fail', '@exception') as $key) {
-      if (!empty($details[$key])) {
-        $details[$key] = number_format($details[$key]);
-      }
-    }
-    return $details;
-  }
-
-  /**
-   * Generate detailed display of test results.
-   *
-   * The display will provide test summaries and assertions as applicable.
-   *
-   * @param array $test Test information.
-   * @param array $result Test result information.
-   * @return string HTML output.
-   */
-  protected function detail_table(array $result) {
-    // Map of status images.
-    $map = array(
-      'pass' => theme('image', 'misc/watchdog-ok.png'),
-      'fail' => theme('image', 'misc/watchdog-error.png'),
-      'exception' => theme('image', 'misc/watchdog-warning.png'),
-      'debug' => theme('image', 'misc/watchdog-warning.png'),
-    );
-
-    // Map of arrow images.
-    $js = array(
-      theme('image', 'misc/menu-collapsed.png', 'Expand', 'Expand', array('class' => 'pifr-assertion')),
-      theme('image', 'misc/menu-expanded.png', 'Collapsed', 'Collapsed', array('class' => 'pifr-assertion')),
-      theme('image', 'misc/menu-leaf.png', 'Leaf', 'Leaf', array('class' => 'pifr-assertion')),
-    );
-    drupal_add_js(array('pifr_assertion' => $js), 'setting');
-
-    // Build list of test summaries.
-    $header = array(t('Test name'), t('Pass'), t('Fail'), t('Exception'));
-    $rows = array();
-    $rows_fail = array();
-    foreach ($result['data'] as $detail) {
-      // Display the summary of the test.
-      $row = array();
-
-      $row[] = ($detail['assertions'] ? $js[0] : $js[2]) . $detail['test_name'];
-      $row[] = $detail['pass'];
-      $row[] = $detail['fail'];
-      $row[] = $detail['exception'];
-
-      // Determine proper class for coloring.
-      $class = 'pass';
-      if ($detail['fail']) {
-        $class = 'fail';
-      }
-      elseif ($detail['exception']) {
-        $class = 'exception';
-      }
-      $rows[] = array(
-        'class' => 'pifr-' . $class,
-        'data' => $row,
-      );
-      if ($class != 'pass') {
-        $rows_fail[] = $rows[count($rows) - 1];
-      }
-
-      // If individual assertions where included, display them below the
-      // summary row with the ability to expand/collapse via javascript.
-      if ($detail['assertions']) {
-        $header_detail = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), t('Status'));
-        $rows_detail = array();
-        foreach ($detail['assertions'] as $assertion) {
-          $row = array();
-          $row[] = $assertion['message'];
-          $row[] = $assertion['message_group'];
-          $row[] = $assertion['file'];
-          $row[] = $assertion['line'];
-          $row[] = $assertion['function'];
-          $row[] = $map[$assertion['status']];
-
-          $rows_detail[] = array(
-            'class' => 'pifr-' . $assertion['status'],
-            'data' => $row
-          );
-        }
-
-        // Add header for assertion messages and include assertion rows.
-        $row = array();
-        $row[] = array(
-          'colspan' => 4,
-          'data' => theme('table', $header_detail, $rows_detail),
-        );
-        $rows[] = array(
-          'class' => 'pifr-assertion-wrapper',
-          'data' => $row,
-        );
-        if ($class != 'pass') {
-          $rows_fail[] = $rows[count($rows) - 1];
-        }
-      }
-    }
-
-    // Generate summary of assertion result.
-    $summary = '';
-    if (isset($result['details']['@fail'])) {
-      $pass = $result['details']['@fail'] + $result['details']['@exception'] == 0;
-      $details = $this->summary_format($result['details']);
-      $summary = t('@pass pass(es), @fail fail(s), and @exception exception(s)', $details);
-      $summary = '<div class="pifr-assertion-summary pifr-' . ($pass ? 'ok' : 'error') . '">' . $summary . '</div>';
-    }
-
-    // Display failed rows above a table containing all rows.
-    if ($rows_fail) {
-      return $summary .
-        '<h3>' . t('Non-pass') . '</h3>' . theme('table', $header, $rows_fail) .
-        '<h3>' . t('All') . '</h3>' . theme('table', $header, $rows);
-    }
-
-    // Merge all components.
-    return $rows ? $summary . theme('table', $header, $rows) : '';
-  }
-
-  /**
-   * Generate detailed summary display of test results.
-   *
-   * The display will provide a list of non-pass tests.
-   *
-   * @param array $test Test information.
-   * @param array $result Test result information.
-   * @return string HTML output.
-   */
-  public function detail_summary(array $test, array $result) {
-    $failed_tests = array();
-    foreach ($result['data'] as $detail) {
-      if ($detail['fail'] + $detail['exception'] > 0) {
-        $details = array(
-          '@pass' => $detail['pass'],
-          '@fail' => $detail['fail'],
-          '@exception' => $detail['exception'],
-        );
-        $details = t('@pass pass(es), @fail fail(s), and @exception exception(s)', $details);
-        $failed_tests[] = $detail['test_name'] . ' (' . $details . ')';
-      }
-    }
-
-    if (!$failed_tests) {
-      $failed_tests[] = 'No relevant data';
-    }
-    return theme('item_list', $failed_tests);
-  }
 }
Index: review/coder/pifr_coder.server.inc
===================================================================
RCS file: review/coder/pifr_coder.server.inc
diff -N review/coder/pifr_coder.server.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/coder/pifr_coder.server.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,35 @@
+<?php
+// $Id: pifr_simpletest.server.inc,v 1.7 2009/12/04 20:29:08 boombatower Exp $
+
+/**
+ * @file
+ * Provide Coder server review implementation.
+ *
+ * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ */
+
+module_load_include('server.inc', 'pifr_drupal');
+
+/**
+ * Coder server review implementation.
+ */
+class pifr_server_review_pifr_coder extends pifr_server_review_pifr_drupal {
+
+  /**
+   * Modify step information.
+   */
+  public function __construct() {
+    $this->steps['review'] = array(
+      'confirmation' => FALSE,
+    );
+    $this->steps['fail'] = array(
+      'title' => 'detect coder flags',
+      'description' => 'Ensure that your code follows the Drupal standard and passes a coder & tough love review.',
+      'confirmation' => 'pifr_coder',
+    );
+    $this->steps['pass'] = array(
+      'title' => 'coder review passed',
+      'confirmation' => 'no-patch',
+    );
+  }
+}
Index: review/drupal/confirmation/install.patch
===================================================================
RCS file: review/drupal/confirmation/install.patch
diff -N review/drupal/confirmation/install.patch
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/drupal/confirmation/install.patch	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,16 @@
+Index: install.php
+===================================================================
+RCS file: /cvs/drupal/drupal/install.php,v
+retrieving revision 1.152
+diff -u -r1.152 install.php
+--- install.php	4 Feb 2009 04:42:26 -0000	1.152
++++ install.php	5 Feb 2009 07:42:26 -0000
+@@ -5,7 +5,7 @@
+  * Root directory of Drupal installation.
+  */
+ define('DRUPAL_ROOT', getcwd());
+-
++break_me();
+ require_once DRUPAL_ROOT . '/includes/install.inc';
+ 
+ /**
Index: review/coder/pifr_coder.client.inc
===================================================================
RCS file: review/coder/pifr_coder.client.inc
diff -N review/coder/pifr_coder.client.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/coder/pifr_coder.client.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,123 @@
+<?php
+// $Id: pifr_simpletest.client.inc,v 1.12 2009/12/04 22:28:54 boombatower Exp $
+
+/**
+ * @file
+ * Provide Coder client review implementation.
+ *
+ * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ */
+
+module_load_include('client.inc', 'pifr_drupal');
+
+/**
+ * SimpleTest PIFR review implementation.
+ */
+class pifr_client_review_pifr_coder extends pifr_client_review_pifr_drupal {
+
+  protected $results = array();
+
+  /**
+   * Remove install operation and add review operation.
+   */
+  public function __construct(array $test) {
+    parent::__construct($test);
+
+    // Remove install operation.
+    unset($this->operations[count($this->operations) - 1]);
+    $this->operations[] = 'review';
+  }
+
+  /**
+   * Perform coder review.
+   */
+  protected function review() {
+    // Get the review information for the selected reviews.
+    $selected_reviews = array('comment', 'sql', 'i18n', 'style', 'security', 'coder_tough_love');
+    $reviews = _coder_reviews();
+    foreach ($reviews as $key => $review) {
+      if (!in_array($key, $selected_reviews)) {
+        unset($reviews[$key]);
+      }
+    }
+
+    // Prepare the base arguments array.
+    $php_extensions = array('php', 'inc', 'install', 'module', 'test');
+    $args = array(
+      '#reviews' => $reviews,
+      '#severity' => SEVERITY_MINOR,
+      '#filename' => '',
+      '#patch' => '',
+      '#php_extensions' => $php_extensions,
+      '#include_extensions' => _coder_get_reviews_extensions($php_extensions, $reviews),
+    );
+
+    // Cycle through all relevant files and perform review.
+    $files = parent::syntax_files();
+    foreach ($files as $file) {
+      // Ensure that the file exists and is not a reference to a file that was
+      // removed in a patch, or the like.
+      if (!file_exists($file)) {
+        $not_found++;
+        continue;
+      }
+
+      $args['#filename'] = $file;
+      $this->results[$file] = do_coder_reviews($args);
+    }
+  }
+
+  /**
+   * Add the Coder results.
+   */
+  public function get_result() {
+    $result = parent::get_result();
+
+    // If result code is one greater then number of operations then all the
+    // operations passed, so the test results need to be added.
+    $fail_code = count($this->operations) + 1;
+    if ($result['code'] == $fail_code) {
+      $result['details'] = array(
+        '@pass' => 0,
+        '@fail' => 0,
+        '@exception' => 0,
+      );
+      $result['data'] = array();
+
+      // Cycle through results for each file.
+      foreach ($results as $file => $result) {
+        $result['data'][$file] = array(
+          'test_name' => $file,
+          'pass' => 0,
+          'fail' => 0,
+          'exception' => 0,
+          'assertions' => array(),
+        );
+
+        foreach ($result as $assertion) {
+          // Fill in details with summary of assertion counts.
+          $result['details']['@' . $assertion['status']]++;
+
+          // Add up summary totals for test class.
+          $result['data'][$file][$assertion['status']]++;
+
+          // Store non-pass assertion until reaching maximum number.
+          if ($non_pass < PIFR_CLIENT_MAX_ASSERTIONS) {
+            // Make file only include name, instead of absolute reference.
+            $assertion['file'] = basename($file);
+
+            // Add assertion to set of non-pass assertions for the class.
+            $result['data'][$file]['assertions'][] = $assertion;
+          }
+        }
+      }
+
+      // Remove file name keys.
+      $result['data'] = array_values($result['data']);
+
+      // Assign the code bassed on the test results.
+      $result['code'] = ($result['details']['@fail'] + $result['details']['@exception'] ? $fail_code : $fail_code + 1);
+    }
+    return $result;
+  }
+}
Index: review/coder/pifr_coder.info
===================================================================
RCS file: review/coder/pifr_coder.info
diff -N review/coder/pifr_coder.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/coder/pifr_coder.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,12 @@
+; $Id$
+name = PIFR Coder
+description = Provide PIFR review implementation for Coder.
+package = Project Issue File Review Plugins
+core = 6.x
+php = 5
+files[] = pifr_coder.client.inc
+files[] = pifr_coder.module
+files[] = pifr_coder.server.inc
+; dependencies[] = coder
+; dependencies[] = coder_tough_love
+dependencies[] = pifr_assertion
Index: review/simpletest/confirmation/review.patch
===================================================================
RCS file: review/simpletest/confirmation/review.patch
diff -N review/simpletest/confirmation/review.patch
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/simpletest/confirmation/review.patch	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,15 @@
+Index: scripts/run-tests.sh
+===================================================================
+RCS file: /cvs/drupal/drupal/scripts/run-tests.sh,v
+retrieving revision 1.28
+diff -u -r1.28 run-tests.sh
+--- scripts/run-tests.sh  10 Jun 2009 16:17:02 -0000  1.28
++++ scripts/run-tests.sh  25 Jun 2009 00:35:00 -0000
+@@ -5,6 +5,7 @@
+  * This script runs Drupal tests from command line.
+  */
+ 
++break_me();
+ define('SIMPLETEST_SCRIPT_COLOR_PASS', 32); // Green.
+ define('SIMPLETEST_SCRIPT_COLOR_FAIL', 31); // Red.
+ define('SIMPLETEST_SCRIPT_COLOR_EXCEPTION', 33); // Brown.
Index: review/drupal/confirmation/syntax.patch
===================================================================
RCS file: review/drupal/confirmation/syntax.patch
diff -N review/drupal/confirmation/syntax.patch
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/drupal/confirmation/syntax.patch	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,16 @@
+Index: install.php
+===================================================================
+RCS file: /cvs/drupal/drupal/install.php,v
+retrieving revision 1.152
+diff -u -r1.152 install.php
+--- install.php	4 Feb 2009 04:42:26 -0000	1.152
++++ install.php	5 Feb 2009 07:44:00 -0000
+@@ -5,7 +5,7 @@
+  * Root directory of Drupal installation.
+  */
+ define('DRUPAL_ROOT', getcwd());
+-
++syntax error
+ require_once DRUPAL_ROOT . '/includes/install.inc';
+ 
+ /**
Index: review/drupal/pifr_drupal.server.inc
===================================================================
RCS file: review/drupal/pifr_drupal.server.inc
diff -N review/drupal/pifr_drupal.server.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/drupal/pifr_drupal.server.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,49 @@
+<?php
+// $Id: pifr_assertion.server.inc,v 1.17 2009/12/08 19:33:59 boombatower Exp $
+
+/**
+ * @file
+ * Provide Drupal server review implementation.
+ *
+ * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ */
+
+module_load_include('server.inc', 'pifr_assertion');
+
+/**
+ * Drupal server review implementation.
+ */
+abstract class pifr_server_review_pifr_drupal extends pifr_server_review_pifr_assertion {
+
+  protected $argument_default = array(
+    'core' => 7,
+    'database' => 'mysql-5.0-isam',
+    'modules' => array(),
+  );
+
+  /**
+   * Modify step information.
+   */
+  public function __construct() {
+    $this->steps['syntax'] = array(
+      'title' => 'detect invalid PHP syntax',
+      'description' => 'Check the syntax of your PHP files.',
+      'summary' => 'Invalid PHP syntax in @filename.',
+      'confirmation' => 'pifr_drupal',
+    );
+    $this->steps['install'] = array(
+      'title' => 'detect a Drupal installation failure',
+      'description' => 'Ensure that you can perform a fresh Drupal install.',
+      'summary' => 'Drupal installation failed on @environment.',
+      'confirmation' => 'pifr_drupal',
+    );
+
+    $this->confirmation_default['vcs']['main'] = array(
+      'repository' => array(
+        'type' => 'cvs',
+        'url' => ':pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal/drupal',
+      ),
+      'vcs_identifier' => 'HEAD',
+    );
+  }
+}
Index: server/confirmation/apply.patch
===================================================================
RCS file: server/confirmation/apply.patch
diff -N server/confirmation/apply.patch
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ server/confirmation/apply.patch	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,1 @@
+fail to apply
Index: server/confirmation/check.patch
===================================================================
RCS file: server/confirmation/check.patch
diff -N server/confirmation/check.patch
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ server/confirmation/check.patch	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,3 @@
+non-unix
+line
+endings
Index: review/coder/pifr_coder.module
===================================================================
RCS file: review/coder/pifr_coder.module
diff -N review/coder/pifr_coder.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ review/coder/pifr_coder.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,62 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Provide PIFR review implementation for Coder.
+ *
+ * @author Jimmy Berry ("boombatower", http://drupal.org/user/214218)
+ */
+
+/**
+ * Implementation of hook_pifr_info().
+ */
+function pifr_coder_pifr_info() {
+  return array(
+    'pifr_coder' => t('Coder'),
+  );
+}
+
+/**
+ * Implementation of hook_theme_registry_alter().
+ */
+function pifr_coder_theme_registry_alter(&$theme_registry) {
+  $theme_registry['coder_warning'] = array(
+    'file' => 'pifr_coder.module',
+    'path' => drupal_get_path('module', 'pifr_coder'),
+    'function' => 'theme_pifr_coder_warning',
+  );
+}
+
+/**
+ * Create assertion information array.
+ *
+ * @see theme_coder_warning()
+ */
+function theme_pifr_coder_warning($warning, $severity_name, $lineno = 0, $line = '') {
+  static $map = array(
+    'critical' => 'pass',
+    'normal' => 'fail',
+    'minor' => 'exception',
+  );
+
+  $assertion = array(
+    'status' => $map[$severity_name],
+    'message' => $warning,
+    'message_group' => 'Other',
+    'function' => 'N/A',
+    'line' => $lineno,
+  );
+
+  if (is_array($warning)) {
+    $assertion['message'] = $warning['#warning'];
+    if (isset($warning['#link'])) {
+      $assertion['message'] .= ' (' . l(t('Drupal Docs'), $warning['#link']) . ')';
+    }
+  }
+  if ($line) {
+    $warning .= ' <pre>' . check_plain($line) . '</pre>';
+  }
+
+  return $assertion;
+}
