2012-09-26 04:57:16 +04: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
2013-12-24 06:33:42 +04:00
from samba . tests import TestSkipped , TestCaseInTempDir
2012-09-26 04:57:16 +04:00
2012-09-28 20:52:41 +04:00
import errno
2012-09-26 04:57:16 +04:00
import os
import re
import subprocess
2013-12-24 06:33:42 +04:00
import xml . etree . ElementTree as ET
2012-09-26 04:57:16 +04:00
2013-12-24 06:33:42 +04:00
class TestCase ( samba . tests . TestCaseInTempDir ) :
2012-09-26 04:57:16 +04:00
def _format_message ( self , parameters , message ) :
parameters = list ( parameters )
parameters . sort ( )
return message + ' \n \n %s ' % ( ' \n ' . join ( parameters ) )
def get_documented_parameters ( sourcedir ) :
2012-10-19 11:16:55 +04: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 20:52:41 +04:00
try :
2013-12-24 06:33:42 +04:00
p = open ( os . path . join ( path , " parameters.all.xml " ) , ' r ' )
except IOError , e :
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 " :
continue
yield name
syn = parameter . findall ( ' synonym ' )
if syn is not None :
for sy in syn :
yield sy . text
p . close ( )
2012-09-26 04:57:16 +04:00
def get_implementation_parameters ( sourcedir ) :
# Reading entries from source code
f = open ( os . path . join ( sourcedir , " lib/param/param_table.c " ) , " r " )
try :
# burn through the preceding lines
while True :
l = f . readline ( )
2014-02-24 03:31:43 +04:00
if l . startswith ( " struct parm_struct parm_table " ) :
2012-09-26 04:57:16 +04:00
break
for l in f . readlines ( ) :
if re . match ( " ^ \ s* \ } \ ; \ s*$ " , l ) :
break
# pull in the param names only
if re . match ( " .*P_SEPARATOR.* " , l ) :
continue
m = re . match ( " \ s* \ .label \ s*= \ s* \" (.*) \" .* " , l )
if not m :
continue
name = m . group ( 1 )
2012-09-27 00:20:42 +04:00
yield name
2012-09-26 04:57:16 +04:00
finally :
f . close ( )
2014-03-06 02:36:06 +04:00
def get_documented_tuples ( sourcedir , omit_no_default = True ) :
2013-12-24 06:33:42 +04: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 ' )
except IOError , e :
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 02:36:06 +04:00
param_type = parameter . attrib . get ( " type " )
if parameter . attrib . get ( ' removed ' ) == " 1 " :
continue
2013-12-24 06:33:42 +04:00
values = parameter . findall ( " value " )
defaults = [ ]
for value in values :
if value . attrib . get ( " type " ) == " default " :
defaults . append ( value )
2014-03-06 02:36:06 +04:00
default_text = None
2013-12-24 06:33:42 +04:00
if len ( defaults ) == 0 :
2014-03-06 02:36:06 +04:00
if omit_no_default :
continue
2013-12-24 06:33:42 +04:00
elif len ( defaults ) > 1 :
raise Exception ( " More than one default found for parameter %s " % name )
2014-03-06 02:36:06 +04:00
else :
default_text = defaults [ 0 ] . text
2013-12-24 06:33:42 +04:00
if default_text is None :
default_text = " "
context = parameter . attrib . get ( " context " )
2014-03-06 02:36:06 +04:00
yield name , default_text , context , param_type
2013-12-24 06:33:42 +04:00
p . close ( )
2012-09-26 04:57:16 +04:00
class SmbDotConfTests ( TestCase ) :
2013-12-24 06:33:42 +04:00
# defines the cases where the defaults may differ from the documentation
special_cases = set ( [ ' log level ' , ' path ' , ' ldapsam:trusted ' , ' spoolss: architecture ' ,
' share:fake_fscaps ' , ' ldapsam:editposix ' , ' rpc_daemon:DAEMON ' ,
' rpc_server:SERVER ' , ' panic action ' , ' homedir map ' , ' NIS homedir ' ,
' server string ' , ' netbios name ' , ' socket options ' , ' use mmap ' ,
' ctdbd socket ' , ' printing ' , ' printcap name ' , ' queueresume command ' ,
' queuepause command ' , ' lpresume command ' , ' lppause command ' ,
2014-05-22 13:42:29 +04:00
' lprm command ' , ' lpq command ' , ' print command ' , ' template homedir ' ,
' spoolss: os_major ' , ' spoolss: os_minor ' , ' spoolss: os_build ' ] )
2013-12-24 06:33:42 +04: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 08:04:06 +04:00
self . blankconf = os . path . join ( self . tempdir , " emptytestsmb.conf " )
f = open ( self . blankconf , ' w ' )
try :
f . write ( " " )
finally :
f . close ( )
2013-12-24 06:33:42 +04:00
def tearDown ( self ) :
super ( SmbDotConfTests , self ) . tearDown ( )
os . unlink ( self . smbconf )
2014-07-03 08:04:06 +04:00
os . unlink ( self . blankconf )
2013-12-24 06:33:42 +04:00
2012-09-26 20:41:19 +04:00
def test_unknown ( self ) :
2013-12-24 06:33:42 +04:00
topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
2012-09-28 20:52:41 +04:00
try :
documented = set ( get_documented_parameters ( topdir ) )
2013-12-24 06:33:42 +04:00
except e :
self . fail ( " Unable to load parameters " )
2012-09-26 04:57:16 +04:00
parameters = set ( get_implementation_parameters ( topdir ) )
2012-09-26 20:41:19 +04:00
# Filter out parametric options, since we can't find them in the parm
# table
documented = set ( [ p for p in documented if not " : " in p ] )
2012-09-26 04:57:16 +04:00
unknown = documented . difference ( parameters )
if len ( unknown ) > 0 :
self . fail ( self . _format_message ( unknown ,
" Parameters that are documented but not in the implementation: " ) )
2012-09-26 20:41:19 +04:00
def test_undocumented ( self ) :
2013-12-24 06:33:42 +04:00
topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
2012-09-28 20:52:41 +04:00
try :
documented = set ( get_documented_parameters ( topdir ) )
2013-12-24 06:33:42 +04:00
except :
self . fail ( " Unable to load parameters " )
2012-09-26 20:41:19 +04:00
parameters = set ( get_implementation_parameters ( topdir ) )
2012-09-26 04:57:16 +04:00
undocumented = parameters . difference ( documented )
if len ( undocumented ) > 0 :
self . fail ( self . _format_message ( undocumented ,
" Parameters that are in the implementation but undocumented: " ) )
2013-12-24 06:33:42 +04:00
def test_default_s3 ( self ) :
self . _test_default ( [ ' bin/testparm ' ] )
2014-03-06 01:12:09 +04:00
self . _set_defaults ( [ ' bin/testparm ' ] )
2013-12-24 06:33:42 +04:00
2014-03-06 02:36:06 +04:00
# registry shares appears to need sudo
self . _set_arbitrary ( [ ' bin/testparm ' ] ,
exceptions = [ ' client lanman auth ' ,
' client plaintext auth ' ,
' registry shares ' ,
2014-03-14 01:27:54 +04:00
' smb ports ' ] )
2014-07-03 08:04:06 +04:00
self . _test_empty ( [ ' bin/testparm ' ] )
2014-03-06 02:36:06 +04:00
2013-12-24 06:33:42 +04:00
def test_default_s4 ( self ) :
self . _test_default ( [ ' bin/samba-tool ' , ' testparm ' ] )
2014-03-06 01:12:09 +04:00
self . _set_defaults ( [ ' bin/samba-tool ' , ' testparm ' ] )
2014-03-14 01:27:54 +04:00
self . _set_arbitrary ( [ ' bin/samba-tool ' , ' testparm ' ] ,
exceptions = [ ' smb ports ' ] )
2014-07-03 08:04:06 +04:00
self . _test_empty ( [ ' bin/samba-tool ' , ' testparm ' ] )
2013-12-24 06:33:42 +04:00
def _test_default ( self , program ) :
topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
try :
2014-03-06 02:36:06 +04:00
defaults = set ( get_documented_tuples ( topdir ) )
2013-12-24 06:33:42 +04:00
except :
self . fail ( " Unable to load parameters " )
bindir = os . path . join ( topdir , " bin " )
failset = set ( )
count = 0
2014-03-06 02:36:06 +04:00
for tuples in defaults :
param , default , context , param_type = tuples
2013-12-24 06:33:42 +04:00
if param in self . special_cases :
continue
section = None
if context == " G " :
section = " global "
elif context == " S " :
section = " test "
else :
self . fail ( " %s has no valid context " % param )
p = subprocess . Popen ( program + [ " -s " , self . smbconf ,
" --section-name " , section , " --parameter-name " , param ] ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE , cwd = topdir ) . communicate ( )
if p [ 0 ] . upper ( ) . strip ( ) != default . upper ( ) :
if not ( p [ 0 ] . upper ( ) . strip ( ) == " " and default == ' " " ' ) :
doc_triple = " %s \n Expected: %s " % ( param , default )
failset . add ( " %s \n Got: %s " % ( doc_triple , p [ 0 ] . upper ( ) . strip ( ) ) )
if len ( failset ) > 0 :
self . fail ( self . _format_message ( failset ,
" Parameters that do not have matching defaults: " ) )
2014-03-06 01:12:09 +04:00
def _set_defaults ( self , program ) :
topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
try :
2014-03-06 02:36:06 +04:00
defaults = set ( get_documented_tuples ( topdir ) )
2014-03-06 01:12:09 +04:00
except :
self . fail ( " Unable to load parameters " )
bindir = os . path . join ( topdir , " bin " )
failset = set ( )
count = 0
2014-03-06 02:36:06 +04:00
for tuples in defaults :
param , default , context , param_type = tuples
2014-03-06 01:12:09 +04:00
2014-03-13 03:24:36 +04:00
if param in [ ' printing ' ] :
2014-03-06 01:12:09 +04:00
continue
section = None
if context == " G " :
section = " global "
elif context == " S " :
section = " test "
else :
self . fail ( " %s has no valid context " % param )
p = subprocess . Popen ( program + [ " -s " , self . smbconf ,
" --section-name " , section , " --parameter-name " , param ,
" --option " , " %s = %s " % ( param , default ) ] ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE , cwd = topdir ) . communicate ( )
if p [ 0 ] . upper ( ) . strip ( ) != default . upper ( ) :
if not ( p [ 0 ] . upper ( ) . strip ( ) == " " and default == ' " " ' ) :
doc_triple = " %s \n Expected: %s " % ( param , default )
failset . add ( " %s \n Got: %s " % ( doc_triple , p [ 0 ] . upper ( ) . strip ( ) ) )
if len ( failset ) > 0 :
self . fail ( self . _format_message ( failset ,
" Parameters that do not have matching defaults: " ) )
2014-03-06 02:36:06 +04:00
def _set_arbitrary ( self , program , exceptions = None ) :
arbitrary = { ' string ' : ' string ' , ' boolean ' : ' yes ' , ' integer ' : ' 5 ' ,
' enum ' : ' ' , ' boolean-auto ' : ' ' , ' char ' : ' a ' , ' list ' : ' a, b, c ' }
2014-07-03 08:04:06 +04:00
opposite_arbitrary = { ' string ' : ' string2 ' , ' boolean ' : ' no ' , ' integer ' : ' 6 ' ,
' enum ' : ' ' , ' boolean-auto ' : ' ' , ' char ' : ' b ' , ' list ' : ' d, e, f ' }
2014-03-06 02:36:06 +04:00
topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
try :
defaults = set ( get_documented_tuples ( topdir , False ) )
except Exception , e :
self . fail ( " Unable to load parameters " + e )
bindir = os . path . join ( topdir , " bin " )
failset = set ( )
count = 0
for tuples in defaults :
param , default , context , param_type = tuples
2014-03-13 03:24:36 +04:00
if param in [ ' printing ' , ' copy ' , ' include ' , ' log level ' ] :
2014-03-06 02:36:06 +04:00
continue
# currently no easy way to set an arbitrary value for these
if param_type in [ ' enum ' , ' boolean-auto ' ] :
continue
if exceptions is not None :
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 )
value_to_use = arbitrary . get ( param_type )
if value_to_use is None :
self . fail ( " %s has an invalid type " % param )
p = subprocess . Popen ( program + [ " -s " , self . smbconf ,
" --section-name " , section , " --parameter-name " , param ,
" --option " , " %s = %s " % ( param , value_to_use ) ] ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE , cwd = topdir ) . communicate ( )
if p [ 0 ] . upper ( ) . strip ( ) != value_to_use . upper ( ) :
# currently no way to distinguish command lists
if param_type == ' list ' :
if " , " . join ( p [ 0 ] . upper ( ) . strip ( ) . split ( ) ) == value_to_use . upper ( ) :
continue
# currently no way to identify octal
if param_type == ' integer ' :
try :
if int ( value_to_use , 8 ) == int ( p [ 0 ] . strip ( ) , 8 ) :
continue
except :
pass
doc_triple = " %s \n Expected: %s " % ( param , value_to_use )
failset . add ( " %s \n Got: %s " % ( doc_triple , p [ 0 ] . upper ( ) . strip ( ) ) )
2014-07-03 08:04:06 +04:00
opposite_value = opposite_arbitrary . get ( param_type )
tempconf = os . path . join ( self . tempdir , " tempsmb.conf " )
g = open ( tempconf , ' w ' )
try :
towrite = section + " \n "
towrite + = param + " = " + opposite_value
g . write ( towrite )
finally :
g . close ( )
p = subprocess . Popen ( program + [ " -s " , tempconf , " --suppress-prompt " ,
" --option " , " %s = %s " % ( param , value_to_use ) ] ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE , cwd = topdir ) . communicate ( )
os . unlink ( tempconf )
# testparm doesn't display a value if they are equivalent
if ( value_to_use . lower ( ) != opposite_value . lower ( ) ) :
for line in p [ 0 ] . splitlines ( ) :
if not line . strip ( ) . startswith ( param ) :
continue
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 ( ) :
continue
# currently no way to identify octal
if param_type == ' integer ' :
try :
if int ( value_to_use , 8 ) == int ( value_found , 8 ) :
continue
except :
pass
doc_triple = " %s \n Expected: %s " % ( param , value_to_use )
failset . add ( " %s \n Got: %s " % ( doc_triple , value_found ) )
2014-03-06 02:36:06 +04:00
if len ( failset ) > 0 :
self . fail ( self . _format_message ( failset ,
" Parameters that were unexpectedly not set: " ) )
2014-07-03 08:04:06 +04:00
def _test_empty ( self , program ) :
topdir = os . path . abspath ( samba . source_tree_topdir ( ) )
p = subprocess . Popen ( program + [ " -s " , self . blankconf , " --suppress-prompt " ] ,
stdout = subprocess . PIPE , stderr = subprocess . PIPE , cwd = topdir ) . communicate ( )
output = " "
for line in p [ 0 ] . splitlines ( ) :
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. " )