[Pytest-commit] commit/pytest: RonnyPfannschmidt: Merged in issue616 (pull request #252)

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Sat Feb 28 10:03:03 CET 2015


1 new commit in pytest:

https://bitbucket.org/pytest-dev/pytest/commits/9c7946ebb69c/
Changeset:   9c7946ebb69c
User:        RonnyPfannschmidt
Date:        2015-02-28 09:02:58+00:00
Summary:     Merged in issue616 (pull request #252)

fix issue616 - conftest visibility fixes.
Affected #:  13 files

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,20 @@
 2.7.0.dev (compared to 2.6.4)
 -----------------------------
 
+- fix issue616: conftest.py files and their contained fixutres are now 
+  properly considered for visibility, independently from the exact
+  current working directory and test arguments that are used.
+  Many thanks to Eric Siegerman and his PR235 which contains 
+  systematic tests for conftest visibility and now passes.
+  This change also introduces the concept of a ``rootdir`` which
+  is printed as a new pytest header and documented in the pytest
+  customize web page.
+
+- change reporting of "diverted" tests, i.e. tests that are collected
+  in one file but actually come from another (e.g. when tests in a test class
+  come from a base class in a different file).  We now show the nodeid 
+  and indicate via a postfix the other file.
+
 - add ability to set command line options by environment variable PYTEST_ADDOPTS.
 
 - added documentation on the new pytest-dev teams on bitbucket and

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -657,6 +657,12 @@
                 sys.stderr.write("INTERNALERROR> %s\n" %line)
                 sys.stderr.flush()
 
+    def cwd_relative_nodeid(self, nodeid):
+        # nodeid's are relative to the rootpath, compute relative to cwd
+        if self.invocation_dir != self.rootdir:
+            fullpath = self.rootdir.join(nodeid)
+            nodeid = self.invocation_dir.bestrelpath(fullpath)
+        return nodeid
 
     @classmethod
     def fromdictargs(cls, option_dict, args):
@@ -691,14 +697,9 @@
 
     def _initini(self, args):
         parsed_args = self._parser.parse_known_args(args)
-        if parsed_args.inifilename:
-            iniconfig = py.iniconfig.IniConfig(parsed_args.inifilename)
-            if 'pytest' in iniconfig.sections:
-                self.inicfg = iniconfig['pytest']
-            else:
-                self.inicfg = {}
-        else:
-            self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
+        r = determine_setup(parsed_args.inifilename, parsed_args.file_or_dir)
+        self.rootdir, self.inifile, self.inicfg = r
+        self.invocation_dir = py.path.local()
         self._parser.addini('addopts', 'extra command line options', 'args')
         self._parser.addini('minversion', 'minimally required pytest version')
 
@@ -859,8 +860,58 @@
                 if exists(p):
                     iniconfig = py.iniconfig.IniConfig(p)
                     if 'pytest' in iniconfig.sections:
-                        return iniconfig['pytest']
-    return {}
+                        return base, p, iniconfig['pytest']
+                    elif inibasename == "pytest.ini":
+                        # allowed to be empty
+                        return base, p, {}
+    return None, None, None
+
+
+def get_common_ancestor(args):
+    # args are what we get after early command line parsing (usually
+    # strings, but can be py.path.local objects as well)
+    common_ancestor = None
+    for arg in args:
+        if str(arg)[0] == "-":
+            continue
+        p = py.path.local(arg)
+        if common_ancestor is None:
+            common_ancestor = p
+        else:
+            if p.relto(common_ancestor) or p == common_ancestor:
+                continue
+            elif common_ancestor.relto(p):
+                common_ancestor = p
+            else:
+                shared = p.common(common_ancestor)
+                if shared is not None:
+                    common_ancestor = shared
+    if common_ancestor is None:
+        common_ancestor = py.path.local()
+    elif not common_ancestor.isdir():
+        common_ancestor = common_ancestor.dirpath()
+    return common_ancestor
+
+
+def determine_setup(inifile, args):
+    if inifile:
+        iniconfig = py.iniconfig.IniConfig(inifile)
+        try:
+            inicfg = iniconfig["pytest"]
+        except KeyError:
+            inicfg = None
+        rootdir = get_common_ancestor(args)
+    else:
+        ancestor = get_common_ancestor(args)
+        rootdir, inifile, inicfg = getcfg(
+            [ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
+        if rootdir is None:
+            for rootdir in ancestor.parts(reverse=True):
+                if rootdir.join("setup.py").exists():
+                    break
+            else:
+                rootdir = ancestor
+    return rootdir, inifile, inicfg or {}
 
 
 def setns(obj, dic):

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -457,9 +457,7 @@
         self.fspath = fspath
 
     def _makeid(self):
-        if self == self.session:
-            return "."
-        relpath = self.session.fspath.bestrelpath(self.fspath)
+        relpath = self.fspath.relto(self.config.rootdir)
         if os.sep != "/":
             relpath = relpath.replace(os.sep, "/")
         return relpath
@@ -510,7 +508,7 @@
         __module__ = 'builtins' # for py3
 
     def __init__(self, config):
-        FSCollector.__init__(self, py.path.local(), parent=None,
+        FSCollector.__init__(self, config.rootdir, parent=None,
                              config=config, session=self)
         self.config.pluginmanager.register(self, name="session", prepend=True)
         self._testsfailed = 0
@@ -520,6 +518,9 @@
         self.startdir = py.path.local()
         self._fs2hookproxy = {}
 
+    def _makeid(self):
+        return ""
+
     def pytest_collectstart(self):
         if self.shouldstop:
             raise self.Interrupted(self.shouldstop)
@@ -663,7 +664,7 @@
             arg = self._tryconvertpyarg(arg)
         parts = str(arg).split("::")
         relpath = parts[0].replace("/", os.sep)
-        path = self.fspath.join(relpath, abs=True)
+        path = self.config.invocation_dir.join(relpath, abs=True)
         if not path.check():
             if self.config.option.pyargs:
                 msg = "file or package not found: "

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d _pytest/pytester.py
--- a/_pytest/pytester.py
+++ b/_pytest/pytester.py
@@ -306,9 +306,8 @@
         session = Session(config)
         assert '::' not in str(arg)
         p = py.path.local(arg)
-        x = session.fspath.bestrelpath(p)
         config.hook.pytest_sessionstart(session=session)
-        res = session.perform_collect([x], genitems=False)[0]
+        res = session.perform_collect([str(p)], genitems=False)[0]
         config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
         return res
 
@@ -395,8 +394,7 @@
     def parseconfigure(self, *args):
         config = self.parseconfig(*args)
         config.do_configure()
-        self.request.addfinalizer(lambda:
-        config.do_unconfigure())
+        self.request.addfinalizer(config.do_unconfigure)
         return config
 
     def getitem(self,  source, funcname="test_func"):

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -1653,11 +1653,9 @@
             # what fixtures are visible for particular tests (as denoted
             # by their test id)
             if p.basename.startswith("conftest.py"):
-                nodeid = self.session.fspath.bestrelpath(p.dirpath())
+                nodeid = p.dirpath().relto(self.config.rootdir)
                 if p.sep != "/":
                     nodeid = nodeid.replace(p.sep, "/")
-                if nodeid == ".":
-                    nodeid = ""
         self.parsefactories(plugin, nodeid)
         self._seenplugins.add(plugin)
 

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d _pytest/skipping.py
--- a/_pytest/skipping.py
+++ b/_pytest/skipping.py
@@ -218,14 +218,14 @@
     failed = terminalreporter.stats.get(stat)
     if failed:
         for rep in failed:
-            pos = rep.nodeid
-            lines.append(format %(pos, ))
+            pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
+            lines.append(format %(pos,))
 
 def show_xfailed(terminalreporter, lines):
     xfailed = terminalreporter.stats.get("xfailed")
     if xfailed:
         for rep in xfailed:
-            pos = rep.nodeid
+            pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
             reason = rep.wasxfail
             lines.append("XFAIL %s" % (pos,))
             if reason:
@@ -235,7 +235,7 @@
     xpassed = terminalreporter.stats.get("xpassed")
     if xpassed:
         for rep in xpassed:
-            pos = rep.nodeid
+            pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid)
             reason = rep.wasxfail
             lines.append("XPASS %s %s" %(pos, reason))
 

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -95,7 +95,7 @@
         self._numcollected = 0
 
         self.stats = {}
-        self.startdir = self.curdir = py.path.local()
+        self.startdir = py.path.local()
         if file is None:
             file = sys.stdout
         self._tw = self.writer = py.io.TerminalWriter(file)
@@ -111,12 +111,12 @@
         char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
         return char in self.reportchars
 
-    def write_fspath_result(self, fspath, res):
+    def write_fspath_result(self, nodeid, res):
+        fspath = self.config.rootdir.join(nodeid.split("::")[0])
         if fspath != self.currentfspath:
             self.currentfspath = fspath
-            #fspath = self.startdir.bestrelpath(fspath)
+            fspath = self.startdir.bestrelpath(fspath)
             self._tw.line()
-            #relpath = self.startdir.bestrelpath(fspath)
             self._tw.write(fspath + " ")
         self._tw.write(res)
 
@@ -182,12 +182,12 @@
     def pytest_runtest_logstart(self, nodeid, location):
         # ensure that the path is printed before the
         # 1st test of a module starts running
-        fspath = nodeid.split("::")[0]
         if self.showlongtestinfo:
-            line = self._locationline(fspath, *location)
+            line = self._locationline(nodeid, *location)
             self.write_ensure_prefix(line, "")
         elif self.showfspath:
-            self.write_fspath_result(fspath, "")
+            fsid = nodeid.split("::")[0]
+            self.write_fspath_result(fsid, "")
 
     def pytest_runtest_logreport(self, report):
         rep = report
@@ -200,7 +200,7 @@
             return
         if self.verbosity <= 0:
             if not hasattr(rep, 'node') and self.showfspath:
-                self.write_fspath_result(rep.fspath, letter)
+                self.write_fspath_result(rep.nodeid, letter)
             else:
                 self._tw.write(letter)
         else:
@@ -213,7 +213,7 @@
                     markup = {'red':True}
                 elif rep.skipped:
                     markup = {'yellow':True}
-            line = self._locationline(str(rep.fspath), *rep.location)
+            line = self._locationline(rep.nodeid, *rep.location)
             if not hasattr(rep, 'node'):
                 self.write_ensure_prefix(line, word, **markup)
                 #self._tw.write(word, **markup)
@@ -237,7 +237,7 @@
         items = [x for x in report.result if isinstance(x, pytest.Item)]
         self._numcollected += len(items)
         if self.hasmarkup:
-            #self.write_fspath_result(report.fspath, 'E')
+            #self.write_fspath_result(report.nodeid, 'E')
             self.report_collect()
 
     def report_collect(self, final=False):
@@ -288,6 +288,10 @@
             self.write_line(line)
 
     def pytest_report_header(self, config):
+        inifile = ""
+        if config.inifile:
+            inifile = config.rootdir.bestrelpath(config.inifile)
+        lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)]
         plugininfo = config.pluginmanager._plugin_distinfo
         if plugininfo:
             l = []
@@ -296,7 +300,8 @@
                 if name.startswith("pytest-"):
                     name = name[7:]
                 l.append(name)
-            return "plugins: %s" % ", ".join(l)
+            lines.append("plugins: %s" % ", ".join(l))
+        return lines
 
     def pytest_collection_finish(self, session):
         if self.config.option.collectonly:
@@ -378,19 +383,24 @@
             else:
                 excrepr.reprcrash.toterminal(self._tw)
 
-    def _locationline(self, collect_fspath, fspath, lineno, domain):
+    def _locationline(self, nodeid, fspath, lineno, domain):
+        def mkrel(nodeid):
+            line = self.config.cwd_relative_nodeid(nodeid)
+            if domain and line.endswith(domain):
+                line = line[:-len(domain)]
+                l = domain.split("[")
+                l[0] = l[0].replace('.', '::')  # don't replace '.' in params
+                line += "[".join(l)
+            return line
         # collect_fspath comes from testid which has a "/"-normalized path
-        if fspath and fspath.replace("\\", "/") != collect_fspath:
-            fspath = "%s <- %s" % (collect_fspath, fspath)
+
         if fspath:
-            line = str(fspath)
-            if domain:
-                split = str(domain).split('[')
-                split[0] = split[0].replace('.', '::')  # don't replace '.' in params
-                line += "::" + '['.join(split)
+            res = mkrel(nodeid).replace("::()", "")  # parens-normalization
+            if nodeid.split("::")[0] != fspath.replace("\\", "/"):
+                res += " <- " + self.startdir.bestrelpath(fspath)
         else:
-            line = "[location]"
-        return line + " "
+            res = "[location]"
+        return res + " "
 
     def _getfailureheadline(self, rep):
         if hasattr(rep, 'location'):

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d doc/en/customize.txt
--- a/doc/en/customize.txt
+++ b/doc/en/customize.txt
@@ -12,37 +12,73 @@
 This will display command line and configuration file settings
 which were registered by installed plugins.
 
+.. _rootdir:
 .. _inifiles:
 
-How test configuration is read from configuration INI-files
--------------------------------------------------------------
+initialization: determining rootdir and inifile
+-----------------------------------------------
 
-``pytest`` searches for the first matching ini-style configuration file
-in the directories of command line argument and the directories above.
-It looks for file basenames in this order::
+.. versionadded:: 2.7
 
+pytest determines a "rootdir" for each test run which depends on
+the command line arguments (specified test files, paths) and on
+the existence of inifiles.  The determined rootdir and ini-file are
+printed as part of the pytest header.  The rootdir is used for constructing
+"nodeids" during collection and may also be used by plugins to store
+project/testrun-specific information.
+
+Here is the algorithm which finds the rootdir from ``args``:
+
+- determine the common ancestor directory for the specified ``args``.
+
+- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the
+  ancestor directory and upwards.  If one is matched, it becomes the
+  ini-file and its directory becomes the rootdir.  An existing
+  ``pytest.ini`` file will always be considered a match whereas
+  ``tox.ini`` and ``setup.cfg`` will only match if they contain
+  a ``[pytest]`` section.
+
+- if no ini-file was found, look for ``setup.py`` upwards from
+  the common ancestor directory to determine the ``rootdir``.
+
+- if no ini-file and no ``setup.py`` was found, use the already
+  determined common ancestor as root directory.  This allows to
+  work with pytest in structures that are not part of a package
+  and don't have any particular ini-file configuration.
+
+Note that options from multiple ini-files candidates are never merged,
+the first one wins (``pytest.ini`` always wins even if it does not
+contain a ``[pytest]`` section).
+
+The ``config`` object will subsequently carry these attributes:
+
+- ``config.rootdir``: the determined root directory, guaranteed to exist.
+
+- ``config.inifile``: the determined ini-file, may be ``None``.
+
+The rootdir is used a reference directory for constructing test
+addresses ("nodeids") and can be used also by plugins for storing
+per-testrun information.
+
+Example::
+
+    py.test path/to/testdir path/other/
+
+will determine the common ancestor as ``path`` and then
+check for ini-files as follows::
+
+    # first look for pytest.ini files
+    path/pytest.ini
+    path/setup.cfg  # must also contain [pytest] section to match
+    path/tox.ini    # must also contain [pytest] section to match
     pytest.ini
-    tox.ini
-    setup.cfg
+    ... # all the way down to the root
 
-Searching stops when the first ``[pytest]`` section is found in any of
-these files.  There is no merging of configuration values from multiple
-files.  Example::
+    # now look for setup.py
+    path/setup.py
+    setup.py
+    ... # all the way down to the root
 
-    py.test path/to/testdir
-
-will look in the following dirs for a config file::
-
-    path/to/testdir/pytest.ini
-    path/to/testdir/tox.ini
-    path/to/testdir/setup.cfg
-    path/to/pytest.ini
-    path/to/tox.ini
-    path/to/setup.cfg
-    ... # up until root of filesystem
-
-If argument is provided to a ``pytest`` run, the current working directory
-is used to start the search.
 
 .. _`how to change command line options defaults`:
 .. _`adding default options`:
@@ -67,6 +103,8 @@
 
 From now on, running ``pytest`` will add the specified options.
 
+
+
 Builtin configuration file options
 ----------------------------------------------
 

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d testing/test_collection.py
--- a/testing/test_collection.py
+++ b/testing/test_collection.py
@@ -343,7 +343,7 @@
             ("pytest_make_collect_report", "collector.fspath == p"),
             ("pytest_pycollect_makeitem", "name == 'test_func'"),
             ("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
-            ("pytest_collectreport", "report.nodeid == '.'")
+            ("pytest_collectreport", "report.nodeid == ''")
         ])
 
     def test_collect_protocol_method(self, testdir):
@@ -478,7 +478,7 @@
         config = testdir.parseconfigure(x)
         col = testdir.getnode(config, x)
         assert isinstance(col, pytest.Module)
-        assert col.name == 'subdir/x.py'
+        assert col.name == 'x.py'
         assert col.parent.parent is None
         for col in col.listchain():
             assert col.config is config

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d testing/test_config.py
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -1,6 +1,6 @@
 import py, pytest
 
-from _pytest.config import getcfg
+from _pytest.config import getcfg, get_common_ancestor, determine_setup
 
 class TestParseIni:
     def test_getcfg_and_config(self, testdir, tmpdir):
@@ -10,7 +10,7 @@
             [pytest]
             name = value
         """))
-        cfg = getcfg([sub], ["setup.cfg"])
+        rootdir, inifile, cfg = getcfg([sub], ["setup.cfg"])
         assert cfg['name'] == "value"
         config = testdir.parseconfigure(sub)
         assert config.inicfg['name'] == 'value'
@@ -400,3 +400,55 @@
             *WT1*test_warn_on_test_item*:5*hello*
             *1 warning*
         """)
