=== added file 'includes/graph.inc'
--- includes/graph.inc	1970-01-01 00:00:00 +0000
+++ includes/graph.inc	2008-09-29 11:56:33 +0000
@@ -0,0 +1,279 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   Provides Graph related Classes.
+ *
+ * The following classes are available:
+ * - Graph which is a basic graph.
+ * - DirectedGraph which is based on Graph.
+ *
+ * Future extensions could provide:
+ * - Directed Acyclic Graph which is a special Directed Graph.
+ * - Tree which is a special Directed Acyclic Graph.
+ */
+
+/**
+ * A graph is a list of links between nodes.
+ *
+ * Each link is bidirectional.
+ */
+class Graph {
+  const GRAPH_LINKS = '_links';
+
+  /**
+   * _list contains a pair of (node id, array of links to node id)
+   *
+   *  A graph like a -- b -- c -- a can be stored as
+   *  (a, (b))
+   *  (b, (c))
+   *  (c, (a))
+   *
+   * but also like
+   *  (a, (b,c))
+   *  (b, (c))
+   *  (c, ())
+   *
+   * @var array()
+   *
+   */
+  protected $_list =array();
+
+  /**
+   * Adds id to the list.
+   *
+   * The links array is proper initialised.
+   *
+   * @param String $id
+   *   The Id of the node to add.
+   * @return Graph
+   */
+  public function add( $id) {
+    if (!isset( $this->_list[$id])) {
+      $this->_list[$id] = array();
+      $this->_list[$id][Graph::GRAPH_LINKS] = array();
+    }
+    return $this;
+  }
+
+  /**
+   * Adds a link between two node ids.
+   *
+   * @param String $from_id
+   *   The start point of the link.
+   * @param String $to_id
+   *   The end point of the link.
+   * @return Graph
+   */
+  public function addLink( $from_id, $to_id) {
+    $this->add( $from_id);
+    $this->add( $to_id);
+    $this->_addLink($from_id, $to_id);
+    return $this;
+  }
+
+  /**
+   * Implementation of bidirection links between the given node ids.
+   *
+   * @param string $from_id
+   * @param string $to_id
+   */
+  protected function _addLink( $from_id, $to_id) {
+    if (!in_array($to_id, $this->getLinks($from_id))) {
+      $this->_list[$from_id][Graph::GRAPH_LINKS][] = $to_id;
+    }
+    if (!in_array($from_id, $this->getLinks($to_id))) {
+      $this->_list[$to_id][Graph::GRAPH_LINKS][] = $from_id;
+    }
+  }
+
+  /**
+   * Adds a list of links to a node.
+   *
+   * @param string $from_id
+   * @param array $to_ids
+   *
+   * @return Graph
+   */
+  public function addLinks( $from_id, $to_ids) {
+    foreach ($to_ids as $to_id) {
+      $this->addLink( $from_id, $to_id);
+    }
+    return $this;
+  }
+
+  /**
+   * Returns all links from the give node id.
+   *
+   * @param string $from_id
+   * @return array of node ids leaving the given node id.
+   */
+  public function getLinks( $id) {
+    return array_values($this->_list[$id][Graph::GRAPH_LINKS]);
+  }
+
+  /**
+   * Gives all participants related to the given node(s).
+   *
+   * @param array $list
+   *   The list of Ids interested in.
+   * @return array
+   *   All Ids related to the given list.
+   */
+  public function getParticipants( $list= array()) {
+    if (empty($list)) {
+      return array_keys($this->_list);
+    }
+    $visited = array();
+    $agenda = array_values($list);
+    while ($id= array_shift($agenda)) {
+      // Prevent infinite looping
+      if (!isset($visited[$id])) {
+        $visited[$id]= TRUE;
+        $agenda= array_merge( $agenda, $this->getLinks($id));
+      }
+    }
+    return array_keys($visited);
+  }
+  public function isCircularMember( $id) {
+    $route = $this->getParticipants(array($id));
+    foreach ($route as $visited_id) {
+      if (in_array($id, $this->getLinks($visited_id))) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+}
+
+class DirectedGraph extends Graph {
+  const GRAPH_ROOT = '_root';
+  // Top level node id.
+  private $_root= DirectedGraph::GRAPH_ROOT;
+
+  /**
+   * Implementation of uni directed link between two nodes
+   *
+   * @see addLink()
+   *
+   * @param string $from_id
+   * @param string $to_id
+   */
+  protected function _addLink( $from_id, $to_id) {
+    if (!in_array($to_id, $this->_list[$from_id][Graph::GRAPH_LINKS])) {
+      $this->_list[$from_id][Graph::GRAPH_LINKS][] = $to_id;
+    }
+  }
+
+  /**
+   * Builds a reversed graph.
+   *
+   * All unidirectional links are reversed.
+   *
+   * @return DirectedGraph
+   */
+  public function getReversedGraph() {
+    $result = new DirectedGraph();
+    foreach (array_keys($this->_list) as $from_id) {
+      $result->add($from_id);
+      foreach ($this->getLinks($from_id) as $to_id) {
+        $result->addLink($to_id, $from_id);
+      }
+    }
+    return $result;
+  }
+
+  protected function getRoot() {
+    return $this->_root;
+  }
+
+  /**
+   * Adds a root element to the graph.
+   *
+   * All elements not linked to will be linked to by a root.
+   *
+   * @see getTSL()
+   *
+   * @param unknown_type $id
+   */
+  protected function addRoot() {
+    $g = $this->getReversedGraph();
+    foreach ($g->_list as $key => $data) {
+      if (empty( $data[Graph::GRAPH_LINKS]) && ($key!==$this->_root)) {
+        $this->addLink($this->_root, $key);
+      }
+    }
+  }
+
+  /**
+   * A subgraph is calculated based on the participants collected based on the given node ids.
+   *
+   * @param array $ids
+   *   The nodes interested in.
+   * @return DirectedGraph
+   *   The subgraph with all participants
+   */
+  public function subGraph( $ids = array()) {
+    $g = new DirectedGraph();
+    $participants= $this->getParticipants( $ids);
+    foreach ($participants as $id) {
+      $g->add($id);
+      // Only participating links are added.
+      $g->addLinks($id, array_intersect($participants, $this->getLinks($id)));
+    }
+    return $g;
+  }
+
+  /**
+   * Calculates the Topological Sorted List.
+   *
+   * A Topological Sorted List is a Depth First Search ordered
+   * list of participants.
+   *
+   * TODO: Do we need a Directed Acyclic Graph?
+   * If there are cycles/loops then the algorithme does not loop forever.
+   * But the TSL is not really a TSL.
+   *
+   * The algorithme is based on the Iterator example from
+   * the book Higher Order Perl where a recusive function
+   * can be rewritten into a loop.
+   *
+   * @param array $ids
+   *   List of nodes interested in.
+   * @return array
+   *   The TSL ordered list of participants
+   */
+  public function getTSL( $ids = array()) {
+    $g= $this->subGraph($ids);
+    // By adding a root the DFS is more cleaner/predictable for tests
+    $g->addRoot();
+    $agenda= array($g->getRoot());
+    $visited= array();
+    $tsl= array();
+    while ($inspect= array_pop($agenda)) {
+      if (!isset($visited[$inspect])) {
+        $visited[$inspect]= TRUE;
+        $links = $g->getLinks($inspect);
+        if (!empty($links)) {
+          array_push($agenda, $inspect);
+          //$agenda = array_merge( $agenda, array_diff( $links, array_keys( $visited)));
+          foreach ($links as $id) {
+            if (!isset($visited[$id])) {
+              $agenda[]= $id;
+            }
+          }
+        }
+        else {
+          // We are done with this node.
+          $tsl[]= $inspect;
+        }
+      }
+      else {
+        // Already inspected so spit it out.
+        $tsl[]= $inspect;
+      }
+    }
+    return array_diff($tsl, array( $g->getRoot()));
+  }
+}
\ No newline at end of file

=== added file 'includes/module.admin.inc'
--- includes/module.admin.inc	1970-01-01 00:00:00 +0000
+++ includes/module.admin.inc	2008-09-29 11:56:33 +0000
@@ -0,0 +1,108 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   Provide module administration functions.
+ *
+ * When administrating modules care must be taken into enabling/disabling
+ * modules. Dependency checks must be taken care of.
+ */
+
+/**
+ * Returns Topological Sorted List of dependant module based on a list of modules.
+ *
+ * A Topological Sorted List is calculated based on the graph the modules
+ * are depending on.
+ *
+ * When enabling lists -dependant on- modules.
+ * When disabling lists -reverse dependant on- modules.
+ *
+ * @param array $module
+ *   List of modules to check for
+ * @param boolean $enabling
+ *   Direction of the list
+ *
+ * @return DirectedGraph
+ *   A TSL of modules in the direction requested without '-root-'.
+ */
+function module_get_tsl($modules=array(), $enabling=TRUE) {
+  return module_graph($modules, $enabling)->getTSL();
+}
+
+/**
+ * Checks whether the list of needed modules to enable or disable the given module overlaps.
+ *
+ * If the list of modules to get $module enabled has more in common
+ * with the list of modules to get the $module disabled then the
+ * given module there is a circular dependency.
+ *
+ * @param string $module
+ *   The module name to test for
+ * @return boolean
+ *   Indicating module is part of a circular dependency.
+ */
+function module_is_part_of_circular( $module) {
+  return _module_full_graph(TRUE)->isCircularMember($module);
+}
+
+/**
+ * Calculates a (part of) the graph of modules dependency relations.
+ *
+ * @param array $modules
+ *   List of modules interested in.
+ * @param boolean $enabling
+ *   Indicates the direction interested in.
+ * @return DirectedGraph
+ */
+function module_graph($modules = array(), $enabling = TRUE) {
+  return _module_full_graph($enabling)->subGraph($modules);
+}
+
+/**
+ * Calculates all modules necessary for enabling or disabling.
+ *
+ * Missing modules have a dependency with '-missing-' so we
+ * can check for missing modules quite easy.
+ *
+ * @param array $modules
+ *   List of modules interested in.
+ * @param boolean $enabling
+ *   Indicates the direction interested in.
+ * @return array
+ *   List of all modules needed.
+ */
+function module_participants($modules = array(), $enabling = TRUE) {
+  return module_graph($modules, $enabling)->getParticipants();
+}
+
+/**
+ * Returns the full graph of all modules.
+ *
+ * This graph is later used for subgraphs and reverse dependencies.
+ *
+ * We saveguard the static variable by returning a clone of the graph.
+ *
+ * @return DirectedGraph
+ */
+function _module_full_graph($enabling) {
+  drupal_autoload_class('DirectedGraph');
+  static $full_graph_enabling;
+  static $full_graph_disabling;
+  if (!isset($full_graph_enabling)) {
+    $all_modules = module_rebuild_cache();
+    $full_graph = new DirectedGraph();
+    foreach ($all_modules as $module => $data) {
+      $full_graph->add( $module);
+      $full_graph->addLinks($module, $data->info['dependencies']);
+    }
+    $full_graph_disabling = $full_graph->getReversedGraph();
+  }
+  // SafeGuard the static by returning a clone.
+  if ($enabling) {
+    return $full_graph->subGraph();
+  }
+  else {
+    return $full_graph_disabling->subGraph();
+  }
+}
\ No newline at end of file

=== modified file 'includes/module.inc'
--- includes/module.inc	2008-09-27 19:03:30 +0000
+++ includes/module.inc	2008-09-29 11:56:33 +0000
@@ -132,75 +132,18 @@
       db_query("INSERT INTO {system} (name, info, type, filename, status, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d)", $file->name, serialize($files[$filename]->info), 'module', $file->filename, 0, $bootstrap);
     }
   }
