Commit 434c0f15 authored by Phyks (Lucas Verney)'s avatar Phyks (Lucas Verney) Committed by Florent Fourcot

Gitlab-CI continuous integration

This commit adds the necessary files to run the CI using Gitlab-CI. For
now, it checks that Weboob builds, then runs the linting script
(checking that every module as an icon and some tests + PyFlakes) and
the unittests. Most of modules unittests cannot run because there is no
backend configured.

Some changes were needed in the pre-existing scripts:
* Edit `weboob_lint` to exit with non-zero code if it finds modules
without icons or tests, so that the build could fail in such a case.
* Edit `run_tests.sh` to set correct exit code on failure and rework
generation of XUNIT output. Also added some doc about useful environment
variables. Added a way to generate an xunit output file when running modules
unittests, passing a `XUNIT_OUT` env variable to `run_tests.sh` script.
* Modification of `setup.cfg` and `run_tests` scripts to handle code
coverage generation. The matching regex in Gitlab for the total code
coverage is `TOTAL: (\d+\%\s*)$)`.

I also added a script to generate a JSON module status matrix from
modules unittests, ready to be sent to a
[Weboob-CI](https://github.com/Phyks/weboob-ci) instance.

NOTE: Required Python modules are taken from the `setup.py` script.
`.ci/requirements.txt` contains the requirements to run the unittests
and the CI, whereas `.ci/requirements_modules.txt` contains the specific
Python modules required at runtime by Weboob modules. The latter could
eventually be replaced by a proper call to `debpydep` script.
parent 14464962
coverage==4.2
flake8==3.2.1
mock==2.0.0
nose==1.3.7
pyflakes==1.3.0
BeautifulSoup
html2text
simplejson
......@@ -12,3 +12,4 @@ modules/modules.list
/localconfig
*.idea/
*.DS_Store
*.coverage*
image: "python:2.7"
before_script:
- "pip install -r .ci/requirements.txt"
- "REQUIREMENTS=$(mktemp) && python setup.py requirements > ${REQUIREMENTS} && pip install -r ${REQUIREMENTS} && rm ${REQUIREMENTS}"
- "pip install -r .ci/requirements_modules.txt"
build:
stage: "build"
script:
- "./tools/local_install.sh ~/bin"
lint:
stage: "test"
script:
- "./tools/pyflakes.sh"
- "./tools/weboob_lint.sh"
unittests:
stage: "test"
script:
- "NOSE_PROCESSES=4 ./tools/run_tests.sh"
doc:
stage: "deploy"
script:
- "cd ./docs && make html"
......@@ -2,6 +2,7 @@
verbosity = 2
detailed-errors = 1
with-doctest = 1
with-coverage = 1
where = weboob
tests = weboob.tools.capabilities.bank.transactions,
weboob.tools.capabilities.paste,
......@@ -18,9 +19,12 @@ tests = weboob.tools.capabilities.bank.transactions,
weboob.browser.tests.url
[isort]
known_first_party=weboob
line_length=120
known_first_party = weboob
line_length = 120
[flake8]
max-line-length = 120
exclude = dist,*.egg-info,build,.git,__pycache__
[easy_install]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Script to format XUNIT output from unittests as a JSON string ready to be sent
to a [Weboob-CI](https://github.com/Phyks/weboob-ci) instance.
* `XUNIT` is the XUNIT file to handle.
* `ORIGIN` is an origin string as described in the Weboob-CI documentation
(basically just a string to identify the source of the unittests results).
"""
from __future__ import print_function
import collections
import json
import sys
import xunitparser
def main(xunit, origin):
with open(xunit, "r") as fh:
ts, tr = xunitparser.parse(fh)
# Get test results for each module
modules = {}
other_testcases = []
for tc in ts:
if not tc.classname.startswith("modules."):
other_testcases.append(repr(tc))
continue
module = tc.classname.split(".")[1]
# In the following, we consider
# good > skipped > bad
# and only make update of a module status according to this order
if tc.good:
if tc.skipped:
# Set to skipped only if previous test was good
if module not in modules or modules[module] == "good":
modules[module] = "skipped"
else:
# Set to good only if no previous result
if module not in modules:
modules[module] = "good"
else:
# Always set to bad on failed test
modules[module] = "bad"
# Agregate results by test result rather than module
results = collections.defaultdict(list)
for module in modules:
results[modules[module]].append(module)
return {
"origin": origin,
"modules": results,
"others": other_testcases
}
if __name__ == "__main__":
if len(sys.argv) < 3:
sys.exit("Usage: %s XUNIT_FILE ORIGIN" % (sys.argv[0]))
print(
json.dumps(
main(sys.argv[1], sys.argv[2]),
sort_keys=True, indent=4, separators=(',', ': ')
)
)
#!/bin/sh
#!/bin/bash
# Mai available environment variables
# * RSYNC_TARGET: target on which to rsync the xunit output.
# * XUNIT_OUT: file in which xunit output should be saved.
# * WEBOOB_BACKENDS: path to the Weboob backends file to use.
# stop on failure
set -e
......@@ -30,6 +35,10 @@ else
RSYNC_TARGET=""
fi
if [ ! -n "${XUNIT_OUT}" ]; then
XUNIT_OUT=""
fi
# find executables
if [ -z "${PYTHON}" ]; then
which python >/dev/null 2>&1 && PYTHON=$(which python)
......@@ -56,11 +65,18 @@ fi
# do not allow undefined variables anymore
set -u
WEBOOB_TMPDIR=$(mktemp -d "${TMPDIR}/weboob_test.XXXXX")
cp "${WEBOOB_BACKENDS}" "${WEBOOB_TMPDIR}/backends"
if [ -f "${WEBOOB_BACKENDS}" ]; then
cp "${WEBOOB_BACKENDS}" "${WEBOOB_TMPDIR}/backends"
else
touch "${WEBOOB_TMPDIR}/backends"
chmod go-r "${WEBOOB_TMPDIR}/backends"
fi
# xunit nose setup
if [ -n "${RSYNC_TARGET}" ]; then
XUNIT_ARGS="--with-xunit --xunit-file=${WEBOOB_TMPDIR}/xunit.xml"
elif [ -n "${XUNIT_OUT}" ]; then
XUNIT_ARGS="--with-xunit --xunit-file=${XUNIT_OUT}"
else
XUNIT_ARGS=""
fi
......@@ -77,17 +93,40 @@ ${PYTHON} "${WEBOOB_DIR}/scripts/weboob-config" update
# allow failing commands past this point
set +e
set -o pipefail
if [ -n "${BACKEND}" ]; then
${PYTHON} ${NOSE} -c /dev/null -sv "${WEBOOB_MODULES}/${BACKEND}/test.py" ${XUNIT_ARGS}
STATUS=$?
STATUS_CORE=0
else
echo "=== Weboob ==="
${PYTHON} ${NOSE} -c ${WEBOOB_DIR}/setup.cfg -sv
CORE_TESTS=$(mktemp)
${PYTHON} ${NOSE} --cover-package weboob -c ${WEBOOB_DIR}/setup.cfg -sv 2>&1 | tee "${CORE_TESTS}"
STATUS_CORE=$?
echo "=== Modules ==="
find "${WEBOOB_MODULES}" -name "test.py" | sort | xargs ${PYTHON} ${NOSE} -c /dev/null -sv ${XUNIT_ARGS}
MODULES_TESTS=$(mktemp)
MODULES_TO_TEST=$(find "${WEBOOB_MODULES}" -name "test.py" | sort | xargs echo)
${PYTHON} ${NOSE} --with-coverage --cover-package modules -c /dev/null -sv ${XUNIT_ARGS} ${MODULES_TO_TEST} 2>&1 | tee ${MODULES_TESTS}
STATUS=$?
# Compute total coverage
echo "=== Total coverage ==="
CORE_STMTS=$(grep "TOTAL" ${CORE_TESTS} | awk '{ print $2; }')
CORE_MISS=$(grep "TOTAL" ${CORE_TESTS} | awk '{ print $3; }')
CORE_COVERAGE=$(grep "TOTAL" ${CORE_TESTS} | awk '{ print $4; }')
MODULES_STMTS=$(grep "TOTAL" ${MODULES_TESTS} | awk '{ print $2; }')
MODULES_MISS=$(grep "TOTAL" ${MODULES_TESTS} | awk '{ print $3; }')
MODULES_COVERAGE=$(grep "TOTAL" ${MODULES_TESTS} | awk '{ print $4; }')
echo "CORE COVERAGE: ${CORE_COVERAGE}"
echo "MODULES COVERAGE: ${MODULES_COVERAGE}"
TOTAL_STMTS=$((${CORE_STMTS} + ${MODULES_STMTS}))
TOTAL_MISS=$((${CORE_MISS} + ${MODULES_MISS}))
TOTAL_COVERAGE=$((100 * (${TOTAL_STMTS} - ${TOTAL_MISS}) / ${TOTAL_STMTS}))
echo "TOTAL: ${TOTAL_COVERAGE}%"
# removal of temp files
rm ${CORE_TESTS}
rm ${MODULES_TESTS}
fi
# xunit transfer
......
......@@ -6,6 +6,7 @@ from __future__ import print_function
from weboob.core import Weboob
import os
import sys
weboob = Weboob()
weboob.modules_loader.load_all()
......@@ -26,3 +27,6 @@ if backends_without_tests:
print('Modules without tests: %s' % backends_without_tests)
if backends_without_icons:
print('Modules without icons: %s' % backends_without_icons)
if backends_without_tests or backends_without_icons:
sys.exit(1)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment