- rework running of liveconfig tests

- better README reflecting how to use things, don't advertise
  run-integration-tests to only have one documented way
  and use less tools for rust-devs that just want to run
  python tests

- fix test skipping and get circle-ci to play along

- update docker related docs as well
This commit is contained in:
holger krekel
2019-08-10 15:58:35 +02:00
parent 5438be891b
commit 1bde9b4dd3
10 changed files with 150 additions and 113 deletions

View File

@@ -16,7 +16,7 @@ export BRANCH=${CIRCLE_BRANCH:-test7}
#fi #fi
# run everything else inside docker (TESTS, DOCS, WHEELS) # run everything else inside docker (TESTS, DOCS, WHEELS)
docker run -e BRANCH -e TESTS -e DOCS \ docker run -e DCC_PY_LIVECONFIG -e BRANCH -e TESTS -e DOCS \
--rm -it -v $(pwd):/mnt -w /mnt \ --rm -it -v $(pwd):/mnt -w /mnt \
deltachat/coredeps ci_scripts/run_all.sh deltachat/coredeps ci_scripts/run_all.sh

View File

@@ -3,9 +3,9 @@
set -e -x set -e -x
# Install Rust # Install Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-04-19 -y curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y
export PATH=/root/.cargo/bin:$PATH export PATH=/root/.cargo/bin:$PATH
rustc --version rustc --version
# remove some 300-400 MB that we don't need for automated builds # remove some 300-400 MB that we don't need for automated builds
rm -rf /root/.rustup/toolchains/nightly-2019-04-19-x86_64-unknown-linux-gnu/share/ rm -rf /root/.rustup/toolchains/nightly-2019-07-10-x86_64-unknown-linux-gnu/share/

View File

@@ -37,6 +37,10 @@ if [ -n "$TESTS" ]; then
export PYTHONDONTWRITEBYTECODE=1 export PYTHONDONTWRITEBYTECODE=1
# run tox # run tox
# XXX we don't run liveconfig tests because they hang sometimes
# see https://github.com/deltachat/deltachat-core-rust/issues/331
unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
popd popd
fi fi

View File