+
+class TestRootdir:
+    def test_simple_noini(self, tmpdir):
+        assert get_common_ancestor([tmpdir]) == tmpdir
+        assert get_common_ancestor([tmpdir.mkdir("a"), tmpdir]) == tmpdir
+        assert get_common_ancestor([tmpdir, tmpdir.join("a")]) == tmpdir
+        with tmpdir.as_cwd():
+            assert get_common_ancestor([]) == tmpdir
+
+    @pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
+    def test_with_ini(self, tmpdir, name):
+        inifile = tmpdir.join(name)
+        inifile.write("[pytest]\n")
+
+        a = tmpdir.mkdir("a")
+        b = a.mkdir("b")
+        for args in ([tmpdir], [a], [b]):
+            rootdir, inifile, inicfg = determine_setup(None, args)
+            assert rootdir == tmpdir
+            assert inifile == inifile
+        rootdir, inifile, inicfg = determine_setup(None, [b,a])
+        assert rootdir == tmpdir
+        assert inifile == inifile
+
+    @pytest.mark.parametrize("name", "setup.cfg tox.ini".split())
+    def test_pytestini_overides_empty_other(self, tmpdir, name):
+        inifile = tmpdir.ensure("pytest.ini")
+        a = tmpdir.mkdir("a")
+        a.ensure(name)
+        rootdir, inifile, inicfg = determine_setup(None, [a])
+        assert rootdir == tmpdir
+        assert inifile == inifile
+
+    def test_setuppy_fallback(self, tmpdir):
+        a = tmpdir.mkdir("a")
+        a.ensure("setup.cfg")
+        tmpdir.ensure("setup.py")
+        rootdir, inifile, inicfg = determine_setup(None, [a])
+        assert rootdir == tmpdir
+        assert inifile is None
+        assert inicfg == {}
+
+    def test_nothing(self, tmpdir):
+        rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
+        assert rootdir == tmpdir
+        assert inifile is None
+        assert inicfg == {}
+
+    def test_with_specific_inifile(self, tmpdir):
+        inifile = tmpdir.ensure("pytest.ini")
+        rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
+        assert rootdir == tmpdir

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d testing/test_conftest.py
--- a/testing/test_conftest.py
+++ b/testing/test_conftest.py
@@ -1,7 +1,9 @@
+from textwrap import dedent
 import py, pytest
 from _pytest.config import Conftest
 
 
