mirror of https://github.com/fail2ban/fail2ban
Merge pull request #133 from grooverdan/development-code-coverage
coverage pragma comments, improved documentation for the developerspull/135/merge
commit
b03e046370
|
@ -0,0 +1,4 @@
|
|||
|
||||
[run]
|
||||
branch = True
|
||||
omit = /usr*
|
|
@ -1,3 +1,6 @@
|
|||
*~
|
||||
build
|
||||
dist
|
||||
*.pyc
|
||||
htmlcov
|
||||
.coverage
|
||||
|
|
122
DEVELOP
122
DEVELOP
|
@ -24,14 +24,96 @@ Request feature. You can find more details on the Fail2Ban wiki
|
|||
Testing
|
||||
=======
|
||||
|
||||
Existing tests can be run by executing `fail2ban-testcases`.
|
||||
Existing tests can be run by executing `fail2ban-testcases`. This has options
|
||||
like --log-level that will probably be useful. `fail2ban-testcases --help` for
|
||||
full options.
|
||||
|
||||
Test cases should cover all usual cases, all exception cases and all inside
|
||||
/ outside boundary conditions.
|
||||
|
||||
Test cases should cover all branches. The coverage tool will help identify
|
||||
missing branches. Also see http://nedbatchelder.com/code/coverage/branch.html
|
||||
for more details.
|
||||
|
||||
Install the package python-coverage to visualise your test coverage. Run the
|
||||
following:
|
||||
|
||||
coverage run fail2ban-testcases
|
||||
coverage html
|
||||
|
||||
Then look at htmlcov/index.html and see how much coverage your test cases
|
||||
exert over the codebase. Full coverage is a good thing however it may not be
|
||||
complete. Try to ensure tests cover as many independant paths through the
|
||||
code.
|
||||
|
||||
Manual Execution. To run in a development environment do:
|
||||
|
||||
./fail2ban-client -c config/ -s /tmp/f2b.sock -i start
|
||||
|
||||
some quick commands:
|
||||
|
||||
status
|
||||
add test pyinotify
|
||||
status test
|
||||
set test addaction iptables
|
||||
set test actionban iptables echo <ip> <cidr> >> /tmp/ban
|
||||
set test actionunban iptables echo <ip> <cidr> >> /tmp/unban
|
||||
get test actionban iptables
|
||||
get test actionunban iptables
|
||||
set test banip 192.168.2.2
|
||||
status test
|
||||
|
||||
|
||||
Documentation about creating tests (when tests are required and some guidelines
|
||||
for creating good tests) will be added soon.
|
||||
|
||||
Coding Standards
|
||||
================
|
||||
Coming Soon.
|
||||
================
|
||||
|
||||
Style
|
||||
-----
|
||||
|
||||
Please use tabs for now. Keep to 80 columns, at least for readable text.
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
Add tests. They should test all the code you add in a meaning way.
|
||||
|
||||
Coverage
|
||||
--------
|
||||
|
||||
Test coverage should always increase as you add code.
|
||||
|
||||
You may use "# pragma: no cover" in the code for branches of code that support
|
||||
older versions on python. For all other uses of "pragma: no cover" or
|
||||
"pragma: no branch" document the reason why its not covered. "I haven't written
|
||||
a test case" isn't a sufficient reason.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Ensure this documentation is up to date after changes. Also ensure that the man
|
||||
pages still are accurage. Ensure that there is sufficient documentation for
|
||||
your new features to be used.
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
Remove them and don't add any more.
|
||||
|
||||
Git
|
||||
---
|
||||
|
||||
Use the following tags in your commit messages:
|
||||
|
||||
'ENH:' for enhancements
|
||||
'BF:' for bug fixes
|
||||
'DOC:' for documenation fixes
|
||||
|
||||
Adding Actions
|
||||
--------------
|
||||
|
||||
If you add an action.d/*.conf file also add a example in config/jail.conf
|
||||
with enabled=false and maxretry=5 for ssh.
|
||||
|
||||
|
||||
Design
|
||||
|
@ -127,12 +209,14 @@ FileContainer
|
|||
.__pos
|
||||
Keeps the position pointer
|
||||
|
||||
|
||||
dnsutils.py
|
||||
~~~~~~~~~~~
|
||||
|
||||
DNSUtils
|
||||
|
||||
Utility class for DNS and IP handling
|
||||
|
||||
RF-Note: convert to functions within a separate submodule
|
||||
|
||||
|
||||
filter*.py
|
||||
~~~~~~~~~~
|
||||
|
@ -156,3 +240,27 @@ action.py
|
|||
~~~~~~~~~
|
||||
|
||||
Takes care about executing start/check/ban/unban/stop commands
|
||||
|
||||
|
||||
Releasing
|
||||
=========
|
||||
|
||||
Ensure the version is correct in ./common/version.py
|
||||
|
||||
# update man pages
|
||||
(cd man ; ./generate-man )
|
||||
|
||||
git commit -m 'update man pages for release' man/*
|
||||
|
||||
python setup.py check
|
||||
python setup.py sdist
|
||||
python setup.py bdist_rpm
|
||||
python setup.py upload
|
||||
|
||||
Run the following and update the wiki with output:
|
||||
|
||||
python -c 'import common.protocol; common.protocol.printWiki()'
|
||||
|
||||
email users and development list of release
|
||||
|
||||
TODO notifing distributors etc.
|
||||
|
|
2
MANIFEST
2
MANIFEST
|
@ -3,6 +3,8 @@ ChangeLog
|
|||
TODO
|
||||
THANKS
|
||||
COPYING
|
||||
DEVELOP
|
||||
doc/run-rootless.txt
|
||||
fail2ban-client
|
||||
fail2ban-server
|
||||
fail2ban-testcases
|
||||
|
|
4
README
4
README
|
@ -91,5 +91,5 @@ 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
|
||||
Fail2Ban; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
Suite 330, Boston, MA 02111-1307 USA
|
||||
Fail2Ban; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||
Street, Fifth Floor, Boston, MA 02110, USA
|
||||
|
|
|
@ -418,7 +418,7 @@ class Fail2banClient:
|
|||
class ServerExecutionException(Exception):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == "__main__": # pragma: no cover - can't test main
|
||||
client = Fail2banClient()
|
||||
# Exit with correct return value
|
||||
if client.start(sys.argv):
|
||||
|
|
|
@ -77,10 +77,10 @@ verbosity = {'debug': 3,
|
|||
'fatal': 0,
|
||||
None: 1}[opts.log_level]
|
||||
|
||||
if opts.log_level is not None:
|
||||
if opts.log_level is not None: # pragma: no cover
|
||||
# so we had explicit settings
|
||||
logSys.setLevel(getattr(logging, opts.log_level.upper()))
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
# suppress the logging but it would leave unittests' progress dots
|
||||
# ticking, unless like with '-l fatal' which would be silent
|
||||
# unless error occurs
|
||||
|
@ -89,9 +89,9 @@ else:
|
|||
# Add the default logging handler
|
||||
stdout = logging.StreamHandler(sys.stdout)
|
||||
# Custom log format for the verbose tests runs
|
||||
if verbosity > 1:
|
||||
if verbosity > 1: # pragma: no cover
|
||||
stdout.setFormatter(logging.Formatter(' %(asctime)-15s %(thread)s %(message)s'))
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
# just prefix with the space
|
||||
stdout.setFormatter(logging.Formatter(' %(message)s'))
|
||||
logSys.addHandler(stdout)
|
||||
|
@ -99,7 +99,7 @@ logSys.addHandler(stdout)
|
|||
#
|
||||
# Let know the version
|
||||
#
|
||||
if not opts.log_level or opts.log_level != 'fatal':
|
||||
if not opts.log_level or opts.log_level != 'fatal': # pragma: no cover
|
||||
print "Fail2ban %s test suite. Python %s. Please wait..." \
|
||||
% (version, str(sys.version).replace('\n', ''))
|
||||
|
||||
|
@ -107,9 +107,9 @@ if not opts.log_level or opts.log_level != 'fatal':
|
|||
#
|
||||
# Gather the tests
|
||||
#
|
||||
if not len(regexps):
|
||||
if not len(regexps): # pragma: no cover
|
||||
tests = unittest.TestSuite()
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
import re
|
||||
class FilteredTestSuite(unittest.TestSuite):
|
||||
_regexps = [re.compile(r) for r in regexps]
|
||||
|
@ -159,13 +159,13 @@ filters = [FilterPoll] # always available
|
|||
try:
|
||||
from server.filtergamin import FilterGamin
|
||||
filters.append(FilterGamin)
|
||||
except Exception, e:
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping gamin backend testing. Got exception '%s'" % e
|
||||
|
||||
try:
|
||||
from server.filterpyinotify import FilterPyinotify
|
||||
filters.append(FilterPyinotify)
|
||||
except Exception, e:
|
||||
except Exception, e: # pragma: no cover
|
||||
print "I: Skipping pyinotify backend testing. Got exception '%s'" % e
|
||||
|
||||
for Filter_ in filters:
|
||||
|
@ -189,7 +189,7 @@ try:
|
|||
|
||||
tests_results = testRunner.run(tests)
|
||||
|
||||
finally:
|
||||
finally: # pragma: no cover
|
||||
# Just for the sake of it reset the TZ
|
||||
# yoh: move all this into setup/teardown methods within tests
|
||||
os.environ.pop('TZ')
|
||||
|
@ -197,5 +197,5 @@ finally:
|
|||
os.environ['TZ'] = old_TZ
|
||||
time.tzset()
|
||||
|
||||
if not tests_results.wasSuccessful():
|
||||
if not tests_results.wasSuccessful(): # pragma: no cover
|
||||
sys.exit(1)
|
||||
|
|
|
@ -49,5 +49,5 @@ details.
|
|||
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with Fail2Ban; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
Boston, MA 02111-1307 USA
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110, USA
|
||||
|
|
|
@ -142,7 +142,7 @@ class AsyncServer(asyncore.dispatcher):
|
|||
if sys.version_info >= (2, 6): # if python 2.6 or greater...
|
||||
logSys.debug("Detected Python 2.6 or greater. asyncore.loop() not using poll")
|
||||
asyncore.loop(use_poll = False) # fixes the "Unexpected communication problem" issue on Python 2.6 and 3.0
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
logSys.debug("NOT Python 2.6/3.* - asyncore.loop() using poll")
|
||||
asyncore.loop(use_poll = True)
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ class Filter(JailThread):
|
|||
# file has been modified and looks for failures.
|
||||
# @return True when the thread exits nicely
|
||||
|
||||
def run(self):
|
||||
def run(self): # pragma: no cover
|
||||
raise Exception("run() is abstract")
|
||||
|
||||
##
|
||||
|
@ -226,7 +226,7 @@ class Filter(JailThread):
|
|||
self.failManager.addFailure(FailTicket(ip, unixTime))
|
||||
|
||||
# Perform the banning of the IP now.
|
||||
try:
|
||||
try: # pragma: no branch - exception is the only way out
|
||||
while True:
|
||||
ticket = self.failManager.toBan()
|
||||
self.jail.putFailTicket(ticket)
|
||||
|
@ -373,7 +373,7 @@ class Filter(JailThread):
|
|||
failList.append([ip, date])
|
||||
# We matched a regex, it is enough to stop.
|
||||
break
|
||||
except RegexException, e:
|
||||
except RegexException, e: # pragma: no cover - unsure if reachable
|
||||
logSys.error(e)
|
||||
return failList
|
||||
|
||||
|
@ -507,7 +507,7 @@ class FileFilter(Filter):
|
|||
try:
|
||||
import hashlib
|
||||
md5sum = hashlib.md5
|
||||
except ImportError:
|
||||
except ImportError: # pragma: no cover
|
||||
# hashlib was introduced in Python 2.5. For compatibility with those
|
||||
# elderly Pythons, import from md5
|
||||
import md5
|
||||
|
|
|
@ -99,11 +99,11 @@ def _copy_lines_between_files(fin, fout, n=None, skip=0, mode='a', terminal_line
|
|||
|
||||
Returns open fout
|
||||
"""
|
||||
if sys.version_info[:2] <= (2,4):
|
||||
if sys.version_info[:2] <= (2,4): # pragma: no cover
|
||||
# on old Python st_mtime is int, so we should give at least 1 sec so
|
||||
# polling filter could detect the change
|
||||
time.sleep(1)
|
||||
if isinstance(fin, str):
|
||||
if isinstance(fin, str): # pragma: no branch - only used with str in test cases
|
||||
fin = open(fin, 'r')
|
||||
if isinstance(fout, str):
|
||||
fout = open(fout, mode)
|
||||
|
@ -353,7 +353,7 @@ def get_monitor_failures_testcase(Filter_):
|
|||
_killfile(self.file, self.name)
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self): # pragma: no cover - will only show up if unexpected exception is thrown
|
||||
return "MonitorFailures%s(%s)" \
|
||||
% (Filter_, hasattr(self, 'name') and self.name or 'tempfile')
|
||||
|
||||
|
|
Loading…
Reference in New Issue