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."""
27 import xml.etree.ElementTree as ET
30 class TestCase(samba.tests.TestCaseInTempDir):
32 def _format_message(self, parameters, message):
33 parameters = list(parameters)
34 parameters = list(map(str, parameters))
36 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_documented_tuples(sourcedir, omit_no_default=True):
63 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
64 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
65 raise Exception("Unable to find parameters.all.xml")
67 p = open(os.path.join(path, "parameters.all.xml"), 'r')
69 raise Exception("Error opening parameters file")
72 root = ET.fromstring(out)
73 for parameter in root:
74 name = parameter.attrib.get("name")
75 param_type = parameter.attrib.get("type")
76 if parameter.attrib.get('removed') == "1":
78 values = parameter.findall("value")
81 if value.attrib.get("type") == "default":
82 defaults.append(value)
85 if len(defaults) == 0:
88 elif len(defaults) > 1:
89 raise Exception("More than one default found for parameter %s" % name)
91 default_text = defaults[0].text
93 if default_text is None:
95 context = parameter.attrib.get("context")
96 yield name, default_text, context, param_type
100 class SmbDotConfTests(TestCase):
102 # defines the cases where the defaults may differ from the documentation
103 special_cases = set([
116 'queueresume command',
117 'queuepause command',
125 'include system krb5 conf',
126 'rpc server dynamic port range',
128 'smbd max async dosmode',
132 super(SmbDotConfTests, self).setUp()
133 # create a minimal smb.conf file for testparm
134 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
135 f = open(self.smbconf, 'w')
144 self.blankconf = os.path.join(self.tempdir, "emptytestsmb.conf")
145 f = open(self.blankconf, 'w')
151 self.topdir = os.path.abspath(samba.source_tree_topdir())
154 self.documented = set(get_documented_parameters(self.topdir))
156 self.fail("Unable to load documented parameters")
159 self.defaults = set(get_documented_tuples(self.topdir))
161 self.fail("Unable to load parameters")
164 self.defaults_all = set(get_documented_tuples(self.topdir, False))
166 self.fail("Unable to load parameters")
169 super(SmbDotConfTests, self).tearDown()
170 os.unlink(self.smbconf)
171 os.unlink(self.blankconf)
173 def test_default_s3(self):
174 self._test_default(['bin/testparm'])
175 self._set_defaults(['bin/testparm'])
177 # registry shares appears to need sudo
178 self._set_arbitrary(['bin/testparm'],
179 exceptions = ['client lanman auth',
180 'client plaintext auth',
183 'rpc server dynamic port range',
184 'name resolve order',
186 self._test_empty(['bin/testparm'])
188 def test_default_s4(self):
189 self._test_default(['bin/samba-tool', 'testparm'])
190 self._set_defaults(['bin/samba-tool', 'testparm'])
191 self._set_arbitrary(['bin/samba-tool', 'testparm'],
192 exceptions=['smb ports',
193 'rpc server dynamic port range',
194 'name resolve order'])
195 self._test_empty(['bin/samba-tool', 'testparm'])
197 def _test_default(self, program):
199 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
200 program = [os.environ["PYTHON"]] + program
204 for tuples in self.defaults:
205 param, default, context, param_type = tuples
207 if param in self.special_cases:
209 # bad, bad parametric options - we don't have their default values
218 self.fail("%s has no valid context" % param)
219 p = subprocess.Popen(program + ["-s",
225 stdout=subprocess.PIPE,
226 stderr=subprocess.PIPE,
227 cwd=self.topdir).communicate()
228 result = p[0].decode().upper().strip()
229 if result != default.upper():
230 if not (result == "" and default == '""'):
231 doc_triple = "%s\n Expected: %s" % (param, default)
232 failset.add("%s\n Got: %s" % (doc_triple, result))
235 self.fail(self._format_message(failset,
236 "Parameters that do not have matching defaults:"))
238 def _set_defaults(self, program):
240 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
241 program = [os.environ["PYTHON"]] + program
245 for tuples in self.defaults:
246 param, default, context, param_type = tuples
250 'rpc server dynamic port range',
251 'smbd max async dosmode',
254 if param in exceptions:
263 self.fail("%s has no valid context" % param)
264 p = subprocess.Popen(program + ["-s",
271 "%s = %s" % (param, default)],
272 stdout=subprocess.PIPE,
273 stderr=subprocess.PIPE,
274 cwd=self.topdir).communicate()
275 result = p[0].decode().upper().strip()
276 if result != default.upper():
277 if not (result == "" and default == '""'):
278 doc_triple = "%s\n Expected: %s" % (param, default)
279 failset.add("%s\n Got: %s" % (doc_triple, result))
282 self.fail(self._format_message(failset,
283 "Parameters that do not have matching defaults:"))
285 def _set_arbitrary(self, program, exceptions=None):
287 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
288 program = [os.environ["PYTHON"]] + program
290 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
291 'boolean-rev': 'yes',
295 'ustring': 'ustring',
296 'enum': '', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
297 opposite_arbitrary = {'string': 'string2', 'boolean': 'no', 'integer': '6',
302 'ustring': 'ustring2',
303 'enum': '', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
307 for tuples in self.defaults_all:
308 param, default, context, param_type = tuples
310 if param in ['printing', 'copy', 'include', 'log level']:
313 # currently no easy way to set an arbitrary value for these
314 if param_type in ['enum', 'boolean-auto']:
317 if exceptions is not None:
318 if param in exceptions:
327 self.fail("%s has no valid context" % param)
329 value_to_use = arbitrary.get(param_type)
330 if value_to_use is None:
331 self.fail("%s has an invalid type" % param)
333 p = subprocess.Popen(program + ["-s",
340 "%s = %s" % (param, value_to_use)],
341 stdout=subprocess.PIPE,
342 stderr=subprocess.PIPE,
343 cwd=self.topdir).communicate()
344 result = p[0].decode().upper().strip()
345 if result != value_to_use.upper():
346 # currently no way to distinguish command lists
347 if param_type == 'list':
348 if ", ".join(result.split()) == value_to_use.upper():
351 # currently no way to identify octal
352 if param_type == 'integer':
354 if int(value_to_use, 8) == int(p[0].strip(), 8):
359 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
360 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
362 opposite_value = opposite_arbitrary.get(param_type)
363 tempconf = os.path.join(self.tempdir, "tempsmb.conf")
364 g = open(tempconf, 'w')
366 towrite = section + "\n"
367 towrite += param + " = " + opposite_value
372 p = subprocess.Popen(program + ["-s",
376 "%s = %s" % (param, value_to_use)],
377 stdout=subprocess.PIPE,
378 stderr=subprocess.PIPE,
379 cwd=self.topdir).communicate()
383 # testparm doesn't display a value if they are equivalent
384 if (value_to_use.lower() != opposite_value.lower()):
385 for line in p[0].decode().splitlines():
386 if not line.strip().startswith(param):
389 value_found = line.split("=")[1].upper().strip()
390 if value_found != value_to_use.upper():
391 # currently no way to distinguish command lists
392 if param_type == 'list':
393 if ", ".join(value_found.split()) == value_to_use.upper():
396 # currently no way to identify octal
397 if param_type == 'integer':
399 if int(value_to_use, 8) == int(value_found, 8):
404 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
405 failset.add("%s\n Got: %s" % (doc_triple, value_found))
408 self.fail(self._format_message(failset,
409 "Parameters that were unexpectedly not set:"))
411 def _test_empty(self, program):
413 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
414 program = [os.environ["PYTHON"]] + program
416 p = subprocess.Popen(program + ["-s",
418 "--suppress-prompt"],
419 stdout=subprocess.PIPE,
420 stderr=subprocess.PIPE,
421 cwd=self.topdir).communicate()
424 for line in p[0].decode().splitlines():
425 if line.strip().startswith('#'):
427 if line.strip().startswith("idmap config *"):
429 output += line.strip().lower() + '\n'
431 if output.strip() != '[global]' and output.strip() != '[globals]':
432 self.fail("Testparm returned unexpected output on an empty smb.conf.")