+
 @pytest.fixture(scope="module", params=["global", "inpackage"])
 def basedir(request):
     from _pytest.tmpdir import tmpdir
@@ -255,3 +257,89 @@
     result.stdout.fnmatch_lines("""
         *--hello-world*
     """)
+
+
+class TestConftestVisibility:
+    def _setup_tree(self, testdir):  # for issue616
+        # example mostly taken from:
+        # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html
+        runner = testdir.mkdir("empty")
+        package = testdir.mkdir("package")
+
+        package.join("conftest.py").write(dedent("""\
+            import pytest
+            @pytest.fixture
+            def fxtr():
+                return "from-package"
+        """))
+        package.join("test_pkgroot.py").write(dedent("""\
+            def test_pkgroot(fxtr):
+                assert fxtr == "from-package"
+        """))
+
+        swc = package.mkdir("swc")
+        swc.join("__init__.py").ensure()
+        swc.join("conftest.py").write(dedent("""\
+            import pytest
+            @pytest.fixture
+            def fxtr():
+                return "from-swc"
+        """))
+        swc.join("test_with_conftest.py").write(dedent("""\
+            def test_with_conftest(fxtr):
+                assert fxtr == "from-swc"
+
+        """))
+
+        snc = package.mkdir("snc")
+        snc.join("__init__.py").ensure()
+        snc.join("test_no_conftest.py").write(dedent("""\
+            def test_no_conftest(fxtr):
+                assert fxtr == "from-package"   # No local conftest.py, so should
+                                                # use value from parent dir's
+
+        """))
+        print ("created directory structure:")
+        for x in testdir.tmpdir.visit():
+            print ("   " + x.relto(testdir.tmpdir))
+
+        return {
+            "runner": runner,
+            "package": package,
+            "swc": swc,
+            "snc": snc}
+
+    # N.B.: "swc" stands for "subdir with conftest.py"
+    #       "snc" stands for "subdir no [i.e. without] conftest.py"
+    @pytest.mark.parametrize("chdir,testarg,expect_ntests_passed", [
+        ("runner",  "..",               3),
+        ("package", "..",               3),
+        ("swc",     "../..",            3),
+        ("snc",     "../..",            3),
+
+        ("runner",  "../package",       3),
+        ("package", ".",                3),
+        ("swc",     "..",               3),
+        ("snc",     "..",               3),
+
+        ("runner",  "../package/swc",   1),
+        ("package", "./swc",            1),
+        ("swc",     ".",                1),
+        ("snc",     "../swc",           1),
+
+        ("runner",  "../package/snc",   1),
+        ("package", "./snc",            1),
+        ("swc",     "../snc",           1),
+        ("snc",     ".",                1),
+    ])
+    @pytest.mark.issue616
+    def test_parsefactories_relative_node_ids(
+            self, testdir, chdir,testarg, expect_ntests_passed):
+        dirs = self._setup_tree(testdir)
+        print("pytest run in cwd: %s" %(
+              dirs[chdir].relto(testdir.tmpdir)))
+        print("pytestarg        : %s" %(testarg))
+        print("expected pass    : %s" %(expect_ntests_passed))
+        with dirs[chdir].as_cwd():
+            reprec = testdir.inline_run(testarg, "-q", "--traceconfig")
+            reprec.assertoutcome(passed=expect_ntests_passed)

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d testing/test_doctest.py
--- a/testing/test_doctest.py
+++ b/testing/test_doctest.py
@@ -1,9 +1,6 @@
 from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
 import py, pytest
 
