Added .travis.yml (for travis-ci) and tox.ini files

Targets:

* Runs the unit-tests for python 2.7
* Tests unit-test coverage
* Try to build docs
* Code style checks:
  * flake8
  * isort

Codes changes:
* Fixed tests for httpdownloader (using tmp dir)
* Implemented a couple of tests for Stats plugin but they fail to run on travis

Issues:
* Can't get py26 to work because of installing libtorrent through apt and
  the option system_site_packages fails for 2.6.
This commit is contained in:
bendikro 2014-09-21 16:37:29 +02:00 committed by Calum Lind
parent 8dc9a0773c
commit 66f2739be7
18 changed files with 285 additions and 62 deletions

35
.travis.yml Normal file
View File

@ -0,0 +1,35 @@
language: python
python:
# - "2.6"
- "2.7"
# command to install dependencies
install:
- pip install tox
- lsb_release -a
- sudo add-apt-repository ppa:deluge-team/ppa -y
- sudo apt-get update
- sudo apt-get install python-libtorrent
script:
- tox
env:
- TOX_ENV=pydef
- TOX_ENV=plugins
- TOX_ENV=flake8
- TOX_ENV=flake8-complexity
- TOX_ENV=isort
- TOX_ENV=docs
- TOX_ENV=testcoverage
virtualenv:
system_site_packages: true
before_script:
- export PYTHONPATH=$PYTHONPATH:$PWD
- python -c "import libtorrent as lt; print lt.version"
script:
- tox -e $TOX_ENV

View File

@ -30,6 +30,7 @@ try:
import dbus import dbus
bus = dbus.SessionBus() bus = dbus.SessionBus()
dbus_fileman = bus.get_object("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1") dbus_fileman = bus.get_object("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1")
except: except:
dbus_fileman = None dbus_fileman = None

View File

@ -1,6 +0,0 @@
#!/bin/sh
while true; do
python test.py
sleep 2
done;

View File

@ -1,22 +0,0 @@
from __future__ import print_function
from deluge.common import fsize
from deluge.ui.client import sclient
sclient.set_core_uri()
def print_totals(totals):
for name, value in totals.iteritems():
print(name, fsize(value))
print("overhead:")
print("up:", fsize(totals["total_upload"] - totals["total_payload_upload"]))
print("down:", fsize(totals["total_download"] - totals["total_payload_download"]))
print("==totals==")
print_totals(sclient.stats_get_totals())
print("==session totals==")
print_totals(sclient.stats_get_session_totals())

View File

@ -0,0 +1,44 @@
import twisted.internet.defer as defer
from twisted.trial import unittest
import deluge.component as component
from deluge.common import fsize
from deluge.tests import common as tests_common
from deluge.ui.client import client
def print_totals(totals):
for name, value in totals.iteritems():
print(name, fsize(value))
print("overhead:")
print("up:", fsize(totals["total_upload"] - totals["total_payload_upload"]))
print("down:", fsize(totals["total_download"] - totals["total_payload_download"]))
class StatsTestCase(unittest.TestCase):
def setUp(self): # NOQA
defer.setDebugging(True)
tests_common.set_tmp_config_dir()
client.start_classic_mode()
client.core.enable_plugin("Stats")
def tearDown(self): # NOQA
client.stop_classic_mode()
def on_shutdown(result):
component._ComponentRegistry.components = {}
return component.shutdown().addCallback(on_shutdown)
def test_client_totals(self):
def callback(args):
print_totals(args)
d = client.stats.get_totals()
d.addCallback(callback)
def test_session_totals(self):
def callback(args):
print_totals(args)
d = client.stats.get_session_totals()
d.addCallback(callback)

View File

@ -2,10 +2,11 @@ from twisted.internet import defer
from twisted.internet.error import CannotListenError from twisted.internet.error import CannotListenError
from twisted.trial import unittest from twisted.trial import unittest
import deluge.tests.common as common
from deluge import error from deluge import error
from deluge.core.authmanager import AUTH_LEVEL_ADMIN from deluge.core.authmanager import AUTH_LEVEL_ADMIN
from deluge.ui.client import Client, client, DaemonSSLProxy from deluge.ui.client import client, Client, DaemonSSLProxy
from . import common
class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy): class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy):

View File

