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 = 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")
153 super(SmbDotConfTests, self).tearDown()
154 os.unlink(self.smbconf)
155 os.unlink(self.blankconf)
157 def test_default_s3(self):
158 self._test_default(['bin/testparm'])
159 self._set_defaults(['bin/testparm'])
161 # registry shares appears to need sudo
162 self._set_arbitrary(['bin/testparm'],
163 exceptions=['client lanman auth',
164 'client plaintext auth',
167 'rpc server dynamic port range',
168 '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):
184 for tuples in self.defaults:
185 param, default, context, param_type = tuples
187 if param in self.special_cases:
189 # bad, bad parametric options - we don't have their default values
198 self.fail("%s has no valid context" % param)
199 p = subprocess.Popen(program + ["-s",
205 stdout=subprocess.PIPE,
206 stderr=subprocess.PIPE,
207 cwd=self.topdir).communicate()
208 result = p[0].decode().upper().strip()
209 if result != default.upper():
210 if not (result == "" and default == '""'):
211 doc_triple = "%s\n Expected: %s" % (param, default)
212 failset.add("%s\n Got: %s" % (doc_triple, result))
215 self.fail(self._format_message(failset,
216 "Parameters that do not have matching defaults:"))
218 def _set_defaults(self, program):
222 for tuples in self.defaults:
223 param, default, context, param_type = tuples
225 if param in ['printing', 'rpc server dynamic port range']:
234 self.fail("%s has no valid context" % param)
235 p = subprocess.Popen(program + ["-s",
242 "%s = %s" % (param, default)],
243 stdout=subprocess.PIPE,
244 stderr=subprocess.PIPE,
245 cwd=self.topdir).communicate()
246 result = p[0].decode().upper().strip()
247 if result != default.upper():
248 if not (result == "" and default == '""'):
249 doc_triple = "%s\n Expected: %s" % (param, default)
250 failset.add("%s\n Got: %s" % (doc_triple, result))
253 self.fail(self._format_message(failset,
254 "Parameters that do not have matching defaults:"))
256 def _set_arbitrary(self, program, exceptions=None):
257 arbitrary = {'string': 'string', 'boolean': 'yes', 'integer': '5',
258 'boolean-rev': 'yes',
262 'ustring': 'ustring',
263 'enum': '', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
264 opposite_arbitrary = {'string': 'string2', 'boolean': 'no', 'integer': '6',
269 'ustring': 'ustring2',
270 'enum': '', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
275 for tuples in self.defaults_all:
276 param, default, context, param_type = tuples
278 if param in ['printing', 'copy', 'include', 'log level']:
281 # currently no easy way to set an arbitrary value for these
282 if param_type in ['enum', 'boolean-auto']:
285 if exceptions is not None:
286 if param in exceptions:
295 self.fail("%s has no valid context" % param)
297 value_to_use = arbitrary.get(param_type)
298 if value_to_use is None:
299 self.fail("%s has an invalid type" % param)
301 p = subprocess.Popen(program + ["-s",
308 "%s = %s" % (param, value_to_use)],
309 stdout=subprocess.PIPE,
310 stderr=subprocess.PIPE,
311 cwd=self.topdir).communicate()
312 result = p[0].decode().upper().strip()
313 if result != value_to_use.upper():
314 # currently no way to distinguish command lists
315 if param_type == 'list':
316 if ", ".join(result.split()) == value_to_use.upper():
319 # currently no way to identify octal
320 if param_type == 'integer':
322 if int(value_to_use, 8) == int(p[0].strip(), 8):
327 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
328 failset.add("%s\n Got: %s" % (doc_triple, p[0].upper().strip()))
330 opposite_value = opposite_arbitrary.get(param_type)
331 tempconf = os.path.join(self.tempdir, "tempsmb.conf")
332 g = open(tempconf, 'w')
334 towrite = section + "\n"
335 towrite += param + " = " + opposite_value
340 p = subprocess.Popen(program + ["-s",
344 "%s = %s" % (param, value_to_use)],
345 stdout=subprocess.PIPE,
346 stderr=subprocess.PIPE,
347 cwd=self.topdir).communicate()
351 # testparm doesn't display a value if they are equivalent
352 if (value_to_use.lower() != opposite_value.lower()):
353 for line in p[0].decode().splitlines():
354 if not line.strip().startswith(param):
357 value_found = line.split("=")[1].upper().strip()
358 if value_found != value_to_use.upper():
359 # currently no way to distinguish command lists
360 if param_type == 'list':
361 if ", ".join(value_found.split()) == value_to_use.upper():
364 # currently no way to identify octal
365 if param_type == 'integer':
367 if int(value_to_use, 8) == int(value_found, 8):
372 doc_triple = "%s\n Expected: %s" % (param, value_to_use)
373 failset.add("%s\n Got: %s" % (doc_triple, value_found))
377 self.fail(self._format_message(failset,
378 "Parameters that were unexpectedly not set:"))
380 def _test_empty(self, program):
381 p = subprocess.Popen(program + ["-s",
383 "--suppress-prompt"],
384 stdout=subprocess.PIPE,
385 stderr=subprocess.PIPE,
386 cwd=self.topdir).communicate()
389 for line in p[0].decode().splitlines():
390 if line.strip().startswith('#'):
392 if line.strip().startswith("idmap config *"):
394 output += line.strip().lower() + '\n'
396 if output.strip() != '[global]' and output.strip() != '[globals]':
397 self.fail("Testparm returned unexpected output on an empty smb.conf.")