1from __future__ import absolute_import, division, print_function 2from textwrap import dedent 3 4import _pytest._code 5import py 6import pytest 7from _pytest.config import PytestPluginManager 8from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR 9 10 11@pytest.fixture(scope="module", params=["global", "inpackage"]) 12def basedir(request, tmpdir_factory): 13 from _pytest.tmpdir import tmpdir 14 15 tmpdir = tmpdir(request, tmpdir_factory) 16 tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3") 17 tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") 18 if request.param == "inpackage": 19 tmpdir.ensure("adir/__init__.py") 20 tmpdir.ensure("adir/b/__init__.py") 21 return tmpdir 22 23 24def ConftestWithSetinitial(path): 25 conftest = PytestPluginManager() 26 conftest_setinitial(conftest, [path]) 27 return conftest 28 29 30def conftest_setinitial(conftest, args, confcutdir=None): 31 32 class Namespace(object): 33 34 def __init__(self): 35 self.file_or_dir = args 36 self.confcutdir = str(confcutdir) 37 self.noconftest = False 38 39 conftest._set_initial_conftests(Namespace()) 40 41 42class TestConftestValueAccessGlobal(object): 43 44 def test_basic_init(self, basedir): 45 conftest = PytestPluginManager() 46 p = basedir.join("adir") 47 assert conftest._rget_with_confmod("a", p)[1] == 1 48 49 def test_immediate_initialiation_and_incremental_are_the_same(self, basedir): 50 conftest = PytestPluginManager() 51 len(conftest._path2confmods) 52 conftest._getconftestmodules(basedir) 53 snap1 = len(conftest._path2confmods) 54 # assert len(conftest._path2confmods) == snap1 + 1 55 conftest._getconftestmodules(basedir.join("adir")) 56 assert len(conftest._path2confmods) == snap1 + 1 57 conftest._getconftestmodules(basedir.join("b")) 58 assert len(conftest._path2confmods) == snap1 + 2 59 60 def test_value_access_not_existing(self, basedir): 61 conftest = ConftestWithSetinitial(basedir) 62 with pytest.raises(KeyError): 63 conftest._rget_with_confmod("a", basedir) 64 65 def test_value_access_by_path(self, basedir): 66 conftest = ConftestWithSetinitial(basedir) 67 adir = basedir.join("adir") 68 assert conftest._rget_with_confmod("a", adir)[1] == 1 69 assert conftest._rget_with_confmod("a", adir.join("b"))[1] == 1.5 70 71 def test_value_access_with_confmod(self, basedir): 72 startdir = basedir.join("adir", "b") 73 startdir.ensure("xx", dir=True) 74 conftest = ConftestWithSetinitial(startdir) 75 mod, value = conftest._rget_with_confmod("a", startdir) 76 assert value == 1.5 77 path = py.path.local(mod.__file__) 78 assert path.dirpath() == basedir.join("adir", "b") 79 assert path.purebasename.startswith("conftest") 80 81 82def test_conftest_in_nonpkg_with_init(tmpdir): 83 tmpdir.ensure("adir-1.0/conftest.py").write("a=1 ; Directory = 3") 84 tmpdir.ensure("adir-1.0/b/conftest.py").write("b=2 ; a = 1.5") 85 tmpdir.ensure("adir-1.0/b/__init__.py") 86 tmpdir.ensure("adir-1.0/__init__.py") 87 ConftestWithSetinitial(tmpdir.join("adir-1.0", "b")) 88 89 90def test_doubledash_considered(testdir): 91 conf = testdir.mkdir("--option") 92 conf.ensure("conftest.py") 93 conftest = PytestPluginManager() 94 conftest_setinitial(conftest, [conf.basename, conf.basename]) 95 values = conftest._getconftestmodules(conf) 96 assert len(values) == 1 97 98 99def test_issue151_load_all_conftests(testdir): 100 names = "code proj src".split() 101 for name in names: 102 p = testdir.mkdir(name) 103 p.ensure("conftest.py") 104 105 conftest = PytestPluginManager() 106 conftest_setinitial(conftest, names) 107 d = list(conftest._conftestpath2mod.values()) 108 assert len(d) == len(names) 109 110 111def test_conftest_global_import(testdir): 112 testdir.makeconftest("x=3") 113 p = testdir.makepyfile( 114 """ 115 import py, pytest 116 from _pytest.config import PytestPluginManager 117 conf = PytestPluginManager() 118 mod = conf._importconftest(py.path.local("conftest.py")) 119 assert mod.x == 3 120 import conftest 121 assert conftest is mod, (conftest, mod) 122 subconf = py.path.local().ensure("sub", "conftest.py") 123 subconf.write("y=4") 124 mod2 = conf._importconftest(subconf) 125 assert mod != mod2 126 assert mod2.y == 4 127 import conftest 128 assert conftest is mod2, (conftest, mod) 129 """ 130 ) 131 res = testdir.runpython(p) 132 assert res.ret == 0 133 134 135def test_conftestcutdir(testdir): 136 conf = testdir.makeconftest("") 137 p = testdir.mkdir("x") 138 conftest = PytestPluginManager() 139 conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p) 140 values = conftest._getconftestmodules(p) 141 assert len(values) == 0 142 values = conftest._getconftestmodules(conf.dirpath()) 143 assert len(values) == 0 144 assert conf not in conftest._conftestpath2mod 145 # but we can still import a conftest directly 146 conftest._importconftest(conf) 147 values = conftest._getconftestmodules(conf.dirpath()) 148 assert values[0].__file__.startswith(str(conf)) 149 # and all sub paths get updated properly 150 values = conftest._getconftestmodules(p) 151 assert len(values) == 1 152 assert values[0].__file__.startswith(str(conf)) 153 154 155def test_conftestcutdir_inplace_considered(testdir): 156 conf = testdir.makeconftest("") 157 conftest = PytestPluginManager() 158 conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath()) 159 values = conftest._getconftestmodules(conf.dirpath()) 160 assert len(values) == 1 161 assert values[0].__file__.startswith(str(conf)) 162 163 164@pytest.mark.parametrize("name", "test tests whatever .dotdir".split()) 165def test_setinitial_conftest_subdirs(testdir, name): 166 sub = testdir.mkdir(name) 167 subconftest = sub.ensure("conftest.py") 168 conftest = PytestPluginManager() 169 conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir) 170 if name not in ("whatever", ".dotdir"): 171 assert subconftest in conftest._conftestpath2mod 172 assert len(conftest._conftestpath2mod) == 1 173 else: 174 assert subconftest not in conftest._conftestpath2mod 175 assert len(conftest._conftestpath2mod) == 0 176 177 178def test_conftest_confcutdir(testdir): 179 testdir.makeconftest("assert 0") 180 x = testdir.mkdir("x") 181 x.join("conftest.py").write( 182 _pytest._code.Source( 183 """ 184 def pytest_addoption(parser): 185 parser.addoption("--xyz", action="store_true") 186 """ 187 ) 188 ) 189 result = testdir.runpytest("-h", "--confcutdir=%s" % x, x) 190 result.stdout.fnmatch_lines(["*--xyz*"]) 191 assert "warning: could not load initial" not in result.stdout.str() 192 193 194def test_no_conftest(testdir): 195 testdir.makeconftest("assert 0") 196 result = testdir.runpytest("--noconftest") 197 assert result.ret == EXIT_NOTESTSCOLLECTED 198 199 result = testdir.runpytest() 200 assert result.ret == EXIT_USAGEERROR 201 202 203def test_conftest_existing_resultlog(testdir): 204 x = testdir.mkdir("tests") 205 x.join("conftest.py").write( 206 _pytest._code.Source( 207 """ 208 def pytest_addoption(parser): 209 parser.addoption("--xyz", action="store_true") 210 """ 211 ) 212 ) 213 testdir.makefile(ext=".log", result="") # Writes result.log 214 result = testdir.runpytest("-h", "--resultlog", "result.log") 215 result.stdout.fnmatch_lines(["*--xyz*"]) 216 217 218def test_conftest_existing_junitxml(testdir): 219 x = testdir.mkdir("tests") 220 x.join("conftest.py").write( 221 _pytest._code.Source( 222 """ 223 def pytest_addoption(parser): 224 parser.addoption("--xyz", action="store_true") 225 """ 226 ) 227 ) 228 testdir.makefile(ext=".xml", junit="") # Writes junit.xml 229 result = testdir.runpytest("-h", "--junitxml", "junit.xml") 230 result.stdout.fnmatch_lines(["*--xyz*"]) 231 232 233def test_conftest_import_order(testdir, monkeypatch): 234 ct1 = testdir.makeconftest("") 235 sub = testdir.mkdir("sub") 236 ct2 = sub.join("conftest.py") 237 ct2.write("") 238 239 def impct(p): 240 return p 241 242 conftest = PytestPluginManager() 243 conftest._confcutdir = testdir.tmpdir 244 monkeypatch.setattr(conftest, "_importconftest", impct) 245 assert conftest._getconftestmodules(sub) == [ct1, ct2] 246 247 248def test_fixture_dependency(testdir, monkeypatch): 249 ct1 = testdir.makeconftest("") 250 ct1 = testdir.makepyfile("__init__.py") 251 ct1.write("") 252 sub = testdir.mkdir("sub") 253 sub.join("__init__.py").write("") 254 sub.join("conftest.py").write( 255 dedent( 256 """ 257 import pytest 258 259 @pytest.fixture 260 def not_needed(): 261 assert False, "Should not be called!" 262 263 @pytest.fixture 264 def foo(): 265 assert False, "Should not be called!" 266 267 @pytest.fixture 268 def bar(foo): 269 return 'bar' 270 """ 271 ) 272 ) 273 subsub = sub.mkdir("subsub") 274 subsub.join("__init__.py").write("") 275 subsub.join("test_bar.py").write( 276 dedent( 277 """ 278 import pytest 279 280 @pytest.fixture 281 def bar(): 282 return 'sub bar' 283 284 def test_event_fixture(bar): 285 assert bar == 'sub bar' 286 """ 287 ) 288 ) 289 result = testdir.runpytest("sub") 290 result.stdout.fnmatch_lines(["*1 passed*"]) 291 292 293def test_conftest_found_with_double_dash(testdir): 294 sub = testdir.mkdir("sub") 295 sub.join("conftest.py").write( 296 dedent( 297 """ 298 def pytest_addoption(parser): 299 parser.addoption("--hello-world", action="store_true") 300 """ 301 ) 302 ) 303 p = sub.join("test_hello.py") 304 p.write("def test_hello(): pass") 305 result = testdir.runpytest(str(p) + "::test_hello", "-h") 306 result.stdout.fnmatch_lines( 307 """ 308 *--hello-world* 309 """ 310 ) 311 312 313class TestConftestVisibility(object): 314 315 def _setup_tree(self, testdir): # for issue616 316 # example mostly taken from: 317 # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html 318 runner = testdir.mkdir("empty") 319 package = testdir.mkdir("package") 320 321 package.join("conftest.py").write( 322 dedent( 323 """\ 324 import pytest 325 @pytest.fixture 326 def fxtr(): 327 return "from-package" 328 """ 329 ) 330 ) 331 package.join("test_pkgroot.py").write( 332 dedent( 333 """\ 334 def test_pkgroot(fxtr): 335 assert fxtr == "from-package" 336 """ 337 ) 338 ) 339 340 swc = package.mkdir("swc") 341 swc.join("__init__.py").ensure() 342 swc.join("conftest.py").write( 343 dedent( 344 """\ 345 import pytest 346 @pytest.fixture 347 def fxtr(): 348 return "from-swc" 349 """ 350 ) 351 ) 352 swc.join("test_with_conftest.py").write( 353 dedent( 354 """\ 355 def test_with_conftest(fxtr): 356 assert fxtr == "from-swc" 357 358 """ 359 ) 360 ) 361 362 snc = package.mkdir("snc") 363 snc.join("__init__.py").ensure() 364 snc.join("test_no_conftest.py").write( 365 dedent( 366 """\ 367 def test_no_conftest(fxtr): 368 assert fxtr == "from-package" # No local conftest.py, so should 369 # use value from parent dir's 370 371 """ 372 ) 373 ) 374 print("created directory structure:") 375 for x in testdir.tmpdir.visit(): 376 print(" " + x.relto(testdir.tmpdir)) 377 378 return {"runner": runner, "package": package, "swc": swc, "snc": snc} 379 380 # N.B.: "swc" stands for "subdir with conftest.py" 381 # "snc" stands for "subdir no [i.e. without] conftest.py" 382 @pytest.mark.parametrize( 383 "chdir,testarg,expect_ntests_passed", 384 [ 385 # Effective target: package/.. 386 ("runner", "..", 3), 387 ("package", "..", 3), 388 ("swc", "../..", 3), 389 ("snc", "../..", 3), 390 # Effective target: package 391 ("runner", "../package", 3), 392 ("package", ".", 3), 393 ("swc", "..", 3), 394 ("snc", "..", 3), 395 # Effective target: package/swc 396 ("runner", "../package/swc", 1), 397 ("package", "./swc", 1), 398 ("swc", ".", 1), 399 ("snc", "../swc", 1), 400 # Effective target: package/snc 401 ("runner", "../package/snc", 1), 402 ("package", "./snc", 1), 403 ("swc", "../snc", 1), 404 ("snc", ".", 1), 405 ], 406 ) 407 @pytest.mark.issue616 408 def test_parsefactories_relative_node_ids( 409 self, testdir, chdir, testarg, expect_ntests_passed 410 ): 411 dirs = self._setup_tree(testdir) 412 print("pytest run in cwd: %s" % (dirs[chdir].relto(testdir.tmpdir))) 413 print("pytestarg : %s" % (testarg)) 414 print("expected pass : %s" % (expect_ntests_passed)) 415 with dirs[chdir].as_cwd(): 416 reprec = testdir.inline_run(testarg, "-q", "--traceconfig") 417 reprec.assertoutcome(passed=expect_ntests_passed) 418 419 420@pytest.mark.parametrize( 421 "confcutdir,passed,error", [(".", 2, 0), ("src", 1, 1), (None, 1, 1)] 422) 423def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error): 424 """Test that conftest files are detected only up to an ini file, unless 425 an explicit --confcutdir option is given. 426 """ 427 root = testdir.tmpdir 428 src = root.join("src").ensure(dir=1) 429 src.join("pytest.ini").write("[pytest]") 430 src.join("conftest.py").write( 431 _pytest._code.Source( 432 """ 433 import pytest 434 @pytest.fixture 435 def fix1(): pass 436 """ 437 ) 438 ) 439 src.join("test_foo.py").write( 440 _pytest._code.Source( 441 """ 442 def test_1(fix1): 443 pass 444 def test_2(out_of_reach): 445 pass 446 """ 447 ) 448 ) 449 root.join("conftest.py").write( 450 _pytest._code.Source( 451 """ 452 import pytest 453 @pytest.fixture 454 def out_of_reach(): pass 455 """ 456 ) 457 ) 458 459 args = [str(src)] 460 if confcutdir: 461 args = ["--confcutdir=%s" % root.join(confcutdir)] 462 result = testdir.runpytest(*args) 463 match = "" 464 if passed: 465 match += "*%d passed*" % passed 466 if error: 467 match += "*%d error*" % error 468 result.stdout.fnmatch_lines(match) 469 470 471def test_issue1073_conftest_special_objects(testdir): 472 testdir.makeconftest( 473 """ 474 class DontTouchMe(object): 475 def __getattr__(self, x): 476 raise Exception('cant touch me') 477 478 x = DontTouchMe() 479 """ 480 ) 481 testdir.makepyfile( 482 """ 483 def test_some(): 484 pass 485 """ 486 ) 487 res = testdir.runpytest() 488 assert res.ret == 0 489 490 491def test_conftest_exception_handling(testdir): 492 testdir.makeconftest( 493 """ 494 raise ValueError() 495 """ 496 ) 497 testdir.makepyfile( 498 """ 499 def test_some(): 500 pass 501 """ 502 ) 503 res = testdir.runpytest() 504 assert res.ret == 4 505 assert "raise ValueError()" in [line.strip() for line in res.errlines] 506 507 508def test_hook_proxy(testdir): 509 """Session's gethookproxy() would cache conftests incorrectly (#2016). 510 It was decided to remove the cache altogether. 511 """ 512 testdir.makepyfile( 513 **{ 514 "root/demo-0/test_foo1.py": "def test1(): pass", 515 "root/demo-a/test_foo2.py": "def test1(): pass", 516 "root/demo-a/conftest.py": """ 517 def pytest_ignore_collect(path, config): 518 return True 519 """, 520 "root/demo-b/test_foo3.py": "def test1(): pass", 521 "root/demo-c/test_foo4.py": "def test1(): pass", 522 } 523 ) 524 result = testdir.runpytest() 525 result.stdout.fnmatch_lines( 526 ["*test_foo1.py*", "*test_foo3.py*", "*test_foo4.py*", "*3 passed*"] 527 ) 528 529 530def test_required_option_help(testdir): 531 testdir.makeconftest("assert 0") 532 x = testdir.mkdir("x") 533 x.join("conftest.py").write( 534 _pytest._code.Source( 535 """ 536 def pytest_addoption(parser): 537 parser.addoption("--xyz", action="store_true", required=True) 538 """ 539 ) 540 ) 541 result = testdir.runpytest("-h", x) 542 assert "argument --xyz is required" not in result.stdout.str() 543 assert "general:" in result.stdout.str() 544