Add test coverage measurement and reporting

- Implement coverage functionality as a setuptools Command subclass
  (in plinth/tests/coverage/test_coverage.py)
- Register the new 'test_coverage' command in setup.py
- Modify the INSTALL file to specify python3-coverage as a new dependency
- Modify the HACKING file to document the new 'test_coverage' command
- Have git (via .gitignore) ignore the '.coverage' output data file and
  the contents of the 'plinth/tests/coverage/report' directory
This commit is contained in:
Bob Girard 2014-11-18 12:06:58 -07:00 committed by Sunil Mohan Adapa
parent 95bf621c08
commit a502da63d5
5 changed files with 114 additions and 6 deletions

2
.gitignore vendored
View File

@ -28,3 +28,5 @@ build/
.emacs.desktop*
*.egg-info/
dist/
.coverage
plinth/tests/coverage/report/

24
HACKING
View File

@ -8,7 +8,7 @@
$ sudo python3 setup.py develop
This will install the python package in a special development mode. Run it
normally. Any updates to the code (and core pakcage data files) do not
normally. Any updates to the code (and core package data files) do not
require re-installation after every modification.
CherryPy web server also monitors changes to the source files and reloads
@ -28,7 +28,7 @@
*Note:* This mode is supported only in a limited manner. The following are
the unknown issues with it:
1. Help pages are also not built. Run 'make -C doc' manaully.
1. Help pages are also not built. Run 'make -C doc' manually.
2. Actions do not work when running as normal user without 'sudo' prefix.
You need to add 'actions' directory to be allowed for 'sudo' commands.
@ -40,6 +40,24 @@
$ python3 setup.py test
## Running the Test Coverage Analysis
1. Run the coverage tool:
$ python3 setup.py test_coverage
Invoking this command generates a binary-format '.coverage' data file in
the top-level project directory which is recreated with each run, and
writes a set of HTML and other supporting files which comprise the
browsable coverage report to the 'plinth/tests/coverage/report' directory.
Index.html presents the coverage summary, broken down by module. Data
columns can be sorted by clicking on the column header or by using mnemonic
hot-keys specified in the keyboard widget in the upper-right corner of the
page. Clicking on the name of a particular source file opens a page that
displays the contents of that file, with color-coding in the left margin to
indicate which statements or branches were executed via the tests (green)
and which statements or branches were not executed (red).
## Testing Inside a Virtual Machine
1. Checkout source on the host.
@ -96,6 +114,8 @@ infrastructure in place for it from the start. Use it like this:
* *CherryPy3* - WSGI web server since Django does not have proper web server
* *Coverage* - Test coverage measurement and reporting tool
* *Django* - Web application framework for Plinth
* *JQuery* - Javascript framework used for convenience

View File

@ -6,7 +6,8 @@
$ sudo apt-get install libjs-jquery libjs-modernizr \
libjs-bootstrap make pandoc python3 python3-cherrypy3 \
python3-django python3-bootstrapform python3-setuptools
python3-coverage python3-django python3-bootstrapform \
python3-setuptools
2. Install Plinth:

View File

@ -0,0 +1,80 @@
#!/usr/bin/python3
# -*- mode: python; mode: auto-fill; fill-column: 80 -*-
"""Support for integration of code test coverage analysis with setuptools.
Derived from 'Adding Test Code Coverage Analysis to a Python Projects Setup Command'
<http://jeetworks.org/adding-test-code-coverage-analysis-to-a-python-projects-setup-command/>
"""
import coverage
import glob
import setuptools
import shutil
import time
import unittest
from plinth import tests
# project directories with testable source code
SOURCE_DIRS = ['plinth'] + glob.glob('plinth/modules/*')
# files to exclude from coverage analysis and reporting
FILES_TO_OMIT = ['plinth/tests/*.py']
# location of coverage HTML report files
COVERAGE_REPORT_DIR = 'plinth/tests/coverage/report'
class TestCoverageCommand(setuptools.Command):
"""
Subclass of setuptools Command to perform code test coverage analysis.
"""
description = "Run test coverage analysis"
user_options = [
('test-module=', 't', "Explicitly specify a single module to test")
]
def initialize_options(self):
"""
Initialize options to default values.
"""
self.test_module = None
def finalize_options(self):
pass
def run(self):
"""
Main command implementation.
"""
# erase any existing HTML report files
try:
shutil.rmtree(COVERAGE_REPORT_DIR, True)
except:
pass
# initialize a test suite for one or all modules
if self.test_module is None:
test_suite = tests.TEST_SUITE
else:
test = unittest.defaultTestLoader.loadTestsFromNames([self.test_module])
test_suite = unittest.TestSuite(test)
# run the coverage analysis
runner = unittest.TextTestRunner()
cov = coverage.coverage(auto_data=True, branch=True, source=SOURCE_DIRS,
omit=FILES_TO_OMIT)
cov.erase() # erase existing coverage data file
cov.start()
runner.run(test_suite)
cov.stop()
# generate an HTML report
html_report_title = ("FreedomBox:Plinth -- Test Coverage as of " +
time.strftime("%x %X %Z"))
cov.html_report(directory=COVERAGE_REPORT_DIR, omit=FILES_TO_OMIT,
title=html_report_title)

View File

@ -31,6 +31,7 @@ import shutil
import subprocess
from plinth import __version__
from plinth.tests.coverage import test_coverage
DIRECTORIES_TO_CREATE = [
@ -113,7 +114,8 @@ setuptools.setup(
install_requires=[
'cherrypy >= 3.0',
'django >= 1.7.0',
'django-bootstrap-form'
'django-bootstrap-form',
'coverage >= 3.7'
],
include_package_data=True,
package_data={'plinth': ['templates/*',
@ -134,6 +136,9 @@ setuptools.setup(
('/etc/plinth/modules-enabled',
glob.glob(os.path.join('data/etc/plinth/modules-enabled',
'*')))],
cmdclass={'clean': CustomClean,
'install_data': CustomInstallData},
cmdclass={
'clean': CustomClean,
'install_data': CustomInstallData,
'test_coverage': test_coverage.TestCoverageCommand
},
)