@ -7,7 +7,8 @@ from twisted.trial import unittest
import deluge.config import deluge.config
from deluge.config import Config from deluge.config import Config
from deluge.tests.common import set_tmp_config_dir
from .common import set_tmp_config_dir
DEFAULTS = {"string": "foobar", "int": 1, "float": 0.435, "bool": True, "unicode": u"foobar"} DEFAULTS = {"string": "foobar", "int": 1, "float": 0.435, "bool": True, "unicode": u"foobar"}

View File

@ -13,11 +13,12 @@ from twisted.web.static import File
import deluge.component as component import deluge.component as component
import deluge.error import deluge.error
import deluge.tests.common as common
from deluge.core.core import Core from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer from deluge.core.rpcserver import RPCServer
from deluge.ui.web.common import compress from deluge.ui.web.common import compress
from . import common
warnings.filterwarnings("ignore", category=RuntimeWarning) warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.resetwarnings() warnings.resetwarnings()

View File

@ -1,3 +1,4 @@
import tempfile
import warnings import warnings
from email.utils import formatdate from email.utils import formatdate
@ -8,11 +9,12 @@ from twisted.trial import unittest
from twisted.web.http import NOT_MODIFIED from twisted.web.http import NOT_MODIFIED
from twisted.web.server import Site from twisted.web.server import Site
import deluge.tests.common as common
from deluge.httpdownloader import download_file from deluge.httpdownloader import download_file
from deluge.log import setup_logger from deluge.log import setup_logger
from deluge.ui.web.common import compress from deluge.ui.web.common import compress
from . import common
try: try:
from twisted.web.resource import Resource from twisted.web.resource import Resource
except ImportError: except ImportError:
@ -25,6 +27,11 @@ warnings.resetwarnings()
rpath = common.rpath rpath = common.rpath
temp_dir = tempfile.mkdtemp()
def fname(name):
return "%s/%s" % (temp_dir, name)
class TestRedirectResource(Resource): class TestRedirectResource(Resource):
@ -132,13 +139,13 @@ class DownloadFileTestCase(unittest.TestCase):
return filename return filename
def test_download(self): def test_download(self):
d = download_file("http://localhost:%d/" % self.listen_port, "index.html") d = download_file("http://localhost:%d/" % self.listen_port, fname("index.html"))
d.addCallback(self.assertEqual, "index.html") d.addCallback(self.assertEqual, fname("index.html"))
return d return d
def test_download_without_required_cookies(self): def test_download_without_required_cookies(self):
url = "http://localhost:%d/cookie" % self.listen_port url = "http://localhost:%d/cookie" % self.listen_port
d = download_file(url, "none") d = download_file(url, fname("none"))
d.addCallback(self.fail) d.addCallback(self.fail)
d.addErrback(self.assertIsInstance, Failure) d.addErrback(self.assertIsInstance, Failure)
return d return d
@ -146,68 +153,68 @@ class DownloadFileTestCase(unittest.TestCase):
def test_download_with_required_cookies(self): def test_download_with_required_cookies(self):
url = "http://localhost:%d/cookie" % self.listen_port url = "http://localhost:%d/cookie" % self.listen_port
cookie = {"cookie": "password=deluge"} cookie = {"cookie": "password=deluge"}
d = download_file(url, "monster", headers=cookie) d = download_file(url, fname("monster"), headers=cookie)
d.addCallback(self.assertEqual, "monster") d.addCallback(self.assertEqual, fname("monster"))
d.addCallback(self.assertContains, "COOKIE MONSTER!") d.addCallback(self.assertContains, "COOKIE MONSTER!")
return d return d
def test_download_with_rename(self): def test_download_with_rename(self):
url = "http://localhost:%d/rename?filename=renamed" % self.listen_port url = "http://localhost:%d/rename?filename=renamed" % self.listen_port
d = download_file(url, "original") d = download_file(url, fname("original"))
d.addCallback(self.assertEqual, "renamed") d.addCallback(self.assertEqual, fname("renamed"))
d.addCallback(self.assertContains, "This file should be called renamed") d.addCallback(self.assertContains, "This file should be called renamed")
return d return d
def test_download_with_rename_exists(self): def test_download_with_rename_exists(self):
open('renamed', 'w').close() open(fname('renamed'), 'w').close()
url = "http://localhost:%d/rename?filename=renamed" % self.listen_port url = "http://localhost:%d/rename?filename=renamed" % self.listen_port
d = download_file(url, "original") d = download_file(url, fname("original"))
d.addCallback(self.assertEqual, "renamed-1") d.addCallback(self.assertEqual, fname("renamed-1"))
d.addCallback(self.assertContains, "This file should be called renamed") d.addCallback(self.assertContains, "This file should be called renamed")
return d return d
def test_download_with_rename_sanitised(self): def test_download_with_rename_sanitised(self):
url = "http://localhost:%d/rename?filename=/etc/passwd" % self.listen_port url = "http://localhost:%d/rename?filename=/etc/passwd" % self.listen_port
d = download_file(url, "original") d = download_file(url, fname("original"))
d.addCallback(self.assertEqual, "passwd") d.addCallback(self.assertEqual, fname("passwd"))
d.addCallback(self.assertContains, "This file should be called /etc/passwd") d.addCallback(self.assertContains, "This file should be called /etc/passwd")
return d return d
def test_download_with_rename_prevented(self): def test_download_with_rename_prevented(self):
url = "http://localhost:%d/rename?filename=spam" % self.listen_port url = "http://localhost:%d/rename?filename=spam" % self.listen_port
d = download_file(url, "forced", force_filename=True) d = download_file(url, fname("forced"), force_filename=True)
d.addCallback(self.assertEqual, "forced") d.addCallback(self.assertEqual, fname("forced"))
d.addCallback(self.assertContains, "This file should be called spam") d.addCallback(self.assertContains, "This file should be called spam")
return d return d
def test_download_with_gzip_encoding(self): def test_download_with_gzip_encoding(self):
url = "http://localhost:%d/gzip?msg=success" % self.listen_port url = "http://localhost:%d/gzip?msg=success" % self.listen_port
d = download_file(url, "gzip_encoded") d = download_file(url, fname("gzip_encoded"))
d.addCallback(self.assertContains, "success") d.addCallback(self.assertContains, "success")
return d return d
def test_download_with_gzip_encoding_disabled(self): def test_download_with_gzip_encoding_disabled(self):
url = "http://localhost:%d/gzip?msg=fail" % self.listen_port url = "http://localhost:%d/gzip?msg=fail" % self.listen_port
d = download_file(url, "gzip_encoded", allow_compression=False) d = download_file(url, fname("gzip_encoded"), allow_compression=False)
d.addCallback(self.failIfContains, "fail") d.addCallback(self.failIfContains, "fail")
return d return d
def test_page_redirect(self): def test_page_redirect(self):
url = 'http://localhost:%d/redirect' % self.listen_port url = 'http://localhost:%d/redirect' % self.listen_port
d = download_file(url, "none") d = download_file(url, fname("none"))
d.addCallback(self.fail) d.addCallback(self.fail)
d.addErrback(self.assertIsInstance, Failure) d.addErrback(self.assertIsInstance, Failure)
return d return d
def test_page_not_found(self): def test_page_not_found(self):
d = download_file("http://localhost:%d/page/not/found" % self.listen_port, "none") d = download_file("http://localhost:%d/page/not/found" % self.listen_port, fname("none"))
d.addCallback(self.fail) d.addCallback(self.fail)
d.addErrback(self.assertIsInstance, Failure) d.addErrback(self.assertIsInstance, Failure)
return d return d
def test_page_not_modified(self): def test_page_not_modified(self):
headers = {'If-Modified-Since': formatdate(usegmt=True)} headers = {'If-Modified-Since': formatdate(usegmt=True)}
d = download_file("http://localhost:%d/" % self.listen_port, "index.html", headers=headers) d = download_file("http://localhost:%d/" % self.listen_port, fname("index.html"), headers=headers)
d.addCallback(self.fail) d.addCallback(self.fail)
d.addErrback(self.assertIsInstance, Failure) d.addErrback(self.assertIsInstance, Failure)
return d return d

