[Tests] Various fixes for unit tests and tox

* Added custom trial reporter for TODO with test example in test_torrentmanager.py
* Set Stats plugin tests as todo
* Disable new_release_check when running unit tests
* Added pytest.mark.slow to test_core.test_test_listen_port
* Get rid of unit test warnings (Caused by bad names in test classes)
* Removed warnings.filterwarnings in test files.
* Added separate tox target for generating test coverage HTML report.
This commit is contained in:
bendikro 2014-10-04 00:14:20 +02:00 committed by Calum Lind
parent 178c417fb0
commit 8334bf9477
10 changed files with 135 additions and 60 deletions

View File

@ -17,6 +17,8 @@ script:
env: env:
- TOX_ENV=pydef - TOX_ENV=pydef
- TOX_ENV=trial
- TOX_ENV=todo
# - TOX_ENV=plugins # - TOX_ENV=plugins
- TOX_ENV=flake8 - TOX_ENV=flake8
- TOX_ENV=flake8-complexity - TOX_ENV=flake8-complexity

View File

@ -1,3 +1,4 @@
import pytest
import twisted.internet.defer as defer import twisted.internet.defer as defer
from twisted.trial import unittest from twisted.trial import unittest
@ -31,13 +32,19 @@ class StatsTestCase(unittest.TestCase):
component._ComponentRegistry.components = {} component._ComponentRegistry.components = {}
return component.shutdown().addCallback(on_shutdown) return component.shutdown().addCallback(on_shutdown)
@pytest.mark.todo
def test_client_totals(self): def test_client_totals(self):
StatsTestCase.test_client_totals.im_func.todo = "To be fixed"
def callback(args): def callback(args):
print_totals(args) print_totals(args)
d = client.stats.get_totals() d = client.stats.get_totals()
d.addCallback(callback) d.addCallback(callback)
@pytest.mark.todo
def test_session_totals(self): def test_session_totals(self):
StatsTestCase.test_session_totals.im_func.todo = "To be fixed"
def callback(args): def callback(args):
print_totals(args) print_totals(args)
d = client.stats.get_session_totals() d = client.stats.get_session_totals()

View File

@ -13,6 +13,11 @@ import deluge.log
deluge.log.setup_logger("none") deluge.log.setup_logger("none")
def disable_new_release_check():
import deluge.core.preferencesmanager
deluge.core.preferencesmanager.DEFAULT_PREFS["new_release_check"] = False
def set_tmp_config_dir(): def set_tmp_config_dir():
config_directory = tempfile.mkdtemp() config_directory = tempfile.mkdtemp()
deluge.configmanager.set_config_dir(config_directory) deluge.configmanager.set_config_dir(config_directory)

View File

