Skip to content

Commit

Permalink
Setup code coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Valsecchi committed Apr 26, 2017
1 parent e660c88 commit b232122
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.venv/
/acceptance_tests/app/c2cwsgiutils
/acceptance_tests/app/c2cwsgiutils_coverage_report.py
/acceptance_tests/app/c2cwsgiutils_run
/acceptance_tests/app/c2cwsgiutils_genversion.py
/acceptance_tests/app/rel_requirements.txt
Expand Down
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 17 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ endif

GIT_TAG := $(shell git describe --tags --first-parent 2>/dev/null || echo "none")
GIT_HASH := $(shell git rev-parse HEAD)
THIS_MAKEFILE_PATH := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR := $(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)

.PHONY: all
all: acceptance
Expand All @@ -27,13 +29,24 @@ all: acceptance
build: build_acceptance build_test_app

.PHONY: acceptance
acceptance: build
mkdir -p reports
acceptance: build_acceptance build_test_app
rm -rf reports/coverage/api reports/acceptance.xml
mkdir -p reports/coverage/api
#run the tests
docker run -e DOCKER_TAG=$(DOCKER_TAG) -v /var/run/docker.sock:/var/run/docker.sock --name c2cwsgiutils_acceptance_$(DOCKER_TAG)_$$PPID $(DOCKER_BASE)_acceptance:$(DOCKER_TAG) py.test -vv --color=yes --junitxml /reports/acceptance.xml $(PYTEST_OPTS) tests; \
status=$$?; \
docker cp c2cwsgiutils_acceptance_$(DOCKER_TAG)_$$PPID:/reports/acceptance.xml reports/acceptance.xml; \
#copy the reports locally \
docker cp c2cwsgiutils_acceptance_$(DOCKER_TAG)_$$PPID:/reports ./; \
status=$$status$$?; \
docker rm c2cwsgiutils_acceptance_$(DOCKER_TAG)_$$PPID; \
status=$$status$$?; \
#generate the HTML report for code coverage \
docker run -v $(THIS_DIR)/reports/coverage/api:/reports/coverage/api:ro --name c2cwsgiutils_acceptance_reports_$(DOCKER_TAG)_$$PPID $(DOCKER_BASE)_test_app:$(DOCKER_TAG) ./c2cwsgiutils_coverage_report.py c2cwsgiutils c2cwsgiutils_app; \
status=$$status$$?; \
#copy the HTML locally \
docker cp c2cwsgiutils_acceptance_reports_$(DOCKER_TAG)_$$PPID:/tmp/coverage/api reports/coverage; \
status=$$status$$?; \
docker rm c2cwsgiutils_acceptance_reports_$(DOCKER_TAG)_$$PPID; \
exit $$status$$?

.PHONY: build_acceptance
Expand All @@ -43,7 +56,7 @@ build_acceptance:

.PHONY: build_test_app
build_test_app:
rsync -a c2cwsgiutils c2cwsgiutils_run c2cwsgiutils_genversion.py rel_requirements.txt setup.cfg acceptance_tests/app/
rsync -a c2cwsgiutils c2cwsgiutils_run c2cwsgiutils_genversion.py c2cwsgiutils_coverage_report.py rel_requirements.txt setup.cfg acceptance_tests/app/
docker build -t $(DOCKER_BASE)_test_app:$(DOCKER_TAG) --build-arg "GIT_TAG=$(GIT_TAG)" --build-arg "GIT_HASH=$(GIT_HASH)" acceptance_tests/app

.venv/timestamp: rel_requirements.txt dev_requirements.txt
Expand Down
1 change: 1 addition & 0 deletions acceptance_tests/tests/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:
SQL_LOG_LEVEL: DEBUG
OTHER_LOG_LEVEL: DEBUG
DEVELOPMENT: 1
COVERAGE: 1
links:
- db
- db_slave
Expand Down
4 changes: 3 additions & 1 deletion acceptance_tests/tests/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from c2cwsgiutils.acceptance.connection import Connection

BASE_URL = 'http://' + utils.DOCKER_GATEWAY + ':8480/api/'
PROJECT_NAME = 'c2cwsgiutils'
LOG = logging.getLogger(__name__)


Expand All @@ -15,7 +16,8 @@ def composition(request):
"""
Fixture that start/stop the Docker composition used for all the tests.
"""
result = Composition(request, 'c2cwsgiutils', '/acceptance_tests/docker-compose.yml')
result = Composition(request, PROJECT_NAME, '/acceptance_tests/docker-compose.yml',
coverage_paths=[PROJECT_NAME + "_app_1:/tmp/coverage"])
utils.wait_url(BASE_URL + 'ping')
return result

Expand Down
2 changes: 2 additions & 0 deletions c2cwsgiutils.iml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/c2cwsgiutils" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/acceptance_tests/app" />
<excludeFolder url="file://$MODULE_DIR$/acceptance_tests/tests" />
<excludeFolder url="file://$MODULE_DIR$/reports" />
</content>
<orderEntry type="inheritedJdk" />
Expand Down
12 changes: 9 additions & 3 deletions c2cwsgiutils/acceptance/composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ def _try(what, fail=True, times=5, delay=10):


