diff --git a/AUTHORS b/AUTHORS index d3244dfe3..3f43c7479 100644 --- a/AUTHORS +++ b/AUTHORS @@ -143,6 +143,7 @@ Ned Batchelder Neven Mundar Nicolas Delaby Oleg Pidsadnyi +Oleg Sushchenko Oliver Bestwalter Omar Kohl Omer Hadari diff --git a/_pytest/config.py b/_pytest/config.py index d97771155..a9b071b6f 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -991,7 +991,8 @@ class Config(object): def _initini(self, args): ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy()) - r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn) + r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn, + rootdir_cmd_arg=ns.rootdir or None) self.rootdir, self.inifile, self.inicfg = r self._parser.extra_info['rootdir'] = self.rootdir self._parser.extra_info['inifile'] = self.inifile @@ -1323,7 +1324,7 @@ def get_dirs_from_args(args): ] -def determine_setup(inifile, args, warnfunc=None): +def determine_setup(inifile, args, warnfunc=None, rootdir_cmd_arg=None): dirs = get_dirs_from_args(args) if inifile: iniconfig = py.iniconfig.IniConfig(inifile) @@ -1346,6 +1347,11 @@ def determine_setup(inifile, args, warnfunc=None): is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/' if is_fs_root: rootdir = ancestor + if rootdir_cmd_arg: + rootdir_abs_path = py.path.local(os.path.expandvars(rootdir_cmd_arg)) + if not os.path.isdir(str(rootdir_abs_path)): + raise UsageError("Directory '{}' not found. Check your '--rootdir' option.".format(rootdir_abs_path)) + rootdir = rootdir_abs_path return rootdir, inifile, inicfg or {} diff --git a/_pytest/main.py b/_pytest/main.py index 1caa7ff1e..f3dbd4344 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -53,6 +53,11 @@ def pytest_addoption(parser): group._addoption("--continue-on-collection-errors", action="store_true", default=False, dest="continue_on_collection_errors", help="Force test execution even if collection errors occur.") + group._addoption("--rootdir", action="store", + dest="rootdir", + help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " + "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " + "'$HOME/root_dir'.") group = parser.getgroup("collect", "collection") group.addoption('--collectonly', '--collect-only', action="store_true", @@ -283,6 +288,7 @@ class Session(nodes.FSCollector): self.trace = config.trace.root.get("collection") self._norecursepatterns = config.getini("norecursedirs") self.startdir = py.path.local() + self.config.pluginmanager.register(self, name="session") def _makeid(self): diff --git a/changelog/1642.trivial b/changelog/1642.trivial new file mode 100644 index 000000000..a6acc5565 --- /dev/null +++ b/changelog/1642.trivial @@ -0,0 +1 @@ +Add ``--rootdir`` command-line option to override the rules for discovering the root directory. See `customize `_ in the documentation for details. diff --git a/doc/en/customize.rst b/doc/en/customize.rst index 9fe094ba1..f819c5974 100644 --- a/doc/en/customize.rst +++ b/doc/en/customize.rst @@ -38,6 +38,10 @@ Here's a summary what ``pytest`` uses ``rootdir`` for: Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or influence how modules are imported. See :ref:`pythonpath` for more details. +``--rootdir=path`` command-line option can be used to force a specific directory. +The directory passed may contain environment variables when it is used in conjunction +with ``addopts`` in a ``pytest.ini`` file. + Finding the ``rootdir`` ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/testing/test_session.py b/testing/test_session.py index 9ec13f523..68534b102 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -1,4 +1,5 @@ from __future__ import absolute_import, division, print_function + import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED @@ -253,3 +254,32 @@ def test_sessionfinish_with_start(testdir): """) res = testdir.runpytest("--collect-only") assert res.ret == EXIT_NOTESTSCOLLECTED + + +@pytest.mark.parametrize("path", ["root", "{relative}/root", "{environment}/root"]) +def test_rootdir_option_arg(testdir, monkeypatch, path): + monkeypatch.setenv('PY_ROOTDIR_PATH', str(testdir.tmpdir)) + path = path.format(relative=str(testdir.tmpdir), + environment='$PY_ROOTDIR_PATH') + + rootdir = testdir.mkdir("root") + rootdir.mkdir("tests") + testdir.makepyfile(""" + import os + def test_one(): + assert 1 + """) + + result = testdir.runpytest("--rootdir={}".format(path)) + result.stdout.fnmatch_lines(['*rootdir: {}/root, inifile:*'.format(testdir.tmpdir), "*1 passed*"]) + + +def test_rootdir_wrong_option_arg(testdir): + testdir.makepyfile(""" + import os + def test_one(): + assert 1 + """) + + result = testdir.runpytest("--rootdir=wrong_dir") + result.stderr.fnmatch_lines(["*Directory *wrong_dir* not found. Check your '--rootdir' option.*"])