summaryrefslogtreecommitdiffstats
path: root/third_party/python/pytest/testing/test_collection.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/pytest/testing/test_collection.py')
-rw-r--r--third_party/python/pytest/testing/test_collection.py944
1 files changed, 944 insertions, 0 deletions
diff --git a/third_party/python/pytest/testing/test_collection.py b/third_party/python/pytest/testing/test_collection.py
new file mode 100644
index 0000000000..657d64c74e
--- /dev/null
+++ b/third_party/python/pytest/testing/test_collection.py
@@ -0,0 +1,944 @@
+from __future__ import absolute_import, division, print_function
+import pprint
+import sys
+import pytest
+
+import _pytest._code
+from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
+
+
+class TestCollector(object):
+
+ def test_collect_versus_item(self):
+ from pytest import Collector, Item
+
+ assert not issubclass(Collector, Item)
+ assert not issubclass(Item, Collector)
+
+ def test_compat_attributes(self, testdir, recwarn):
+ modcol = testdir.getmodulecol(
+ """
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """
+ )
+ recwarn.clear()
+ assert modcol.Module == pytest.Module
+ assert modcol.Class == pytest.Class
+ assert modcol.Item == pytest.Item
+ assert modcol.File == pytest.File
+ assert modcol.Function == pytest.Function
+
+ def test_check_equality(self, testdir):
+ modcol = testdir.getmodulecol(
+ """
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """
+ )
+ fn1 = testdir.collect_by_name(modcol, "test_pass")
+ assert isinstance(fn1, pytest.Function)
+ fn2 = testdir.collect_by_name(modcol, "test_pass")
+ assert isinstance(fn2, pytest.Function)
+
+ assert fn1 == fn2
+ assert fn1 != modcol
+ if sys.version_info < (3, 0):
+ assert cmp(fn1, fn2) == 0 # NOQA
+ assert hash(fn1) == hash(fn2)
+
+ fn3 = testdir.collect_by_name(modcol, "test_fail")
+ assert isinstance(fn3, pytest.Function)
+ assert not (fn1 == fn3)
+ assert fn1 != fn3
+
+ for fn in fn1, fn2, fn3:
+ assert fn != 3
+ assert fn != modcol
+ assert fn != [1, 2, 3]
+ assert [1, 2, 3] != fn
+ assert modcol != fn
+
+ def test_getparent(self, testdir):
+ modcol = testdir.getmodulecol(
+ """
+ class TestClass(object):
+ def test_foo():
+ pass
+ """
+ )
+ cls = testdir.collect_by_name(modcol, "TestClass")
+ fn = testdir.collect_by_name(testdir.collect_by_name(cls, "()"), "test_foo")
+
+ parent = fn.getparent(pytest.Module)
+ assert parent is modcol
+
+ parent = fn.getparent(pytest.Function)
+ assert parent is fn
+
+ parent = fn.getparent(pytest.Class)
+ assert parent is cls
+
+ def test_getcustomfile_roundtrip(self, testdir):
+ hello = testdir.makefile(".xxx", hello="world")
+ testdir.makepyfile(
+ conftest="""
+ import pytest
+ class CustomFile(pytest.File):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".xxx":
+ return CustomFile(path, parent=parent)
+ """
+ )
+ node = testdir.getpathnode(hello)
+ assert isinstance(node, pytest.File)
+ assert node.name == "hello.xxx"
+ nodes = node.session.perform_collect([node.nodeid], genitems=False)
+ assert len(nodes) == 1
+ assert isinstance(nodes[0], pytest.File)
+
+ def test_can_skip_class_with_test_attr(self, testdir):
+ """Assure test class is skipped when using `__test__=False` (See #2007)."""
+ testdir.makepyfile(
+ """
+ class TestFoo(object):
+ __test__ = False
+ def __init__(self):
+ pass
+ def test_foo():
+ assert True
+ """
+ )
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["collected 0 items", "*no tests ran in*"])
+
+
+class TestCollectFS(object):
+
+ def test_ignored_certain_directories(self, testdir):
+ tmpdir = testdir.tmpdir
+ tmpdir.ensure("build", "test_notfound.py")
+ tmpdir.ensure("dist", "test_notfound.py")
+ tmpdir.ensure("_darcs", "test_notfound.py")
+ tmpdir.ensure("CVS", "test_notfound.py")
+ tmpdir.ensure("{arch}", "test_notfound.py")
+ tmpdir.ensure(".whatever", "test_notfound.py")
+ tmpdir.ensure(".bzr", "test_notfound.py")
+ tmpdir.ensure("normal", "test_found.py")
+ for x in tmpdir.visit("test_*.py"):
+ x.write("def test_hello(): pass")
+
+ result = testdir.runpytest("--collect-only")
+ s = result.stdout.str()
+ assert "test_notfound" not in s
+ assert "test_found" in s
+
+ @pytest.mark.parametrize(
+ "fname",
+ (
+ "activate",
+ "activate.csh",
+ "activate.fish",
+ "Activate",
+ "Activate.bat",
+ "Activate.ps1",
+ ),
+ )
+ def test_ignored_virtualenvs(self, testdir, fname):
+ bindir = "Scripts" if sys.platform.startswith("win") else "bin"
+ testdir.tmpdir.ensure("virtual", bindir, fname)
+ testfile = testdir.tmpdir.ensure("virtual", "test_invenv.py")
+ testfile.write("def test_hello(): pass")
+
+ # by default, ignore tests inside a virtualenv
+ result = testdir.runpytest()
+ assert "test_invenv" not in result.stdout.str()
+ # allow test collection if user insists
+ result = testdir.runpytest("--collect-in-virtualenv")
+ assert "test_invenv" in result.stdout.str()
+ # allow test collection if user directly passes in the directory
+ result = testdir.runpytest("virtual")
+ assert "test_invenv" in result.stdout.str()
+
+ @pytest.mark.parametrize(
+ "fname",
+ (
+ "activate",
+ "activate.csh",
+ "activate.fish",
+ "Activate",
+ "Activate.bat",
+ "Activate.ps1",
+ ),
+ )
+ def test_ignored_virtualenvs_norecursedirs_precedence(self, testdir, fname):
+ bindir = "Scripts" if sys.platform.startswith("win") else "bin"
+ # norecursedirs takes priority
+ testdir.tmpdir.ensure(".virtual", bindir, fname)
+ testfile = testdir.tmpdir.ensure(".virtual", "test_invenv.py")
+ testfile.write("def test_hello(): pass")
+ result = testdir.runpytest("--collect-in-virtualenv")
+ assert "test_invenv" not in result.stdout.str()
+ # ...unless the virtualenv is explicitly given on the CLI
+ result = testdir.runpytest("--collect-in-virtualenv", ".virtual")
+ assert "test_invenv" in result.stdout.str()
+
+ @pytest.mark.parametrize(
+ "fname",
+ (
+ "activate",
+ "activate.csh",
+ "activate.fish",
+ "Activate",
+ "Activate.bat",
+ "Activate.ps1",
+ ),
+ )
+ def test__in_venv(self, testdir, fname):
+ """Directly test the virtual env detection function"""
+ bindir = "Scripts" if sys.platform.startswith("win") else "bin"
+ # no bin/activate, not a virtualenv
+ base_path = testdir.tmpdir.mkdir("venv")
+ assert _in_venv(base_path) is False
+ # with bin/activate, totally a virtualenv
+ base_path.ensure(bindir, fname)
+ assert _in_venv(base_path) is True
+
+ def test_custom_norecursedirs(self, testdir):
+ testdir.makeini(
+ """
+ [pytest]
+ norecursedirs = mydir xyz*
+ """
+ )
+ tmpdir = testdir.tmpdir
+ tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass")
+ tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0")
+ tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass")
+ rec = testdir.inline_run()
+ rec.assertoutcome(passed=1)
+ rec = testdir.inline_run("xyz123/test_2.py")
+ rec.assertoutcome(failed=1)
+
+ def test_testpaths_ini(self, testdir, monkeypatch):
+ testdir.makeini(
+ """
+ [pytest]
+ testpaths = gui uts
+ """
+ )
+ tmpdir = testdir.tmpdir
+ tmpdir.ensure("env", "test_1.py").write("def test_env(): pass")
+ tmpdir.ensure("gui", "test_2.py").write("def test_gui(): pass")
+ tmpdir.ensure("uts", "test_3.py").write("def test_uts(): pass")
+
+ # executing from rootdir only tests from `testpaths` directories
+ # are collected
+ items, reprec = testdir.inline_genitems("-v")
+ assert [x.name for x in items] == ["test_gui", "test_uts"]
+
+ # check that explicitly passing directories in the command-line
+ # collects the tests
+ for dirname in ("env", "gui", "uts"):
+ items, reprec = testdir.inline_genitems(tmpdir.join(dirname))
+ assert [x.name for x in items] == ["test_%s" % dirname]
+
+ # changing cwd to each subdirectory and running pytest without
+ # arguments collects the tests in that directory normally
+ for dirname in ("env", "gui", "uts"):
+ monkeypatch.chdir(testdir.tmpdir.join(dirname))
+ items, reprec = testdir.inline_genitems()
+ assert [x.name for x in items] == ["test_%s" % dirname]
+
+
+class TestCollectPluginHookRelay(object):
+
+ def test_pytest_collect_file(self, testdir):
+ wascalled = []
+
+ class Plugin(object):
+
+ def pytest_collect_file(self, path, parent):
+ if not path.basename.startswith("."):
+ # Ignore hidden files, e.g. .testmondata.
+ wascalled.append(path)
+
+ testdir.makefile(".abc", "xyz")
+ pytest.main([testdir.tmpdir], plugins=[Plugin()])
+ assert len(wascalled) == 1
+ assert wascalled[0].ext == ".abc"
+
+ def test_pytest_collect_directory(self, testdir):
+ wascalled = []
+
+ class Plugin(object):
+
+ def pytest_collect_directory(self, path, parent):
+ wascalled.append(path.basename)
+
+ testdir.mkdir("hello")
+ testdir.mkdir("world")
+ pytest.main(testdir.tmpdir, plugins=[Plugin()])
+ assert "hello" in wascalled
+ assert "world" in wascalled
+
+
+class TestPrunetraceback(object):
+
+ def test_custom_repr_failure(self, testdir):
+ p = testdir.makepyfile(
+ """
+ import not_exists
+ """
+ )
+ testdir.makeconftest(
+ """
+ import pytest
+ def pytest_collect_file(path, parent):
+ return MyFile(path, parent)
+ class MyError(Exception):
+ pass
+ class MyFile(pytest.File):
+ def collect(self):
+ raise MyError()
+ def repr_failure(self, excinfo):
+ if excinfo.errisinstance(MyError):
+ return "hello world"
+ return pytest.File.repr_failure(self, excinfo)
+ """
+ )
+
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines(["*ERROR collecting*", "*hello world*"])
+
+ @pytest.mark.xfail(reason="other mechanism for adding to reporting needed")
+ def test_collect_report_postprocessing(self, testdir):
+ p = testdir.makepyfile(
+ """
+ import not_exists
+ """
+ )
+ testdir.makeconftest(
+ """
+ import pytest
+ @pytest.hookimpl(hookwrapper=True)
+ def pytest_make_collect_report():
+ outcome = yield
+ rep = outcome.get_result()
+ rep.headerlines += ["header1"]
+ outcome.force_result(rep)
+ """
+ )
+ result = testdir.runpytest(p)
+ result.stdout.fnmatch_lines(["*ERROR collecting*", "*header1*"])
+
+
+class TestCustomConftests(object):
+
+ def test_ignore_collect_path(self, testdir):
+ testdir.makeconftest(
+ """
+ def pytest_ignore_collect(path, config):
+ return path.basename.startswith("x") or \
+ path.basename == "test_one.py"
+ """
+ )
+ sub = testdir.mkdir("xy123")
+ sub.ensure("test_hello.py").write("syntax error")
+ sub.join("conftest.py").write("syntax error")
+ testdir.makepyfile("def test_hello(): pass")
+ testdir.makepyfile(test_one="syntax error")
+ result = testdir.runpytest("--fulltrace")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*1 passed*"])
+
+ def test_ignore_collect_not_called_on_argument(self, testdir):
+ testdir.makeconftest(
+ """
+ def pytest_ignore_collect(path, config):
+ return True
+ """
+ )
+ p = testdir.makepyfile("def test_hello(): pass")
+ result = testdir.runpytest(p)
+ assert result.ret == 0
+ result.stdout.fnmatch_lines("*1 passed*")
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ result.stdout.fnmatch_lines("*collected 0 items*")
+
+ def test_collectignore_exclude_on_option(self, testdir):
+ testdir.makeconftest(
+ """
+ collect_ignore = ['hello', 'test_world.py']
+ def pytest_addoption(parser):
+ parser.addoption("--XX", action="store_true", default=False)
+ def pytest_configure(config):
+ if config.getvalue("XX"):
+ collect_ignore[:] = []
+ """
+ )
+ testdir.mkdir("hello")
+ testdir.makepyfile(test_world="def test_hello(): pass")
+ result = testdir.runpytest()
+ assert result.ret == EXIT_NOTESTSCOLLECTED
+ assert "passed" not in result.stdout.str()
+ result = testdir.runpytest("--XX")
+ assert result.ret == 0
+ assert "passed" in result.stdout.str()
+
+ def test_pytest_fs_collect_hooks_are_seen(self, testdir):
+ testdir.makeconftest(
+ """
+ import pytest
+ class MyModule(pytest.Module):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".py":
+ return MyModule(path, parent)
+ """
+ )
+ testdir.mkdir("sub")
+ testdir.makepyfile("def test_x(): pass")
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines(["*MyModule*", "*test_x*"])
+
+ def test_pytest_collect_file_from_sister_dir(self, testdir):
+ sub1 = testdir.mkpydir("sub1")
+ sub2 = testdir.mkpydir("sub2")
+ conf1 = testdir.makeconftest(
+ """
+ import pytest
+ class MyModule1(pytest.Module):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".py":
+ return MyModule1(path, parent)
+ """
+ )
+ conf1.move(sub1.join(conf1.basename))
+ conf2 = testdir.makeconftest(
+ """
+ import pytest
+ class MyModule2(pytest.Module):
+ pass
+ def pytest_collect_file(path, parent):
+ if path.ext == ".py":
+ return MyModule2(path, parent)
+ """
+ )
+ conf2.move(sub2.join(conf2.basename))
+ p = testdir.makepyfile("def test_x(): pass")
+ p.copy(sub1.join(p.basename))
+ p.copy(sub2.join(p.basename))
+ result = testdir.runpytest("--collect-only")
+ result.stdout.fnmatch_lines(["*MyModule1*", "*MyModule2*", "*test_x*"])
+
+
+class TestSession(object):
+
+ def test_parsearg(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ subdir = testdir.mkdir("sub")
+ subdir.ensure("__init__.py")
+ target = subdir.join(p.basename)
+ p.move(target)
+ subdir.chdir()
+ config = testdir.parseconfig(p.basename)
+ rcol = Session(config=config)
+ assert rcol.fspath == subdir
+ parts = rcol._parsearg(p.basename)
+
+ assert parts[0] == target
+ assert len(parts) == 1
+ parts = rcol._parsearg(p.basename + "::test_func")
+ assert parts[0] == target
+ assert parts[1] == "test_func"
+ assert len(parts) == 2
+
+ def test_collect_topdir(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ id = "::".join([p.basename, "test_func"])
+ # XXX migrate to collectonly? (see below)
+ config = testdir.parseconfig(id)
+ topdir = testdir.tmpdir
+ rcol = Session(config)
+ assert topdir == rcol.fspath
+ # rootid = rcol.nodeid
+ # root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
+ # assert root2 == rcol, rootid
+ colitems = rcol.perform_collect([rcol.nodeid], genitems=False)
+ 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")
+ id = "::".join([p.basename, "test_func"])
+ items, hookrec = testdir.inline_genitems(id)
+ item, = items
+ assert item.name == "test_func"
+ newid = item.nodeid
+ assert newid == id
+ pprint.pprint(hookrec.calls)
+ topdir = testdir.tmpdir # noqa
+ hookrec.assert_contains(
+ [
+ ("pytest_collectstart", "collector.fspath == topdir"),
+ ("pytest_make_collect_report", "collector.fspath == topdir"),
+ ("pytest_collectstart", "collector.fspath == p"),
+ ("pytest_make_collect_report", "collector.fspath == p"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("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(
+ """
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """
+ )
+ normid = p.basename + "::TestClass::()::test_method"
+ for id in [
+ p.basename,
+ p.basename + "::TestClass",
+ p.basename + "::TestClass::()",
+ normid,
+ ]:
+ items, hookrec = testdir.inline_genitems(id)
+ assert len(items) == 1
+ 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")
+ testdir.makeconftest(
+ """
+ import pytest
+ class SpecialItem(pytest.Item):
+ def runtest(self):
+ return # ok
+ class SpecialFile(pytest.File):
+ def collect(self):
+ return [SpecialItem(name="check", parent=self)]
+ def pytest_collect_file(path, parent):
+ if path.basename == %r:
+ return SpecialFile(fspath=path, parent=parent)
+ """
+ % p.basename
+ )
+ id = p.basename
+
+ items, hookrec = testdir.inline_genitems(id)
+ pprint.pprint(hookrec.calls)
+ assert len(items) == 2
+ hookrec.assert_contains(
+ [
+ ("pytest_collectstart", "collector.fspath == collector.session.fspath"),
+ (
+ "pytest_collectstart",
+ "collector.__class__.__name__ == 'SpecialFile'",
+ ),
+ ("pytest_collectstart", "collector.__class__.__name__ == 'Module'"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
+ ]
+ )
+ assert len(self.get_reported_items(hookrec)) == 2
+
+ def test_collect_subdir_event_ordering(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ aaa = testdir.mkpydir("aaa")
+ test_aaa = aaa.join("test_aaa.py")
+ p.move(test_aaa)
+
+ items, hookrec = testdir.inline_genitems()
+ assert len(items) == 1
+ pprint.pprint(hookrec.calls)
+ hookrec.assert_contains(
+ [
+ ("pytest_collectstart", "collector.fspath == test_aaa"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid.startswith('aaa/test_aaa.py')"),
+ ]
+ )
+
+ def test_collect_two_commandline_args(self, testdir):
+ p = testdir.makepyfile("def test_func(): pass")
+ aaa = testdir.mkpydir("aaa")
+ bbb = testdir.mkpydir("bbb")
+ test_aaa = aaa.join("test_aaa.py")
+ p.copy(test_aaa)
+ test_bbb = bbb.join("test_bbb.py")
+ p.move(test_bbb)
+
+ id = "."
+
+ items, hookrec = testdir.inline_genitems(id)
+ assert len(items) == 2
+ pprint.pprint(hookrec.calls)
+ hookrec.assert_contains(
+ [
+ ("pytest_collectstart", "collector.fspath == test_aaa"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"),
+ ("pytest_collectstart", "collector.fspath == test_bbb"),
+ ("pytest_pycollect_makeitem", "name == 'test_func'"),
+ ("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"),
+ ]
+ )
+
+ def test_serialization_byid(self, testdir):
+ testdir.makepyfile("def test_func(): pass")
+ items, hookrec = testdir.inline_genitems()
+ assert len(items) == 1
+ item, = items
+ items2, hookrec = testdir.inline_genitems(item.nodeid)
+ item2, = items2
+ assert item2.name == item.name
+ assert item2.fspath == item.fspath
+
+ def test_find_byid_without_instance_parents(self, testdir):
+ p = testdir.makepyfile(
+ """
+ class TestClass(object):
+ def test_method(self):
+ pass
+ """
+ )
+ 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):
+ x = tmpdir.ensure("x.py")
+ with tmpdir.as_cwd():
+ config = testdir.parseconfigure(x)
+ col = testdir.getnode(config, x)
+ assert isinstance(col, pytest.Module)
+ assert col.name == "x.py"
+ assert col.parent.parent is None
+ for col in col.listchain():
+ assert col.config is config
+
+ def test_pkgfile(self, testdir):
+ tmpdir = testdir.tmpdir
+ subdir = tmpdir.join("subdir")
+ x = subdir.ensure("x.py")
+ subdir.ensure("__init__.py")
+ with subdir.as_cwd():
+ config = testdir.parseconfigure(x)
+ col = testdir.getnode(config, x)
+ assert isinstance(col, pytest.Module)
+ assert col.name == "x.py"
+ assert col.parent.parent is None
+ for col in col.listchain():
+ assert col.config is config
+
+
+class Test_genitems(object):
+
+ def test_check_collect_hashes(self, testdir):
+ p = testdir.makepyfile(
+ """
+ def test_1():
+ pass
+
+ def test_2():
+ pass
+ """
+ )
+ p.copy(p.dirpath(p.purebasename + "2" + ".py"))
+ items, reprec = testdir.inline_genitems(p.dirpath())
+ assert len(items) == 4
+ for numi, i in enumerate(items):
+ for numj, j in enumerate(items):
+ if numj != numi:
+ assert hash(i) != hash(j)
+ assert i != j
+
+ def test_example_items1(self, testdir):
+ p = testdir.makepyfile(
+ """
+ def testone():
+ pass
+
+ class TestX(object):
+ def testmethod_one(self):
+ pass
+
+ class TestY(TestX):
+ pass
+ """
+ )
+ items, reprec = testdir.inline_genitems(p)
+ assert len(items) == 3
+ assert items[0].name == "testone"
+ assert items[1].name == "testmethod_one"
+ assert items[2].name == "testmethod_one"
+
+ # let's also test getmodpath here
+ assert items[0].getmodpath() == "testone"
+ assert items[1].getmodpath() == "TestX.testmethod_one"
+ assert items[2].getmodpath() == "TestY.testmethod_one"
+
+ s = items[0].getmodpath(stopatmodule=False)
+ assert s.endswith("test_example_items1.testone")
+ print(s)
+
+ def test_class_and_functions_discovery_using_glob(self, testdir):
+ """
+ tests that python_classes and python_functions config options work
+ as prefixes and glob-like patterns (issue #600).
+ """
+ testdir.makeini(
+ """
+ [pytest]
+ python_classes = *Suite Test
+ python_functions = *_test test
+ """
+ )
+ p = testdir.makepyfile(
+ """
+ class MyTestSuite(object):
+ def x_test(self):
+ pass
+
+ class TestCase(object):
+ def test_y(self):
+ pass
+ """
+ )
+ items, reprec = testdir.inline_genitems(p)
+ ids = [x.getmodpath() for x in items]
+ assert ids == ["MyTestSuite.x_test", "TestCase.test_y"]
+
+
+def test_matchnodes_two_collections_same_file(testdir):
+ testdir.makeconftest(
+ """
+ import pytest
+ def pytest_configure(config):
+ config.pluginmanager.register(Plugin2())
+
+ class Plugin2(object):
+ def pytest_collect_file(self, path, parent):
+ if path.ext == ".abc":
+ return MyFile2(path, parent)
+
+ def pytest_collect_file(path, parent):
+ if path.ext == ".abc":
+ return MyFile1(path, parent)
+
+ class MyFile1(pytest.Item, pytest.File):
+ def runtest(self):
+ pass
+ class MyFile2(pytest.File):
+ def collect(self):
+ return [Item2("hello", parent=self)]
+
+ class Item2(pytest.Item):
+ def runtest(self):
+ pass
+ """
+ )
+ p = testdir.makefile(".abc", "")
+ result = testdir.runpytest()
+ assert result.ret == 0
+ result.stdout.fnmatch_lines(["*2 passed*"])
+ res = testdir.runpytest("%s::hello" % p.basename)
+ res.stdout.fnmatch_lines(["*1 passed*"])
+
+
+class TestNodekeywords(object):
+
+ def test_no_under(self, testdir):
+ modcol = testdir.getmodulecol(
+ """
+ def test_pass(): pass
+ def test_fail(): assert 0
+ """
+ )
+ values = list(modcol.keywords)
+ assert modcol.name in values
+ for x in values:
+ assert not x.startswith("_")
+ assert modcol.name in repr(modcol.keywords)
+
+ def test_issue345(self, testdir):
+ testdir.makepyfile(
+ """
+ def test_should_not_be_selected():
+ assert False, 'I should not have been selected to run'
+
+ def test___repr__():
+ pass
+ """
+ )
+ reprec = testdir.inline_run("-k repr")
+ reprec.assertoutcome(passed=1, failed=0)
+
+
+COLLECTION_ERROR_PY_FILES = dict(
+ test_01_failure="""
+ def test_1():
+ assert False
+ """,
+ test_02_import_error="""
+ import asdfasdfasdf
+ def test_2():
+ assert True
+ """,
+ test_03_import_error="""
+ import asdfasdfasdf
+ def test_3():
+ assert True
+ """,
+ test_04_success="""
+ def test_4():
+ assert True
+ """,
+)
+
+
+def test_exit_on_collection_error(testdir):
+ """Verify that all collection errors are collected and no tests executed"""
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest()
+ assert res.ret == 2
+
+ res.stdout.fnmatch_lines(
+ [
+ "collected 2 items / 2 errors",
+ "*ERROR collecting test_02_import_error.py*",
+ "*No module named *asdfa*",
+ "*ERROR collecting test_03_import_error.py*",
+ "*No module named *asdfa*",
+ ]
+ )
+
+
+def test_exit_on_collection_with_maxfail_smaller_than_n_errors(testdir):
+ """
+ Verify collection is aborted once maxfail errors are encountered ignoring
+ further modules which would cause more collection errors.
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--maxfail=1")
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines(
+ ["*ERROR collecting test_02_import_error.py*", "*No module named *asdfa*"]
+ )
+
+ assert "test_03" not in res.stdout.str()
+
+
+def test_exit_on_collection_with_maxfail_bigger_than_n_errors(testdir):
+ """
+ Verify the test run aborts due to collection errors even if maxfail count of
+ errors was not reached.
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--maxfail=4")
+ assert res.ret == 2
+
+ res.stdout.fnmatch_lines(
+ [
+ "collected 2 items / 2 errors",
+ "*ERROR collecting test_02_import_error.py*",
+ "*No module named *asdfa*",
+ "*ERROR collecting test_03_import_error.py*",
+ "*No module named *asdfa*",
+ ]
+ )
+
+
+def test_continue_on_collection_errors(testdir):
+ """
+ Verify tests are executed even when collection errors occur when the
+ --continue-on-collection-errors flag is set
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--continue-on-collection-errors")
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines(
+ ["collected 2 items / 2 errors", "*1 failed, 1 passed, 2 error*"]
+ )
+
+
+def test_continue_on_collection_errors_maxfail(testdir):
+ """
+ Verify tests are executed even when collection errors occur and that maxfail
+ is honoured (including the collection error count).
+ 4 tests: 2 collection errors + 1 failure + 1 success
+ test_4 is never executed because the test run is with --maxfail=3 which
+ means it is interrupted after the 2 collection errors + 1 failure.
+ """
+ testdir.makepyfile(**COLLECTION_ERROR_PY_FILES)
+
+ res = testdir.runpytest("--continue-on-collection-errors", "--maxfail=3")
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 error*"])
+
+
+def test_fixture_scope_sibling_conftests(testdir):
+ """Regression test case for https://github.com/pytest-dev/pytest/issues/2836"""
+ foo_path = testdir.mkpydir("foo")
+ foo_path.join("conftest.py").write(
+ _pytest._code.Source(
+ """
+ import pytest
+ @pytest.fixture
+ def fix():
+ return 1
+ """
+ )
+ )
+ foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1")
+
+ # Tests in `food/` should not see the conftest fixture from `foo/`
+ food_path = testdir.mkpydir("food")
+ food_path.join("test_food.py").write("def test_food(fix): assert fix == 1")
+
+ res = testdir.runpytest()
+ assert res.ret == 1
+
+ res.stdout.fnmatch_lines(
+ [
+ "*ERROR at setup of test_food*",
+ "E*fixture 'fix' not found",
+ "*1 passed, 1 error*",
+ ]
+ )