-  $files = _module_build_dependencies($files);
+  _module_build_dependencies($files);
   return $files;
 }
 
-/**
- * Find dependencies any level deep and fill in dependents information too.
- *
- * If module A depends on B which in turn depends on C then this function will
- * add C to the list of modules A depends on. This will be repeated until
- * module A has a list of all modules it depends on. If it depends on itself,
- * called a circular dependency, that's marked by adding a nonexistent module,
- * called -circular- to this list of modules. Because this does not exist,
- * it'll be impossible to switch module A on.
- *
- * Also we fill in a dependents array in $file->info. Using the names above,
- * the dependents array of module B lists A.
- *
- * @param $files
- *   The array of filesystem objects used to rebuild the cache.
- * @return
- *   The same array with dependencies and dependents added where applicable.
- */
-function _module_build_dependencies($files) {
-  do {
-    $new_dependency = FALSE;
-    foreach ($files as $filename => $file) {
-      // We will modify this object (module A, see doxygen for module A, B, C).
-      $file = &$files[$filename];
-      if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
-        foreach ($file->info['dependencies'] as $dependency_name) {
-          // This is a nonexistent module.
-          if ($dependency_name == '-circular-' || !isset($files[$dependency_name])) {
-            continue;
-          }
-          // $dependency_name is module B (again, see doxygen).
-          $files[$dependency_name]->info['dependents'][$filename] = $filename;
-          $dependency = $files[$dependency_name];
-          if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) {
-            // Let's find possible C modules.
-            foreach ($dependency->info['dependencies'] as $candidate) {
-              if (array_search($candidate, $file->info['dependencies']) === FALSE) {
-                // Is this a circular dependency?
-                if ($candidate == $filename) {
-                  // As a module name can not contain dashes, this makes
-                  // impossible to switch on the module.
-                  $candidate = '-circular-';
-                  // Do not display the message or add -circular- more than once.
-                  if (array_search($candidate, $file->info['dependencies']) !== FALSE) {
-                    continue;
-                  }
-                  drupal_set_message(t('%module is part of a circular dependency. This is not supported and you will not be able to switch it on.', array('%module' => $file->info['name'])), 'error');
-                }
-                else {
-                  // We added a new dependency to module A. The next loop will
-                  // be able to use this as "B module" thus finding even
-                  // deeper dependencies.
-                  $new_dependency = TRUE;
-                }
-                $file->info['dependencies'][] = $candidate;
-              }
-            }
-          }
-        }
+function _module_build_dependencies(&$files) {
+  foreach ($files as $module_name => $module) {
+    foreach ($module->info['dependencies'] as $dep_module) {
+      if (!in_array($module_name, $files[$dep_module]->info['dependents'])) {
+        $files[$dep_module]->info['dependents'][] = $module_name;
       }
-      // Don't forget to break the reference.
-      unset($file);
     }
-  } while ($new_dependency);
-  return $files;
+  }
 }
 
 /**
@@ -269,6 +212,13 @@
  *   An array of module names.
  */
 function module_enable($module_list) {
+  // TODO: hack for making tests run.
+  require_once 'module.admin.inc';
+  require_once 'graph.inc';
+
+  drupal_function_exists('module_get_tsl');
+  $module_list =  module_get_tsl( $module_list);
+
   $invoke_modules = array();
   foreach ($module_list as $module) {
     $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE type = '%s' AND name = '%s'", 'module', $module));
@@ -306,6 +256,9 @@
  *   An array of module names.
  */
 function module_disable($module_list) {
+  drupal_function_exists('module_get_tsl');
+  $module_list =  module_get_tsl( $module_list, FALSE);
+
   $invoke_modules = array();
   foreach ($module_list as $module) {
     if (module_exists($module)) {
@@ -484,4 +437,4 @@
  */
 function drupal_required_modules() {
   return array('block', 'filter', 'node', 'system', 'user');
-}
+}
\ No newline at end of file

=== added file 'modules/simpletest/tests/graph.test'
--- modules/simpletest/tests/graph.test	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/graph.test	2008-09-29 11:56:33 +0000
@@ -0,0 +1,131 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   Contains tests for the graph.inc classes.
+ *
+ * Both Graph and DirectedGraph are tested.
+ *
+ */
+class GraphIncTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Graph Tests'),
+      'description' => t('Test graph.inc API.'),
+      'group' => t('Graph'),
+    );
+  }
+
+  function testGraphs() {
+    // Simple Grapsh b -- a -- c -- b
+    $g = new Graph();
+    $g->addLink('b', 'a')->addLink('c', 'a')->addLink('c', 'b');
+    $tests = array();
+    $tests['Graph->getLinks(a)'] = array(
+      'expected' => array( 'b', 'c'),
+      'result' => $g->getLinks('a'),
+      'sort_result' => TRUE,
+    );
+    $tests['Graph->getParticipants()'] = array(
+      'expected' => array( 'a', 'b', 'c'),
+      'result' => $g->getParticipants(),
+      'sort_result' => TRUE,
+    );
+    $tests['Graph->getParticipants(a)'] = array(
+      'expected' => array( 'a', 'b', 'c'),
+      'result' => $g->getParticipants(array('a')),
+      'sort_result' => TRUE,
+    );
+    // Now same graph as a DirectedGraph
+    $h= new DirectedGraph();
+    $h->addLink('b', 'a')->addLink('c', 'a')->addLink('c', 'b');
+    $tests['DirectedGraph->getLinks(a)'] = array(
+      'expected' => array(),
+      'result' => $h->getLinks('a'),
+      'sort_result' => TRUE,
+    );
+    $tests['DirectedGraph->getParticipants'] = array(
+      'expected' => array( 'a', 'b', 'c'),
+      'result' => $h->getParticipants(),
+      'sort_result' => TRUE,
+    );
+    $tests['DirectedGraph->getParticipants(a)'] = array(
+      'expected' => array('a'),
+      'result' => $h->getParticipants(array('a')),
+      'sort_result' => TRUE,
+    );
+    $tests['DirectedGraph->subgraph(b)'] = array(
+      'expected' => array('a', 'b'),
+      'result' => $h->subGraph(array('b'))->getParticipants(),
+      'sort_result' => TRUE,
+    );
+    // Check test results
+    foreach ($tests as $test => $data) {
+      $expected = $data['expected'];
+      $result = $data['result'];
+      if (isset($data['sort_result']) && $data['sort_result']) {
+        asort($result);
+        $result= array_values($result);
+      }
+      $this->assertTrue( $result===$expected, t("@m gives result '@r'", array( '@m' => $test, '@r' => implode(', ', $result))));
+    }
+  }
+
+  function testTSL() {
+    $g= new DirectedGraph();
+    $g->addLink('a', 'b')->addLink('b', 'c')->addLink('c', 'd');
+    $g->addLink('e', 'f')->addLink('f', 'c');
+    $g->addLink('x', 'y')->addLink('y', 'z');
+    $g->addLink('p', 'q')->addLink('q', 'r')->addLink('r', 'p');
+
+    $tests = array();
+    $tests['TSL (a)'] = array(
+      'expected' => array('d', 'c', 'b', 'a'),
+      'result' => $g->getTSL(array('a')),
+    );
+    $tests['Reversed TSL (c)'] = array(
+      'expected' => array('a', 'b', 'e', 'f', 'c'),
+      'expected_alt' => array('e', 'f', 'a', 'b', 'c'),
+      'result' => $g->getReversedGraph()->getTSL(array('c')),
+    );
+    $tests['Subgraph TSL (a)'] = array(
+      'expected' => $g->getTSL(array('a')),
+      'result' => $g->subGraph(array('a'))->getTSL(),
+    );
+    $tests['TSL (z)'] = array(
+      'expected' => array('z'),
+      'result' => $g->getTSL(array('z')),
+    );
+    $tests['Reversed TSL (z)'] = array(
+      'expected' => array('x', 'y', 'z'),
+      'result' => $g->getReversedGraph()->getTSL(array('z')),
+    );
+  }
+  function testCircularMember() {
+    $g= new DirectedGraph();
+    // Non circular part of graph
+    $g->addLink('x', 'y')->addLink('y', 'z');
+    // Circular part of graph
+    $g->addLink('p', 'q')->addLink('q', 'r')->addLink('r', 'p');
+    $tests['Is circular member (p)'] = array(
+      'expected' => TRUE,
+      'result' => $g->isCircularMember('p'),
+    );
+    $tests['Is circular member (x)'] = array(
+      'expected' => FALSE,
+      'result' => $g->isCircularMember('x'),
+    );
+    foreach ($tests as $test => $data) {
+      $expected = $data['expected'];
+      $result = $data['result'];
+      if (isset($data['expected_alt']) && $result!==$expected) {
+        $expected= $data['expected_alt'];
+      }
+      $this->assertTrue( $result===$expected, t("@t", array( '@t' => $test)));
+    }
+  }
+}

=== added directory 'modules/simpletest/tests/module'
=== added file 'modules/simpletest/tests/module.test'
--- modules/simpletest/tests/module.test	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module.test	2008-09-29 11:56:33 +0000
@@ -0,0 +1,208 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   Provides SimpleTests for module.inc.
+ *
+ * The dummy modules module/module_xyz_test are available
+ * for the tests. The x/y/z combo is a circular dependency.
+ */
+
+class ModuleIncTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Module Tests'),
+      'description' => t('Test module.inc API.'),
+      'group' => t('Module'),
+    );
+  }
+
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    // Enable dummy module.
+    parent::setUp('module_a_test');
+    drupal_function_exists('module_get_tsl');
+  }
+
+  /**
+   * Checks the module enabled status.
+   *
+   * @param string $module
+   * @return boolean
+   *   Indicates the enabled status of the given module.
+   */
+  function moduleIsEnabled( $module) {
+    $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE type = '%s' AND name = '%s'", 'module', $module));
+    return is_object($existing) && !($existing->status == 0);
+  }
+
+  function modulesAreEnabled( $modules) {
+    foreach ($modules as $module) {
+      if (!$this->moduleIsEnabled($module)) return FALSE;
+    }
+    return TRUE;
+  }
+
+  function modulesAreDisabled( $modules) {
+    foreach ($modules as $module) {
+      if ($this->moduleIsEnabled($module)) return FALSE;
+    }
+    return TRUE;
+  }
+
+  /**
+   * Test for a working module_enable.
+   *
+   * This is for testing that the normal setup is functional. We intend to
+   * refactor module.inc so here we make sure it's still in op. This is of
+   * course not the case... breaking module.inc while render the whole test infra
+   * useless. Anyway this test should be here.
+   */
+  function testModuleAIsEnabled() {
+    $this->assertTrue($this->moduleIsEnabled('module_a_test'), 'module_a_test is enabled by setup.');
+  }
+
+  /**
+   * Test module enabling.
+   *
+   * Modules C and D depends on Module B which is dependent on module A.
+   */
+  function testDependingModule() {
+    $this->assertTrue($this->moduleIsEnabled('module_a_test'), 'A is enabled by setup.');
+    $this->assertFalse($this->moduleIsEnabled('module_b_test'), 'B is disabled before enabling.');
+    // simple enabling
+    module_enable( array('module_b_test'));
+    $this->assertTrue($this->moduleIsEnabled('module_b_test'), 'B is enabled.');
+    // dependent disabling
+    module_disable(array('module_a_test'));
+    $this->assertFalse($this->moduleIsEnabled('module_b_test'),
+      'Disabling A leads to a dependent disabled B.'
+    );
+    // group enabling
+    module_enable( array('module_a_test', 'module_b_test'));
+    $this->assertTrue($this->modulesAreEnabled(array('module_a_test', 'module_b_test')), 'A and B are enabled.');
+    // group disabling
+    module_disable(array('module_a_test', 'module_b_test'));
+    $this->assertTrue($this->modulesAreDisabled(array('module_a_test', 'module_b_test')), 'A and B are disabled.');
+    // depending enabling
+    module_enable(array('module_c_test', 'module_d_test'));
+    $this->assertTrue($this->modulesAreEnabled(array('module_a_test', 'module_b_test', 'module_c_test', 'module_d_test')),
+       'Enabling C and D needs A and B enabled too.'
+    );
+    // cascading disabling
+    module_disable(array('module_a_test'));
+    $this->assertTrue($this->modulesAreDisabled(array('module_a_test', 'module_b_test', 'module_c_test', 'module_d_test')),
+      'Disabling A needs disabled B, C and D.'
+    );
+  }
+
+  function testTSL() {
+    $tests = array();
+    $tests[] = array(
+      'modules' => array('module_a_test'),
+      'expected' => array('module_a_test'),
+      'direction' => 'enabling',
+    );
+    $tests[] = array(
+      'modules' => array('module_b_test'),
+      'expected' => array('module_a_test', 'module_b_test'),
+      'direction' => 'enabling',
+    );
+    $tests[] = array(
+      'modules' => array('module_c_test'),
+      'expected' => array('module_a_test', 'module_b_test', 'module_c_test'),
+      'direction' => 'enabling',
+    );
+    $tests[] = array(
+      'modules' => array('module_a_test'),
+      'expected' => array('module_c_test', 'module_d_test', 'module_b_test', 'module_a_test'),
+      'expected_alt' => array('module_d_test', 'module_c_test', 'module_b_test', 'module_a_test'),
+      'direction' => 'disabling',
+    );
+    foreach ($tests as $test) {
+      $modules = $test['modules'];
+      $direction = $test['direction'];
+      $expected = $test['expected'];
+      $result = module_get_tsl($modules, $direction=='enabling');
+      if (isset($test['expected_alt']) && $result!=$expected) {
+        $expected=$test['expected_alt'];
+      }
+      $this->assertEqual($result, $expected,
+        t("TSL @d '@m' results in @r.",
+          array(
+            "@d" => $direction,
+            "@m" => implode(",", $modules),
+            "@r" => implode(",", $result),
+          )
+        )
+      );
+    }
+  }
+
+  function testParticipants() {
+    $all_modules = module_participants();
+    $core_modules = drupal_required_modules();
+    $this->assertTrue(count($all_modules)>=count($core_modules),
+       "There are more modules then the few drupal required ones."
+    );
+    $tests = array();
+    $tests[] = array(
+      'modules' => array('module_a_test'),
+      'expected' => array('module_a_test'),
+      'direction' => 'enabling',
+    );
+    $tests[] = array(
+      'modules' => array('module_a_test'),
+      'expected' => array('module_a_test', 'module_b_test', 'module_c_test', 'module_d_test'),
+      'direction' => 'disabling',
+    );
+    $tests[] = array(
+      'modules' => array('module_d_test'),
+      'expected' => array('module_a_test', 'module_b_test', 'module_d_test'),
+      'direction' => 'enabling',
+    );
+    $tests[] = array(
+      'modules' => array('module_d_test', 'module_c_test'),
+      'expected' => array('module_a_test', 'module_b_test', 'module_c_test', 'module_d_test'),
+      'direction' => 'enabling',
+    );
+    foreach ($tests as $test) {
+      $modules = $test['modules'];
+      $direction = $test['direction'];
+      $expected = $test['expected'];
+      $result = array_values( module_participants($modules, $direction=='enabling'));
+      asort($result);
+      asort($expected);
+      // skipping the next two lines renders the assertEqual useless  for some tests
+      $result = array_values($result);
+      $expected = array_values($expected);
+      $this->assertTrue($result===$expected,
+        t("module_participants for @m when @d gave '@r' versus '@e'",
+          array(
+            "@d" => $direction,
+            "@m" => implode(",", $modules),
+            "@r" => implode(",", $result),
+            "@e" => implode(",", $expected),
+          )
+        )
+      );
+    }
+  }
+
+  function testCircular() {
+    // Non circular
+    $module = 'module_a_test';
+    $circular = module_is_part_of_circular($module);
+    $this->assertFalse( $circular, "'$module' is not part of a circular dependency");
+    // Circular
+    $module = 'module_x_test';
+    $circular = module_is_part_of_circular($module);
+    $this->assertTrue( $circular, "'$module' is part of a circular dependency");
+  }
+}

