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'])
137 super(SmbDotConfTests
, self
).setUp()
138 # create a minimal smb.conf file for testparm
139 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
140 f
= open(self
.smbconf
, 'w')
149 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
150 f
= open(self
.blankconf
, 'w')
157 super(SmbDotConfTests
, self
).tearDown()
158 os
.unlink(self
.smbconf
)
159 os
.unlink(self
.blankconf
)
161 def test_unknown(self
):
162 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
164 documented
= set(get_documented_parameters(topdir
))
166 self
.fail("Unable to load parameters")
167 parameters
= set(get_implementation_parameters(topdir
))
168 # Filter out parametric options, since we can't find them in the parm
170 documented
= set([p
for p
in documented
if not ":" in p
])
171 unknown
= documented
.difference(parameters
)
173 self
.fail(self
._format
_message
(unknown
,
174 "Parameters that are documented but not in the implementation:"))
176 def test_undocumented(self
):
177 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
179 documented
= set(get_documented_parameters(topdir
))
181 self
.fail("Unable to load parameters")
182 parameters
= set(get_implementation_parameters(topdir
))
183 undocumented
= parameters
.difference(documented
)
184 if len(undocumented
) > 0:
185 self
.fail(self
._format
_message
(undocumented
,
186 "Parameters that are in the implementation but undocumented:"))
188 def test_default_s3(self
):
189 self
._test
_default
(['bin/testparm'])
190 self
._set
_defaults
(['bin/testparm'])
192 # registry shares appears to need sudo
193 self
._set
_arbitrary
(['bin/testparm'],
194 exceptions
= ['client lanman auth',
195 'client plaintext auth',
198 self
._test
_empty
(['bin/testparm'])
200 def test_default_s4(self
):
201 self
._test
_default
(['bin/samba-tool', 'testparm'])
202 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
203 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
204 exceptions
= ['smb ports'])
205 self
._test
_empty
(['bin/samba-tool', 'testparm'])
207 def _test_default(self
, program
):
208 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
210 defaults
= set(get_documented_tuples(topdir
))
212 self
.fail("Unable to load parameters")
213 bindir
= os
.path
.join(topdir
, "bin")
217 for tuples
in defaults
:
218 param
, default
, context
, param_type
= tuples
219 if param
in self
.special_cases
:
227 self
.fail("%s has no valid context" % param
)
228 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
229 "--section-name", section
, "--parameter-name", param
],
230 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
231 if p
[0].upper().strip() != default
.upper():
232 if not (p
[0].upper().strip() == "" and default
== '""'):
233 doc_triple
= "%s\n Expected: %s" % (param
, default
)
234 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
237 self
.fail(self
._format
_message
(failset
,
238 "Parameters that do not have matching defaults:"))
240 def _set_defaults(self
, program
):
241 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
243 defaults
= set(get_documented_tuples(topdir
))
245 self
.fail("Unable to load parameters")
246 bindir
= os
.path
.join(topdir
, "bin")
250 for tuples
in defaults
:
251 param
, default
, context
, param_type
= tuples
253 if param
in ['printing']:
262 self
.fail("%s has no valid context" % param
)
263 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
264 "--section-name", section
, "--parameter-name", param
,
265 "--option", "%s = %s" % (param
, default
)],
266 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
267 if p
[0].upper().strip() != default
.upper():
268 if not (p
[0].upper().strip() == "" and default
== '""'):
269 doc_triple
= "%s\n Expected: %s" % (param
, default
)
270 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
273 self
.fail(self
._format
_message
(failset
,
274 "Parameters that do not have matching defaults:"))
276 def _set_arbitrary(self
, program
, exceptions
=None):
277 arbitrary
= {'string': 'string', 'boolean': 'yes', 'integer': '5',
278 'enum':'', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
279 opposite_arbitrary
= {'string': 'string2', 'boolean': 'no', 'integer': '6',
280 'enum':'', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
281 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
283 defaults
= set(get_documented_tuples(topdir
, False))
285 self
.fail("Unable to load parameters" + e
)
286 bindir
= os
.path
.join(topdir
, "bin")
290 for tuples
in defaults
:
291 param
, default
, context
, param_type
= tuples
293 if param
in ['printing', 'copy', 'include', 'log level']:
296 # currently no easy way to set an arbitrary value for these
297 if param_type
in ['enum', 'boolean-auto']:
300 if exceptions
is not None:
301 if param
in exceptions
:
310 self
.fail("%s has no valid context" % param
)
312 value_to_use
= arbitrary
.get(param_type
)
313 if value_to_use
is None:
314 self
.fail("%s has an invalid type" % param
)
316 p
= subprocess
.Popen(program
+ ["-s", self
.smbconf
,
317 "--section-name", section
, "--parameter-name", param
,
318 "--option", "%s = %s" % (param
, value_to_use
)],
319 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
320 if p
[0].upper().strip() != value_to_use
.upper():
321 # currently no way to distinguish command lists
322 if param_type
== 'list':
323 if ", ".join(p
[0].upper().strip().split()) == value_to_use
.upper():
326 # currently no way to identify octal
327 if param_type
== 'integer':
329 if int(value_to_use
, 8) == int(p
[0].strip(), 8):
334 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
335 failset
.add("%s\n Got: %s" % (doc_triple
, p
[0].upper().strip()))
337 opposite_value
= opposite_arbitrary
.get(param_type
)
338 tempconf
= os
.path
.join(self
.tempdir
, "tempsmb.conf")
339 g
= open(tempconf
, 'w')
341 towrite
= section
+ "\n"
342 towrite
+= param
+ " = " + opposite_value
347 p
= subprocess
.Popen(program
+ ["-s", tempconf
, "--suppress-prompt",
348 "--option", "%s = %s" % (param
, value_to_use
)],
349 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
353 # testparm doesn't display a value if they are equivalent
354 if (value_to_use
.lower() != opposite_value
.lower()):
355 for line
in p
[0].splitlines():
356 if not line
.strip().startswith(param
):
359 value_found
= line
.split("=")[1].upper().strip()
360 if value_found
!= value_to_use
.upper():
361 # currently no way to distinguish command lists
362 if param_type
== 'list':
363 if ", ".join(value_found
.split()) == value_to_use
.upper():
366 # currently no way to identify octal
367 if param_type
== 'integer':
369 if int(value_to_use
, 8) == int(value_found
, 8):
374 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
375 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
379 self
.fail(self
._format
_message
(failset
,
380 "Parameters that were unexpectedly not set:"))
382 def _test_empty(self
, program
):
383 topdir
= os
.path
.abspath(samba
.source_tree_topdir())
385 p
= subprocess
.Popen(program
+ ["-s", self
.blankconf
, "--suppress-prompt"],
386 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, cwd
=topdir
).communicate()
389 for line
in p
[0].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.")