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_param_table_full(sourcedir
, filename
="lib/param/param_table_static.c"):
63 # Reading entries from source code
64 f
= open(os
.path
.join(sourcedir
, filename
), "r")
66 # burn through the preceding lines
69 if l
.startswith("struct parm_struct parm_table"):
72 for l
in f
.readlines():
74 if re
.match("^\s*\}\;\s*$", l
):
75 # end of the table reached
78 if re
.match("^\s*\{\s*$", l
):
89 if re
.match("^\s*\},\s*$", l
):
91 yield _label
, _type
, _class
, _offset
, _special
, _enum_list
, _flags
94 m
= re
.match("^\s*\.([^\s]+)\s*=\s*(.*),.*", l
)
101 if attrib
== "label":
103 elif attrib
== "type":
105 elif attrib
== "p_class":
107 elif attrib
== "offset":
109 elif attrib
== "special":
111 elif attrib
== "enum_list":
113 elif attrib
== "flags":
120 def get_documented_tuples(sourcedir
, omit_no_default
=True):
121 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
122 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
123 raise Exception("Unable to find parameters.all.xml")
125 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
127 raise Exception("Error opening parameters file")
130 root
= ET
.fromstring(out
)
131 for parameter
in root
:
132 name
= parameter
.attrib
.get("name")
133 param_type
= parameter
.attrib
.get("type")
134 if parameter
.attrib
.get('removed') == "1":
136 values
= parameter
.findall("value")
139 if value
.attrib
.get("type") == "default":
140 defaults
.append(value
)
143 if len(defaults
) == 0:
146 elif len(defaults
) > 1:
147 raise Exception("More than one default found for parameter %s" % name
)
149 default_text
= defaults
[0].text
151 if default_text
is None:
153 context
= parameter
.attrib
.get("context")
154 yield name
, default_text
, context
, param_type
157 class SmbDotConfTests(TestCase
):
159 # defines the cases where the defaults may differ from the documentation
160 special_cases
= set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
161 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
162 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
163 'server string', 'netbios name', 'socket options', 'use mmap',
164 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
165 'queuepause command','lpresume command', 'lppause command',
166 'lprm command', 'lpq command', 'print command', 'template homedir',
167 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build',
168 'max open files', 'fss: prune stale', 'fss: sequence timeout'])
171 super(SmbDotConfTests
, self
).setUp()
172 # create a minimal smb.conf file for testparm
173 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
174 f
= open(self
.smbconf
, 'w')
183 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
184 f
= open(self
.blankconf
, 'w')
190 self
.topdir
= os
.path
.abspath(samba
.source_tree_topdir())
193 self
.documented
= set(get_documented_parameters(self
.topdir
))
195 self
.fail("Unable to load documented parameters")
198 self
.table_gen
= set(get_param_table_full(self
.topdir
,
199 "bin/default/lib/param/param_table_gen.c"))
201 self
.fail("Unable to load generated parameter table")
204 self
.defaults
= set(get_documented_tuples(self
.topdir
))
206 self
.fail("Unable to load parameters")
209 self
.defaults_all
= set(get_documented_tuples(self
.topdir
, False))
211 self
.fail("Unable to load parameters")
215 super(SmbDotConfTests
, self
).tearDown()
216 os
.unlink(self
.smbconf
)
217 os
.unlink(self
.blankconf
)
219 def test_default_s3(self
):
220 self
._test
_default
(['bin/testparm'])
221 self
._set
_defaults
(['bin/testparm'])
223 # registry shares appears to need sudo
224 self
._set
_arbitrary
(['bin/testparm'],
225 exceptions
= ['client lanman auth',
226 'client plaintext auth',
229 self
._test
_empty
(['bin/testparm'])
231 def test_default_s4(self
):
232 self
._test
_default
(['bin/samba-tool', 'testparm'])
233 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
234 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
235 exceptions
= ['smb ports'])
236 self
._test
_empty
(['bin/samba-tool', 'testparm'])
238 def _test_default(self
, program
):
242 for tuples
in self
.defaults
:
243 param
, default
, context
, param_type
= tuples
244 if param
in self
.special_cases
:
252 self
.fail("%s has no valid context" % param
)
253 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
254 "--section-name", section
, "--parameter-name", param
],
255 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
256 if p
[0].upper().strip() != default
.upper():
257 if not (p
[0].upper().strip() == "" and default
== '""'):
258 doc_triple
= "%s\n Expected: %s" % (param
, default
)
259 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
262 self
.fail(self
._format
_message
(failset
,
263 "Parameters that do not have matching defaults:"))
265 def _set_defaults(self
, program
):
269 for tuples
in self
.defaults
:
270 param
, default
, context
, param_type
= tuples
272 if param
in ['printing']:
281 self
.fail("%s has no valid context" % param
)
282 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
283 "--section-name", section
, "--parameter-name", param
,
284 "--option", "%s = %s" % (param
, default
)],
285 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
286 if p
[0].upper().strip() != default
.upper():
287 if not (p
[0].upper().strip() == "" and default
== '""'):
288 doc_triple
= "%s\n Expected: %s" % (param
, default
)
289 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
292 self
.fail(self
._format
_message
(failset
,
293 "Parameters that do not have matching defaults:"))
295 def _set_arbitrary(self
, program
, exceptions
=None):
296 arbitrary
= {'string': 'string', 'boolean': 'yes', 'integer': '5',
297 'boolean-rev': 'yes',
301 'ustring': 'ustring',
302 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
303 opposite_arbitrary
= {'string': 'string2', 'boolean': 'no', 'integer': '6',
308 'ustring': 'ustring2',
309 'enum':'', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
314 for tuples
in self
.defaults_all
:
315 param
, default
, context
, param_type
= tuples
317 if param
in ['printing', 'copy', 'include', 'log level']:
320 # currently no easy way to set an arbitrary value for these
321 if param_type
in ['enum', 'boolean-auto']:
324 if exceptions
is not None:
325 if param
in exceptions
:
334 self
.fail("%s has no valid context" % param
)
336 value_to_use
= arbitrary
.get(param_type
)
337 if value_to_use
is None:
338 self
.fail("%s has an invalid type" % param
)
340 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
341 "--section-name", section
, "--parameter-name", param
,
342 "--option", "%s = %s" % (param
, value_to_use
)],
343 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
344 if p
[0].upper().strip() != value_to_use
.upper():
345 # currently no way to distinguish command lists
346 if param_type
== 'list':
347 if ", ".join(p
[0].upper().strip().split()) == value_to_use
.upper():
350 # currently no way to identify octal
351 if param_type
== 'integer':
353 if int(value_to_use
, 8) == int(p
[0].strip(), 8):
358 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
359 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
361 opposite_value
= opposite_arbitrary
.get(param_type
)
362 tempconf
= os
.path
.join(self
.tempdir
, "tempsmb.conf")
363 g
= open(tempconf
, 'w')
365 towrite
= section
+ "\n"
366 towrite
+= param
+ " = " + opposite_value
371 p
= subprocess
.Popen(program
+ ["-s", tempconf
, "--suppress-prompt",
372 "--option", "%s = %s" % (param
, value_to_use
)],
373 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
377 # testparm doesn't display a value if they are equivalent
378 if (value_to_use
.lower() != opposite_value
.lower()):
379 for line
in p
[0].splitlines():
380 if not line
.strip().startswith(param
):
383 value_found
= line
.split("=")[1].upper().strip()
384 if value_found
!= value_to_use
.upper():
385 # currently no way to distinguish command lists
386 if param_type
== 'list':
387 if ", ".join(value_found
.split()) == value_to_use
.upper():
390 # currently no way to identify octal
391 if param_type
== 'integer':
393 if int(value_to_use
, 8) == int(value_found
, 8):
398 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
399 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
403 self
.fail(self
._format
_message
(failset
,
404 "Parameters that were unexpectedly not set:"))
406 def _test_empty(self
, program
):
407 p
= subprocess
.Popen(program
+ ["-s", self
.blankconf
, "--suppress-prompt"],
408 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
411 for line
in p
[0].splitlines():
412 if line
.strip().startswith('#'):
414 if line
.strip().startswith("idmap config *"):
416 output
+= line
.strip().lower() + '\n'
418 if output
.strip() != '[global]' and output
.strip() != '[globals]':
419 self
.fail("Testparm returned unexpected output on an empty smb.conf.")