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

Rework fixtures #50

Merged
merged 16 commits into from
Aug 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ci/rtd-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
sphinx >= 1.6.1
sphinx_rtd_theme
sphinxcontrib-trio
# Workaround for this weird issue:
# https://travis-ci.org/python-trio/pytest-trio/jobs/407495415
attrs >= 17.4.0
3 changes: 3 additions & 0 deletions pytest_trio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""Top-level package for pytest-trio."""

from ._version import __version__
from .plugin import trio_fixture

__all__ = ["trio_fixture"]
15 changes: 15 additions & 0 deletions pytest_trio/_tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest


def enable_trio_mode_via_pytest_ini(testdir):
testdir.makefile(".ini", pytest="[pytest]\ntrio_mode = true\n")


def enable_trio_mode_via_conftest_py(testdir):
testdir.makeconftest("from pytest_trio.enable_trio_mode import *")


enable_trio_mode = pytest.mark.parametrize(
"enable_trio_mode",
[enable_trio_mode_via_pytest_ini, enable_trio_mode_via_conftest_py]
)
20 changes: 12 additions & 8 deletions pytest_trio/_tests/test_async_yield_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,39 +317,43 @@ def test_async_yield_fixture_crashed_teardown_allow_other_teardowns(
import trio
from async_generator import async_generator, yield_

events = []
setup_events = set()
teardown_events = set()

@pytest.fixture
@async_generator
async def good_fixture():
async with trio.open_nursery() as nursery:
events.append('good_fixture setup')
setup_events.add('good_fixture setup')
await yield_(None)
events.append('good_fixture teardown')
teardown_events.add('good_fixture teardown')

@pytest.fixture
@async_generator
async def bad_fixture():
async with trio.open_nursery() as nursery:
events.append('bad_fixture setup')
setup_events.add('bad_fixture setup')
await yield_(None)
events.append('bad_fixture teardown')
teardown_events.add('bad_fixture teardown')
raise RuntimeError('Crash during fixture teardown')

def test_before():
assert not events
assert not setup_events
assert not teardown_events

@pytest.mark.trio
async def test_actual_test(bad_fixture, good_fixture):
pass

def test_after():
assert events == [
assert setup_events == {
'good_fixture setup',
'bad_fixture setup',
}
assert teardown_events == {
'bad_fixture teardown',
'good_fixture teardown',
]
}
"""
)
)
Expand Down
2 changes: 1 addition & 1 deletion pytest_trio/_tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_check_async_test_called():
"""
)

result = testdir.runpytest()
result = testdir.runpytest("-s")

result.assert_outcomes(passed=2)

Expand Down
37 changes: 37 additions & 0 deletions pytest_trio/_tests/test_contextvars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import pytest
from pytest_trio import trio_fixture

import contextvars

cv = contextvars.ContextVar("cv", default=None)


@trio_fixture
def cv_checker():
assert cv.get() is None
yield
assert cv.get() is None


@trio_fixture
def cv_setter(cv_checker):
assert cv.get() is None
token = cv.set("cv_setter")
yield
assert cv.get() == "cv_setter2"
cv.reset(token)
assert cv.get() is None


@trio_fixture
def cv_setter2(cv_setter):
assert cv.get() == "cv_setter"
# Intentionally leak, so can check that this is visible back in cv_setter
cv.set("cv_setter2")
yield
assert cv.get() == "cv_setter2"


@pytest.mark.trio
async def test_contextvars(cv_setter2):
assert cv.get() == "cv_setter2"
119 changes: 119 additions & 0 deletions pytest_trio/_tests/test_fixture_mistakes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import pytest
from pytest_trio import trio_fixture

from .helpers import enable_trio_mode


def test_trio_fixture_with_non_trio_test(testdir):
testdir.makepyfile(
"""
import trio
from pytest_trio import trio_fixture
import pytest

@trio_fixture
def trio_time():
return trio.current_time()

@pytest.fixture
def indirect_trio_time(trio_time):
return trio_time + 1

@pytest.mark.trio
async def test_async(mock_clock, trio_time, indirect_trio_time):
assert trio_time == 0
assert indirect_trio_time == 1

def test_sync(trio_time):
pass

def test_sync_indirect(indirect_trio_time):
pass
"""
)

result = testdir.runpytest()

result.assert_outcomes(passed=1, error=2)
result.stdout.fnmatch_lines(
["*: Trio fixtures can only be used by Trio tests*"]
)


def test_trio_fixture_with_wrong_scope_without_trio_mode(testdir):
# There's a trick here: when you have a non-function-scope fixture, it's
# not instantiated for any particular function (obviously). So... when our
# pytest_fixture_setup hook tries to check for marks, it can't normally
# see @pytest.mark.trio. So... it's actually almost impossible to have an
# async fixture get treated as a Trio fixture *and* have it be
# non-function-scope. But, class-scoped fixtures can see marks on the
# class, so this is one way (the only way?) it can happen:
testdir.makepyfile(
"""
import pytest

@pytest.fixture(scope="class")
async def async_class_fixture():
pass

@pytest.mark.trio
class TestFoo:
async def test_foo(self, async_class_fixture):
pass
"""
)

result = testdir.runpytest()

result.assert_outcomes(error=1)
result.stdout.fnmatch_lines(["*: Trio fixtures must be function-scope*"])


@enable_trio_mode
def test_trio_fixture_with_wrong_scope_in_trio_mode(testdir, enable_trio_mode):
enable_trio_mode(testdir)

testdir.makepyfile(
"""
import pytest

@pytest.fixture(scope="session")
async def async_session_fixture():
pass


async def test_whatever(async_session_fixture):
pass
"""
)

result = testdir.runpytest()

result.assert_outcomes(error=1)
result.stdout.fnmatch_lines(["*: Trio fixtures must be function-scope*"])


@enable_trio_mode
def test_async_fixture_with_sync_test_in_trio_mode(testdir, enable_trio_mode):
enable_trio_mode(testdir)

testdir.makepyfile(
"""
import pytest

@pytest.fixture
async def async_fixture():
pass


def test_whatever(async_fixture):
pass
"""
)

result = testdir.runpytest()

result.assert_outcomes(error=1)
result.stdout.fnmatch_lines(
["*: Trio fixtures can only be used by Trio tests*"]
)
18 changes: 18 additions & 0 deletions pytest_trio/_tests/test_fixture_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest
from pytest_trio import trio_fixture
import trio


@trio_fixture
def fixture_with_unique_name(nursery):
nursery.start_soon(trio.sleep_forever)


@pytest.mark.trio
async def test_fixture_names(fixture_with_unique_name):
# This might be a bit fragile ... if we rearrange the nursery hierarchy
# somehow so it breaks, then we can make it more robust.
task = trio.hazmat.current_task()
assert task.name == "<test 'test_fixture_names'>"
sibling_names = {task.name for task in task.parent_nursery.child_tasks}
assert "<fixture 'fixture_with_unique_name'>" in sibling_names
Loading