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