-import pdb
-
-
 class TestDoctests:
 
     def test_collect_testtextfile(self, testdir):

diff -r f7f076b07d105d2fceeb38c998910be33f9fe290 -r 9c7946ebb69c7d4c778e5e6c46e026ca715ae18d testing/test_terminal.py
--- a/testing/test_terminal.py
+++ b/testing/test_terminal.py
@@ -77,11 +77,11 @@
     def test_writeline(self, testdir, linecomp):
         modcol = testdir.getmodulecol("def test_one(): pass")
         rep = TerminalReporter(modcol.config, file=linecomp.stringio)
-        rep.write_fspath_result(py.path.local("xy.py"), '.')
+        rep.write_fspath_result(modcol.nodeid, ".")
         rep.write_line("hello world")
         lines = linecomp.stringio.getvalue().split('\n')
         assert not lines[0]
-        assert lines[1].endswith("xy.py .")
+        assert lines[1].endswith(modcol.name + " .")
         assert lines[2] == "hello world"
 
     def test_show_runtest_logstart(self, testdir, linecomp):
@@ -126,7 +126,7 @@
         ])
         result = testdir.runpytest("-v", p2)
         result.stdout.fnmatch_lines([
-            "*test_p2.py <- *test_p1.py::TestMore::test_p1*",
+            "*test_p2.py::TestMore::test_p1* <- *test_p1.py*PASSED",
         ])
 
     def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):

Repository URL: https://bitbucket.org/pytest-dev/pytest/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.


More information about the pytest-commit mailing list