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',
224 # create a minimal smb.conf file for testparm
225 self
.smbconf
= os
.path
.join(self
.tempdir
, "paramtestsmb.conf")
226 f
= open(self
.smbconf
, 'w')
235 self
.blankconf
= os
.path
.join(self
.tempdir
, "emptytestsmb.conf")
236 f
= open(self
.blankconf
, 'w')
242 self
.topdir
= os
.path
.abspath(samba
.source_tree_topdir())
245 self
.documented
= set(get_documented_parameters(self
.topdir
))
247 self
.fail("Unable to load documented parameters")
250 self
.defaults
= set(get_documented_tuples(self
.topdir
))
252 self
.fail("Unable to load parameters")
255 self
.defaults_all
= set(get_documented_tuples(self
.topdir
, False))
257 self
.fail("Unable to load parameters")
261 os
.unlink(self
.smbconf
)
262 os
.unlink(self
.blankconf
)
264 def test_default_s3(self
):
265 self
._test
_default
(['bin/testparm'])
266 self
._set
_defaults
(['bin/testparm'])
268 # registry shares appears to need sudo
269 self
._set
_arbitrary
(['bin/testparm'],
270 exceptions
= ['client lanman auth',
271 'client plaintext auth',
274 'rpc server dynamic port range',
275 'name resolve order',
277 self
._test
_empty
(['bin/testparm'])
279 def test_default_s4(self
):
280 self
._test
_default
(['bin/samba-tool', 'testparm'])
281 self
._set
_defaults
(['bin/samba-tool', 'testparm'])
282 self
._set
_arbitrary
(['bin/samba-tool', 'testparm'],
283 exceptions
=['smb ports',
284 'rpc server dynamic port range',
285 'name resolve order'])
286 self
._test
_empty
(['bin/samba-tool', 'testparm'])
288 def _test_default(self
, program
):
290 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
291 program
= [os
.environ
["PYTHON"]] + program
295 with concurrent
.futures
.ProcessPoolExecutor(max_workers
=get_max_worker_count()) as executor
:
298 for tuples
in self
.defaults
:
299 param
, default
, context
, param_type
= tuples
301 if param
in self
.special_cases
:
303 # bad, bad parametric options - we don't have their default values
312 self
.fail("%s has no valid context" % param
)
314 program_arg1
= ["--configfile=%s" % (self
.smbconf
)]
315 if (program
[0] == 'bin/testparm'):
316 program_arg1
= ["--suppress-prompt", self
.smbconf
]
318 cmdline
= program
+ program_arg1
+ [
324 future
= executor
.submit(check_or_set_smbconf_default
, cmdline
, self
.topdir
, param
, default
)
325 result_futures
.append(future
)
327 for f
in concurrent
.futures
.as_completed(result_futures
):
329 result
, param
, default_param
= f
.result()
331 doc_triple
= "%s\n Expected: %s" % (param
, default_param
)
332 failset
.add("%s\n Got: %s" % (doc_triple
, result
))
335 self
.fail(self
._format
_message
(failset
,
336 "Parameters that do not have matching defaults:"))
338 def _set_defaults(self
, program
):
340 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
341 program
= [os
.environ
["PYTHON"]] + program
345 with concurrent
.futures
.ProcessPoolExecutor(max_workers
=get_max_worker_count()) as executor
:
348 for tuples
in self
.defaults
:
349 param
, default
, context
, param_type
= tuples
353 'smbd max async dosmode',
356 if param
in exceptions
:
365 self
.fail("%s has no valid context" % param
)
367 program_arg1
= ["--configfile=%s" % (self
.smbconf
)]
368 if (program
[0] == 'bin/testparm'):
369 program_arg1
= ["--suppress-prompt", self
.smbconf
]
371 cmdline
= program
+ program_arg1
+ [
377 "%s = %s" % (param
, default
)]
378 future
= executor
.submit(check_or_set_smbconf_default
, cmdline
, self
.topdir
, param
, default
)
379 result_futures
.append(future
)
381 for f
in concurrent
.futures
.as_completed(result_futures
):
383 result
, param
, default_param
= f
.result()
385 doc_triple
= "%s\n Expected: %s" % (param
, default
)
386 failset
.add("%s\n Got: %s" % (doc_triple
, result
))
389 self
.fail(self
._format
_message
(failset
,
390 "Parameters that do not have matching defaults:"))
392 def _set_arbitrary(self
, program
, exceptions
=None):
394 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
395 program
= [os
.environ
["PYTHON"]] + program
397 arbitrary
= {'string': 'string', 'boolean': 'yes', 'integer': '5',
398 'boolean-rev': 'yes',
402 'ustring': 'ustring',
403 'enum': '', 'boolean-auto': '', 'char': 'a', 'list': 'a, b, c'}
404 opposite_arbitrary
= {'string': 'string2', 'boolean': 'no', 'integer': '6',
409 'ustring': 'ustring2',
410 'enum': '', 'boolean-auto': '', 'char': 'b', 'list': 'd, e, f'}
414 with concurrent
.futures
.ProcessPoolExecutor(max_workers
=get_max_worker_count()) as executor
:
418 for tuples
in self
.defaults_all
:
419 param
, default
, context
, param_type
= tuples
421 if param
in ['printing', 'copy', 'include', 'log level']:
424 # currently no easy way to set an arbitrary value for these
425 if param_type
in ['enum', 'boolean-auto']:
428 if exceptions
is not None:
429 if param
in exceptions
:
438 self
.fail("%s has no valid context" % param
)
440 value_to_use
= arbitrary
.get(param_type
)
441 if value_to_use
is None:
442 self
.fail("%s has an invalid type" % param
)
444 program_arg1
= ["--configfile=%s" % (self
.smbconf
)]
445 if (program
[0] == 'bin/testparm'):
446 program_arg1
= ["--suppress-prompt", self
.smbconf
]
448 cmdline
= program
+ program_arg1
+ [
454 "%s = %s" % (param
, value_to_use
)]
456 future
= executor
.submit(set_smbconf_arbitrary
, cmdline
, self
.topdir
, param
, param_type
, value_to_use
)
457 result_futures1
.append(future
)
459 opposite_value
= opposite_arbitrary
.get(param_type
)
461 cmdline
= program
+ ["--suppress-prompt",
463 "%s = %s" % (param
, value_to_use
)]
465 future
= executor
.submit(set_smbconf_arbitrary_opposite
, cmdline
, self
.topdir
, self
.tempdir
,
466 section
, param
, param_type
, opposite_value
, value_to_use
)
467 result_futures2
.append(future
)
469 for f
in concurrent
.futures
.as_completed(result_futures1
):
471 result
, param
, value_to_use
= f
.result()
473 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
474 failset
.add("%s\n Got: %s" % (doc_triple
, result
))
476 for f
in concurrent
.futures
.as_completed(result_futures2
):
478 param
, value_to_use
, value_found
= f
.result()
480 doc_triple
= "%s\n Expected: %s" % (param
, value_to_use
)
481 failset
.add("%s\n Got: %s" % (doc_triple
, value_found
))
484 self
.fail(self
._format
_message
(failset
,
485 "Parameters that were unexpectedly not set:"))
487 def _test_empty(self
, program
):
489 if program
[0] == 'bin/samba-tool' and os
.getenv("PYTHON", None):
490 program
= [os
.environ
["PYTHON"]] + program
492 program_arg1
= ["--configfile=%s" % (self
.blankconf
), "--suppress-prompt"]
493 if (program
[0] == 'bin/testparm'):
494 program_arg1
= ["--suppress-prompt", self
.blankconf
]
496 print(program
+ program_arg1
)
497 p
= subprocess
.Popen(program
+ program_arg1
,
498 stdout
=subprocess
.PIPE
,
499 stderr
=subprocess
.PIPE
,
500 cwd
=self
.topdir
).communicate()
503 for line
in p
[0].decode().splitlines():
504 if line
.strip().startswith('#'):
506 if line
.strip().startswith("idmap config *"):
508 output
+= line
.strip().lower() + '\n'
510 if output
.strip() != '[global]' and output
.strip() != '[globals]':
511 self
.fail("Testparm returned unexpected output on an empty smb.conf.")