Skip to content

Commit

Permalink
fix: only process valid test nodes in report, close #246 (#247)
Browse files Browse the repository at this point in the history
* fix: only process valid test nodes in report, close #246

* chore: ignore build metadata

* fix: ensure unused snapshots processed in deterministic order
  • Loading branch information
Noah committed Jun 2, 2020
1 parent c4e8e5c commit 8ed194c
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ wheels/
*.egg
MANIFEST
version.txt
pip-wheel-metadata

# Installer logs
pip-log.txt
Expand Down
10 changes: 7 additions & 3 deletions src/syrupy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,24 @@ def pytest_sessionstart(session: Any) -> None:
config._syrupy.start()


def pytest_collection_modifyitems(session: Any, config: Any, items: List[Any]) -> None:
def pytest_collection_modifyitems(
session: Any, config: Any, items: List["pytest.Item"]
) -> None:
"""
After tests are collected and before any modification is performed.
https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_collection_modifyitems
"""
config._syrupy._all_items.update(items)
for item in config._syrupy.filter_valid_items(items):
config._syrupy._all_items[item] = True


def pytest_collection_finish(session: Any) -> None:
"""
After collection has been performed and modified.
https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_collection_finish
"""
session.config._syrupy._ran_items.update(session.items)
for item in session.config._syrupy.filter_valid_items(session.items):
session.config._syrupy._ran_items[item] = True


def pytest_sessionfinish(session: Any, exitstatus: int) -> None:
Expand Down
8 changes: 4 additions & 4 deletions src/syrupy/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterator,
List,
Set,
)

import attr
import pytest

from .data import (
Snapshot,
Expand All @@ -36,8 +36,8 @@
@attr.s
class SnapshotReport:
base_dir: str = attr.ib()
all_items: Set[Any] = attr.ib()
ran_items: Set[Any] = attr.ib()
all_items: Dict["pytest.Item", bool] = attr.ib()
ran_items: Dict["pytest.Item", bool] = attr.ib()
update_snapshots: bool = attr.ib()
is_providing_paths: bool = attr.ib()
is_providing_nodes: bool = attr.ib()
Expand Down
16 changes: 10 additions & 6 deletions src/syrupy/session.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
List,
Optional,
Set,
)

import attr
import pytest

from .constants import EXIT_STATUS_FAIL_UNUSED
from .data import SnapshotFossils
Expand All @@ -28,15 +28,15 @@ class SnapshotSession:
is_providing_paths: bool = attr.ib()
is_providing_nodes: bool = attr.ib()
report: Optional["SnapshotReport"] = attr.ib(default=None)
_all_items: Set[Any] = attr.ib(factory=set)
_ran_items: Set[Any] = attr.ib(factory=set)
_all_items: Dict["pytest.Item", bool] = attr.ib(factory=dict)
_ran_items: Dict["pytest.Item", bool] = attr.ib(factory=dict)
_assertions: List["SnapshotAssertion"] = attr.ib(factory=list)
_extensions: Dict[str, "AbstractSyrupyExtension"] = attr.ib(factory=dict)

def start(self) -> None:
self.report = None
self._all_items = set()
self._ran_items = set()
self._all_items = {}
self._ran_items = {}
self._assertions = []
self._extensions = {}

Expand Down Expand Up @@ -89,3 +89,7 @@ def remove_unused_snapshots(
)
elif snapshot_location not in used_snapshot_fossils:
Path(snapshot_location).unlink()

@staticmethod
def filter_valid_items(items: List["pytest.Item"]) -> Iterable["pytest.Item"]:
return (item for item in items if isinstance(item, pytest.Function))
3 changes: 3 additions & 0 deletions stubs/pytest.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ from typing import Any, Callable, TypeVar
ReturnType = TypeVar("ReturnType")

def fixture(func: Callable[..., ReturnType]) -> Callable[..., ReturnType]: ...

class Function: ...
class Item: ...
28 changes: 28 additions & 0 deletions tests/test_integration_pytest_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from .utils import clean_output


def test_ignores_non_function_nodes(testdir):
conftest = """
import pytest
class CustomItem(pytest.Item, pytest.File):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._nodeid += "::CUSTOM"
def runtest(self):
pass
def pytest_collect_file(path, parent):
return CustomItem(path, parent)
"""
testcase = """
def test_example(snapshot):
assert snapshot == 1
"""
testdir.makepyfile(conftest=conftest)
testdir.makepyfile(test_file=testcase)
result = testdir.runpytest("test_file.py", "-v", "--snapshot-update")
result_stdout = clean_output(result.stdout.str())
assert result.ret == 0
assert "test_file.py::CUSTOM" in result_stdout

0 comments on commit 8ed194c

Please sign in to comment.