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
)
35 return message
+ '\n\n %s' % ('\n '.join(parameters
))
38 def get_documented_parameters(sourcedir
):
39 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
40 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
41 raise Exception("Unable to find parameters.all.xml")
43 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
45 raise Exception("Error opening parameters file")
48 root
= ET
.fromstring(out
)
49 for parameter
in root
:
50 name
= parameter
.attrib
.get('name')
51 if parameter
.attrib
.get('removed') == "1":
54 syn
= parameter
.findall('synonym')
61 def get_implementation_parameters(sourcedir
):
62 # Reading entries from source code
63 f
= open(os
.path
.join(sourcedir
, "lib/param/param_table_static.c"), "r")
65 # burn through the preceding lines
68 if l
.startswith("struct parm_struct parm_table"):
71 for l
in f
.readlines():
72 if re
.match("^\s*\}\;\s*$", l
):
74 # pull in the param names only
75 m
= re
.match("\s*\.label\s*=\s*\"(.*)\".*", l
)
84 def get_documented_tuples(sourcedir
, omit_no_default
=True):
85 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
86 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
87 raise Exception("Unable to find parameters.all.xml")
89 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
91 raise Exception("Error opening parameters file")
94 root
= ET
.fromstring(out
)
95 for parameter
in root
:
96 name
= parameter
.attrib
.get("name")
97 param_type
= parameter
.attrib
.get("type")
98 if parameter
.attrib
.get('removed') == "1":
100 values
= parameter
.findall("value")
103 if value
.attrib
.get("type") == "default":
104 defaults
.append(value
)
107 if len(defaults
) == 0:
110 elif len(defaults
) > 1:
111 raise Exception("More than one default found for parameter %s" % name
)
113 default_text
= defaults
[0].text
115 if default_text
is None:
117 context
= parameter
.attrib
.get("context")
118 yield name
, default_text
, context
, param_type
121 class SmbDotConfTests(TestCase
):
123 # defines the cases where the defaults may differ from the documentation
124 special_cases
= set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
125 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
126 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
127 'server string', 'netbios name', 'socket options', 'use mmap',
128 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
129 'queuepause command','lpresume command', 'lppause command',
130 'lprm command', 'lpq command', 'print command', 'template homedir',
131 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build',
132 'max open files', 'fss: prune stale', 'fss: sequence timeout'])
135 super(SmbDotConfTests
, self
).setUp()
136 # create a minimal smb.conf file for testparm
137 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
138 f
= open(self
.smbconf
, 'w')
147 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
148 f
= open(self
.blankconf
, 'w')
155 super(SmbDotConfTests
, self
).tearDown()
156 os
.unlink(self
.smbconf
)
157 os
.unlink(self
.blankconf
)
159 def test_unknown(self
):
160 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
162 documented
= set(get_documented_parameters(topdir
))
164 self
.fail("Unable to load parameters")
165 parameters
= set(get_implementation_parameters(topdir
))
166 # Filter out parametric options, since we can't find them in the parm
168 documented
= set([p
for p
in documented
if not ":" in p
])
169 unknown
= documented
.difference(parameters
)
171 self
.fail(self
._format
_message
(unknown
,
172 "Parameters that are documented but not in the implementation:"))
174 def test_undocumented(self
):
175 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
177 documented
= set(get_documented_parameters(topdir
))
179 self
.fail("Unable to load parameters")
180 parameters
= set(get_implementation_parameters(topdir
))
181 undocumented
= parameters
.difference(documented
)
182 if len(undocumented
) > 0:
183 self
.fail(self
._format
_message
(undocumented
,
184 "Parameters that are in the implementation but undocumented:"))
186 def test_default_s3(self
):
187 self
._test
_default
(['bin/testparm'])
188 self
._set
_defaults
(['bin/testparm'])
190 # registry shares appears to need sudo
191 self
._set
_arbitrary
(['bin/testparm'],
192 exceptions
= ['client lanman auth',
193 'client plaintext auth',
196 self
._test
_empty
(['bin/testparm'])
198 def test_default_s4(self
):
199 self
._test
_default
(['bin/samba-tool', 'testparm'])
200 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
201 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
202 exceptions
= ['smb ports'])
203 self
._test
_empty
(['bin/samba-tool', 'testparm'])
205 def _test_default(self
, program
):
206 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
208 defaults
= set(get_documented_tuples(topdir
))
210 self
.fail("Unable to load parameters")
211 bindir
= os
.path
.join(topdir
, "bin")
215 for tuples
in defaults
:
216 param
, default
, context
, param_type
= tuples
217 if param
in self
.special_cases
:
225 self
.fail("%s has no valid context" % param
)
226 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
227 "--section-name", section
, "--parameter-name", param
],
228 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
229 if p
[0].upper().strip() != default
.upper():
230 if not (p
[0].upper().strip() == "" and default
== '""'):
231 doc_triple
= "%s\n Expected: %s" % (param
, default
)
232 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
235 self
.fail(self
._format
_message
(failset
,
236 "Parameters that do not have matching defaults:"))
238 def _set_defaults(self
, program
):
239 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
241 defaults
= set(get_documented_tuples(topdir
))
243 self
.fail("Unable to load parameters")
244 bindir
= os
.path
.join(topdir
, "bin")
248 for tuples
in 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
=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'}
289 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
291 defaults
= set(get_documented_tuples(topdir
, False))
293 self
.fail("Unable to load parameters" + e
)
294 bindir
= os
.path
.join(topdir
, "bin")
298 for tuples
in defaults
:
299 param
, default
, context
, param_type
= tuples
301 if param
in ['printing', 'copy', 'include', 'log level']:
304 # currently no easy way to set an arbitrary value for these
305 if param_type
in ['enum', 'boolean-auto']:
308 if exceptions
is not None:
309 if param
in exceptions
:
318 self
.fail("%s has no valid context" % param
)
320 value_to_use
= arbitrary
.get(param_type
)
321 if value_to_use
is None:
322 self
.fail("%s has an invalid type" % param
)
324 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
325 "--section-name", section
, "--parameter-name", param
,
326 "--option", "%s = %s" % (param
, value_to_use
)],
327 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
328 if p
[0].upper().strip() != value_to_use
.upper():
329 # currently no way to distinguish command lists
330 if param_type
== 'list':
331 if ", ".join(p
[0].upper().strip().split()) == value_to_use
.upper():
334 # currently no way to identify octal
335 if param_type
== 'integer':
337 if int(value_to_use
, 8) == int(p
[0].strip(), 8):
342 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
343 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
345 opposite_value
= opposite_arbitrary
.get(param_type
)
346 tempconf
= os
.path
.join(self
.tempdir
, "tempsmb.conf")
347 g
= open(tempconf
, 'w')
349 towrite
= section
+ "\n"
350 towrite
+= param
+ " = " + opposite_value
355 p
= subprocess
.Popen(program
+ ["-s", tempconf
, "--suppress-prompt",
356 "--option", "%s = %s" % (param
, value_to_use
)],
357 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
361 # testparm doesn't display a value if they are equivalent
362 if (value_to_use
.lower() != opposite_value
.lower()):
363 for line
in p
[0].splitlines():
364 if not line
.strip().startswith(param
):
367 value_found
= line
.split("=")[1].upper().strip()
368 if value_found
!= value_to_use
.upper():
369 # currently no way to distinguish command lists
370 if param_type
== 'list':
371 if ", ".join(value_found
.split()) == value_to_use
.upper():
374 # currently no way to identify octal
375 if param_type
== 'integer':
377 if int(value_to_use
, 8) == int(value_found
, 8):
382 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
383 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
387 self
.fail(self
._format
_message
(failset
,
388 "Parameters that were unexpectedly not set:"))
390 def _test_empty(self
, program
):
391 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
393 p
= subprocess
.Popen(program
+ ["-s", self
.blankconf
, "--suppress-prompt"],
394 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
397 for line
in p
[0].splitlines():
398 if line
.strip().startswith('#'):
400 if line
.strip().startswith("idmap config *"):
402 output
+= line
.strip().lower() + '\n'
404 if output
.strip() != '[global]' and output
.strip() != '[globals]':
405 self
.fail("Testparm returned unexpected output on an empty smb.conf.")