Index: scripts/coder_format/coder_format.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/coder/scripts/coder_format/coder_format.inc,v
retrieving revision 1.2.4.5
diff -u -p -r1.2.4.5 coder_format.inc
--- scripts/coder_format/coder_format.inc	16 Jan 2008 22:26:33 -0000	1.2.4.5
+++ scripts/coder_format/coder_format.inc	19 Jan 2008 15:34:21 -0000
@@ -43,7 +43,7 @@ function coder_format_recursive($root, $
   
   if (!$undo) {
     // Fetch files to process.
-    $mask = '.module$|.inc$|.install|.profile$';
+    $mask = '\.php$|\.module$|\.inc$|\.install|\.profile$';
     $nomask = array('.', '..', 'CVS', '.svn');
     $files = file_scan_directory($root, $mask, $nomask, 0, true);
     foreach ($files as $file) {
@@ -88,17 +88,7 @@ function coder_format_file($sourcefile =
   fclose($fd);
   
   if ($code !== false) {
-    // Preprocess source code.
-    $code = coder_exec_processors($code, 'coder_preprocessor');
-    
-    // Process source code.
-    $code = coder_format_string($code);
-    
-    // Postprocess source code.
-    $code = coder_exec_processors($code, 'coder_postprocessor');
-    
-    // Fix beginning and end of code.
-    $code = coder_trim_php($code);
+    $code = coder_format_string_all($code);
     
     if ($code !== false) {
       // Write formatted source code to target file.
@@ -113,6 +103,29 @@ function coder_format_file($sourcefile =
 }
 
 /**
+ * Formats source code according to Drupal conventions, also using
+ * post and pre-processors.
+ * 
+ * @param
+ *   $code Code to process.
+ */
+function coder_format_string_all($code) {
+  // Preprocess source code.
+  $code = coder_exec_processors($code, 'coder_preprocessor');
+  
+  // Process source code.
+  $code = coder_format_string($code);
+  
+  // Postprocess source code.
+  $code = coder_exec_processors($code, 'coder_postprocessor');
+  
+  // Fix beginning and end of code.
+  $code = coder_trim_php($code);
+  
+  return $code;
+}
+
+/**
  * Format the source code according to Drupal coding style guidelines.
  *
  * This function uses PHP's tokenizer functions.
@@ -166,6 +179,13 @@ function coder_format_file($sourcefile =
  *   $inline_if bool
  *      Controls formatting of ? and : for inline ifs until a ; (semicolon) is
  *      processed.
+ *   $in_function_declaration
+ *      Prevents whitespace after & for function declarations, e.g.
+ *      function &foo(). Is true after function token but before first
+ *      parenthesis.
+ *   $in_parenthesis
+ *      Array of parenthesis level to whether or not the structure
+ *      is multiline.
  *
  * @param $code
  *      The source code to format.
@@ -187,11 +207,15 @@ function coder_format_string($code = '')
   $in_do_while    = false;
   
   // Whitespace controls:
-  $in_object   = false;
-  $in_at       = false;
-  $in_php      = false;
-  $in_quote    = false;
-  $inline_if   = false;
+  $in_object        = false;
+  $in_at            = false;
+  $in_php           = false;
+  $in_quote         = false;
+  $inline_if        = false;
+  $in_multiline     = array();
+  
+  // Whether or not a function token was encountered:
+  $in_function_declaration = false;
   
   $result      = '';
   $lasttoken   = array(0);
@@ -220,7 +244,8 @@ function coder_format_string($code = '')
               ++$braces_in_case;
             }
             ++$_coder_indent;
-            $result = rtrim($result) .' '. $text . coder_br();
+            $result = rtrim($result) .' '. $text;
+            coder_br($result);
           }
           else {
             $in_brace = true;
@@ -243,9 +268,10 @@ function coder_format_string($code = '')
             $result = rtrim($result);
             if (substr($result, -1) != '{') {
               // Avoid line break in empty curly braces.
-              $result .= coder_br();
+              coder_br($result);
             }
-            $result .= $text . coder_br();
+            $result .= $text;
+            coder_br($result);
           }
           else {
             $in_brace = false;
@@ -256,7 +282,7 @@ function coder_format_string($code = '')
         case ';':
           $result = rtrim($result) . $text;
           if (!$parenthesis && !$in_heredoc) {
-            $result .= coder_br();
+            coder_br($result);
           }
           else {
             $result .= ' ';
@@ -279,24 +305,33 @@ function coder_format_string($code = '')
             if ($in_case) {
               ++$_coder_indent;
             }
-            $result = rtrim($result) . $text . coder_br();
+            $result = rtrim($result) . $text;
+            coder_br($result);
           }
           break;
         
         case '(':
           $result .= $text;
           ++$parenthesis;
+          // Not multiline until proven so by whitespace.
+          $in_multiline[$parenthesis] = false;
+          // Terminate function declaration, as a parenthesis indicates
+          // the beginning of the arguments. This will catch all other
+          // instances of parentheses, but in this case it's not a problem.
+          $in_function_declaration = false;
           break;
         
         case ')':
-          if (!$in_quote && !$in_heredoc && substr(rtrim($result), -1) == ',') {
-            // Fix indent of right parenthesis in multiline arrays by
+          if (!$in_quote && !$in_heredoc && (substr(rtrim($result), -1) == ',' || $in_multiline[$parenthesis])) {
+            // Fix indent of right parenthesis in multiline structures by
             // increasing indent for each parenthesis and decreasing one level.
             $_coder_indent = $_coder_indent + $parenthesis - 1;
-            $result = rtrim($result) . coder_br() . $text;
-            $_coder_indent = $_coder_indent - $parenthesis + 1;
-          }
-          else {
+            $result = rtrim($result);
+            coder_br($result);
+            $result .= $text;
+            // Undo temporary change.
+            $_coder_indent = $_coder_indent - ($parenthesis - 1);
+          } else {
             $result .= $text;
           }
           if ($parenthesis) {
@@ -340,14 +375,24 @@ function coder_format_string($code = '')
             $result .= $text;
           }
           else {
-            $result = rtrim($result) .' '. $text .' ';
+            $result = rtrim($result) .' '. $text;
+            // Ampersands used to declare reference return value for
+            // functions should not have trailing space.
+            if (!$in_function_declaration) {
+              $result .= ' ';
+            }
           }
           break;
 
         case '-':
           $result = rtrim($result);
           // Do not add a space before negative numbers or variables.
-          if (substr($result, -1) == '>' || substr($result, -1) == '=' || substr($result, -1) == ',' || substr($result, -1) == ':') {
+          $c = substr($result, -1);
+          // If hyphen trails after open parentheses, it should be adjacent.
+          if ($c == '(') {
+            $result .= ltrim($text);
+          }
+          elseif ($c == '>' || $c == '=' || $c == ',' || $c == ':' || $c == '?') {
             $result .= ' '. $text;
           }
           else {
@@ -397,11 +442,11 @@ function coder_format_string($code = '')
           $in_php = true;
           // Add a line break between two PHP tags.
           if (substr(rtrim($result), -2) == '?>') {
-            $result .= coder_br();
+            coder_br($result);
           }
           $result .= trim($text);
           if ($first_php_tag) {
-            $result .= coder_br();
+            coder_br($result);
             $first_php_tag = false;
           }
           else {
@@ -454,18 +499,26 @@ function coder_format_string($code = '')
           // Avoid duplicate line feeds outside arrays.
           $c = $parenthesis ? 0 : 1;
           
-          for ($c, $cc = substr_count($text, chr(10)); $c < $cc; ++$c) {
+          for ($c, $cc = substr_count($text, "\n"); $c < $cc; ++$c) {
             if ($parenthesis) {
               // Add extra indent for each parenthesis in multiline definitions (f.e. arrays).
               $_coder_indent = $_coder_indent + $parenthesis;
-              $result = rtrim($result) . coder_br();
+              $result = rtrim($result);
+              coder_br($result);
               $_coder_indent = $_coder_indent - $parenthesis;
             }
             else {
               // Discard any whitespace, just insert a line break.
-              $result .= coder_br();
+              coder_br($result);
             }
           }
+          
+          // If there were newlines present inside a parenthesis,
+          // turn on multiline mode.
+          if ($cc && $parenthesis) {
+            $in_multiline[$parenthesis] = true;
+          }
+          
           break;
         
         case T_IF:
@@ -504,7 +557,9 @@ function coder_format_string($code = '')
         case T_ELSE:
         case T_ELSEIF:
           // Write else and else if to a new line.
-          $result = rtrim($result) . coder_br() . trim($text) .' ';
+          $result = rtrim($result);
+          coder_br($result);
+          $result .= trim($text) .' ';
           break;
         
         case T_CASE:
@@ -515,19 +570,22 @@ function coder_format_string($code = '')
             $in_case = true;
             // Add a line break between cases.
             if (substr($result, -1) != '{') {
-              $result .= coder_br();
+              coder_br($result);
             }
           }
           else {
             // Decrease current indent to align multiple cases.
             --$_coder_indent;
           }
-          $result .= coder_br() . trim($text) .' ';
+          coder_br($result);
+          $result .= trim($text) .' ';
           break;
         
         case T_BREAK:
           // Write break to a new line.
-          $result = rtrim($result) . coder_br() . trim($text);
+          $result = rtrim($result);
+          coder_br($result);
+          $result .= trim($text);
           if ($in_case && !$braces_in_case) {
             --$_coder_indent;
             $in_case = false;
@@ -546,13 +604,16 @@ function coder_format_string($code = '')
           break;
         
         case T_FUNCTION:
+          $in_function_declaration = true;
+          // Fall through.
         case T_CLASS:
           // Write function and class to new lines.
           $result = rtrim($result);
           if (substr($result, -1) == '}') {
-            $result .= coder_br();
+            coder_br($result);
           }
-          $result .= coder_br() . trim($text) .' ';
+          coder_br($result);
+          $result .= trim($text) .' ';
           break;
         
         case T_EXTENDS:
@@ -600,7 +661,9 @@ function coder_format_string($code = '')
         case T_DOC_COMMENT:
           if (substr($text, 0, 3) == '/**') {
             // Prepend a new line.
-            $result = rtrim($result) . coder_br() . coder_br();
+            $result = rtrim($result);
+            coder_br($result);
+            coder_br($result);
             
             // Remove carriage returns.
             $text = str_replace("\r", '', $text);
@@ -612,7 +675,8 @@ function coder_format_string($code = '')
               
               // Add a new line between function description and first parameter description.
               if (!$params_fixed && substr($lines[$l], 0, 8) == '* @param' && $lines[$l - 1] != '*') {
-                $result .= ' *'. coder_br();
+                $result .= ' *';
+                coder_br($result);
                 $params_fixed = true;
               }
               else if (!$params_fixed && substr($lines[$l], 0, 8) == '* @param') {
@@ -622,7 +686,8 @@ function coder_format_string($code = '')
               
               // Add a new line between function params and return.
               if (substr($lines[$l], 0, 9) == '* @return' && $lines[$l - 1] != '*') {
-                $result .= ' *'. coder_br();
+                $result .= ' *';
+                coder_br($result);
               }
               
               // Add one space indent to get ' *[...]'.
@@ -631,7 +696,7 @@ function coder_format_string($code = '')
               }
               $result .= $lines[$l];
               if ($l < count($lines)) {
-                $result .= coder_br();
+                coder_br($result);
               }
             }
           }
@@ -640,12 +705,13 @@ function coder_format_string($code = '')
             if ($parenthesis) {
               // Add extra indent for each parenthesis in multiline definitions (f.e. arrays).
               $_coder_indent = $_coder_indent + $parenthesis;
-              $result = rtrim($result) . coder_br();
+              $result = rtrim($result);
+              coder_br($result);
               $_coder_indent = $_coder_indent - $parenthesis;
             }
             else {
               // Discard any whitespace, just insert a line break.
-              $result .= coder_br();
+              coder_br($result);
             }
           }
           break;
@@ -655,12 +721,14 @@ function coder_format_string($code = '')
           break;
         
         case T_START_HEREDOC:
-          $result .= trim($text) . coder_br(false);
+          $result .= trim($text);
+          coder_br($result, false);
           $in_heredoc = true;
           break;
         
         case T_END_HEREDOC:
-          $result .= trim($text) . coder_br(false);
+          $result .= trim($text);
+          coder_br($result, false);
           $in_heredoc = false;
           break;
         
@@ -678,20 +746,36 @@ function coder_format_string($code = '')
 
 /**
  * Generate a line feed including current line indent.
- *
+ * 
+ * This function will also remove all line indentation from the
+ * previous line if no text was added.
+ * 
+ * @param $result
+ *   Result variable to append break and indent to.
  * @param $add_indent
  *   Whether to add current line indent after line feed.
- * @return
- *   The resulting string.
  */
-function coder_br($add_indent = true) {
+function coder_br(&$result, $add_indent = true) {
   global $_coder_indent;
   
+  // Scan result backwards for whitespace.
+  for ($i = strlen($result) - 1; $i >= 0; $i--) {
+    if ($result[$i] == ' ') {
+      continue;
+    }
+    if ($result[$i] == "\n") {
+      $result = rtrim($result, ' ');
+      break;
+    }
+    // Non-whitespace was encountered, no changes necessary.
+    break;
+  }
+  
   $output = "\n";
   if ($add_indent && $_coder_indent >= 0) {
     $output .= str_repeat('  ', $_coder_indent);
   }
-  return $output;
+  $result .= $output;
 }
 
 /**
@@ -852,23 +936,34 @@ function coder_preprocessor_ml_array_add
     // (\n|(?X>!\);).+?,?\n) matches a line break or the first array item.
     // (.*?[^,;]) matches the rest array items.
     // ,?(\n\s*)\); matches the end of multiline array, optionally including a comma.
-    '#search' => '/(^[\040\t]*(?!\*|\/\/)[^\*\/\n]*?\sarray\()(\n|(?>!\);).+?,?\n)(.*?[^,;]),?(\n\s*\);)/ism',
-    '#replace' => '$1$2$3,$4',
+    //             1                                          2                   3           4
+    '#search' => '/(^[\040\t]*(?!\*|\/\/)[^\*\/\n]*?\sarray\()(\n|(?>!\);).+?,?\n)(.*?[^,;]),?(\n\s*\)[;])/ism',
+    '#replace_callback' => 'coder_ml_array_add_comma',
     //'#debug' => true,
   );
 }
 
+function coder_ml_array_add_comma($matches) {
+  $contents = $matches[2] . $matches[3];
+  // Add commas to all lines that are missing them, this lets us
+  // handle nested arrays too:
+  $contents = preg_replace("/(=>.*?),?(\n?\s*\))/", '$1,$2', $contents);
+  return $matches[1] . $contents .','. $matches[4];
+}
+
 function coder_preprocessor_inline_comment() {
   return array(
     '#title' => 'Move inline comments above remarked line.',
     '#weight' => 2,
     // [\040\t] matches only a space or tab.
     // (?!case) prevents matching of case statements.
+    // (?!*) prevents matching of docblock statements.
+    // (?!//) prevents matching of comment statements
     // \S prevents matching of lines containing only a comment.
     // [^:] prevents matching of URL protocols.
-    // [^;\$] prevents matching of CVS keyword Id comment and double slashes.
-    //   in quotes (f.e. "W3C//DTD").
-    '#search' => '@^([\040\t]*)(?!case)(\S.+?)[\040\t]*[^:]//\s*([^;\$]+?)$@m',
+    // [^;,\$] prevents matching of CVS keyword Id comment and double slashes
+    //   in quotes (f.e. "W3C//DTD") or in multiline arrays (f.e. "//",)
+    '#search' => '@^([\040\t]*)(?!case)(?!\*)(?!//)(\S.+?)[\040\t]*[^:]//\s*([^;,\$]+?)$@m',
     '#replace' => "$1// $3\n$1$2",
   );
 }
@@ -948,6 +1043,64 @@ function coder_replace_multiple_vars($ma
   return $return;
 }
 
+function coder_postprocessor_indent_multiline_array() {
+  // Still buggy, disabled for now.
+  return array(
+    '#title' => 'Align equal signs of multiline array assignments in the same column.',
+    // ?: prevents capturing
+    // \s* initial whitespace
+    // ([\'"]).+?\1 matches a string key
+    // .+? matches any other key w/o whitespace
+    // \s*=>\s* matches associative array arrow syntax
+    // .+? matches value
+    '#search' => '/^(?:\s*(?:(?:([\'"]).+?\1|.+?)\s*=>\s*.+?|\),\s?)$){3,}/mi',
+    //'#replace_callback' => 'coder_replace_indent_multiline_array',
+  );
+}
+
+function coder_replace_indent_multiline_array($matches) {
+  // Separate out important components of the multiline array:
+  // (\s*) matches existing indent as \1
+  // (([\'"]).+?\2|\$.+?|[+\-]?(?:0x)?[0-9A-F]+) matches key as \2
+  //    ([\'"]).+?\3 matches a quoted key, quote used is \3
+  //    \.+? matches anything else
+  // \),\s*? matches a closing parenthesis in a nested array
+  // \s*=>\s* matches existing indentation and arrow to be discarded
+  // (.+?) matches value as \4
+  // {3,} requires three or more of these lines
+  // mi enables multiline and caseless mode
+  preg_match_all('/^(\s*)(?:(([\'"]).+?\3|\.+?)\s*=>\s*(.+?),?|\),)\s*?$/mi', $matches[0], $vars, PREG_SET_ORDER);
+  // Determine max key length for varying indentations:
+  $maxlengths = array();
+  foreach ($vars as $var) {
+    list(, $indent, $key) = $var;
+    if (!isset($maxlengths[$indent])) {
+      $maxlengths[$indent] = 0;
+    }
+    if (($t = strlen($key)) > $maxlengths[$indent]) {
+      $maxlengths[$indent] = $t;
+    }
+  }
+  // Reconstruct variable array declaration:
+  $return = '';
+  foreach ($vars as $var) {
+    list(, $indent, $key,, $value) = $var;
+    if ($key === null) {
+      $return .= "$indent),\n";
+      continue;
+    }
+    $spaces = str_repeat(' ', $maxlengths[$indent] - strlen($key));
+    if ($value !== 'array(') {
+      $comma = ',';
+    } else {
+      $comma = '';
+    }
+    $return .= "$indent$key$spaces => $value$comma\n";
+  }
+  $return = rtrim($return, "\n");
+  return $return;
+}
+
 function coder_postprocessor_array_rearrange() {
   // @bug common.inc, comment.module:
   // Not yet working properly 25/03/2007 sun.
Index: tests/coder_format/CoderTestCase.php
===================================================================
RCS file: tests/coder_format/CoderTestCase.php
diff -N tests/coder_format/CoderTestCase.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/CoderTestCase.php	19 Jan 2008 15:24:21 -0000
@@ -0,0 +1,12 @@
+<?php
+// $Id$
+
+require_once drupal_get_path('module', 'coder') .'/scripts/coder_format/coder_format.inc';
+
+class CoderTestCase extends DrupalTestCase {
+  function assertFormat($input, $expect) {
+    $result = coder_format_string_all($input);
+    $this->assertIdentical($result, $input);
+  }
+}
+
Index: tests/coder_format/CoderTestFile.php
===================================================================
RCS file: tests/coder_format/CoderTestFile.php
diff -N tests/coder_format/CoderTestFile.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/CoderTestFile.php	19 Jan 2008 15:28:28 -0000
@@ -0,0 +1,158 @@
+<?php
+// $Id$
+
+
+/**
+ * Represents coder test file for full coder_format_string_all() tests.
+ */
+class CoderTestFile extends SimpleExpectation {
+
+  /* Filename of test */
+  var$filename;
+
+  /* Test name */
+  var$test;
+
+  /* PHP to be parsed */
+  var$input = '';
+
+  /* Expected output */
+  var$expect = '';
+
+  /* Actual result */
+  var$actual;
+
+  /* Whether or not <?php and other stuff should be added */
+  var$full;
+
+  /**
+   * Loads this class from a file.
+   *
+   * @param $filename String filename to load
+   */
+  function load($filename) {
+    $this->filename = $filename;
+    $fh             = fopen($filename, 'r');
+    $state          = '';
+    while (($line = fgets($fh)) !== false) {
+      // Normalize newlines.
+      $line = rtrim($line, "\n\r");
+      if (substr($line, 0, 2) == '--') {
+        // Detected section.
+        $state = trim($line, ' -');
+        continue;
+      }
+      if (!$state) {
+        list($key, $line) = explode(': ', $line, 2);
+      }
+      else {
+        $key = $state;
+      }
+      switch ($key) {
+        case 'INPUT':
+          $this->input .= $line ."\n";
+          break;
+
+        case 'EXPECT':
+          $this->expect .= $line ."\n";
+          break;
+
+        case 'TEST':
+          $this->test = $line;
+          break;
+
+        case 'FULL':
+          $this->full = (bool) $line;
+          break;
+      }
+    }
+    fclose($fh);
+    if ($this->expect === '') $this->expect = $this->input;
+    if (!$this->full) {
+      $prepend      = "<?php\n//$". "Id$\n\n";
+      $this->input  = $prepend . trim($this->input, "\n") ."\n\n";
+      $this->expect = $prepend . trim($this->expect, "\n") ."\n\n";
+    }
+  }
+
+  /**
+   * Implements SimpleExpectation::test().
+   *
+   * @param $filename Filename of test file to test.
+   */
+  function test($filename = false) {
+    if ($filename) $this->load($filename);
+    $this->actual = coder_format_string_all($this->input);
+    return $this->expect === $this->actual;
+  }
+
+  /**
+   * Implements SimpleExpectation::testMessage().
+   */
+  function testMessage() {
+    $message = $this->test .' test at '. htmlspecialchars($this->filename);
+    return $message;
+  }
+
+  /**
+   * Renders the test with an HTML diff table.
+   */
+  function render() {
+    drupal_add_css(drupal_get_path('module', 'coder') .'/tests/coder_format/coder-diff.css', 'module', 'all', false);
+    $diff     = new Text_Diff('auto', array(explode("\n", $this->expect), explode("\n", $this->actual)));
+    $renderer = new Text_Diff_Renderer_parallel($this->test .' test at '. htmlspecialchars($this->filename));
+    $renderer->original = 'Expected';
+    $renderer->final    = 'Actual';
+    $message .= $renderer->render($diff);
+    return $message;
+  }
+}
+
+/**
+ * Parallel diff renderer for HTML tables with original text on left,
+ * new text on right, and changed text highlighted with appropriate classes.
+ */
+class Text_Diff_Renderer_parallel extends Text_Diff_Renderer {
+  /* String header for left column */
+  var $original = 'Original';
+
+  /* String header for right column */
+  var $final = 'Final';
+  // these are big to ensure entire string is output
+  var $_leading_context_lines = 10000;
+  var $_trailing_context_lines = 10000;
+  var $title;
+  
+  function Text_Diff_Renderer_parallel($title) {
+    $this->title = $title;
+  }
+
+  function _blockHeader() {}
+
+  function _startDiff() {
+    return '<table class="diff"><thead><tr><th colspan="2">'. $this->title .'</th></tr><tr><th>'. $this->original .'</th><th>'. $this->final .'</th></tr></thead><tbody>';
+  }
+
+  function _endDiff() {
+    return '</tbody></table>';
+  }
+
+  function _context($lines) {
+    return '<tr><td><pre>'. htmlspecialchars(implode("\n", $lines)) .'</pre></td>
+          <td><pre>'. htmlspecialchars(implode("\n", $lines)) .'</pre></td></tr>';
+  }
+
+  function _added($lines) {
+    return '<tr><td>&nbsp;</td><td class="added"><pre>'. htmlspecialchars(implode("\n", $lines)) .'</pre></td></tr>';
+  }
+
+  function _deleted($lines) {
+    return '<tr><td class="deleted"><pre>'. htmlspecialchars(implode("\n", $lines)) .'</pre></td><td>&nbsp;</td></tr>';
+  }
+
+  function _changed($orig, $final) {
+    return '<tr class="changed"><td><pre>'. htmlspecialchars(implode("\n", $orig)) .'</pre></td>
+        <td><pre>'. htmlspecialchars(implode("\n", $final)) .'</pre></td></tr>';
+  }
+}
+
Index: tests/coder_format/README.txt
===================================================================
RCS file: tests/coder_format/README.txt
diff -N tests/coder_format/README.txt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/README.txt	19 Jan 2008 15:14:36 -0000
@@ -0,0 +1,77 @@
+/* $Id$ */
+
+-- SUMMARY --
+
+Code Format unit tests
+
+These files are implementations for the SimpleTest Drupal module. There
+is a bit of custom code here to make things go as smoothly as possible,
+and a little bit of necessary setup. Here's what you'll need:
+
+
+-- REQUIREMENTS --
+
+* SimpleTest module, along with the patch in
+  http://drupal.org/node/211823
+
+* SimpleTest Framework
+  https://sourceforge.net/project/showfiles.php?group_id=76550
+
+* Text_Diff package from PEAR
+  http://pear.php.net/package/Text_Diff
+
+
+-- INSTALLATION --
+
+* If not already done, install SimpleTest module and SimpleTest framework as
+  usual.
+
+* Apply above mentioned patch to SimpleTest module. See
+  http://drupal.org/patch/apply for further information.
+
+  FYI: This patch fixes some incompatibilities with our heavy OOP testing
+  framework for coder_format. It should not break other tests.
+
+* Download the Text_Diff package from PEAR into this directory, i.e.
+
+  tests/coder_format/
+
+  ..., extract the archive and rename the folder from "Text_Diff-0.x.x" to
+  "Text_Diff".
+
+* If not already done, go to admin/build/modules, and enable Coder module.
+
+
+-- USAGE --
+
+* Go to admin/build/simpletest, and select Coder Format Tests, and run tests.
+
+
+-- CUSTOMIZATIONS --
+
+Currently, only the all.test is implemented, which is used to test
+the overall output of coder_format_string_all(). Appropriate .phpt test
+files are located in the sub-directory all/.
+
+The internal format for coder_format tests is:
+
+  TEST: Name of test
+
+  --INPUT--
+  // PHP code to input
+
+  --EXPECT--
+  // Coder cleaned code expected
+
+Note that <?php and CVS Id tags are not necessary. If you would like to test for
+those, additionally specify in the file head:
+
+  FULL: 1
+
+
+-- CONTACT --
+
+Current maintainers:
+* Edward Z. Yang (ezyang)
+* Daniel F. Kudwien (sun) - dev@unleashedmind.com
+
Index: tests/coder_format/all.test
===================================================================
RCS file: tests/coder_format/all.test
diff -N tests/coder_format/all.test
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/all.test	19 Jan 2008 15:29:57 -0000
@@ -0,0 +1,36 @@
+<?php
+// $Id$
+
+// Load PEAR Text_Diff library.
+require_once 'Text_Diff/Diff.php';
+require_once 'Text_Diff/Diff/Renderer.php';
+
+// Load coder_format test cases.
+require_once 'CoderTestCase.php';
+require_once 'CoderTestFile.php';
+
+/**
+ * Coder Format tests.
+ */
+class CoderFormatTest extends CoderTestCase {
+  function get_info() {
+    return array(
+      'name'  => 'Full coder_format tests',
+      'desc'  => t('Tests all of the functionality of the coder_format script.'),
+      'group' => 'Coder Format Tests',
+    );
+  }
+  
+  function test() {
+    $dir   = drupal_get_path('module', 'coder') . '/tests/coder_format/tests';
+    $files = array_keys(file_scan_directory($dir, '\.phpt$'));
+    foreach ($files as $file) {
+      $expectation = new CoderTestFile();
+      $result = $this->assert($expectation, $file, '%s');
+      if (!$result) {
+        echo $expectation->render();
+      }
+    }
+  }
+}
+
Index: tests/coder_format/coder-diff.css
===================================================================
RCS file: tests/coder_format/coder-diff.css
diff -N tests/coder_format/coder-diff.css
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/coder-diff.css	19 Jan 2008 15:16:46 -0000
@@ -0,0 +1,17 @@
+/* $Id$ */
+
+.diff {
+  margin: 5%;
+  width: 87%;
+}
+.diff th {
+  width: 50%;
+}
+.diff pre {
+  margin: 0;
+  padding: 0;
+  background: none;
+}
+.diff .changed, .diff .deleted, .diff .added {
+  background: #D3E7F4 !important;
+}
Index: tests/coder_format/tests/br.phpt
===================================================================
RCS file: tests/coder_format/tests/br.phpt
diff -N tests/coder_format/tests/br.phpt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/tests/br.phpt	18 Jan 2008 22:47:14 -0000
@@ -0,0 +1,31 @@
+TEST: Blank lines with whitespace
+
+--INPUT--
+// Change:
+if ($foo) {
+  bar();
+  
+  baz();
+}
+
+// No change:
+if ($foo) {
+  bar();
+
+  baz();
+}
+
+--EXPECT--
+// Change:
+if ($foo) {
+  bar();
+
+  baz();
+}
+
+// No change:
+if ($foo) {
+  bar();
+
+  baz();
+}
Index: tests/coder_format/tests/comments.phpt
===================================================================
RCS file: tests/coder_format/tests/comments.phpt
diff -N tests/coder_format/tests/comments.phpt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/tests/comments.phpt	18 Jan 2008 22:51:02 -0000
@@ -0,0 +1,48 @@
+TEST: Comment movement
+--INPUT--
+// Change:
+$foo = foo(); // Move up.
+if ($foo) {
+  bar($foo); // Move up.
+}
+
+// Don't change:
+$string = '//';
+$foo    = 'http://google.com';
+$string = array(
+  'foo', '//',
+);
+
+// Comment // comment.
+
+/**
+ * This does stuff with FOO//BOO.
+ */
+function foo() {
+  return 'boo';
+}
+
+--EXPECT--
+// Change:
+// Move up.
+$foo = foo();
+if ($foo) {
+  // Move up.
+  bar($foo);
+}
+
+// Don't change:
+$string = '//';
+$foo    = 'http://google.com';
+$string = array(
+  'foo', '//',
+);
+
+// Comment // comment.
+
+/**
+ * This does stuff with FOO//BOO.
+ */
+function foo() {
+  return 'boo';
+}
Index: tests/coder_format/tests/misc.phpt
===================================================================
RCS file: tests/coder_format/tests/misc.phpt
diff -N tests/coder_format/tests/misc.phpt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/tests/misc.phpt	18 Jan 2008 22:51:24 -0000
@@ -0,0 +1,38 @@
+TEST: Miscellaneous (split me!)
+--INPUT--
+// No change:
+$foo = $bar ? -1 : 0;
+$foo = (-1 + 1);
+
+if ($foo) {
+  if ($bar) {
+    // Trall!
+  }
+}
+
+function &foo() {
+  echo 'asdf';
+}
+
+foo(
+  $bar
+);
+
+--EXPECT--
+// No change:
+$foo = $bar ? -1 : 0;
+$foo = (-1 + 1);
+
+if ($foo) {
+  if ($bar) {
+    // Trall!
+  }
+}
+
+function &foo() {
+  echo 'asdf';
+}
+
+foo(
+  $bar
+);
Index: tests/coder_format/tests/ml_array.phpt
===================================================================
RCS file: tests/coder_format/tests/ml_array.phpt
diff -N tests/coder_format/tests/ml_array.phpt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/coder_format/tests/ml_array.phpt	18 Jan 2008 22:57:02 -0000
@@ -0,0 +1,70 @@
+TEST: Multiline arrays
+
+-- INPUT --
+// Change:
+$var = array(
+  'install_page' => array(
+    'arguments' => array(
+      'content' => NULL),
+  ),
+);
+
+$array = array(
+  'foo' => 'bar',
+  2 => $foo,
+  0x000000 => 'asdf',
+  "CRIVENS" => 3,
+  $bar => 'asdf',
+);
+
+$deep = array(
+  'foo' => array(
+    'croon' => 'asdf',
+    'f' => 'a'
+  ),
+  'barasdfsadf' => array(
+    'asdfasdfasdf' => 'd',
+    'fsd' => 23
+  )
+);
+
+drupal_add_link(array('rel' => 'alternate',
+                      'type' => 'application/rss+xml',
+                      'title' => $title,
+                      'href' => $url));
+
+-- EXPECT --
+// Change:
+$var = array(
+  'install_page' => array(
+    'arguments' => array(
+      'content' => NULL,
+    ),
+  ),
+);
+
+$array = array(
+  'foo' => 'bar',
+  2 => $foo,
+  0x000000 => 'asdf',
+  "CRIVENS" => 3,
+  $bar => 'asdf',
+);
+
+$deep = array(
+  'foo' => array(
+    'croon' => 'asdf',
+    'f' => 'a',
+  ),
+  'barasdfsadf' => array(
+    'asdfasdfasdf' => 'd',
+    'fsd' => 23,
+  ),
+);
+
+drupal_add_link(array('rel' => 'alternate',
+    'type' => 'application/rss+xml',
+    'title' => $title,
+    'href' => $url
+  ));
+
