1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
4 # Tests for documentation.
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 """Tests for presence of documentation."""
24 from samba.tests import TestSkipped, TestCaseInTempDir
30 import xml.etree.ElementTree as ET
32 class TestCase(samba.tests.TestCaseInTempDir):
34 def _format_message(self, parameters, message):
35 parameters = list(parameters)
37 return message + '\n\n %s' % ('\n '.join(parameters))
39 def get_documented_parameters(sourcedir):
40 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
41 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
42 raise Exception("Unable to find parameters.all.xml")
44 p = open(os.path.join(path, "parameters.all.xml"), 'r')
46 raise Exception("Error opening parameters file")
49 root = ET.fromstring(out)
50 for parameter in root:
51 name = parameter.attrib.get('name')
52 if parameter.attrib.get('removed') == "1":
55 syn = parameter.findall('synonym')
62 def get_implementation_parameters(sourcedir):
63 # Reading entries from source code
64 f = open(os.path.join(sourcedir, "lib/param/param_table.c"), "r")
66 # burn through the preceding lines
69 if l.startswith("static struct parm_struct parm_table"):
72 for l in f.readlines():
73 if re.match("^\s*\}\;\s*$", l):
75 # pull in the param names only
76 if re.match(".*P_SEPARATOR.*", l):
78 m = re.match("\s*\.label\s*=\s*\"(.*)\".*", l)
87 def get_documented_tuples(sourcedir, omit_no_default=True):
88 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
89 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
90 raise Exception("Unable to find parameters.all.xml")
92 p = open(os.path.join(path, "parameters.all.xml"), 'r')
94 raise Exception("Error opening parameters file")
97 root = ET.fromstring(out)
98 for parameter in root:
99 name = parameter.attrib.get("name")
100 param_type = parameter.attrib.get("type")
101 if parameter.attrib.get('removed') == "1":
103 values = parameter.findall("value")
106 if value.attrib.get("type") == "default":
107 defaults.append(value)
110 if len(defaults) == 0:
113 elif len(defaults) > 1:
114 raise Exception("More than one default found for parameter %s" % name)
116 default_text = defaults[0].text
118 if default_text is None:
120 context = parameter.attrib.get("context")
121 yield name, default_text, context, param_type
124 class SmbDotConfTests(TestCase):
126 # defines the cases where the defaults may differ from the documentation
127 special_cases = set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
128 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
129 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
130 'server string', 'netbios name', 'socket options', 'use mmap',
131 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
132 'queuepause command','lpresume command', 'lppause command',
133 'lprm command', 'lpq command', 'print command', 'template homedir'])
136 super(SmbDotConfTests, self).setUp()
137 # create a minimal smb.conf file for testparm
138 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
139 f = open(self.smbconf, 'w')
149 super(SmbDotConfTests, self).tearDown()
150 os.unlink(self.smbconf)
152 def test_unknown(self):
153 topdir = os.path.abspath(samba.source_tree_topdir())
155 documented = set(get_documented_parameters(topdir))
157 self.fail("Unable to load parameters")
158 parameters = set(get_implementation_parameters(topdir))
159 # Filter out parametric options, since we can't find them in the parm
161 documented = set([p for p in documented if not ":" in p])
162 unknown = documented.difference(parameters)
164 self.fail(self._format_message(unknown,
165 "Parameters that are documented but not in the implementation:"))
167 def test_undocumented(self):
168 topdir = os.path.abspath(samba.source_tree_topdir())
170 documented = set(get_documented_parameters(topdir))
172 self.fail("Unable to load parameters")
173 parameters = set(get_implementation_parameters(topdir))
174 undocumented = parameters.difference(documented)
175 if len(undocumented) > 0:
176 self.fail(self._format_message(undocumented,
177 "Parameters that are in the implementation but undocumented:"))
179 def test_default_s3(self):
180 self._test_default(['bin/testparm'])
181 self._set_defaults(['bin/testparm'])
183 # registry shares appears to need sudo
184 self._set_arbitrary(['bin/testparm'],
185 exceptions = ['client lanman auth',
186 'client plaintext auth',
192 def test_default_s4(self):
193 self._test_default(['bin/samba-tool', 'testparm'])
194 self._set_defaults(['bin/samba-tool', 'testparm'])
195 self._set_arbitrary(['bin/samba-tool', 'testparm'])
197 def _test_default(self, program):
198 topdir = os.path.abspath(samba.source_tree_topdir())
200 defaults = set(get_documented_tuples(topdir))
202 self.fail("Unable to load parameters")
203 bindir = os.path.join(topdir, "bin")
207 for tuples in defaults:
208 param, default, context, param_type = tuples
209 if param in self.special_cases:
217 self.fail("%s has no valid context" % param)
218 p = subprocess.Popen(program + ["-s", self.smbconf,
219 "--section-name", section, "--parameter-name", param],
220 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
221 if p[0].upper().strip() != default.upper():
222 if not (p[0].upper().strip() == "" and default == '""'):
223 doc_triple = "%s\n Expected: %s" % (param, default)
224 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
227 self.fail(self._format_message(failset,
228 "Parameters that do not have matching defaults:"))
230 def _set_defaults(self, program):
231 topdir = os.path.abspath(samba.source_tree_topdir())
233 defaults = set(get_documented_tuples(topdir))
235 self.fail("Unable to load parameters")
236 bindir = os.path.join(topdir, "bin")
240 for tuples in defaults:
241 param, default, context, param_type = tuples
243 # temporarily remove parametric options - no dump available in s4
244 if param in ['printing'] or ':' in param:
253 self.fail("%s has no valid context" % param)
254 p = subprocess.Popen(program + ["-s", self.smbconf,
255 "--section-name", section, "--parameter-name", param,
256 "--option", "%s = %s" % (param, default)],
257 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
258 if p[0].upper().strip() != default.upper():
259 if not (p[0].upper().strip() == "" and default == '""'):
260 doc_triple = "%s\n Expected: %s" % (param, default)
261 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
264 self.fail(self._format_message(failset,
265 "Parameters that do not have matching defaults:"))
267 def _set_arbitrary(self, program, exceptions=None):
268 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
269 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
271 topdir = os.path.abspath(samba.source_tree_topdir())
273 defaults = set(get_documented_tuples(topdir, False))
275 self.fail("Unable to load parameters" + e)
276 bindir = os.path.join(topdir, "bin")
280 for tuples in defaults:
281 param, default, context, param_type = tuples
283 # temporarily remove parametric options - no dump available in s4
284 if param in ['printing', 'copy', 'include', 'log level'] or ':' in param:
287 # currently no easy way to set an arbitrary value for these
288 if param_type in ['enum', 'boolean-auto']:
291 if exceptions is not None:
292 if param in exceptions:
301 self.fail("%s has no valid context" % param)
303 value_to_use = arbitrary.get(param_type)
304 if value_to_use is None:
305 self.fail("%s has an invalid type" % param)
307 p = subprocess.Popen(program + ["-s", self.smbconf,
308 "--section-name", section, "--parameter-name", param,
309 "--option", "%s = %s" % (param, value_to_use)],
310 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
311 if p[0].upper().strip() != value_to_use.upper():
312 # currently no way to distinguish command lists
313 if param_type == 'list':
314 if ", ".join(p[0].upper().strip().split()) == value_to_use.upper():
317 # currently no way to identify octal
318 if param_type == 'integer':
320 if int(value_to_use, 8) == int(p[0].strip(), 8):
325 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
326 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
329 self.fail(self._format_message(failset,
330 "Parameters that were unexpectedly not set:"))