=== added file 'modules/simpletest/tests/module/module_a_test.info'
--- modules/simpletest/tests/module/module_a_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_a_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,8 @@
+; $Id$
+name = "Module Test A"
+description = "Support module for module.inc tests."
+core = 7.x
+package = Testing
+files[] = module_a_test.module
+version = VERSION
+hidden = TRUE

=== added file 'modules/simpletest/tests/module/module_a_test.module'
--- modules/simpletest/tests/module/module_a_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_a_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_a_test_function_1() {
+  return TRUE;
+}

=== added file 'modules/simpletest/tests/module/module_b_test.info'
--- modules/simpletest/tests/module/module_b_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_b_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,9 @@
+; $Id$
+name = "Module Test B"
+description = "Support module for module.inc tests."
+core = 7.x
+dependencies[] = module_a_test
+package = Testing
+files[] = module_b_test.module
+version = VERSION
+hidden = TRUE
\ No newline at end of file

=== added file 'modules/simpletest/tests/module/module_b_test.module'
--- modules/simpletest/tests/module/module_b_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_b_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_b_test_function_1() {
+  return TRUE;
+}

=== added file 'modules/simpletest/tests/module/module_c_test.info'
--- modules/simpletest/tests/module/module_c_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_c_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,9 @@
+; $Id$
+name = "Module Test C"
+description = "Support module for module.inc tests."
+core = 7.x
+dependencies[] = module_b_test
+package = Testing
+files[] = module_c_test.module
+version = VERSION
+hidden = TRUE
\ No newline at end of file

