Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | ''' |
2 | Test cases for pyclbr.py | |
3 | Nick Mathewson | |
4 | ''' | |
5 | from test.test_support import run_unittest | |
6 | import unittest, sys | |
7 | from types import ClassType, FunctionType, MethodType, BuiltinFunctionType | |
8 | import pyclbr | |
9 | from unittest import TestCase | |
10 | ||
11 | StaticMethodType = type(staticmethod(lambda: None)) | |
12 | ClassMethodType = type(classmethod(lambda c: None)) | |
13 | ||
14 | # This next line triggers an error on old versions of pyclbr. | |
15 | ||
16 | from commands import getstatus | |
17 | ||
18 | # Here we test the python class browser code. | |
19 | # | |
20 | # The main function in this suite, 'testModule', compares the output | |
21 | # of pyclbr with the introspected members of a module. Because pyclbr | |
22 | # is imperfect (as designed), testModule is called with a set of | |
23 | # members to ignore. | |
24 | ||
25 | class PyclbrTest(TestCase): | |
26 | ||
27 | def assertListEq(self, l1, l2, ignore): | |
28 | ''' succeed iff {l1} - {ignore} == {l2} - {ignore} ''' | |
29 | missing = (set(l1) ^ set(l2)) - set(ignore) | |
30 | if missing: | |
31 | print >>sys.stderr, "l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore) | |
32 | self.fail("%r missing" % missing.pop()) | |
33 | ||
34 | def assertHasattr(self, obj, attr, ignore): | |
35 | ''' succeed iff hasattr(obj,attr) or attr in ignore. ''' | |
36 | if attr in ignore: return | |
37 | if not hasattr(obj, attr): print "???", attr | |
38 | self.failUnless(hasattr(obj, attr), | |
39 | 'expected hasattr(%r, %r)' % (obj, attr)) | |
40 | ||
41 | ||
42 | def assertHaskey(self, obj, key, ignore): | |
43 | ''' succeed iff obj.has_key(key) or key in ignore. ''' | |
44 | if key in ignore: return | |
45 | if not obj.has_key(key): | |
46 | print >>sys.stderr, "***",key | |
47 | self.failUnless(obj.has_key(key)) | |
48 | ||
49 | def assertEqualsOrIgnored(self, a, b, ignore): | |
50 | ''' succeed iff a == b or a in ignore or b in ignore ''' | |
51 | if a not in ignore and b not in ignore: | |
52 | self.assertEquals(a, b) | |
53 | ||
54 | def checkModule(self, moduleName, module=None, ignore=()): | |
55 | ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds | |
56 | to the actual module object, module. Any identifiers in | |
57 | ignore are ignored. If no module is provided, the appropriate | |
58 | module is loaded with __import__.''' | |
59 | ||
60 | if module == None: | |
61 | # Import it. | |
62 | # ('<silly>' is to work around an API silliness in __import__) | |
63 | module = __import__(moduleName, globals(), {}, ['<silly>']) | |
64 | ||
65 | dict = pyclbr.readmodule_ex(moduleName) | |
66 | ||
67 | def ismethod(oclass, obj, name): | |
68 | classdict = oclass.__dict__ | |
69 | if isinstance(obj, FunctionType): | |
70 | if not isinstance(classdict[name], StaticMethodType): | |
71 | return False | |
72 | else: | |
73 | if not isinstance(obj, MethodType): | |
74 | return False | |
75 | if obj.im_self is not None: | |
76 | if (not isinstance(classdict[name], ClassMethodType) or | |
77 | obj.im_self is not oclass): | |
78 | return False | |
79 | else: | |
80 | if not isinstance(classdict[name], FunctionType): | |
81 | return False | |
82 | ||
83 | objname = obj.__name__ | |
84 | if objname.startswith("__") and not objname.endswith("__"): | |
85 | objname = "_%s%s" % (obj.im_class.__name__, objname) | |
86 | return objname == name | |
87 | ||
88 | # Make sure the toplevel functions and classes are the same. | |
89 | for name, value in dict.items(): | |
90 | if name in ignore: | |
91 | continue | |
92 | self.assertHasattr(module, name, ignore) | |
93 | py_item = getattr(module, name) | |
94 | if isinstance(value, pyclbr.Function): | |
95 | self.assert_(isinstance(py_item, (FunctionType, BuiltinFunctionType))) | |
96 | else: | |
97 | self.failUnless(isinstance(py_item, (ClassType, type))) | |
98 | real_bases = [base.__name__ for base in py_item.__bases__] | |
99 | pyclbr_bases = [ getattr(base, 'name', base) | |
100 | for base in value.super ] | |
101 | ||
102 | try: | |
103 | self.assertListEq(real_bases, pyclbr_bases, ignore) | |
104 | except: | |
105 | print >>sys.stderr, "class=%s" % py_item | |
106 | raise | |
107 | ||
108 | actualMethods = [] | |
109 | for m in py_item.__dict__.keys(): | |
110 | if ismethod(py_item, getattr(py_item, m), m): | |
111 | actualMethods.append(m) | |
112 | foundMethods = [] | |
113 | for m in value.methods.keys(): | |
114 | if m[:2] == '__' and m[-2:] != '__': | |
115 | foundMethods.append('_'+name+m) | |
116 | else: | |
117 | foundMethods.append(m) | |
118 | ||
119 | try: | |
120 | self.assertListEq(foundMethods, actualMethods, ignore) | |
121 | self.assertEquals(py_item.__module__, value.module) | |
122 | ||
123 | self.assertEqualsOrIgnored(py_item.__name__, value.name, | |
124 | ignore) | |
125 | # can't check file or lineno | |
126 | except: | |
127 | print >>sys.stderr, "class=%s" % py_item | |
128 | raise | |
129 | ||
130 | # Now check for missing stuff. | |
131 | def defined_in(item, module): | |
132 | if isinstance(item, ClassType): | |
133 | return item.__module__ == module.__name__ | |
134 | if isinstance(item, FunctionType): | |
135 | return item.func_globals is module.__dict__ | |
136 | return False | |
137 | for name in dir(module): | |
138 | item = getattr(module, name) | |
139 | if isinstance(item, (ClassType, FunctionType)): | |
140 | if defined_in(item, module): | |
141 | self.assertHaskey(dict, name, ignore) | |
142 | ||
143 | def test_easy(self): | |
144 | self.checkModule('pyclbr') | |
145 | self.checkModule('doctest') | |
146 | self.checkModule('rfc822') | |
147 | self.checkModule('difflib') | |
148 | ||
149 | def test_decorators(self): | |
150 | # XXX: See comment in pyclbr_input.py for a test that would fail | |
151 | # if it were not commented out. | |
152 | # | |
153 | self.checkModule('test.pyclbr_input') | |
154 | ||
155 | def test_others(self): | |
156 | cm = self.checkModule | |
157 | ||
158 | # These were once about the 10 longest modules | |
159 | cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator | |
160 | cm('cgi', ignore=('log',)) # set with = in module | |
161 | cm('mhlib') | |
162 | cm('urllib', ignore=('getproxies_registry', | |
163 | 'open_https', | |
164 | 'getproxies_internetconfig',)) # not on all platforms | |
165 | cm('pickle') | |
166 | cm('aifc', ignore=('openfp',)) # set with = in module | |
167 | cm('Cookie') | |
168 | cm('sre_parse', ignore=('dump',)) # from sre_constants import * | |
169 | cm('pdb') | |
170 | cm('pydoc') | |
171 | ||
172 | # Tests for modules inside packages | |
173 | cm('email.Parser') | |
174 | cm('test.test_pyclbr') | |
175 | ||
176 | ||
177 | def test_main(): | |
178 | run_unittest(PyclbrTest) | |
179 | ||
180 | ||
181 | if __name__ == "__main__": | |
182 | test_main() |