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."""
24 from samba
.tests
import TestSkipped
, TestCaseInTempDir
30 import xml
.etree
.ElementTree
as ET
32 class TestCase(samba
.tests
.TestCaseInTempDir
):
34 def _format_message(self
, parameters
, message
):
35 parameters
= list(parameters
)
37 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.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 if re
.match(".*P_SEPARATOR.*", l
):
78 m
= re
.match("\s*\.label\s*=\s*\"(.*)\".*", l
)
87 def get_documented_tuples(sourcedir
, omit_no_default
=True):
88 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
89 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
90 raise Exception("Unable to find parameters.all.xml")
92 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
94 raise Exception("Error opening parameters file")
97 root
= ET
.fromstring(out
)
98 for parameter
in root
:
99 name
= parameter
.attrib
.get("name")
100 param_type
= parameter
.attrib
.get("type")
101 if parameter
.attrib
.get('removed') == "1":
103 values
= parameter
.findall("value")
106 if value
.attrib
.get("type") == "default":
107 defaults
.append(value
)
110 if len(defaults
) == 0:
113 elif len(defaults
) > 1:
114 raise Exception("More than one default found for parameter %s" % name
)
116 default_text
= defaults
[0].text
118 if default_text
is None:
120 context
= parameter
.attrib
.get("context")
121 yield name
, default_text
, context
, param_type
124 class SmbDotConfTests(TestCase
):
126 # defines the cases where the defaults may differ from the documentation
127 special_cases
= set(['log level', 'path', 'ldapsam:trusted', 'spoolss: architecture',
128 'share:fake_fscaps', 'ldapsam:editposix', 'rpc_daemon:DAEMON',
129 'rpc_server:SERVER', 'panic action', 'homedir map', 'NIS homedir',
130 'server string', 'netbios name', 'socket options', 'use mmap',
131 'ctdbd socket', 'printing', 'printcap name', 'queueresume command',
132 'queuepause command','lpresume command', 'lppause command',
133 'lprm command', 'lpq command', 'print command', 'template homedir',
134 'spoolss: os_major', 'spoolss: os_minor', 'spoolss: os_build',
138 super(SmbDotConfTests
, self
).setUp()
139 # create a minimal smb.conf file for testparm
140 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
141 f
= open(self
.smbconf
, 'w')
150 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
151 f
= open(self
.blankconf
, 'w')
158 super(SmbDotConfTests
, self
).tearDown()
159 os
.unlink(self
.smbconf
)
160 os
.unlink(self
.blankconf
)
162 def test_unknown(self
):
163 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
165 documented
= set(get_documented_parameters(topdir
))
167 self
.fail("Unable to load parameters")
168 parameters
= set(get_implementation_parameters(topdir
))
169 # Filter out parametric options, since we can't find them in the parm
171 documented
= set([p
for p
in documented
if not ":" in p
])
172 unknown
= documented
.difference(parameters
)
174 self
.fail(self
._format
_message
(unknown
,
175 "Parameters that are documented but not in the implementation:"))
177 def test_undocumented(self
):
178 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
180 documented
= set(get_documented_parameters(topdir
))
182 self
.fail("Unable to load parameters")
183 parameters
= set(get_implementation_parameters(topdir
))
184 undocumented
= parameters
.difference(documented
)
185 if len(undocumented
) > 0:
186 self
.fail(self
._format
_message
(undocumented
,
187 "Parameters that are in the implementation but undocumented:"))
189 def test_default_s3(self
):
190 self
._test
_default
(['bin/testparm'])
191 self
._set
_defaults
(['bin/testparm'])
193 # registry shares appears to need sudo
194 self
._set
_arbitrary
(['bin/testparm'],
195 exceptions
= ['client lanman auth',
196 'client plaintext auth',
199 self
._test
_empty
(['bin/testparm'])
201 def test_default_s4(self
):
202 self
._test
_default
(['bin/samba-tool', 'testparm'])
203 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
204 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
205 exceptions
= ['smb ports'])
206 self
._test
_empty
(['bin/samba-tool', 'testparm'])
208 def _test_default(self
, program
):
209 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
211 defaults
= set(get_documented_tuples(topdir
))
213 self
.fail("Unable to load parameters")
214 bindir
= os
.path
.join(topdir
, "bin")
218 for tuples
in defaults
:
219 param
, default
, context
, param_type
= tuples
220 if param
in self
.special_cases
:
228 self
.fail("%s has no valid context" % param
)
229 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
230 "--section-name", section
, "--parameter-name", param
],
231 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
232 if p
[0].upper().strip() != default
.upper():
233 if not (p
[0].upper().strip() == "" and default
== '""'):
234 doc_triple
= "%s\n Expected: %s" % (param
, default
)
235 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
238 self
.fail(self
._format
_message
(failset
,
239 "Parameters that do not have matching defaults:"))
241 def _set_defaults(self
, program
):
242 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
244 defaults
= set(get_documented_tuples(topdir
))
246 self
.fail("Unable to load parameters")
247 bindir
= os
.path
.join(topdir
, "bin")
251 for tuples
in defaults
:
252 param
, default
, context
, param_type
= tuples
254 if param
in ['printing']:
263 self
.fail("%s has no valid context" % param
)
264 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
265 "--section-name", section
, "--parameter-name", param
,
266 "--option", "%s = %s" % (param
, default
)],
267 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
268 if p
[0].upper().strip() != default
.upper():
269 if not (p
[0].upper().strip() == "" and default
== '""'):
270 doc_triple
= "%s\n Expected: %s" % (param
, default
)
271 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
274 self
.fail(self
._format
_message
(failset
,
275 "Parameters that do not have matching defaults:"))
277 def _set_arbitrary(self
, program
, exceptions
=None):
278 arbitrary
= {'string': 'string', 'boolean': 'yes', 'integer': '5',
279 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
280 opposite_arbitrary
= {'string': 'string2', 'boolean': 'no', 'integer': '6',
281 'enum':'', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
282 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
284 defaults
= set(get_documented_tuples(topdir
, False))
286 self
.fail("Unable to load parameters" + e
)
287 bindir
= os
.path
.join(topdir
, "bin")
291 for tuples
in defaults
:
292 param
, default
, context
, param_type
= tuples
294 if param
in ['printing', 'copy', 'include', 'log level']:
297 # currently no easy way to set an arbitrary value for these
298 if param_type
in ['enum', 'boolean-auto']:
301 if exceptions
is not None:
302 if param
in exceptions
:
311 self
.fail("%s has no valid context" % param
)
313 value_to_use
= arbitrary
.get(param_type
)
314 if value_to_use
is None:
315 self
.fail("%s has an invalid type" % param
)
317 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
318 "--section-name", section
, "--parameter-name", param
,
319 "--option", "%s = %s" % (param
, value_to_use
)],
320 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
321 if p
[0].upper().strip() != value_to_use
.upper():
322 # currently no way to distinguish command lists
323 if param_type
== 'list':
324 if ", ".join(p
[0].upper().strip().split()) == value_to_use
.upper():
327 # currently no way to identify octal
328 if param_type
== 'integer':
330 if int(value_to_use
, 8) == int(p
[0].strip(), 8):
335 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
336 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
338 opposite_value
= opposite_arbitrary
.get(param_type
)
339 tempconf
= os
.path
.join(self
.tempdir
, "tempsmb.conf")
340 g
= open(tempconf
, 'w')
342 towrite
= section
+ "\n"
343 towrite
+= param
+ " = " + opposite_value
348 p
= subprocess
.Popen(program
+ ["-s", tempconf
, "--suppress-prompt",
349 "--option", "%s = %s" % (param
, value_to_use
)],
350 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
354 # testparm doesn't display a value if they are equivalent
355 if (value_to_use
.lower() != opposite_value
.lower()):
356 for line
in p
[0].splitlines():
357 if not line
.strip().startswith(param
):
360 value_found
= line
.split("=")[1].upper().strip()
361 if value_found
!= value_to_use
.upper():
362 # currently no way to distinguish command lists
363 if param_type
== 'list':
364 if ", ".join(value_found
.split()) == value_to_use
.upper():
367 # currently no way to identify octal
368 if param_type
== 'integer':
370 if int(value_to_use
, 8) == int(value_found
, 8):
375 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
376 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
380 self
.fail(self
._format
_message
(failset
,
381 "Parameters that were unexpectedly not set:"))
383 def _test_empty(self
, program
):
384 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
386 p
= subprocess
.Popen(program
+ ["-s", self
.blankconf
, "--suppress-prompt"],
387 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=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.")