class Composition(object):
def __init__(self, request, project_name, composition):
def __init__(self, request, project_name, composition, coverage_paths=None):
self.project_name = project_name
self.composition = composition
self.coverage_paths = coverage_paths
env = Composition._get_env()
if os.environ.get("docker_stop", "1") == "1":
request.addfinalizer(self.stop_all)
if os.environ.get("docker_start", "1") == "1":
_try(lambda:
subprocess.check_call(['docker-compose', '--file', composition,
Expand All @@ -58,12 +57,19 @@ def __init__(self, request, project_name, composition):
'--project-name', project_name, 'logs', '--follow', '--no-color'],
env=env, stderr=subprocess.STDOUT)
request.addfinalizer(log_watcher.kill)
if os.environ.get("docker_stop", "1") == "1":
request.addfinalizer(self.stop_all)

def stop_all(self):
_try(lambda:
subprocess.check_call(['docker-compose', '--file', self.composition,
'--project-name', self.project_name, 'stop'], env=Composition._get_env(),
stderr=subprocess.STDOUT))
if self.coverage_paths:
target_dir = "/reports/"
os.makedirs(target_dir, exist_ok=True)
for path in self.coverage_paths:
subprocess.check_call(['docker', 'cp', path, target_dir], stderr=subprocess.STDOUT)

def stop(self, container):
_try(lambda:
Expand Down
26 changes: 26 additions & 0 deletions c2cwsgiutils/coverage_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import coverage
import logging
import os
import signal

LOG = logging.getLogger(__name__)


def init():
"""
Maybe setup code coverage.
"""
if os.environ.get("COVERAGE", "0") != "1":
return
LOG.warning("Setting up code coverage")
signal.signal(signal.SIGINT, signal_handler)
report_dir = "/tmp/coverage/api"
os.makedirs(report_dir, exist_ok=True)
cov = coverage.Coverage(data_file=os.path.join(report_dir, 'coverage'), data_suffix=True,
auto_data=True, branch=True, source=None)
cov.start()


def signal_handler(signal, frame):
LOG.info("Caught signal, exiting...")
exit(42)
4 changes: 2 additions & 2 deletions c2cwsgiutils/wsgi_app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Module used by c2cwsgiutils_run.sh to provide a WSGI application when starting gunicorn
"""
from c2cwsgiutils import wsgi

from c2cwsgiutils import wsgi, coverage_setup

coverage_setup.init()
application = wsgi.create_application()
18 changes: 18 additions & 0 deletions c2cwsgiutils_coverage_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
import coverage
import shutil
import sys


def main():
sources = sys.argv[1:]
report_dir = '/reports/coverage/api'
dest_dir = '/tmp/coverage/api'
shutil.rmtree(dest_dir, ignore_errors=True)
shutil.copytree(report_dir, dest_dir)
cov = coverage.Coverage(data_file='coverage', data_suffix=True, source=sources, branch=True)
cov.combine([dest_dir], strict=True)
cov.html_report(directory=dest_dir)
cov.report()

main()
28 changes: 22 additions & 6 deletions c2cwsgiutils_run
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,33 @@ then
GUNICORN_PARAMS="-b :80 --worker-class gthread --threads 10 --workers 5"
fi

trap ctrl_c TERM INT
function ctrl_c() {
echo "Caught a signal"
kill -SIGINT $PID
_term() {
echo "Caught SIGTERM signal!"
kill -TERM "$child" 2>/dev/null
wait "$child"
exit 1
}

_int() {
echo "Caught SIGINT signal!"
kill -INT "$child" 2>/dev/null
wait "$child"
exit 1
}

trap _term SIGTERM
trap _int SIGINT

while true
do
gunicorn $GUNICORN_PARAMS c2cwsgiutils.wsgi_app:application &
PID=$!
wait $PID
child=$!
wait "$child"
exit_status=$?
if [ $exit_status -eq 0 ]
then
exit 0
fi
echo "gunicorn exited with an error ($exit_status), restarting in 1s"
sleep 1
done
1 change: 1 addition & 0 deletions rel_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ requests==2.13.0
SQLAlchemy==1.1.6
zope.interface==4.3.3
zope.sqlalchemy==0.7.7
coverage==4.3.4
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import setup, find_packages


VERSION = '0.9.2'
VERSION = '0.10.0'
HERE = os.path.abspath(os.path.dirname(__file__))
INSTALL_REQUIRES = open(os.path.join(HERE, 'rel_requirements.txt')).read().splitlines()

Expand Down Expand Up @@ -36,6 +36,7 @@
},
scripts=[
'c2cwsgiutils_run',
'c2cwsgiutils_genversion.py'
'c2cwsgiutils_genversion.py',
'c2cwsgiutils_coverage_report.py'
]
)

0 comments on commit b232122

Please sign in to comment.