3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2011
6 # Loosely based on bzrlib's test_source.py
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 """Source level Python tests."""
30 samba.ensure_external_module("pep8", "pep8")
33 from samba.tests import (
39 def get_python_source_files():
40 """Iterate over all Python source files."""
41 library_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "samba"))
42 assert os.path.isdir(library_dir), library_dir
44 for root, dirs, files in os.walk(library_dir):
47 yield os.path.abspath(os.path.join(root, f))
49 bindir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "bin"))
50 assert os.path.isdir(bindir), bindir
51 for f in os.listdir(bindir):
52 p = os.path.abspath(os.path.join(bindir, f))
53 if not os.path.islink(p):
55 target = os.readlink(p)
56 if os.path.dirname(target).endswith("scripting/bin"):
58 wafsambadir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "buildtools", "wafsamba"))
59 assert os.path.isdir(wafsambadir), wafsambadir
60 for root, dirs, files in os.walk(wafsambadir):
63 yield os.path.abspath(os.path.join(root, f))
66 def get_source_file_contents():
67 """Iterate over the contents of all python files."""
68 for fname in get_python_source_files():
72 if e.errno == errno.ENOENT:
73 warnings.warn("source file %s broken link?" % fname)
84 class TestSource(TestCase):
86 def test_copyright(self):
87 """Test that all Python files have a valid copyright statement."""
90 copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
92 for fname, text in get_source_file_contents():
93 if fname.endswith("ms_schema.py"):
94 # FIXME: Not sure who holds copyright on ms_schema.py
96 if "wafsamba" in fname:
97 # FIXME: No copyright headers in wafsamba
99 match = copyright_re.search(text)
101 incorrect.append((fname, 'no copyright line found\n'))
104 help_text = ["Some files have missing or incorrect copyright"
108 for fname, comment in incorrect:
109 help_text.append(fname)
110 help_text.append((' ' * 4) + comment)
112 self.fail('\n'.join(help_text))
115 """Test that all .py files have a GPL disclaimer."""
119 # This program is free software; you can redistribute it and/or modify
120 # it under the terms of the GNU General Public License as published by
121 # the Free Software Foundation; either version 3 of the License, or
122 # (at your option) any later version.
124 # This program is distributed in the hope that it will be useful,
125 # but WITHOUT ANY WARRANTY; without even the implied warranty of
126 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
127 # GNU General Public License for more details.
129 # You should have received a copy of the GNU General Public License
130 # along with this program. If not, see <http://www.gnu.org/licenses/>.
132 gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
134 for fname, text in get_source_file_contents():
135 if "wafsamba" in fname:
136 # FIXME: License to wafsamba hasn't been clarified yet
138 if not gpl_re.search(text):
139 incorrect.append(fname)
142 help_text = ['Some files have missing or incomplete GPL statement',
144 for fname in incorrect:
145 help_text.append((' ' * 4) + fname)
147 self.fail('\n'.join(help_text))
149 def _push_file(self, dict_, fname, line_no):
150 if fname not in dict_:
151 dict_[fname] = [line_no]
153 dict_[fname].append(line_no)
155 def _format_message(self, dict_, message):
156 files = ["%s: %s" % (f, ', '.join([str(i + 1) for i in lines]))
157 for f, lines in dict_.items()]
159 return message + '\n\n %s' % ('\n '.join(files))
161 def _iter_source_files_lines(self):
162 for fname, text in get_source_file_contents():
163 lines = text.splitlines(True)
164 last_line_no = len(lines) - 1
165 for line_no, line in enumerate(lines):
166 yield fname, line_no, line
168 def test_no_tabs(self):
169 """Check that there are no tabs in Python files."""
171 for fname, line_no, line in self._iter_source_files_lines():
173 self._push_file(tabs, fname, line_no)
175 self.fail(self._format_message(tabs,
176 'Tab characters were found in the following source files.'
177 '\nThey should either be replaced by "\\t" or by spaces:'))
179 def test_unix_newlines(self):
180 """Check for unix new lines."""
181 illegal_newlines = {}
182 for fname, line_no, line in self._iter_source_files_lines():
183 if not line.endswith('\n') or line.endswith('\r\n'):
184 self._push_file(illegal_newlines, fname, line_no)
186 self.fail(self._format_message(illegal_newlines,
187 'Non-unix newlines were found in the following source files:'))
190 'E401', # multiple imports on one line
191 'E501', # line too long
192 'E251', # no spaces around keyword / parameter equals
193 'E201', # whitespace after '['
194 'E202', # whitespace before ')'
195 'E302', # expected 2 blank lines, found 1
196 'E231', # missing whitespace after ','
197 'E225', # missing whitespace around operator
198 'E111', # indentation is not a multiple of four
199 'E261', # at least two spaces before inline comment
200 'E702', # multiple statements on one line (semicolon)
201 'E221', # multiple spaces before operator
202 'E303', # too many blank lines (2)
203 'E203', # whitespace before ':'
204 'E222', # multiple spaces after operator
205 'E301', # expected 1 blank line, found 0
206 'E211', # whitespace before '('
207 'E701', # multiple statements on one line (colon)
211 pep8.process_options()
212 pep8.options.repeat = True
215 for fname, text in get_source_file_contents():
216 def report_error(line_number, offset, text, check):
218 if code in self.pep8_ignore:
219 code = 'W' + code[1:]
220 text = code + text[4:]
221 print "%s:%s: %s" % (fname, line_number, text)
222 summary = (fname, line_number, offset, text, check)
224 pep8_warnings.append(summary)
226 pep8_errors.append(summary)
227 lines = text.splitlines(True)
228 checker = pep8.Checker(fname, lines)
229 checker.report_error = report_error
231 if len(pep8_errors) > 0:
232 self.fail('there were %d pep8 errors' % len(pep8_errors))