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
30 class TestCase(samba.tests.TestCaseInTempDir):
32 def _format_message(self, parameters, message):
33 parameters = list(parameters)
34 parameters = 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
99 class SmbDotConfTests(TestCase):
101 # defines the cases where the defaults may differ from the documentation
102 special_cases = set(['log level', 'path',
103 'panic action', 'homedir map', 'NIS homedir',
104 'server string', 'netbios name', 'socket options', 'use mmap',
105 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
106 'queuepause command','lpresume command', 'lppause command',
107 'lprm command', 'lpq command', 'print command', 'template homedir',
109 'include system krb5 conf', 'rpc server dynamic port range',
113 super(SmbDotConfTests, self).setUp()
114 # create a minimal smb.conf file for testparm
115 self.smbconf = os.path.join(self.tempdir, "paramtestsmb.conf")
116 f = open(self.smbconf, 'w')
125 self.blankconf = os.path.join(self.tempdir, "emptytestsmb.conf")
126 f = open(self.blankconf, 'w')
132 self.topdir = os.path.abspath(samba.source_tree_topdir())
135 self.documented = set(get_documented_parameters(self.topdir))
137 self.fail("Unable to load documented parameters")
140 self.defaults = set(get_documented_tuples(self.topdir))
142 self.fail("Unable to load parameters")
145 self.defaults_all = set(get_documented_tuples(self.topdir, False))
147 self.fail("Unable to load parameters")
151 super(SmbDotConfTests, self).tearDown()
152 os.unlink(self.smbconf)
153 os.unlink(self.blankconf)
155 def test_default_s3(self):
156 self._test_default(['bin/testparm'])
157 self._set_defaults(['bin/testparm'])
159 # registry shares appears to need sudo
160 self._set_arbitrary(['bin/testparm'],
161 exceptions = ['client lanman auth',
162 'client plaintext auth',
165 'rpc server dynamic port range',
166 'name resolve order'])
167 self._test_empty(['bin/testparm'])
169 def test_default_s4(self):
170 self._test_default(['bin/samba-tool', 'testparm'])
171 self._set_defaults(['bin/samba-tool', 'testparm'])
172 self._set_arbitrary(['bin/samba-tool', 'testparm'],
173 exceptions = ['smb ports',
174 'rpc server dynamic port range',
175 'name resolve order'])
176 self._test_empty(['bin/samba-tool', 'testparm'])
178 def _test_default(self, program):
182 for tuples in self.defaults:
183 param, default, context, param_type = tuples
185 if param in self.special_cases:
187 # bad, bad parametric options - we don't have their default values
196 self.fail("%s has no valid context" % param)
197 p = subprocess.Popen(program + ["-s",
203 stdout=subprocess.PIPE,
204 stderr=subprocess.PIPE,
205 cwd=self.topdir).communicate()
206 result = p[0].decode().upper().strip()
207 if result != default.upper():
208 if not (result == "" and default == '""'):
209 doc_triple = "%s\n Expected: %s" % (param, default)
210 failset.add("%s\n Got: %s" % (doc_triple, result))
213 self.fail(self._format_message(failset,
214 "Parameters that do not have matching defaults:"))
216 def _set_defaults(self, program):
220 for tuples in self.defaults:
221 param, default, context, param_type = tuples
223 if param in ['printing', 'rpc server dynamic port range']:
232 self.fail("%s has no valid context" % param)
233 p = subprocess.Popen(program + ["-s",
240 "%s = %s" % (param, default)],
241 stdout=subprocess.PIPE,
242 stderr=subprocess.PIPE,
243 cwd=self.topdir).communicate()
244 result = p[0].decode().upper().strip()
245 if result != default.upper():
246 if not (result == "" and default == '""'):
247 doc_triple = "%s\n Expected: %s" % (param, default)
248 failset.add("%s\n Got: %s" % (doc_triple, result))
251 self.fail(self._format_message(failset,
252 "Parameters that do not have matching defaults:"))
254 def _set_arbitrary(self, program, exceptions=None):
255 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
256 'boolean-rev': 'yes',
260 'ustring': 'ustring',
261 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
262 opposite_arbitrary = {'string': 'string2', 'boolean': 'no', 'integer': '6',
267 'ustring': 'ustring2',
268 'enum':'', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
273 for tuples in self.defaults_all:
274 param, default, context, param_type = tuples
276 if param in ['printing', 'copy', 'include', 'log level']:
279 # currently no easy way to set an arbitrary value for these
280 if param_type in ['enum', 'boolean-auto']:
283 if exceptions is not None:
284 if param in exceptions:
293 self.fail("%s has no valid context" % param)
295 value_to_use = arbitrary.get(param_type)
296 if value_to_use is None:
297 self.fail("%s has an invalid type" % param)
299 p = subprocess.Popen(program + ["-s",
306 "%s = %s" % (param, value_to_use)],
307 stdout=subprocess.PIPE,
308 stderr=subprocess.PIPE,
309 cwd=self.topdir).communicate()
310 result = p[0].decode().upper().strip()
311 if result != value_to_use.upper():
312 # currently no way to distinguish command lists
313 if param_type == 'list':
314 if ", ".join(result.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()))
328 opposite_value = opposite_arbitrary.get(param_type)
329 tempconf = os.path.join(self.tempdir, "tempsmb.conf")
330 g = open(tempconf, 'w')
332 towrite = section + "\n"
333 towrite += param + " = " + opposite_value
338 p = subprocess.Popen(program + ["-s",
342 "%s = %s" % (param, value_to_use)],
343 stdout=subprocess.PIPE,
344 stderr=subprocess.PIPE,
345 cwd=self.topdir).communicate()
349 # testparm doesn't display a value if they are equivalent
350 if (value_to_use.lower() != opposite_value.lower()):
351 for line in p[0].decode().splitlines():
352 if not line.strip().startswith(param):
355 value_found = line.split("=")[1].upper().strip()
356 if value_found != value_to_use.upper():
357 # currently no way to distinguish command lists
358 if param_type == 'list':
359 if ", ".join(value_found.split()) == value_to_use.upper():
362 # currently no way to identify octal
363 if param_type == 'integer':
365 if int(value_to_use, 8) == int(value_found, 8):
370 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
371 failset.add("%s\n Got: %s" % (doc_triple, value_found))
375 self.fail(self._format_message(failset,
376 "Parameters that were unexpectedly not set:"))
378 def _test_empty(self, program):
379 p = subprocess.Popen(program + ["-s",
381 "--suppress-prompt"],
382 stdout=subprocess.PIPE,
383 stderr=subprocess.PIPE,
384 cwd=self.topdir).communicate()
387 for line in p[0].decode().splitlines():
388 if line.strip().startswith('#'):
390 if line.strip().startswith("idmap config *"):
392 output += line.strip().lower() + '\n'
394 if output.strip() != '[global]' and output.strip() != '[globals]':
395 self.fail("Testparm returned unexpected output on an empty smb.conf.")