| import pytest, py |
| |
| from _pytest.main import Session, EXIT_NOTESTSCOLLECTED |
| |
| class TestCollector: |
| 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 py.std.sys.version_info < (3, 0): |
| assert cmp(fn1, fn2) == 0 |
| 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: |
| 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) |
| |
| class TestCollectFS: |
| def test_ignored_certain_directories(self, testdir): |
| tmpdir = testdir.tmpdir |
| 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 |
| |
| 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: |
| def test_pytest_collect_file(self, testdir): |
| wascalled = [] |
| class Plugin: |
| def pytest_collect_file(self, path, parent): |
| 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: |
| 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: |
| def test_collection_error(self, testdir): |
| p = testdir.makepyfile(""" |
| import not_exists |
| """) |
| result = testdir.runpytest(p) |
| assert "__import__" not in result.stdout.str(), "too long traceback" |
| result.stdout.fnmatch_lines([ |
| "*ERROR collecting*", |
| "*mport*not_exists*" |
| ]) |
| |
| 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 |
| def pytest_make_collect_report(__multicall__): |
| rep = __multicall__.execute() |
| rep.headerlines += ["header1"] |
| return rep |
| """) |
| result = testdir.runpytest(p) |
| result.stdout.fnmatch_lines([ |
| "*ERROR collecting*", |
| "*header1*", |
| ]) |
| |
| |
| class TestCustomConftests: |
| 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: |
| 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 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 |
| py.std.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.nodeid.startswith(p.basename)"), |
| ("pytest_collectreport", "report.nodeid == ''") |
| ]) |
| |
| def test_collect_protocol_method(self, testdir): |
| p = testdir.makepyfile(""" |
| class TestClass: |
| 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 |
| |
| 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) |
| py.std.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)"), |
| #("pytest_collectreport", |
| # "report.fspath == %r" % str(rcol.fspath)), |
| ]) |
| |
| 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 |
| py.std.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 |
| py.std.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: |
| 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") |
| |
| class Test_getinitialnodes: |
| def test_global_file(self, testdir, tmpdir): |
| x = tmpdir.ensure("x.py") |
| config = testdir.parseconfigure(x) |
| col = testdir.getnode(config, x) |
| assert isinstance(col, pytest.Module) |
| assert col.name == 'x.py' |
| assert col.parent.name == testdir.tmpdir.basename |
| 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") |
| 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: |
| 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: |
| 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: |
| def x_test(self): |
| pass |
| |
| class TestCase: |
| 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: |
| 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: |
| def test_no_under(self, testdir): |
| modcol = testdir.getmodulecol(""" |
| def test_pass(): pass |
| def test_fail(): assert 0 |
| """) |
| l = list(modcol.keywords) |
| assert modcol.name in l |
| for x in l: |
| 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) |