30c083076ff1a45a24a0de0e23f11d0d5c6c7b2d
[garming/samba-autobuild/.git] / python / samba / tests / usage.py
1 # Unix SMB/CIFS implementation.
2 # Copyright © Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import sys
19 import subprocess
20 from samba.tests import TestCase
21 from unittest import TestSuite
22 import re
23 import stat
24
25 if 'SRCDIR_ABS' in os.environ:
26     BASEDIR = os.environ['SRCDIR_ABS']
27 else:
28     BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
29                                            '../../..'))
30
31 TEST_DIRS = [
32     "bootstrap",
33     "testdata",
34     "ctdb",
35     "dfs_server",
36     "pidl",
37     "auth",
38     "packaging",
39     "python",
40     "include",
41     "nsswitch",
42     "libcli",
43     "coverity",
44     "release-scripts",
45     "testprogs",
46     "bin",
47     "source3",
48     "docs-xml",
49     "buildtools",
50     "file_server",
51     "dynconfig",
52     "source4",
53     "tests",
54     "libds",
55     "selftest",
56     "lib",
57     "script",
58     "traffic",
59     "testsuite",
60     "libgpo",
61     "wintest",
62     "librpc",
63 ]
64
65
66 EXCLUDE_USAGE = {
67     'script/autobuild.py',  # defaults to mount /memdisk/
68     'script/bisect-test.py',
69     'ctdb/utils/etcd/ctdb_etcd_lock',
70     'selftest/filter-subunit',
71     'selftest/format-subunit',
72     'bin/gen_output.py',  # too much output!
73     'source4/scripting/bin/gen_output.py',
74     'lib/ldb/tests/python/index.py',
75     'lib/ldb/tests/python/api.py',
76     'source4/selftest/tests.py',
77     'buildtools/bin/waf',
78     'selftest/tap2subunit',
79     'script/show_test_time',
80     'source4/scripting/bin/subunitrun',
81     'source3/selftest/tests.py',
82     'selftest/tests.py',
83     'python/samba/subunit/run.py',
84     'bin/python/samba/subunit/run.py',
85     'python/samba/tests/dcerpc/raw_protocol.py'
86 }
87
88
89 EXCLUDE_DIRS = {
90     'source3/script/tests',
91     'python/examples',
92     'source4/dsdb/tests/python',
93     'bin/ab',
94     'bin/python/samba/tests',
95     'bin/python/samba/tests/dcerpc',
96 }
97
98
99 def _init_git_file_finder():
100     """Generate a function that quickly answers the question:
101     'is this a git file?'
102     """
103     git_file_cache = set()
104     p = subprocess.run(['git',
105                         '-C', BASEDIR,
106                         'ls-files',
107                         '-z'],
108                        stdout=subprocess.PIPE)
109     if p.returncode == 0:
110         for fn in p.stdout.split(b'\0'):
111             git_file_cache.add(os.path.join(BASEDIR, fn.decode('utf-8')))
112     return git_file_cache.__contains__
113
114
115 is_git_file = _init_git_file_finder()
116
117
118 def python_script_iterator(d=BASEDIR, _cache={}):
119     """Generate an iterator over executable Python scripts. By default it
120     walks the entire source tree.
121     """
122     if d not in _cache:
123         cache = {}
124         _cache[d] = cache
125         pyshebang = re.compile(br'#!.+python').match
126         safename = re.compile(r'\W+').sub
127         for subdir in TEST_DIRS:
128             sd = os.path.join(d, subdir)
129             for root, dirs, files in os.walk(sd, followlinks=False):
130                 for fn in files:
131                     if fn.endswith('~'):
132                         continue
133                     if fn.endswith('.inst'):
134                         continue
135                     ffn = os.path.join(root, fn)
136                     if not (subdir == 'bin' or is_git_file(ffn)):
137                         continue
138
139                     try:
140                         s = os.stat(ffn)
141                     except FileNotFoundError:
142                         continue
143                     if not s.st_mode & stat.S_IXUSR:
144                         continue
145                     try:
146                         f = open(ffn, 'rb')
147                     except OSError as e:
148                         print("could not open %s: %s" % (ffn, e))
149                         continue
150                     line = f.read(40)
151                     f.close()
152                     if not pyshebang(line):
153                         continue
154                     name = safename('_', fn)
155                     while name in cache:
156                         name += '_'
157                     cache[name] = ffn
158
159     return _cache[d].items()
160
161
162 class PythonScriptUsageTests(TestCase):
163     """Python scripts run without arguments should print a usage string,
164         not fail with a traceback.
165         """
166
167     @classmethod
168     def initialise(cls):
169         for name, filename in python_script_iterator():
170             # We add the actual tests after the class definition so we
171             # can give individual names to them, so we can have a
172             # knownfail list.
173             fn = filename.replace(BASEDIR, '').lstrip('/')
174
175             if fn in EXCLUDE_USAGE:
176                 print("skipping %s (EXCLUDE_USAGE)" % filename)
177                 continue
178
179             if os.path.dirname(fn) in EXCLUDE_DIRS:
180                 print("skipping %s (EXCLUDE_DIRS)" % filename)
181                 continue
182
183             def _f(self, filename=filename):
184                 print(filename)
185                 try:
186                     p = subprocess.Popen(['python3', filename],
187                                          stderr=subprocess.PIPE,
188                                          stdout=subprocess.PIPE)
189                     out, err = p.communicate(timeout=5)
190                 except OSError as e:
191                     self.fail("Error: %s" % e)
192                 except subprocess.SubprocessError as e:
193                     self.fail("Subprocess error: %s" % e)
194
195                 err = err.decode('utf-8')
196                 out = out.decode('utf-8')
197                 self.assertNotIn('Traceback', err)
198
199                 self.assertIn('usage', out.lower() + err.lower(),
200                               'stdout:\n%s\nstderr:\n%s' % (out, err))
201
202             setattr(cls, 'test_%s' % name, _f)
203
204
205 PythonScriptUsageTests.initialise()