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."""
28 import xml.etree.ElementTree as ET
31 class TestCase(samba.tests.TestCaseInTempDir):
33 def _format_message(self, parameters, message):
34 parameters = list(parameters)
35 parameters = list(map(str, parameters))
37 return message + '\n\n %s' % ('\n '.join(parameters))
40 def get_documented_parameters(sourcedir):
41 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
42 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
43 raise Exception("Unable to find parameters.all.xml")
45 p = open(os.path.join(path, "parameters.all.xml"), 'r')
47 raise Exception("Error opening parameters file")
50 root = ET.fromstring(out)
51 for parameter in root:
52 name = parameter.attrib.get('name')
53 if parameter.attrib.get('removed') == "1":
56 syn = parameter.findall('synonym')
63 def get_documented_tuples(sourcedir, omit_no_default=True):
64 path = os.path.join(sourcedir, "bin", "default", "docs-xml", "smbdotconf")
65 if not os.path.exists(os.path.join(path, "parameters.all.xml")):
66 raise Exception("Unable to find parameters.all.xml")
68 p = open(os.path.join(path, "parameters.all.xml"), 'r')
70 raise Exception("Error opening parameters file")
73 root = ET.fromstring(out)
74 for parameter in root:
75 name = parameter.attrib.get("name")
76 param_type = parameter.attrib.get("type")
77 if parameter.attrib.get('removed') == "1":
79 values = parameter.findall("value")
82 if value.attrib.get("type") == "default":
83 defaults.append(value)
86 if len(defaults) == 0:
89 elif len(defaults) > 1:
90 raise Exception("More than one default found for parameter %s" % name)
92 default_text = defaults[0].text
94 if default_text is None:
96 context = parameter.attrib.get("context")
97 yield name, default_text, context, param_type
101 class SmbDotConfTests(TestCase):
103 # defines the cases where the defaults may differ from the documentation
104 special_cases = set(['log level', 'path',
105 'panic action', 'homedir map', 'NIS homedir',
106 'server string', 'netbios name', 'socket options', 'use mmap',
107 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
108 'queuepause command', 'lpresume command', 'lppause command',
109 'lprm command', 'lpq command', 'print command', 'template homedir',
111 'include system krb5 conf', 'rpc server dynamic port range',
115 super(SmbDotConfTests, self).setUp()
116 # create a minimal smb.conf file for testparm
117 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
118 f = open(self.smbconf, 'w')
127 self.blankconf = os.path.join(self.tempdir, "emptytestsmb.conf")
128 f = open(self.blankconf, 'w')
134 self.topdir = os.path.abspath(samba.source_tree_topdir())
137 self.documented = set(get_documented_parameters(self.topdir))
139 self.fail("Unable to load documented parameters")
142 self.defaults = set(get_documented_tuples(self.topdir))
144 self.fail("Unable to load parameters")
147 self.defaults_all = set(get_documented_tuples(self.topdir, False))
149 self.fail("Unable to load parameters")
152 super(SmbDotConfTests, self).tearDown()
153 os.unlink(self.smbconf)
154 os.unlink(self.blankconf)
156 def test_default_s3(self):
157 self._test_default(['bin/testparm'])
158 self._set_defaults(['bin/testparm'])
160 # registry shares appears to need sudo
161 self._set_arbitrary(['bin/testparm'],
162 exceptions = ['client lanman auth',
163 'client plaintext auth',
166 'rpc server dynamic port range',
167 'name resolve order',
169 self._test_empty(['bin/testparm'])
171 def test_default_s4(self):
172 self._test_default(['bin/samba-tool', 'testparm'])
173 self._set_defaults(['bin/samba-tool', 'testparm'])
174 self._set_arbitrary(['bin/samba-tool', 'testparm'],
175 exceptions=['smb ports',
176 'rpc server dynamic port range',
177 'name resolve order'])
178 self._test_empty(['bin/samba-tool', 'testparm'])
180 def _test_default(self, program):
182 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
183 program = [os.environ["PYTHON"]] + program
188 for tuples in self.defaults:
189 param, default, context, param_type = tuples
191 if param in self.special_cases:
193 # bad, bad parametric options - we don't have their default values
202 self.fail("%s has no valid context" % param)
203 p = subprocess.Popen(program + ["-s",
209 stdout=subprocess.PIPE,
210 stderr=subprocess.PIPE,
211 cwd=self.topdir).communicate()
212 result = p[0].decode().upper().strip()
213 if result != default.upper():
214 if not (result == "" and default == '""'):
215 doc_triple = "%s\n Expected: %s" % (param, default)
216 failset.add("%s\n Got: %s" % (doc_triple, result))
219 self.fail(self._format_message(failset,
220 "Parameters that do not have matching defaults:"))
222 def _set_defaults(self, program):
224 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
225 program = [os.environ["PYTHON"]] + program
230 for tuples in self.defaults:
231 param, default, context, param_type = tuples
233 if param in ['printing', 'rpc server dynamic port range']:
242 self.fail("%s has no valid context" % param)
243 p = subprocess.Popen(program + ["-s",
250 "%s = %s" % (param, default)],
251 stdout=subprocess.PIPE,
252 stderr=subprocess.PIPE,
253 cwd=self.topdir).communicate()
254 result = p[0].decode().upper().strip()
255 if result != default.upper():
256 if not (result == "" and default == '""'):
257 doc_triple = "%s\n Expected: %s" % (param, default)
258 failset.add("%s\n Got: %s" % (doc_triple, result))
261 self.fail(self._format_message(failset,
262 "Parameters that do not have matching defaults:"))
264 def _set_arbitrary(self, program, exceptions=None):
266 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
267 program = [os.environ["PYTHON"]] + program
269 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
270 'boolean-rev': 'yes',
274 'ustring': 'ustring',
275 'enum': '', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
276 opposite_arbitrary = {'string': 'string2', 'boolean': 'no', 'integer': '6',
281 'ustring': 'ustring2',
282 'enum': '', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
287 for tuples in self.defaults_all:
288 param, default, context, param_type = tuples
290 if param in ['printing', 'copy', 'include', 'log level']:
293 # currently no easy way to set an arbitrary value for these
294 if param_type in ['enum', 'boolean-auto']:
297 if exceptions is not None:
298 if param in exceptions:
307 self.fail("%s has no valid context" % param)
309 value_to_use = arbitrary.get(param_type)
310 if value_to_use is None:
311 self.fail("%s has an invalid type" % param)
313 p = subprocess.Popen(program + ["-s",
320 "%s = %s" % (param, value_to_use)],
321 stdout=subprocess.PIPE,
322 stderr=subprocess.PIPE,
323 cwd=self.topdir).communicate()
324 result = p[0].decode().upper().strip()
325 if result != value_to_use.upper():
326 # currently no way to distinguish command lists
327 if param_type == 'list':
328 if ", ".join(result.split()) == value_to_use.upper():
331 # currently no way to identify octal
332 if param_type == 'integer':
334 if int(value_to_use, 8) == int(p[0].strip(), 8):
339 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
340 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
342 opposite_value = opposite_arbitrary.get(param_type)
343 tempconf = os.path.join(self.tempdir, "tempsmb.conf")
344 g = open(tempconf, 'w')
346 towrite = section + "\n"
347 towrite += param + " = " + opposite_value
352 p = subprocess.Popen(program + ["-s",
356 "%s = %s" % (param, value_to_use)],
357 stdout=subprocess.PIPE,
358 stderr=subprocess.PIPE,
359 cwd=self.topdir).communicate()
363 # testparm doesn't display a value if they are equivalent
364 if (value_to_use.lower() != opposite_value.lower()):
365 for line in p[0].decode().splitlines():
366 if not line.strip().startswith(param):
369 value_found = line.split("=")[1].upper().strip()
370 if value_found != value_to_use.upper():
371 # currently no way to distinguish command lists
372 if param_type == 'list':
373 if ", ".join(value_found.split()) == value_to_use.upper():
376 # currently no way to identify octal
377 if param_type == 'integer':
379 if int(value_to_use, 8) == int(value_found, 8):
384 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
385 failset.add("%s\n Got: %s" % (doc_triple, value_found))
388 self.fail(self._format_message(failset,
389 "Parameters that were unexpectedly not set:"))
391 def _test_empty(self, program):
393 if program[0] == 'bin/samba-tool' and os.getenv("PYTHON", None):
394 program = [os.environ["PYTHON"]] + program
396 p = subprocess.Popen(program + ["-s",
398 "--suppress-prompt"],
399 stdout=subprocess.PIPE,
400 stderr=subprocess.PIPE,
401 cwd=self.topdir).communicate()
404 for line in p[0].decode().splitlines():
405 if line.strip().startswith('#'):
407 if line.strip().startswith("idmap config *"):
409 output += line.strip().lower() + '\n'
411 if output.strip() != '[global]' and output.strip() != '[globals]':
412 self.fail("Testparm returned unexpected output on an empty smb.conf.")