1# -*- coding: utf-8 -*-
2from __future__ import absolute_import, division, print_function
3import os
4import sys
5import types
6
7import six
8
9import _pytest._code
10import py
11import pytest
12from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
13
14
15class TestGeneralUsage(object):
16
17    def test_config_error(self, testdir):
18        testdir.makeconftest(
19            """
20            def pytest_configure(config):
21                import pytest
22                raise pytest.UsageError("hello")
23        """
24        )
25        result = testdir.runpytest(testdir.tmpdir)
26        assert result.ret != 0
27        result.stderr.fnmatch_lines(["*ERROR: hello"])
28
29    def test_root_conftest_syntax_error(self, testdir):
30        testdir.makepyfile(conftest="raise SyntaxError\n")
31        result = testdir.runpytest()
32        result.stderr.fnmatch_lines(["*raise SyntaxError*"])
33        assert result.ret != 0
34
35    def test_early_hook_error_issue38_1(self, testdir):
36        testdir.makeconftest(
37            """
38            def pytest_sessionstart():
39                0 / 0
40        """
41        )
42        result = testdir.runpytest(testdir.tmpdir)
43        assert result.ret != 0
44        # tracestyle is native by default for hook failures
45        result.stdout.fnmatch_lines(
46            ["*INTERNALERROR*File*conftest.py*line 2*", "*0 / 0*"]
47        )
48        result = testdir.runpytest(testdir.tmpdir, "--fulltrace")
49        assert result.ret != 0
50        # tracestyle is native by default for hook failures
51        result.stdout.fnmatch_lines(
52            ["*INTERNALERROR*def pytest_sessionstart():*", "*INTERNALERROR*0 / 0*"]
53        )
54
55    def test_early_hook_configure_error_issue38(self, testdir):
56        testdir.makeconftest(
57            """
58            def pytest_configure():
59                0 / 0
60        """
61        )
62        result = testdir.runpytest(testdir.tmpdir)
63        assert result.ret != 0
64        # here we get it on stderr
65        result.stderr.fnmatch_lines(
66            ["*INTERNALERROR*File*conftest.py*line 2*", "*0 / 0*"]
67        )
68
69    def test_file_not_found(self, testdir):
70        result = testdir.runpytest("asd")
71        assert result.ret != 0
72        result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
73
74    def test_file_not_found_unconfigure_issue143(self, testdir):
75        testdir.makeconftest(
76            """
77            def pytest_configure():
78                print("---configure")
79            def pytest_unconfigure():
80                print("---unconfigure")
81        """
82        )
83        result = testdir.runpytest("-s", "asd")
84        assert result.ret == 4  # EXIT_USAGEERROR
85        result.stderr.fnmatch_lines(["ERROR: file not found*asd"])
86        result.stdout.fnmatch_lines(["*---configure", "*---unconfigure"])
87
88    def test_config_preparse_plugin_option(self, testdir):
89        testdir.makepyfile(
90            pytest_xyz="""
91            def pytest_addoption(parser):
92                parser.addoption("--xyz", dest="xyz", action="store")
93        """
94        )
95        testdir.makepyfile(
96            test_one="""
97            def test_option(pytestconfig):
98                assert pytestconfig.option.xyz == "123"
99        """
100        )
101        result = testdir.runpytest("-p", "pytest_xyz", "--xyz=123", syspathinsert=True)
102        assert result.ret == 0
103        result.stdout.fnmatch_lines(["*1 passed*"])
104
105    def test_assertion_magic(self, testdir):
106        p = testdir.makepyfile(
107            """
108            def test_this():
109                x = 0
110                assert x
111        """
112        )
113        result = testdir.runpytest(p)
114        result.stdout.fnmatch_lines([">       assert x", "E       assert 0"])
115        assert result.ret == 1
116
117    def test_nested_import_error(self, testdir):
118        p = testdir.makepyfile(
119            """
120                import import_fails
121                def test_this():
122                    assert import_fails.a == 1
123        """
124        )
125        testdir.makepyfile(import_fails="import does_not_work")
126        result = testdir.runpytest(p)
127        result.stdout.fnmatch_lines(
128            [
129                # XXX on jython this fails:  ">   import import_fails",
130                "ImportError while importing test module*",
131                "*No module named *does_not_work*",
132            ]
133        )
134        assert result.ret == 2
135
136    def test_not_collectable_arguments(self, testdir):
137        p1 = testdir.makepyfile("")
138        p2 = testdir.makefile(".pyc", "123")
139        result = testdir.runpytest(p1, p2)
140        assert result.ret
141        result.stderr.fnmatch_lines(["*ERROR: not found:*%s" % (p2.basename,)])
142
143    def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
144        testdir.makepyfile("")
145        testdir.makeconftest("import qwerty")
146        result = testdir.runpytest("--help")
147        result.stdout.fnmatch_lines(
148            """
149            *--version*
150            *warning*conftest.py*
151        """
152        )
153        result = testdir.runpytest()
154        result.stderr.fnmatch_lines(
155            """
156            *ERROR*could not load*conftest.py*
157        """
158        )
159
160    def test_early_skip(self, testdir):
161        testdir.mkdir("xyz")
162        testdir.makeconftest(
163            """
164            import pytest
165            def pytest_collect_directory():
166                pytest.skip("early")
167        """
168        )
169        result = testdir.runpytest()
170        assert result.ret == EXIT_NOTESTSCOLLECTED
171        result.stdout.fnmatch_lines(["*1 skip*"])
172
173    def test_issue88_initial_file_multinodes(self, testdir):
174        testdir.makeconftest(
175            """
176            import pytest
177            class MyFile(pytest.File):
178                def collect(self):
179                    return [MyItem("hello", parent=self)]
180            def pytest_collect_file(path, parent):
181                return MyFile(path, parent)
182            class MyItem(pytest.Item):
183                pass
184        """
185        )
186        p = testdir.makepyfile("def test_hello(): pass")
187        result = testdir.runpytest(p, "--collect-only")
188        result.stdout.fnmatch_lines(["*MyFile*test_issue88*", "*Module*test_issue88*"])
189
190    def test_issue93_initialnode_importing_capturing(self, testdir):
191        testdir.makeconftest(
192            """
193            import sys
194            print ("should not be seen")
195            sys.stderr.write("stder42\\n")
196        """
197        )
198        result = testdir.runpytest()
199        assert result.ret == EXIT_NOTESTSCOLLECTED
200        assert "should not be seen" not in result.stdout.str()
201        assert "stderr42" not in result.stderr.str()
202
203    def test_conftest_printing_shows_if_error(self, testdir):
204        testdir.makeconftest(
205            """
206            print ("should be seen")
207            assert 0
208        """
209        )
210        result = testdir.runpytest()
211        assert result.ret != 0
212        assert "should be seen" in result.stdout.str()
213
214    @pytest.mark.skipif(
215        not hasattr(py.path.local, "mksymlinkto"),
216        reason="symlink not available on this platform",
217    )
218    def test_chdir(self, testdir):
219        testdir.tmpdir.join("py").mksymlinkto(py._pydir)
220        p = testdir.tmpdir.join("main.py")
221        p.write(
222            _pytest._code.Source(
223                """
224            import sys, os
225            sys.path.insert(0, '')
226            import py
227            print (py.__file__)
228            print (py.__path__)
229            os.chdir(os.path.dirname(os.getcwd()))
230            print (py.log)
231        """
232            )
233        )
234        result = testdir.runpython(p)
235        assert not result.ret
236
237    def test_issue109_sibling_conftests_not_loaded(self, testdir):
238        sub1 = testdir.mkdir("sub1")
239        sub2 = testdir.mkdir("sub2")
240        sub1.join("conftest.py").write("assert 0")
241        result = testdir.runpytest(sub2)
242        assert result.ret == EXIT_NOTESTSCOLLECTED
243        sub2.ensure("__init__.py")
244        p = sub2.ensure("test_hello.py")
245        result = testdir.runpytest(p)
246        assert result.ret == EXIT_NOTESTSCOLLECTED
247        result = testdir.runpytest(sub1)
248        assert result.ret == EXIT_USAGEERROR
249
250    def test_directory_skipped(self, testdir):
251        testdir.makeconftest(
252            """
253            import pytest
254            def pytest_ignore_collect():
255                pytest.skip("intentional")
256        """
257        )
258        testdir.makepyfile("def test_hello(): pass")
259        result = testdir.runpytest()
260        assert result.ret == EXIT_NOTESTSCOLLECTED
261        result.stdout.fnmatch_lines(["*1 skipped*"])
262
263    def test_multiple_items_per_collector_byid(self, testdir):
264        c = testdir.makeconftest(
265            """
266            import pytest
267            class MyItem(pytest.Item):
268                def runtest(self):
269                    pass
270            class MyCollector(pytest.File):
271                def collect(self):
272                    return [MyItem(name="xyz", parent=self)]
273            def pytest_collect_file(path, parent):
274                if path.basename.startswith("conftest"):
275                    return MyCollector(path, parent)
276        """
277        )
278        result = testdir.runpytest(c.basename + "::" + "xyz")
279        assert result.ret == 0
280        result.stdout.fnmatch_lines(["*1 pass*"])
281
282    def test_skip_on_generated_funcarg_id(self, testdir):
283        testdir.makeconftest(
284            """
285            import pytest
286            def pytest_generate_tests(metafunc):
287                metafunc.addcall({'x': 3}, id='hello-123')
288            def pytest_runtest_setup(item):
289                print (item.keywords)
290                if 'hello-123' in item.keywords:
291                    pytest.skip("hello")
292                assert 0
293        """
294        )
295        p = testdir.makepyfile("""def test_func(x): pass""")
296        res = testdir.runpytest(p)
297        assert res.ret == 0
298        res.stdout.fnmatch_lines(["*1 skipped*"])
299
300    def test_direct_addressing_selects(self, testdir):
301        p = testdir.makepyfile(
302            """
303            def pytest_generate_tests(metafunc):
304                metafunc.addcall({'i': 1}, id="1")
305                metafunc.addcall({'i': 2}, id="2")
306            def test_func(i):
307                pass
308        """
309        )
310        res = testdir.runpytest(p.basename + "::" + "test_func[1]")
311        assert res.ret == 0
312        res.stdout.fnmatch_lines(["*1 passed*"])
313
314    def test_direct_addressing_notfound(self, testdir):
315        p = testdir.makepyfile(
316            """
317            def test_func():
318                pass
319        """
320        )
321        res = testdir.runpytest(p.basename + "::" + "test_notfound")
322        assert res.ret
323        res.stderr.fnmatch_lines(["*ERROR*not found*"])
324
325    def test_docstring_on_hookspec(self):
326        from _pytest import hookspec
327
328        for name, value in vars(hookspec).items():
329            if name.startswith("pytest_"):
330                assert value.__doc__, "no docstring for %s" % name
331
332    def test_initialization_error_issue49(self, testdir):
333        testdir.makeconftest(
334            """
335            def pytest_configure():
336                x
337        """
338        )
339        result = testdir.runpytest()
340        assert result.ret == 3  # internal error
341        result.stderr.fnmatch_lines(["INTERNAL*pytest_configure*", "INTERNAL*x*"])
342        assert "sessionstarttime" not in result.stderr.str()
343
344    @pytest.mark.parametrize("lookfor", ["test_fun.py::test_a"])
345    def test_issue134_report_error_when_collecting_member(self, testdir, lookfor):
346        testdir.makepyfile(
347            test_fun="""
348            def test_a():
349                pass
350            def"""
351        )
352        result = testdir.runpytest(lookfor)
353        result.stdout.fnmatch_lines(["*SyntaxError*"])
354        if "::" in lookfor:
355            result.stderr.fnmatch_lines(["*ERROR*"])
356            assert result.ret == 4  # usage error only if item not found
357
358    def test_report_all_failed_collections_initargs(self, testdir):
359        testdir.makepyfile(test_a="def", test_b="def")
360        result = testdir.runpytest("test_a.py::a", "test_b.py::b")
361        result.stderr.fnmatch_lines(["*ERROR*test_a.py::a*", "*ERROR*test_b.py::b*"])
362
363    @pytest.mark.usefixtures("recwarn")
364    def test_namespace_import_doesnt_confuse_import_hook(self, testdir):
365        """
366        Ref #383. Python 3.3's namespace package messed with our import hooks
367        Importing a module that didn't exist, even if the ImportError was
368        gracefully handled, would make our test crash.
369
370        Use recwarn here to silence this warning in Python 2.7:
371            ImportWarning: Not importing directory '...\not_a_package': missing __init__.py
372        """
373        testdir.mkdir("not_a_package")
374        p = testdir.makepyfile(
375            """
376            try:
377                from not_a_package import doesnt_exist
378            except ImportError:
379                # We handle the import error gracefully here
380                pass
381
382            def test_whatever():
383                pass
384        """
385        )
386        res = testdir.runpytest(p.basename)
387        assert res.ret == 0
388
389    def test_unknown_option(self, testdir):
390        result = testdir.runpytest("--qwlkej")
391        result.stderr.fnmatch_lines(
392            """
393            *unrecognized*
394        """
395        )
396
397    def test_getsourcelines_error_issue553(self, testdir, monkeypatch):
398        monkeypatch.setattr("inspect.getsourcelines", None)
399        p = testdir.makepyfile(
400            """
401            def raise_error(obj):
402                raise IOError('source code not available')
403
404            import inspect
405            inspect.getsourcelines = raise_error
406
407            def test_foo(invalid_fixture):
408                pass
409        """
410        )
411        res = testdir.runpytest(p)
412        res.stdout.fnmatch_lines(
413            ["*source code not available*", "E*fixture 'invalid_fixture' not found"]
414        )
415
416    def test_plugins_given_as_strings(self, tmpdir, monkeypatch):
417        """test that str values passed to main() as `plugins` arg
418        are interpreted as module names to be imported and registered.
419        #855.
420        """
421        with pytest.raises(ImportError) as excinfo:
422            pytest.main([str(tmpdir)], plugins=["invalid.module"])
423        assert "invalid" in str(excinfo.value)
424
425        p = tmpdir.join("test_test_plugins_given_as_strings.py")
426        p.write("def test_foo(): pass")
427        mod = types.ModuleType("myplugin")
428        monkeypatch.setitem(sys.modules, "myplugin", mod)
429        assert pytest.main(args=[str(tmpdir)], plugins=["myplugin"]) == 0
430
431    def test_parametrized_with_bytes_regex(self, testdir):
432        p = testdir.makepyfile(
433            """
434            import re
435            import pytest
436            @pytest.mark.parametrize('r', [re.compile(b'foo')])
437            def test_stuff(r):
438                pass
439        """
440        )
441        res = testdir.runpytest(p)
442        res.stdout.fnmatch_lines(["*1 passed*"])
443
444    def test_parametrized_with_null_bytes(self, testdir):
445        """Test parametrization with values that contain null bytes and unicode characters (#2644, #2957)"""
446        p = testdir.makepyfile(
447            u"""
448            # encoding: UTF-8
449            import pytest
450
451            @pytest.mark.parametrize("data", [b"\\x00", "\\x00", u'ação'])
452            def test_foo(data):
453                assert data
454        """
455        )
456        res = testdir.runpytest(p)
457        res.assert_outcomes(passed=3)
458
459
460class TestInvocationVariants(object):
461
462    def test_earlyinit(self, testdir):
463        p = testdir.makepyfile(
464            """
465            import pytest
466            assert hasattr(pytest, 'mark')
467        """
468        )
469        result = testdir.runpython(p)
470        assert result.ret == 0
471
472    @pytest.mark.xfail("sys.platform.startswith('java')")
473    def test_pydoc(self, testdir):
474        for name in ("py.test", "pytest"):
475            result = testdir.runpython_c("import %s;help(%s)" % (name, name))
476            assert result.ret == 0
477            s = result.stdout.str()
478            assert "MarkGenerator" in s
479
480    def test_import_star_py_dot_test(self, testdir):
481        p = testdir.makepyfile(
482            """
483            from py.test import *
484            #collect
485            #cmdline
486            #Item
487            # assert collect.Item is Item
488            # assert collect.Collector is Collector
489            main
490            skip
491            xfail
492        """
493        )
494        result = testdir.runpython(p)
495        assert result.ret == 0
496
497    def test_import_star_pytest(self, testdir):
498        p = testdir.makepyfile(
499            """
500            from pytest import *
501            #Item
502            #File
503            main
504            skip
505            xfail
506        """
507        )
508        result = testdir.runpython(p)
509        assert result.ret == 0
510
511    def test_double_pytestcmdline(self, testdir):
512        p = testdir.makepyfile(
513            run="""
514            import pytest
515            pytest.main()
516            pytest.main()
517        """
518        )
519        testdir.makepyfile(
520            """
521            def test_hello():
522                pass
523        """
524        )
525        result = testdir.runpython(p)
526        result.stdout.fnmatch_lines(["*1 passed*", "*1 passed*"])
527
528    def test_python_minus_m_invocation_ok(self, testdir):
529        p1 = testdir.makepyfile("def test_hello(): pass")
530        res = testdir.run(sys.executable, "-m", "pytest", str(p1))
531        assert res.ret == 0
532
533    def test_python_minus_m_invocation_fail(self, testdir):
534        p1 = testdir.makepyfile("def test_fail(): 0/0")
535        res = testdir.run(sys.executable, "-m", "pytest", str(p1))
536        assert res.ret == 1
537
538    def test_python_pytest_package(self, testdir):
539        p1 = testdir.makepyfile("def test_pass(): pass")
540        res = testdir.run(sys.executable, "-m", "pytest", str(p1))
541        assert res.ret == 0
542        res.stdout.fnmatch_lines(["*1 passed*"])
543
544    def test_equivalence_pytest_pytest(self):
545        assert pytest.main == py.test.cmdline.main
546
547    def test_invoke_with_string(self, capsys):
548        retcode = pytest.main("-h")
549        assert not retcode
550        out, err = capsys.readouterr()
551        assert "--help" in out
552        pytest.raises(ValueError, lambda: pytest.main(0))
553
554    def test_invoke_with_path(self, tmpdir, capsys):
555        retcode = pytest.main(tmpdir)
556        assert retcode == EXIT_NOTESTSCOLLECTED
557        out, err = capsys.readouterr()
558
559    def test_invoke_plugin_api(self, testdir, capsys):
560
561        class MyPlugin(object):
562
563            def pytest_addoption(self, parser):
564                parser.addoption("--myopt")
565
566        pytest.main(["-h"], plugins=[MyPlugin()])
567        out, err = capsys.readouterr()
568        assert "--myopt" in out
569
570    def test_pyargs_importerror(self, testdir, monkeypatch):
571        monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
572        path = testdir.mkpydir("tpkg")
573        path.join("test_hello.py").write("raise ImportError")
574
575        result = testdir.runpytest("--pyargs", "tpkg.test_hello", syspathinsert=True)
576        assert result.ret != 0
577
578        result.stdout.fnmatch_lines(["collected*0*items*/*1*errors"])
579
580    def test_cmdline_python_package(self, testdir, monkeypatch):
581        import warnings
582
583        monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
584        path = testdir.mkpydir("tpkg")
585        path.join("test_hello.py").write("def test_hello(): pass")
586        path.join("test_world.py").write("def test_world(): pass")
587        result = testdir.runpytest("--pyargs", "tpkg")
588        assert result.ret == 0
589        result.stdout.fnmatch_lines(["*2 passed*"])
590        result = testdir.runpytest("--pyargs", "tpkg.test_hello", syspathinsert=True)
591        assert result.ret == 0
592        result.stdout.fnmatch_lines(["*1 passed*"])
593
594        def join_pythonpath(what):
595            cur = os.environ.get("PYTHONPATH")
596            if cur:
597                return str(what) + os.pathsep + cur
598            return what
599
600        empty_package = testdir.mkpydir("empty_package")
601        monkeypatch.setenv("PYTHONPATH", join_pythonpath(empty_package))
602        # the path which is not a package raises a warning on pypy;
603        # no idea why only pypy and not normal python warn about it here
604        with warnings.catch_warnings():
605            warnings.simplefilter("ignore", ImportWarning)
606            result = testdir.runpytest("--pyargs", ".")
607        assert result.ret == 0
608        result.stdout.fnmatch_lines(["*2 passed*"])
609
610        monkeypatch.setenv("PYTHONPATH", join_pythonpath(testdir))
611        result = testdir.runpytest("--pyargs", "tpkg.test_missing", syspathinsert=True)
612        assert result.ret != 0
613        result.stderr.fnmatch_lines(["*not*found*test_missing*"])
614
615    def test_cmdline_python_namespace_package(self, testdir, monkeypatch):
616        """
617        test --pyargs option with namespace packages (#1567)
618        """
619        monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
620
621        search_path = []
622        for dirname in "hello", "world":
623            d = testdir.mkdir(dirname)
624            search_path.append(d)
625            ns = d.mkdir("ns_pkg")
626            ns.join("__init__.py").write(
627                "__import__('pkg_resources').declare_namespace(__name__)"
628            )
629            lib = ns.mkdir(dirname)
630            lib.ensure("__init__.py")
631            lib.join("test_{}.py".format(dirname)).write(
632                "def test_{}(): pass\n" "def test_other():pass".format(dirname)
633            )
634
635        # The structure of the test directory is now:
636        # .
637        # ├── hello
638        # │   └── ns_pkg
639        # │       ├── __init__.py
640        # │       └── hello
641        # │           ├── __init__.py
642        # │           └── test_hello.py
643        # └── world
644        #     └── ns_pkg
645        #         ├── __init__.py
646        #         └── world
647        #             ├── __init__.py
648        #             └── test_world.py
649
650        def join_pythonpath(*dirs):
651            cur = os.environ.get("PYTHONPATH")
652            if cur:
653                dirs += (cur,)
654            return os.pathsep.join(str(p) for p in dirs)
655
656        monkeypatch.setenv("PYTHONPATH", join_pythonpath(*search_path))
657        for p in search_path:
658            monkeypatch.syspath_prepend(p)
659
660        # mixed module and filenames:
661        os.chdir("world")
662        result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "ns_pkg/world")
663        assert result.ret == 0
664        result.stdout.fnmatch_lines(
665            [
666                "*test_hello.py::test_hello*PASSED*",
667                "*test_hello.py::test_other*PASSED*",
668                "*test_world.py::test_world*PASSED*",
669                "*test_world.py::test_other*PASSED*",
670                "*4 passed*",
671            ]
672        )
673
674        # specify tests within a module
675        testdir.chdir()
676        result = testdir.runpytest(
677            "--pyargs", "-v", "ns_pkg.world.test_world::test_other"
678        )
679        assert result.ret == 0
680        result.stdout.fnmatch_lines(
681            ["*test_world.py::test_other*PASSED*", "*1 passed*"]
682        )
683
684    @pytest.mark.skipif(not hasattr(os, "symlink"), reason="requires symlinks")
685    def test_cmdline_python_package_symlink(self, testdir, monkeypatch):
686        """
687        test --pyargs option with packages with path containing symlink can
688        have conftest.py in their package (#2985)
689        """
690        # dummy check that we can actually create symlinks: on Windows `os.symlink` is available,
691        # but normal users require special admin privileges to create symlinks.
692        if sys.platform == "win32":
693            try:
694                os.symlink(
695                    str(testdir.tmpdir.ensure("tmpfile")),
696                    str(testdir.tmpdir.join("tmpfile2")),
697                )
698            except OSError as e:
699                pytest.skip(six.text_type(e.args[0]))
700        monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
701
702        search_path = ["lib", os.path.join("local", "lib")]
703
704        dirname = "lib"
705        d = testdir.mkdir(dirname)
706        foo = d.mkdir("foo")
707        foo.ensure("__init__.py")
708        lib = foo.mkdir("bar")
709        lib.ensure("__init__.py")
710        lib.join("test_bar.py").write(
711            "def test_bar(): pass\n" "def test_other(a_fixture):pass"
712        )
713        lib.join("conftest.py").write(
714            "import pytest\n" "@pytest.fixture\n" "def a_fixture():pass"
715        )
716
717        d_local = testdir.mkdir("local")
718        symlink_location = os.path.join(str(d_local), "lib")
719        if six.PY2:
720            os.symlink(str(d), symlink_location)
721        else:
722            os.symlink(str(d), symlink_location, target_is_directory=True)
723
724        # The structure of the test directory is now:
725        # .
726        # ├── local
727        # │   └── lib -> ../lib
728        # └── lib
729        #     └── foo
730        #         ├── __init__.py
731        #         └── bar
732        #             ├── __init__.py
733        #             ├── conftest.py
734        #             └── test_bar.py
735
736        def join_pythonpath(*dirs):
737            cur = os.getenv("PYTHONPATH")
738            if cur:
739                dirs += (cur,)
740            return os.pathsep.join(str(p) for p in dirs)
741
742        monkeypatch.setenv("PYTHONPATH", join_pythonpath(*search_path))
743        for p in search_path:
744            monkeypatch.syspath_prepend(p)
745
746        # module picked up in symlink-ed directory:
747        result = testdir.runpytest("--pyargs", "-v", "foo.bar")
748        testdir.chdir()
749        assert result.ret == 0
750        result.stdout.fnmatch_lines(
751            [
752                "*lib/foo/bar/test_bar.py::test_bar*PASSED*",
753                "*lib/foo/bar/test_bar.py::test_other*PASSED*",
754                "*2 passed*",
755            ]
756        )
757
758    def test_cmdline_python_package_not_exists(self, testdir):
759        result = testdir.runpytest("--pyargs", "tpkgwhatv")
760        assert result.ret
761        result.stderr.fnmatch_lines(["ERROR*file*or*package*not*found*"])
762
763    @pytest.mark.xfail(reason="decide: feature or bug")
764    def test_noclass_discovery_if_not_testcase(self, testdir):
765        testpath = testdir.makepyfile(
766            """
767            import unittest
768            class TestHello(object):
769                def test_hello(self):
770                    assert self.attr
771
772            class RealTest(unittest.TestCase, TestHello):
773                attr = 42
774        """
775        )
776        reprec = testdir.inline_run(testpath)
777        reprec.assertoutcome(passed=1)
778
779    def test_doctest_id(self, testdir):
780        testdir.makefile(
781            ".txt",
782            """
783            >>> x=3
784            >>> x
785            4
786        """,
787        )
788        result = testdir.runpytest("-rf")
789        lines = result.stdout.str().splitlines()
790        for line in lines:
791            if line.startswith("FAIL "):
792                testid = line[5:].strip()
793                break
794        result = testdir.runpytest(testid, "-rf")
795        result.stdout.fnmatch_lines([line, "*1 failed*"])
796
797    def test_core_backward_compatibility(self):
798        """Test backward compatibility for get_plugin_manager function. See #787."""
799        import _pytest.config
800
801        assert type(
802            _pytest.config.get_plugin_manager()
803        ) is _pytest.config.PytestPluginManager
804
805    def test_has_plugin(self, request):
806        """Test hasplugin function of the plugin manager (#932)."""
807        assert request.config.pluginmanager.hasplugin("python")
808
809
810class TestDurations(object):
811    source = """
812        import time
813        frag = 0.002
814        def test_something():
815            pass
816        def test_2():
817            time.sleep(frag*5)
818        def test_1():
819            time.sleep(frag)
820        def test_3():
821            time.sleep(frag*10)
822    """
823
824    def test_calls(self, testdir):
825        testdir.makepyfile(self.source)
826        result = testdir.runpytest("--durations=10")
827        assert result.ret == 0
828        result.stdout.fnmatch_lines_random(
829            ["*durations*", "*call*test_3*", "*call*test_2*", "*call*test_1*"]
830        )
831
832    def test_calls_show_2(self, testdir):
833        testdir.makepyfile(self.source)
834        result = testdir.runpytest("--durations=2")
835        assert result.ret == 0
836        lines = result.stdout.get_lines_after("*slowest*durations*")
837        assert "4 passed" in lines[2]
838
839    def test_calls_showall(self, testdir):
840        testdir.makepyfile(self.source)
841        result = testdir.runpytest("--durations=0")
842        assert result.ret == 0
843        for x in "123":
844            for y in ("call",):  # 'setup', 'call', 'teardown':
845                for line in result.stdout.lines:
846                    if ("test_%s" % x) in line and y in line:
847                        break
848                else:
849                    raise AssertionError("not found %s %s" % (x, y))
850
851    def test_with_deselected(self, testdir):
852        testdir.makepyfile(self.source)
853        result = testdir.runpytest("--durations=2", "-k test_1")
854        assert result.ret == 0
855        result.stdout.fnmatch_lines(["*durations*", "*call*test_1*"])
856
857    def test_with_failing_collection(self, testdir):
858        testdir.makepyfile(self.source)
859        testdir.makepyfile(test_collecterror="""xyz""")
860        result = testdir.runpytest("--durations=2", "-k test_1")
861        assert result.ret == 2
862        result.stdout.fnmatch_lines(["*Interrupted: 1 errors during collection*"])
863        # Collection errors abort test execution, therefore no duration is
864        # output
865        assert "duration" not in result.stdout.str()
866
867    def test_with_not(self, testdir):
868        testdir.makepyfile(self.source)
869        result = testdir.runpytest("-k not 1")
870        assert result.ret == 0
871
872
873class TestDurationWithFixture(object):
874    source = """
875        import time
876        frag = 0.001
877        def setup_function(func):
878            time.sleep(frag * 3)
879        def test_1():
880            time.sleep(frag*2)
881        def test_2():
882            time.sleep(frag)
883    """
884
885    def test_setup_function(self, testdir):
886        testdir.makepyfile(self.source)
887        result = testdir.runpytest("--durations=10")
888        assert result.ret == 0
889
890        result.stdout.fnmatch_lines_random(
891            """
892            *durations*
893            * setup *test_1*
894            * call *test_1*
895        """
896        )
897
898
899def test_zipimport_hook(testdir, tmpdir):
900    """Test package loader is being used correctly (see #1837)."""
901    zipapp = pytest.importorskip("zipapp")
902    testdir.tmpdir.join("app").ensure(dir=1)
903    testdir.makepyfile(
904        **{
905            "app/foo.py": """
906            import pytest
907            def main():
908                pytest.main(['--pyarg', 'foo'])
909        """
910        }
911    )
912    target = tmpdir.join("foo.zip")
913    zipapp.create_archive(str(testdir.tmpdir.join("app")), str(target), main="foo:main")
914    result = testdir.runpython(target)
915    assert result.ret == 0
916    result.stderr.fnmatch_lines(["*not found*foo*"])
917    assert "INTERNALERROR>" not in result.stdout.str()
918
919
920def test_import_plugin_unicode_name(testdir):
921    testdir.makepyfile(myplugin="")
922    testdir.makepyfile(
923        """
924        def test(): pass
925    """
926    )
927    testdir.makeconftest(
928        """
929        pytest_plugins = [u'myplugin']
930    """
931    )
932    r = testdir.runpytest()
933    assert r.ret == 0
934
935
936def test_deferred_hook_checking(testdir):
937    """
938    Check hooks as late as possible (#1821).
939    """
940    testdir.syspathinsert()
941    testdir.makepyfile(
942        **{
943            "plugin.py": """
944        class Hooks(object):
945            def pytest_my_hook(self, config):
946                pass
947
948        def pytest_configure(config):
949            config.pluginmanager.add_hookspecs(Hooks)
950        """,
951            "conftest.py": """
952            pytest_plugins = ['plugin']
953            def pytest_my_hook(config):
954                return 40
955        """,
956            "test_foo.py": """
957            def test(request):
958                assert request.config.hook.pytest_my_hook(config=request.config) == [40]
959        """,
960        }
961    )
962    result = testdir.runpytest()
963    result.stdout.fnmatch_lines(["* 1 passed *"])
964
965
966def test_fixture_values_leak(testdir):
967    """Ensure that fixture objects are properly destroyed by the garbage collector at the end of their expected
968    life-times (#2981).
969    """
970    testdir.makepyfile(
971        """
972        import attr
973        import gc
974        import pytest
975        import weakref
976
977        @attr.s
978        class SomeObj(object):
979            name = attr.ib()
980
981        fix_of_test1_ref = None
982        session_ref = None
983
984        @pytest.fixture(scope='session')
985        def session_fix():
986            global session_ref
987            obj = SomeObj(name='session-fixture')
988            session_ref = weakref.ref(obj)
989            return obj
990
991        @pytest.fixture
992        def fix(session_fix):
993            global fix_of_test1_ref
994            obj = SomeObj(name='local-fixture')
995            fix_of_test1_ref = weakref.ref(obj)
996            return obj
997
998        def test1(fix):
999            assert fix_of_test1_ref() is fix
1000
1001        def test2():
1002            gc.collect()
1003            # fixture "fix" created during test1 must have been destroyed by now
1004            assert fix_of_test1_ref() is None
1005    """
1006    )
1007    result = testdir.runpytest()
1008    result.stdout.fnmatch_lines(["* 2 passed *"])
1009
1010
1011def test_fixture_order_respects_scope(testdir):
1012    """Ensure that fixtures are created according to scope order, regression test for #2405
1013    """
1014    testdir.makepyfile(
1015        """
1016        import pytest
1017
1018        data = {}
1019
1020        @pytest.fixture(scope='module')
1021        def clean_data():
1022            data.clear()
1023
1024        @pytest.fixture(autouse=True)
1025        def add_data():
1026            data.update(value=True)
1027
1028        @pytest.mark.usefixtures('clean_data')
1029        def test_value():
1030            assert data.get('value')
1031    """
1032    )
1033    result = testdir.runpytest()
1034    assert result.ret == 0
1035
1036
1037def test_frame_leak_on_failing_test(testdir):
1038    """pytest would leak garbage referencing the frames of tests that failed that could never be reclaimed (#2798)
1039
1040    Unfortunately it was not possible to remove the actual circles because most of them
1041    are made of traceback objects which cannot be weakly referenced. Those objects at least
1042    can be eventually claimed by the garbage collector.
1043    """
1044    testdir.makepyfile(
1045        """
1046        import gc
1047        import weakref
1048
1049        class Obj:
1050            pass
1051
1052        ref = None
1053
1054        def test1():
1055            obj = Obj()
1056            global ref
1057            ref = weakref.ref(obj)
1058            assert 0
1059
1060        def test2():
1061            gc.collect()
1062            assert ref() is None
1063    """
1064    )
1065    result = testdir.runpytest_subprocess()
1066    result.stdout.fnmatch_lines(["*1 failed, 1 passed in*"])
1067