diff options
author | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2013-12-20 15:12:50 +0100 |
---|---|---|
committer | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2013-12-20 15:12:50 +0100 |
commit | f24227aae012740653e8708379b7e9c22f938400 (patch) | |
tree | d334468d560e36f3c2cf04d16da386e3e108c05e | |
parent | 7ecac59ebc534e869a772efa6dbd973fb8843f44 (diff) | |
download | pylint-f24227aae012740653e8708379b7e9c22f938400.tar.gz |
[pyreverse] more refactoring to get actually predicable results (take 2)
simplify pyreverse related test code on the way.
-rw-r--r-- | pyreverse/diagrams.py | 12 | ||||
-rw-r--r-- | pyreverse/writer.py | 13 | ||||
-rw-r--r-- | test/data/classes_No_Name.dot | 16 | ||||
-rw-r--r-- | test/data/packages_No_Name.dot | 8 | ||||
-rw-r--r-- | test/unittest_pyreverse_diadefs.py | 25 | ||||
-rw-r--r-- | test/unittest_pyreverse_writer.py | 106 | ||||
-rw-r--r-- | test/utils.py | 98 |
7 files changed, 113 insertions, 165 deletions
diff --git a/pyreverse/diagrams.py b/pyreverse/diagrams.py index a53427a..288ce0f 100644 --- a/pyreverse/diagrams.py +++ b/pyreverse/diagrams.py @@ -1,4 +1,4 @@ -# Copyright (c) 2004-2012 LOGILAB S.A. (Paris, FRANCE). +# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE). # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This program is free software; you can redistribute it and/or modify it under @@ -19,16 +19,8 @@ import astroid from pylint.pyreverse.utils import is_interface, FilterMixIn -def set_counter(value): - """Figure counter (re)set""" - Figure._UID_COUNT = value - -class Figure: +class Figure(object): """base class for counter handling""" - _UID_COUNT = 0 - def __init__(self): - Figure._UID_COUNT += 1 - self.fig_id = Figure._UID_COUNT class Relationship(Figure): """a relation ship from an object in the diagram to another diff --git a/pyreverse/writer.py b/pyreverse/writer.py index b41deb6..446d6d6 100644 --- a/pyreverse/writer.py +++ b/pyreverse/writer.py @@ -45,9 +45,9 @@ class DiagramWriter(object): def write_packages(self, diagram): """write a package diagram""" # sorted to get predictable (hence testable) results - for label, obj in sorted( (self.get_title(obj), obj) - for obj in diagram.modules() ): - self.printer.emit_node(obj.fig_id, label=label, shape='box') + for i, obj in enumerate(sorted(diagram.modules(), key=lambda x: x.title)): + self.printer.emit_node(i, label=self.get_title(obj), shape='box') + obj.fig_id = i # package dependencies for rel in diagram.get_relationships('depends'): self.printer.emit_edge(rel.from_object.fig_id, rel.to_object.fig_id, @@ -56,8 +56,9 @@ class DiagramWriter(object): def write_classes(self, diagram): """write a class diagram""" # sorted to get predictable (hence testable) results - for obj in sorted(diagram.objects, key=lambda x: x.fig_id): - self.printer.emit_node(obj.fig_id, **self.get_values(obj) ) + for i, obj in enumerate(sorted(diagram.objects, key=lambda x: x.title)): + self.printer.emit_node(i, **self.get_values(obj) ) + obj.fig_id = i # inheritance links for rel in diagram.get_relationships('specialization'): self.printer.emit_edge(rel.from_object.fig_id, rel.to_object.fig_id, @@ -116,7 +117,7 @@ class DotWriter(DiagramWriter): The label contains all attributes and methods """ - label = obj.title + label = obj.title if obj.shape == 'interface': label = u"«interface»\\n%s" % label if not self.config.only_classnames: diff --git a/test/data/classes_No_Name.dot b/test/data/classes_No_Name.dot index ba4fa08..e75553e 100644 --- a/test/data/classes_No_Name.dot +++ b/test/data/classes_No_Name.dot @@ -1,12 +1,12 @@ digraph "classes_No_Name" { charset="utf-8" rankdir=BT -"4" [label="{Ancestor|attr : str\lcls_member\l|set_value()\lget_value()\l}", shape="record"]; -"5" [label="{Specialization|relation\ltop : str\lTYPE : str\l|}", shape="record"]; -"8" [label="{«interface»\nInterface|\l|get_value()\lset_value()\l}", shape="record"]; -"9" [label="{DoNothing|\l|}", shape="record"]; -"5" -> "4" [arrowhead="empty", arrowtail="none"]; -"4" -> "8" [arrowhead="empty", arrowtail="node", style="dashed"]; -"9" -> "4" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="cls_member", style="solid"]; -"9" -> "5" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="relation", style="solid"]; +"0" [label="{Ancestor|attr : str\lcls_member\l|set_value()\lget_value()\l}", shape="record"]; +"1" [label="{DoNothing|\l|}", shape="record"]; +"2" [label="{«interface»\nInterface|\l|get_value()\lset_value()\l}", shape="record"]; +"3" [label="{Specialization|relation\ltop : str\lTYPE : str\l|}", shape="record"]; +"3" -> "0" [arrowhead="empty", arrowtail="none"]; +"0" -> "2" [arrowhead="empty", arrowtail="node", style="dashed"]; +"1" -> "0" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="cls_member", style="solid"]; +"1" -> "3" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="relation", style="solid"]; } diff --git a/test/data/packages_No_Name.dot b/test/data/packages_No_Name.dot index dba6ac2..1ceeb72 100644 --- a/test/data/packages_No_Name.dot +++ b/test/data/packages_No_Name.dot @@ -1,8 +1,8 @@ digraph "packages_No_Name" { charset="utf-8" rankdir=BT -"6" [label="data", shape="box"]; -"3" [label="data.clientmodule_test", shape="box"]; -"7" [label="data.suppliermodule_test", shape="box"]; -"3" -> "7" [arrowhead="open", arrowtail="none"]; +"0" [label="data", shape="box"]; +"1" [label="data.clientmodule_test", shape="box"]; +"2" [label="data.suppliermodule_test", shape="box"]; +"1" -> "2" [arrowhead="open", arrowtail="none"]; } diff --git a/test/unittest_pyreverse_diadefs.py b/test/unittest_pyreverse_diadefs.py index ebcf371..a42d73a 100644 --- a/test/unittest_pyreverse_diadefs.py +++ b/test/unittest_pyreverse_diadefs.py @@ -1,4 +1,4 @@ -# Copyright (c) 2000-2004 LOGILAB S.A. (Paris, FRANCE). +# Copyright (c) 2000-2013 LOGILAB S.A. (Paris, FRANCE). # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This program is free software; you can redistribute it and/or modify it under @@ -26,15 +26,10 @@ from astroid.inspector import Linker from pylint.pyreverse.diadefslib import * -from utils import Config +from unittest_pyreverse_writer import Config, get_project -def astroid_wrapper(func, modname): - return func(modname) - -PROJECT = MANAGER.project_from_files(['data'], astroid_wrapper) - -CONFIG = Config() -HANDLER = DiadefsHandler(CONFIG) +PROJECT = get_project('data') +HANDLER = DiadefsHandler(Config()) def _process_classes(classes): """extract class names of a list""" @@ -43,7 +38,7 @@ def _process_classes(classes): def _process_relations(relations): """extract relation indices from a relation list""" result = [] - for rel_type, rels in relations.iteritems(): + for rel_type, rels in relations.iteritems(): for rel in rels: result.append( (rel_type, rel.from_object.title, rel.to_object.title) ) @@ -54,7 +49,7 @@ def _process_relations(relations): class DiaDefGeneratorTC(unittest.TestCase): def test_option_values(self): """test for ancestor, associated and module options""" - handler = DiadefsHandler( Config()) + handler = DiadefsHandler(Config()) df_h = DiaDefGenerator(Linker(PROJECT), handler) cl_config = Config() cl_config.classes = ['Specialization'] @@ -125,15 +120,15 @@ class DefaultDiadefGeneratorTC(unittest.TestCase): different classes possibly in different modules""" # XXX should be catching pyreverse environnement problem but doesn't # pyreverse doesn't extracts the relations but this test ok - project = MANAGER.project_from_files(['data'], astroid_wrapper) - handler = DiadefsHandler( Config() ) + project = get_project('data') + handler = DiadefsHandler(Config()) diadefs = handler.get_diadefs(project, Linker(project, tag=True) ) cd = diadefs[1] relations = _process_relations(cd.relationships) self.assertEqual(relations, self._should_rels) def test_known_values2(self): - project = MANAGER.project_from_files(['data.clientmodule_test'], astroid_wrapper) + project = get_project('data.clientmodule_test') dd = DefaultDiadefGenerator(Linker(project), HANDLER).visit(project) self.assertEqual(len(dd), 1) keys = [d.TYPE for d in dd] @@ -158,7 +153,7 @@ class ClassDiadefGeneratorTC(unittest.TestCase): (True, special), (True, 'data.suppliermodule_test.DoNothing'), ]) - + def test_known_values2(self): HANDLER.config.module_names = False cd = ClassDiadefGenerator(Linker(PROJECT), HANDLER).class_diagram(PROJECT, 'data.clientmodule_test.Specialization') diff --git a/test/unittest_pyreverse_writer.py b/test/unittest_pyreverse_writer.py index 5d4e4e3..b850679 100644 --- a/test/unittest_pyreverse_writer.py +++ b/test/unittest_pyreverse_writer.py @@ -17,39 +17,96 @@ unittest for visitors.diadefs and extensions.diadefslib modules """ -from os.path import abspath, dirname, join -from astroid.inspector import Linker + +import os +import sys +import codecs +from os.path import join, dirname, abspath +from difflib import unified_diff + from logilab.common.testlib import TestCase, unittest_main +from astroid import MANAGER +from astroid.inspector import Linker + from pylint.pyreverse.diadefslib import DefaultDiadefGenerator, DiadefsHandler -from pylint.pyreverse.diagrams import set_counter from pylint.pyreverse.writer import DotWriter - from pylint.pyreverse.utils import get_visibility -from utils import FileTC, build_file_case, get_project, Config -project = get_project(join(dirname(abspath(__file__)), 'data')) -linker = Linker(project) -set_counter(0) -config = Config() +_DEFAULTS = { + 'all_ancestors': None, 'show_associated': None, + 'module_names': None, + 'output_format': 'dot', 'diadefs_file': None, 'quiet': 0, + 'show_ancestors': None, 'classes': (), 'all_associated': None, + 'mode': 'PUB_ONLY', 'show_builtin': False, 'only_classnames': False + } + +class Config(object): + """config object for tests""" + def __init__(self): + for attr, value in _DEFAULTS.items(): + setattr(self, attr, value) + + +def _file_lines(path): + # we don't care about the actual encoding, but python3 forces us to pick one + lines = [line.strip() for line in codecs.open(path, encoding='latin1').readlines() + if (line.find('squeleton generated by ') == -1 and + not line.startswith('__revision__ = "$Id:'))] + return [line for line in lines if line] + +def get_project(module, name=None): + """return a astroid project representation""" + # flush cache + MANAGER._modules_by_name = {} + def _astroid_wrapper(func, modname): + return func(modname) + return MANAGER.project_from_files([module], _astroid_wrapper, + project_name=name) + +CONFIG = Config() + +class DotWriterTC(TestCase): + + @classmethod + def setUpClass(cls): + project = get_project(cls.datadir) + linker = Linker(project) + handler = DiadefsHandler(CONFIG) + dd = DefaultDiadefGenerator(linker, handler).visit(project) + for diagram in dd: + diagram.extract_relationships() + writer = DotWriter(CONFIG) + writer.write(dd) -handler = DiadefsHandler(config) -dd = DefaultDiadefGenerator(linker, handler).visit(project) -for diagram in dd: - diagram.extract_relationships() + @classmethod + def tearDownClass(cls): + for fname in ('packages_No_Name.dot', 'classes_No_Name.dot',): + try: + os.remove(fname) + except: + continue -class DotWriterTC(FileTC): + def _test_same_file(self, generated_file): + expected_file = self.datapath(generated_file) + generated = _file_lines(generated_file) + expected = _file_lines(expected_file) + generated = '\n'.join(generated) + expected = '\n'.join(expected) + files = "\n *** expected : %s, generated : %s \n" % ( + expected_file, generated_file) + self.assertEqual(expected, generated, '%s%s' % ( + files, '\n'.join(line for line in unified_diff( + expected.splitlines(), generated.splitlines() ))) ) + os.remove(generated_file) - generated_files = ('packages_No_Name.dot', 'classes_No_Name.dot',) - sort = False + def test_package_diagram(self): + self._test_same_file('packages_No_Name.dot') - def setUp(self): - FileTC.setUp(self) - writer = DotWriter(config) - writer.write(dd) + def test_class_diagram(self): + self._test_same_file('classes_No_Name.dot') -build_file_case(DotWriterTC) class GetVisibilityTC(TestCase): @@ -62,16 +119,17 @@ class GetVisibilityTC(TestCase): for name in ["__g_", "____dsf", "__23_9"]: got = get_visibility(name) self.assertEqual(got, 'private', - 'got %s instead of private for value %s' % (got, name)) + 'got %s instead of private for value %s' % (got, name)) def test_public(self): self.assertEqual(get_visibility('simple'), 'public') def test_protected(self): - for name in ["_","__", "___", "____", "_____", "___e__", "_nextsimple", "_filter_it_"]: + for name in ["_","__", "___", "____", "_____", "___e__", + "_nextsimple", "_filter_it_"]: got = get_visibility(name) self.assertEqual(got, 'protected', - 'got %s instead of protected for value %s' % (got, name)) + 'got %s instead of protected for value %s' % (got, name)) if __name__ == '__main__': diff --git a/test/utils.py b/test/utils.py deleted file mode 100644 index e7f80e8..0000000 --- a/test/utils.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -"""some pylint test utilities -""" - -import os -import sys -import codecs -from os.path import join, dirname, abspath -from difflib import unified_diff - -from logilab.common.testlib import TestCase -from astroid import MANAGER - - -def _astroid_wrapper(func, modname): - return func(modname) - -def _file_lines(path): - # we don't care about the actual encoding, but python3 forces us to pick one - lines = [line.strip() for line in codecs.open(path, encoding='latin1').readlines() - if (line.find('squeleton generated by ') == -1 and - not line.startswith('__revision__ = "$Id:'))] - return [line for line in lines if line] - -def get_project(module, name=None): - """return a astroid project representation - """ - manager = MANAGER - # flush cache - manager._modules_by_name = {} - return manager.project_from_files([module], _astroid_wrapper, - project_name=name) - -DEFAULTS = {'all_ancestors': None, 'show_associated': None, - 'module_names': None, - 'output_format': 'dot', 'diadefs_file': None, 'quiet': 0, - 'show_ancestors': None, 'classes': (), 'all_associated': None, - 'mode': 'PUB_ONLY', 'show_builtin': False, 'only_classnames': False} - -class Config(object): - """config object for tests""" - def __init__(self): - for attr, value in DEFAULTS.items(): - setattr(self, attr, value) - -class FileTC(TestCase): - """base test case for testing file output""" - - generated_files = () - sort = True - - def setUp(self): - self.expected_files = [join(dirname(abspath(__file__)), 'data', file) - for file in self.generated_files] - - def tearDown(self): - for fname in self.generated_files: - try: - os.remove(fname) - except: - continue - - def _test_same_file(self, index): - generated_file = self.generated_files[index] - expected_file = self.expected_files[index] - generated = _file_lines(generated_file) - expected = _file_lines(expected_file) - if self.sort: - generated = sorted(generated) - expected = sorted(expected) - generated = '\n'.join(generated) - expected = '\n'.join(expected) - files = "\n *** expected : %s, generated : %s \n" % ( - expected_file, generated_file) - self.assertEqual(expected, generated, '%s%s' % ( - files, '\n'.join(line for line in unified_diff( - expected.splitlines(), generated.splitlines() ))) ) - os.remove(generated_file) - - -def build_file_case(filetc): - for i in range(len(filetc.generated_files)): - setattr(filetc, 'test_same_file_%s' %i, - lambda self, index=i: self._test_same_file(index)) |