2019-03-17 04:47:40 +03:00
# Unix SMB/CIFS implementation.
# Copyright © Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
#
# 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/>.
import os
import sys
import subprocess
2019-06-27 07:57:22 +03:00
from samba . tests import TestCase , check_help_consistency
2019-03-17 04:47:40 +03:00
from unittest import TestSuite
import re
import stat
if ' SRCDIR_ABS ' in os . environ :
BASEDIR = os . environ [ ' SRCDIR_ABS ' ]
else :
BASEDIR = os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) ,
' ../../.. ' ) )
TEST_DIRS = [
" bootstrap " ,
" testdata " ,
" ctdb " ,
" dfs_server " ,
" pidl " ,
" auth " ,
" packaging " ,
" python " ,
" include " ,
" nsswitch " ,
" libcli " ,
" coverity " ,
" release-scripts " ,
" testprogs " ,
" bin " ,
" source3 " ,
" docs-xml " ,
" buildtools " ,
" file_server " ,
" dynconfig " ,
" source4 " ,
" tests " ,
" libds " ,
" selftest " ,
" lib " ,
" script " ,
" traffic " ,
" testsuite " ,
" libgpo " ,
" wintest " ,
" librpc " ,
]
EXCLUDE_USAGE = {
' script/autobuild.py ' , # defaults to mount /memdisk/
' script/bisect-test.py ' ,
' ctdb/utils/etcd/ctdb_etcd_lock ' ,
' selftest/filter-subunit ' ,
' selftest/format-subunit ' ,
' bin/gen_output.py ' , # too much output!
' source4/scripting/bin/gen_output.py ' ,
' lib/ldb/tests/python/index.py ' ,
' lib/ldb/tests/python/api.py ' ,
' source4/selftest/tests.py ' ,
' buildtools/bin/waf ' ,
' selftest/tap2subunit ' ,
' script/show_test_time ' ,
' source4/scripting/bin/subunitrun ' ,
2019-07-22 04:35:21 +03:00
' bin/samba_downgrade_db ' ,
' source4/scripting/bin/samba_downgrade_db ' ,
2019-03-17 04:47:40 +03:00
' source3/selftest/tests.py ' ,
' selftest/tests.py ' ,
' python/samba/subunit/run.py ' ,
' bin/python/samba/subunit/run.py ' ,
' python/samba/tests/dcerpc/raw_protocol.py '
}
2019-06-27 07:57:22 +03:00
EXCLUDE_HELP = {
' selftest/tap2subunit ' ,
' wintest/test-s3.py ' ,
' wintest/test-s4-howto.py ' ,
}
2019-03-17 04:47:40 +03:00
EXCLUDE_DIRS = {
' source3/script/tests ' ,
' python/examples ' ,
' source4/dsdb/tests/python ' ,
' bin/ab ' ,
' bin/python/samba/tests ' ,
' bin/python/samba/tests/dcerpc ' ,
}
def _init_git_file_finder ( ) :
""" Generate a function that quickly answers the question:
' is this a git file? '
"""
git_file_cache = set ( )
p = subprocess . run ( [ ' git ' ,
' -C ' , BASEDIR ,
' ls-files ' ,
' -z ' ] ,
stdout = subprocess . PIPE )
if p . returncode == 0 :
for fn in p . stdout . split ( b ' \0 ' ) :
git_file_cache . add ( os . path . join ( BASEDIR , fn . decode ( ' utf-8 ' ) ) )
return git_file_cache . __contains__
is_git_file = _init_git_file_finder ( )
2019-06-27 07:57:22 +03:00
def script_iterator ( d = BASEDIR , cache = None ,
shebang_filter = None ,
filename_filter = None ,
subdirs = TEST_DIRS ) :
if not cache :
2019-03-17 04:47:40 +03:00
safename = re . compile ( r ' \ W+ ' ) . sub
2019-06-27 07:57:22 +03:00
for subdir in subdirs :
2019-03-17 04:47:40 +03:00
sd = os . path . join ( d , subdir )
for root , dirs , files in os . walk ( sd , followlinks = False ) :
for fn in files :
if fn . endswith ( ' ~ ' ) :
continue
if fn . endswith ( ' .inst ' ) :
continue
ffn = os . path . join ( root , fn )
try :
s = os . stat ( ffn )
except FileNotFoundError :
continue
if not s . st_mode & stat . S_IXUSR :
continue
2019-06-27 07:57:22 +03:00
if not ( subdir == ' bin ' or is_git_file ( ffn ) ) :
2019-03-17 04:47:40 +03:00
continue
2019-06-27 07:57:22 +03:00
if filename_filter is not None :
if not filename_filter ( ffn ) :
continue
if shebang_filter is not None :
try :
f = open ( ffn , ' rb ' )
except OSError as e :
print ( " could not open %s : %s " % ( ffn , e ) )
continue
line = f . read ( 40 )
f . close ( )
if not shebang_filter ( line ) :
continue
2019-03-17 04:47:40 +03:00
name = safename ( ' _ ' , fn )
while name in cache :
name + = ' _ '
cache [ name ] = ffn
2019-06-27 07:57:22 +03:00
return cache . items ( )
# For ELF we only look at /bin/* top level.
def elf_file_name ( fn ) :
fn = fn . partition ( ' bin/ ' ) [ 2 ]
return fn and ' / ' not in fn and ' test ' not in fn and ' ldb ' in fn
def elf_shebang ( x ) :
return x [ : 4 ] == b ' \x7f ELF '
elf_cache = { }
def elf_iterator ( ) :
return script_iterator ( BASEDIR , elf_cache ,
shebang_filter = elf_shebang ,
filename_filter = elf_file_name ,
subdirs = [ ' bin ' ] )
perl_shebang = re . compile ( br ' #!.+perl ' ) . match
perl_script_cache = { }
def perl_script_iterator ( ) :
return script_iterator ( BASEDIR , perl_script_cache , perl_shebang )
python_shebang = re . compile ( br ' #!.+python ' ) . match
python_script_cache = { }
def python_script_iterator ( ) :
return script_iterator ( BASEDIR , python_script_cache , python_shebang )
class PerlScriptUsageTests ( TestCase ) :
""" Perl scripts run without arguments should print a usage string,
not fail with a traceback .
"""
@classmethod
def initialise ( cls ) :
for name , filename in perl_script_iterator ( ) :
print ( name , filename )
2019-03-17 04:47:40 +03:00
class PythonScriptUsageTests ( TestCase ) :
""" Python scripts run without arguments should print a usage string,
not fail with a traceback .
2019-06-27 07:57:22 +03:00
"""
2019-03-17 04:47:40 +03:00
@classmethod
def initialise ( cls ) :
for name , filename in python_script_iterator ( ) :
# We add the actual tests after the class definition so we
# can give individual names to them, so we can have a
# knownfail list.
fn = filename . replace ( BASEDIR , ' ' ) . lstrip ( ' / ' )
if fn in EXCLUDE_USAGE :
print ( " skipping %s (EXCLUDE_USAGE) " % filename )
continue
if os . path . dirname ( fn ) in EXCLUDE_DIRS :
print ( " skipping %s (EXCLUDE_DIRS) " % filename )
continue
def _f ( self , filename = filename ) :
print ( filename )
try :
p = subprocess . Popen ( [ ' python3 ' , filename ] ,
stderr = subprocess . PIPE ,
stdout = subprocess . PIPE )
out , err = p . communicate ( timeout = 5 )
except OSError as e :
self . fail ( " Error: %s " % e )
except subprocess . SubprocessError as e :
self . fail ( " Subprocess error: %s " % e )
err = err . decode ( ' utf-8 ' )
out = out . decode ( ' utf-8 ' )
self . assertNotIn ( ' Traceback ' , err )
self . assertIn ( ' usage ' , out . lower ( ) + err . lower ( ) ,
' stdout: \n %s \n stderr: \n %s ' % ( out , err ) )
setattr ( cls , ' test_ %s ' % name , _f )
2019-06-27 07:57:22 +03:00
class HelpTestSuper ( TestCase ) :
2019-06-27 07:57:22 +03:00
""" Python scripts run with -h or --help should print a help string,
and exit with success .
"""
2019-06-27 07:57:22 +03:00
check_return_code = True
2019-06-27 07:57:22 +03:00
check_consistency = True
2019-06-27 07:57:22 +03:00
check_contains_usage = True
check_multiline = True
check_merged_out_and_err = False
interpreter = None
options_start = None
options_end = None
def iterator ( self ) :
raise NotImplementedError ( " Subclass this "
" and add an iterator function! " )
2019-06-27 07:57:22 +03:00
@classmethod
def initialise ( cls ) :
2019-06-27 07:57:22 +03:00
for name , filename in cls . iterator ( ) :
2019-06-27 07:57:22 +03:00
# We add the actual tests after the class definition so we
# can give individual names to them, so we can have a
# knownfail list.
fn = filename . replace ( BASEDIR , ' ' ) . lstrip ( ' / ' )
if fn in EXCLUDE_HELP :
print ( " skipping %s (EXCLUDE_HELP) " % filename )
continue
if os . path . dirname ( fn ) in EXCLUDE_DIRS :
print ( " skipping %s (EXCLUDE_DIRS) " % filename )
continue
def _f ( self , filename = filename ) :
print ( filename )
for h in ( ' --help ' , ' -h ' ) :
2019-06-27 07:57:22 +03:00
cmd = [ filename , h ]
if self . interpreter :
cmd . insert ( 0 , self . interpreter )
2019-06-27 07:57:22 +03:00
try :
2019-06-27 07:57:22 +03:00
p = subprocess . Popen ( cmd ,
2019-06-27 07:57:22 +03:00
stderr = subprocess . PIPE ,
stdout = subprocess . PIPE )
out , err = p . communicate ( timeout = 5 )
except OSError as e :
self . fail ( " Error: %s " % e )
except subprocess . SubprocessError as e :
self . fail ( " Subprocess error: %s " % e )
err = err . decode ( ' utf-8 ' )
2019-06-27 07:57:22 +03:00
out = out . decode ( ' utf-8 ' )
if self . check_merged_out_and_err :
out = " %s \n %s " % ( out , err )
2019-06-27 07:57:22 +03:00
2019-06-27 07:57:22 +03:00
outl = out [ : 500 ] . lower ( )
2019-06-27 07:57:22 +03:00
# NOTE:
# These assertions are heuristics, not policy.
# If your script fails this test when it shouldn't
# just add it to EXCLUDE_HELP above or change the
# heuristic.
# --help should produce:
# * multiple lines of help on stdout (not stderr),
# * including a "Usage:" string,
2019-06-27 07:57:22 +03:00
# * not contradict itself or repeat options,
2019-06-27 07:57:22 +03:00
# * and return success.
2019-06-27 07:57:22 +03:00
#print(out.encode('utf8'))
#print(err.encode('utf8'))
2019-06-27 07:57:22 +03:00
if self . check_consistency :
errors = check_help_consistency ( out ,
self . options_start ,
self . options_end )
if errors is not None :
self . fail ( errors )
2019-06-27 07:57:22 +03:00
if self . check_return_code :
self . assertEqual ( p . returncode , 0 ,
" %s %s \n returncode should not be %d " %
( filename , h , p . returncode ) )
if self . check_contains_usage :
self . assertIn ( ' usage ' , outl , ' lacks " Usage: " \n ' )
if self . check_multiline :
self . assertIn ( ' \n ' , out , ' expected multi-line output ' )
2019-06-27 07:57:22 +03:00
setattr ( cls , ' test_ %s ' % name , _f )
2019-06-27 07:57:22 +03:00
class PythonScriptHelpTests ( HelpTestSuper ) :
""" Python scripts run with -h or --help should print a help string,
and exit with success .
"""
iterator = python_script_iterator
interpreter = ' python3 '
class ElfHelpTests ( HelpTestSuper ) :
""" ELF binaries run with -h or --help should print a help string,
and exit with success .
"""
iterator = elf_iterator
check_return_code = False
check_merged_out_and_err = True
PerlScriptUsageTests . initialise ( )
2019-03-17 04:47:40 +03:00
PythonScriptUsageTests . initialise ( )
2019-06-27 07:57:22 +03:00
PythonScriptHelpTests . initialise ( )
2019-06-27 07:57:22 +03:00
ElfHelpTests . initialise ( )