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."""
28 samba
.ensure_external_module("pep8", "pep8")
31 from samba
.tests
import (
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
):
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
):
52 target
= os
.readlink(p
)
53 if os
.path
.dirname(target
).endswith("scripting/bin"):
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
):
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():
69 if e
.errno
== errno
.ENOENT
:
70 warnings
.warn("source file %s broken link?" % fname
)
81 class TestSource(TestCase
):
83 def test_copyright(self
):
84 """Test that all Python files have a valid copyright statement."""
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
93 if "wafsamba" in fname
:
94 # FIXME: No copyright headers in wafsamba
96 match
= copyright_re
.search(text
)
98 incorrect
.append((fname
, 'no copyright line found\n'))
101 help_text
= ["Some files have missing or incorrect copyright"
105 for fname
, comment
in incorrect
:
106 help_text
.append(fname
)
107 help_text
.append((' ' * 4) + comment
)
109 self
.fail('\n'.join(help_text
))
112 """Test that all .py files have a GPL disclaimer."""
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
135 if not gpl_re
.search(text
):
136 incorrect
.append(fname
)
139 help_text
= ['Some files have missing or incomplete GPL statement',
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
]
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()]
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."""
168 for fname
, line_no
, line
in self
._iter
_source
_files
_lines
():
170 self
._push
_file
(tabs
, fname
, line_no
)
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
)
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
():
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:'))
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)
238 pep8
.process_options()
239 pep8
.options
.repeat
= True
242 for fname
, text
in get_source_file_contents():
243 def report_error(line_number
, offset
, text
, check
):
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
)
251 pep8_warnings
.append(summary
)
253 pep8_errors
.append(summary
)
254 lines
= text
.splitlines(True)
255 checker
= pep8
.Checker(fname
, lines
)
256 checker
.report_error
= report_error
258 if len(pep8_errors
) > 0:
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
)))