Code

Ticket #17954: 17954.diff

File 17954.diff, 5.6 KB (added by claudep, 2 years ago)

Patch from pull request (+ one wrapped line)

Line 
1diff --git a/django/test/simple.py b/django/test/simple.py
2index de8b527..62e7326 100644
3--- a/django/test/simple.py
4+++ b/django/test/simple.py
5@@ -193,31 +193,35 @@ def reorder_suite(suite, classes):
6 
7 
8 def dependency_ordered(test_databases, dependencies):
9-    """Reorder test_databases into an order that honors the dependencies
10+    """
11+    Reorder test_databases into an order that honors the dependencies
12     described in TEST_DEPENDENCIES.
13     """
14     ordered_test_databases = []
15     resolved_databases = set()
16+
17+    # Maps db signature to dependencies of all it's aliases
18+    dependencies_map = {}
19+
20+    # sanity check - no DB can depend on it's own alias
21+    for sig, (_, aliases) in test_databases:
22+        all_deps = set()
23+        for alias in aliases:
24+            all_deps.update(dependencies.get(alias, []))
25+        if not all_deps.isdisjoint(aliases):
26+            raise ImproperlyConfigured(
27+                "Circular dependency: databases %r depend on each other, "
28+                "but are aliases." % aliases)
29+        dependencies_map[sig] = all_deps
30+
31     while test_databases:
32         changed = False
33         deferred = []
34 
35-        while test_databases:
36-            signature, (db_name, aliases) = test_databases.pop()
37-            dependencies_satisfied = True
38-            for alias in aliases:
39-                if alias in dependencies:
40-                    if all(a in resolved_databases
41-                           for a in dependencies[alias]):
42-                        # all dependencies for this alias are satisfied
43-                        dependencies.pop(alias)
44-                        resolved_databases.add(alias)
45-                    else:
46-                        dependencies_satisfied = False
47-                else:
48-                    resolved_databases.add(alias)
49-
50-            if dependencies_satisfied:
51+        # Try to find a DB that has all it's dependencies met
52+        for signature, (db_name, aliases) in test_databases:
53+            if dependencies_map[signature].issubset(resolved_databases):
54+                resolved_databases.update(aliases)
55                 ordered_test_databases.append((signature, (db_name, aliases)))
56                 changed = True
57             else:
58@@ -282,9 +286,9 @@ class DjangoTestSuiteRunner(object):
59                 # we only need to create the test database once.
60                 item = test_databases.setdefault(
61                     connection.creation.test_db_signature(),
62-                    (connection.settings_dict['NAME'], [])
63+                    (connection.settings_dict['NAME'], set())
64                 )
65-                item[1].append(alias)
66+                item[1].add(alias)
67 
68                 if 'TEST_DEPENDENCIES' in connection.settings_dict:
69                     dependencies[alias] = (
70@@ -297,26 +301,20 @@ class DjangoTestSuiteRunner(object):
71         # Second pass -- actually create the databases.
72         old_names = []
73         mirrors = []
74+
75         for signature, (db_name, aliases) in dependency_ordered(
76             test_databases.items(), dependencies):
77+            test_db_name = None
78             # Actually create the database for the first connection
79-            connection = connections[aliases[0]]
80-            old_names.append((connection, db_name, True))
81-            test_db_name = connection.creation.create_test_db(
82-                self.verbosity, autoclobber=not self.interactive)
83-            for alias in aliases[1:]:
84+
85+            for alias in aliases:
86                 connection = connections[alias]
87-                if db_name:
88-                    old_names.append((connection, db_name, False))
89-                    connection.settings_dict['NAME'] = test_db_name
90+                old_names.append((connection, db_name, True))
91+                if test_db_name is None:
92+                    test_db_name = connection.creation.create_test_db(
93+                            self.verbosity, autoclobber=not self.interactive)
94                 else:
95-                    # If settings_dict['NAME'] isn't defined, we have a backend
96-                    # where the name isn't important -- e.g., SQLite, which
97-                    # uses :memory:. Force create the database instead of
98-                    # assuming it's a duplicate.
99-                    old_names.append((connection, db_name, True))
100-                    connection.creation.create_test_db(
101-                        self.verbosity, autoclobber=not self.interactive)
102+                    connection.settings_dict['NAME'] = test_db_name
103 
104         for alias, mirror_alias in mirrored_aliases.items():
105             mirrors.append((alias, connections[alias].settings_dict['NAME']))
106diff --git a/tests/regressiontests/test_runner/tests.py b/tests/regressiontests/test_runner/tests.py
107index ccb65b4..2af4eb9 100644
108--- a/tests/regressiontests/test_runner/tests.py
109+++ b/tests/regressiontests/test_runner/tests.py
110@@ -110,6 +110,25 @@ class DependencyOrderingTests(unittest.TestCase):
111 
112         self.assertRaises(ImproperlyConfigured, simple.dependency_ordered, raw, dependencies=dependencies)
113 
114+    def test_own_alias_dependency(self):
115+        raw = [
116+            ('s1', ('s1_db', ['alpha', 'bravo']))
117+        ]
118+        dependencies = {
119+            'alpha': ['bravo']
120+        }
121+
122+        with self.assertRaises(ImproperlyConfigured):
123+            simple.dependency_ordered(raw, dependencies=dependencies)
124+
125+        # reordering aliases shouldn't matter
126+        raw = [
127+            ('s1', ('s1_db', ['bravo', 'alpha']))
128+        ]
129+
130+        with self.assertRaises(ImproperlyConfigured):
131+            simple.dependency_ordered(raw, dependencies=dependencies)
132+
133 
134 class MockTestRunner(object):
135     invoked = False