@ -5,7 +5,7 @@ import deluge.component as component
from .basetest import BaseTestCase from .basetest import BaseTestCase
class TestComponent(component.Component): class ComponentTester(component.Component):
def __init__(self, name, depend=None): def __init__(self, name, depend=None):
component.Component.__init__(self, name, depend=depend) component.Component.__init__(self, name, depend=depend)
self.start_count = 0 self.start_count = 0
@ -18,7 +18,7 @@ class TestComponent(component.Component):
self.stop_count += 1 self.stop_count += 1
class TestComponentDelayStart(TestComponent): class ComponentTesterDelayStart(ComponentTester):
def start(self): def start(self):
def do_sleep(): def do_sleep():
import time import time
@ -30,7 +30,7 @@ class TestComponentDelayStart(TestComponent):
return d.addCallback(on_done) return d.addCallback(on_done)
class TestComponentUpdate(component.Component): class ComponentTesterUpdate(component.Component):
def __init__(self, name): def __init__(self, name):
component.Component.__init__(self, name) component.Component.__init__(self, name)
self.counter = 0 self.counter = 0
@ -44,7 +44,7 @@ class TestComponentUpdate(component.Component):
self.stop_count += 1 self.stop_count += 1
class TestComponentShutdown(component.Component): class ComponentTesterShutdown(component.Component):
def __init__(self, name): def __init__(self, name):
component.Component.__init__(self, name) component.Component.__init__(self, name)
self.shutdowned = False self.shutdowned = False
@ -66,7 +66,7 @@ class ComponentTestClass(BaseTestCase):
self.assertEquals(c._component_state, "Started") self.assertEquals(c._component_state, "Started")
self.assertEquals(c.start_count, 1) self.assertEquals(c.start_count, 1)
c = TestComponent("test_start_c1") c = ComponentTester("test_start_c1")
d = component.start(["test_start_c1"]) d = component.start(["test_start_c1"])
d.addCallback(on_start, c) d.addCallback(on_start, c)
return d return d
@ -85,19 +85,19 @@ class ComponentTestClass(BaseTestCase):
self.assertEquals(c2.start_count, 1) self.assertEquals(c2.start_count, 1)
return component.stop(["test_start_depends_c1"]).addCallback(on_stop, c1, c2) return component.stop(["test_start_depends_c1"]).addCallback(on_stop, c1, c2)
c1 = TestComponent("test_start_depends_c1") c1 = ComponentTester("test_start_depends_c1")
c2 = TestComponent("test_start_depends_c2", depend=["test_start_depends_c1"]) c2 = ComponentTester("test_start_depends_c2", depend=["test_start_depends_c1"])
d = component.start(["test_start_depends_c2"]) d = component.start(["test_start_depends_c2"])
d.addCallback(on_start, c1, c2) d.addCallback(on_start, c1, c2)
return d return d
def start_with_depends(self): def start_with_depends(self):
c1 = TestComponentDelayStart("test_start_all_c1") c1 = ComponentTesterDelayStart("test_start_all_c1")
c2 = TestComponent("test_start_all_c2", depend=["test_start_all_c4"]) c2 = ComponentTester("test_start_all_c2", depend=["test_start_all_c4"])
c3 = TestComponentDelayStart("test_start_all_c3", depend=["test_start_all_c5", "test_start_all_c1"]) c3 = ComponentTesterDelayStart("test_start_all_c3", depend=["test_start_all_c5", "test_start_all_c1"])
c4 = TestComponent("test_start_all_c4", depend=["test_start_all_c3"]) c4 = ComponentTester("test_start_all_c4", depend=["test_start_all_c3"])
c5 = TestComponent("test_start_all_c5") c5 = ComponentTester("test_start_all_c5")
d = component.start() d = component.start()
return (d, c1, c2, c3, c4, c5) return (d, c1, c2, c3, c4, c5)
@ -118,10 +118,10 @@ class ComponentTestClass(BaseTestCase):
return ret[0] return ret[0]
def test_register_exception(self): def test_register_exception(self):
TestComponent("test_register_exception_c1") ComponentTester("test_register_exception_c1")
self.assertRaises( self.assertRaises(
component.ComponentAlreadyRegistered, component.ComponentAlreadyRegistered,
TestComponent, ComponentTester,
"test_register_exception_c1") "test_register_exception_c1")
def test_stop_component(self): def test_stop_component(self):
@ -134,7 +134,7 @@ class ComponentTestClass(BaseTestCase):
self.assertEquals(c._component_state, "Started") self.assertEquals(c._component_state, "Started")
return component.stop(["test_stop_component_c1"]).addCallback(on_stop, c) return component.stop(["test_stop_component_c1"]).addCallback(on_stop, c)
c = TestComponentUpdate("test_stop_component_c1") c = ComponentTesterUpdate("test_stop_component_c1")
d = component.start(["test_stop_component_c1"]) d = component.start(["test_stop_component_c1"])
d.addCallback(on_start, c) d.addCallback(on_start, c)
return d return d
@ -162,7 +162,7 @@ class ComponentTestClass(BaseTestCase):
self.assertNotEqual(c1.counter, counter) self.assertNotEqual(c1.counter, counter)
return component.stop() return component.stop()
c1 = TestComponentUpdate("test_update_c1") c1 = ComponentTesterUpdate("test_update_c1")
cnt = int(c1.counter) cnt = int(c1.counter)
d = component.start(["test_update_c1"]) d = component.start(["test_update_c1"])
@ -182,7 +182,7 @@ class ComponentTestClass(BaseTestCase):
d.addCallback(on_pause, c1, counter) d.addCallback(on_pause, c1, counter)
return d return d
c1 = TestComponentUpdate("test_pause_c1") c1 = ComponentTesterUpdate("test_pause_c1")
cnt = int(c1.counter) cnt = int(c1.counter)
d = component.start(["test_pause_c1"]) d = component.start(["test_pause_c1"])
@ -200,7 +200,7 @@ class ComponentTestClass(BaseTestCase):
d.addCallback(on_shutdown, c1) d.addCallback(on_shutdown, c1)
return d return d
c1 = TestComponentShutdown("test_shutdown_c1") c1 = ComponentTesterShutdown("test_shutdown_c1")
d = component.start(["test_shutdown_c1"]) d = component.start(["test_shutdown_c1"])
d.addCallback(on_start, c1) d.addCallback(on_start, c1)
return d return d

