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