Skip to content

Commit

Permalink
Tests: replace nose with pytest
Browse files Browse the repository at this point in the history
nose is no longer maintained, see
nose-devs/nose#1099 (comment).

This was initially submitted here: https://bugs.gentoo.org/878695.

This commit switches to pytest.

Amongst the changes, here are the most relevant ones:
* Create a conftest.py that contains fixtures.
* Replace nose.with_setup with yield fixtures.
* Use pytest.skip instead of print and nose.plugins.skip.SkipTest.
* Use pytest.mark.slow instead of nose.plugins.attrib.attr("slow").
* Use pytest.mark.parametrize instead of parameterized, simplify
  parametrized tests that use both `shell` and `inverse_order`.
* Update Travis config.
* Update SConscript.
* Update documentation.
  • Loading branch information
sbraz committed Jan 3, 2023
1 parent 2711b84 commit fad186e
Show file tree
Hide file tree
Showing 51 changed files with 210 additions and 393 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ script:
- scons config
- export RM_TS_PRINT_CMD=1
- export RM_TS_PEDANTIC=0
- sudo -E nosetests -s -v -a '!slow'
- sudo -E pytest -s -v -k 'not slow'
6 changes: 3 additions & 3 deletions docs/developers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,13 @@ Notable targets

:test:

Build the tests (requires ``python`` and ``nosetest`` installed).
Optionally ``valgrind`` can be installed to run the tests through
Build the tests (requires ``python`` and ``pytest`` installed).
Optionally ``valgrind`` can be installed to run the tests through
valgrind:

.. code-block:: bash
$ USE_VALGRIND=1 nosetests # or nosetests-3.3, python3 needed.
$ USE_VALGRIND=1 pytest
:xgettext:

Expand Down
2 changes: 1 addition & 1 deletion docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Here's a list of readily prepared commands for known operating systems:

.. code-block:: bash
$ apt-get install pkg-config git scons python3-sphinx python3-nose gettext build-essential
$ apt-get install pkg-config git scons python3-sphinx python3-pytest gettext build-essential
# Optional dependencies for more features:
$ apt-get install libelf-dev libglib2.0-dev libblkid-dev libjson-glib-1.0 libjson-glib-dev
# Optional dependencies for the GUI:
Expand Down
23 changes: 10 additions & 13 deletions docs/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Testsuite
complete yet (and probably never will), but it's already a valuable boost of
confidence in ``rmlint's`` correctness.

The tests are based on ``nosetest`` and are written in ``python>=3.0``.
The tests are based on ``pytest`` and are written in ``python>=3.0``.
Every testcase just runs the (previously built) ``rmlint`` binary a
and parses its json output. So they are technically blackbox-tests.

Expand Down Expand Up @@ -34,17 +34,17 @@ variables which are:
- ``RM_TS_PRINT_CMD``: Print the command that is currently run.
- ``RM_TS_KEEP_TESTDIR``: If a test failed, keep the test files.

