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("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',
134 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build'])
137 super(SmbDotConfTests, self).setUp()
138 # create a minimal smb.conf file for testparm
139 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
140 f = open(self.smbconf, 'w')
150 super(SmbDotConfTests, self).tearDown()
151 os.unlink(self.smbconf)
153 def test_unknown(self):
154 topdir = os.path.abspath(samba.source_tree_topdir())
156 documented = set(get_documented_parameters(topdir))
158 self.fail("Unable to load parameters")
159 parameters = set(get_implementation_parameters(topdir))
160 # Filter out parametric options, since we can't find them in the parm
162 documented = set([p for p in documented if not ":" in p])
163 unknown = documented.difference(parameters)
165 self.fail(self._format_message(unknown,
166 "Parameters that are documented but not in the implementation:"))
168 def test_undocumented(self):
169 topdir = os.path.abspath(samba.source_tree_topdir())
171 documented = set(get_documented_parameters(topdir))
173 self.fail("Unable to load parameters")
174 parameters = set(get_implementation_parameters(topdir))
175 undocumented = parameters.difference(documented)
176 if len(undocumented) > 0:
177 self.fail(self._format_message(undocumented,
178 "Parameters that are in the implementation but undocumented:"))
180 def test_default_s3(self):
181 self._test_default(['bin/testparm'])
182 self._set_defaults(['bin/testparm'])
184 # registry shares appears to need sudo
185 self._set_arbitrary(['bin/testparm'],
186 exceptions = ['client lanman auth',
187 'client plaintext auth',
193 def test_default_s4(self):
194 self._test_default(['bin/samba-tool', 'testparm'])
195 self._set_defaults(['bin/samba-tool', 'testparm'])
196 self._set_arbitrary(['bin/samba-tool', 'testparm'])
198 def _test_default(self, program):
199 topdir = os.path.abspath(samba.source_tree_topdir())
201 defaults = set(get_documented_tuples(topdir))
203 self.fail("Unable to load parameters")
204 bindir = os.path.join(topdir, "bin")
208 for tuples in defaults:
209 param, default, context, param_type = tuples
210 if param in self.special_cases:
218 self.fail("%s has no valid context" % param)
219 p = subprocess.Popen(program + ["-s", self.smbconf,
220 "--section-name", section, "--parameter-name", param],
221 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
222 if p[0].upper().strip() != default.upper():
223 if not (p[0].upper().strip() == "" and default == '""'):
224 doc_triple = "%s\n Expected: %s" % (param, default)
225 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
228 self.fail(self._format_message(failset,
229 "Parameters that do not have matching defaults:"))
231 def _set_defaults(self, program):
232 topdir = os.path.abspath(samba.source_tree_topdir())
234 defaults = set(get_documented_tuples(topdir))
236 self.fail("Unable to load parameters")
237 bindir = os.path.join(topdir, "bin")
241 for tuples in defaults:
242 param, default, context, param_type = tuples
244 if param in ['printing']:
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 if param in ['printing', 'copy', 'include', 'log level']:
286 # currently no easy way to set an arbitrary value for these
287 if param_type in ['enum', 'boolean-auto']:
290 if exceptions is not None:
291 if param in exceptions:
300 self.fail("%s has no valid context" % param)
302 value_to_use = arbitrary.get(param_type)
303 if value_to_use is None:
304 self.fail("%s has an invalid type" % param)
306 p = subprocess.Popen(program + ["-s", self.smbconf,
307 "--section-name", section, "--parameter-name", param,
308 "--option", "%s = %s" % (param, value_to_use)],
309 stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=topdir).communicate()
310 if p[0].upper().strip() != value_to_use.upper():
311 # currently no way to distinguish command lists
312 if param_type == 'list':
313 if ", ".join(p[0].upper().strip().split()) == value_to_use.upper():
316 # currently no way to identify octal
317 if param_type == 'integer':
319 if int(value_to_use, 8) == int(p[0].strip(), 8):
324 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
325 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
328 self.fail(self._format_message(failset,
329 "Parameters that were unexpectedly not set:"))