1import os 2import sys 3 4import py 5import pytest 6 7import tox 8from tox.interpreters import NoInterpreterInfo 9from tox.session.commands.run.sequential import installpkg, runtestenv 10from tox.venv import ( 11 CreationConfig, 12 VirtualEnv, 13 getdigest, 14 prepend_shebang_interpreter, 15 tox_testenv_create, 16 tox_testenv_install_deps, 17) 18 19 20def test_getdigest(tmpdir): 21 assert getdigest(tmpdir) == "0" * 32 22 23 24def test_getsupportedinterpreter(monkeypatch, newconfig, mocksession): 25 config = newconfig( 26 [], 27 """\ 28 [testenv:python] 29 basepython={} 30 """.format( 31 sys.executable 32 ), 33 ) 34 mocksession.new_config(config) 35 venv = mocksession.getvenv("python") 36 interp = venv.getsupportedinterpreter() 37 # realpath needed for debian symlinks 38 assert py.path.local(interp).realpath() == py.path.local(sys.executable).realpath() 39 monkeypatch.setattr(tox.INFO, "IS_WIN", True) 40 monkeypatch.setattr(venv.envconfig, "basepython", "jython") 41 with pytest.raises(tox.exception.UnsupportedInterpreter): 42 venv.getsupportedinterpreter() 43 monkeypatch.undo() 44 monkeypatch.setattr(venv.envconfig, "envname", "py1") 45 monkeypatch.setattr(venv.envconfig, "basepython", "notexisting") 46 with pytest.raises(tox.exception.InterpreterNotFound): 47 venv.getsupportedinterpreter() 48 monkeypatch.undo() 49 # check that we properly report when no version_info is present 50 info = NoInterpreterInfo(name=venv.name) 51 info.executable = "something" 52 monkeypatch.setattr(config.interpreters, "get_info", lambda *args, **kw: info) 53 with pytest.raises(tox.exception.InvocationError): 54 venv.getsupportedinterpreter() 55 56 57def test_create(mocksession, newconfig): 58 config = newconfig( 59 [], 60 """\ 61 [testenv:py123] 62 """, 63 ) 64 envconfig = config.envconfigs["py123"] 65 mocksession.new_config(config) 66 venv = mocksession.getvenv("py123") 67 assert venv.path == envconfig.envdir 68 assert not venv.path.check() 69 with mocksession.newaction(venv.name, "getenv") as action: 70 tox_testenv_create(action=action, venv=venv) 71 pcalls = mocksession._pcalls 72 assert len(pcalls) >= 1 73 args = pcalls[0].args 74 assert "virtualenv" == str(args[2]) 75 if not tox.INFO.IS_WIN: 76 # realpath is needed for stuff like the debian symlinks 77 our_sys_path = py.path.local(sys.executable).realpath() 78 assert our_sys_path == py.path.local(args[0]).realpath() 79 # assert Envconfig.toxworkdir in args 80 assert venv.getcommandpath("easy_install", cwd=py.path.local()) 81 interp = venv._getliveconfig().base_resolved_python_path 82 assert interp == venv.envconfig.python_info.executable 83 assert venv.path_config.check(exists=False) 84 85 86def test_create_KeyboardInterrupt(mocksession, newconfig, mocker): 87 config = newconfig( 88 [], 89 """\ 90 [testenv:py123] 91 """, 92 ) 93 mocksession.new_config(config) 94 venv = mocksession.getvenv("py123") 95 with mocker.patch.object(venv, "_pcall", side_effect=KeyboardInterrupt): 96 with pytest.raises(KeyboardInterrupt): 97 venv.setupenv() 98 99 assert venv.status == "keyboardinterrupt" 100 101 102def test_commandpath_venv_precedence(tmpdir, monkeypatch, mocksession, newconfig): 103 config = newconfig( 104 [], 105 """\ 106 [testenv:py123] 107 """, 108 ) 109 mocksession.new_config(config) 110 venv = mocksession.getvenv("py123") 111 envconfig = venv.envconfig 112 tmpdir.ensure("easy_install") 113 monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep) 114 envconfig.envbindir.ensure("easy_install") 115 p = venv.getcommandpath("easy_install") 116 assert py.path.local(p).relto(envconfig.envbindir), p 117 118 119def test_create_sitepackages(mocksession, newconfig): 120 config = newconfig( 121 [], 122 """\ 123 [testenv:site] 124 sitepackages=True 125 126 [testenv:nosite] 127 sitepackages=False 128 """, 129 ) 130 mocksession.new_config(config) 131 venv = mocksession.getvenv("site") 132 with mocksession.newaction(venv.name, "getenv") as action: 133 tox_testenv_create(action=action, venv=venv) 134 pcalls = mocksession._pcalls 135 assert len(pcalls) >= 1 136 args = pcalls[0].args 137 assert "--system-site-packages" in map(str, args) 138 mocksession._clearmocks() 139 140 venv = mocksession.getvenv("nosite") 141 with mocksession.newaction(venv.name, "getenv") as action: 142 tox_testenv_create(action=action, venv=venv) 143 pcalls = mocksession._pcalls 144 assert len(pcalls) >= 1 145 args = pcalls[0].args 146 assert "--system-site-packages" not in map(str, args) 147 assert "--no-site-packages" not in map(str, args) 148 149 150def test_install_deps_wildcard(newmocksession): 151 mocksession = newmocksession( 152 [], 153 """\ 154 [tox] 155 distshare = {toxworkdir}/distshare 156 [testenv:py123] 157 deps= 158 {distshare}/dep1-* 159 """, 160 ) 161 venv = mocksession.getvenv("py123") 162 with mocksession.newaction(venv.name, "getenv") as action: 163 tox_testenv_create(action=action, venv=venv) 164 pcalls = mocksession._pcalls 165 assert len(pcalls) == 1 166 distshare = venv.envconfig.config.distshare 167 distshare.ensure("dep1-1.0.zip") 168 distshare.ensure("dep1-1.1.zip") 169 170 tox_testenv_install_deps(action=action, venv=venv) 171 assert len(pcalls) == 2 172 args = pcalls[-1].args 173 assert pcalls[-1].cwd == venv.envconfig.config.toxinidir 174 175 assert py.path.local.sysfind("python") == args[0] 176 assert ["-m", "pip"] == args[1:3] 177 assert args[3] == "install" 178 args = [arg for arg in args if str(arg).endswith("dep1-1.1.zip")] 179 assert len(args) == 1 180 181 182def test_install_deps_indexserver(newmocksession): 183 mocksession = newmocksession( 184 [], 185 """\ 186 [tox] 187 indexserver = 188 abc = ABC 189 abc2 = ABC 190 [testenv:py123] 191 deps= 192 dep1 193 :abc:dep2 194 :abc2:dep3 195 """, 196 ) 197 venv = mocksession.getvenv("py123") 198 with mocksession.newaction(venv.name, "getenv") as action: 199 tox_testenv_create(action=action, venv=venv) 200 pcalls = mocksession._pcalls 201 assert len(pcalls) == 1 202 pcalls[:] = [] 203 204 tox_testenv_install_deps(action=action, venv=venv) 205 # two different index servers, two calls 206 assert len(pcalls) == 3 207 args = " ".join(pcalls[0].args) 208 assert "-i " not in args 209 assert "dep1" in args 210 211 args = " ".join(pcalls[1].args) 212 assert "-i ABC" in args 213 assert "dep2" in args 214 args = " ".join(pcalls[2].args) 215 assert "-i ABC" in args 216 assert "dep3" in args 217 218 219def test_install_deps_pre(newmocksession): 220 mocksession = newmocksession( 221 [], 222 """\ 223 [testenv] 224 pip_pre=true 225 deps= 226 dep1 227 """, 228 ) 229 venv = mocksession.getvenv("python") 230 with mocksession.newaction(venv.name, "getenv") as action: 231 tox_testenv_create(action=action, venv=venv) 232 pcalls = mocksession._pcalls 233 assert len(pcalls) == 1 234 pcalls[:] = [] 235 236 tox_testenv_install_deps(action=action, venv=venv) 237 assert len(pcalls) == 1 238 args = " ".join(pcalls[0].args) 239 assert "--pre " in args 240 assert "dep1" in args 241 242 243def test_installpkg_indexserver(newmocksession, tmpdir): 244 mocksession = newmocksession( 245 [], 246 """\ 247 [tox] 248 indexserver = 249 default = ABC 250 """, 251 ) 252 venv = mocksession.getvenv("python") 253 pcalls = mocksession._pcalls 254 p = tmpdir.ensure("distfile.tar.gz") 255 installpkg(venv, p) 256 # two different index servers, two calls 257 assert len(pcalls) == 1 258 args = " ".join(pcalls[0].args) 259 assert "-i ABC" in args 260 261 262def test_install_recreate(newmocksession, tmpdir): 263 pkg = tmpdir.ensure("package.tar.gz") 264 mocksession = newmocksession( 265 ["--recreate"], 266 """\ 267 [testenv] 268 deps=xyz 269 """, 270 ) 271 venv = mocksession.getvenv("python") 272 273 with mocksession.newaction(venv.name, "update") as action: 274 venv.update(action) 275 installpkg(venv, pkg) 276 mocksession.report.expect("verbosity0", "*create*") 277 venv.update(action) 278 mocksession.report.expect("verbosity0", "*recreate*") 279 280 281def test_install_sdist_extras(newmocksession): 282 mocksession = newmocksession( 283 [], 284 """\ 285 [testenv] 286 extras = testing 287 development 288 """, 289 ) 290 venv = mocksession.getvenv("python") 291 with mocksession.newaction(venv.name, "getenv") as action: 292 tox_testenv_create(action=action, venv=venv) 293 pcalls = mocksession._pcalls 294 assert len(pcalls) == 1 295 pcalls[:] = [] 296 297 venv.installpkg("distfile.tar.gz", action=action) 298 assert "distfile.tar.gz[testing,development]" in pcalls[-1].args 299 300 301def test_develop_extras(newmocksession, tmpdir): 302 mocksession = newmocksession( 303 [], 304 """\ 305 [testenv] 306 extras = testing 307 development 308 """, 309 ) 310 venv = mocksession.getvenv("python") 311 with mocksession.newaction(venv.name, "getenv") as action: 312 tox_testenv_create(action=action, venv=venv) 313 pcalls = mocksession._pcalls 314 assert len(pcalls) == 1 315 pcalls[:] = [] 316 317 venv.developpkg(tmpdir, action=action) 318 expected = "{}[testing,development]".format(tmpdir.strpath) 319 assert expected in pcalls[-1].args 320 321 322def test_env_variables_added_to_needs_reinstall(tmpdir, mocksession, newconfig, monkeypatch): 323 tmpdir.ensure("setup.py") 324 monkeypatch.setenv("TEMP_PASS_VAR", "123") 325 monkeypatch.setenv("TEMP_NOPASS_VAR", "456") 326 config = newconfig( 327 [], 328 """\ 329 [testenv:python] 330 passenv = temp_pass_var 331 setenv = 332 CUSTOM_VAR = 789 333 """, 334 ) 335 mocksession.new_config(config) 336 venv = mocksession.getvenv("python") 337 with mocksession.newaction(venv.name, "hello") as action: 338 venv._needs_reinstall(tmpdir, action) 339 340 pcalls = mocksession._pcalls 341 assert len(pcalls) == 2 342 env = pcalls[0].env 343 344 # should have access to setenv vars 345 assert "CUSTOM_VAR" in env 346 assert env["CUSTOM_VAR"] == "789" 347 348 # should have access to passenv vars 349 assert "TEMP_PASS_VAR" in env 350 assert env["TEMP_PASS_VAR"] == "123" 351 352 # should also have access to full invocation environment, 353 # for backward compatibility, and to match behavior of venv.run_install_command() 354 assert "TEMP_NOPASS_VAR" in env 355 assert env["TEMP_NOPASS_VAR"] == "456" 356 357 358def test_test_hashseed_is_in_output(newmocksession, monkeypatch): 359 seed = "123456789" 360 monkeypatch.setattr("tox.config.make_hashseed", lambda: seed) 361 mocksession = newmocksession([], "") 362 venv = mocksession.getvenv("python") 363 with mocksession.newaction(venv.name, "update") as action: 364 venv.update(action) 365 tox.venv.tox_runtest_pre(venv) 366 mocksession.report.expect("verbosity0", "run-test-pre: PYTHONHASHSEED='{}'".format(seed)) 367 368 369def test_test_runtests_action_command_is_in_output(newmocksession): 370 mocksession = newmocksession( 371 [], 372 """\ 373 [testenv] 374 commands = echo foo bar 375 """, 376 ) 377 venv = mocksession.getvenv("python") 378 with mocksession.newaction(venv.name, "update") as action: 379 venv.update(action) 380 venv.test() 381 mocksession.report.expect("verbosity0", "*run-test:*commands?0? | echo foo bar") 382 383 384def test_install_error(newmocksession): 385 mocksession = newmocksession( 386 ["--recreate"], 387 """\ 388 [testenv] 389 deps=xyz 390 commands= 391 qwelkqw 392 """, 393 ) 394 venv = mocksession.getvenv("python") 395 venv.test() 396 mocksession.report.expect("error", "*not find*qwelkqw*") 397 assert venv.status == "commands failed" 398 399 400def test_install_command_not_installed(newmocksession): 401 mocksession = newmocksession( 402 ["--recreate"], 403 """\ 404 [testenv] 405 commands= 406 pytest 407 """, 408 ) 409 venv = mocksession.getvenv("python") 410 venv.status = 0 411 venv.test() 412 mocksession.report.expect("warning", "*test command found but not*") 413 assert venv.status == 0 414 415 416def test_install_command_whitelisted(newmocksession): 417 mocksession = newmocksession( 418 ["--recreate"], 419 """\ 420 [testenv] 421 whitelist_externals = pytest 422 xy* 423 commands= 424 pytest 425 xyz 426 """, 427 ) 428 venv = mocksession.getvenv("python") 429 venv.test() 430 mocksession.report.expect("warning", "*test command found but not*", invert=True) 431 assert venv.status == "commands failed" 432 433 434def test_install_command_not_installed_bash(newmocksession): 435 mocksession = newmocksession( 436 ["--recreate"], 437 """\ 438 [testenv] 439 commands= 440 bash 441 """, 442 ) 443 venv = mocksession.getvenv("python") 444 venv.test() 445 mocksession.report.expect("warning", "*test command found but not*") 446 447 448def test_install_python3(newmocksession): 449 if not py.path.local.sysfind("python3"): 450 pytest.skip("needs python3") 451 mocksession = newmocksession( 452 [], 453 """\ 454 [testenv:py123] 455 basepython=python3 456 deps= 457 dep1 458 dep2 459 """, 460 ) 461 venv = mocksession.getvenv("py123") 462 with mocksession.newaction(venv.name, "getenv") as action: 463 tox_testenv_create(action=action, venv=venv) 464 pcalls = mocksession._pcalls 465 assert len(pcalls) == 1 466 args = pcalls[0].args 467 assert str(args[2]) == "virtualenv" 468 pcalls[:] = [] 469 with mocksession.newaction(venv.name, "hello") as action: 470 venv._install(["hello"], action=action) 471 assert len(pcalls) == 1 472 args = pcalls[0].args 473 assert py.path.local.sysfind("python") == args[0] 474 assert ["-m", "pip"] == args[1:3] 475 for _ in args: 476 assert "--download-cache" not in args, args 477 478 479class TestCreationConfig: 480 def test_basic(self, newconfig, mocksession, tmpdir): 481 config = newconfig([], "") 482 mocksession.new_config(config) 483 venv = mocksession.getvenv("python") 484 cconfig = venv._getliveconfig() 485 assert cconfig.matches(cconfig) 486 path = tmpdir.join("configdump") 487 cconfig.writeconfig(path) 488 newconfig = CreationConfig.readconfig(path) 489 assert newconfig.matches(cconfig) 490 assert cconfig.matches(newconfig) 491 492 def test_matchingdependencies(self, newconfig, mocksession): 493 config = newconfig( 494 [], 495 """\ 496 [testenv] 497 deps=abc 498 """, 499 ) 500 mocksession.new_config(config) 501 venv = mocksession.getvenv("python") 502 cconfig = venv._getliveconfig() 503 config = newconfig( 504 [], 505 """\ 506 [testenv] 507 deps=xyz 508 """, 509 ) 510 mocksession.new_config(config) 511 venv = mocksession.getvenv("python") 512 otherconfig = venv._getliveconfig() 513 assert not cconfig.matches(otherconfig) 514 515 def test_matchingdependencies_file(self, newconfig, mocksession): 516 config = newconfig( 517 [], 518 """\ 519 [tox] 520 distshare={toxworkdir}/distshare 521 [testenv] 522 deps=abc 523 {distshare}/xyz.zip 524 """, 525 ) 526 xyz = config.distshare.join("xyz.zip") 527 xyz.ensure() 528 mocksession.new_config(config) 529 venv = mocksession.getvenv("python") 530 cconfig = venv._getliveconfig() 531 assert cconfig.matches(cconfig) 532 xyz.write("hello") 533 newconfig = venv._getliveconfig() 534 assert not cconfig.matches(newconfig) 535 536 def test_matchingdependencies_latest(self, newconfig, mocksession): 537 config = newconfig( 538 [], 539 """\ 540 [tox] 541 distshare={toxworkdir}/distshare 542 [testenv] 543 deps={distshare}/xyz-* 544 """, 545 ) 546 config.distshare.ensure("xyz-1.2.0.zip") 547 xyz2 = config.distshare.ensure("xyz-1.2.1.zip") 548 mocksession.new_config(config) 549 venv = mocksession.getvenv("python") 550 cconfig = venv._getliveconfig() 551 md5, path = cconfig.deps[0] 552 assert path == xyz2 553 assert md5 == path.computehash() 554 555 def test_python_recreation(self, tmpdir, newconfig, mocksession): 556 pkg = tmpdir.ensure("package.tar.gz") 557 config = newconfig(["-v"], "") 558 mocksession.new_config(config) 559 venv = mocksession.getvenv("python") 560 create_config = venv._getliveconfig() 561 with mocksession.newaction(venv.name, "update") as action: 562 venv.update(action) 563 assert not venv.path_config.check() 564 installpkg(venv, pkg) 565 assert venv.path_config.check() 566 assert mocksession._pcalls 567 args1 = map(str, mocksession._pcalls[0].args) 568 assert "virtualenv" in " ".join(args1) 569 mocksession.report.expect("*", "*create*") 570 # modify config and check that recreation happens 571 mocksession._clearmocks() 572 with mocksession.newaction(venv.name, "update") as action: 573 venv.update(action) 574 mocksession.report.expect("*", "*reusing*") 575 mocksession._clearmocks() 576 with mocksession.newaction(venv.name, "update") as action: 577 create_config.base_resolved_python_path = py.path.local("balla") 578 create_config.writeconfig(venv.path_config) 579 venv.update(action) 580 mocksession.report.expect("verbosity0", "*recreate*") 581 582 def test_dep_recreation(self, newconfig, mocksession): 583 config = newconfig([], "") 584 mocksession.new_config(config) 585 venv = mocksession.getvenv("python") 586 with mocksession.newaction(venv.name, "update") as action: 587 venv.update(action) 588 cconfig = venv._getliveconfig() 589 cconfig.deps[:] = [("1" * 32, "xyz.zip")] 590 cconfig.writeconfig(venv.path_config) 591 mocksession._clearmocks() 592 with mocksession.newaction(venv.name, "update") as action: 593 venv.update(action) 594 mocksession.report.expect("*", "*recreate*") 595 596 def test_develop_recreation(self, newconfig, mocksession): 597 config = newconfig([], "") 598 mocksession.new_config(config) 599 venv = mocksession.getvenv("python") 600 with mocksession.newaction(venv.name, "update") as action: 601 venv.update(action) 602 cconfig = venv._getliveconfig() 603 cconfig.usedevelop = True 604 cconfig.writeconfig(venv.path_config) 605 mocksession._clearmocks() 606 with mocksession.newaction(venv.name, "update") as action: 607 venv.update(action) 608 mocksession.report.expect("verbosity0", "*recreate*") 609 610 611class TestVenvTest: 612 def test_envbindir_path(self, newmocksession, monkeypatch): 613 monkeypatch.setenv("PIP_RESPECT_VIRTUALENV", "1") 614 mocksession = newmocksession( 615 [], 616 """\ 617 [testenv:python] 618 commands=abc 619 """, 620 ) 621 venv = mocksession.getvenv("python") 622 with mocksession.newaction(venv.name, "getenv") as action: 623 monkeypatch.setenv("PATH", "xyz") 624 sysfind_calls = [] 625 monkeypatch.setattr( 626 "py.path.local.sysfind", 627 classmethod(lambda *args, **kwargs: sysfind_calls.append(kwargs) or 0 / 0), 628 ) 629 630 with pytest.raises(ZeroDivisionError): 631 venv._install(list("123"), action=action) 632 assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir] 633 with pytest.raises(ZeroDivisionError): 634 venv.test(action) 635 assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir] 636 with pytest.raises(ZeroDivisionError): 637 venv.run_install_command(["qwe"], action=action) 638 assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir] 639 monkeypatch.setenv("PIP_RESPECT_VIRTUALENV", "1") 640 monkeypatch.setenv("PIP_REQUIRE_VIRTUALENV", "1") 641 monkeypatch.setenv("__PYVENV_LAUNCHER__", "1") 642 643 prev_pcall = venv._pcall 644 645 def collect(*args, **kwargs): 646 env = kwargs["env"] 647 assert "PIP_RESPECT_VIRTUALENV" not in env 648 assert "PIP_REQUIRE_VIRTUALENV" not in env 649 assert "__PYVENV_LAUNCHER__" not in env 650 assert env["PIP_USER"] == "0" 651 assert env["PIP_NO_DEPS"] == "0" 652 return prev_pcall(*args, **kwargs) 653 654 monkeypatch.setattr(venv, "_pcall", collect) 655 with pytest.raises(ZeroDivisionError): 656 venv.run_install_command(["qwe"], action=action) 657 658 def test_pythonpath_remove(self, newmocksession, monkeypatch, caplog): 659 monkeypatch.setenv("PYTHONPATH", "/my/awesome/library") 660 mocksession = newmocksession( 661 [], 662 """\ 663 [testenv:python] 664 commands=abc 665 """, 666 ) 667 venv = mocksession.getvenv("python") 668 with mocksession.newaction(venv.name, "getenv") as action: 669 venv.run_install_command(["qwe"], action=action) 670 mocksession.report.expect("warning", "*Discarding $PYTHONPATH from environment*") 671 672 pcalls = mocksession._pcalls 673 assert len(pcalls) == 1 674 assert "PYTHONPATH" not in pcalls[0].env 675 676 def test_pythonpath_keep(self, newmocksession, monkeypatch, caplog): 677 # passenv = PYTHONPATH allows PYTHONPATH to stay in environment 678 monkeypatch.setenv("PYTHONPATH", "/my/awesome/library") 679 mocksession = newmocksession( 680 [], 681 """\ 682 [testenv:python] 683 commands=abc 684 passenv = PYTHONPATH 685 """, 686 ) 687 venv = mocksession.getvenv("python") 688 with mocksession.newaction(venv.name, "getenv") as action: 689 venv.run_install_command(["qwe"], action=action) 690 mocksession.report.not_expect("warning", "*Discarding $PYTHONPATH from environment*") 691 assert "PYTHONPATH" in os.environ 692 693 pcalls = mocksession._pcalls 694 assert len(pcalls) == 1 695 assert pcalls[0].env["PYTHONPATH"] == "/my/awesome/library" 696 697 def test_pythonpath_empty(self, newmocksession, monkeypatch, caplog): 698 monkeypatch.setenv("PYTHONPATH", "") 699 mocksession = newmocksession( 700 [], 701 """\ 702 [testenv:python] 703 commands=abc 704 """, 705 ) 706 venv = mocksession.getvenv("python") 707 with mocksession.newaction(venv.name, "getenv") as action: 708 venv.run_install_command(["qwe"], action=action) 709 if sys.version_info < (3, 4): 710 mocksession.report.expect("warning", "*Discarding $PYTHONPATH from environment*") 711 else: 712 with pytest.raises(AssertionError): 713 mocksession.report.expect("warning", "*Discarding $PYTHONPATH from environment*") 714 pcalls = mocksession._pcalls 715 assert len(pcalls) == 1 716 assert "PYTHONPATH" not in pcalls[0].env 717 718 719def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatch): 720 monkeypatch.delenv("PYTHONPATH", raising=False) 721 pkg = tmpdir.ensure("package.tar.gz") 722 monkeypatch.setenv("X123", "123") 723 monkeypatch.setenv("YY", "456") 724 config = newconfig( 725 [], 726 """\ 727 [testenv:python] 728 commands=python -V 729 passenv = x123 730 setenv = 731 ENV_VAR = value 732 PYTHONPATH = value 733 """, 734 ) 735 mocksession._clearmocks() 736 mocksession.new_config(config) 737 venv = mocksession.getvenv("python") 738 installpkg(venv, pkg) 739 venv.test() 740 741 pcalls = mocksession._pcalls 742 assert len(pcalls) == 2 743 for x in pcalls: 744 env = x.env 745 assert env is not None 746 assert "ENV_VAR" in env 747 assert env["ENV_VAR"] == "value" 748 assert env["VIRTUAL_ENV"] == str(venv.path) 749 assert env["X123"] == "123" 750 assert "PYTHONPATH" in env 751 assert env["PYTHONPATH"] == "value" 752 # all env variables are passed for installation 753 assert pcalls[0].env["YY"] == "456" 754 assert "YY" not in pcalls[1].env 755 756 assert {"ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"}.issubset(pcalls[1].env) 757 758 # setenv does not trigger PYTHONPATH warnings 759 mocksession.report.not_expect("warning", "*Discarding $PYTHONPATH from environment*") 760 761 # for e in os.environ: 762 # assert e in env 763 764 765def test_installpkg_no_upgrade(tmpdir, newmocksession): 766 pkg = tmpdir.ensure("package.tar.gz") 767 mocksession = newmocksession([], "") 768 venv = mocksession.getvenv("python") 769 venv.just_created = True 770 venv.envconfig.envdir.ensure(dir=1) 771 installpkg(venv, pkg) 772 pcalls = mocksession._pcalls 773 assert len(pcalls) == 1 774 assert pcalls[0].args[1:-1] == ["-m", "pip", "install", "--exists-action", "w"] 775 776 777@pytest.mark.parametrize("count, level", [(0, 0), (1, 0), (2, 0), (3, 1), (4, 2), (5, 3), (6, 3)]) 778def test_install_command_verbosity(tmpdir, newmocksession, count, level): 779 pkg = tmpdir.ensure("package.tar.gz") 780 mock_session = newmocksession(["-{}".format("v" * count)], "") 781 env = mock_session.getvenv("python") 782 env.just_created = True 783 env.envconfig.envdir.ensure(dir=1) 784 installpkg(env, pkg) 785 pcalls = mock_session._pcalls 786 assert len(pcalls) == 1 787 expected = ["-m", "pip", "install", "--exists-action", "w"] + (["-v"] * level) 788 assert pcalls[0].args[1:-1] == expected 789 790 791def test_installpkg_upgrade(newmocksession, tmpdir): 792 pkg = tmpdir.ensure("package.tar.gz") 793 mocksession = newmocksession([], "") 794 venv = mocksession.getvenv("python") 795 assert not hasattr(venv, "just_created") 796 installpkg(venv, pkg) 797 pcalls = mocksession._pcalls 798 assert len(pcalls) == 1 799 index = pcalls[0].args.index(pkg.basename) 800 assert index >= 0 801 assert "-U" in pcalls[0].args[:index] 802 assert "--no-deps" in pcalls[0].args[:index] 803 804 805def test_run_install_command(newmocksession): 806 mocksession = newmocksession([], "") 807 venv = mocksession.getvenv("python") 808 venv.just_created = True 809 venv.envconfig.envdir.ensure(dir=1) 810 with mocksession.newaction(venv.name, "hello") as action: 811 venv.run_install_command(packages=["whatever"], action=action) 812 pcalls = mocksession._pcalls 813 assert len(pcalls) == 1 814 args = pcalls[0].args 815 assert py.path.local.sysfind("python") == args[0] 816 assert ["-m", "pip"] == args[1:3] 817 assert "install" in args 818 env = pcalls[0].env 819 assert env is not None 820 821 822def test_run_custom_install_command(newmocksession): 823 mocksession = newmocksession( 824 [], 825 """\ 826 [testenv] 827 install_command=easy_install {opts} {packages} 828 """, 829 ) 830 venv = mocksession.getvenv("python") 831 venv.just_created = True 832 venv.envconfig.envdir.ensure(dir=1) 833 with mocksession.newaction(venv.name, "hello") as action: 834 venv.run_install_command(packages=["whatever"], action=action) 835 pcalls = mocksession._pcalls 836 assert len(pcalls) == 1 837 assert "easy_install" in pcalls[0].args[0] 838 assert pcalls[0].args[1:] == ["whatever"] 839 840 841def test_command_relative_issue36(newmocksession, tmpdir, monkeypatch): 842 mocksession = newmocksession( 843 [], 844 """\ 845 [testenv] 846 """, 847 ) 848 x = tmpdir.ensure("x") 849 venv = mocksession.getvenv("python") 850 x2 = venv.getcommandpath("./x", cwd=tmpdir) 851 assert x == x2 852 mocksession.report.not_expect("warning", "*test command found but not*") 853 x3 = venv.getcommandpath("/bin/bash", cwd=tmpdir) 854 assert x3 == "/bin/bash" 855 mocksession.report.not_expect("warning", "*test command found but not*") 856 monkeypatch.setenv("PATH", str(tmpdir)) 857 x4 = venv.getcommandpath("x", cwd=tmpdir) 858 assert x4.endswith(os.sep + "x") 859 mocksession.report.expect("warning", "*test command found but not*") 860 861 862def test_ignore_outcome_failing_cmd(newmocksession): 863 mocksession = newmocksession( 864 [], 865 """\ 866 [testenv] 867 commands=testenv_fail 868 ignore_outcome=True 869 """, 870 ) 871 872 venv = mocksession.getvenv("python") 873 venv.test() 874 assert venv.status == "ignored failed command" 875 mocksession.report.expect("warning", "*command failed but result from testenv is ignored*") 876 877 878def test_tox_testenv_create(newmocksession): 879 log = [] 880 881 class Plugin: 882 @tox.hookimpl 883 def tox_testenv_create(self, action, venv): 884 assert isinstance(action, tox.session.Action) 885 assert isinstance(venv, VirtualEnv) 886 log.append(1) 887 888 @tox.hookimpl 889 def tox_testenv_install_deps(self, action, venv): 890 assert isinstance(action, tox.session.Action) 891 assert isinstance(venv, VirtualEnv) 892 log.append(2) 893 894 mocksession = newmocksession( 895 [], 896 """\ 897 [testenv] 898 commands=testenv_fail 899 ignore_outcome=True 900 """, 901 plugins=[Plugin()], 902 ) 903 904 venv = mocksession.getvenv("python") 905 with mocksession.newaction(venv.name, "getenv") as action: 906 venv.update(action=action) 907 assert log == [1, 2] 908 909 910def test_tox_testenv_pre_post(newmocksession): 911 log = [] 912 913 class Plugin: 914 @tox.hookimpl 915 def tox_runtest_pre(self): 916 log.append("started") 917 918 @tox.hookimpl 919 def tox_runtest_post(self): 920 log.append("finished") 921 922 mocksession = newmocksession( 923 [], 924 """\ 925 [testenv] 926 commands=testenv_fail 927 """, 928 plugins=[Plugin()], 929 ) 930 931 venv = mocksession.getvenv("python") 932 venv.status = None 933 assert log == [] 934 runtestenv(venv, venv.envconfig.config) 935 assert log == ["started", "finished"] 936 937 938@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 939def test_tox_testenv_interpret_shebang_empty_instance(tmpdir): 940 testfile = tmpdir.join("check_shebang_empty_instance.py") 941 base_args = [str(testfile), "arg1", "arg2", "arg3"] 942 943 # empty instance 944 testfile.write("") 945 args = prepend_shebang_interpreter(base_args) 946 assert args == base_args 947 948 949@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 950def test_tox_testenv_interpret_shebang_empty_interpreter(tmpdir): 951 testfile = tmpdir.join("check_shebang_empty_interpreter.py") 952 base_args = [str(testfile), "arg1", "arg2", "arg3"] 953 954 # empty interpreter 955 testfile.write("#!") 956 args = prepend_shebang_interpreter(base_args) 957 assert args == base_args 958 959 960@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 961def test_tox_testenv_interpret_shebang_empty_interpreter_ws(tmpdir): 962 testfile = tmpdir.join("check_shebang_empty_interpreter_ws.py") 963 base_args = [str(testfile), "arg1", "arg2", "arg3"] 964 965 # empty interpreter (whitespaces) 966 testfile.write("#! \n") 967 args = prepend_shebang_interpreter(base_args) 968 assert args == base_args 969 970 971@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 972def test_tox_testenv_interpret_shebang_non_utf8(tmpdir): 973 testfile = tmpdir.join("check_non_utf8.py") 974 base_args = [str(testfile), "arg1", "arg2", "arg3"] 975 976 testfile.write_binary(b"#!\x9a\xef\x12\xaf\n") 977 args = prepend_shebang_interpreter(base_args) 978 assert args == base_args 979 980 981@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 982def test_tox_testenv_interpret_shebang_interpreter_simple(tmpdir): 983 testfile = tmpdir.join("check_shebang_interpreter_simple.py") 984 base_args = [str(testfile), "arg1", "arg2", "arg3"] 985 986 # interpreter (simple) 987 testfile.write("#!interpreter") 988 args = prepend_shebang_interpreter(base_args) 989 assert args == ["interpreter"] + base_args 990 991 992@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 993def test_tox_testenv_interpret_shebang_interpreter_ws(tmpdir): 994 testfile = tmpdir.join("check_shebang_interpreter_ws.py") 995 base_args = [str(testfile), "arg1", "arg2", "arg3"] 996 997 # interpreter (whitespaces) 998 testfile.write("#! interpreter \n\n") 999 args = prepend_shebang_interpreter(base_args) 1000 assert args == ["interpreter"] + base_args 1001 1002 1003@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 1004def test_tox_testenv_interpret_shebang_interpreter_arg(tmpdir): 1005 testfile = tmpdir.join("check_shebang_interpreter_arg.py") 1006 base_args = [str(testfile), "arg1", "arg2", "arg3"] 1007 1008 # interpreter with argument 1009 testfile.write("#!interpreter argx\n") 1010 args = prepend_shebang_interpreter(base_args) 1011 assert args == ["interpreter", "argx"] + base_args 1012 1013 1014@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 1015def test_tox_testenv_interpret_shebang_interpreter_args(tmpdir): 1016 testfile = tmpdir.join("check_shebang_interpreter_args.py") 1017 base_args = [str(testfile), "arg1", "arg2", "arg3"] 1018 1019 # interpreter with argument (ensure single argument) 1020 testfile.write("#!interpreter argx argx-part2\n") 1021 args = prepend_shebang_interpreter(base_args) 1022 assert args == ["interpreter", "argx argx-part2"] + base_args 1023 1024 1025@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 1026def test_tox_testenv_interpret_shebang_real(tmpdir): 1027 testfile = tmpdir.join("check_shebang_real.py") 1028 base_args = [str(testfile), "arg1", "arg2", "arg3"] 1029 1030 # interpreter (real example) 1031 testfile.write("#!/usr/bin/env python\n") 1032 args = prepend_shebang_interpreter(base_args) 1033 assert args == ["/usr/bin/env", "python"] + base_args 1034 1035 1036@pytest.mark.skipif("sys.platform == 'win32'", reason="no shebang on Windows") 1037def test_tox_testenv_interpret_shebang_long_example(tmpdir): 1038 testfile = tmpdir.join("check_shebang_long_example.py") 1039 base_args = [str(testfile), "arg1", "arg2", "arg3"] 1040 1041 # interpreter (long example) 1042 testfile.write( 1043 "#!this-is-an-example-of-a-very-long-interpret-directive-what-should-" 1044 "be-directly-invoked-when-tox-needs-to-invoked-the-provided-script-" 1045 "name-in-the-argument-list" 1046 ) 1047 args = prepend_shebang_interpreter(base_args) 1048 expected = [ 1049 "this-is-an-example-of-a-very-long-interpret-directive-what-should-be-" 1050 "directly-invoked-when-tox-needs-to-invoked-the-provided-script-name-" 1051 "in-the-argument-list" 1052 ] 1053 1054 assert args == expected + base_args 1055 1056 1057@pytest.mark.parametrize("download", [True, False, None]) 1058def test_create_download(mocksession, newconfig, download): 1059 config = newconfig( 1060 [], 1061 """\ 1062 [testenv:env] 1063 {} 1064 """.format( 1065 "download={}".format(download) if download else "" 1066 ), 1067 ) 1068 mocksession.new_config(config) 1069 venv = mocksession.getvenv("env") 1070 with mocksession.newaction(venv.name, "getenv") as action: 1071 tox_testenv_create(action=action, venv=venv) 1072 pcalls = mocksession._pcalls 1073 assert len(pcalls) >= 1 1074 args = pcalls[0].args 1075 if download is True: 1076 assert "--no-download" not in map(str, args) 1077 else: 1078 assert "--no-download" in map(str, args) 1079 mocksession._clearmocks() 1080