View File

@ -7,12 +7,13 @@ from twisted.trial import unittest
import deluge.component as component import deluge.component as component
import deluge.core.torrent import deluge.core.torrent
import deluge.tests.common as common
from deluge._libtorrent import lt from deluge._libtorrent import lt
from deluge.core.core import Core from deluge.core.core import Core
from deluge.core.rpcserver import RPCServer from deluge.core.rpcserver import RPCServer
from deluge.core.torrent import Torrent from deluge.core.torrent import Torrent
from . import common
config_setup = False config_setup = False
core = None core = None
rpcserver = None rpcserver = None

View File

@ -3,9 +3,10 @@ import os
from twisted.trial import unittest from twisted.trial import unittest
import deluge.ui.tracker_icons import deluge.ui.tracker_icons
from deluge.tests.common import set_tmp_config_dir
from deluge.ui.tracker_icons import TrackerIcon, TrackerIcons from deluge.ui.tracker_icons import TrackerIcon, TrackerIcons
from .common import set_tmp_config_dir
set_tmp_config_dir() set_tmp_config_dir()
icons = TrackerIcons() icons = TrackerIcons()

View File

@ -29,7 +29,7 @@ from deluge import component, httpdownloader
from deluge.common import is_magnet from deluge.common import is_magnet
from deluge.configmanager import ConfigManager, get_config_dir from deluge.configmanager import ConfigManager, get_config_dir
from deluge.ui import common as uicommon from deluge.ui import common as uicommon
from deluge.ui.client import Client, client from deluge.ui.client import client, Client
from deluge.ui.coreconfig import CoreConfig from deluge.ui.coreconfig import CoreConfig
from deluge.ui.sessionproxy import SessionProxy from deluge.ui.sessionproxy import SessionProxy
from deluge.ui.web.common import _, compress from deluge.ui.web.common import _, compress

