Skip to content

Commit 2094ff8

Browse files
committedNov 13, 2018
Added QGIS testing environment to dockers
Also: - renamed .docker to docker (now it's public and official) - added dependencies for python CI testing
1 parent 7cce9b3 commit 2094ff8

File tree

10 files changed

+371
-3
lines changed

10 files changed

+371
-3
lines changed
 
File renamed without changes.

‎.docker/qgis.dockerfile renamed to ‎docker/qgis.dockerfile

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ ARG CACHE_DIR
88
FROM qgis/qgis3-build-deps:${DOCKER_TAG}
99
MAINTAINER Denis Rouzaud <denis@opengis.ch>
1010

11+
LABEL Description="Docker container with QGIS for CI testing" Vendor="QGIS.org" Version="1.1"
12+
13+
1114
ENV CC=/usr/lib/ccache/clang
1215
ENV CXX=/usr/lib/ccache/clang++
1316
ENV QT_SELECT=5
@@ -25,7 +28,7 @@ RUN cmake \
2528
-GNinja \
2629
-DCMAKE_BUILD_TYPE=Release \
2730
-DCMAKE_INSTALL_PREFIX=/usr \
28-
-DWITH_DESKTOP=OFF \
31+
-DWITH_DESKTOP=ON \
2932
-DWITH_SERVER=ON \
3033
-DWITH_3D=ON \
3134
-DWITH_BINDINGS=ON \
@@ -42,4 +45,28 @@ RUN cmake \
4245
&& ninja install \
4346
&& rm -rf /usr/src/QGIS
4447

48+
################################################################################
49+
# Python testing environment setup
50+
51+
# Add QGIS test runner
52+
COPY docker/qgis_resources/test_runner/qgis_* /usr/bin/
53+
54+
# Make all scripts executable
55+
RUN chmod +x /usr/bin/qgis_*
56+
57+
# Add supervisor service configuration script
58+
COPY docker/qgis_resources/supervisor/supervisord.conf /etc/supervisor/
59+
COPY docker/qgis_resources/supervisor/supervisor.xvfb.conf /etc/supervisor/supervisor.d/
60+
61+
# Python paths are for
62+
# - kartoza images (compiled)
63+
# - deb installed
64+
# - built from git
65+
# needed to find PyQt wrapper provided by QGIS
66+
ENV PYTHONPATH=/usr/share/qgis/python/:/usr/lib/python3/dist-packages/qgis:/usr/share/qgis/python/qgis
67+
68+
4569
WORKDIR /
70+
71+
# Run supervisor
72+
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

‎.docker/qgis3-build-deps.dockerfile renamed to ‎docker/qgis3-build-deps.dockerfile

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM ubuntu:18.04
22
MAINTAINER Denis Rouzaud <denis@opengis.ch>
33

4-
LABEL Description="Docker container with QGIS dependencies" Vendor="QGIS.org" Version="1.0"
4+
LABEL Description="Docker container with QGIS dependencies" Vendor="QGIS.org" Version="1.1"
55

66
# && echo "deb http://ppa.launchpad.net/ubuntugis/ubuntugis-unstable/ubuntu xenial main" >> /etc/apt/sources.list \
77
# && echo "deb-src http://ppa.launchpad.net/ubuntugis/ubuntugis-unstable/ubuntu xenial main" >> /etc/apt/sources.list \
@@ -11,7 +11,8 @@ LABEL Description="Docker container with QGIS dependencies" Vendor="QGIS.org" Ve
1111
RUN apt-get update \
1212
&& apt-get install -y software-properties-common \
1313
&& apt-get update \
14-
&& apt-get install -y \
14+
&& DEBIAN_FRONTEND=noninteractive \
15+
apt-get install -y \
1516
apt-transport-https \
1617
bison \
1718
ca-certificates \
@@ -77,6 +78,7 @@ RUN apt-get update \
7778
python3-pyqt5.qsci \
7879
python3-pyqt5.qtsql \
7980
python3-pyqt5.qtsvg \
81+
python3-pyqt5.qtwebkit \
8082
python3-sip \
8183
python3-sip-dev \
8284
python3-termcolor \
@@ -103,6 +105,11 @@ RUN apt-get update \
103105
xfonts-base \
104106
xfonts-scalable \
105107
xvfb \
108+
opencl-headers \
109+
ocl-icd-libopencl1 \
110+
ocl-icd-opencl-dev \
111+
supervisor \
112+
expect \
106113
&& pip3 install \
107114
psycopg2 \
108115
numpy \
@@ -114,6 +121,12 @@ RUN apt-get update \
114121
owslib \
115122
oauthlib \
116123
pyopenssl \
124+
pep8 \
125+
pexpect \
126+
capturer \
127+
sphinx \
128+
requests \
129+
six \
117130
&& apt-get clean
118131

119132

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pep8
2+
pexpect
3+
capturer
4+
sphinx
5+
requests
6+
future
7+
six
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
; Supervisor config file for Xvfb
2+
3+
[program:Xvfb]
4+
command=/usr/bin/Xvfb :99 -screen 0 1024x768x24 -ac +extension GLX +render -noreset -nolisten tcp
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
; Supervisor config file.
2+
3+
[supervisord]
4+
nodaemon=true
5+
logfile=/var/log/supervisor/supervisord.log
6+
logfile_maxbytes=50MB
7+
logfile_backups=10
8+
loglevel=info
9+
pidfile=/var/run/supervisord.pid
10+
childlogdir=/var/log
11+
12+
[include]
13+
files = /etc/supervisor/supervisor.d/*.conf
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/bin/bash
2+
# Setup QGIS for the automated tests
3+
# This is normally called from Travis or rundockertests.sh
4+
# before running the tests for a particular plugin
5+
#
6+
# Note: on QGIS3 assumes the default profile for root user
7+
#
8+
# - create the folders
9+
# - install startup.py monkey patches
10+
# - disable tips
11+
# - enable the plugin
12+
13+
PLUGIN_NAME=$1
14+
CONF_FOLDER="/root/.config/QGIS"
15+
CONF_FILE="${CONF_FOLDER}/QGIS2.conf"
16+
CONF_MASTER_FOLDER="/root/.local/share/QGIS/QGIS3/profiles/default/QGIS/"
17+
CONF_MASTER_FILE="${CONF_MASTER_FOLDER}/QGIS3.ini"
18+
QGIS_FOLDER="/root/.qgis2"
19+
20+
QGIS_MASTER_FOLDER="/root/.local/share/QGIS/QGIS3/profiles/default"
21+
PLUGIN_FOLDER="${QGIS_FOLDER}/python/plugins"
22+
PLUGIN_MASTER_FOLDER="${QGIS_MASTER_FOLDER}/python/plugins"
23+
24+
STARTUP_FOLDER="${QGIS_FOLDER}/python"
25+
STARTUP_MASTER_FOLDER="/root/.local/share/QGIS/QGIS3/"
26+
27+
# Creates the config file
28+
mkdir -p $CONF_FOLDER
29+
if [ -e "$CONF_FILE" ]; then
30+
rm -f $CONF_FILE
31+
fi
32+
touch $CONF_FILE
33+
34+
35+
mkdir -p $CONF_MASTER_FOLDER
36+
if [ -e "$CONF_MASTER_FILE" ]; then
37+
rm -f $CONF_MASTER_FILE
38+
fi
39+
touch $CONF_MASTER_FILE
40+
41+
# Creates plugin folder
42+
mkdir -p $PLUGIN_FOLDER
43+
mkdir -p $PLUGIN_MASTER_FOLDER
44+
mkdir -p $STARTUP_MASTER_FOLDER
45+
46+
# Install the monkey patches to prevent modal stacktrace on python errors
47+
cp /usr/bin/qgis_startup.py ${STARTUP_FOLDER}/startup.py
48+
cp /usr/bin/qgis_startup.py ${STARTUP_MASTER_FOLDER}/startup.py
49+
50+
# Disable tips
51+
printf "[Qgis]\n" >> $CONF_FILE
52+
# !!!! Note that on master it is lowercase !!!!
53+
printf "[qgis]\n" >> $CONF_MASTER_FILE
54+
SHOW_TIPS=`qgis --help 2>&1 | head -2 | grep 'QGIS - ' | perl -npe 'chomp; s/QGIS - (\d+)\.(\d+).*/showTips\1\2=false/'`
55+
printf "$SHOW_TIPS\n\n" >> $CONF_FILE
56+
printf "$SHOW_TIPS\n\n" >> $CONF_MASTER_FILE
57+
58+
if [ -n "$PLUGIN_NAME" ]; then
59+
# Enable plugin
60+
printf '[PythonPlugins]\n' >> $CONF_FILE
61+
printf "${PLUGIN_NAME}=true\n\n" >> $CONF_FILE
62+
63+
printf '[PythonPlugins]\n' >> $CONF_MASTER_FILE
64+
printf "${PLUGIN_NAME}=true\n\n" >> $CONF_MASTER_FILE
65+
fi
66+
67+
# Disable firstRunVersionFlag for master
68+
printf "\n[migration]\n" >> $CONF_MASTER_FILE
69+
printf "fileVersion=2\n" >> $CONF_MASTER_FILE
70+
printf "firstRunVersionFlag=29900\n" >> $CONF_MASTER_FILE
71+
printf "settings=true\n\n" >> $CONF_MASTER_FILE
72+
73+
74+
# Install the plugin
75+
if [ ! -L "${PLUGIN_FOLDER}/${PLUGIN_NAME}" ]; then
76+
ln -s /tests_directory/${PLUGIN_NAME} ${PLUGIN_FOLDER}
77+
echo "Plugin folder linked in ${PLUGIN_FOLDER}/${PLUGIN_NAME}"
78+
fi
79+
if [ ! -d "${PLUGIN_MASTER_FOLDER}/${PLUGIN_NAME}" ]; then
80+
ln -s /tests_directory/${PLUGIN_NAME} ${PLUGIN_MASTER_FOLDER}
81+
echo "Plugin master folder linked in ${PLUGIN_MASTER_FOLDER}/${PLUGIN_NAME}"
82+
fi
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
Disable QGIS modal error dialog.
3+
4+
This script is meant to be run automatically when QGIS starts.
5+
Is should be renamed to `startup.py` and placed into ~/.qgis2/python/startup.py
6+
or ~/.qgis-dev/python/startup.py or ~/.qgis3/python/startup.py
7+
8+
"""
9+
from qgis import utils
10+
import traceback
11+
12+
13+
def _showException(type, value, tb, msg, messagebar=False):
14+
print(msg)
15+
logmessage = ''
16+
for s in traceback.format_exception(type, value, tb):
17+
logmessage += s.decode('utf-8', 'replace') if hasattr(s, 'decode') else s
18+
print(logmessage)
19+
20+
21+
def _open_stack_dialog(type, value, tb, msg, pop_error=True):
22+
print(msg)
23+
24+
25+
utils.showException = _showException
26+
utils.open_stack_dialog = _open_stack_dialog
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
***************************************************************************
6+
Launches a unit test inside QGIS and exit the application.
7+
8+
Arguments:
9+
10+
accepts a single argument with the package name in python dotted notation,
11+
the program tries first to load the module and launch the `run_all`
12+
function of the module, if that fails it considers the last part of
13+
the dotted path to be the function name and the previous part to be the
14+
module.
15+
16+
Extra options for QGIS command line can be passed in the env var
17+
QGIS_EXTRA_OPTIONS
18+
19+
Example run:
20+
21+
# Will load geoserverexplorer.test.catalogtests and run `run_all`
22+
QGIS_EXTRA_OPTIONS='--optionspath .' \
23+
GSHOSTNAME=localhost \
24+
python qgis_testrunner.py geoserverexplorer.test.catalogtests
25+
26+
27+
GSHOSTNAME=localhost \
28+
python qgis_testrunner.py geoserverexplorer.test.catalogtests.run_my
29+
30+
31+
---------------------
32+
Date : May 2016
33+
Copyright : (C) 2016 by Alessandro Pasotti
34+
Email : apasotti at boundlessgeo dot com
35+
***************************************************************************
36+
* *
37+
* This program is free software; you can redistribute it and/or modify *
38+
* it under the terms of the GNU General Public License as published by *
39+
* the Free Software Foundation; either version 2 of the License, or *
40+
* (at your option) any later version. *
41+
* *
42+
***************************************************************************
43+
"""
44+
45+
__author__ = 'Alessandro Pasotti'
46+
__date__ = 'May 2016'
47+
48+
import os
49+
import re
50+
import sys
51+
import traceback
52+
import signal
53+
import importlib
54+
from pexpect import run
55+
from pipes import quote
56+
57+
from qgis.utils import iface
58+
59+
60+
def eprint(text):
61+
sys.__stderr__.write(text + "\n")
62+
63+
64+
def __get_test_function(test_module_name):
65+
"""
66+
Load the test module and return the test function
67+
"""
68+
print("QGIS Test Runner - Trying to import %s" % test_module_name)
69+
try:
70+
test_module = importlib.import_module(test_module_name)
71+
function_name = 'run_all'
72+
except ImportError as e:
73+
traceback.print_exc(file=sys.stdout)
74+
# Strip latest name
75+
pos = test_module_name.rfind('.')
76+
if pos <= 0:
77+
raise e
78+
test_module_name, function_name = test_module_name[:pos], test_module_name[pos + 1:]
79+
print("QGIS Test Runner - Trying to import %s" % test_module_name)
80+
sys.stdout.flush()
81+
try:
82+
test_module = importlib.import_module(test_module_name)
83+
except ImportError as e:
84+
traceback.print_exc(file=sys.stdout)
85+
raise e
86+
return getattr(test_module, function_name, None)
87+
88+
89+
if iface is None:
90+
"""
91+
Launch QGIS and passes itself as an init script
92+
"""
93+
sys.path.append(os.getcwd())
94+
test_module_name = sys.argv[-1]
95+
if __get_test_function(test_module_name) is None:
96+
print("QGIS Test Runner - [ERROR] cannot load test function from %s" % test_module_name)
97+
sys.exit(1)
98+
try:
99+
me = __file__
100+
except NameError:
101+
me = sys.argv[0]
102+
os.environ['QGIS_DEBUG'] = '1'
103+
args = [
104+
'qgis',
105+
os.environ.get('QGIS_EXTRA_OPTIONS', ''),
106+
'--nologo',
107+
'--noversioncheck',
108+
'--code',
109+
me,
110+
test_module_name, # Must be the last one!
111+
]
112+
command_line = ' '.join(args)
113+
print("QGIS Test Runner - launching QGIS as %s ..." % command_line)
114+
out, returncode = run("sh -c " + quote(command_line), withexitstatus=1)
115+
assert returncode is not None
116+
print("QGIS Test Runner - QGIS exited.")
117+
ok = out.find('(failures=') < 0 and \
118+
len(re.findall(r'Ran \d+ tests in\s',
119+
out, re.MULTILINE)) > 0
120+
print('=' * 60)
121+
if not ok:
122+
print(out)
123+
else:
124+
eprint(out)
125+
if len(out) == 0:
126+
print("QGIS Test Runner - [WARNING] subprocess returned no output")
127+
print('=' * 60)
128+
129+
print("QGIS Test Runner - %s bytes returned and finished with exit code: %s" % (len(out), 0 if ok else 1))
130+
sys.exit(0 if ok else 1)
131+
132+
else: # We are inside QGIS!
133+
# Start as soon as the initializationCompleted signal is fired
134+
from qgis.core import QgsApplication, QgsProjectBadLayerHandler, QgsProject
135+
from PyQt.QtCore import QDir
136+
from qgis.utils import iface
137+
138+
class QgsProjectBadLayerDefaultHandler(QgsProjectBadLayerHandler):
139+
def handleBadLayers(self, layers, dom):
140+
pass
141+
142+
# Monkey patch QGIS Python console
143+
from console.console_output import writeOut
144+
145+
def _write(self, m):
146+
sys.__stdout__.write(m)
147+
writeOut.write = _write
148+
149+
# Add current working dir to the python path
150+
sys.path.append(QDir.current().path())
151+
152+
def __run_test():
153+
"""
154+
Run the test specified as last argument in the command line.
155+
"""
156+
# Disable modal handler for bad layers
157+
QgsProject.instance().setBadLayerHandler(QgsProjectBadLayerDefaultHandler())
158+
eprint("QGIS Test Runner Inside - starting the tests ...")
159+
try:
160+
test_module_name = QgsApplication.instance().arguments()[-1]
161+
function_name = __get_test_function(test_module_name)
162+
eprint("QGIS Test Runner Inside - executing function %s" % function_name)
163+
function_name()
164+
except Exception as e:
165+
eprint("QGIS Test Runner Inside - [FAILED] Exception: %s" % e)
166+
# Print tb
167+
traceback.print_exc(file=sys.stdout)
168+
app = QgsApplication.instance()
169+
os.kill(app.applicationPid(), signal.SIGTERM)
170+
iface.initializationCompleted.connect(__run_test)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
# Run a test inside QGIS
3+
### Turn on debug mode ###
4+
#set -x
5+
6+
TEST_NAME=$1
7+
8+
cd /tests_directory
9+
echo "Running test $1 ..."
10+
OUTPUT=$(QGIS_TEST_MODULE=${TEST_NAME} unbuffer qgis --version-migration --nologo --code /usr/bin/qgis_testrunner.py $TEST_NAME 2>/dev/null | tee /dev/tty)
11+
EXIT_CODE="$?"
12+
if [ -z "$OUTPUT" ]; then
13+
echo "ERROR: no output from the test runner! (exit code: ${EXIT_CODE})"
14+
exit 1
15+
fi
16+
echo $OUTPUT | grep -q FAILED
17+
IS_FAILED="$?"
18+
echo $OUTPUT | grep OK | grep -q 'Ran'
19+
IS_PASSED="$?"
20+
echo $OUTPUT | grep "QGIS died on signal"
21+
IS_DEAD="$?"
22+
echo "Finished running test $1."
23+
if [ "$IS_PASSED" -eq "0" ] && [ "$IS_FAILED" -eq "1" ] && [ "$IS_DEAD" -eq "1" ]; then
24+
exit 0;
25+
fi
26+
exit 1

0 commit comments

Comments
 (0)
Please sign in to comment.