@@ -39,6 +39,12 @@ and push them to a python package index. To install the latest github ``master``
pip install -i https://m.devpi.net/dc/master deltachat pip install -i https://m.devpi.net/dc/master deltachat
.. note::
If you can help to automate the building of wheels for Mac or Windows,
that'd be much appreciated! please then get
`in contact with us <https://delta.chat/en/contribute>`_.
Installing bindings from source Installing bindings from source
=============================== ===============================
@@ -48,34 +54,56 @@ to core deltachat library::
git clone https://github.com/deltachat/deltachat-core-rust git clone https://github.com/deltachat/deltachat-core-rust
cd deltachat-core-rust cd deltachat-core-rust
cargo build -p deltachat_ffi --release
This will result in a ``libdeltachat.so`` and ``libdeltachat.a`` files
in the ``target/release`` directory. These files are needed for
creating the python bindings for deltachat::
cd python cd python
DCC_RS_DEV=`pwd`/.. pip install -e .
Now test if the bindings find the correct library:: It is always a good idea to create a python "virtualenv".
Install "virtualenv" on your system and run::
python -c 'import deltachat ; print(deltachat.__version__)' virtualenv venv
source venv/bin/activate
This should print your deltachat bindings version. Afterwards ``which python`` tells you that it comes out of the "venv"
directory that contains all python install artifacts. Let's first
install test tools::
pip install pytest pytest-timeout pytest-faulthandler requests
then cargo-build and install the deltachat bindings::
python install_python_bindings.py
The bindings will be installed in release mode but with debug symbols.
The release mode is neccessary because some tests generate RSA keys
which is prohibitively slow in debug mode.
After succcessul binding installation you can finally run the tests::
pytest -v tests
.. note:: .. note::
If you can help to automate the building of wheels for Mac or Windows, Some tests are sometimes failing/hanging because of
that'd be much appreciated! please then get https://github.com/deltachat/deltachat-core-rust/issues/331
`in contact with us <https://delta.chat/en/contribute>`_. and
https://github.com/deltachat/deltachat-core-rust/issues/326
Using a system-installed deltachat-core-rust
--------------------------------------------
When calling ``pip`` without specifying the ``DCC_RS_DEV`` environment running "live" tests (experimental)
variable cffi will try to use a ``deltachat.h`` from a system location -----------------------------------
like ``/usr/local/include`` and will try to dynamically link against a
``libdeltachat.so`` in a similar location (e.g. ``/usr/local/lib``). If you want to run "liveconfig" functional tests you can set
``DCC_PY_LIVECONFIG`` to:
- a particular https-url that you can ask for from the delta
chat devs.
- or the path of a file that contains two lines, each describing
via "addr=... mail_pwd=..." a test account login that will
be used for the live tests.
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real
e-mail accounts and run through all functional "liveconfig" tests.
Code examples Code examples
@@ -84,68 +112,34 @@ Code examples
You may look at `examples <https://py.delta.chat/examples.html>`_. You may look at `examples <https://py.delta.chat/examples.html>`_.
Running tests
=============
Get a checkout of the `deltachat-core-rust github repository`_ and type::
pip install tox
./run-integration-tests.sh
If you want to run functional tests with real
e-mail test accounts, generate a "liveconfig" file where each
lines contains test account settings, for example::
# 'liveconfig' file specifying imap/smtp accounts
addr=some-email@example.org mail_pw=password
addr=other-email@example.org mail_pw=otherpassword
The "keyword=value" style allows to specify any
`deltachat account config setting <https://c.delta.chat/classdc__context__t.html#aff3b894f6cfca46cab5248fdffdf083d>`_ so you can also specify smtp or imap servers, ports, ssl modes etc.
Typically DC's automatic configuration allows to not specify these settings.
The ``run-integration-tests.sh`` script will automatically use
``python/liveconfig`` if it exists, to manually run tests with this
``liveconfig`` file use::
tox -- --liveconfig liveconfig
.. _`deltachat-core-rust github repository`: https://github.com/deltachat/deltachat-core-rust .. _`deltachat-core-rust github repository`: https://github.com/deltachat/deltachat-core-rust
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust .. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
Running test using a debug build
--------------------------------
If you need to examine e.g. a coredump you may want to run the tests
using a debug build::
DCC_RS_TARGET=debug ./run-integration-tests.sh -e py37 -- -x -v -k failing_test
Building manylinux1 wheels Building manylinux1 wheels
========================== ==========================
.. note::
This section may not fully work.
Building portable manylinux1 wheels which come with libdeltachat.so Building portable manylinux1 wheels which come with libdeltachat.so
and all it's dependencies is easy using the provided docker tooling. and all it's dependencies is easy using the provided docker tooling.
using docker pull / premade images using docker pull / premade images
------------------------------------ ------------------------------------
We publish a build environment under the ``deltachat/wheel`` tag so We publish a build environment under the ``deltachat/coredeps`` tag so
that you can pull it from the ``hub.docker.com`` site's "deltachat" that you can pull it from the ``hub.docker.com`` site's "deltachat"
organization:: organization::
$ docker pull deltachat/wheel $ docker pull deltachat/coredeps
The ``deltachat/wheel`` image can be used to build both libdeltachat.so This docker image can be used to run tests and build Python wheels for all interpreters::
and the Python wheels::
$ docker run --rm -it -v $(pwd):/io/ deltachat/wheel /io/python/wheelbuilder/build-wheels.sh $ bash ci_scripts/ci_run.sh
This command runs a script within the image, after mounting ``$(pwd)`` as ``/io`` within This command runs tests and build-wheel scripts in a docker container.
the docker image. The script is specified as a path within the docker image's filesystem.
The resulting wheel files will be in ``python/wheelhouse``.
Optionally build your own docker image Optionally build your own docker image
@@ -154,10 +148,10 @@ Optionally build your own docker image
If you want to build your own custom docker image you can do this:: If you want to build your own custom docker image you can do this::
$ cd deltachat-core # cd to deltachat-core checkout directory $ cd deltachat-core # cd to deltachat-core checkout directory
$ docker build -t deltachat/wheel python/wheelbuilder/ $ docker build -t deltachat/coredeps ci_scripts/docker_coredeps
This will use the ``python/wheelbuilder/Dockerfile`` to build This will use the ``ci_scripts/docker_coredeps/Dockerfile`` to build
up docker image called ``deltachat/wheel``. You can afterwards up docker image called ``deltachat/coredeps``. You can afterwards
find it with:: find it with::
$ docker images $ docker images