View File

@ -14,6 +14,7 @@ import os
import sys import sys
from datetime import date from datetime import date
import mock
import pkg_resources import pkg_resources
try: try:
@ -22,6 +23,7 @@ except ImportError:
get_version = None get_version = None
on_rtd = os.environ.get('READTHEDOCS', None) == 'True' on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
on_travis = os.environ.get('TRAVIS', None) == 'true'
# If your extensions are in another directory, add it here. If the directory # If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it # is relative to the documentation root, use os.path.abspath to make it
@ -55,7 +57,7 @@ MOCK_MODULES = ['deluge.ui.languages', 'deluge.ui.countries', 'deluge.ui.gtkui.g
'twisted.web', 'twisted.web.client', 'twisted.web.error', 'twisted.web', 'twisted.web.client', 'twisted.web.error',
'win32gui', 'win32api', 'win32con', '_winreg'] 'win32gui', 'win32api', 'win32con', '_winreg']
if on_rtd: if on_rtd or on_travis:
MOCK_MODULES += ['libtorrent', 'pygtk', "gtk", "gobject", "gtk.gdk", "pango", "cairo", "pangocairo"] MOCK_MODULES += ['libtorrent', 'pygtk', "gtk", "gobject", "gtk.gdk", "pango", "cairo", "pangocairo"]
for mod_name in MOCK_MODULES: for mod_name in MOCK_MODULES:
@ -67,7 +69,7 @@ for mod_name in MOCK_MODULES:
# Add any Sphinx extension module names here, as strings. They can be extensions # Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinxcontrib.napoleon'] extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinxcontrib.napoleon', 'sphinx.ext.coverage']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']

View File

@ -14,10 +14,6 @@ includes = glib, gio, cairo, pango, pangocairo, atk, gobject, gtk.keysyms,
zope.interface, mako.cache, email.mime, libtorrent, gtkosx_application zope.interface, mako.cache, email.mime, libtorrent, gtkosx_application
frameworks = CoreFoundation, Foundation, AppKit frameworks = CoreFoundation, Foundation, AppKit
[flake8]
max-line-length = 120
builtins = _,__request__
[isort] [isort]
known_standard_library=unicodedata known_standard_library=unicodedata
line_length=120 line_length=120

View File

