Add samba.tests.source, which checks Python files for copyright lines, license header...
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / tests / source.py
blobc67634912de2692c9d2718c73062d915570266f2
1 #!/usr/bin/env python
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."""
24 import errno
25 import os
26 import re
27 import warnings
29 from samba.tests import (
30 TestCase,
34 def get_python_source_files():
35 """Iterate over all Python source files."""
36 library_dir = os.path.join(os.path.dirname(__file__), "..", "..", "samba")
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.join(os.path.dirname(__file__), "..", "..", "..", "..", "bin")
44 for f in os.listdir(bindir):
45 p = os.path.abspath(os.path.join(bindir, f))
46 if not os.path.islink(p):
47 continue
48 target = os.readlink(p)
49 if os.path.dirname(target).endswith("scripting/bin"):
50 yield p
53 def get_source_file_contents():
54 """Iterate over the contents of all python files."""
55 for fname in get_python_source_files():
56 try:
57 f = open(fname, 'rb')
58 except IOError, e:
59 if e.errno == errno.ENOENT:
60 warnings.warn("source file %s broken link?" % fname)
61 continue
62 else:
63 raise
64 try:
65 text = f.read()
66 finally:
67 f.close()
68 yield fname, text
71 class TestSource(TestCase):
73 def test_copyright(self):
74 """Test that all Python files have a valid copyright statement."""
75 incorrect = []
77 copyright_re = re.compile('#\\s*copyright.*(?=\n)', re.I)
79 for fname, text in get_source_file_contents():
80 if fname.endswith("ms_schema.py"):
81 # FIXME: Not sure who holds copyright on ms_schema.py
82 continue
83 match = copyright_re.search(text)
84 if not match:
85 incorrect.append((fname, 'no copyright line found\n'))
87 if incorrect:
88 help_text = ["Some files have missing or incorrect copyright"
89 " statements.",
90 "",
92 for fname, comment in incorrect:
93 help_text.append(fname)
94 help_text.append((' ' * 4) + comment)
96 self.fail('\n'.join(help_text))
98 def test_gpl(self):
99 """Test that all .py files have a GPL disclaimer."""
100 incorrect = []
102 gpl_txt = """
103 # This program is free software; you can redistribute it and/or modify
104 # it under the terms of the GNU General Public License as published by
105 # the Free Software Foundation; either version 3 of the License, or
106 # (at your option) any later version.
108 # This program is distributed in the hope that it will be useful,
109 # but WITHOUT ANY WARRANTY; without even the implied warranty of
110 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
111 # GNU General Public License for more details.
113 # You should have received a copy of the GNU General Public License
114 # along with this program. If not, see <http://www.gnu.org/licenses/>.
116 gpl_re = re.compile(re.escape(gpl_txt), re.MULTILINE)
118 for fname, text in get_source_file_contents():
119 if not gpl_re.search(text):
120 incorrect.append(fname)
122 if incorrect:
123 help_text = ['Some files have missing or incomplete GPL statement',
124 gpl_txt]
125 for fname in incorrect:
126 help_text.append((' ' * 4) + fname)
128 self.fail('\n'.join(help_text))
130 def _push_file(self, dict_, fname, line_no):
131 if fname not in dict_:
132 dict_[fname] = [line_no]
133 else:
134 dict_[fname].append(line_no)
136 def _format_message(self, dict_, message):
137 files = ["%s: %s" % (f, ', '.join([str(i + 1) for i in lines]))
138 for f, lines in dict_.items()]
139 files.sort()
140 return message + '\n\n %s' % ('\n '.join(files))
142 def _iter_source_files_lines(self):
143 for fname, text in get_source_file_contents():
144 lines = text.splitlines(True)
145 last_line_no = len(lines) - 1
146 for line_no, line in enumerate(lines):
147 yield fname, line_no, line
149 def test_no_tabs(self):
150 """Check that there are no tabs in Python files."""
151 tabs = {}
152 for fname, line_no, line in self._iter_source_files_lines():
153 if '\t' in line:
154 self._push_file(tabs, fname, line_no)
155 if tabs:
156 self.fail(self._format_message(tabs,
157 'Tab characters were found in the following source files.'
158 '\nThey should either be replaced by "\\t" or by spaces:'))
160 def test_unix_newlines(self):
161 """Check for unix new lines."""
162 illegal_newlines = {}
163 for fname, line_no, line in self._iter_source_files_lines():
164 if not line.endswith('\n') or line.endswith('\r\n'):
165 self._push_file(illegal_newlines, fname, line_no)
166 if illegal_newlines:
167 self.fail(self._format_message(illegal_newlines,
168 'Non-unix newlines were found in the following source files:'))