pymessaging: Add irpc_servers_byname() and irpc_all_servers()
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / tests / source.py
blob2612ae68cf5b4a7aac885a6ea29b625a75d078b2
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2011
4 # Loosely based on bzrlib's test_source.py
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Source level Python tests."""
22 import errno
23 import os
24 import re
25 import warnings
27 import samba
28 samba.ensure_external_module("pep8", "pep8")
29 import pep8
31 from samba.tests import (
32 TestCase,
36 def get_python_source_files():
37 """Iterate over all Python source files."""
38 library_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "samba"))
39 assert os.path.isdir(library_dir), library_dir
41 for root, dirs, files in os.walk(library_dir):
42 for f in files:
43 if f.endswith(".py"):
44 yield os.path.abspath(os.path.join(root, f))
46 bindir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "bin"))
47 assert os.path.isdir(bindir), bindir
48 for f in os.listdir(bindir):
49 p = os.path.abspath(os.path.join(bindir, f))
50 if not os.path.islink(p):
51 continue
52 target = os.readlink(p)
53 if os.path.dirname(target).endswith("scripting/bin"):
54 yield p
55 wafsambadir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "buildtools", "wafsamba"))
56 assert os.path.isdir(wafsambadir), wafsambadir
57 for root, dirs, files in os.walk(wafsambadir):
58 for f in files:
59 if f.endswith(".py"):
60 yield os.path.abspath(os.path.join(root, f))
63 def get_source_file_contents():
64 """Iterate over the contents of all python files."""
65 for fname in get_python_source_files():
66 try:
67 f = open(fname, 'rb')
68 except IOError, e:
69 if e.errno == errno.ENOENT:
70 warnings.warn("source file %s broken link?" % fname)
71 continue
72 else:
73 raise
74 try:
75 text = f.read()
76 finally:
77 f.close()
78 yield fname, text
81 class TestSource(TestCase):
83 def test_copyright(self):
84 """Test that all Python files have a valid copyright statement."""
85 incorrect = []
87 copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
89 for fname, text in get_source_file_contents():
90 if fname.endswith("ms_schema.py"):
91 # FIXME: Not sure who holds copyright on ms_schema.py
92 continue
93 if "wafsamba" in fname:
94 # FIXME: No copyright headers in wafsamba
95 continue
96 match = copyright_re.search(text)
97 if not match:
98 incorrect.append((fname, 'no copyright line found\n'))
100 if incorrect:
101 help_text = ["Some files have missing or incorrect copyright"
102 " statements.",
105 for fname, comment in incorrect:
106 help_text.append(fname)
107 help_text.append((' ' * 4) + comment)
109 self.fail('\n'.join(help_text))
111 def test_gpl(self):
112 """Test that all .py files have a GPL disclaimer."""
113 incorrect = []
115 gpl_txt = """
116 # This program is free software; you can redistribute it and/or modify
117 # it under the terms of the GNU General Public License as published by
118 # the Free Software Foundation; either version 3 of the License, or
119 # (at your option) any later version.
121 # This program is distributed in the hope that it will be useful,
122 # but WITHOUT ANY WARRANTY; without even the implied warranty of
123 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
124 # GNU General Public License for more details.
126 # You should have received a copy of the GNU General Public License
127 # along with this program. If not, see <http://www.gnu.org/licenses/>.
129 gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
131 for fname, text in get_source_file_contents():
132 if "wafsamba" in fname:
133 # FIXME: License to wafsamba hasn't been clarified yet
134 continue
135 if not gpl_re.search(text):
136 incorrect.append(fname)
138 if incorrect:
139 help_text = ['Some files have missing or incomplete GPL statement',
140 gpl_txt]
141 for fname in incorrect:
142 help_text.append((' ' * 4) + fname)
144 self.fail('\n'.join(help_text))
146 def _push_file(self, dict_, fname, line_no):
147 if fname not in dict_:
148 dict_[fname] = [line_no]
149 else:
150 dict_[fname].append(line_no)
152 def _format_message(self, dict_, message):
153 files = ["%s: %s" % (f, ', '.join([str(i + 1) for i in lines]))
154 for f, lines in dict_.items()]
155 files.sort()
156 return message + '\n\n %s' % ('\n '.join(files))
158 def _iter_source_files_lines(self):
159 for fname, text in get_source_file_contents():
160 lines = text.splitlines(True)
161 last_line_no = len(lines) - 1
162 for line_no, line in enumerate(lines):
163 yield fname, line_no, line
165 def test_no_tabs(self):
166 """Check that there are no tabs in Python files."""
167 tabs = {}
168 for fname, line_no, line in self._iter_source_files_lines():
169 if '\t' in line:
170 self._push_file(tabs, fname, line_no)
171 if tabs:
172 self.fail(self._format_message(tabs,
173 'Tab characters were found in the following source files.'
174 '\nThey should either be replaced by "\\t" or by spaces:'))
176 def test_unix_newlines(self):
177 """Check for unix new lines."""
178 illegal_newlines = {}
179 for fname, line_no, line in self._iter_source_files_lines():
180 if not line.endswith('\n') or line.endswith('\r\n'):
181 self._push_file(illegal_newlines, fname, line_no)
182 if illegal_newlines:
183 self.fail(self._format_message(illegal_newlines,
184 'Non-unix newlines were found in the following source files:'))
186 def test_trailing_whitespace(self):
187 """Check that there is not trailing whitespace in Python files."""
188 trailing_whitespace = {}
189 for fname, line_no, line in self._iter_source_files_lines():
190 if line.rstrip("\n").endswith(" "):
191 self._push_file(trailing_whitespace, fname, line_no)
192 if trailing_whitespace:
193 self.fail(self._format_message(trailing_whitespace,
194 'Trailing whitespace was found in the following source files.'))
196 def test_shebang_lines(self):
197 """Check that files with shebang lines and only those are executable."""
198 files_with_shebang = {}
199 files_without_shebang= {}
200 for fname, line_no, line in self._iter_source_files_lines():
201 if line_no >= 1:
202 continue
203 executable = (os.stat(fname).st_mode & 0111)
204 has_shebang = line.startswith("#!")
205 if has_shebang and not executable:
206 self._push_file(files_with_shebang, fname, line_no)
207 if not has_shebang and executable:
208 self._push_file(files_without_shebang, fname, line_no)
209 if files_with_shebang:
210 self.fail(self._format_message(files_with_shebang,
211 'Files with shebang line that are not executable:'))
212 if files_without_shebang:
213 self.fail(self._format_message(files_without_shebang,
214 'Files without shebang line that are executable:'))
216 pep8_ignore = [
217 'E401', # multiple imports on one line
218 'E501', # line too long
219 'E251', # no spaces around keyword / parameter equals
220 'E201', # whitespace after '['
221 'E202', # whitespace before ')'
222 'E302', # expected 2 blank lines, found 1
223 'E231', # missing whitespace after ','
224 'E225', # missing whitespace around operator
225 'E111', # indentation is not a multiple of four
226 'E261', # at least two spaces before inline comment
227 'E702', # multiple statements on one line (semicolon)
228 'E221', # multiple spaces before operator
229 'E303', # too many blank lines (2)
230 'E203', # whitespace before ':'
231 'E222', # multiple spaces after operator
232 'E301', # expected 1 blank line, found 0
233 'E211', # whitespace before '('
234 'E701', # multiple statements on one line (colon)
237 def test_pep8(self):
238 pep8.process_options()
239 pep8.options.repeat = True
240 pep8_errors = []
241 pep8_warnings = []
242 for fname, text in get_source_file_contents():
243 def report_error(line_number, offset, text, check):
244 code = text[:4]
245 if code in self.pep8_ignore:
246 code = 'W' + code[1:]
247 text = code + text[4:]
248 print "%s:%s: %s" % (fname, line_number, text)
249 summary = (fname, line_number, offset, text, check)
250 if code[0] == 'W':
251 pep8_warnings.append(summary)
252 else:
253 pep8_errors.append(summary)
254 lines = text.splitlines(True)
255 checker = pep8.Checker(fname, lines)
256 checker.report_error = report_error
257 checker.check_all()
258 if len(pep8_errors) > 0:
259 d = {}
260 for (fname, line_no, offset, text, check) in pep8_errors:
261 d.setdefault(fname, []).append(line_no - 1)
262 self.fail(self._format_message(d,
263 'There were %d PEP8 errors:' % len(pep8_errors)))