smbd: use metadata_fsp(fsp) in copy_access_posix_acl() for SMB_VFS_SYS_ACL_SET_FD
[Samba.git] / python / samba / tests / source.py
blobc3ff8e0fc0b3e2beb2e8e7b573af28626be99c2f
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 io
23 import errno
24 import os
25 import re
26 import warnings
28 from samba.tests import (
29 TestCase,
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):
39 for f in files:
40 if f.endswith(".py"):
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):
48 continue
49 target = os.readlink(p)
50 if os.path.dirname(target).endswith("scripting/bin"):
51 yield p
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):
55 for f in files:
56 if f.endswith(".py"):
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():
63 try:
64 f = io.open(fname, mode='r', encoding='utf-8')
65 except IOError as e:
66 if e.errno == errno.ENOENT:
67 warnings.warn("source file %s broken link?" % fname)
68 continue
69 else:
70 raise
71 try:
72 text = f.read()
73 finally:
74 f.close()
75 yield fname, text
78 class TestSource(TestCase):
80 def test_copyright(self):
81 """Test that all Python files have a valid copyright statement."""
82 incorrect = []
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
89 continue
90 if "wafsamba" in fname:
91 # FIXME: No copyright headers in wafsamba
92 continue
93 if fname.endswith("python/samba/tests/krb5/kcrypto.py"):
94 # Imported from MIT testing repo
95 continue
96 if fname.endswith("python/samba/tests/krb5/rfc4120_pyasn1_generated.py"):
97 # Autogenerated
98 continue
99 match = copyright_re.search(text)
100 if not match:
101 incorrect.append((fname, 'no copyright line found\n'))
103 if incorrect:
104 help_text = [
105 "Some files have missing or incorrect copyright"
106 " statements.", ""]
107 for fname, comment in incorrect:
108 help_text.append(fname)
109 help_text.append((' ' * 4) + comment)
111 self.fail('\n'.join(help_text))
113 def test_gpl(self):
114 """Test that all .py files have a GPL disclaimer."""
115 incorrect = []
117 gpl_txts = [
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/>.
131 """,
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/>.
145 """,
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
153 continue
154 if fname.endswith("/python/samba/subunit/run.py"):
155 # Imported from subunit/testtools, which are dual
156 # Apache2/BSD-3.
157 continue
158 if fname.endswith("python/samba/tests/krb5/kcrypto.py"):
159 # Imported from MIT testing repo
160 continue
161 if fname.endswith("python/samba/tests/krb5/rfc4120_pyasn1_generated.py"):
162 # Autogenerated
163 continue
164 if not gpl_re.search(text):
165 incorrect.append(fname)
167 if incorrect:
168 help_text = ['Some files have missing or incomplete GPL statement',
169 gpl_txts[-1]]
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]
178 else:
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()]
184 files.sort()
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."""
195 tabs = {}
196 for fname, line_no, line in self._iter_source_files_lines():
197 if '\t' in line:
198 self._push_file(tabs, fname, line_no)
199 if tabs:
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)
210 if illegal_newlines:
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():
229 if line_no >= 1:
230 continue
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:'))