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_implementation_parameters(sourcedir
):
63 # Reading entries from source code
64 f
= open(os
.path
.join(sourcedir
, "lib/param/param_table_static.c"), "r")
66 # burn through the preceding lines
69 if l
.startswith("struct parm_struct parm_table"):
72 for l
in f
.readlines():
73 if re
.match("^\s*\}\;\s*$", l
):
75 # pull in the param names only
76 m
= re
.match("\s*\.label\s*=\s*\"(.*)\".*", l
)
85 def get_documented_tuples(sourcedir
, omit_no_default
=True):
86 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
87 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
88 raise Exception("Unable to find parameters.all.xml")
90 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
92 raise Exception("Error opening parameters file")
95 root
= ET
.fromstring(out
)
96 for parameter
in root
:
97 name
= parameter
.attrib
.get("name")
98 param_type
= parameter
.attrib
.get("type")
99 if parameter
.attrib
.get('removed') == "1":
101 values
= parameter
.findall("value")
104 if value
.attrib
.get("type") == "default":
105 defaults
.append(value
)
108 if len(defaults
) == 0:
111 elif len(defaults
) > 1:
112 raise Exception("More than one default found for parameter %s" % name
)
114 default_text
= defaults
[0].text
116 if default_text
is None:
118 context
= parameter
.attrib
.get("context")
119 yield name
, default_text
, context
, param_type
122 class SmbDotConfTests(TestCase
):
124 # defines the cases where the defaults may differ from the documentation
125 special_cases
= set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
126 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
127 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
128 'server string', 'netbios name', 'socket options', 'use mmap',
129 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
130 'queuepause command','lpresume command', 'lppause command',
131 'lprm command', 'lpq command', 'print command', 'template homedir',
132 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build',
133 'max open files', 'fss: prune stale', 'fss: sequence timeout'])
136 super(SmbDotConfTests
, self
).setUp()
137 # create a minimal smb.conf file for testparm
138 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
139 f
= open(self
.smbconf
, 'w')
148 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
149 f
= open(self
.blankconf
, 'w')
155 self
.topdir
= os
.path
.abspath(samba
.source_tree_topdir())
158 self
.documented
= set(get_documented_parameters(self
.topdir
))
160 self
.fail("Unable to load documented parameters")
163 self
.parameters
= set(get_implementation_parameters(self
.topdir
))
165 self
.fail("Unable to load implemented parameters")
168 self
.defaults
= set(get_documented_tuples(self
.topdir
))
170 self
.fail("Unable to load parameters")
173 self
.defaults_all
= set(get_documented_tuples(self
.topdir
, False))
175 self
.fail("Unable to load parameters")
179 super(SmbDotConfTests
, self
).tearDown()
180 os
.unlink(self
.smbconf
)
181 os
.unlink(self
.blankconf
)
183 def test_unknown(self
):
184 # Filter out parametric options, since we can't find them in the parm
186 documented
= set([p
for p
in self
.documented
if not ":" in p
])
187 unknown
= documented
.difference(self
.parameters
)
189 self
.fail(self
._format
_message
(unknown
,
190 "Parameters that are documented but not in the implementation:"))
192 def test_undocumented(self
):
193 undocumented
= self
.parameters
.difference(self
.documented
)
194 if len(undocumented
) > 0:
195 self
.fail(self
._format
_message
(undocumented
,
196 "Parameters that are in the implementation but undocumented:"))
198 def test_default_s3(self
):
199 self
._test
_default
(['bin/testparm'])
200 self
._set
_defaults
(['bin/testparm'])
202 # registry shares appears to need sudo
203 self
._set
_arbitrary
(['bin/testparm'],
204 exceptions
= ['client lanman auth',
205 'client plaintext auth',
208 self
._test
_empty
(['bin/testparm'])
210 def test_default_s4(self
):
211 self
._test
_default
(['bin/samba-tool', 'testparm'])
212 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
213 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
214 exceptions
= ['smb ports'])
215 self
._test
_empty
(['bin/samba-tool', 'testparm'])
217 def _test_default(self
, program
):
221 for tuples
in self
.defaults
:
222 param
, default
, context
, param_type
= tuples
223 if param
in self
.special_cases
:
231 self
.fail("%s has no valid context" % param
)
232 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
233 "--section-name", section
, "--parameter-name", param
],
234 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
235 if p
[0].upper().strip() != default
.upper():
236 if not (p
[0].upper().strip() == "" and default
== '""'):
237 doc_triple
= "%s\n Expected: %s" % (param
, default
)
238 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
241 self
.fail(self
._format
_message
(failset
,
242 "Parameters that do not have matching defaults:"))
244 def _set_defaults(self
, program
):
248 for tuples
in self
.defaults
:
249 param
, default
, context
, param_type
= tuples
251 if param
in ['printing']:
260 self
.fail("%s has no valid context" % param
)
261 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
262 "--section-name", section
, "--parameter-name", param
,
263 "--option", "%s = %s" % (param
, default
)],
264 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
265 if p
[0].upper().strip() != default
.upper():
266 if not (p
[0].upper().strip() == "" and default
== '""'):
267 doc_triple
= "%s\n Expected: %s" % (param
, default
)
268 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
271 self
.fail(self
._format
_message
(failset
,
272 "Parameters that do not have matching defaults:"))
274 def _set_arbitrary(self
, program
, exceptions
=None):
275 arbitrary
= {'string': 'string', 'boolean': 'yes', 'integer': '5',
276 'boolean-rev': 'yes',
280 'ustring': 'ustring',
281 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
282 opposite_arbitrary
= {'string': 'string2', 'boolean': 'no', 'integer': '6',
287 'ustring': 'ustring2',
288 'enum':'', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
293 for tuples
in self
.defaults_all
:
294 param
, default
, context
, param_type
= tuples
296 if param
in ['printing', 'copy', 'include', 'log level']:
299 # currently no easy way to set an arbitrary value for these
300 if param_type
in ['enum', 'boolean-auto']:
303 if exceptions
is not None:
304 if param
in exceptions
:
313 self
.fail("%s has no valid context" % param
)
315 value_to_use
= arbitrary
.get(param_type
)
316 if value_to_use
is None:
317 self
.fail("%s has an invalid type" % param
)
319 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
320 "--section-name", section
, "--parameter-name", param
,
321 "--option", "%s = %s" % (param
, value_to_use
)],
322 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
323 if p
[0].upper().strip() != value_to_use
.upper():
324 # currently no way to distinguish command lists
325 if param_type
== 'list':
326 if ", ".join(p
[0].upper().strip().split()) == value_to_use
.upper():
329 # currently no way to identify octal
330 if param_type
== 'integer':
332 if int(value_to_use
, 8) == int(p
[0].strip(), 8):
337 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
338 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
340 opposite_value
= opposite_arbitrary
.get(param_type
)
341 tempconf
= os
.path
.join(self
.tempdir
, "tempsmb.conf")
342 g
= open(tempconf
, 'w')
344 towrite
= section
+ "\n"
345 towrite
+= param
+ " = " + opposite_value
350 p
= subprocess
.Popen(program
+ ["-s", tempconf
, "--suppress-prompt",
351 "--option", "%s = %s" % (param
, value_to_use
)],
352 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
356 # testparm doesn't display a value if they are equivalent
357 if (value_to_use
.lower() != opposite_value
.lower()):
358 for line
in p
[0].splitlines():
359 if not line
.strip().startswith(param
):
362 value_found
= line
.split("=")[1].upper().strip()
363 if value_found
!= value_to_use
.upper():
364 # currently no way to distinguish command lists
365 if param_type
== 'list':
366 if ", ".join(value_found
.split()) == value_to_use
.upper():
369 # currently no way to identify octal
370 if param_type
== 'integer':
372 if int(value_to_use
, 8) == int(value_found
, 8):
377 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
378 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
382 self
.fail(self
._format
_message
(failset
,
383 "Parameters that were unexpectedly not set:"))
385 def _test_empty(self
, program
):
386 p
= subprocess
.Popen(program
+ ["-s", self
.blankconf
, "--suppress-prompt"],
387 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=self
.topdir
).communicate()
390 for line
in p
[0].splitlines():
391 if line
.strip().startswith('#'):
393 if line
.strip().startswith("idmap config *"):
395 output
+= line
.strip().lower() + '\n'
397 if output
.strip() != '[global]' and output
.strip() != '[globals]':
398 self
.fail("Testparm returned unexpected output on an empty smb.conf.")