diff --git a/changelog/5003.bugfix.rst b/changelog/5003.bugfix.rst new file mode 100644 index 000000000..8d18a50e6 --- /dev/null +++ b/changelog/5003.bugfix.rst @@ -0,0 +1 @@ +Fix line offset with mark collection error (off by one). diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 887f323f9..39701a39b 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -203,7 +203,9 @@ def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0): def getfslineno(obj): """ Return source location (path, lineno) for the given object. - If the source cannot be determined return ("", -1) + If the source cannot be determined return ("", -1). + + The line number is 0-based. """ from .code import Code diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index d65d8b9d8..0021dd5d6 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -44,7 +44,7 @@ def get_empty_parameterset_mark(config, argnames, func): f_name = func.__name__ _, lineno = getfslineno(func) raise Collector.CollectError( - "Empty parameter set in '%s' at line %d" % (f_name, lineno) + "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) ) else: raise LookupError(requested_mark) diff --git a/testing/test_mark.py b/testing/test_mark.py index f7d8cf689..2851dbc16 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -8,6 +8,7 @@ import sys import six import pytest +from _pytest.main import EXIT_INTERRUPTED from _pytest.mark import EMPTY_PARAMETERSET_OPTION from _pytest.mark import MarkGenerator as Mark from _pytest.nodes import Collector @@ -859,20 +860,34 @@ def test_parameterset_for_fail_at_collect(testdir): config = testdir.parseconfig() from _pytest.mark import pytest_configure, get_empty_parameterset_mark - from _pytest.compat import getfslineno pytest_configure(config) - test_func = all - func_name = test_func.__name__ - _, func_lineno = getfslineno(test_func) - expected_errmsg = r"Empty parameter set in '%s' at line %d" % ( - func_name, - func_lineno, - ) + with pytest.raises( + Collector.CollectError, + match=r"Empty parameter set in 'pytest_configure' at line \d\d+", + ): + get_empty_parameterset_mark(config, ["a"], pytest_configure) - with pytest.raises(Collector.CollectError, match=expected_errmsg): - get_empty_parameterset_mark(config, ["a"], test_func) + p1 = testdir.makepyfile( + """ + import pytest + + @pytest.mark.parametrize("empty", []) + def test(): + pass + """ + ) + result = testdir.runpytest(str(p1)) + result.stdout.fnmatch_lines( + [ + "collected 0 items / 1 errors", + "* ERROR collecting test_parameterset_for_fail_at_collect.py *", + "Empty parameter set in 'test' at line 3", + "*= 1 error in *", + ] + ) + assert result.ret == EXIT_INTERRUPTED def test_parameterset_for_parametrize_bad_markname(testdir):