File: conftest.py

package info (click to toggle)
streamlink 8.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,564 kB
  • sloc: python: 51,188; sh: 184; makefile: 152
file content (147 lines) | stat: -rw-r--r-- 4,505 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
from __future__ import annotations

import os
import sys
from functools import partial
from typing import TYPE_CHECKING, Any

import pytest
import requests_mock as rm

from streamlink.session import Streamlink

# noinspection PyProtectedMember
from streamlink.utils.thread import _threadname_counters  # noqa: PLC2701


if TYPE_CHECKING:
    from collections.abc import Callable, Mapping


_TEST_CONDITION_MARKERS: Mapping[str, tuple[bool, str] | Callable[[Any], tuple[bool, str]]] = {
    "posix_only": (os.name == "posix", "only applicable on a POSIX OS"),
    "windows_only": (os.name == "nt", "only applicable on Windows"),
    "python": lambda *ver, **_: (  # pragma: no cover
        sys.version_info >= ver,
        f"only applicable on Python {'.'.join(str(v) for v in ver)} and above",
    ),
}

_TEST_PRIORITIES = (
    "build_backend/",
    "tests/testutils/",
    "tests/utils/",
    "tests/session/",
    None,
    "tests/stream/",
    "tests/test_plugins.py",
    "tests/plugins/",
    "tests/cli/",
)


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", "python(version): tests which are only applicable on specific Python versions")
    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 = {
        item: next(
            (
                idx
                for idx, string in enumerate(_TEST_PRIORITIES)
                if string is not None and item.nodeid.startswith(string)
            ),
            default,
        )
        for item in items
    }  # fmt: skip
    items.sort(key=lambda item: priorities.get(item, default))


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
        data = _TEST_CONDITION_MARKERS[m.name]
        kwargs = dict(m.kwargs)
        reason = kwargs.pop("reason", None)
        if callable(data):
            cond, msg = data(*m.args, **kwargs)
        else:
            cond, msg = data
        if not cond:
            pytest.skip(msg if not reason else f"{msg} ({reason})")


# ========================
# globally shared fixtures
# ========================


@pytest.fixture()
def session(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch):
    options = getattr(request, "param", {})
    plugins_builtin = options.pop("plugins-builtin", False)
    plugins_lazy = options.pop("plugins-lazy", False)

    session = Streamlink(
        options=options,
        plugins_builtin=plugins_builtin,
        plugins_lazy=plugins_lazy,
    )

    try:
        yield session
    finally:
        Streamlink.resolve_url.cache_clear()


@pytest.fixture()
def requests_mock(requests_mock: rm.Mocker) -> rm.Mocker:
    """
    Override of the default `requests_mock` fixture, with `InvalidRequest` raised on unknown requests
    """
    requests_mock.register_uri(rm.ANY, rm.ANY, exc=rm.exceptions.InvalidRequest)
    return requests_mock


@pytest.fixture()
def os_environ(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch) -> dict[str, str]:
    class FakeEnviron(dict):
        def __setitem__(self, key, value):
            if key == "PYTEST_CURRENT_TEST":
                return
            return super().__setitem__(key, value)

    fakeenviron = FakeEnviron(getattr(request, "param", {}))
    monkeypatch.setattr("os.environ", fakeenviron)

    return fakeenviron


@pytest.fixture(autouse=True, scope="session")
def _patch_trio_run():
    import trio  # noqa: PLC0415

    trio_run = trio.run
    # `strict_exception_groups` changed from False to True in `trio==0.25`:
    # Patch `trio.run()` and make older versions of trio behave like `trio>=0.25`
    # as pytest-trio doesn't allow setting custom `trio.run()` args/kwargs
    trio.run = partial(trio.run, strict_exception_groups=True)
    yield
    trio.run = trio_run


@pytest.fixture(autouse=True)
def _clear_threadname_counters():
    yield
    _threadname_counters.clear()