Additionally slow tests can be omitted with by appending ``-a '!slow'`` to
the commandline. More information on this syntax can be found on the `nosetest
Additionally slow tests can be omitted with by appending ``-k 'not slow'`` to
the commandline. More information on this syntax can be found on the `pytest
documentation`_.

.. _`nosetest documentation`: http://nose.readthedocs.org/en/latest/plugins/attrib.html
.. _`pytest documentation`: https://docs.pytest.org/en/stable/example/markers.html

Before each release we call the testsuite (at least) like this:

.. code-block:: bash
$ sudo RM_TS_USE_VALGRIND=1 RM_TS_PRINT_CMD=1 RM_TS_PEDANTIC=1 nosetests-3.4 -s -a '!slow !known_issue'
$ sudo RM_TS_USE_VALGRIND=1 RM_TS_PRINT_CMD=1 RM_TS_PEDANTIC=1 pytest -s -a 'not slow and not known_issue'
The ``sudo`` here is there for executing some tests that need root access (like
the creating of bad user and group ids). Most tests will work without.
Expand All @@ -59,7 +59,7 @@ were executed (and how often) by the testsuite. Here's a short quickstart using
.. code-block:: bash
$ CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs -ftest-coverage" scons -j4 DEBUG=1
$ sudo RM_TS_USE_VALGRIND=1 RM_TS_PRINT_CMD=1 RM_TS_PEDANTIC=1 nosetests-3.4 -s -a '!slow !known_issue'
$ sudo RM_TS_USE_VALGRIND=1 RM_TS_PRINT_CMD=1 RM_TS_PEDANTIC=1 pytest -s -a 'slow and not known_issue'
$ lcov --capture --directory . --output-file coverage.info
$ genhtml coverage.info --output-directory out
Expand All @@ -85,11 +85,9 @@ A template for a testcase looks like this:

.. code-block:: python
from nose import with_setup
from tests.utils import *
@with_setup(usual_setup_func, usual_teardown_func)
def test_basic():
def test_basic(usual_setup_usual_teardown):
create_file('xxx', 'a')
create_file('xxx', 'b')
Expand Down Expand Up @@ -117,11 +115,10 @@ Rules

.. code-block:: python
from nose.plugins.attrib import attr
import pytest
@attr('slow')
@with_setup(usual_setup_func, usual_teardown_func)
def test_debian_support():
@pytest.mark.slow
def test_debian_support(usual_setup_usual_teardown):
assert random.choice([True, False]):
* Unresolved issues can be marked with `known_issue` attribute to avoid failing automated travis testing
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
markers =
slow: slow tests.
3 changes: 1 addition & 2 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
nose==1.3.7
parameterized==0.6.1
xattr==0.9.6
psutil==5.6.6
pytest
8 changes: 4 additions & 4 deletions tests/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ def cmd_exists(cmd):


def run_tests(target=None, source=None, env=None):
names = ['nosetests-3.3', 'nosetests-3', 'python3-nosetests', 'nosetests3', 'nosetests']
names = ["pytest"]
exes = [exe for exe in names if cmd_exists(exe)]
if any(exes):
name = exes[0]
print('Found nosetests as "{}"'.format(name))
Exit(subprocess.call(name + ' -s -v -a !slow', shell=True))
print('Found pytest as "{}"'.format(name))
Exit(subprocess.call(name + ' -s -v -k "not slow"', shell=True))

print('Unable to find nosetests, tried these: ' + str(names))
print('Unable to find pytest, tried these: ' + str(names))
Exit(-1)


Expand Down
21 changes: 21 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pytest

from tests.utils import mount_bind_teardown_func, usual_setup_func, usual_teardown_func

@pytest.fixture(params=["sh", "bash", "dash"])
def shell(request):
yield request.param


@pytest.fixture
def usual_setup_usual_teardown():
usual_setup_func()
yield
usual_teardown_func()


@pytest.fixture
def usual_setup_mount_bind_teardown():
usual_setup_func()
yield
mount_bind_teardown_func()
4 changes: 1 addition & 3 deletions tests/test_formatters/test_csv.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env python3
# encoding: utf-8
from nose import with_setup
from tests.utils import *

import csv
Expand All @@ -10,8 +9,7 @@ def csv_string_to_data(csv_dump):
return data[1:]


@with_setup(usual_setup_func, usual_teardown_func)
def test_simple():
def test_simple(usual_setup_usual_teardown):
create_file('1234', 'a')
create_file('1234', 'b')
create_file('1234', 'stupid\'file,name')
Expand Down
4 changes: 1 addition & 3 deletions tests/test_formatters/test_json.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
#!/usr/bin/env python3
# encoding: utf-8
from nose import with_setup
from tests.utils import *


@with_setup(usual_setup_func, usual_teardown_func)
def test_simple():
def test_simple(usual_setup_usual_teardown):
full_path_a = create_file('x', '\t\r\"\b\f\\')
full_path_b = create_file('x', '\"\t\n2134124')
head, *data, footer = run_rmlint('-S a')
Expand Down
4 changes: 1 addition & 3 deletions tests/test_formatters/test_others.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
#!/usr/bin/env python3
# encoding: utf-8
from nose import with_setup
from tests.utils import *


@with_setup(usual_setup_func, usual_teardown_func)
def test_just_call_it():
def test_just_call_it(usual_setup_usual_teardown):
create_file('1234', 'a')
create_file('1234', 'b')

Expand Down
11 changes: 4 additions & 7 deletions tests/test_formatters/test_py.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#!/usr/bin/env python3
# encoding: utf-8
from nose import with_setup
from tests.utils import *

import subprocess

from parameterized import parameterized
import pytest


def _check_interpreter(interpreter):
Expand All @@ -16,16 +15,14 @@ def _check_interpreter(interpreter):
return False


@parameterized(["python2", "python3"])
@with_setup(usual_setup_func, usual_teardown_func)
def test_paranoia(interpreter):
@pytest.mark.parametrize("interpreter", ["python2", "python3"])
def test_paranoia(usual_setup_usual_teardown, interpreter):
if not _check_interpreter(interpreter):
print(
pytest.skip(
"Interpreter {} does not seem to be working, skipping test".format(
interpreter
)
)
return

create_file('xxx', 'a')
create_file('xxx', 'b')
Expand Down
42 changes: 11 additions & 31 deletions tests/test_formatters/test_sh.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#!/usr/bin/env python3
# encoding: utf-8
from nose import with_setup
from tests.utils import *

import subprocess

from parameterized import parameterized
import pytest


def run_shell_script(shell, sh_path, *args):
Expand All @@ -21,9 +20,7 @@ def filter_part_of_directory(data):
return [e for e in data if e['type'] != 'part_of_directory']


@with_setup(usual_setup_func, usual_teardown_func)
@parameterized([("sh", ), ("bash", ), ("dash", )])
def test_basic(shell):
def test_basic(usual_setup_usual_teardown, shell):
create_file('xxx', 'a')
create_file('xxx', 'b')

Expand Down Expand Up @@ -71,9 +68,7 @@ def test_basic(shell):
assert '/a' in text


@parameterized([("sh", ), ("bash", ), ("dash", )])
@with_setup(usual_setup_func, usual_teardown_func)
def test_paranoia(shell):
def test_paranoia(usual_setup_usual_teardown, shell):
create_file('xxx', 'a')
create_file('xxx', 'b')
create_file('xxx', 'c')
Expand Down Expand Up @@ -124,8 +119,7 @@ def test_paranoia(shell):
assert footer['duplicates'] == 0


@with_setup(usual_setup_func, usual_teardown_func)
def test_anon_pipe():
def test_anon_pipe(usual_setup_usual_teardown):
create_file('xxx', 'long-dummy-file-1')
create_file('xxx', 'long-dummy-file-2')

Expand All @@ -140,9 +134,7 @@ def test_anon_pipe():
assert b'/long-dummy-file-2' in data


@parameterized([("sh", ), ("bash", ), ("dash", )])
@with_setup(usual_setup_func, usual_teardown_func)
def test_hardlink_duplicate_directories(shell):
def test_hardlink_duplicate_directories(usual_setup_usual_teardown, shell):
create_file('xxx', 'dir_a/x')
create_file('xxx', 'dir_b/x')

Expand Down Expand Up @@ -174,12 +166,8 @@ def _check_if_empty_dirs_deleted(shell, inverse_order, sh_path, data):
assert not os.path.exists(data[1]["path"])


@parameterized([
("sh", False), ("bash", False), ("dash", False),
("sh", True), ("bash", True), ("dash", True)
])
@with_setup(usual_setup_func, usual_teardown_func)
def test_remove_empty_dirs(shell, inverse_order):
@pytest.mark.parametrize("inverse_order", [False, True])
def test_remove_empty_dirs(usual_setup_usual_teardown, shell, inverse_order):
create_file('xxx', 'deep/a/b/c/d/e/1')
create_file('xxx', 'deep/x/2')

Expand Down Expand Up @@ -207,12 +195,8 @@ def test_remove_empty_dirs(shell, inverse_order):
_check_if_empty_dirs_deleted(shell, inverse_order, sh_path, data)


@parameterized([
("sh", False), ("bash", False), ("dash", False),
("sh", True), ("bash", True), ("dash", True)
])
@with_setup(usual_setup_func, usual_teardown_func)
def test_remove_empty_dirs_with_dupe_dirs(shell, inverse_order):
@pytest.mark.parametrize("inverse_order", [False, True])
def test_remove_empty_dirs_with_dupe_dirs(usual_setup_usual_teardown, shell, inverse_order):
create_file('xxx', 'deep/a/b/c/d/e/1')
create_file('xxx', 'deep/x/1')

Expand Down Expand Up @@ -240,9 +224,7 @@ def test_remove_empty_dirs_with_dupe_dirs(shell, inverse_order):

_check_if_empty_dirs_deleted(shell, inverse_order, sh_path, data)

@with_setup(usual_setup_func, usual_teardown_func)
@parameterized([("sh", ), ("bash", ), ("dash", )])
def test_cleanup_emptydirs(shell):
def test_cleanup_emptydirs(usual_setup_usual_teardown, shell):
create_file('xxx', 'dir1/a')

# create some ugly dir names
Expand Down Expand Up @@ -271,9 +253,7 @@ def test_cleanup_emptydirs(shell):



@with_setup(usual_setup_func, usual_teardown_func)
@parameterized([("sh", ), ("bash", ), ("dash", )])
def test_keep_parent_timestamps(shell):
def test_keep_parent_timestamps(usual_setup_usual_teardown, shell):
create_file('xxx', 'dir/a')
create_file('xxx', 'dir/b')

Expand Down
Loading

0 comments on commit fad186e

Please sign in to comment.