2012-09-26 02:57:16 +02:00
# Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
#
# Tests for documentation.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
""" Tests for presence of documentation. """
import samba
import samba . tests
import os
import subprocess
2013-12-24 15:33:42 +13:00
import xml . etree . ElementTree as ET
2020-06-17 10:04:27 +02:00
import multiprocessing
import concurrent . futures
2020-06-17 11:24:13 +02:00
import tempfile
2012-09-26 02:57:16 +02:00
2013-12-24 15:33:42 +13:00
class TestCase ( samba . tests . TestCaseInTempDir ) :
2012-09-26 02:57:16 +02:00
def _format_message ( self , parameters , message ) :
parameters = list ( parameters )
2018-09-26 17:22:16 +01:00
parameters = list ( map ( str , parameters ) )
2012-09-26 02:57:16 +02:00
parameters . sort ( )
return message + ' \n \n %s ' % ( ' \n ' . join ( parameters ) )
2020-06-17 10:04:27 +02:00
def get_max_worker_count ( ) :
cpu_count = multiprocessing . cpu_count ( )
# Always run two processes in parallel
2020-06-19 17:35:19 +02:00
if cpu_count < 2 :
2020-06-17 10:04:27 +02:00
return 2
max_workers = int ( cpu_count / 2 )
2020-06-19 17:35:19 +02:00
if max_workers < 2 :
return 2
2020-06-17 10:04:27 +02:00
return max_workers
def check_or_set_smbconf_default ( cmdline , topdir , param , default_param ) :
p = subprocess . Popen ( cmdline ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
cwd = topdir ) . communicate ( )
result = p [ 0 ] . decode ( ) . upper ( ) . strip ( )
if result != default_param . upper ( ) :
if not ( result == " " and default_param == ' " " ' ) :
return result , param , default_param
return None
2014-12-14 20:03:28 +00:00
2021-11-19 16:16:30 +13:00
def set_smbconf_arbitrary ( cmdline , topdir , param , param_type , value_to_use ) :
2020-06-17 11:24:13 +02:00
p = subprocess . Popen ( cmdline ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
cwd = topdir ) . communicate ( )
result = p [ 0 ] . decode ( ) . upper ( ) . strip ( )
if result != value_to_use . upper ( ) :
# currently no way to distinguish command lists
if param_type == ' list ' :
if " , " . join ( result . split ( ) ) == value_to_use . upper ( ) :
return None
# currently no way to identify octal
if param_type == ' integer ' :
try :
if int ( value_to_use , 8 ) == int ( p [ 0 ] . strip ( ) , 8 ) :
return None
except :
pass
return result , param , value_to_use
return None
2021-11-19 16:15:50 +13:00
def set_smbconf_arbitrary_opposite ( cmdline , topdir , tempdir , section , param ,
param_type , opposite_value , value_to_use ) :
2020-06-17 11:24:13 +02:00
g = tempfile . NamedTemporaryFile ( mode = ' w ' , dir = tempdir , delete = False )
try :
towrite = section + " \n "
towrite + = param + " = " + opposite_value
g . write ( towrite )
finally :
g . close ( )
p = subprocess . Popen ( cmdline + [ " -s " , g . name ] ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
cwd = topdir ) . communicate ( )
os . unlink ( g . name )
# testparm doesn't display a value if they are equivalent
if ( value_to_use . lower ( ) != opposite_value . lower ( ) ) :
for line in p [ 0 ] . decode ( ) . splitlines ( ) :
if not line . strip ( ) . startswith ( param ) :
return None
value_found = line . split ( " = " ) [ 1 ] . upper ( ) . strip ( )
if value_found != value_to_use . upper ( ) :
# currently no way to distinguish command lists
if param_type == ' list ' :
if " , " . join ( value_found . split ( ) ) == value_to_use . upper ( ) :
return None
# currently no way to identify octal
if param_type == ' integer ' :
try :
if int ( value_to_use , 8 ) == int ( value_found , 8 ) :
continue
except :
pass
return param , value_to_use , value_found
return None
2012-09-26 02:57:16 +02:00
def get_documented_parameters ( sourcedir ) :
2012-10-19 09:16:55 +02:00
path = os . path . join ( sourcedir , " bin " , " default " , " docs-xml " , " smbdotconf " )
if not os . path . exists ( os . path . join ( path , " parameters.all.xml " ) ) :
raise Exception ( " Unable to find parameters.all.xml " )
2012-09-28 09:52:41 -07:00
try :
2013-12-24 15:33:42 +13:00
p = open ( os . path . join ( path , " parameters.all.xml " ) , ' r ' )
2018-02-14 10:27:52 +13:00
except IOError as e :
2013-12-24 15:33:42 +13:00
raise Exception ( " Error opening parameters file " )
out = p . read ( )
root = ET . fromstring ( out )
for parameter in root :
name = parameter . attrib . get ( ' name ' )
if parameter . attrib . get ( ' removed ' ) == " 1 " :
2018-07-30 18:13:57 +12:00
continue
2013-12-24 15:33:42 +13:00
yield name
syn = parameter . findall ( ' synonym ' )
if syn is not None :
for sy in syn :
yield sy . text
p . close ( )
2012-09-26 02:57:16 +02:00
2014-03-06 11:36:06 +13:00
def get_documented_tuples ( sourcedir , omit_no_default = True ) :
2013-12-24 15:33:42 +13:00
path = os . path . join ( sourcedir , " bin " , " default " , " docs-xml " , " smbdotconf " )
if not os . path . exists ( os . path . join ( path , " parameters.all.xml " ) ) :
raise Exception ( " Unable to find parameters.all.xml " )
try :
p = open ( os . path . join ( path , " parameters.all.xml " ) , ' r ' )
2018-02-14 10:27:52 +13:00
except IOError as e :
2013-12-24 15:33:42 +13:00
raise Exception ( " Error opening parameters file " )
out = p . read ( )
root = ET . fromstring ( out )
for parameter in root :
name = parameter . attrib . get ( " name " )
2014-03-06 11:36:06 +13:00
param_type = parameter . attrib . get ( " type " )
if parameter . attrib . get ( ' removed ' ) == " 1 " :
2018-07-30 18:13:57 +12:00
continue
2013-12-24 15:33:42 +13:00
values = parameter . findall ( " value " )
defaults = [ ]
for value in values :
if value . attrib . get ( " type " ) == " default " :
defaults . append ( value )
2014-03-06 11:36:06 +13:00
default_text = None
2013-12-24 15:33:42 +13:00
if len ( defaults ) == 0 :
2014-03-06 11:36:06 +13:00
if omit_no_default :
continue
2013-12-24 15:33:42 +13:00
elif len ( defaults ) > 1 :
raise Exception ( " More than one default found for parameter %s " % name )
2014-03-06 11:36:06 +13:00
else :
default_text = defaults [ 0 ] . text
2013-12-24 15:33:42 +13:00
if default_text is None :
default_text = " "
context = parameter . attrib . get ( " context " )
2014-03-06 11:36:06 +13:00
yield name , default_text , context , param_type
2013-12-24 15:33:42 +13:00
p . close ( )
2012-09-26 02:57:16 +02:00
2018-07-30 18:20:39 +12:00
2012-09-26 02:57:16 +02:00
class SmbDotConfTests ( TestCase ) :
2013-12-24 15:33:42 +13:00
# defines the cases where the defaults may differ from the documentation
2020-06-17 09:25:17 +12:00
#
# Please pass the default via waf rather than adding to this
# list if at all possible.
2018-12-03 14:59:55 +01:00
special_cases = set ( [
' log level ' ,
' path ' ,
' panic action ' ,
' homedir map ' ,
' NIS homedir ' ,
' server string ' ,
' netbios name ' ,
' socket options ' ,
' ctdbd socket ' ,
' printing ' ,
' printcap name ' ,
' queueresume command ' ,
' queuepause command ' ,
' lpresume command ' ,
' lppause command ' ,
' lprm command ' ,
' lpq command ' ,
' print command ' ,
' template homedir ' ,
' max open files ' ,
' include system krb5 conf ' ,
2018-12-02 09:22:56 +01:00
' smbd max async dosmode ' ,
2018-12-03 14:59:55 +01:00
] )
2013-12-24 15:33:42 +13:00
def setUp ( self ) :
super ( SmbDotConfTests , self ) . setUp ( )
# create a minimal smb.conf file for testparm
self . smbconf = os . path . join ( self . tempdir , " paramtestsmb.conf " )
f = open ( self . smbconf , ' w ' )
try :
f . write ( """
[ test ]
path = /
""" )
finally :
f . close ( )
2014-07-03 16:04:06 +12:00
self . blankconf = os . path . join ( self . tempdir , " emptytestsmb.conf " )
f = open ( self . blankconf , ' w ' )
try :
f . write ( " " )
finally :
f . close ( )
2015-07-22 15:16:16 +02:00
self . topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
try :
self . documented = set ( get_documented_parameters ( self . topdir ) )
except :
self . fail ( " Unable to load documented parameters " )
try :
self . defaults = set ( get_documented_tuples ( self . topdir ) )
except :
self . fail ( " Unable to load parameters " )
try :
self . defaults_all = set ( get_documented_tuples ( self . topdir , False ) )
except :
self . fail ( " Unable to load parameters " )
2013-12-24 15:33:42 +13:00
def tearDown ( self ) :
super ( SmbDotConfTests , self ) . tearDown ( )
os . unlink ( self . smbconf )
2014-07-03 16:04:06 +12:00
os . unlink ( self . blankconf )
2013-12-24 15:33:42 +13:00
def test_default_s3 ( self ) :
self . _test_default ( [ ' bin/testparm ' ] )
2014-03-06 10:12:09 +13:00
self . _set_defaults ( [ ' bin/testparm ' ] )
2013-12-24 15:33:42 +13:00
2014-03-06 11:36:06 +13:00
# registry shares appears to need sudo
self . _set_arbitrary ( [ ' bin/testparm ' ] ,
2018-08-21 14:58:01 -07:00
exceptions = [ ' client lanman auth ' ,
' client plaintext auth ' ,
' registry shares ' ,
' smb ports ' ,
' rpc server dynamic port range ' ,
' name resolve order ' ,
' clustering ' ] )
2014-07-03 16:04:06 +12:00
self . _test_empty ( [ ' bin/testparm ' ] )
2014-03-06 11:36:06 +13:00
2013-12-24 15:33:42 +13:00
def test_default_s4 ( self ) :
self . _test_default ( [ ' bin/samba-tool ' , ' testparm ' ] )
2014-03-06 10:12:09 +13:00
self . _set_defaults ( [ ' bin/samba-tool ' , ' testparm ' ] )
2014-03-14 10:27:54 +13:00
self . _set_arbitrary ( [ ' bin/samba-tool ' , ' testparm ' ] ,
2018-07-30 18:19:21 +12:00
exceptions = [ ' smb ports ' ,
2018-08-22 18:27:10 +12:00
' rpc server dynamic port range ' ,
' name resolve order ' ] )
2014-07-03 16:04:06 +12:00
self . _test_empty ( [ ' bin/samba-tool ' , ' testparm ' ] )
2013-12-24 15:33:42 +13:00
def _test_default ( self , program ) :
2018-09-27 16:08:34 +01:00
if program [ 0 ] == ' bin/samba-tool ' and os . getenv ( " PYTHON " , None ) :
program = [ os . environ [ " PYTHON " ] ] + program
2013-12-24 15:33:42 +13:00
failset = set ( )
2020-06-17 10:04:27 +02:00
with concurrent . futures . ProcessPoolExecutor ( max_workers = get_max_worker_count ( ) ) as executor :
result_futures = [ ]
for tuples in self . defaults :
param , default , context , param_type = tuples
if param in self . special_cases :
continue
# bad, bad parametric options - we don't have their default values
if ' : ' in param :
continue
section = None
if context == " G " :
section = " global "
elif context == " S " :
section = " test "
else :
self . fail ( " %s has no valid context " % param )
2021-04-14 11:44:51 +02:00
program_arg1 = [ " --configfile= %s " % ( self . smbconf ) ]
if ( program [ 0 ] == ' bin/testparm ' ) :
program_arg1 = [ " --suppress-prompt " , self . smbconf ]
cmdline = program + program_arg1 + [
2020-06-17 10:04:27 +02:00
" --section-name " ,
section ,
" --parameter-name " ,
param ]
future = executor . submit ( check_or_set_smbconf_default , cmdline , self . topdir , param , default )
result_futures . append ( future )
for f in concurrent . futures . as_completed ( result_futures ) :
if f . result ( ) :
result , param , default_param = f . result ( )
doc_triple = " %s \n Expected: %s " % ( param , default_param )
2018-04-05 13:48:36 +12:00
failset . add ( " %s \n Got: %s " % ( doc_triple , result ) )
2013-12-24 15:33:42 +13:00
if len ( failset ) > 0 :
self . fail ( self . _format_message ( failset ,
2018-07-30 18:16:12 +12:00
" Parameters that do not have matching defaults: " ) )
2014-03-06 10:12:09 +13:00
def _set_defaults ( self , program ) :
2018-09-27 16:08:34 +01:00
if program [ 0 ] == ' bin/samba-tool ' and os . getenv ( " PYTHON " , None ) :
program = [ os . environ [ " PYTHON " ] ] + program
2014-03-06 10:12:09 +13:00
failset = set ( )
2020-06-17 10:04:27 +02:00
with concurrent . futures . ProcessPoolExecutor ( max_workers = get_max_worker_count ( ) ) as executor :
result_futures = [ ]
for tuples in self . defaults :
param , default , context , param_type = tuples
exceptions = set ( [
' printing ' ,
' smbd max async dosmode ' ,
] )
if param in exceptions :
continue
section = None
if context == " G " :
section = " global "
elif context == " S " :
section = " test "
else :
self . fail ( " %s has no valid context " % param )
2021-04-14 11:44:51 +02:00
program_arg1 = [ " --configfile= %s " % ( self . smbconf ) ]
if ( program [ 0 ] == ' bin/testparm ' ) :
program_arg1 = [ " --suppress-prompt " , self . smbconf ]
cmdline = program + program_arg1 + [
2020-06-17 10:04:27 +02:00
" --section-name " ,
section ,
" --parameter-name " ,
param ,
" --option " ,
" %s = %s " % ( param , default ) ]
future = executor . submit ( check_or_set_smbconf_default , cmdline , self . topdir , param , default )
result_futures . append ( future )
for f in concurrent . futures . as_completed ( result_futures ) :
if f . result ( ) :
result , param , default_param = f . result ( )
2014-03-06 10:12:09 +13:00
doc_triple = " %s \n Expected: %s " % ( param , default )
2018-04-05 13:48:36 +12:00
failset . add ( " %s \n Got: %s " % ( doc_triple , result ) )
2014-03-06 10:12:09 +13:00
if len ( failset ) > 0 :
self . fail ( self . _format_message ( failset ,
2018-07-30 18:16:12 +12:00
" Parameters that do not have matching defaults: " ) )
2014-03-06 11:36:06 +13:00
def _set_arbitrary ( self , program , exceptions = None ) :
2018-09-27 16:08:34 +01:00
if program [ 0 ] == ' bin/samba-tool ' and os . getenv ( " PYTHON " , None ) :
program = [ os . environ [ " PYTHON " ] ] + program
2014-03-06 11:36:06 +13:00
arbitrary = { ' string ' : ' string ' , ' boolean ' : ' yes ' , ' integer ' : ' 5 ' ,
2015-07-22 10:31:46 +02:00
' boolean-rev ' : ' yes ' ,
2015-07-24 01:28:56 +02:00
' cmdlist ' : ' a b c ' ,
' bytes ' : ' 10 ' ,
' octal ' : ' 0123 ' ,
' ustring ' : ' ustring ' ,
2018-07-30 18:19:05 +12:00
' enum ' : ' ' , ' boolean-auto ' : ' ' , ' char ' : ' a ' , ' list ' : ' a, b, c ' }
2014-07-03 16:04:06 +12:00
opposite_arbitrary = { ' string ' : ' string2 ' , ' boolean ' : ' no ' , ' integer ' : ' 6 ' ,
2015-07-22 10:31:46 +02:00
' boolean-rev ' : ' no ' ,
2015-07-24 01:28:56 +02:00
' cmdlist ' : ' d e f ' ,
' bytes ' : ' 11 ' ,
' octal ' : ' 0567 ' ,
' ustring ' : ' ustring2 ' ,
2018-07-30 18:19:05 +12:00
' enum ' : ' ' , ' boolean-auto ' : ' ' , ' char ' : ' b ' , ' list ' : ' d, e, f ' }
2015-07-22 15:16:16 +02:00
2014-03-06 11:36:06 +13:00
failset = set ( )
2020-06-17 11:24:13 +02:00
with concurrent . futures . ProcessPoolExecutor ( max_workers = get_max_worker_count ( ) ) as executor :
result_futures1 = [ ]
result_futures2 = [ ]
2014-03-06 11:36:06 +13:00
2020-06-17 11:24:13 +02:00
for tuples in self . defaults_all :
param , default , context , param_type = tuples
2014-03-06 11:36:06 +13:00
2020-06-17 11:24:13 +02:00
if param in [ ' printing ' , ' copy ' , ' include ' , ' log level ' ] :
continue
2014-03-06 11:36:06 +13:00
2020-06-17 11:24:13 +02:00
# currently no easy way to set an arbitrary value for these
if param_type in [ ' enum ' , ' boolean-auto ' ] :
2014-03-06 11:36:06 +13:00
continue
2020-06-17 11:24:13 +02:00
if exceptions is not None :
if param in exceptions :
2014-03-06 11:36:06 +13:00
continue
2020-06-17 11:24:13 +02:00
section = None
if context == " G " :
section = " global "
elif context == " S " :
section = " test "
else :
self . fail ( " %s has no valid context " % param )
2014-03-06 11:36:06 +13:00
2020-06-17 11:24:13 +02:00
value_to_use = arbitrary . get ( param_type )
if value_to_use is None :
self . fail ( " %s has an invalid type " % param )
2021-04-14 11:44:51 +02:00
program_arg1 = [ " --configfile= %s " % ( self . smbconf ) ]
if ( program [ 0 ] == ' bin/testparm ' ) :
program_arg1 = [ " --suppress-prompt " , self . smbconf ]
cmdline = program + program_arg1 + [
2020-06-17 11:24:13 +02:00
" --section-name " ,
section ,
" --parameter-name " ,
param ,
" --option " ,
" %s = %s " % ( param , value_to_use ) ]
2021-11-19 16:16:30 +13:00
future = executor . submit ( set_smbconf_arbitrary , cmdline , self . topdir , param , param_type , value_to_use )
2020-06-17 11:24:13 +02:00
result_futures1 . append ( future )
opposite_value = opposite_arbitrary . get ( param_type )
cmdline = program + [ " --suppress-prompt " ,
" --option " ,
" %s = %s " % ( param , value_to_use ) ]
2021-11-19 16:15:50 +13:00
future = executor . submit ( set_smbconf_arbitrary_opposite , cmdline , self . topdir , self . tempdir ,
section , param , param_type , opposite_value , value_to_use )
2020-06-17 11:24:13 +02:00
result_futures2 . append ( future )
for f in concurrent . futures . as_completed ( result_futures1 ) :
if f . result ( ) :
result , param , value_to_use = f . result ( )
doc_triple = " %s \n Expected: %s " % ( param , value_to_use )
failset . add ( " %s \n Got: %s " % ( doc_triple , result ) )
for f in concurrent . futures . as_completed ( result_futures2 ) :
if f . result ( ) :
param , value_to_use , value_found = f . result ( )
2014-07-03 16:04:06 +12:00
2020-06-17 11:24:13 +02:00
doc_triple = " %s \n Expected: %s " % ( param , value_to_use )
failset . add ( " %s \n Got: %s " % ( doc_triple , value_found ) )
2014-07-03 16:04:06 +12:00
2014-03-06 11:36:06 +13:00
if len ( failset ) > 0 :
self . fail ( self . _format_message ( failset ,
2018-07-30 18:16:12 +12:00
" Parameters that were unexpectedly not set: " ) )
2014-07-03 16:04:06 +12:00
def _test_empty ( self , program ) :
2018-09-27 16:08:34 +01:00
if program [ 0 ] == ' bin/samba-tool ' and os . getenv ( " PYTHON " , None ) :
program = [ os . environ [ " PYTHON " ] ] + program
2021-04-14 11:44:51 +02:00
program_arg1 = [ " --configfile= %s " % ( self . blankconf ) , " --suppress-prompt " ]
if ( program [ 0 ] == ' bin/testparm ' ) :
program_arg1 = [ " --suppress-prompt " , self . blankconf ]
print ( program + program_arg1 )
p = subprocess . Popen ( program + program_arg1 ,
2018-07-30 18:16:12 +12:00
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE ,
cwd = self . topdir ) . communicate ( )
2014-07-03 16:04:06 +12:00
output = " "
2018-04-05 13:48:36 +12:00
for line in p [ 0 ] . decode ( ) . splitlines ( ) :
2014-07-03 16:04:06 +12:00
if line . strip ( ) . startswith ( ' # ' ) :
continue
if line . strip ( ) . startswith ( " idmap config * " ) :
continue
output + = line . strip ( ) . lower ( ) + ' \n '
if output . strip ( ) != ' [global] ' and output . strip ( ) != ' [globals] ' :
self . fail ( " Testparm returned unexpected output on an empty smb.conf. " )