=== added file 'modules/simpletest/tests/module/module_c_test.module'
--- modules/simpletest/tests/module/module_c_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_c_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_c_test_function_1() {
+  return TRUE;
+}

=== added file 'modules/simpletest/tests/module/module_d_test.info'
--- modules/simpletest/tests/module/module_d_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_d_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,9 @@
+; $Id$
+name = "Module Test D"
+description = "Support module for module.inc tests."
+core = 7.x
+dependencies[] = module_b_test
+package = Testing
+files[] = module_d_test.module
+version = VERSION
+hidden = TRUE
\ No newline at end of file

=== added file 'modules/simpletest/tests/module/module_d_test.module'
--- modules/simpletest/tests/module/module_d_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_d_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_d_test_function_1() {
+  return TRUE;
+}

=== added file 'modules/simpletest/tests/module/module_x_test.info'
--- modules/simpletest/tests/module/module_x_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_x_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,9 @@
+; $Id$
+name = "Module Test X"
+description = "Support module for circular module.inc tests."
+core = 7.x
+dependencies[] = module_y_test
+package = Testing
+files[] = module_x_test.module
+version = VERSION
+hidden = TRUE
\ No newline at end of file

=== added file 'modules/simpletest/tests/module/module_x_test.module'
--- modules/simpletest/tests/module/module_x_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_x_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_x_test_function_1() {
+  return TRUE;
+}

