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