View File

@ -1,7 +1,7 @@
import os import os
import warnings
from hashlib import sha1 as sha from hashlib import sha1 as sha
import pytest
from twisted.internet import reactor from twisted.internet import reactor
from twisted.internet.error import CannotListenError from twisted.internet.error import CannotListenError
from twisted.python.failure import Failure from twisted.python.failure import Failure
@ -19,13 +19,12 @@ from deluge.ui.web.common import compress
from . import common from . import common
from .basetest import BaseTestCase from .basetest import BaseTestCase
warnings.filterwarnings("ignore", category=RuntimeWarning) common.disable_new_release_check()
warnings.resetwarnings()
rpath = common.rpath rpath = common.rpath
class TestCookieResource(Resource): class CookieResource(Resource):
def render(self, request): def render(self, request):
if request.getCookie("password") != "deluge": if request.getCookie("password") != "deluge":
@ -36,7 +35,7 @@ class TestCookieResource(Resource):
return open(rpath("ubuntu-9.04-desktop-i386.iso.torrent")).read() return open(rpath("ubuntu-9.04-desktop-i386.iso.torrent")).read()
class TestPartialDownload(Resource): class PartialDownload(Resource):
def render(self, request): def render(self, request):
data = open(rpath("ubuntu-9.04-desktop-i386.iso.torrent")).read() data = open(rpath("ubuntu-9.04-desktop-i386.iso.torrent")).read()
@ -47,7 +46,7 @@ class TestPartialDownload(Resource):
return data return data
class TestRedirectResource(Resource): class RedirectResource(Resource):
def render(self, request): def render(self, request):
request.redirect("/ubuntu-9.04-desktop-i386.iso.torrent") request.redirect("/ubuntu-9.04-desktop-i386.iso.torrent")
@ -60,9 +59,9 @@ class TopLevelResource(Resource):
def __init__(self): def __init__(self):
Resource.__init__(self) Resource.__init__(self)
self.putChild("cookie", TestCookieResource()) self.putChild("cookie", CookieResource())
self.putChild("partial", TestPartialDownload()) self.putChild("partial", PartialDownload())
self.putChild("redirect", TestRedirectResource()) self.putChild("redirect", RedirectResource())
self.putChild("ubuntu-9.04-desktop-i386.iso.torrent", self.putChild("ubuntu-9.04-desktop-i386.iso.torrent",
File(common.rpath("ubuntu-9.04-desktop-i386.iso.torrent"))) File(common.rpath("ubuntu-9.04-desktop-i386.iso.torrent")))
@ -196,6 +195,7 @@ class CoreTestCase(BaseTestCase):
self.assertTrue(space >= 0) self.assertTrue(space >= 0)
self.assertEquals(self.core.get_free_space("/someinvalidpath"), -1) self.assertEquals(self.core.get_free_space("/someinvalidpath"), -1)
@pytest.mark.slow
def test_test_listen_port(self): def test_test_listen_port(self):
d = self.core.test_listen_port() d = self.core.test_listen_port()