@ -18,6 +18,7 @@ from distutils.command.build import build as _build
from distutils.command.clean import clean as _clean from distutils.command.clean import clean as _clean
from setuptools import find_packages, setup from setuptools import find_packages, setup
from setuptools.command.test import test as TestCommand
import msgfmt import msgfmt
from version import get_version from version import get_version
@ -35,6 +36,23 @@ def windows_check():
desktop_data = 'deluge/ui/data/share/applications/deluge.desktop' desktop_data = 'deluge/ui/data/share/applications/deluge.desktop'
class PyTest(TestCommand):
def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = []
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import pytest
errcode = pytest.main(self.test_args)
sys.exit(errcode)
class BuildTranslations(cmd.Command): class BuildTranslations(cmd.Command):
description = 'Compile .po files into .mo files & create .desktop file' description = 'Compile .po files into .mo files & create .desktop file'
@ -269,7 +287,8 @@ cmdclass = {
'build_docs': BuildDocs, 'build_docs': BuildDocs,
'clean_plugins': CleanPlugins, 'clean_plugins': CleanPlugins,
'clean': Clean, 'clean': Clean,
'egg_info_plugins': EggInfoPlugins 'egg_info_plugins': EggInfoPlugins,
'test': PyTest,
} }
# Data files to be installed to the system # Data files to be installed to the system
@ -333,6 +352,7 @@ setup(
url="http://deluge-torrent.org", url="http://deluge-torrent.org",
license="GPLv3", license="GPLv3",
cmdclass=cmdclass, cmdclass=cmdclass,
tests_require=['pytest'],
data_files=_data_files, data_files=_data_files,
package_data={"deluge": ["ui/gtkui/glade/*.glade", package_data={"deluge": ["ui/gtkui/glade/*.glade",
"ui/gtkui/glade/*.ui", "ui/gtkui/glade/*.ui",

141
tox.ini Normal file
View File

@ -0,0 +1,141 @@
# Tox (http://tox.testrun.org/) is a tool for running tests
# in multiple virtualenvs. This configuration file will run the
# test suite on all supported python versions. To use it, "pip install tox"
# and then run "tox" from this directory.
[flake8]
max-line-length = 120
builtins = _,__request__
exclude = .tox, .git, dist, build
ignore = E123,E133,E226,E241,E242
[tox]
envlist = py27, py26, flake8, isort, docs
[testenv]
commands = {envpython} setup.py test
sitepackages=True
deps =
twisted
service_identity
mako
chardet
pyopenssl
pyxdg
pytest
whitelist_externals=
py.test
setenv =
PYTHONPATH = {env:PYTHONPATH}:{env:PWD}
[pytest]
python_functions=test_
norecursedirs=.tox .git dist build
pep8maxlinelength = 120
whitelist_externals=
{[testenv]whitelist_externals}
commands=
py.test deluge
[testenv:testcoverage]
install_command=pip install {opts} {packages}
deps =
{[testenv]deps}
pytest-cov
coverage
whitelist_externals=
{[testenv]whitelist_externals}
coverage
commands=
coverage run --branch --source=deluge -m py.test deluge/tests/
coverage report
# For creating html report
# coverage html -d docs/build/htmlcoverage
[testenv:pydef]
commands=
python -c "import libtorrent as lt; print lt.version"
py.test deluge/tests
[testenv:plugins]
commands=
py.test deluge/plugins
[testenv:py26]
basepython=python2.6
commands=
{[testenv:pydef]commands}
[testenv:py27]
basepython=python2.7
commands=
{[testenv:pydef]commands}
[testenv:isort]
deps =
{[testenv]deps}
isort
whitelist_externals=
{[testenv]whitelist_externals}
isort
commands=
python -c "import subprocess, sys; output = subprocess.check_output('isort --recursive --diff --stdout deluge docs/ *.py', shell=True); print output; sys.exit(len(output) != 0)"
[testenv:flake8]
setenv =
{[testenv]setenv}
whitelist_externals=
{[testenv]whitelist_externals}
flake8
deps =
{[testenv]deps}
flake8
pep8-naming
commands=
flake8 deluge
[testenv:flake8-complexity]
setenv =
{[testenv]setenv}
whitelist_externals=
{[testenv]whitelist_externals}
flake8
sh
deps =
{[testenv:flake8]deps}
mccabe
commands=
sh -c "flake8 --max-complexity 10 deluge || true"
[testenv:docscoverage]
changedir=docs
install_command=pip install {opts} {packages}
deps =
{[testenv]deps}
sphinx
sphinxcontrib-napoleon
coverage
pytest-cov
whitelist_externals=
{[testenv]whitelist_externals}
mkdir
sphinx-build
commands=
mkdir -p build/doccoverage
sphinx-build -W -b coverage -d build/doctrees source build/doccoverage
py.test --doctest-glob='*.rst'
[testenv:docs]
changedir=docs
install_command=pip install {opts} --allow-external PIL --allow-unverified PIL {packages}
whitelist_externals=
{[testenv]whitelist_externals}
sphinx-build
deps =
{[testenv]deps}
sphinx
sphinxcontrib-napoleon
PIL
commands=
python -c "import sphinxcontrib.napoleon"
sphinx-build -E -W -b html -d build/doctrees source build/html