Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix / improve tests #630

Merged
merged 12 commits into from
Jun 21, 2024
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ include pyftpdlib/prefork.py
include pyftpdlib/servers.py
include pyftpdlib/test/README
include pyftpdlib/test/__init__.py
include pyftpdlib/test/conftest.py
include pyftpdlib/test/keycert.pem
include pyftpdlib/test/test_authorizers.py
include pyftpdlib/test/test_cli.py
include pyftpdlib/test/test_filesystems.py
include pyftpdlib/test/test_functional.py
include pyftpdlib/test/test_functional_ssl.py
include pyftpdlib/test/test_ioloop.py
include pyftpdlib/test/test_misc.py
include pyftpdlib/test/test_servers.py
include pyproject.toml
include scripts/ftpbench
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ifeq ($(PYVER), 2)
ipaddress \
mock \
psutil \
pytest \
pyopenssl \
pysendfile \
setuptools
Expand Down Expand Up @@ -138,9 +139,9 @@ test-ioloop: ## Run IOLoop tests.
${MAKE} install
$(TEST_PREFIX) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) pyftpdlib/test/test_ioloop.py

test-misc: ## Run miscellaneous tests.
test-cli: ## Run miscellaneous tests.
${MAKE} install
$(TEST_PREFIX) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) pyftpdlib/test/test_misc.py
$(TEST_PREFIX) $(PYTHON) -m pytest $(PYTEST_ARGS) $(ARGS) pyftpdlib/test/test_cli.py

test-lastfailed: ## Run previously failed tests
${MAKE} install
Expand Down
17 changes: 0 additions & 17 deletions pyftpdlib/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,6 @@
class PyftpdlibTestCase(unittest.TestCase):
"""All test classes inherit from this one."""

def setUp(self):
self._test_ctx = {}
self._test_ctx["threads"] = set(threading.enumerate())

def tearDown(self):
if not hasattr(self, "_test_ctx"):
raise AssertionError(
"super().setUp() was not called for this test class"
)
threads = set(threading.enumerate())
if len(threads) > len(self._test_ctx["threads"]):
extra = threads - self._test_ctx["threads"]
raise AssertionError(
"%s orphaned thread(s) were left behind: %r"
% (len(extra), extra)
)

def __str__(self):
# Print a full path representation of the single unit tests
# being run.
Expand Down
67 changes: 67 additions & 0 deletions pyftpdlib/test/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright (C) 2007 Giampaolo Rodola' <[email protected]>.
# Use of this source code is governed by MIT license that can be
# found in the LICENSE file.

"""
pytest config file (file name has special meaning), executed before
running tests.

In here we tell pytest to execute setup/teardown functions before/after
each unit-test. We do so to make sure no orphaned resources are left
behind.

In unittest terms, this is equivalent to implicitly defining setUp(),
tearDown(), setUpClass(), tearDownClass() methods for each test class.
"""

import threading
import warnings

import pytest


def collect_resources():
# Note: files and sockets are already collected by pytest, so no
# need to use psutil for it.
res = {}
res["threads"] = set(threading.enumerate())
return res


def setup(origin):
ctx = collect_resources()
ctx["_origin"] = origin
return ctx


def warn(msg):
warnings.warn(msg, ResourceWarning, stacklevel=3)


def assert_closed_resources(setup_ctx, request):
if request.session.testsfailed:
return # no need to warn if test already failed

before = setup_ctx.copy()
after = collect_resources()
for key, value in before.items():
if key.startswith("_"):
continue
msg = "%r left some unclosed %r resources behind: " % (
setup_ctx['_origin'],
key,
)
extra = after[key] - before[key]
if extra:
if isinstance(value, set):
msg += repr(extra)
warn(msg)
elif extra > 0: # unused, here just in case we extend it later
msg += "before=%r, after=%r" % (before[key], after[key])
warn(msg)


@pytest.fixture(autouse=True, scope="function")
def for_each_test_method(request):
ctx = setup(request.node.nodeid)
request.addfinalizer(lambda: assert_closed_resources(ctx, request))
9 changes: 4 additions & 5 deletions pyftpdlib/test/test_authorizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import random
import string
import sys
import unittest
import warnings

import pytest
Expand Down Expand Up @@ -484,9 +483,9 @@ def test_override_user_errors(self):
# =====================================================================


@unittest.skipUnless(POSIX, "UNIX only")
@unittest.skipUnless(
UnixAuthorizer is not None, "UnixAuthorizer class not available"
@pytest.mark.skipif(not POSIX, reason="UNIX only")
@pytest.mark.skipif(
UnixAuthorizer is None, reason="UnixAuthorizer class not available"
)
class TestUnixAuthorizer(_SharedAuthorizerTests, PyftpdlibTestCase):
"""Unix authorizer specific tests."""
Expand Down Expand Up @@ -614,7 +613,7 @@ def test_not_root(self):
# =====================================================================


@unittest.skipUnless(WINDOWS, "Windows only")
@pytest.mark.skipif(not WINDOWS, reason="Windows only")
class TestWindowsAuthorizer(_SharedAuthorizerTests, PyftpdlibTestCase):
"""Windows authorizer specific tests."""

Expand Down
12 changes: 8 additions & 4 deletions pyftpdlib/test/test_misc.py → pyftpdlib/test/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.servers import FTPServer
from pyftpdlib.test import PyftpdlibTestCase
from pyftpdlib.test import mock


class TestCommandLineParser(PyftpdlibTestCase):
Expand All @@ -36,7 +37,6 @@ class DummyFTPServer(FTPServer):

def serve_forever(self, *args, **kwargs):
self.close_all()
return

if PY3:
import io
Expand All @@ -45,10 +45,12 @@ def serve_forever(self, *args, **kwargs):
else:
self.devnull = BytesIO()
self.original_ftpserver_class = FTPServer
self.clog = mock.patch("pyftpdlib.__main__.config_logging")
self.clog.start()
pyftpdlib.__main__.FTPServer = DummyFTPServer

def tearDown(self):
self.devnull.close()
self.clog.stop()
pyftpdlib.servers.FTPServer = self.original_ftpserver_class
super().tearDown()

Expand Down Expand Up @@ -77,6 +79,7 @@ def test_write_opt(self):
main(["-w", "-p", "0"])

with warnings.catch_warnings():
warnings.filterwarnings("ignore")
ftpd = main(["-w", "-p", "0"])
perms = ftpd.handler.authorizer.get_perms("anonymous")
assert (
Expand All @@ -85,8 +88,9 @@ def test_write_opt(self):
)

# unexpected argument
with pytest.raises(SystemExit):
main(["-w", "foo", "-p", "0"])
with warnings.catch_warnings():
with pytest.raises(SystemExit):
main(["-w", "foo", "-p", "0"])

def test_directory_opt(self):
dirname = self.get_testfn()
Expand Down
5 changes: 3 additions & 2 deletions pyftpdlib/test/test_filesystems.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

import os
import tempfile
import unittest

import pytest

from pyftpdlib._compat import getcwdu
from pyftpdlib._compat import u
Expand Down Expand Up @@ -194,7 +195,7 @@ def test_validpath_external_symlink(self):
safe_rmpath(testfn)


@unittest.skipUnless(POSIX, "UNIX only")
@pytest.mark.skipif(not POSIX, reason="UNIX only")
class TestUnixFilesystem(PyftpdlibTestCase):

def test_case(self):
Expand Down
Loading
Loading