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

Fix incorrect "collected items" report when specifying tests on the command-line #2468

Merged
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
6 changes: 5 additions & 1 deletion _pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,11 @@ def _matchnodes(self, matching, names):
if not has_matched and len(rep.result) == 1 and x.name == "()":
nextnames.insert(0, name)
resultnodes.extend(self.matchnodes([x], nextnames))
node.ihook.pytest_collectreport(report=rep)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this could break some expectations plugins had in some way? If so, maybe it should go to features?

Copy link
Member Author

@nicoddemus nicoddemus Jun 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered about that myself... but I suspect everyone expects that an item seen by pytest_collectreport to also appear later on pytest_collection_modifyitems (for example), which was not True previously.

But I doubt this will break anything because this change only enters in effect if the user is passing a test in the command line explicitly (test_foo.py::test_foo).

else:
# report collection failures here to avoid failing to run some test
# specified in the command line because the module could not be
# imported (#134)
node.ihook.pytest_collectreport(report=rep)
return resultnodes

def genitems(self, node):
Expand Down
2 changes: 1 addition & 1 deletion _pytest/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def report_collect(self, final=False):
line = "collected "
else:
line = "collecting "
line += str(self._numcollected) + " items"
line += str(self._numcollected) + " item" + ('' if self._numcollected == 1 else 's')
if errors:
line += " / %d errors" % errors
if skipped:
Expand Down
1 change: 1 addition & 0 deletions changelog/2464.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix incorrect "collected items" report when specifying tests on the command-line.
4 changes: 2 additions & 2 deletions testing/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,8 @@ def pytest_configure():
])
assert 'sessionstarttime' not in result.stderr.str()

@pytest.mark.parametrize('lookfor', ['test_fun.py', 'test_fun.py::test_a'])
def test_issue134_report_syntaxerror_when_collecting_member(self, testdir, lookfor):
@pytest.mark.parametrize('lookfor', ['test_fun.py::test_a'])
def test_issue134_report_error_when_collecting_member(self, testdir, lookfor):
testdir.makepyfile(test_fun="""
def test_a():
pass
Expand Down
19 changes: 14 additions & 5 deletions testing/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,11 @@ def test_collect_topdir(self, testdir):
assert len(colitems) == 1
assert colitems[0].fspath == p

def get_reported_items(self, hookrec):
"""Return pytest.Item instances reported by the pytest_collectreport hook"""
calls = hookrec.getcalls('pytest_collectreport')
return [x for call in calls for x in call.report.result
if isinstance(x, pytest.Item)]

def test_collect_protocol_single_function(self, testdir):
p = testdir.makepyfile("def test_func(): pass")
Expand All @@ -386,9 +391,10 @@ def test_collect_protocol_single_function(self, testdir):
("pytest_collectstart", "collector.fspath == p"),
("pytest_make_collect_report", "collector.fspath == p"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
("pytest_collectreport", "report.nodeid == ''")
("pytest_collectreport", "report.result[0].name == 'test_func'"),
])
# ensure we are reporting the collection of the single test item (#2464)
assert [x.name for x in self.get_reported_items(hookrec)] == ['test_func']

def test_collect_protocol_method(self, testdir):
p = testdir.makepyfile("""
Expand All @@ -407,6 +413,8 @@ def test_method(self):
assert items[0].name == "test_method"
newid = items[0].nodeid
assert newid == normid
# ensure we are reporting the collection of the single test item (#2464)
assert [x.name for x in self.get_reported_items(hookrec)] == ['test_method']

def test_collect_custom_nodes_multi_id(self, testdir):
p = testdir.makepyfile("def test_func(): pass")
Expand Down Expand Up @@ -436,9 +444,8 @@ def pytest_collect_file(path, parent):
"collector.__class__.__name__ == 'Module'"),
("pytest_pycollect_makeitem", "name == 'test_func'"),
("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
#("pytest_collectreport",
# "report.fspath == %r" % str(rcol.fspath)),
])
assert len(self.get_reported_items(hookrec)) == 2

def test_collect_subdir_event_ordering(self, testdir):
p = testdir.makepyfile("def test_func(): pass")
Expand Down Expand Up @@ -495,11 +502,13 @@ class TestClass(object):
def test_method(self):
pass
""")
arg = p.basename + ("::TestClass::test_method")
arg = p.basename + "::TestClass::test_method"
items, hookrec = testdir.inline_genitems(arg)
assert len(items) == 1
item, = items
assert item.nodeid.endswith("TestClass::()::test_method")
# ensure we are reporting the collection of the single test item (#2464)
assert [x.name for x in self.get_reported_items(hookrec)] == ['test_method']

class Test_getinitialnodes(object):
def test_global_file(self, testdir, tmpdir):
Expand Down
4 changes: 2 additions & 2 deletions testing/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,12 +513,12 @@ def test_foo():
assert 1
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines('*collected 1 items*')
result.stdout.fnmatch_lines('*collected 1 item*')
result.stdout.fnmatch_lines('*1 passed*')
assert result.ret == main.EXIT_OK

result = testdir.runpytest('-k nonmatch')
result.stdout.fnmatch_lines('*collected 1 items*')
result.stdout.fnmatch_lines('*collected 1 item*')
result.stdout.fnmatch_lines('*1 deselected*')
assert result.ret == main.EXIT_NOTESTSCOLLECTED

Expand Down
9 changes: 9 additions & 0 deletions testing/test_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@ def test_foobar():
assert result.ret == 2
result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])

def test_collect_single_item(self, testdir):
"""Use singular 'item' when reporting a single test item"""
testdir.makepyfile("""
def test_foobar():
pass
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines(['collected 1 item'])


class TestCollectonly(object):
def test_collectonly_basic(self, testdir):
Expand Down