2011-09-13 03:09:16 +04:00
# Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2011
#
# Loosely based on bzrlib's test_source.py
#
# 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/>.
#
""" Source level Python tests. """
2018-04-05 03:49:09 +03:00
import io
2011-09-13 03:09:16 +04:00
import errno
import os
import re
import warnings
from samba . tests import (
TestCase ,
)
def get_python_source_files ( ) :
""" Iterate over all Python source files. """
2012-02-09 16:12:06 +04:00
library_dir = os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " samba " ) )
assert os . path . isdir ( library_dir ) , library_dir
2011-09-13 03:09:16 +04:00
for root , dirs , files in os . walk ( library_dir ) :
for f in files :
if f . endswith ( " .py " ) :
yield os . path . abspath ( os . path . join ( root , f ) )
2012-02-09 16:12:06 +04:00
bindir = os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " .. " , " .. " , " bin " ) )
assert os . path . isdir ( bindir ) , bindir
2011-09-13 03:09:16 +04:00
for f in os . listdir ( bindir ) :
p = os . path . abspath ( os . path . join ( bindir , f ) )
if not os . path . islink ( p ) :
continue
target = os . readlink ( p )
if os . path . dirname ( target ) . endswith ( " scripting/bin " ) :
yield p
2012-02-09 16:12:06 +04:00
wafsambadir = os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " , " .. " , " .. " , " buildtools " , " wafsamba " ) )
assert os . path . isdir ( wafsambadir ) , wafsambadir
for root , dirs , files in os . walk ( wafsambadir ) :
for f in files :
if f . endswith ( " .py " ) :
yield os . path . abspath ( os . path . join ( root , f ) )
2011-09-13 03:09:16 +04:00
def get_source_file_contents ( ) :
""" Iterate over the contents of all python files. """
for fname in get_python_source_files ( ) :
try :
2018-04-05 03:49:09 +03:00
f = io . open ( fname , mode = ' r ' , encoding = ' utf-8 ' )
2018-02-14 00:27:52 +03:00
except IOError as e :
2011-09-13 03:09:16 +04:00
if e . errno == errno . ENOENT :
warnings . warn ( " source file %s broken link? " % fname )
continue
else :
raise
try :
text = f . read ( )
finally :
f . close ( )
yield fname , text
class TestSource ( TestCase ) :
def test_copyright ( self ) :
""" Test that all Python files have a valid copyright statement. """
incorrect = [ ]
copyright_re = re . compile ( ' # \\ s*copyright.*(?= \n ) ' , re . I )
for fname , text in get_source_file_contents ( ) :
if fname . endswith ( " ms_schema.py " ) :
# FIXME: Not sure who holds copyright on ms_schema.py
continue
2012-02-09 16:12:06 +04:00
if " wafsamba " in fname :
# FIXME: No copyright headers in wafsamba
continue
2011-09-13 03:09:16 +04:00
match = copyright_re . search ( text )
if not match :
incorrect . append ( ( fname , ' no copyright line found \n ' ) )
if incorrect :
2014-06-02 04:53:01 +04:00
help_text = [
" Some files have missing or incorrect copyright "
" statements. " , " " ]
2011-09-13 03:09:16 +04:00
for fname , comment in incorrect :
help_text . append ( fname )
help_text . append ( ( ' ' * 4 ) + comment )
self . fail ( ' \n ' . join ( help_text ) )
def test_gpl ( self ) :
""" Test that all .py files have a GPL disclaimer. """
incorrect = [ ]
gpl_txt = """
# 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/>.
"""
gpl_re = re . compile ( re . escape ( gpl_txt ) , re . MULTILINE )
for fname , text in get_source_file_contents ( ) :
2012-02-09 16:12:06 +04:00
if " wafsamba " in fname :
# FIXME: License to wafsamba hasn't been clarified yet
continue
2014-12-11 04:33:00 +03:00
if fname . endswith ( " /python/samba/subunit/run.py " ) :
# Imported from subunit/testtools, which are dual
# Apache2/BSD-3.
continue
2011-09-13 03:09:16 +04:00
if not gpl_re . search ( text ) :
incorrect . append ( fname )
if incorrect :
help_text = [ ' Some files have missing or incomplete GPL statement ' ,
gpl_txt ]
for fname in incorrect :
help_text . append ( ( ' ' * 4 ) + fname )
self . fail ( ' \n ' . join ( help_text ) )
def _push_file ( self , dict_ , fname , line_no ) :
if fname not in dict_ :
dict_ [ fname ] = [ line_no ]
else :
dict_ [ fname ] . append ( line_no )
def _format_message ( self , dict_ , message ) :
files = [ " %s : %s " % ( f , ' , ' . join ( [ str ( i + 1 ) for i in lines ] ) )
for f , lines in dict_ . items ( ) ]
files . sort ( )
return message + ' \n \n %s ' % ( ' \n ' . join ( files ) )
def _iter_source_files_lines ( self ) :
for fname , text in get_source_file_contents ( ) :
lines = text . splitlines ( True )
last_line_no = len ( lines ) - 1
for line_no , line in enumerate ( lines ) :
yield fname , line_no , line
def test_no_tabs ( self ) :
""" Check that there are no tabs in Python files. """
tabs = { }
for fname , line_no , line in self . _iter_source_files_lines ( ) :
if ' \t ' in line :
self . _push_file ( tabs , fname , line_no )
if tabs :
self . fail ( self . _format_message ( tabs ,
' Tab characters were found in the following source files. '
' \n They should either be replaced by " \\ t " or by spaces: ' ) )
def test_unix_newlines ( self ) :
""" Check for unix new lines. """
illegal_newlines = { }
for fname , line_no , line in self . _iter_source_files_lines ( ) :
if not line . endswith ( ' \n ' ) or line . endswith ( ' \r \n ' ) :
self . _push_file ( illegal_newlines , fname , line_no )
if illegal_newlines :
self . fail ( self . _format_message ( illegal_newlines ,
' Non-unix newlines were found in the following source files: ' ) )
2011-12-09 00:56:08 +04:00
2012-09-27 20:30:26 +04:00
def test_trailing_whitespace ( self ) :
""" Check that there is not trailing whitespace in Python files. """
trailing_whitespace = { }
for fname , line_no , line in self . _iter_source_files_lines ( ) :
if line . rstrip ( " \n " ) . endswith ( " " ) :
self . _push_file ( trailing_whitespace , fname , line_no )
if trailing_whitespace :
self . fail ( self . _format_message ( trailing_whitespace ,
' Trailing whitespace was found in the following source files. ' ) )
2012-03-15 19:03:36 +04:00
def test_shebang_lines ( self ) :
""" Check that files with shebang lines and only those are executable. """
files_with_shebang = { }
files_without_shebang = { }
for fname , line_no , line in self . _iter_source_files_lines ( ) :
if line_no > = 1 :
continue
2018-02-23 16:09:24 +03:00
executable = ( os . stat ( fname ) . st_mode & 0o111 )
2012-03-15 19:03:36 +04:00
has_shebang = line . startswith ( " #! " )
if has_shebang and not executable :
self . _push_file ( files_with_shebang , fname , line_no )
if not has_shebang and executable :
self . _push_file ( files_without_shebang , fname , line_no )
if files_with_shebang :
self . fail ( self . _format_message ( files_with_shebang ,
2014-09-27 20:03:04 +04:00
' Files with shebang line that are not executable: ' ) )
2012-03-15 19:03:36 +04:00
if files_without_shebang :
self . fail ( self . _format_message ( files_without_shebang ,
2014-09-27 20:03:04 +04:00
' Files without shebang line that are executable: ' ) )