blob: 64c22eb1756a8d8125d2d7b2e3991337c2b98df3 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""Tests for packaging.depgraph """
2import io
3import os
4import re
5import sys
6import packaging.database
7from packaging import depgraph
8
9from packaging.tests import unittest, support
Ezio Melotticad648c2011-05-19 21:25:10 +030010from packaging.tests.support import requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +020011
12
13class DepGraphTestCase(support.LoggingCatcher,
14 unittest.TestCase):
15
16 DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff')
17 DISTROS_EGG = ('bacon', 'banana', 'strawberry', 'cheese')
18 BAD_EGGS = ('nut',)
19
20 EDGE = re.compile(
21 r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]')
22
23 def checkLists(self, l1, l2):
24 """ Compare two lists without taking the order into consideration """
25 self.assertListEqual(sorted(l1), sorted(l2))
26
27 def setUp(self):
28 super(DepGraphTestCase, self).setUp()
29 path = os.path.join(os.path.dirname(__file__), 'fake_dists')
30 path = os.path.abspath(path)
31 sys.path.insert(0, path)
32 self.addCleanup(sys.path.remove, path)
33 self.addCleanup(packaging.database.enable_cache)
34 packaging.database.disable_cache()
35
36 def test_generate_graph(self):
37 dists = []
38 for name in self.DISTROS_DIST:
39 dist = packaging.database.get_distribution(name)
40 self.assertNotEqual(dist, None)
41 dists.append(dist)
42
43 choxie, grammar, towel = dists
44
45 graph = depgraph.generate_graph(dists)
46
47 deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
48 self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
49 self.assertIn(choxie, graph.reverse_list[towel])
50 self.checkLists(graph.missing[choxie], ['nut'])
51
52 deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
53 self.checkLists([], deps)
54 self.checkLists(graph.missing[grammar], ['truffles (>=1.2)'])
55
56 deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
57 self.checkLists([], deps)
58 self.checkLists(graph.missing[towel], ['bacon (<=0.2)'])
59
Ezio Melotticad648c2011-05-19 21:25:10 +030060 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +020061 def test_generate_graph_egg(self):
62 dists = []
63 for name in self.DISTROS_DIST + self.DISTROS_EGG:
64 dist = packaging.database.get_distribution(name, use_egg_info=True)
65 self.assertNotEqual(dist, None)
66 dists.append(dist)
67
68 choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
69
70 graph = depgraph.generate_graph(dists)
71
72 deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
73 self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
74 self.assertIn(choxie, graph.reverse_list[towel])
75 self.checkLists(graph.missing[choxie], ['nut'])
76
77 deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
78 self.checkLists([('bacon', 'truffles (>=1.2)')], deps)
79 self.checkLists(graph.missing[grammar], [])
80 self.assertIn(grammar, graph.reverse_list[bacon])
81
82 deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
83 self.checkLists([('bacon', 'bacon (<=0.2)')], deps)
84 self.checkLists(graph.missing[towel], [])
85 self.assertIn(towel, graph.reverse_list[bacon])
86
87 deps = [(x.name, y) for x, y in graph.adjacency_list[bacon]]
88 self.checkLists([], deps)
89 self.checkLists(graph.missing[bacon], [])
90
91 deps = [(x.name, y) for x, y in graph.adjacency_list[banana]]
92 self.checkLists([('strawberry', 'strawberry (>=0.5)')], deps)
93 self.checkLists(graph.missing[banana], [])
94 self.assertIn(banana, graph.reverse_list[strawberry])
95
96 deps = [(x.name, y) for x, y in graph.adjacency_list[strawberry]]
97 self.checkLists([], deps)
98 self.checkLists(graph.missing[strawberry], [])
99
100 deps = [(x.name, y) for x, y in graph.adjacency_list[cheese]]
101 self.checkLists([], deps)
102 self.checkLists(graph.missing[cheese], [])
103
104 def test_dependent_dists(self):
105 dists = []
106 for name in self.DISTROS_DIST:
107 dist = packaging.database.get_distribution(name)
108 self.assertNotEqual(dist, None)
109 dists.append(dist)
110
111 choxie, grammar, towel = dists
112
113 deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
114 self.checkLists([], deps)
115
116 deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
117 self.checkLists([], deps)
118
119 deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
120 self.checkLists(['choxie'], deps)
121
Ezio Melotticad648c2011-05-19 21:25:10 +0300122 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200123 def test_dependent_dists_egg(self):
124 dists = []
125 for name in self.DISTROS_DIST + self.DISTROS_EGG:
126 dist = packaging.database.get_distribution(name, use_egg_info=True)
127 self.assertNotEqual(dist, None)
128 dists.append(dist)
129
130 choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
131
132 deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
133 self.checkLists([], deps)
134
135 deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
136 self.checkLists([], deps)
137
138 deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
139 self.checkLists(['choxie'], deps)
140
141 deps = [d.name for d in depgraph.dependent_dists(dists, bacon)]
142 self.checkLists(['choxie', 'towel-stuff', 'grammar'], deps)
143
144 deps = [d.name for d in depgraph.dependent_dists(dists, strawberry)]
145 self.checkLists(['banana'], deps)
146
147 deps = [d.name for d in depgraph.dependent_dists(dists, cheese)]
148 self.checkLists([], deps)
149
Ezio Melotticad648c2011-05-19 21:25:10 +0300150 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200151 def test_graph_to_dot(self):
152 expected = (
153 ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
154 ('grammar', 'bacon', 'truffles (>=1.2)'),
155 ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
156 ('banana', 'strawberry', 'strawberry (>=0.5)'),
157 )
158
159 dists = []
160 for name in self.DISTROS_DIST + self.DISTROS_EGG:
161 dist = packaging.database.get_distribution(name, use_egg_info=True)
162 self.assertNotEqual(dist, None)
163 dists.append(dist)
164
165 graph = depgraph.generate_graph(dists)
166 buf = io.StringIO()
167 depgraph.graph_to_dot(graph, buf)
168 buf.seek(0)
169 matches = []
170 lines = buf.readlines()
171 for line in lines[1:-1]: # skip the first and the last lines
172 if line[-1] == '\n':
173 line = line[:-1]
174 match = self.EDGE.match(line.strip())
175 self.assertIsNot(match, None)
176 matches.append(match.groups())
177
178 self.checkLists(matches, expected)
179
Ezio Melotticad648c2011-05-19 21:25:10 +0300180 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200181 def test_graph_disconnected_to_dot(self):
182 dependencies_expected = (
183 ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
184 ('grammar', 'bacon', 'truffles (>=1.2)'),
185 ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
186 ('banana', 'strawberry', 'strawberry (>=0.5)'),
187 )
188 disconnected_expected = ('cheese', 'bacon', 'strawberry')
189
190 dists = []
191 for name in self.DISTROS_DIST + self.DISTROS_EGG:
192 dist = packaging.database.get_distribution(name, use_egg_info=True)
193 self.assertNotEqual(dist, None)
194 dists.append(dist)
195
196 graph = depgraph.generate_graph(dists)
197 buf = io.StringIO()
198 depgraph.graph_to_dot(graph, buf, skip_disconnected=False)
199 buf.seek(0)
200 lines = buf.readlines()
201
202 dependencies_lines = []
203 disconnected_lines = []
204
205 # First sort output lines into dependencies and disconnected lines.
206 # We also skip the attribute lines, and don't include the "{" and "}"
207 # lines.
208 disconnected_active = False
209 for line in lines[1:-1]: # Skip first and last line
210 if line.startswith('subgraph disconnected'):
211 disconnected_active = True
212 continue
213 if line.startswith('}') and disconnected_active:
214 disconnected_active = False
215 continue
216
217 if disconnected_active:
218 # Skip the 'label = "Disconnected"', etc. attribute lines.
219 if ' = ' not in line:
220 disconnected_lines.append(line)
221 else:
222 dependencies_lines.append(line)
223
224 dependencies_matches = []
225 for line in dependencies_lines:
226 if line[-1] == '\n':
227 line = line[:-1]
228 match = self.EDGE.match(line.strip())
229 self.assertIsNot(match, None)
230 dependencies_matches.append(match.groups())
231
232 disconnected_matches = []
233 for line in disconnected_lines:
234 if line[-1] == '\n':
235 line = line[:-1]
236 line = line.strip('"')
237 disconnected_matches.append(line)
238
239 self.checkLists(dependencies_matches, dependencies_expected)
240 self.checkLists(disconnected_matches, disconnected_expected)
241
Ezio Melotticad648c2011-05-19 21:25:10 +0300242 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200243 def test_graph_bad_version_to_dot(self):
244 expected = (
245 ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
246 ('grammar', 'bacon', 'truffles (>=1.2)'),
247 ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
248 ('banana', 'strawberry', 'strawberry (>=0.5)'),
249 )
250
251 dists = []
252 for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
253 dist = packaging.database.get_distribution(name, use_egg_info=True)
254 self.assertNotEqual(dist, None)
255 dists.append(dist)
256
257 graph = depgraph.generate_graph(dists)
258 buf = io.StringIO()
259 depgraph.graph_to_dot(graph, buf)
260 buf.seek(0)
261 matches = []
262 lines = buf.readlines()
263 for line in lines[1:-1]: # skip the first and the last lines
264 if line[-1] == '\n':
265 line = line[:-1]
266 match = self.EDGE.match(line.strip())
267 self.assertIsNot(match, None)
268 matches.append(match.groups())
269
270 self.checkLists(matches, expected)
271
Ezio Melotticad648c2011-05-19 21:25:10 +0300272 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200273 def test_repr(self):
274 dists = []
275 for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
276 dist = packaging.database.get_distribution(name, use_egg_info=True)
277 self.assertNotEqual(dist, None)
278 dists.append(dist)
279
280 graph = depgraph.generate_graph(dists)
281 self.assertTrue(repr(graph))
282
Ezio Melotticad648c2011-05-19 21:25:10 +0300283 @requires_zlib
Tarek Ziade1231a4e2011-05-19 13:07:25 +0200284 def test_main(self):
285 tempout = io.StringIO()
286 old = sys.stdout
287 sys.stdout = tempout
288 oldargv = sys.argv[:]
289 sys.argv[:] = ['script.py']
290 try:
291 try:
292 depgraph.main()
293 except SystemExit:
294 pass
295 finally:
296 sys.stdout = old
297 sys.argv[:] = oldargv
298
299 # checks what main did XXX could do more here
300 tempout.seek(0)
301 res = tempout.read()
302 self.assertIn('towel', res)
303
304
305def test_suite():
306 return unittest.makeSuite(DepGraphTestCase)
307
308if __name__ == "__main__":
309 unittest.main(defaultTest="test_suite")