Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1219
diff -u -p -r1.1219 common.inc
--- includes/common.inc	13 Sep 2010 06:44:58 -0000	1.1219
+++ includes/common.inc	14 Sep 2010 03:58:29 -0000
@@ -4776,7 +4776,8 @@ function drupal_cron_cleanup() {
  * the file name ($key = 'filename'), the file name without the extension ($key
  * = 'name'), or the full file stream URI ($key = 'uri'). If you use a key of
  * 'filename' or 'name', files found later in the search will take precedence
- * over files found earlier; if you choose a key of 'uri', you will get all
+ * over files found earlier (unless they belong to a module or theme not
+ * compatible with Drupal core); if you choose a key of 'uri', you will get all
  * files found.
  *
  * @param string $mask
@@ -4830,7 +4831,31 @@ function drupal_system_listing($mask, $d
     require_once DRUPAL_ROOT . '/includes/file.inc';
   }
   foreach ($searchdir as $dir) {
-    $files = array_merge($files, file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth)));
+    $files_to_add = file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth));
+
+    // Duplicate files found in later search directories take precedence over
+    // earlier ones, so we want them to overwrite keys in our resulting
+    // $files array.
+    // The exception to this is if the later file is from a module or theme not
+    // compatible with Drupal core. This may occur during upgrades of Drupal
+    // core when new modules exist in core while older contrib modules with the
+    // same name exist in a directory such as sites/all/modules/.
+    foreach (array_intersect_key($files_to_add, $files) as $key => $file) {
+      // If it has no info file, then we just behave liberally and accept the
+      // new resource on the list for merging.
+      if (file_exists($info_file = dirname($file->uri) . '/' . $file->name . '.info')) {
+        // Get the .info file for the module or theme this file belongs to.
+        $info = drupal_parse_info_file($info_file);
+
+        // If the module or theme is incompatible with Drupal core, remove it
+        // from the array for the current search directory, so it is not
+        // overwritten when merged with the $files array.
+        if (isset($info['core']) && $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
+          unset($files_to_add[$key]);
+        }
+      }
+    }
+    $files = array_merge($files, $files_to_add);
   }
 
   return $files;
Index: modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
===================================================================
RCS file: modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
diff -N modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,8 @@
+; $Id$
+name = "Drupal system listing compatible test"
+description = "Support module for testing the drupal_system_listing function."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = drupal_system_listing_compatible_test.module
+hidden = TRUE
Index: modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
===================================================================
RCS file: modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
diff -N modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,2 @@
+<?php
+// $Id$
Index: modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
===================================================================
RCS file: modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
diff -N modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,8 @@
+; $Id$
+name = "Drupal system listing incompatible test"
+description = "Support module for testing the drupal_system_listing function."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = drupal_system_listing_incompatible_test.module
+hidden = TRUE
Index: modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
===================================================================
RCS file: modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
diff -N modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,2 @@
+<?php
+// $Id$
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.125
diff -u -p -r1.125 common.test
--- modules/simpletest/tests/common.test	9 Sep 2010 20:22:00 -0000	1.125
+++ modules/simpletest/tests/common.test	14 Sep 2010 03:58:29 -0000
@@ -1806,6 +1806,66 @@ class ParseInfoFilesTestCase extends Dru
 }
 
 /**
+ * Tests for the drupal_system_listing() function.
+ */
+class DrupalSystemListingTestCase extends DrupalWebTestCase {
+  /**
+   * Use the testing profile; this is needed for testDirectoryPrecedence().
+   */
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Drupal system listing',
+      'description' => 'Tests the mechanism for scanning system directories in drupal_system_listing().',
+      'group' => 'System',
+    );
+  }
+
+  /**
+   * Test that files in different directories take precedence as expected.
+   */
+  function testDirectoryPrecedence() {
+    // Define the module files we will search for, and the directory precedence
+    // we expect.
+    $expected_directories = array(
+      // When the copy of the module in the profile directory is incompatible
+      // with Drupal core, the copy in the core modules directory takes
+      // precedence.
+      'drupal_system_listing_incompatible_test' => array(
+        'modules/simpletest/tests',
+        'profiles/testing/modules',
+      ),
+      // When both copies of the module are compatible with Drupal core, the
+      // copy in the profile directory takes precedence.
+      'drupal_system_listing_compatible_test' => array(
+        'profiles/testing/modules',
+        'modules/simpletest/tests',
+      ),
+    );
+
+    // This test relies on two versions of the same module existing in
+    // different places in the filesystem. Without that, the test has no
+    // meaning, so assert their presence first.
+    foreach ($expected_directories as $module => $directories) {
+      foreach ($directories as $directory) {
+        $filename = "$directory/$module/$module.module";
+        $this->assertTrue(file_exists(DRUPAL_ROOT . '/' . $filename), t('@filename exists.', array('@filename' => $filename)));
+      }
+    }
+
+    // Now scan the directories and check that the files take precedence as
+    // expected.
+    $files = drupal_system_listing('/\.module$/', 'modules', 'name', 1);
+    foreach ($expected_directories as $module => $directories) {
+      $expected_directory = array_shift($directories);
+      $expected_filename = "$expected_directory/$module/$module.module";
+      $this->assertEqual($files[$module]->uri, $expected_filename, t('Module @module was found at @filename.', array('@module' => $module, '@filename' => $expected_filename)));
+    }
+  }
+}
+
+/**
  * Tests for the format_date() function.
  */
 class FormatDateUnitTest extends DrupalWebTestCase {
Index: profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
===================================================================
RCS file: profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
diff -N profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,8 @@
+; $Id$
+name = "Drupal system listing compatible test"
+description = "Support module for testing the drupal_system_listing function."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = drupal_system_listing_compatible_test.module
+hidden = TRUE
Index: profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
===================================================================
RCS file: profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
diff -N profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,2 @@
+<?php
+// $Id$
Index: profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
===================================================================
RCS file: profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
diff -N profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,11 @@
+; $Id$
+name = "Drupal system listing incompatible test"
+description = "Support module for testing the drupal_system_listing function."
+package = Testing
+version = VERSION
+; This deliberately has the wrong core version, to test that it does not take
+; precedence over the version of the same module that is in the
+; modules/simpletest/tests directory.
+core = 6.x
+files[] = drupal_system_listing_incompatible_test.module
+hidden = TRUE
Index: profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
===================================================================
RCS file: profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
diff -N profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module	14 Sep 2010 03:58:29 -0000
@@ -0,0 +1,2 @@
+<?php
+// $Id$