=== added file 'modules/simpletest/tests/module/module_y_test.info'
--- modules/simpletest/tests/module/module_y_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_y_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,9 @@
+; $Id$
+name = "Module Test Y"
+description = "Support module for circular module.inc tests."
+core = 7.x
+dependencies[] = module_z_test
+package = Testing
+files[] = module_y_test.module
+version = VERSION
+hidden = TRUE
\ No newline at end of file

=== added file 'modules/simpletest/tests/module/module_y_test.module'
--- modules/simpletest/tests/module/module_y_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_y_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_y_test_function_1() {
+  return TRUE;
+}

=== added file 'modules/simpletest/tests/module/module_z_test.info'
--- modules/simpletest/tests/module/module_z_test.info	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_z_test.info	2008-09-29 11:56:33 +0000
@@ -0,0 +1,9 @@
+; $Id$
+name = "Module Test Z"
+description = "Support module for circular module.inc tests."
+core = 7.x
+dependencies[] = module_x_test
+package = Testing
+files[] = module_z_test.module
+version = VERSION
+hidden = TRUE
\ No newline at end of file

=== added file 'modules/simpletest/tests/module/module_z_test.module'
--- modules/simpletest/tests/module/module_z_test.module	1970-01-01 00:00:00 +0000
+++ modules/simpletest/tests/module/module_z_test.module	2008-09-29 11:56:33 +0000
@@ -0,0 +1,5 @@
+<?php
+
+function module_z_test_function_1() {
+  return TRUE;
+}

