from contextlib import contextmanager import sys from tempfile import NamedTemporaryFile try: from unittest import mock except ImportError: import mock import pytest import pipdeptree as p # Tests for DAG classes def mock_pkgs(simple_graph): for node, children in simple_graph.items(): nk, nv = node p = mock.Mock(key=nk, project_name=nk, version=nv) as_req = mock.Mock(key=nk, project_name=nk, specs=[('==', nv)]) p.as_requirement = mock.Mock(return_value=as_req) reqs = [] for child in children: ck, cv = child r = mock.Mock(key=ck, project_name=ck, specs=cv) reqs.append(r) p.requires = mock.Mock(return_value=reqs) yield p def mock_PackageDAG(simple_graph): pkgs = list(mock_pkgs(simple_graph)) return p.PackageDAG.from_pkgs(pkgs) # util for comparing tree contents with a simple graph def dag_to_dict(g): return {k.key: [v.key for v in vs] for k, vs in g._obj.items()} def sort_map_values(m): return {k: sorted(v) for k, v in m.items()} t = mock_PackageDAG({ ('a', '3.4.0'): [('b', [('>=', '2.0.0')]), ('c', [('>=', '5.7.1')])], ('b', '2.3.1'): [('d', [('>=', '2.30'), ('<', '2.42')])], ('c', '5.10.0'): [('d', [('>=', '2.30')]), ('e', [('>=', '0.12.1')])], ('d', '2.35'): [('e', [('>=', '0.9.0')])], ('e', '0.12.1'): [], ('f', '3.1'): [('b', [('>=', '2.1.0')])], ('g', '6.8.3rc1'): [('e', [('>=', '0.9.0')]), ('f', [('>=', '3.0.0')])] }) def test_PackageDAG__get_node_as_parent(): assert 'b' == t.get_node_as_parent('b').key assert 'c' == t.get_node_as_parent('c').key def test_PackageDAG_filter(): # When both show_only and exclude are not specified, same tree # object is returned assert t.filter(None, None) is t # when show_only is specified g1 = dag_to_dict(t.filter(set(['a', 'd']), None)) expected = {'a': ['b', 'c'], 'b': ['d'], 'c': ['d', 'e'], 'd': ['e'], 'e': []} assert expected == g1 # when exclude is specified g2 = dag_to_dict(t.filter(None, ['d'])) expected = {'a': ['b', 'c'], 'b': [], 'c': ['e'], 'e': [], 'f': ['b'], 'g': ['e', 'f']} assert expected == g2 # when both show_only and exclude are specified g3 = dag_to_dict(t.filter(set(['a', 'g']), set(['d', 'e']))) expected = {'a': ['b', 'c'], 'b': [], 'c': [], 'f': ['b'], 'g': ['f']} assert expected == g3 # when conflicting values in show_only and exclude, AssertionError # is raised with pytest.raises(AssertionError): dag_to_dict(t.filter(set(['d']), set(['D', 'e']))) def test_PackageDAG_reverse(): t1 = t.reverse() expected = {'a': [], 'b': ['a', 'f'], 'c': ['a'], 'd': ['b', 'c'], 'e': ['c', 'd', 'g'], 'f': ['g'], 'g': []} assert isinstance(t1, p.ReversedPackageDAG) assert sort_map_values(expected) == sort_map_values(dag_to_dict(t1)) assert all([isinstance(k, p.ReqPackage) for k in t1.keys()]) assert all([isinstance(v, p.DistPackage) for v in p.flatten(t1.values())]) # testing reversal of ReversedPackageDAG instance expected = {'a': ['b', 'c'], 'b': ['d'], 'c': ['d', 'e'], 'd': ['e'], 'e': [], 'f': ['b'], 'g': ['e', 'f']} t2 = t1.reverse() assert isinstance(t2, p.PackageDAG) assert sort_map_values(expected) == sort_map_values(dag_to_dict(t2)) assert all([isinstance(k, p.DistPackage) for k in t2.keys()]) assert all([isinstance(v, p.ReqPackage) for v in p.flatten(t2.values())]) # Tests for Package classes # # Note: For all render methods, we are only testing for frozen=False # as mocks with frozen=True are a lot more complicated def test_DistPackage__render_as_root(): foo = mock.Mock(key='foo', project_name='foo', version='20.4.1') dp = p.DistPackage(foo) is_frozen = False assert 'foo==20.4.1' == dp.render_as_root(is_frozen) def test_DistPackage__render_as_branch(): foo = mock.Mock(key='foo', project_name='foo', version='20.4.1') bar = mock.Mock(key='bar', project_name='bar', version='4.1.0') bar_req = mock.Mock(key='bar', project_name='bar', version='4.1.0', specs=[('>=', '4.0')]) rp = p.ReqPackage(bar_req, dist=bar) dp = p.DistPackage(foo).as_parent_of(rp) is_frozen = False assert 'foo==20.4.1 [requires: bar>=4.0]' == dp.render_as_branch(is_frozen) def test_DistPackage__as_parent_of(): foo = mock.Mock(key='foo', project_name='foo', version='20.4.1') dp = p.DistPackage(foo) assert dp.req is None bar = mock.Mock(key='bar', project_name='bar', version='4.1.0') bar_req = mock.Mock(key='bar', project_name='bar', version='4.1.0', specs=[('>=', '4.0')]) rp = p.ReqPackage(bar_req, dist=bar) dp1 = dp.as_parent_of(rp) assert dp1._obj == dp._obj assert dp1.req is rp dp2 = dp.as_parent_of(None) assert dp2 is dp def test_DistPackage__as_dict(): foo = mock.Mock(key='foo', project_name='foo', version='1.3.2b1') dp = p.DistPackage(foo) result = dp.as_dict() expected = {'key': 'foo', 'package_name': 'foo', 'installed_version': '1.3.2b1'} assert expected == result def test_ReqPackage__render_as_root(): bar = mock.Mock(key='bar', project_name='bar', version='4.1.0') bar_req = mock.Mock(key='bar', project_name='bar', version='4.1.0', specs=[('>=', '4.0')]) rp = p.ReqPackage(bar_req, dist=bar) is_frozen = False assert 'bar==4.1.0' == rp.render_as_root(is_frozen) def test_ReqPackage__render_as_branch(): bar = mock.Mock(key='bar', project_name='bar', version='4.1.0') bar_req = mock.Mock(key='bar', project_name='bar', version='4.1.0', specs=[('>=', '4.0')]) rp = p.ReqPackage(bar_req, dist=bar) is_frozen = False assert 'bar [required: >=4.0, installed: 4.1.0]' == rp.render_as_branch(is_frozen) def test_ReqPackage__as_dict(): bar = mock.Mock(key='bar', project_name='bar', version='4.1.0') bar_req = mock.Mock(key='bar', project_name='bar', version='4.1.0', specs=[('>=', '4.0')]) rp = p.ReqPackage(bar_req, dist=bar) result = rp.as_dict() expected = {'key': 'bar', 'package_name': 'bar', 'installed_version': '4.1.0', 'required_version': '>=4.0'} assert expected == result # Tests for graph outputs def test_render_pdf(): output = p.dump_graphviz(t, output_format='pdf') @contextmanager def redirect_stdout(new_target): old_target, sys.stdout = sys.stdout, new_target try: yield new_target finally: sys.stdout = old_target with NamedTemporaryFile(delete=True) as f: with redirect_stdout(f): p.print_graphviz(output) rf = open(f.name, 'rb') assert b'%PDF' == rf.read()[:4] # @NOTE: rf is not closed to avoid "bad filedescriptor" error def test_render_svg(capsys): output = p.dump_graphviz(t, output_format='svg') p.print_graphviz(output) out, _ = capsys.readouterr() assert out.startswith('') # Tests for the argparse parser def test_parser_default(): parser = p.get_parser() args = parser.parse_args([]) assert not args.json assert args.output_format is None def test_parser_j(): parser = p.get_parser() args = parser.parse_args(['-j']) assert args.json assert args.output_format is None def test_parser_json(): parser = p.get_parser() args = parser.parse_args(['--json']) assert args.json assert args.output_format is None def test_parser_json_tree(): parser = p.get_parser() args = parser.parse_args(['--json-tree']) assert args.json_tree assert not args.json assert args.output_format is None def test_parser_pdf(): parser = p.get_parser() args = parser.parse_args(['--graph-output', 'pdf']) assert args.output_format == 'pdf' assert not args.json def test_parser_svg(): parser = p.get_parser() args = parser.parse_args(['--graph-output', 'svg']) assert args.output_format == 'svg' assert not args.json