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 from samba
.tests
import (
33 def get_python_source_files():
34 """Iterate over all Python source files."""
35 library_dir
= os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "..", "..", "samba"))
36 assert os
.path
.isdir(library_dir
), library_dir
38 for root
, dirs
, files
in os
.walk(library_dir
):
41 yield os
.path
.abspath(os
.path
.join(root
, f
))
43 bindir
= os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "..", "..", "..", "..", "bin"))
44 assert os
.path
.isdir(bindir
), bindir
45 for f
in os
.listdir(bindir
):
46 p
= os
.path
.abspath(os
.path
.join(bindir
, f
))
47 if not os
.path
.islink(p
):
49 target
= os
.readlink(p
)
50 if os
.path
.dirname(target
).endswith("scripting/bin"):
52 wafsambadir
= os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "..", "..", "..", "..", "buildtools", "wafsamba"))
53 assert os
.path
.isdir(wafsambadir
), wafsambadir
54 for root
, dirs
, files
in os
.walk(wafsambadir
):
57 yield os
.path
.abspath(os
.path
.join(root
, f
))
60 def get_source_file_contents():
61 """Iterate over the contents of all python files."""
62 for fname
in get_python_source_files():
64 f
= io
.open(fname
, mode
='r', encoding
='utf-8')
66 if e
.errno
== errno
.ENOENT
:
67 warnings
.warn("source file %s broken link?" % fname
)
78 class TestSource(TestCase
):
80 def test_copyright(self
):
81 """Test that all Python files have a valid copyright statement."""
84 copyright_re
= re
.compile('#\\s*copyright.*(?=\n)', re
.I
)
86 for fname
, text
in get_source_file_contents():
87 if fname
.endswith("ms_schema.py"):
88 # FIXME: Not sure who holds copyright on ms_schema.py
90 if "wafsamba" in fname
:
91 # FIXME: No copyright headers in wafsamba
93 if fname
.endswith("python/samba/tests/krb5/kcrypto.py"):
94 # Imported from MIT testing repo
96 if fname
.endswith("python/samba/tests/krb5/rfc4120_pyasn1_generated.py"):
99 match
= copyright_re
.search(text
)
101 incorrect
.append((fname
, 'no copyright line found\n'))
105 "Some files have missing or incorrect copyright"
107 for fname
, comment
in incorrect
:
108 help_text
.append(fname
)
109 help_text
.append((' ' * 4) + comment
)
111 self
.fail('\n'.join(help_text
))
114 """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/>.
133 # This program is free software: you can redistribute it and/or modify
134 # it under the terms of the GNU General Public License as published by
135 # the Free Software Foundation, either version 3 of the License, or
136 # (at your option) any later version.
138 # This program is distributed in the hope that it will be useful,
139 # but WITHOUT ANY WARRANTY; without even the implied warranty of
140 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
141 # GNU General Public License for more details.
143 # You should have received a copy of the GNU General Public License
144 # along with this program. If not, see <https://www.gnu.org/licenses/>.
147 gpl_re
= f
'(?:{"|".join(map(re.escape, gpl_txts))})'
148 gpl_re
= re
.compile(gpl_re
, re
.MULTILINE
)
150 for fname
, text
in get_source_file_contents():
151 if "wafsamba" in fname
:
152 # FIXME: License to wafsamba hasn't been clarified yet
154 if fname
.endswith("/python/samba/subunit/run.py"):
155 # Imported from subunit/testtools, which are dual
158 if fname
.endswith("python/samba/tests/krb5/kcrypto.py"):
159 # Imported from MIT testing repo
161 if fname
.endswith("python/samba/tests/krb5/rfc4120_pyasn1_generated.py"):
164 if not gpl_re
.search(text
):
165 incorrect
.append(fname
)
168 help_text
= ['Some files have missing or incomplete GPL statement',
170 for fname
in incorrect
:
171 help_text
.append((' ' * 4) + fname
)
173 self
.fail('\n'.join(help_text
))
175 def _push_file(self
, dict_
, fname
, line_no
):
176 if fname
not in dict_
:
177 dict_
[fname
] = [line_no
]
179 dict_
[fname
].append(line_no
)
181 def _format_message(self
, dict_
, message
):
182 files
= ["%s: %s" % (f
, ', '.join([str(i
+ 1) for i
in lines
]))
183 for f
, lines
in dict_
.items()]
185 return message
+ '\n\n %s' % ('\n '.join(files
))
187 def _iter_source_files_lines(self
):
188 for fname
, text
in get_source_file_contents():
189 lines
= text
.splitlines(True)
190 for line_no
, line
in enumerate(lines
):
191 yield fname
, line_no
, line
193 def test_no_tabs(self
):
194 """Check that there are no tabs in Python files."""
196 for fname
, line_no
, line
in self
._iter
_source
_files
_lines
():
198 self
._push
_file
(tabs
, fname
, line_no
)
200 self
.fail(self
._format
_message
(tabs
,
201 'Tab characters were found in the following source files.'
202 '\nThey should either be replaced by "\\t" or by spaces:'))
204 def test_unix_newlines(self
):
205 """Check for unix new lines."""
206 illegal_newlines
= {}
207 for fname
, line_no
, line
in self
._iter
_source
_files
_lines
():
208 if not line
.endswith('\n') or line
.endswith('\r\n'):
209 self
._push
_file
(illegal_newlines
, fname
, line_no
)
211 self
.fail(self
._format
_message
(illegal_newlines
,
212 'Non-unix newlines were found in the following source files:'))
214 def test_trailing_whitespace(self
):
215 """Check that there is not trailing whitespace in Python files."""
216 trailing_whitespace
= {}
217 for fname
, line_no
, line
in self
._iter
_source
_files
_lines
():
218 if line
.rstrip("\n").endswith(" "):
219 self
._push
_file
(trailing_whitespace
, fname
, line_no
)
220 if trailing_whitespace
:
221 self
.fail(self
._format
_message
(trailing_whitespace
,
222 'Trailing whitespace was found in the following source files.'))
224 def test_shebang_lines(self
):
225 """Check that files with shebang lines and only those are executable."""
226 files_with_shebang
= {}
227 files_without_shebang
= {}
228 for fname
, line_no
, line
in self
._iter
_source
_files
_lines
():
231 executable
= (os
.stat(fname
).st_mode
& 0o111)
232 has_shebang
= line
.startswith("#!")
233 if has_shebang
and not executable
:
234 self
._push
_file
(files_with_shebang
, fname
, line_no
)
235 if not has_shebang
and executable
:
236 self
._push
_file
(files_without_shebang
, fname
, line_no
)
237 if files_with_shebang
:
238 self
.fail(self
._format
_message
(files_with_shebang
,
239 'Files with shebang line that are not executable:'))
240 if files_without_shebang
:
241 self
.fail(self
._format
_message
(files_without_shebang
,
242 'Files without shebang line that are executable:'))