cpython/Lib/packaging/tests/test_depgraph.py

302 lines
11 KiB
Python

"""Tests for packaging.depgraph """
import io
import os
import re
import sys
import packaging.database
from packaging import depgraph
from packaging.tests import unittest, support
class DepGraphTestCase(support.LoggingCatcher,
unittest.TestCase):
DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff')
DISTROS_EGG = ('bacon', 'banana', 'strawberry', 'cheese')
BAD_EGGS = ('nut',)
EDGE = re.compile(
r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]')
def checkLists(self, l1, l2):
""" Compare two lists without taking the order into consideration """
self.assertListEqual(sorted(l1), sorted(l2))
def setUp(self):
super(DepGraphTestCase, self).setUp()
path = os.path.join(os.path.dirname(__file__), 'fake_dists')
path = os.path.abspath(path)
sys.path.insert(0, path)
self.addCleanup(sys.path.remove, path)
self.addCleanup(packaging.database.enable_cache)
packaging.database.disable_cache()
def test_generate_graph(self):
dists = []
for name in self.DISTROS_DIST:
dist = packaging.database.get_distribution(name)
self.assertNotEqual(dist, None)
dists.append(dist)
choxie, grammar, towel = dists
graph = depgraph.generate_graph(dists)
deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
self.assertIn(choxie, graph.reverse_list[towel])
self.checkLists(graph.missing[choxie], ['nut'])
deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
self.checkLists([], deps)
self.checkLists(graph.missing[grammar], ['truffles (>=1.2)'])
deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
self.checkLists([], deps)
self.checkLists(graph.missing[towel], ['bacon (<=0.2)'])
def test_generate_graph_egg(self):
dists = []
for name in self.DISTROS_DIST + self.DISTROS_EGG:
dist = packaging.database.get_distribution(name, use_egg_info=True)
self.assertNotEqual(dist, None)
dists.append(dist)
choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
graph = depgraph.generate_graph(dists)
deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
self.assertIn(choxie, graph.reverse_list[towel])
self.checkLists(graph.missing[choxie], ['nut'])
deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
self.checkLists([('bacon', 'truffles (>=1.2)')], deps)
self.checkLists(graph.missing[grammar], [])
self.assertIn(grammar, graph.reverse_list[bacon])
deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
self.checkLists([('bacon', 'bacon (<=0.2)')], deps)
self.checkLists(graph.missing[towel], [])
self.assertIn(towel, graph.reverse_list[bacon])
deps = [(x.name, y) for x, y in graph.adjacency_list[bacon]]
self.checkLists([], deps)
self.checkLists(graph.missing[bacon], [])
deps = [(x.name, y) for x, y in graph.adjacency_list[banana]]
self.checkLists([('strawberry', 'strawberry (>=0.5)')], deps)
self.checkLists(graph.missing[banana], [])
self.assertIn(banana, graph.reverse_list[strawberry])
deps = [(x.name, y) for x, y in graph.adjacency_list[strawberry]]
self.checkLists([], deps)
self.checkLists(graph.missing[strawberry], [])
deps = [(x.name, y) for x, y in graph.adjacency_list[cheese]]
self.checkLists([], deps)
self.checkLists(graph.missing[cheese], [])
def test_dependent_dists(self):
dists = []
for name in self.DISTROS_DIST:
dist = packaging.database.get_distribution(name)
self.assertNotEqual(dist, None)
dists.append(dist)
choxie, grammar, towel = dists
deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
self.checkLists([], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
self.checkLists([], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
self.checkLists(['choxie'], deps)
def test_dependent_dists_egg(self):
dists = []
for name in self.DISTROS_DIST + self.DISTROS_EGG:
dist = packaging.database.get_distribution(name, use_egg_info=True)
self.assertNotEqual(dist, None)
dists.append(dist)
choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
self.checkLists([], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
self.checkLists([], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
self.checkLists(['choxie'], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, bacon)]
self.checkLists(['choxie', 'towel-stuff', 'grammar'], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, strawberry)]
self.checkLists(['banana'], deps)
deps = [d.name for d in depgraph.dependent_dists(dists, cheese)]
self.checkLists([], deps)
def test_graph_to_dot(self):
expected = (
('towel-stuff', 'bacon', 'bacon (<=0.2)'),
('grammar', 'bacon', 'truffles (>=1.2)'),
('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
('banana', 'strawberry', 'strawberry (>=0.5)'),
)
dists = []
for name in self.DISTROS_DIST + self.DISTROS_EGG:
dist = packaging.database.get_distribution(name, use_egg_info=True)
self.assertNotEqual(dist, None)
dists.append(dist)
graph = depgraph.generate_graph(dists)
buf = io.StringIO()
depgraph.graph_to_dot(graph, buf)
buf.seek(0)
matches = []
lines = buf.readlines()
for line in lines[1:-1]: # skip the first and the last lines
if line[-1] == '\n':
line = line[:-1]
match = self.EDGE.match(line.strip())
self.assertIsNot(match, None)
matches.append(match.groups())
self.checkLists(matches, expected)
def test_graph_disconnected_to_dot(self):
dependencies_expected = (
('towel-stuff', 'bacon', 'bacon (<=0.2)'),
('grammar', 'bacon', 'truffles (>=1.2)'),
('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
('banana', 'strawberry', 'strawberry (>=0.5)'),
)
disconnected_expected = ('cheese', 'bacon', 'strawberry')
dists = []
for name in self.DISTROS_DIST + self.DISTROS_EGG:
dist = packaging.database.get_distribution(name, use_egg_info=True)
self.assertNotEqual(dist, None)
dists.append(dist)
graph = depgraph.generate_graph(dists)
buf = io.StringIO()
depgraph.graph_to_dot(graph, buf, skip_disconnected=False)
buf.seek(0)
lines = buf.readlines()
dependencies_lines = []
disconnected_lines = []
# First sort output lines into dependencies and disconnected lines.
# We also skip the attribute lines, and don't include the "{" and "}"
# lines.
disconnected_active = False
for line in lines[1:-1]: # Skip first and last line
if line.startswith('subgraph disconnected'):
disconnected_active = True
continue
if line.startswith('}') and disconnected_active:
disconnected_active = False
continue
if disconnected_active:
# Skip the 'label = "Disconnected"', etc. attribute lines.
if ' = ' not in line:
disconnected_lines.append(line)
else:
dependencies_lines.append(line)
dependencies_matches = []
for line in dependencies_lines:
if line[-1] == '\n':
line = line[:-1]
match = self.EDGE.match(line.strip())
self.assertIsNot(match, None)
dependencies_matches.append(match.groups())
disconnected_matches = []
for line in disconnected_lines:
if line[-1] == '\n':
line = line[:-1]
line = line.strip('"')
disconnected_matches.append(line)
self.checkLists(dependencies_matches, dependencies_expected)
self.checkLists(disconnected_matches, disconnected_expected)
def test_graph_bad_version_to_dot(self):
expected = (
('towel-stuff', 'bacon', 'bacon (<=0.2)'),
('grammar', 'bacon', 'truffles (>=1.2)'),
('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
('banana', 'strawberry', 'strawberry (>=0.5)'),
)
dists = []
for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
dist = packaging.database.get_distribution(name, use_egg_info=True)
self.assertNotEqual(dist, None)
dists.append(dist)
graph = depgraph.generate_graph(dists)
buf = io.StringIO()
depgraph.graph_to_dot(graph, buf)
buf.seek(0)
matches = []
lines = buf.readlines()
for line in lines[1:-1]: # skip the first and the last lines
if line[-1] == '\n':
line = line[:-1]
match = self.EDGE.match(line.strip())
self.assertIsNot(match, None)
matches.append(match.groups())
self.checkLists(matches, expected)
def test_repr(self):
dists = []
for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
dist = packaging.database.get_distribution(name, use_egg_info=True)
self.assertNotEqual(dist, None)
dists.append(dist)
graph = depgraph.generate_graph(dists)
self.assertTrue(repr(graph))
def test_main(self):
tempout = io.StringIO()
old = sys.stdout
sys.stdout = tempout
oldargv = sys.argv[:]
sys.argv[:] = ['script.py']
try:
try:
depgraph.main()
except SystemExit:
pass
finally:
sys.stdout = old
sys.argv[:] = oldargv
# checks what main did XXX could do more here
tempout.seek(0)
res = tempout.read()
self.assertIn('towel', res)
def test_suite():
return unittest.makeSuite(DepGraphTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")