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."""
27 import xml
.etree
.ElementTree
as ET
28 import multiprocessing
29 import concurrent
.futures
32 class TestCase(samba
.tests
.TestCaseInTempDir
):
34 def _format_message(self
, parameters
, message
):
35 parameters
= list(parameters
)
36 parameters
= list(map(str, parameters
))
38 return message
+ '\n\n %s' % ('\n '.join(parameters
))
40 def get_max_worker_count():
41 cpu_count
= multiprocessing
.cpu_count()
43 # Always run two processes in parallel
47 max_workers
= int(cpu_count
/ 2)
53 def check_or_set_smbconf_default(cmdline
, topdir
, param
, default_param
):
54 p
= subprocess
.Popen(cmdline
,
55 stdout
=subprocess
.PIPE
,
56 stderr
=subprocess
.PIPE
,
57 cwd
=topdir
).communicate()
58 result
= p
[0].decode().upper().strip()
59 if result
!= default_param
.upper():
60 if not (result
== "" and default_param
== '""'):
61 return result
, param
, default_param
65 def set_smbconf_arbitrary(cmdline
, topdir
, param
, param_type
, value_to_use
):
66 p
= subprocess
.Popen(cmdline
,
67 stdout
=subprocess
.PIPE
,
68 stderr
=subprocess
.PIPE
,
69 cwd
=topdir
).communicate()
70 result
= p
[0].decode().upper().strip()
71 if result
!= value_to_use
.upper():
72 # currently no way to distinguish command lists
73 if param_type
== 'list':
74 if ", ".join(result
.split()) == value_to_use
.upper():
77 # currently no way to identify octal
78 if param_type
== 'integer':
80 if int(value_to_use
, 8) == int(p
[0].strip(), 8):
85 return result
, param
, value_to_use
89 def set_smbconf_arbitrary_opposite(cmdline
, topdir
, tempdir
, section
, param
,
90 param_type
, opposite_value
, value_to_use
):
91 g
= tempfile
.NamedTemporaryFile(mode
='w', dir=tempdir
, delete
=False)
93 towrite
= section
+ "\n"
94 towrite
+= param
+ " = " + opposite_value
99 p
= subprocess
.Popen(cmdline
+ ["-s", g
.name
],
100 stdout
=subprocess
.PIPE
,
101 stderr
=subprocess
.PIPE
,
102 cwd
=topdir
).communicate()
105 # testparm doesn't display a value if they are equivalent
106 if (value_to_use
.lower() != opposite_value
.lower()):
107 for line
in p
[0].decode().splitlines():
108 if not line
.strip().startswith(param
):
111 value_found
= line
.split("=")[1].upper().strip()
112 if value_found
!= value_to_use
.upper():
113 # currently no way to distinguish command lists
114 if param_type
== 'list':
115 if ", ".join(value_found
.split()) == value_to_use
.upper():
118 # currently no way to identify octal
119 if param_type
== 'integer':
121 if int(value_to_use
, 8) == int(value_found
, 8):
126 return param
, value_to_use
, value_found
130 def get_documented_parameters(sourcedir
):
131 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
132 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
133 raise Exception("Unable to find parameters.all.xml")
135 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
137 raise Exception("Error opening parameters file")
140 root
= ET
.fromstring(out
)
141 for parameter
in root
:
142 name
= parameter
.attrib
.get('name')
143 if parameter
.attrib
.get('removed') == "1":
146 syn
= parameter
.findall('synonym')
153 def get_documented_tuples(sourcedir
, omit_no_default
=True):
154 path
= os
.path
.join(sourcedir
, "bin", "default", "docs-xml", "smbdotconf")
155 if not os
.path
.exists(os
.path
.join(path
, "parameters.all.xml")):
156 raise Exception("Unable to find parameters.all.xml")
158 p
= open(os
.path
.join(path
, "parameters.all.xml"), 'r')
160 raise Exception("Error opening parameters file")
163 root
= ET
.fromstring(out
)
164 for parameter
in root
:
165 name
= parameter
.attrib
.get("name")
166 param_type
= parameter
.attrib
.get("type")
167 if parameter
.attrib
.get('removed') == "1":
169 values
= parameter
.findall("value")
172 if value
.attrib
.get("type") == "default":
173 defaults
.append(value
)
176 if len(defaults
) == 0:
179 elif len(defaults
) > 1:
180 raise Exception("More than one default found for parameter %s" % name
)
182 default_text
= defaults
[0].text
184 if default_text
is None:
186 context
= parameter
.attrib
.get("context")
187 yield name
, default_text
, context
, param_type
191 class SmbDotConfTests(TestCase
):
193 # defines the cases where the defaults may differ from the documentation
195 # Please pass the default via waf rather than adding to this
196 # list if at all possible.
197 special_cases
= set([
209 'queueresume command',
210 'queuepause command',
218 'include system krb5 conf',
219 'smbd max async dosmode',
225 # create a minimal smb.conf file for testparm
226 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
227 f
= open(self
.smbconf
, 'w')
236 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
237 f
= open(self
.blankconf
, 'w')
243 self
.topdir
= os
.path
.abspath(samba
.source_tree_topdir())
246 self
.documented
= set(get_documented_parameters(self
.topdir
))
248 self
.fail("Unable to load documented parameters")
251 self
.defaults
= set(get_documented_tuples(self
.topdir
))
253 self
.fail("Unable to load parameters")
256 self
.defaults_all
= set(get_documented_tuples(self
.topdir
, False))
258 self
.fail("Unable to load parameters")
262 os
.unlink(self
.smbconf
)
263 os
.unlink(self
.blankconf
)
265 def test_default_s3(self
):
266 self
._test
_default
(['bin/testparm'])
267 self
._set
_defaults
(['bin/testparm'])
269 # registry shares appears to need sudo
270 self
._set
_arbitrary
(['bin/testparm'],
271 exceptions
= ['client lanman auth',
272 'client plaintext auth',
275 'rpc server dynamic port range',
276 'name resolve order',
278 self
._test
_empty
(['bin/testparm'])
280 def test_default_s4(self
):
281 self
._test
_default
(['bin/samba-tool', 'testparm'])
282 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
283 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
284 exceptions
=['smb ports',
285 'rpc server dynamic port range',
286 'name resolve order'])
287 self
._test
_empty
(['bin/samba-tool', 'testparm'])
289 def _test_default(self
, program
):
291 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
292 program
= [os
.environ
["PYTHON"]] + program
296 with concurrent
.futures
.ProcessPoolExecutor(max_workers
=get_max_worker_count()) as executor
:
299 for tuples
in self
.defaults
:
300 param
, default
, context
, param_type
= tuples
302 if param
in self
.special_cases
:
304 # bad, bad parametric options - we don't have their default values
313 self
.fail("%s has no valid context" % param
)
315 program_arg1
= ["--configfile=%s" % (self
.smbconf
)]
316 if (program
[0] == 'bin/testparm'):
317 program_arg1
= ["--suppress-prompt", self
.smbconf
]
319 cmdline
= program
+ program_arg1
+ [
325 future
= executor
.submit(check_or_set_smbconf_default
, cmdline
, self
.topdir
, param
, default
)
326 result_futures
.append(future
)
328 for f
in concurrent
.futures
.as_completed(result_futures
):
330 result
, param
, default_param
= f
.result()
332 doc_triple
= "%s\n Expected: %s" % (param
, default_param
)
333 failset
.add("%s\n Got: %s" % (doc_triple
, result
))
336 self
.fail(self
._format
_message
(failset
,
337 "Parameters that do not have matching defaults:"))
339 def _set_defaults(self
, program
):
341 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
342 program
= [os
.environ
["PYTHON"]] + program
346 with concurrent
.futures
.ProcessPoolExecutor(max_workers
=get_max_worker_count()) as executor
:
349 for tuples
in self
.defaults
:
350 param
, default
, context
, param_type
= tuples
354 'smbd max async dosmode',
357 if param
in exceptions
:
366 self
.fail("%s has no valid context" % param
)
368 program_arg1
= ["--configfile=%s" % (self
.smbconf
)]
369 if (program
[0] == 'bin/testparm'):
370 program_arg1
= ["--suppress-prompt", self
.smbconf
]
372 cmdline
= program
+ program_arg1
+ [
378 "%s = %s" % (param
, default
)]
379 future
= executor
.submit(check_or_set_smbconf_default
, cmdline
, self
.topdir
, param
, default
)
380 result_futures
.append(future
)
382 for f
in concurrent
.futures
.as_completed(result_futures
):
384 result
, param
, default_param
= f
.result()
386 doc_triple
= "%s\n Expected: %s" % (param
, default
)
387 failset
.add("%s\n Got: %s" % (doc_triple
, result
))
390 self
.fail(self
._format
_message
(failset
,
391 "Parameters that do not have matching defaults:"))
393 def _set_arbitrary(self
, program
, exceptions
=None):
395 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
396 program
= [os
.environ
["PYTHON"]] + program
398 arbitrary
= {'string': 'string', 'boolean': 'yes', 'integer': '5',
399 'boolean-rev': 'yes',
403 'ustring': 'ustring',
404 'enum': '', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
405 opposite_arbitrary
= {'string': 'string2', 'boolean': 'no', 'integer': '6',
410 'ustring': 'ustring2',
411 'enum': '', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
415 with concurrent
.futures
.ProcessPoolExecutor(max_workers
=get_max_worker_count()) as executor
:
419 for tuples
in self
.defaults_all
:
420 param
, default
, context
, param_type
= tuples
422 if param
in ['printing', 'copy', 'include', 'log level']:
425 # currently no easy way to set an arbitrary value for these
426 if param_type
in ['enum', 'boolean-auto']:
429 if exceptions
is not None:
430 if param
in exceptions
:
439 self
.fail("%s has no valid context" % param
)
441 value_to_use
= arbitrary
.get(param_type
)
442 if value_to_use
is None:
443 self
.fail("%s has an invalid type" % param
)
445 program_arg1
= ["--configfile=%s" % (self
.smbconf
)]
446 if (program
[0] == 'bin/testparm'):
447 program_arg1
= ["--suppress-prompt", self
.smbconf
]
449 cmdline
= program
+ program_arg1
+ [
455 "%s = %s" % (param
, value_to_use
)]
457 future
= executor
.submit(set_smbconf_arbitrary
, cmdline
, self
.topdir
, param
, param_type
, value_to_use
)
458 result_futures1
.append(future
)
460 opposite_value
= opposite_arbitrary
.get(param_type
)
462 cmdline
= program
+ ["--suppress-prompt",
464 "%s = %s" % (param
, value_to_use
)]
466 future
= executor
.submit(set_smbconf_arbitrary_opposite
, cmdline
, self
.topdir
, self
.tempdir
,
467 section
, param
, param_type
, opposite_value
, value_to_use
)
468 result_futures2
.append(future
)
470 for f
in concurrent
.futures
.as_completed(result_futures1
):
472 result
, param
, value_to_use
= f
.result()
474 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
475 failset
.add("%s\n Got: %s" % (doc_triple
, result
))
477 for f
in concurrent
.futures
.as_completed(result_futures2
):
479 param
, value_to_use
, value_found
= f
.result()
481 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
482 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
485 self
.fail(self
._format
_message
(failset
,
486 "Parameters that were unexpectedly not set:"))
488 def _test_empty(self
, program
):
490 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
491 program
= [os
.environ
["PYTHON"]] + program
493 program_arg1
= ["--configfile=%s" % (self
.blankconf
), "--suppress-prompt"]
494 if (program
[0] == 'bin/testparm'):
495 program_arg1
= ["--suppress-prompt", self
.blankconf
]
497 print(program
+ program_arg1
)
498 p
= subprocess
.Popen(program
+ program_arg1
,
499 stdout
=subprocess
.PIPE
,
500 stderr
=subprocess
.PIPE
,
501 cwd
=self
.topdir
).communicate()
504 for line
in p
[0].decode().splitlines():
505 if line
.strip().startswith('#'):
507 if line
.strip().startswith("idmap config *"):
509 output
+= line
.strip().lower() + '\n'
511 if output
.strip() != '[global]' and output
.strip() != '[globals]':
512 self
.fail("Testparm returned unexpected output on an empty smb.conf.")