Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.341
diff -u -p -r1.341 menu.inc
--- includes/menu.inc	24 Aug 2009 01:49:41 -0000	1.341
+++ includes/menu.inc	2 Sep 2009 13:58:36 -0000
@@ -1225,28 +1225,29 @@ function menu_tree_data(array $links, ar
  * the next menu link.
  */
 function _menu_tree_data(&$links, $parents, $depth) {
-  $done = FALSE;
   $tree = array();
-  while (!$done && $item = array_pop($links)) {
+  while ($item = array_pop($links)) {
     // We need to determine if we're on the path to root so we can later build
     // the correct active trail and breadcrumb.
     $item['in_active_trail'] = in_array($item['mlid'], $parents);
-    // Look ahead to the next link, but leave it on the array so it's available
-    // to other recursive function calls if we return or build a sub-tree.
-    $next = end($links);
     // Add the current link to the tree.
     $tree[$item['mlid']] = array(
       'link' => $item,
       'below' => array(),
     );
+    // Look ahead to the next link, but leave it on the array so it's available
+    // to other recursive function calls if we return or build a sub-tree.
+    $next = end($links);
     // Check whether the next link is the first in a new sub-tree.
     if ($next && $next['depth'] > $depth) {
       // Recursively call _menu_tree_data to build the sub-tree.
       $tree[$item['mlid']]['below'] = _menu_tree_data($links, $parents, $next['depth']);
+      // Fetch next link after filling the sub-tree.
+      $next = end($links);
     }
-    else {
-      // Determine if we should exit the loop and return.
-      $done = (!$next || $next['depth'] < $depth);
+    // Determine if we should exit the loop and return.
+    if (!$next || $next['depth'] < $depth) {
+      break;
     }
   }
   return $tree;
Index: modules/simpletest/tests/menu.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/menu.test,v
retrieving revision 1.13
diff -u -p -r1.13 menu.test
--- modules/simpletest/tests/menu.test	14 Jul 2009 20:53:16 -0000	1.13
+++ modules/simpletest/tests/menu.test	2 Sep 2009 13:58:36 -0000
@@ -164,3 +164,82 @@ class MenuRebuildTestCase extends Drupal
   }
 
 }
+
+class MenuRecursionTestCase extends DrupalUnitTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Menu recursion tests',
+      'description' => 'Tests recursive functions in menu.inc',
+      'group' => 'Menu',
+    );
+  }
+
+  /**
+   * Test if menu_tree_data() works properly.
+   */
+  function testMenuTreeData() {
+    // Generate a very large menu tree with random depths.
+    $text1 = $this->_generate(200);
+
+    $menu = $this->_text_to_menu($text1);
+    $tree = menu_tree_data($menu);
+    $text2 = $this->_tree_to_text($tree);
+    
+    $this->assertEqual($text1, $text2, t('menu_tree_data() builds a tree with the correct depths.'));
+  }
+
+  /**
+   * Prints a menu with random (but valid) depths.
+   * The format is:
+   * 0
+   * -1
+   * --2
+   * ---3
+   * -4
+   * --5
+   */
+  function _generate($num) {
+    $menu = '';
+    // Start at level 0.
+    $depth = 0;
+    for ($i = 0; $i < $num; $i++) {
+      $menu .= str_pad('', $depth, '-') . $i . "\n";
+      // Modify depth (don't increase it by more than one, but make an increase more likely than a decrease).
+      $depth += min(1, rand(-5, 10));
+      // Don't go out of level 0.
+      $depth = max($depth, 0);
+    }
+    return $menu;
+  }
+
+  /**
+   * Read the text generated above and turn it into a menu link array.
+   */
+  function _text_to_menu($text) {
+    $menu = array();
+    foreach (explode("\n", $text) as $line) {
+      if (preg_match('/^(-*)(.+)$/', $line, $match)) {
+        $menu[] = array(
+          'mlid' => $match[2],
+          // The menu system expects depth to start at 1, not 0.
+          'depth' => strlen($match[1]) + 1,
+        );
+      }
+    }
+    return $menu;
+  }
+
+  /**
+   * Take a recursive menu tree tree and render it into the same format as generate().
+   */
+  function _tree_to_text($tree, $indent = '') {
+    $text = '';
+    foreach ($tree as $item) {
+      $text .= $indent . $item['link']['mlid'] . "\n";
+      $text .= $this->_tree_to_text($item['below'], $indent . '-');
+    }
+    return $text;
+  }
+}
+
+