View File

@@ -6,29 +6,18 @@
import os import os
import subprocess import subprocess
import os
if __name__ == "__main__": if __name__ == "__main__":
os.environ["DCC_RS_TARGET"] = target = "release" os.environ["DCC_RS_TARGET"] = target = "release"
if "DCC_RS_DEV" not in os.environ:
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.environ["DCC_RS_DEV"] = dn
toml = os.path.join(os.getcwd(), "..", "Cargo.toml") os.environ["RUSTFLAGS"] = "-g"
assert os.path.exists(toml)
with open(toml) as f:
s = orig = f.read()
s += "\n"
s += "[profile.release]\n"
s += "debug = true\n"
with open(toml, "w") as f:
f.write(s)
print("temporarily modifying Cargo.toml to provide release build with debug symbols ")
try:
subprocess.check_call([ subprocess.check_call([
"cargo", "build", "-p", "deltachat_ffi", "--" + target "cargo", "build", "-p", "deltachat_ffi", "--" + target
]) ])
finally:
with open(toml, "w") as f:
f.write(orig)
print("\nreseted Cargo.toml to previous original state")
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True) subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
subprocess.check_call([ subprocess.check_call([

View File

@@ -6,10 +6,15 @@ import platform
import os import os
import cffi import cffi
import shutil import shutil
from os.path import dirname as dn
from os.path import abspath
def ffibuilder(): def ffibuilder():
projdir = os.environ.get('DCC_RS_DEV') projdir = os.environ.get('DCC_RS_DEV')
if not projdir:
p = dn(dn(dn(dn(abspath(__file__)))))
projdir = os.environ["DCC_RS_DEV"] = p
target = os.environ.get('DCC_RS_TARGET', 'release') target = os.environ.get('DCC_RS_TARGET', 'release')
if projdir: if projdir:
if platform.system() == 'Darwin': if platform.system() == 'Darwin':

View File

@@ -50,8 +50,9 @@ class Account(object):
self._configkeys = self.get_config("sys.config_keys").split() self._configkeys = self.get_config("sys.config_keys").split()
self._imex_completed = threading.Event() self._imex_completed = threading.Event()
def __del__(self): # XXX this can cause "illegal instructions" at test ends so we omit it for now
self.shutdown() # def __del__(self):
# self.shutdown()
def _check_config_key(self, name): def _check_config_key(self, name):
if name not in self._configkeys: if name not in self._configkeys:

View File

@@ -1,9 +1,9 @@
from __future__ import print_function from __future__ import print_function
import os import os
import pytest import pytest
import requests
import time import time
from deltachat import Account from deltachat import Account
from deltachat import props
from deltachat.capi import lib from deltachat.capi import lib
import tempfile import tempfile
@@ -36,6 +36,8 @@ def pytest_runtest_call(item):
def pytest_report_header(config, startdir): def pytest_report_header(config, startdir):
summary = []
t = tempfile.mktemp() t = tempfile.mktemp()
try: try:
ac = Account(t, eventlogging=False) ac = Account(t, eventlogging=False)
@@ -43,13 +45,18 @@ def pytest_report_header(config, startdir):
ac.shutdown() ac.shutdown()
finally: finally:
os.remove(t) os.remove(t)
summary = ['Deltachat core={} sqlite={}'.format( summary.extend(['Deltachat core={} sqlite={}'.format(
info['deltachat_core_version'], info['deltachat_core_version'],
info['sqlite_version'], info['sqlite_version'],
)] )])
cfg = config.getoption('--liveconfig')
cfg = config.option.liveconfig
if cfg: if cfg:
summary.append('Liveconfig: {}'.format(os.path.abspath(cfg))) if "#" in cfg:
url, token = cfg.split("#", 1)
summary.append('Liveconfig provider: {}#<token ommitted>'.format(url))
else:
summary.append('Liveconfig file: {}'.format(cfg))
return summary return summary
@@ -66,9 +73,56 @@ def data():
return Data() return Data()
class SessionLiveConfigFromFile:
def __init__(self, fn):
self.fn = fn
self.configlist = []
for line in open(fn):
if line.strip() and not line.strip().startswith('#'):
d = {}
for part in line.split():
name, value = part.split("=")
d[name] = value
self.configlist.append(d)
def get(self, index):
return self.configlist[index]
def exists(self):
return bool(self.configlist)
class SessionLiveConfigFromURL:
def __init__(self, url, create_token):
self.configlist = []
for i in range(2):
res = requests.post(url, json={"token_create_user": int(create_token)})
if res.status_code != 200:
pytest.skip("creating newtmpuser failed {!r}".format(res))
d = res.json()
config = dict(addr=d["email"], mail_pw=d["password"])
self.configlist.append(config)
def get(self, index):
return self.configlist[index]
def exists(self):
return bool(self.configlist)
@pytest.fixture(scope="session")
def session_liveconfig(request):
liveconfig_opt = request.config.option.liveconfig
if liveconfig_opt:
if liveconfig_opt.startswith("http"):
url, create_token = liveconfig_opt.split("#", 1)
return SessionLiveConfigFromURL(url, create_token)
else:
return SessionLiveConfigFromFile(liveconfig_opt)
@pytest.fixture @pytest.fixture
def acfactory(pytestconfig, tmpdir, request): def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
fn = pytestconfig.getoption("--liveconfig")
class AccountMaker: class AccountMaker:
def __init__(self): def __init__(self):
@@ -82,18 +136,6 @@ def acfactory(pytestconfig, tmpdir, request):
fin = self._finalizers.pop() fin = self._finalizers.pop()
fin() fin()
@props.cached
def configlist(self):
configlist = []
for line in open(fn):
if line.strip() and not line.strip().startswith('#'):
d = {}
for part in line.split():
name, value = part.split("=")
d[name] = value
configlist.append(d)
return configlist
def get_unconfigured_account(self): def get_unconfigured_account(self):
self.offline_count += 1 self.offline_count += 1
tmpdb = tmpdir.join("offlinedb%d" % self.offline_count) tmpdb = tmpdir.join("offlinedb%d" % self.offline_count)
@@ -116,10 +158,10 @@ def acfactory(pytestconfig, tmpdir, request):
return ac return ac
def get_online_configuring_account(self): def get_online_configuring_account(self):
if not fn: if not session_liveconfig:
pytest.skip("specify a --liveconfig file to run tests with real accounts") pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
configdict = session_liveconfig.get(self.live_count)
self.live_count += 1 self.live_count += 1
configdict = self.configlist.pop(0)
if "e2ee_enabled" not in configdict: if "e2ee_enabled" not in configdict:
configdict["e2ee_enabled"] = "1" configdict["e2ee_enabled"] = "1"
tmpdb = tmpdir.join("livedb%d" % self.live_count) tmpdb = tmpdir.join("livedb%d" % self.live_count)

View File

@@ -8,7 +8,7 @@ envlist =
[testenv] [testenv]
commands = commands =
pytest -v -rsXx {posargs:tests} pytest -s -v -rsXx {posargs:tests}
python tests/package_wheels.py {toxworkdir}/wheelhouse python tests/package_wheels.py {toxworkdir}/wheelhouse
passenv = passenv =
TRAVIS TRAVIS
@@ -19,6 +19,7 @@ deps =
pytest pytest
pytest-faulthandler pytest-faulthandler
pdbpp pdbpp
requests
[testenv:auditwheels] [testenv:auditwheels]
skipsdist = True skipsdist = True
@@ -51,6 +52,7 @@ commands =
[pytest] [pytest]
addopts = -v -rs
python_files = tests/test_*.py python_files = tests/test_*.py
norecursedirs = .tox norecursedirs = .tox
xfail_strict=true xfail_strict=true

View File

@@ -23,7 +23,7 @@ if [ $? != 0 ]; then
fi fi
pushd python pushd python
if [ -e "./liveconfig" ]; then if [ -e "./liveconfig" && -z "$DCC_PY_LIVECONFIG" ]; then
export DCC_PY_LIVECONFIG=liveconfig export DCC_PY_LIVECONFIG=liveconfig
fi fi
tox "$@" tox "$@"