tests: add custom markers for conditional tests

- Remove `posix_only`/`windows_only` `skipIf` marker aliases
- Add `posix_only`/`windows_only` custom markers based on generic
  conditional test markers in the root conftest module
This commit is contained in:
bastimeyer 2023-03-09 15:26:54 +01:00 committed by Sebastian Meyer
parent 5ff3b32ce5
commit 8dae1eea72
11 changed files with 69 additions and 59 deletions

View File

@ -1,4 +1,3 @@
import os
import signal
import freezegun.config
@ -23,10 +22,3 @@ freezegun.config.configure(extend_ignore_list=["_pytest.runner", "_pytest.termin
# make pytest rewrite assertions in dynamically parametrized plugin tests
# https://docs.pytest.org/en/stable/how-to/writing_plugins.html#assertion-rewriting
pytest.register_assert_rewrite("tests.plugins")
windows_only = pytest.mark.skipif(os.name != "nt", reason="test only applicable on Windows")
posix_only = pytest.mark.skipif(os.name != "posix", reason="test only applicable on a POSIX OS")
__all__ = ["windows_only", "posix_only"]

View File

@ -8,7 +8,6 @@ from unittest.mock import Mock, call, patch
import pytest
from streamlink_cli.output import FileOutput, PlayerOutput
from tests import posix_only, windows_only
@patch("streamlink_cli.output.stdout")
@ -70,12 +69,12 @@ class TestFileOutput(unittest.TestCase):
return mock_path
@posix_only
@pytest.mark.posix_only()
@patch("builtins.open")
def test_open_posix(self, mock_open: Mock, mock_stdout: Mock):
self._test_open(mock_open, mock_stdout)
@windows_only
@pytest.mark.windows_only()
@patch("streamlink_cli.output.msvcrt")
@patch("builtins.open")
def test_open_windows(self, mock_open: Mock, mock_msvcrt: Mock, mock_stdout: Mock):

View File

@ -4,7 +4,6 @@ from unittest.mock import Mock, call, patch
import pytest
from streamlink_cli.output import PlayerOutput
from tests import posix_only, windows_only
@pytest.fixture()
@ -28,25 +27,25 @@ def mock_popen(playeroutput: PlayerOutput):
pytest.param(
dict(cmd="mpv", title="foo bar"),
["mpv", "--force-media-title=foo bar", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="MPV title POSIX",
),
pytest.param(
{"cmd": "mpv.exe", "title": "foo bar"},
"mpv.exe \"--force-media-title=foo bar\" -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="MPV title Windows",
),
pytest.param(
{"cmd": "vlc", "title": "foo bar"},
["vlc", "--input-title-format", "foo bar", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="VLC title POSIX",
),
pytest.param(
{"cmd": "vlc.exe", "title": "foo bar"},
"vlc.exe --input-title-format \"foo bar\" -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="VLC title Windows",
),
], indirect=["playeroutput"])
@ -61,73 +60,73 @@ def test_playeroutput(mock_popen: Mock, playeroutput: PlayerOutput, expected):
pytest.param(
dict(cmd="foo"),
["foo", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="None POSIX",
),
pytest.param(
dict(cmd="foo", args="--bar"),
["foo", "--bar", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="Implicit POSIX",
),
pytest.param(
dict(cmd="foo", args="--bar {playerinput}"),
["foo", "--bar", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="Explicit POSIX",
),
pytest.param(
dict(cmd="foo", args="--bar {filename}"),
["foo", "--bar", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="Fallback POSIX",
),
pytest.param(
dict(cmd="foo", args="--bar {playerinput} {filename}"),
["foo", "--bar", "-", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="Fallback duplicate POSIX",
),
pytest.param(
dict(cmd="foo", args="--bar {qux}"),
["foo", "--bar", "{qux}", "-"],
marks=posix_only,
marks=pytest.mark.posix_only,
id="Unknown POSIX",
),
pytest.param(
dict(cmd="foo"),
"foo -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="None Windows",
),
pytest.param(
dict(cmd="foo", args="--bar"),
"foo --bar -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="Implicit Windows",
),
pytest.param(
dict(cmd="foo", args="--bar {playerinput}"),
"foo --bar -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="Explicit Windows",
),
pytest.param(
dict(cmd="foo", args="--bar {filename}"),
"foo --bar -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="Fallback Windows",
),
pytest.param(
dict(cmd="foo", args="--bar {playerinput} {filename}"),
"foo --bar - -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="Fallback duplicate Windows",
),
pytest.param(
dict(cmd="foo", args="--bar {qux}"),
"foo --bar {qux} -",
marks=windows_only,
marks=pytest.mark.windows_only,
id="Unknown Windows",
),
], indirect=["playeroutput"])

View File

@ -2,10 +2,11 @@ import unittest
from pathlib import Path
from unittest.mock import ANY, Mock, call, patch
import pytest
import streamlink_cli.main
import tests
from streamlink import Streamlink
from tests import posix_only, windows_only
class CommandLineTestCase(unittest.TestCase):
@ -51,7 +52,7 @@ class CommandLineTestCase(unittest.TestCase):
assert mock_call.call_args_list == [call(commandline, stderr=ANY, stdout=ANY)]
@posix_only
@pytest.mark.posix_only()
class TestCommandLinePOSIX(CommandLineTestCase):
"""
Commandline tests under POSIX-like operating systems
@ -89,7 +90,7 @@ class TestCommandLinePOSIX(CommandLineTestCase):
["/usr/bin/player", "-v", "-"])
@windows_only
@pytest.mark.windows_only()
class TestCommandLineWindows(CommandLineTestCase):
"""
Commandline tests for Windows

View File

@ -1,10 +1,11 @@
from unittest.mock import Mock, patch
from tests import posix_only, windows_only
import pytest
from tests.cli.test_cmdline import CommandLineTestCase
@posix_only
@pytest.mark.posix_only()
@patch("streamlink_cli.main.NamedPipe", Mock(return_value=Mock(path="/tmp/streamlinkpipe")))
class TestCommandLineWithPlayerFifoPosix(CommandLineTestCase):
def test_player_fifo_default(self):
@ -16,7 +17,7 @@ class TestCommandLineWithPlayerFifoPosix(CommandLineTestCase):
)
@windows_only
@pytest.mark.windows_only()
@patch("streamlink_cli.main.NamedPipe", Mock(return_value=Mock(path="\\\\.\\pipe\\streamlinkpipe")))
class TestCommandLineWithPlayerFifoWindows(CommandLineTestCase):
def test_player_fifo_default(self):

View File

@ -1,8 +1,9 @@
from tests import posix_only, windows_only
import pytest
from tests.cli.test_cmdline import CommandLineTestCase
@posix_only
@pytest.mark.posix_only()
class TestCommandLineWithTitlePOSIX(CommandLineTestCase):
def test_open_player_with_title_vlc(self):
self._test_args(["streamlink", "-p", "/usr/bin/vlc",
@ -26,7 +27,7 @@ class TestCommandLineWithTitlePOSIX(CommandLineTestCase):
["mpv", "--force-media-title=★ ★ ★", "-"])
@windows_only
@pytest.mark.windows_only()
class TestCommandLineWithTitleWindows(CommandLineTestCase):
def test_open_player_with_title_vlc(self):
self._test_args(

View File

@ -28,7 +28,6 @@ from streamlink_cli.main import (
resolve_stream_name,
)
from streamlink_cli.output import FileOutput, PlayerOutput
from tests import posix_only, windows_only
from tests.plugin.testplugin import TestPlugin as _TestPlugin
@ -637,7 +636,7 @@ class TestCLIMainLoggingStreams(_TestCLIMainLogging):
class TestCLIMainLoggingInfos(_TestCLIMainLogging):
@posix_only
@pytest.mark.posix_only()
@patch("streamlink_cli.main.log")
def test_log_root_warning(self, mock_log):
self.subject(["streamlink"], euid=0)
@ -774,7 +773,7 @@ class TestCLIMainLoggingLogfile(_TestCLIMainLogging):
)
@posix_only
@pytest.mark.posix_only()
class TestCLIMainLoggingLogfilePosix(_TestCLIMainLogging):
@patch("sys.stdout")
@patch("builtins.open")
@ -813,7 +812,7 @@ class TestCLIMainLoggingLogfilePosix(_TestCLIMainLogging):
)
@windows_only
@pytest.mark.windows_only()
class TestCLIMainLoggingLogfileWindows(_TestCLIMainLogging):
@patch("sys.stdout")
@patch("builtins.open")

View File

@ -5,7 +5,6 @@ from unittest.mock import patch
import pytest
from streamlink_cli.utils.path import replace_chars, replace_path
from tests import posix_only, windows_only
@pytest.mark.parametrize("char", list(range(32)))
@ -13,29 +12,29 @@ def test_replace_chars_unprintable(char: int):
assert replace_chars(f"foo{chr(char)}{chr(char)}bar") == "foo_bar", "Replaces unprintable characters"
@posix_only
@pytest.mark.posix_only()
@pytest.mark.parametrize("char", "/".split())
def test_replace_chars_posix(char: str):
assert replace_chars(f"foo{char}{char}bar") == "foo_bar", "Replaces multiple unsupported characters in a row"
@windows_only
@pytest.mark.windows_only()
@pytest.mark.parametrize("char", "\x7f\"*/:<>?\\|".split())
def test_replace_chars_windows(char: str):
assert replace_chars(f"foo{char}{char}bar") == "foo_bar", "Replaces multiple unsupported characters in a row"
@posix_only
@pytest.mark.posix_only()
def test_replace_chars_posix_all():
assert replace_chars("".join(chr(i) for i in range(32)) + "/") == "_"
@windows_only
@pytest.mark.windows_only()
def test_replace_chars_windows_all():
assert replace_chars("".join(chr(i) for i in range(32)) + "\x7f\"*/:<>?\\|") == "_"
@posix_only
@pytest.mark.posix_only()
def test_replace_chars_posix_override():
all_chars = "".join(chr(i) for i in range(32)) + "\x7f\"*:/<>?\\|"
assert replace_chars(all_chars) == "_\x7f\"*:_<>?\\|"
@ -45,7 +44,7 @@ def test_replace_chars_posix_override():
assert replace_chars(all_chars, "win32") == "_"
@windows_only
@pytest.mark.windows_only()
def test_replace_chars_windows_override():
all_chars = "".join(chr(i) for i in range(32)) + "\x7f\"*:/<>?\\|"
assert replace_chars(all_chars) == "_"
@ -68,14 +67,14 @@ def test_replace_path():
assert replace_path(path, mapper) == expected, "Only replaces mapped parts which are in the special parts tuple"
@posix_only
@pytest.mark.posix_only()
def test_replace_path_expanduser_posix():
with patch.object(os, "environ", {"HOME": "/home/foo"}):
assert replace_path("~/bar", lambda s: s) == Path("/home/foo/bar")
assert replace_path("foo/bar", lambda s: dict(foo="~").get(s, s)) == Path("~/bar")
@windows_only
@pytest.mark.windows_only()
def test_replace_path_expanduser_windows():
with patch.object(os, "environ", {"USERPROFILE": "C:\\Users\\foo"}):
assert replace_path("~\\bar", lambda s: s) == Path("C:\\Users\\foo\\bar")

View File

@ -7,7 +7,6 @@ import freezegun
import pytest
from streamlink_cli.utils.progress import Progress, ProgressFormatter
from tests import posix_only, windows_only
class TestProgressFormatter:
@ -183,7 +182,7 @@ class TestPrint:
def progress(self, stream: StringIO):
return Progress(stream, Mock())
@posix_only
@pytest.mark.posix_only()
def test_print_posix(self, progress: Progress, stream: StringIO):
progress.print_inplace("foo")
progress.print_inplace("barbaz")
@ -192,7 +191,7 @@ class TestPrint:
progress.print_end()
assert stream.getvalue() == "\rfoo \rbarbaz \r0123456789\rabcdefghijk\n"
@windows_only
@pytest.mark.windows_only()
def test_print_windows(self, progress: Progress, stream: StringIO):
progress.print_inplace("foo")
progress.print_inplace("barbaz")

View File

@ -1,9 +1,15 @@
import os
import sys
from typing import List
from typing import Dict, List, Tuple
import pytest
_TEST_CONDITION_MARKERS: Dict[str, Tuple[bool, str]] = {
"posix_only": (os.name == "posix", "only applicable on a POSIX OS"),
"windows_only": (os.name == "nt", "only applicable on Windows"),
}
_TEST_PRIORITIES = (
"tests/testutils/",
"tests/utils/",
@ -15,6 +21,16 @@ _TEST_PRIORITIES = (
)
def pytest_configure(config: pytest.Config):
config.addinivalue_line("markers", "posix_only: tests which are only applicable on a POSIX OS")
config.addinivalue_line("markers", "windows_only: tests which are only applicable on Windows")
config.addinivalue_line("markers", "nomockedhttprequest: tests where no mocked HTTP request will be made")
def pytest_runtest_setup(item: pytest.Item):
_check_test_condition(item)
def pytest_collection_modifyitems(items: List[pytest.Item]): # pragma: no cover
default = next((idx for idx, string in enumerate(_TEST_PRIORITIES) if string is None), sys.maxsize)
priorities = {
@ -31,5 +47,10 @@ def pytest_collection_modifyitems(items: List[pytest.Item]): # pragma: no cover
items.sort(key=lambda item: priorities.get(item, default))
def pytest_configure(config: pytest.Config):
config.addinivalue_line("markers", "nomockedhttprequest: tests where no mocked HTTP request will be made")
def _check_test_condition(item: pytest.Item): # pragma: no cover
for m in item.iter_markers():
if m.name not in _TEST_CONDITION_MARKERS:
continue
cond, msg = _TEST_CONDITION_MARKERS[m.name]
if not cond:
pytest.skip(msg if not m.args and not m.kwargs else f"{msg} ({m.kwargs.get('reason') or m.args[0]})")

View File

@ -5,7 +5,6 @@ from unittest.mock import Mock, call, patch
import pytest
from streamlink.utils.named_pipe import NamedPipe, NamedPipeBase, NamedPipePosix, NamedPipeWindows
from tests import posix_only, windows_only
try:
@ -78,7 +77,7 @@ class TestNamedPipe(unittest.TestCase):
]
@posix_only
@pytest.mark.posix_only()
class TestNamedPipePosix(unittest.TestCase):
def test_export(self):
assert NamedPipe is NamedPipePosix
@ -121,7 +120,7 @@ class TestNamedPipePosix(unittest.TestCase):
assert not reader.is_alive()
@windows_only
@pytest.mark.windows_only()
class TestNamedPipeWindows(unittest.TestCase):
def test_export(self):
assert NamedPipe is NamedPipeWindows