diff --git a/.travis.yml b/.travis.yml index d4ec976..f04232b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: include: - os: osx language: generic - env: TOXENV=py37 + env: TOXENV=py36 - python: 3.7 env: TOXENV="isort,lint,coverage" @@ -29,7 +29,10 @@ matrix: before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - brew upgrade python; + # We don't need to get the latest version of Python 3 on macOS. While it + # would be nice, the 5-10 minutes it takes to update Homebrew and upgrade + # the various dependent packages, means that it's not worth it. + # brew upgrade python; python3 -m venv venv; source venv/bin/activate; fi diff --git a/tests/test_venv.py b/tests/test_venv.py index 149de76..2ad3533 100644 --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -1,17 +1,18 @@ import os import sys -import pluggy import py import pytest import tox -import tox.config from tox.interpreters import NoInterpreterInfo -from tox.venv import CreationConfig -from tox.venv import getdigest -from tox.venv import tox_testenv_install_deps -from tox.venv import VirtualEnv +from tox.venv import ( + CreationConfig, + VirtualEnv, + getdigest, + prepend_shebang_interpreter, + tox_testenv_install_deps, +) from tox_venv.hooks import use_builtin_venv @@ -25,34 +26,45 @@ def test_getdigest(tmpdir): def test_getsupportedinterpreter(monkeypatch, newconfig, mocksession): - config = newconfig([], """ + config = newconfig( + [], + """ [testenv:python] - basepython=%s - """ % sys.executable) - venv = VirtualEnv(config.envconfigs['python'], session=mocksession) + basepython={} + """.format( + sys.executable + ), + ) + venv = VirtualEnv(config.envconfigs["python"], session=mocksession) interp = venv.getsupportedinterpreter() # realpath needed for debian symlinks assert py.path.local(interp).realpath() == py.path.local(sys.executable).realpath() - monkeypatch.setattr(sys, 'platform', "win32") - monkeypatch.setattr(venv.envconfig, 'basepython', 'jython') - pytest.raises(tox.exception.UnsupportedInterpreter, venv.getsupportedinterpreter) + monkeypatch.setattr(tox.INFO, "IS_WIN", True) + monkeypatch.setattr(venv.envconfig, "basepython", "jython") + with pytest.raises(tox.exception.UnsupportedInterpreter): + venv.getsupportedinterpreter() monkeypatch.undo() monkeypatch.setattr(venv.envconfig, "envname", "py1") - monkeypatch.setattr(venv.envconfig, 'basepython', 'notexistingpython') - pytest.raises(tox.exception.InterpreterNotFound, venv.getsupportedinterpreter) + monkeypatch.setattr(venv.envconfig, "basepython", "notexistingpython") + with pytest.raises(tox.exception.InterpreterNotFound): + venv.getsupportedinterpreter() monkeypatch.undo() # check that we properly report when no version_info is present info = NoInterpreterInfo(name=venv.name) info.executable = "something" monkeypatch.setattr(config.interpreters, "get_info", lambda *args, **kw: info) - pytest.raises(tox.exception.InvocationError, venv.getsupportedinterpreter) + with pytest.raises(tox.exception.InvocationError): + venv.getsupportedinterpreter() -def test_create(monkeypatch, mocksession, newconfig): - config = newconfig([], """ +def test_create(mocksession, newconfig): + config = newconfig( + [], + """ [testenv:py123] - """) - envconfig = config.envconfigs['py123'] + """, + ) + envconfig = config.envconfigs["py123"] venv = VirtualEnv(envconfig, session=mocksession) assert venv.path == envconfig.envdir assert not venv.path.check() @@ -61,30 +73,32 @@ def test_create(monkeypatch, mocksession, newconfig): pcalls = mocksession._pcalls assert len(pcalls) >= 1 args = pcalls[0].args - module = 'venv' if use_builtin_venv(venv) else 'virtualenv' + module = "venv" if use_builtin_venv(venv) else "virtualenv" assert module == str(args[2]) - if sys.platform != "win32": + if not tox.INFO.IS_WIN: executable = sys.executable - if use_builtin_venv(venv) and hasattr(sys, 'real_prefix'): + if use_builtin_venv(venv) and hasattr(sys, "real_prefix"): # workaround virtualenv prefixing issue w/ venv on python3 - executable = 'python{}.{}'.format(*sys.version_info) - executable = os.path.join(sys.real_prefix, 'bin', executable) + executable = "python{}.{}".format(*sys.version_info) + executable = os.path.join(sys.real_prefix, "bin", executable) # realpath is needed for stuff like the debian symlinks - assert py.path.local(executable).realpath() == py.path.local(args[0]).realpath() + our_sys_path = py.path.local(executable).realpath() + assert our_sys_path == py.path.local(args[0]).realpath() # assert Envconfig.toxworkdir in args assert venv.getcommandpath("easy_install", cwd=py.path.local()) - interp = venv._getliveconfig().python + interp = venv._getliveconfig().base_resolved_python_path assert interp == venv.envconfig.python_info.executable assert venv.path_config.check(exists=False) -@pytest.mark.skipif("sys.platform == 'win32'") -def test_commandpath_venv_precedence(tmpdir, monkeypatch, - mocksession, newconfig): - config = newconfig([], """ +def test_commandpath_venv_precedence(tmpdir, monkeypatch, mocksession, newconfig): + config = newconfig( + [], + """ [testenv:py123] - """) - envconfig = config.envconfigs['py123'] + """, + ) + envconfig = config.envconfigs["py123"] venv = VirtualEnv(envconfig, session=mocksession) tmpdir.ensure("easy_install") monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep) @@ -93,15 +107,18 @@ def test_commandpath_venv_precedence(tmpdir, monkeypatch, assert py.path.local(p).relto(envconfig.envbindir), p -def test_create_sitepackages(monkeypatch, mocksession, newconfig): - config = newconfig([], """ +def test_create_sitepackages(mocksession, newconfig): + config = newconfig( + [], + """ [testenv:site] sitepackages=True [testenv:nosite] sitepackages=False - """) - envconfig = config.envconfigs['site'] + """, + ) + envconfig = config.envconfigs["site"] venv = VirtualEnv(envconfig, session=mocksession) action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) @@ -111,7 +128,7 @@ def test_create_sitepackages(monkeypatch, mocksession, newconfig): assert "--system-site-packages" in map(str, args) mocksession._clearmocks() - envconfig = config.envconfigs['nosite'] + envconfig = config.envconfigs["nosite"] venv = VirtualEnv(envconfig, session=mocksession) action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) @@ -123,13 +140,16 @@ def test_create_sitepackages(monkeypatch, mocksession, newconfig): def test_install_deps_wildcard(newmocksession): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [tox] distshare = {toxworkdir}/distshare [testenv:py123] deps= {distshare}/dep1-* - """) + """, + ) venv = mocksession.getenv("py123") action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) @@ -143,14 +163,18 @@ def test_install_deps_wildcard(newmocksession): assert len(pcalls) == 2 args = pcalls[-1].args assert pcalls[-1].cwd == venv.envconfig.config.toxinidir - assert "pip" in str(args[0]) - assert args[1] == "install" + + assert py.path.local.sysfind("python") == args[0] + assert ["-m", "pip"] == args[1:3] + assert args[3] == "install" args = [arg for arg in args if str(arg).endswith("dep1-1.1.zip")] assert len(args) == 1 def test_install_deps_indexserver(newmocksession): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [tox] indexserver = abc = ABC @@ -160,8 +184,9 @@ def test_install_deps_indexserver(newmocksession): dep1 :abc:dep2 :abc2:dep3 - """) - venv = mocksession.getenv('py123') + """, + ) + venv = mocksession.getenv("py123") action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) pcalls = mocksession._pcalls @@ -184,13 +209,16 @@ def test_install_deps_indexserver(newmocksession): def test_install_deps_pre(newmocksession): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] pip_pre=true deps= dep1 - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) pcalls = mocksession._pcalls @@ -205,12 +233,15 @@ def test_install_deps_pre(newmocksession): def test_installpkg_indexserver(newmocksession, tmpdir): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [tox] indexserver = default = ABC - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") pcalls = mocksession._pcalls p = tmpdir.ensure("distfile.tar.gz") mocksession.installpkg(venv, p) @@ -222,11 +253,14 @@ def test_installpkg_indexserver(newmocksession, tmpdir): def test_install_recreate(newmocksession, tmpdir): pkg = tmpdir.ensure("package.tar.gz") - mocksession = newmocksession(['--recreate'], """ + mocksession = newmocksession( + ["--recreate"], + """ [testenv] deps=xyz - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") action = mocksession.newaction(venv, "update") venv.update(action) @@ -237,29 +271,35 @@ def test_install_recreate(newmocksession, tmpdir): def test_install_sdist_extras(newmocksession): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] extras = testing development - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) pcalls = mocksession._pcalls assert len(pcalls) == 1 pcalls[:] = [] - venv.installpkg('distfile.tar.gz', action=action) - assert 'distfile.tar.gz[testing,development]' in pcalls[-1].args + venv.installpkg("distfile.tar.gz", action=action) + assert "distfile.tar.gz[testing,development]" in pcalls[-1].args def test_develop_extras(newmocksession, tmpdir): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] extras = testing development - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) pcalls = mocksession._pcalls @@ -267,7 +307,7 @@ def test_develop_extras(newmocksession, tmpdir): pcalls[:] = [] venv.developpkg(tmpdir, action=action) - expected = "%s[testing,development]" % tmpdir.strpath + expected = "{}[testing,development]".format(tmpdir.strpath) assert expected in pcalls[-1].args @@ -275,14 +315,17 @@ def test_env_variables_added_to_needs_reinstall(tmpdir, mocksession, newconfig, tmpdir.ensure("setup.py") monkeypatch.setenv("TEMP_PASS_VAR", "123") monkeypatch.setenv("TEMP_NOPASS_VAR", "456") - config = newconfig([], """ + config = newconfig( + [], + """ [testenv:python] passenv = temp_pass_var setenv = CUSTOM_VAR = 789 - """) + """, + ) - venv = VirtualEnv(config.envconfigs['python'], session=mocksession) + venv = VirtualEnv(config.envconfigs["python"], session=mocksession) action = mocksession.newaction(venv, "hello") venv._needs_reinstall(tmpdir, action) @@ -292,132 +335,144 @@ def test_env_variables_added_to_needs_reinstall(tmpdir, mocksession, newconfig, env = pcalls[0].env # should have access to setenv vars - assert 'CUSTOM_VAR' in env - assert env['CUSTOM_VAR'] == '789' + assert "CUSTOM_VAR" in env + assert env["CUSTOM_VAR"] == "789" # should have access to passenv vars - assert 'TEMP_PASS_VAR' in env - assert env['TEMP_PASS_VAR'] == "123" + assert "TEMP_PASS_VAR" in env + assert env["TEMP_PASS_VAR"] == "123" # should also have access to full invocation environment, # for backward compatibility, and to match behavior of venv.run_install_command() - assert 'TEMP_NOPASS_VAR' in env + assert "TEMP_NOPASS_VAR" in env assert env["TEMP_NOPASS_VAR"] == "456" -def test_test_hashseed_is_in_output(newmocksession): - original_make_hashseed = tox.config.make_hashseed - tox.config.make_hashseed = lambda: '123456789' - try: - mocksession = newmocksession([], ''' - [testenv] - ''') - finally: - tox.config.make_hashseed = original_make_hashseed - venv = mocksession.getenv('python') +def test_test_hashseed_is_in_output(newmocksession, monkeypatch): + seed = "123456789" + monkeypatch.setattr("tox.config.make_hashseed", lambda: seed) + mocksession = newmocksession([], "") + venv = mocksession.getenv("python") action = mocksession.newaction(venv, "update") venv.update(action) - venv.test() - mocksession.report.expect("verbosity0", "python runtests: PYTHONHASHSEED='123456789'") + tox.venv.tox_runtest_pre(venv) + mocksession.report.expect("verbosity0", "run-test-pre: PYTHONHASHSEED='{}'".format(seed)) def test_test_runtests_action_command_is_in_output(newmocksession): - mocksession = newmocksession([], ''' + mocksession = newmocksession( + [], + """ [testenv] commands = echo foo bar - ''') - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") action = mocksession.newaction(venv, "update") venv.update(action) venv.test() mocksession.report.expect("verbosity0", "*runtests*commands?0? | echo foo bar") -def test_install_error(newmocksession, monkeypatch): - mocksession = newmocksession(['--recreate'], """ +def test_install_error(newmocksession): + mocksession = newmocksession( + ["--recreate"], + """ [testenv] deps=xyz commands= qwelkqw - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") venv.test() mocksession.report.expect("error", "*not find*qwelkqw*") assert venv.status == "commands failed" -def test_install_command_not_installed(newmocksession, monkeypatch): - mocksession = newmocksession(['--recreate'], """ +def test_install_command_not_installed(newmocksession): + mocksession = newmocksession( + ["--recreate"], + """ [testenv] commands= pytest - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") + venv.status = 0 venv.test() mocksession.report.expect("warning", "*test command found but not*") assert venv.status == 0 -def test_install_command_whitelisted(newmocksession, monkeypatch): - mocksession = newmocksession(['--recreate'], """ +def test_install_command_whitelisted(newmocksession): + mocksession = newmocksession( + ["--recreate"], + """ [testenv] whitelist_externals = pytest xy* commands= pytest xyz - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") venv.test() - mocksession.report.expect("warning", "*test command found but not*", - invert=True) + mocksession.report.expect("warning", "*test command found but not*", invert=True) assert venv.status == "commands failed" -@pytest.mark.skipif("not sys.platform.startswith('linux')") def test_install_command_not_installed_bash(newmocksession): - mocksession = newmocksession(['--recreate'], """ + mocksession = newmocksession( + ["--recreate"], + """ [testenv] commands= bash - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") venv.test() mocksession.report.expect("warning", "*test command found but not*") -def test_install_python3(tmpdir, newmocksession): - if not py.path.local.sysfind('python3'): +def test_install_python3(newmocksession): + if not py.path.local.sysfind("python3"): pytest.skip("needs python3") - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv:py123] basepython=python3 deps= dep1 dep2 - """) - venv = mocksession.getenv('py123') + """, + ) + venv = mocksession.getenv("py123") action = mocksession.newaction(venv, "getenv") tox_testenv_create(action=action, venv=venv) pcalls = mocksession._pcalls assert len(pcalls) == 1 args = pcalls[0].args - assert str(args[2]) == 'venv' + assert str(args[2]) == "venv" pcalls[:] = [] action = mocksession.newaction(venv, "hello") venv._install(["hello"], action=action) assert len(pcalls) == 1 args = pcalls[0].args - assert "pip" in args[0] + assert py.path.local.sysfind("python") == args[0] + assert ["-m", "pip"] == args[1:3] for _ in args: assert "--download-cache" not in args, args class TestCreationConfig: - def test_basic(self, newconfig, mocksession, tmpdir): config = newconfig([], "") - envconfig = config.envconfigs['python'] + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) cconfig = venv._getliveconfig() assert cconfig.matches(cconfig) @@ -428,33 +483,42 @@ def test_basic(self, newconfig, mocksession, tmpdir): assert cconfig.matches(newconfig) def test_matchingdependencies(self, newconfig, mocksession): - config = newconfig([], """ + config = newconfig( + [], + """ [testenv] deps=abc - """) - envconfig = config.envconfigs['python'] + """, + ) + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) cconfig = venv._getliveconfig() - config = newconfig([], """ + config = newconfig( + [], + """ [testenv] deps=xyz - """) - envconfig = config.envconfigs['python'] + """, + ) + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) otherconfig = venv._getliveconfig() assert not cconfig.matches(otherconfig) def test_matchingdependencies_file(self, newconfig, mocksession): - config = newconfig([], """ + config = newconfig( + [], + """ [tox] distshare={toxworkdir}/distshare [testenv] deps=abc {distshare}/xyz.zip - """) + """, + ) xyz = config.distshare.join("xyz.zip") xyz.ensure() - envconfig = config.envconfigs['python'] + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) cconfig = venv._getliveconfig() assert cconfig.matches(cconfig) @@ -463,15 +527,18 @@ def test_matchingdependencies_file(self, newconfig, mocksession): assert not cconfig.matches(newconfig) def test_matchingdependencies_latest(self, newconfig, mocksession): - config = newconfig([], """ + config = newconfig( + [], + """ [tox] distshare={toxworkdir}/distshare [testenv] deps={distshare}/xyz-* - """) + """, + ) config.distshare.ensure("xyz-1.2.0.zip") xyz2 = config.distshare.ensure("xyz-1.2.1.zip") - envconfig = config.envconfigs['python'] + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) cconfig = venv._getliveconfig() md5, path = cconfig.deps[0] @@ -481,9 +548,9 @@ def test_matchingdependencies_latest(self, newconfig, mocksession): def test_python_recreation(self, tmpdir, newconfig, mocksession): pkg = tmpdir.ensure("package.tar.gz") config = newconfig([], "") - envconfig = config.envconfigs['python'] + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) - cconfig = venv._getliveconfig() + create_config = venv._getliveconfig() action = mocksession.newaction(venv, "update") venv.update(action) assert not venv.path_config.check() @@ -491,7 +558,7 @@ def test_python_recreation(self, tmpdir, newconfig, mocksession): assert venv.path_config.check() assert mocksession._pcalls args1 = map(str, mocksession._pcalls[0].args) - module = 'venv' if use_builtin_venv(venv) else 'virtualenv' + module = "venv" if use_builtin_venv(venv) else "virtualenv" assert module in " ".join(args1) mocksession.report.expect("*", "*create*") # modify config and check that recreation happens @@ -501,14 +568,14 @@ def test_python_recreation(self, tmpdir, newconfig, mocksession): mocksession.report.expect("*", "*reusing*") mocksession._clearmocks() action = mocksession.newaction(venv, "update") - cconfig.python = py.path.local("balla") - cconfig.writeconfig(venv.path_config) + create_config.base_resolved_python_path = py.path.local("balla") + create_config.writeconfig(venv.path_config) venv.update(action) mocksession.report.expect("verbosity0", "*recreate*") def test_dep_recreation(self, newconfig, mocksession): config = newconfig([], "") - envconfig = config.envconfigs['python'] + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) action = mocksession.newaction(venv, "update") venv.update(action) @@ -522,7 +589,7 @@ def test_dep_recreation(self, newconfig, mocksession): def test_develop_recreation(self, newconfig, mocksession): config = newconfig([], "") - envconfig = config.envconfigs['python'] + envconfig = config.envconfigs["python"] venv = VirtualEnv(envconfig, session=mocksession) action = mocksession.newaction(venv, "update") venv.update(action) @@ -536,89 +603,103 @@ def test_develop_recreation(self, newconfig, mocksession): class TestVenvTest: - def test_envbindir_path(self, newmocksession, monkeypatch): monkeypatch.setenv("PIP_RESPECT_VIRTUALENV", "1") - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv:python] commands=abc - """) + """, + ) venv = mocksession.getenv("python") action = mocksession.newaction(venv, "getenv") monkeypatch.setenv("PATH", "xyz") sysfind_calls = [] - monkeypatch.setattr("py.path.local.sysfind", classmethod( - lambda *args, **kwargs: sysfind_calls.append(kwargs) or 0 / 0)) + monkeypatch.setattr( + "py.path.local.sysfind", + classmethod(lambda *args, **kwargs: sysfind_calls.append(kwargs) or 0 / 0), + ) with pytest.raises(ZeroDivisionError): - venv._install(list('123'), action=action) + venv._install(list("123"), action=action) assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir] with pytest.raises(ZeroDivisionError): venv.test(action) assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir] with pytest.raises(ZeroDivisionError): - venv.run_install_command(['qwe'], action=action) + venv.run_install_command(["qwe"], action=action) assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir] monkeypatch.setenv("PIP_RESPECT_VIRTUALENV", "1") monkeypatch.setenv("PIP_REQUIRE_VIRTUALENV", "1") monkeypatch.setenv("__PYVENV_LAUNCHER__", "1") with pytest.raises(ZeroDivisionError): - venv.run_install_command(['qwe'], action=action) - assert 'PIP_RESPECT_VIRTUALENV' not in os.environ - assert 'PIP_REQUIRE_VIRTUALENV' not in os.environ - assert '__PYVENV_LAUNCHER__' not in os.environ + venv.run_install_command(["qwe"], action=action) + assert "PIP_RESPECT_VIRTUALENV" not in os.environ + assert "PIP_REQUIRE_VIRTUALENV" not in os.environ + assert "__PYVENV_LAUNCHER__" not in os.environ + assert os.environ["PIP_USER"] == "0" + assert os.environ["PIP_NO_DEPS"] == "0" def test_pythonpath_usage(self, newmocksession, monkeypatch): monkeypatch.setenv("PYTHONPATH", "/my/awesome/library") - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv:python] commands=abc - """) + """, + ) venv = mocksession.getenv("python") action = mocksession.newaction(venv, "getenv") - venv.run_install_command(['qwe'], action=action) - assert 'PYTHONPATH' not in os.environ + venv.run_install_command(["qwe"], action=action) + assert "PYTHONPATH" not in os.environ mocksession.report.expect("warning", "*Discarding $PYTHONPATH from environment*") pcalls = mocksession._pcalls assert len(pcalls) == 1 - assert 'PYTHONPATH' not in pcalls[0].env + assert "PYTHONPATH" not in pcalls[0].env # passenv = PYTHONPATH allows PYTHONPATH to stay in environment monkeypatch.setenv("PYTHONPATH", "/my/awesome/library") - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv:python] commands=abc passenv = PYTHONPATH - """) + """, + ) venv = mocksession.getenv("python") action = mocksession.newaction(venv, "getenv") - venv.run_install_command(['qwe'], action=action) - assert 'PYTHONPATH' in os.environ + venv.run_install_command(["qwe"], action=action) + assert "PYTHONPATH" in os.environ mocksession.report.not_expect("warning", "*Discarding $PYTHONPATH from environment*") pcalls = mocksession._pcalls assert len(pcalls) == 2 - assert pcalls[1].env['PYTHONPATH'] == '/my/awesome/library' + assert pcalls[1].env["PYTHONPATH"] == "/my/awesome/library" -# FIXME this test fails when run in isolation - find what this depends on -# AssertionError: found warning('*Discarding $PYTHONPATH [...] def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatch): + monkeypatch.delenv("PYTHONPATH", raising=False) pkg = tmpdir.ensure("package.tar.gz") monkeypatch.setenv("X123", "123") monkeypatch.setenv("YY", "456") - config = newconfig([], """ + config = newconfig( + [], + """ [testenv:python] commands=python -V passenv = x123 setenv = ENV_VAR = value PYTHONPATH = value - """) + """, + ) mocksession._clearmocks() - venv = VirtualEnv(config.envconfigs['python'], session=mocksession) + venv = VirtualEnv(config.envconfigs["python"], session=mocksession) mocksession.installpkg(venv, pkg) venv.test() @@ -627,18 +708,17 @@ def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatc for x in pcalls: env = x.env assert env is not None - assert 'ENV_VAR' in env - assert env['ENV_VAR'] == 'value' - assert env['VIRTUAL_ENV'] == str(venv.path) - assert env['X123'] == "123" - assert 'PYTHONPATH' in env - assert env['PYTHONPATH'] == 'value' + assert "ENV_VAR" in env + assert env["ENV_VAR"] == "value" + assert env["VIRTUAL_ENV"] == str(venv.path) + assert env["X123"] == "123" + assert "PYTHONPATH" in env + assert env["PYTHONPATH"] == "value" # all env variables are passed for installation assert pcalls[0].env["YY"] == "456" assert "YY" not in pcalls[1].env - assert {"ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"} \ - .issubset(pcalls[1].env) + assert {"ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"}.issubset(pcalls[1].env) # setenv does not trigger PYTHONPATH warnings mocksession.report.not_expect("warning", "*Discarding $PYTHONPATH from environment*") @@ -650,64 +730,86 @@ def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatc def test_installpkg_no_upgrade(tmpdir, newmocksession): pkg = tmpdir.ensure("package.tar.gz") mocksession = newmocksession([], "") - venv = mocksession.getenv('python') + venv = mocksession.getenv("python") venv.just_created = True venv.envconfig.envdir.ensure(dir=1) mocksession.installpkg(venv, pkg) pcalls = mocksession._pcalls assert len(pcalls) == 1 - assert '-U' not in pcalls[0].args + assert pcalls[0].args[1:-1] == ["-m", "pip", "install", "--exists-action", "w"] + + +@pytest.mark.parametrize("count, level", [(0, 0), (1, 0), (2, 0), (3, 1), (4, 2), (5, 3), (6, 3)]) +def test_install_command_verbosity(tmpdir, newmocksession, count, level): + pkg = tmpdir.ensure("package.tar.gz") + mock_session = newmocksession(["-{}".format("v" * count)], "") + env = mock_session.getenv("python") + env.just_created = True + env.envconfig.envdir.ensure(dir=1) + mock_session.installpkg(env, pkg) + pcalls = mock_session._pcalls + assert len(pcalls) == 1 + expected = ["-m", "pip", "install", "--exists-action", "w"] + (["-v"] * level) + assert pcalls[0].args[1:-1] == expected def test_installpkg_upgrade(newmocksession, tmpdir): pkg = tmpdir.ensure("package.tar.gz") mocksession = newmocksession([], "") - venv = mocksession.getenv('python') - assert not hasattr(venv, 'just_created') + venv = mocksession.getenv("python") + assert not hasattr(venv, "just_created") mocksession.installpkg(venv, pkg) pcalls = mocksession._pcalls assert len(pcalls) == 1 index = pcalls[0].args.index(str(pkg)) assert index >= 0 - assert '-U' in pcalls[0].args[:index] - assert '--no-deps' in pcalls[0].args[:index] + assert "-U" in pcalls[0].args[:index] + assert "--no-deps" in pcalls[0].args[:index] def test_run_install_command(newmocksession): mocksession = newmocksession([], "") - venv = mocksession.getenv('python') + venv = mocksession.getenv("python") venv.just_created = True venv.envconfig.envdir.ensure(dir=1) action = mocksession.newaction(venv, "hello") venv.run_install_command(packages=["whatever"], action=action) pcalls = mocksession._pcalls assert len(pcalls) == 1 - assert 'pip' in pcalls[0].args[0] - assert 'install' in pcalls[0].args + args = pcalls[0].args + assert py.path.local.sysfind("python") == args[0] + assert ["-m", "pip"] == args[1:3] + assert "install" in args env = pcalls[0].env assert env is not None def test_run_custom_install_command(newmocksession): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] install_command=easy_install {opts} {packages} - """) - venv = mocksession.getenv('python') + """, + ) + venv = mocksession.getenv("python") venv.just_created = True venv.envconfig.envdir.ensure(dir=1) action = mocksession.newaction(venv, "hello") venv.run_install_command(packages=["whatever"], action=action) pcalls = mocksession._pcalls assert len(pcalls) == 1 - assert 'easy_install' in pcalls[0].args[0] - assert pcalls[0].args[1:] == ['whatever'] + assert "easy_install" in pcalls[0].args[0] + assert pcalls[0].args[1:] == ["whatever"] def test_command_relative_issue36(newmocksession, tmpdir, monkeypatch): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] - """) + """, + ) x = tmpdir.ensure("x") venv = mocksession.getenv("python") x2 = venv.getcommandpath("./x", cwd=tmpdir) @@ -718,22 +820,24 @@ def test_command_relative_issue36(newmocksession, tmpdir, monkeypatch): mocksession.report.not_expect("warning", "*test command found but not*") monkeypatch.setenv("PATH", str(tmpdir)) x4 = venv.getcommandpath("x", cwd=tmpdir) - assert x4.endswith(os.sep + 'x') + assert x4.endswith(os.sep + "x") mocksession.report.expect("warning", "*test command found but not*") def test_ignore_outcome_failing_cmd(newmocksession): - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] commands=testenv_fail ignore_outcome=True - """) + """, + ) - venv = mocksession.getenv('python') + venv = mocksession.getenv("python") venv.test() assert venv.status == "ignored failed command" - mocksession.report.expect("warning", "*command failed but result from " - "testenv is ignored*") + mocksession.report.expect("warning", "*command failed but result from testenv is ignored*") def test_tox_testenv_create(newmocksession): @@ -742,19 +846,27 @@ def test_tox_testenv_create(newmocksession): class Plugin: @tox.hookimpl def tox_testenv_create(self, action, venv): + assert isinstance(action, tox.session.Action) + assert isinstance(venv, VirtualEnv) log.append(1) @tox.hookimpl def tox_testenv_install_deps(self, action, venv): + assert isinstance(action, tox.session.Action) + assert isinstance(venv, VirtualEnv) log.append(2) - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] commands=testenv_fail ignore_outcome=True - """, plugins=[Plugin()]) + """, + plugins=[Plugin()], + ) - venv = mocksession.getenv('python') + venv = mocksession.getenv("python") venv.update(action=mocksession.newaction(venv, "getenv")) assert log == [1, 2] @@ -764,20 +876,143 @@ def test_tox_testenv_pre_post(newmocksession): class Plugin: @tox.hookimpl - def tox_runtest_pre(self, venv): - log.append('started') + def tox_runtest_pre(self): + log.append("started") @tox.hookimpl - def tox_runtest_post(self, venv): - log.append('finished') + def tox_runtest_post(self): + log.append("finished") - mocksession = newmocksession([], """ + mocksession = newmocksession( + [], + """ [testenv] commands=testenv_fail - """, plugins=[Plugin()]) + """, + plugins=[Plugin()], + ) - venv = mocksession.getenv('python') + venv = mocksession.getenv("python") venv.status = None assert log == [] mocksession.runtestenv(venv) - assert log == ['started', 'finished'] + assert log == ["started", "finished"] + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_empty_instance(tmpdir): + testfile = tmpdir.join("check_shebang_empty_instance.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # empty instance + testfile.write("") + args = prepend_shebang_interpreter(base_args) + assert args == base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_empty_interpreter(tmpdir): + testfile = tmpdir.join("check_shebang_empty_interpreter.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # empty interpreter + testfile.write("#!") + args = prepend_shebang_interpreter(base_args) + assert args == base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_empty_interpreter_ws(tmpdir): + testfile = tmpdir.join("check_shebang_empty_interpreter_ws.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # empty interpreter (whitespaces) + testfile.write("#! \n") + args = prepend_shebang_interpreter(base_args) + assert args == base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_non_utf8(tmpdir): + testfile = tmpdir.join("check_non_utf8.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + testfile.write_binary(b"#!\x9a\xef\x12\xaf\n") + args = prepend_shebang_interpreter(base_args) + assert args == base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_interpreter_simple(tmpdir): + testfile = tmpdir.join("check_shebang_interpreter_simple.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # interpreter (simple) + testfile.write("#!interpreter") + args = prepend_shebang_interpreter(base_args) + assert args == ["interpreter"] + base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_interpreter_ws(tmpdir): + testfile = tmpdir.join("check_shebang_interpreter_ws.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # interpreter (whitespaces) + testfile.write("#! interpreter \n\n") + args = prepend_shebang_interpreter(base_args) + assert args == ["interpreter"] + base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_interpreter_arg(tmpdir): + testfile = tmpdir.join("check_shebang_interpreter_arg.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # interpreter with argument + testfile.write("#!interpreter argx\n") + args = prepend_shebang_interpreter(base_args) + assert args == ["interpreter", "argx"] + base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_interpreter_args(tmpdir): + testfile = tmpdir.join("check_shebang_interpreter_args.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # interpreter with argument (ensure single argument) + testfile.write("#!interpreter argx argx-part2\n") + args = prepend_shebang_interpreter(base_args) + assert args == ["interpreter", "argx argx-part2"] + base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_real(tmpdir): + testfile = tmpdir.join("check_shebang_real.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # interpreter (real example) + testfile.write("#!/usr/bin/env python\n") + args = prepend_shebang_interpreter(base_args) + assert args == ["/usr/bin/env", "python"] + base_args + + +@pytest.mark.skipif("sys.platform == 'win32'") +def test_tox_testenv_interpret_shebang_long_example(tmpdir): + testfile = tmpdir.join("check_shebang_long_example.py") + base_args = [str(testfile), "arg1", "arg2", "arg3"] + + # interpreter (long example) + testfile.write( + "#!this-is-an-example-of-a-very-long-interpret-directive-what-should-" + "be-directly-invoked-when-tox-needs-to-invoked-the-provided-script-" + "name-in-the-argument-list" + ) + args = prepend_shebang_interpreter(base_args) + expected = [ + "this-is-an-example-of-a-very-long-interpret-directive-what-should-be-" + "directly-invoked-when-tox-needs-to-invoked-the-provided-script-name-" + "in-the-argument-list" + ] + + assert args == expected + base_args diff --git a/tests/test_z_cmdline.py b/tests/test_z_cmdline.py index c4c6824..7a9100c 100644 --- a/tests/test_z_cmdline.py +++ b/tests/test_z_cmdline.py @@ -1,33 +1,32 @@ +import json import os -import platform import re +import shutil import subprocess import sys +import tempfile import py import pytest import tox from tox._pytestplugin import ReportExpectMock +from tox.config import parseconfig +from tox.session import Session -try: - import json -except ImportError: - import simplejson as json +from tox_venv.hooks import use_builtin_venv pytest_plugins = "pytester" -from tox.session import Session # noqa #E402 module level import not at top of file -from tox.config import parseconfig # noqa #E402 module level import not at top of file - -from tox_venv.hooks import use_builtin_venv # noqa #E402 module level import not at top of file - def test_report_protocol(newconfig): - config = newconfig([], """ + config = newconfig( + [], + """ [testenv:mypython] deps=xy - """) + """, + ) class Popen: def __init__(self, *args, **kwargs): @@ -39,8 +38,7 @@ def communicate(self): def wait(self): pass - session = Session(config, popen=Popen, - Report=ReportExpectMock) + session = Session(config, popen=Popen, Report=ReportExpectMock) report = session.report report.expect("using") venv = session.getvenv("mypython") @@ -49,97 +47,27 @@ def wait(self): report.expect("logpopen") -def test__resolve_pkg(tmpdir, mocksession): - distshare = tmpdir.join("distshare") - spec = distshare.join("pkg123-*") - pytest.raises(tox.exception.MissingDirectory, 'mocksession._resolve_pkg(spec)') - distshare.ensure(dir=1) - pytest.raises(tox.exception.MissingDependency, 'mocksession._resolve_pkg(spec)') - distshare.ensure("pkg123-1.3.5.zip") - p = distshare.ensure("pkg123-1.4.5.zip") - - mocksession.report.clear() - result = mocksession._resolve_pkg(spec) - assert result == p - mocksession.report.expect("info", "determin*pkg123*") - distshare.ensure("pkg123-1.4.7dev.zip") - mocksession._clearmocks() - result = mocksession._resolve_pkg(spec) - mocksession.report.expect("warning", "*1.4.7*") - assert result == p - mocksession._clearmocks() - distshare.ensure("pkg123-1.4.5a1.tar.gz") - result = mocksession._resolve_pkg(spec) - assert result == p - - -def test__resolve_pkg_doubledash(tmpdir, mocksession): - distshare = tmpdir.join("distshare") - p = distshare.ensure("pkg-mine-1.3.0.zip") - res = mocksession._resolve_pkg(distshare.join("pkg-mine*")) - assert res == p - distshare.ensure("pkg-mine-1.3.0a1.zip") - res = mocksession._resolve_pkg(distshare.join("pkg-mine*")) - assert res == p - - class TestSession: - def test_make_sdist(self, initproj): - initproj("example123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' - ''' - }) - config = parseconfig([]) - session = Session(config) - sdist = session.get_installpkg_path() - assert sdist.check() - assert sdist.ext == ".zip" - assert sdist == config.distdir.join(sdist.basename) - sdist2 = session.get_installpkg_path() - assert sdist2 == sdist - sdist.write("hello") - assert sdist.stat().size < 10 - sdist_new = Session(config).get_installpkg_path() - assert sdist_new == sdist - assert sdist_new.stat().size > 10 - - def test_make_sdist_distshare(self, tmpdir, initproj): - distshare = tmpdir.join("distshare") - initproj("example123-0.6", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' - [tox] - distshare=%s - ''' % distshare - }) - config = parseconfig([]) - session = Session(config) - sdist = session.get_installpkg_path() - assert sdist.check() - assert sdist.ext == ".zip" - assert sdist == config.distdir.join(sdist.basename) - sdist_share = config.distshare.join(sdist.basename) - assert sdist_share.check() - assert sdist_share.read("rb") == sdist.read("rb"), (sdist_share, sdist) - def test_log_pcall(self, mocksession): mocksession.config.logdir.ensure(dir=1) assert not mocksession.config.logdir.listdir() action = mocksession.newaction(None, "something") - action.popen(["echo", ]) + action.popen(["echo"]) match = mocksession.report.getnext("logpopen") assert match[1].outpath.relto(mocksession.config.logdir) assert match[1].shell is False def test_summary_status(self, initproj, capfd): - initproj("logexample123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "logexample123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv:hello] [testenv:world] - ''' - }) + """, + }, + ) config = parseconfig([]) session = Session(config) envs = session.venvlist @@ -151,19 +79,22 @@ def test_summary_status(self, initproj, capfd): assert not env2.status session._summary() out, err = capfd.readouterr() - exp = "%s: FAIL XYZ" % env1.envconfig.envname + exp = "{}: FAIL XYZ".format(env1.envconfig.envname) assert exp in out - exp = "%s: commands succeeded" % env2.envconfig.envname + exp = "{}: commands succeeded".format(env2.envconfig.envname) assert exp in out - def test_getvenv(self, initproj, capfd): - initproj("logexample123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + def test_getvenv(self, initproj): + initproj( + "logexample123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv:hello] [testenv:world] - ''' - }) + """, + }, + ) config = parseconfig([]) session = Session(config) venv1 = session.getvenv("hello") @@ -172,163 +103,160 @@ def test_getvenv(self, initproj, capfd): venv1 = session.getvenv("world") venv2 = session.getvenv("world") assert venv1 is venv2 - pytest.raises(LookupError, lambda: session.getvenv("qwe")) - - -# not sure we want this option ATM -def XXX_test_package(cmd, initproj): - initproj("myproj-0.6", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'MANIFEST.in': """ - include doc - include myproj - """, - 'tox.ini': '' - }) - result = cmd("package") - assert not result.ret - assert any(re.match(r'.*created sdist package at.*', l) for l in result.outlines) - - -def test_minversion(cmd, initproj): - initproj("interp123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' - [tox] - minversion = 6.0 - ''' - }) - result = cmd("-v") - assert re.match(r'ERROR: MinVersionError: tox version is .*,' - r' required is at least 6.0', result.out) - assert result.ret + with pytest.raises(LookupError): + session.getvenv("qwe") def test_notoxini_help_still_works(initproj, cmd): - initproj("example123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - }) + initproj("example123-0.5", filedefs={"tests": {"test_hello.py": "def test_hello(): pass"}}) result = cmd("-h") - assert result.err == "ERROR: toxini file 'tox.ini' not found\n" - assert result.out.startswith('usage: ') - assert any('--help' in l for l in result.outlines) + msg = "ERROR: tox config file (either pyproject.toml, tox.ini, setup.cfg) not found\n" + assert result.err == msg + assert result.out.startswith("usage: ") + assert any("--help" in l for l in result.outlines), result.outlines assert not result.ret def test_notoxini_help_ini_still_works(initproj, cmd): - initproj("example123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - }) + initproj("example123-0.5", filedefs={"tests": {"test_hello.py": "def test_hello(): pass"}}) result = cmd("--help-ini") - assert any('setenv' in l for l in result.outlines) + assert any("setenv" in l for l in result.outlines), result.outlines assert not result.ret def test_envdir_equals_toxini_errors_out(cmd, initproj): - initproj("interp123-0.7", filedefs={ - 'tox.ini': ''' + initproj( + "interp123-0.7", + filedefs={ + "tox.ini": """ [testenv] envdir={toxinidir} - ''' - }) + """ + }, + ) result = cmd() assert result.outlines[1] == "ERROR: ConfigError: envdir must not equal toxinidir" - assert re.match(r'ERROR: venv \'python\' in .* would delete project', result.outlines[0]) - assert result.ret + assert re.match(r"ERROR: venv \'python\' in .* would delete project", result.outlines[0]) + assert result.ret, "{}\n{}".format(result.err, result.out) def test_run_custom_install_command_error(cmd, initproj): - initproj("interp123-0.5", filedefs={ - 'tox.ini': ''' + initproj( + "interp123-0.5", + filedefs={ + "tox.ini": """ [testenv] install_command=./tox.ini {opts} {packages} - ''' - }) + """ + }, + ) result = cmd() - assert re.match(r"ERROR: invocation failed \(errno \d+\), args: \['.*[/\\]tox\.ini", - result.outlines[-1]) - assert result.ret + assert re.match( + r"ERROR: invocation failed \(errno \d+\), args: .*[/\\]tox\.ini", result.outlines[-1] + ) + assert result.ret, "{}\n{}".format(result.err, result.out) def test_unknown_interpreter_and_env(cmd, initproj): - initproj("interp123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "interp123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv:python] basepython=xyz_unknown_interpreter [testenv] changedir=tests - ''' - }) + """, + }, + ) result = cmd() - assert result.ret - assert any('ERROR: InterpreterNotFound: xyz_unknown_interpreter' == l for l in result.outlines) + assert result.ret, "{}\n{}".format(result.err, result.out) + assert any( + "ERROR: InterpreterNotFound: xyz_unknown_interpreter" == l for l in result.outlines + ), result.outlines result = cmd("-exyz") - assert result.ret + assert result.ret, "{}\n{}".format(result.err, result.out) assert result.out == "ERROR: unknown environment 'xyz'\n" def test_unknown_interpreter(cmd, initproj): - initproj("interp123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "interp123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv:python] basepython=xyz_unknown_interpreter [testenv] changedir=tests - ''' - }) + """, + }, + ) result = cmd() - assert result.ret - assert any('ERROR: InterpreterNotFound: xyz_unknown_interpreter' == l for l in result.outlines) + assert result.ret, "{}\n{}".format(result.err, result.out) + assert any( + "ERROR: InterpreterNotFound: xyz_unknown_interpreter" == l for l in result.outlines + ), result.outlines def test_skip_platform_mismatch(cmd, initproj): - initproj("interp123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "interp123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv] changedir=tests platform=x123 - ''' - }) + """, + }, + ) result = cmd() assert not result.ret - assert any('SKIPPED: python: platform mismatch' == l for l in result.outlines) + assert any( + "SKIPPED: python: platform mismatch" == l for l in result.outlines + ), result.outlines def test_skip_unknown_interpreter(cmd, initproj): - initproj("interp123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "interp123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv:python] basepython=xyz_unknown_interpreter [testenv] changedir=tests - ''' - }) + """, + }, + ) result = cmd("--skip-missing-interpreters") assert not result.ret - msg = 'SKIPPED: python: InterpreterNotFound: xyz_unknown_interpreter' - assert any(msg == l for l in result.outlines) + msg = "SKIPPED: python: InterpreterNotFound: xyz_unknown_interpreter" + assert any(msg == l for l in result.outlines), result.outlines def test_skip_unknown_interpreter_result_json(cmd, initproj, tmpdir): report_path = tmpdir.join("toxresult.json") - initproj("interp123-0.5", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "interp123-0.5", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv:python] basepython=xyz_unknown_interpreter [testenv] changedir=tests - ''' - }) + """, + }, + ) result = cmd("--skip-missing-interpreters", "--result-json", report_path) assert not result.ret - msg = 'SKIPPED: python: InterpreterNotFound: xyz_unknown_interpreter' - assert any(msg == l for l in result.outlines) + msg = "SKIPPED: python: InterpreterNotFound: xyz_unknown_interpreter" + assert any(msg == l for l in result.outlines), result.outlines setup_result_from_json = json.load(report_path)["testenvs"]["python"]["setup"] for setup_step in setup_result_from_json: assert "InterpreterNotFound" in setup_step["output"] @@ -336,132 +264,138 @@ def test_skip_unknown_interpreter_result_json(cmd, initproj, tmpdir): def test_unknown_dep(cmd, initproj): - initproj("dep123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "dep123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [testenv] deps=qweqwe123 changedir=tests - ''' - }) + """, + }, + ) result = cmd() - assert result.ret - assert result.outlines[-1].startswith('ERROR: python: could not install deps [qweqwe123];') + assert result.ret, "{}\n{}".format(result.err, result.out) + assert result.outlines[-1].startswith("ERROR: python: could not install deps [qweqwe123];") def test_venv_special_chars_issue252(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'tox.ini': ''' + initproj( + "pkg123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "tox.ini": """ [tox] envlist = special&&1 [testenv:special&&1] changedir=tests - ''' - }) + """, + }, + ) result = cmd() - assert result.ret == 0 - pattern = re.compile('special&&1 installed: .*pkg123==0.7.*') - assert any(pattern.match(line) for line in result.outlines) + assert result.ret == 0, "{}\n{}".format(result.err, result.out) + pattern = re.compile("special&&1 installed: .*pkg123==0.7.*") + assert any(pattern.match(line) for line in result.outlines), result.outlines def test_unknown_environment(cmd, initproj): - initproj("env123-0.7", filedefs={ - 'tox.ini': '' - }) + initproj("env123-0.7", filedefs={"tox.ini": ""}) result = cmd("-e", "qpwoei") - assert result.ret + assert result.ret, "{}\n{}".format(result.err, result.out) assert result.out == "ERROR: unknown environment 'qpwoei'\n" -def test_skip_sdist(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'setup.py': """ - syntax error - """, - 'tox.ini': ''' - [tox] - skipsdist=True - [testenv] - commands=python -c "print('done')" - ''' - }) - result = cmd() - assert result.ret == 0 - - def test_minimal_setup_py_empty(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'setup.py': """ + initproj( + "pkg123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "setup.py": """ """, - 'tox.ini': '' - }) + "tox.ini": "", + }, + ) result = cmd() - assert result.ret == 1 - assert result.outlines[-1] == 'ERROR: setup.py is empty' + assert result.ret == 1, "{}\n{}".format(result.err, result.out) + assert result.outlines[-1] == "ERROR: setup.py is empty" def test_minimal_setup_py_comment_only(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'setup.py': """\n# some comment + initproj( + "pkg123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "setup.py": """\n# some comment """, - 'tox.ini': '' - - }) + "tox.ini": "", + }, + ) result = cmd() - assert result.ret == 1 - assert result.outlines[-1] == 'ERROR: setup.py is empty' + assert result.ret == 1, "{}\n{}".format(result.err, result.out) + assert result.outlines[-1] == "ERROR: setup.py is empty" def test_minimal_setup_py_non_functional(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'setup.py': """ + initproj( + "pkg123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "setup.py": """ import sys """, - 'tox.ini': '' - - }) + "tox.ini": "", + }, + ) result = cmd() - assert result.ret == 1 - assert any(re.match(r'.*ERROR.*check setup.py.*', l) for l in result.outlines) + assert result.ret == 1, "{}\n{}".format(result.err, result.out) + assert any(re.match(r".*ERROR.*check setup.py.*", l) for l in result.outlines), result.outlines def test_sdist_fails(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'setup.py': """ + initproj( + "pkg123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "setup.py": """ syntax error """, - 'tox.ini': '', - }) + "tox.ini": "", + }, + ) result = cmd() - assert result.ret - assert any(re.match(r'.*FAIL.*could not package project.*', l) for l in result.outlines) + assert result.ret, "{}\n{}".format(result.err, result.out) + assert any( + re.match(r".*FAIL.*could not package project.*", l) for l in result.outlines + ), result.outlines def test_no_setup_py_exits(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tox.ini': """ + initproj( + "pkg123-0.7", + filedefs={ + "tox.ini": """ [testenv] commands=python -c "2 + 2" """ - }) + }, + ) os.remove("setup.py") result = cmd() - assert result.ret - assert any(re.match(r'.*ERROR.*No setup.py file found.*', l) for l in result.outlines) + assert result.ret, "{}\n{}".format(result.err, result.out) + assert any( + re.match(r".*ERROR.*No setup.py file found.*", l) for l in result.outlines + ), result.outlines def test_package_install_fails(cmd, initproj): - initproj("pkg123-0.7", filedefs={ - 'tests': {'test_hello.py': "def test_hello(): pass"}, - 'setup.py': """ + initproj( + "pkg123-0.7", + filedefs={ + "tests": {"test_hello.py": "def test_hello(): pass"}, + "setup.py": """ from setuptools import setup setup( name='pkg123', @@ -473,42 +407,49 @@ def test_package_install_fails(cmd, initproj): install_requires=['qweqwe123'], ) """, - 'tox.ini': '', - }) + "tox.ini": "", + }, + ) result = cmd() - assert result.ret - assert result.outlines[-1].startswith('ERROR: python: InvocationError for command ') + assert result.ret, "{}\n{}".format(result.err, result.out) + assert result.outlines[-1].startswith("ERROR: python: InvocationError for command ") @pytest.fixture def example123(initproj): - yield initproj("example123-0.5", filedefs={ - 'tests': { - 'test_hello.py': """ + yield initproj( + "example123-0.5", + filedefs={ + "tests": { + "test_hello.py": """ def test_hello(pytestconfig): pass - """, - }, - 'tox.ini': ''' + """ + }, + "tox.ini": """ [testenv] changedir=tests commands= pytest --basetemp={envtmpdir} \ --junitxml=junit-{envname}.xml deps=pytest - ''' - }) + """, + }, + ) def test_toxuone_env(cmd, example123): result = cmd() assert not result.ret - assert re.match(r'.*generated\W+xml\W+file.*junit-python\.xml' - r'.*\W+1\W+passed.*', result.out, re.DOTALL) - result = cmd("-epython", ) + assert re.match( + r".*generated\W+xml\W+file.*junit-python\.xml" r".*\W+1\W+passed.*", result.out, re.DOTALL + ) + result = cmd("-epython") assert not result.ret - assert re.match(r'.*\W+1\W+passed.*' - r'summary.*' - r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + assert re.match( + r".*\W+1\W+passed.*" r"summary.*" r"python:\W+commands\W+succeeded.*", + result.out, + re.DOTALL, + ) def test_different_config_cwd(cmd, example123, monkeypatch): @@ -516,9 +457,11 @@ def test_different_config_cwd(cmd, example123, monkeypatch): monkeypatch.chdir(example123.dirname) result = cmd("-c", "example123/tox.ini") assert not result.ret - assert re.match(r'.*\W+1\W+passed.*' - r'summary.*' - r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + assert re.match( + r".*\W+1\W+passed.*" r"summary.*" r"python:\W+commands\W+succeeded.*", + result.out, + re.DOTALL, + ) def test_json(cmd, example123): @@ -528,42 +471,57 @@ def test_json(cmd, example123): testfile.write("def test_fail(): assert 0") jsonpath = example123.join("res.json") result = cmd("--result-json", jsonpath) - assert result.ret == 1 + assert result.ret == 1, "{}\n{}".format(result.err, result.out) data = json.load(jsonpath.open("r")) verify_json_report_format(data) - assert re.match(r'.*\W+1\W+failed.*' - r'summary.*' - r'python:\W+commands\W+failed.*', result.out, re.DOTALL) + assert re.match( + r".*\W+1\W+failed.*" r"summary.*" r"python:\W+commands\W+failed.*", result.out, re.DOTALL + ) def test_developz(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ - """}) + initproj( + "example123", + filedefs={ + "tox.ini": """ + """ + }, + ) result = cmd("-vv", "--develop") assert not result.ret assert "sdist-make" not in result.out def test_usedevelop(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ + initproj( + "example123", + filedefs={ + "tox.ini": """ [testenv] usedevelop=True - """}) + """ + }, + ) result = cmd("-vv") assert not result.ret assert "sdist-make" not in result.out def test_usedevelop_mixed(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ - [testenv:devenv] + initproj( + "example123", + filedefs={ + "tox.ini": """ + [testenv:dev] usedevelop=True [testenv:nondev] usedevelop=False - """}) + """ + }, + ) - # running only 'devenv' should not do sdist - result = cmd("-vv", "-e", "devenv") + # running only 'dev' should not do sdist + result = cmd("-vv", "-e", "dev") assert not result.ret assert "sdist-make" not in result.out @@ -573,44 +531,59 @@ def test_usedevelop_mixed(initproj, cmd): assert "sdist-make" in result.out +@pytest.mark.parametrize("skipsdist", [False, True]) @pytest.mark.parametrize("src_root", [".", "src"]) -def test_test_usedevelop(cmd, initproj, src_root, monkeypatch): - base = initproj("example123-0.5", src_root=src_root, filedefs={ - 'tests': { - 'test_hello.py': """ +def test_test_usedevelop(cmd, initproj, src_root, skipsdist, monkeypatch): + name = "example123-spameggs" + base = initproj( + (name, "0.5"), + src_root=src_root, + filedefs={ + "tests": { + "test_hello.py": """ def test_hello(pytestconfig): pass - """, - }, - 'tox.ini': ''' + """ + }, + "tox.ini": """ [testenv] usedevelop=True changedir=tests commands= pytest --basetemp={envtmpdir} --junitxml=junit-{envname}.xml [] - deps=pytest - ''' - }) + deps=pytest""" + + """ + skipsdist={} + """.format( + skipsdist + ), + }, + ) result = cmd("-v") assert not result.ret - assert re.match(r'.*generated\W+xml\W+file.*junit-python\.xml' - r'.*\W+1\W+passed.*', result.out, re.DOTALL) + assert re.match( + r".*generated\W+xml\W+file.*junit-python\.xml" r".*\W+1\W+passed.*", result.out, re.DOTALL + ) assert "sdist-make" not in result.out - result = cmd("-epython", ) + result = cmd("-epython") assert not result.ret assert "develop-inst-noop" in result.out - assert re.match(r'.*\W+1\W+passed.*' - r'summary.*' - r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + assert re.match( + r".*\W+1\W+passed.*" r"summary.*" r"python:\W+commands\W+succeeded.*", + result.out, + re.DOTALL, + ) # see that things work with a different CWD monkeypatch.chdir(base.dirname) - result = cmd("-c", "example123/tox.ini") + result = cmd("-c", "{}/tox.ini".format(name)) assert not result.ret assert "develop-inst-noop" in result.out - assert re.match(r'.*\W+1\W+passed.*' - r'summary.*' - r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + assert re.match( + r".*\W+1\W+passed.*" r"summary.*" r"python:\W+commands\W+succeeded.*", + result.out, + re.DOTALL, + ) monkeypatch.chdir(base) # see that tests can also fail and retcode is correct @@ -618,43 +591,58 @@ def test_hello(pytestconfig): assert testfile.check() testfile.write("def test_fail(): assert 0") result = cmd() - assert result.ret + assert result.ret, "{}\n{}".format(result.err, result.out) assert "develop-inst-noop" in result.out - assert re.match(r'.*\W+1\W+failed.*' - r'summary.*' - r'python:\W+commands\W+failed.*', result.out, re.DOTALL) + assert re.match( + r".*\W+1\W+failed.*" r"summary.*" r"python:\W+commands\W+failed.*", result.out, re.DOTALL + ) # test develop is called if setup.py changes setup_py = py.path.local("setup.py") - setup_py.write(setup_py.read() + ' ') + setup_py.write(setup_py.read() + " ") result = cmd() - assert result.ret + assert result.ret, "{}\n{}".format(result.err, result.out) assert "develop-inst-nodeps" in result.out -def _alwayscopy_not_supported(): - # This is due to virtualenv bugs with alwayscopy in some platforms - # see: https://github.com/pypa/virtualenv/issues/565 - if hasattr(platform, 'linux_distribution'): - _dist = platform.linux_distribution(full_distribution_name=False) - (name, version, arch) = _dist - if any((name == 'centos' and version[0] == '7', - name == 'SuSE' and arch == 'x86_64')): - return True - return False +def test_warning_emitted(cmd, initproj): + initproj( + "spam-0.0.1", + filedefs={ + "tox.ini": """ + [testenv] + skipsdist=True + usedevelop=True + """, + "setup.py": """ + from setuptools import setup + from warnings import warn + warn("I am a warning") + + setup(name="spam", version="0.0.1") + """, + }, + ) + result = cmd() + result = cmd() + assert "develop-inst-noop" in result.out + assert "I am a warning" in result.err -@pytest.mark.skipif(_alwayscopy_not_supported(), reason="Platform doesnt support alwayscopy") def test_alwayscopy(initproj, cmd, mocksession): - initproj("example123", filedefs={'tox.ini': """ + initproj( + "example123", + filedefs={ + "tox.ini": """ [testenv] commands={envpython} --version alwayscopy=True - """}) - venv = mocksession.getenv('python') + """ + }, + ) + venv = mocksession.getenv("python") result = cmd("-vv") assert not result.ret - if use_builtin_venv(venv): assert "venv --copies" in result.out else: @@ -662,192 +650,181 @@ def test_alwayscopy(initproj, cmd, mocksession): def test_alwayscopy_default(initproj, cmd, mocksession): - initproj("example123", filedefs={'tox.ini': """ + initproj( + "example123", + filedefs={ + "tox.ini": """ [testenv] commands={envpython} --version - """}) - venv = mocksession.getenv('python') + """ + }, + ) + venv = mocksession.getenv("python") result = cmd("-vv") assert not result.ret - if use_builtin_venv(venv): assert "venv --copies" not in result.out else: assert "virtualenv --always-copy" not in result.out +@pytest.mark.skipif("sys.platform == 'win32'") def test_empty_activity_ignored(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ + initproj( + "example123", + filedefs={ + "tox.ini": """ [testenv] list_dependencies_command=echo commands={envpython} --version - """}) + """ + }, + ) result = cmd() assert not result.ret assert "installed:" not in result.out +@pytest.mark.skipif("sys.platform == 'win32'") def test_empty_activity_shown_verbose(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ + initproj( + "example123", + filedefs={ + "tox.ini": """ [testenv] list_dependencies_command=echo commands={envpython} --version - """}) + """ + }, + ) result = cmd("-v") assert not result.ret assert "installed:" in result.out def test_test_piphelp(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ - # content of: tox.ini - [testenv] - commands=pip -h - [testenv:py27] - basepython=python - [testenv:py36] - basepython=python - """}) - result = cmd() - assert not result.ret + initproj( + "example123", + filedefs={ + "tox.ini": """ + # content of: tox.ini + [testenv] + commands=pip -h + """ + }, + ) + result = cmd("-vv") + assert not result.ret, "{}\n{}".format(result.err, result.err) def test_notest(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ + initproj( + "example123", + filedefs={ + "tox.ini": """ # content of: tox.ini [testenv:py26] basepython=python - """}) + """ + }, + ) result = cmd("-v", "--notest") assert not result.ret - assert re.match(r'.*summary.*' - r'py26\W+skipped\W+tests.*', result.out, re.DOTALL) + assert re.match(r".*summary.*" r"py26\W+skipped\W+tests.*", result.out, re.DOTALL) result = cmd("-v", "--notest", "-epy26") assert not result.ret - assert re.match(r'.*py26\W+reusing.*', result.out, re.DOTALL) + assert re.match(r".*py26\W+reusing.*", result.out, re.DOTALL) + + +def test_notest_setup_py_error(initproj, cmd): + initproj( + "example123", + filedefs={ + "setup.py": """\ + from setuptools import setup + setup(name='x', install_requires=['fakefakefakefakefakefake']), + """, + "tox.ini": "", + }, + ) + result = cmd("--notest") + assert result.ret + assert re.search("ERROR:.*InvocationError", result.out) def test_PYC(initproj, cmd, monkeypatch): - initproj("example123", filedefs={'tox.ini': ''}) - monkeypatch.setenv("PYTHONDOWNWRITEBYTECODE", 1) + initproj("example123", filedefs={"tox.ini": ""}) + monkeypatch.setenv("PYTHONDOWNWRITEBYTECODE", "1") result = cmd("-v", "--notest") assert not result.ret - assert 'create' in result.out + assert "create" in result.out def test_env_VIRTUALENV_PYTHON(initproj, cmd, monkeypatch): - initproj("example123", filedefs={'tox.ini': ''}) - monkeypatch.setenv("VIRTUALENV_PYTHON", '/FOO') + initproj("example123", filedefs={"tox.ini": ""}) + monkeypatch.setenv("VIRTUALENV_PYTHON", "/FOO") result = cmd("-v", "--notest") assert not result.ret, result.outlines - assert 'create' in result.out - - -def test_sdistonly(initproj, cmd): - initproj("example123", filedefs={'tox.ini': """ - """}) - result = cmd("-v", "--sdistonly") - assert not result.ret - assert re.match(r'.*sdist-make.*setup.py.*', result.out, re.DOTALL) - assert "-mvirtualenv" not in result.out - - -def test_separate_sdist_no_sdistfile(cmd, initproj, tmpdir): - distshare = tmpdir.join("distshare") - initproj(("pkg123-foo", "0.7"), filedefs={ - 'tox.ini': """ - [tox] - distshare={} - """.format(distshare) - }) - result = cmd("--sdistonly") - assert not result.ret - distshare_files = distshare.listdir() - assert len(distshare_files) == 1 - sdistfile = distshare_files[0] - assert 'pkg123-foo-0.7.zip' in str(sdistfile) - - -def test_separate_sdist(cmd, initproj, tmpdir): - distshare = tmpdir.join("distshare") - initproj("pkg123-0.7", filedefs={ - 'tox.ini': """ - [tox] - distshare=%s - sdistsrc={distshare}/pkg123-0.7.zip - """ % distshare - }) - result = cmd("--sdistonly") - assert not result.ret - sdistfiles = distshare.listdir() - assert len(sdistfiles) == 1 - sdistfile = sdistfiles[0] - result = cmd("-v", "--notest") - assert not result.ret - assert "python inst: {}".format(sdistfile) in result.out - - -def test_sdist_latest(tmpdir, newconfig): - distshare = tmpdir.join("distshare") - config = newconfig([], """ - [tox] - distshare=%s - sdistsrc={distshare}/pkg123-* - """ % distshare) - p = distshare.ensure("pkg123-1.4.5.zip") - distshare.ensure("pkg123-1.4.5a1.zip") - session = Session(config) - sdist_path = session.get_installpkg_path() - assert sdist_path == p - - -def test_installpkg(tmpdir, newconfig): - p = tmpdir.ensure("pkg123-1.0.zip") - config = newconfig(["--installpkg=%s" % p], "") - session = Session(config) - sdist_path = session.get_installpkg_path() - assert sdist_path == p + assert "create" in result.out def test_envsitepackagesdir(cmd, initproj): - initproj("pkg512-0.0.5", filedefs={ - 'tox.ini': """ + initproj( + "pkg512-0.0.5", + filedefs={ + "tox.ini": """ [testenv] commands= python -c "print(r'X:{envsitepackagesdir}')" - """}) + """ + }, + ) result = cmd() - assert result.ret == 0 - assert re.match(r'.*\nX:.*tox.*site-packages.*', result.out, re.DOTALL) + assert result.ret == 0, "{}\n{}".format(result.err, result.out) + assert re.match(r".*\nX:.*tox.*site-packages.*", result.out, re.DOTALL) def test_envsitepackagesdir_skip_missing_issue280(cmd, initproj): - initproj("pkg513-0.0.5", filedefs={ - 'tox.ini': """ + initproj( + "pkg513-0.0.5", + filedefs={ + "tox.ini": """ [testenv] basepython=/usr/bin/qwelkjqwle commands= {envsitepackagesdir} - """}) + """ + }, + ) result = cmd("--skip-missing-interpreters") - assert result.ret == 0 - assert re.match(r'.*SKIPPED:.*qwelkj.*', result.out, re.DOTALL) + assert result.ret == 0, "{}\n{}".format(result.err, result.out) + assert re.match(r".*SKIPPED:.*qwelkj.*", result.out, re.DOTALL) -@pytest.mark.parametrize('verbosity', ['', '-v', '-vv']) +@pytest.mark.parametrize("verbosity", ["", "-v", "-vv"]) def test_verbosity(cmd, initproj, verbosity): - initproj("pkgX-0.0.5", filedefs={ - 'tox.ini': """ + initproj( + "pkgX-0.0.5", + # Note: This is related to https://github.com/tox-dev/tox#935 + # For some reason, the .egg-info/ directory is interacting with the + # PYTHONPATH on Appveyor, causing the package to *not* be installed + # since pip already thinks it is. By setting the `src_root`, we can + # avoid the issue. + src_root="src", + filedefs={ + "tox.ini": """ [testenv] - """}) + """ + }, + ) result = cmd(verbosity) - assert result.ret == 0 + assert result.ret == 0, "{}\n{}".format(result.err, result.out) needle = "Successfully installed pkgX-0.0.5" - if verbosity == '-vv': - assert any(needle in line for line in result.outlines) + if verbosity == "-vv": + assert any(needle in line for line in result.outlines), result.outlines else: - assert all(needle not in line for line in result.outlines) + assert all(needle not in line for line in result.outlines), result.outlines def verify_json_report_format(data, testenvs=True): @@ -870,24 +847,27 @@ def verify_json_report_format(data, testenvs=True): def test_envtmpdir(initproj, cmd): - initproj("foo", filedefs={ - # This file first checks that envtmpdir is existent and empty. Then it - # creates an empty file in that directory. The tox command is run - # twice below, so this is to test whether the directory is cleared - # before the second run. - 'check_empty_envtmpdir.py': '''if True: + initproj( + "foo", + filedefs={ + # This file first checks that envtmpdir is existent and empty. Then it + # creates an empty file in that directory. The tox command is run + # twice below, so this is to test whether the directory is cleared + # before the second run. + "check_empty_envtmpdir.py": """if True: import os from sys import argv envtmpdir = argv[1] assert os.path.exists(envtmpdir) assert os.listdir(envtmpdir) == [] open(os.path.join(envtmpdir, 'test'), 'w').close() - ''', - 'tox.ini': ''' + """, + "tox.ini": """ [testenv] commands=python check_empty_envtmpdir.py {envtmpdir} - ''' - }) + """, + }, + ) result = cmd() assert not result.ret @@ -897,54 +877,64 @@ def test_envtmpdir(initproj, cmd): def test_missing_env_fails(initproj, cmd): - initproj("foo", filedefs={'tox.ini': "[testenv:foo]\ncommands={env:VAR}"}) + initproj("foo", filedefs={"tox.ini": "[testenv:foo]\ncommands={env:VAR}"}) result = cmd() - assert result.ret == 1 - assert result.out.endswith("foo: unresolvable substitution(s): 'VAR'." - " Environment variables are missing or defined recursively.\n") + assert result.ret == 1, "{}\n{}".format(result.err, result.out) + assert result.out.endswith( + "foo: unresolvable substitution(s): 'VAR'." + " Environment variables are missing or defined recursively.\n" + ) -def test_tox_console_script(): - result = subprocess.check_call(['tox', '--help']) +def test_tox_console_script(initproj): + initproj("help", filedefs={"tox.ini": ""}) + result = subprocess.check_call(["tox", "--help"]) assert result == 0 -def test_tox_quickstart_script(): - result = subprocess.check_call(['tox-quickstart', '--help']) +def test_tox_quickstart_script(initproj): + initproj("help", filedefs={"tox.ini": ""}) + result = subprocess.check_call(["tox-quickstart", "--help"]) assert result == 0 -def test_tox_cmdline_no_args(monkeypatch): - monkeypatch.setattr(sys, 'argv', ['caller_script', '--help']) +def test_tox_cmdline_no_args(monkeypatch, initproj): + initproj("help", filedefs={"tox.ini": ""}) + monkeypatch.setattr(sys, "argv", ["caller_script", "--help"]) with pytest.raises(SystemExit): tox.cmdline() -def test_tox_cmdline_args(monkeypatch): +def test_tox_cmdline_args(initproj): + initproj("help", filedefs={"tox.ini": ""}) with pytest.raises(SystemExit): - tox.cmdline(['caller_script', '--help']) + tox.cmdline(["caller_script", "--help"]) -@pytest.mark.parametrize('exit_code', [0, 6]) +@pytest.mark.parametrize("exit_code", [0, 6]) def test_exit_code(initproj, cmd, exit_code, mocker): """ Check for correct InvocationError, with exit code, except for zero exit code """ - mocker.spy(tox, '_exit_code_str') - tox_ini_content = "[testenv:foo]\ncommands=python -c 'import sys; sys.exit(%d)'" % exit_code - initproj("foo", filedefs={'tox.ini': tox_ini_content}) + import tox.exception + + mocker.spy(tox.exception, "exit_code_str") + tox_ini_content = "[testenv:foo]\ncommands=python -c 'import sys; sys.exit({:d})'".format( + exit_code + ) + initproj("foo", filedefs={"tox.ini": tox_ini_content}) cmd() if exit_code: # need mocker.spy above - assert tox._exit_code_str.call_count == 1 - (args, kwargs) = tox._exit_code_str.call_args + assert tox.exception.exit_code_str.call_count == 1 + (args, kwargs) = tox.exception.exit_code_str.call_args assert kwargs == {} (call_error_name, call_command, call_exit_code) = args - assert call_error_name == 'InvocationError' + assert call_error_name == "InvocationError" # quotes are removed in result.out # do not include "python" as it is changed to python.EXE by appveyor - expected_command_arg = ' -c import sys; sys.exit(%d)' % exit_code + expected_command_arg = " -c import sys; sys.exit({:d})".format(exit_code) assert expected_command_arg in call_command assert call_exit_code == exit_code else: # need mocker.spy above - assert tox._exit_code_str.call_count == 0 + assert tox.exception.exit_code_str.call_count == 0 diff --git a/tox.ini b/tox.ini index 3d888c6..1114f6f 100644 --- a/tox.ini +++ b/tox.ini @@ -8,16 +8,14 @@ commands = pytest {posargs:tests} setenv = PYTHONDONTWRITEBYTECODE=1 deps = - tox[testing]==3.0.0 - pytest==3.7 + tox[testing]==3.7.0 [testenv:coverage] commands = coverage run -m pytest {posargs:tests} usedevelop = True deps = + {[testenv]deps} coverage - tox[testing]==3.0.0 - pytest==3.7 ; Don't lint tests, since they're mostly copied from tox