View File

@ -1,5 +1,4 @@
import tempfile import tempfile
import warnings
from email.utils import formatdate from email.utils import formatdate
from twisted.internet import reactor from twisted.internet import reactor
@ -22,10 +21,6 @@ except ImportError:
from twisted.web.error import Resource from twisted.web.error import Resource
warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.resetwarnings()
rpath = common.rpath rpath = common.rpath
temp_dir = tempfile.mkdtemp() temp_dir = tempfile.mkdtemp()
@ -34,13 +29,13 @@ def fname(name):
return "%s/%s" % (temp_dir, name) return "%s/%s" % (temp_dir, name)
class TestRedirectResource(Resource): class RedirectResource(Resource):
def render(self, request): def render(self, request):
request.redirect("http://localhost:51242/") request.redirect("http://localhost:51242/")
class TestRenameResource(Resource): class RenameResource(Resource):
def render(self, request): def render(self, request):
filename = request.args.get("filename", ["renamed_file"])[0] filename = request.args.get("filename", ["renamed_file"])[0]
@ -50,7 +45,7 @@ class TestRenameResource(Resource):
return "This file should be called " + filename return "This file should be called " + filename
class TestCookieResource(Resource): class CookieResource(Resource):
def render(self, request): def render(self, request):
request.setHeader("Content-Type", "text/plain") request.setHeader("Content-Type", "text/plain")
@ -63,7 +58,7 @@ class TestCookieResource(Resource):
return request.getCookie("password") return request.getCookie("password")
class TestGzipResource(Resource): class GzipResource(Resource):
def render(self, request): def render(self, request):
message = request.args.get("msg", ["EFFICIENCY!"])[0] message = request.args.get("msg", ["EFFICIENCY!"])[0]
@ -77,10 +72,10 @@ class TopLevelResource(Resource):
def __init__(self): def __init__(self):
Resource.__init__(self) Resource.__init__(self)
self.putChild("cookie", TestCookieResource()) self.putChild("cookie", CookieResource())
self.putChild("gzip", TestGzipResource()) self.putChild("gzip", GzipResource())
self.putChild("redirect", TestRedirectResource()) self.putChild("redirect", RedirectResource())
self.putChild("rename", TestRenameResource()) self.putChild("rename", RenameResource())
def getChild(self, path, request): # NOQA def getChild(self, path, request): # NOQA
if path == "": if path == "":
@ -97,7 +92,7 @@ class TopLevelResource(Resource):
class DownloadFileTestCase(unittest.TestCase): class DownloadFileTestCase(unittest.TestCase):
def setUp(self): # NOQA def setUp(self): # NOQA
setup_logger("warning", "log_file") setup_logger("warning", fname("log_file"))
self.website = Site(TopLevelResource()) self.website = Site(TopLevelResource())
self.listen_port = 51242 self.listen_port = 51242
tries = 10 tries = 10

View File

@ -16,8 +16,6 @@ class LogTestCase(unittest.TestCase):
def test_old_log_deprecation_warning(self): def test_old_log_deprecation_warning(self):
import warnings import warnings
from deluge.log import LOG from deluge.log import LOG
warnings.filterwarnings("ignore", category=DeprecationWarning,
module="deluge.tests.test_log")
d = defer.Deferred() d = defer.Deferred()
d.addCallback(LOG.debug, "foo") d.addCallback(LOG.debug, "foo")
self.assertFailure(d, DeprecationWarning) self.assertFailure(d, DeprecationWarning)

View File

@ -0,0 +1,35 @@
#! /usr/bin/env python
from twisted.plugin import IPlugin
from twisted.trial.itrial import IReporter
from twisted.trial.reporter import TreeReporter
from zope.interface import implements
class _Reporter(object):
implements(IPlugin, IReporter)
def __init__(self, name, module, description, longOpt, shortOpt, klass): # NOQA
self.name = name
self.module = module
self.description = description
self.longOpt = longOpt
self.shortOpt = shortOpt
self.klass = klass
deluge = _Reporter("Deluge reporter that suppresses Stacktrace from TODO tests",
"twisted.plugins.delugereporter",
description="Deluge Reporter",
longOpt="deluge-reporter",
shortOpt=None,
klass="DelugeReporter")
class DelugeReporter(TreeReporter):
def __init__(self, *args, **kwargs):
TreeReporter.__init__(self, *args, **kwargs)
def addExpectedFailure(self, *args): # NOQA
# super(TreeReporter, self).addExpectedFailure(*args)
self.endLine('[TODO]', self.TODO)

View File

@ -18,5 +18,5 @@ frameworks = CoreFoundation, Foundation, AppKit
known_standard_library=unicodedata known_standard_library=unicodedata
line_length=120 line_length=120
skip=gtkui.py skip=gtkui.py
known_third_party=pygtk,gtk,gobject,gtk.gdk,pango,cairo,pangocairo known_third_party=pygtk,gtk,gobject,gtk.gdk,pango,cairo,pangocairo,twisted
order_by_type=true order_by_type=true

65
tox.ini
View File

@ -10,11 +10,11 @@ ignore = E133
exclude = .tox,.git,dist,build exclude = .tox,.git,dist,build
[tox] [tox]
envlist = py27, py26, flake8, isort, docs envlist = py27, flake8, isort, docs
minversion=1.8 minversion=1.8
[testenv] [testenv]
setenv = PYTHONPATH = {env:PYTHONPATH}:{env:PWD} setenv = PYTHONPATH = {env:PWD}:
sitepackages = True sitepackages = True
deps = deps =
twisted twisted
@ -34,20 +34,10 @@ pep8maxlinelength = 120
whitelist_externals= {[testenv]whitelist_externals} whitelist_externals= {[testenv]whitelist_externals}
commands = py.test deluge commands = py.test deluge
[testenv:testcoverage]
install_command = pip install {opts} {packages} ##############
deps = # Unit tests
{[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] [testenv:pydef]
commands = commands =
@ -79,6 +69,11 @@ commands = {[testenv:pydef]commands}
basepython = python2.7 basepython = python2.7
commands = {[testenv:pydef]commands} commands = {[testenv:pydef]commands}
###########################
# Code style verification
###########################
[testenv:isort] [testenv:isort]
deps = deps =
{[testenv]deps} {[testenv]deps}
@ -110,6 +105,44 @@ deps =
mccabe mccabe
commands = flake8 --exit-zero --max-complexity 15 deluge commands = flake8 --exit-zero --max-complexity 15 deluge
######################
# Unit Test coverage
######################
[testcoveragebase]
#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 "not (todo or gtkui)" deluge/tests/
[testenv:testcoverage]
deps = {[testcoveragebase]deps}
setenv = {[testenv]setenv}
whitelist_externals = {[testcoveragebase]whitelist_externals}
commands =
{[testcoveragebase]commands}
coverage report
[testenv:testcoverage-html]
deps = {[testcoveragebase]deps}
setenv = {[testenv]setenv}
whitelist_externals = {[testcoveragebase]whitelist_externals}
commands =
{[testcoveragebase]commands}
coverage html -d docs/build/htmlcoverage
######################
# Docs generation
######################
# We do not have all dependencies on RTD and travis so we exclude the # We do not have all dependencies on RTD and travis so we exclude the
# site packages (sitepackages=False) when building docs so that local # site packages (sitepackages=False) when building docs so that local
# tests have a similar environment. # tests have a similar environment.