1172 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			1172 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Python
		
	
	
	
| import os
 | |
| import shutil
 | |
| import stat
 | |
| import sys
 | |
| 
 | |
| import py
 | |
| 
 | |
| import pytest
 | |
| from _pytest.config import ExitCode
 | |
| from _pytest.pytester import Testdir
 | |
| 
 | |
| pytest_plugins = ("pytester",)
 | |
| 
 | |
| 
 | |
| class TestNewAPI:
 | |
|     def test_config_cache_makedir(self, testdir):
 | |
|         testdir.makeini("[pytest]")
 | |
|         config = testdir.parseconfigure()
 | |
|         with pytest.raises(ValueError):
 | |
|             config.cache.makedir("key/name")
 | |
| 
 | |
|         p = config.cache.makedir("name")
 | |
|         assert p.check()
 | |
| 
 | |
|     def test_config_cache_dataerror(self, testdir):
 | |
|         testdir.makeini("[pytest]")
 | |
|         config = testdir.parseconfigure()
 | |
|         cache = config.cache
 | |
|         pytest.raises(TypeError, lambda: cache.set("key/name", cache))
 | |
|         config.cache.set("key/name", 0)
 | |
|         config.cache._getvaluepath("key/name").write_bytes(b"123invalid")
 | |
|         val = config.cache.get("key/name", -2)
 | |
|         assert val == -2
 | |
| 
 | |
|     @pytest.mark.filterwarnings("ignore:could not create cache path")
 | |
|     def test_cache_writefail_cachfile_silent(self, testdir):
 | |
|         testdir.makeini("[pytest]")
 | |
|         testdir.tmpdir.join(".pytest_cache").write("gone wrong")
 | |
|         config = testdir.parseconfigure()
 | |
|         cache = config.cache
 | |
|         cache.set("test/broken", [])
 | |
| 
 | |
|     @pytest.mark.skipif(sys.platform.startswith("win"), reason="no chmod on windows")
 | |
|     @pytest.mark.filterwarnings(
 | |
|         "ignore:could not create cache path:pytest.PytestWarning"
 | |
|     )
 | |
|     def test_cache_writefail_permissions(self, testdir):
 | |
|         testdir.makeini("[pytest]")
 | |
|         cache_dir = str(testdir.tmpdir.ensure_dir(".pytest_cache"))
 | |
|         mode = os.stat(cache_dir)[stat.ST_MODE]
 | |
|         testdir.tmpdir.ensure_dir(".pytest_cache").chmod(0)
 | |
|         try:
 | |
|             config = testdir.parseconfigure()
 | |
|             cache = config.cache
 | |
|             cache.set("test/broken", [])
 | |
|         finally:
 | |
|             testdir.tmpdir.ensure_dir(".pytest_cache").chmod(mode)
 | |
| 
 | |
|     @pytest.mark.skipif(sys.platform.startswith("win"), reason="no chmod on windows")
 | |
|     @pytest.mark.filterwarnings("default")
 | |
|     def test_cache_failure_warns(self, testdir, monkeypatch):
 | |
|         monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1")
 | |
|         cache_dir = str(testdir.tmpdir.ensure_dir(".pytest_cache"))
 | |
|         mode = os.stat(cache_dir)[stat.ST_MODE]
 | |
|         testdir.tmpdir.ensure_dir(".pytest_cache").chmod(0)
 | |
|         try:
 | |
|             testdir.makepyfile("def test_error(): raise Exception")
 | |
|             result = testdir.runpytest()
 | |
|             assert result.ret == 1
 | |
|             # warnings from nodeids, lastfailed, and stepwise
 | |
|             result.stdout.fnmatch_lines(
 | |
|                 [
 | |
|                     # Validate location/stacklevel of warning from cacheprovider.
 | |
|                     "*= warnings summary =*",
 | |
|                     "*/cacheprovider.py:*",
 | |
|                     "  */cacheprovider.py:*: PytestCacheWarning: could not create cache path "
 | |
|                     "{}/v/cache/nodeids".format(cache_dir),
 | |
|                     '    config.cache.set("cache/nodeids", sorted(self.cached_nodeids))',
 | |
|                     "*1 failed, 3 warnings in*",
 | |
|                 ]
 | |
|             )
 | |
|         finally:
 | |
|             testdir.tmpdir.ensure_dir(".pytest_cache").chmod(mode)
 | |
| 
 | |
|     def test_config_cache(self, testdir):
 | |
|         testdir.makeconftest(
 | |
|             """
 | |
|             def pytest_configure(config):
 | |
|                 # see that we get cache information early on
 | |
|                 assert hasattr(config, "cache")
 | |
|         """
 | |
|         )
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             def test_session(pytestconfig):
 | |
|                 assert hasattr(pytestconfig, "cache")
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         assert result.ret == 0
 | |
|         result.stdout.fnmatch_lines(["*1 passed*"])
 | |
| 
 | |
|     def test_cachefuncarg(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             import pytest
 | |
|             def test_cachefuncarg(cache):
 | |
|                 val = cache.get("some/thing", None)
 | |
|                 assert val is None
 | |
|                 cache.set("some/thing", [1])
 | |
|                 pytest.raises(TypeError, lambda: cache.get("some/thing"))
 | |
|                 val = cache.get("some/thing", [])
 | |
|                 assert val == [1]
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         assert result.ret == 0
 | |
|         result.stdout.fnmatch_lines(["*1 passed*"])
 | |
| 
 | |
|     def test_custom_rel_cache_dir(self, testdir):
 | |
|         rel_cache_dir = os.path.join("custom_cache_dir", "subdir")
 | |
|         testdir.makeini(
 | |
|             """
 | |
|             [pytest]
 | |
|             cache_dir = {cache_dir}
 | |
|         """.format(
 | |
|                 cache_dir=rel_cache_dir
 | |
|             )
 | |
|         )
 | |
|         testdir.makepyfile(test_errored="def test_error():\n    assert False")
 | |
|         testdir.runpytest()
 | |
|         assert testdir.tmpdir.join(rel_cache_dir).isdir()
 | |
| 
 | |
|     def test_custom_abs_cache_dir(self, testdir, tmpdir_factory):
 | |
|         tmp = str(tmpdir_factory.mktemp("tmp"))
 | |
|         abs_cache_dir = os.path.join(tmp, "custom_cache_dir")
 | |
|         testdir.makeini(
 | |
|             """
 | |
|             [pytest]
 | |
|             cache_dir = {cache_dir}
 | |
|         """.format(
 | |
|                 cache_dir=abs_cache_dir
 | |
|             )
 | |
|         )
 | |
|         testdir.makepyfile(test_errored="def test_error():\n    assert False")
 | |
|         testdir.runpytest()
 | |
|         assert py.path.local(abs_cache_dir).isdir()
 | |
| 
 | |
|     def test_custom_cache_dir_with_env_var(self, testdir, monkeypatch):
 | |
|         monkeypatch.setenv("env_var", "custom_cache_dir")
 | |
|         testdir.makeini(
 | |
|             """
 | |
|             [pytest]
 | |
|             cache_dir = {cache_dir}
 | |
|         """.format(
 | |
|                 cache_dir="$env_var"
 | |
|             )
 | |
|         )
 | |
|         testdir.makepyfile(test_errored="def test_error():\n    assert False")
 | |
|         testdir.runpytest()
 | |
|         assert testdir.tmpdir.join("custom_cache_dir").isdir()
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("env", ((), ("TOX_ENV_DIR", "/tox_env_dir")))
 | |
| def test_cache_reportheader(env, testdir, monkeypatch):
 | |
|     testdir.makepyfile("""def test_foo(): pass""")
 | |
|     if env:
 | |
|         monkeypatch.setenv(*env)
 | |
|         expected = os.path.join(env[1], ".pytest_cache")
 | |
|     else:
 | |
|         monkeypatch.delenv("TOX_ENV_DIR", raising=False)
 | |
|         expected = ".pytest_cache"
 | |
|     result = testdir.runpytest("-v")
 | |
|     result.stdout.fnmatch_lines(["cachedir: %s" % expected])
 | |
| 
 | |
| 
 | |
| def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
 | |
|     external_cache = tmpdir_factory.mktemp(
 | |
|         "test_cache_reportheader_external_abspath_abs"
 | |
|     )
 | |
| 
 | |
|     testdir.makepyfile("def test_hello(): pass")
 | |
|     testdir.makeini(
 | |
|         """
 | |
|     [pytest]
 | |
|     cache_dir = {abscache}
 | |
|     """.format(
 | |
|             abscache=external_cache
 | |
|         )
 | |
|     )
 | |
|     result = testdir.runpytest("-v")
 | |
|     result.stdout.fnmatch_lines(
 | |
|         ["cachedir: {abscache}".format(abscache=external_cache)]
 | |
|     )
 | |
| 
 | |
| 
 | |
| def test_cache_show(testdir):
 | |
|     result = testdir.runpytest("--cache-show")
 | |
|     assert result.ret == 0
 | |
|     result.stdout.fnmatch_lines(["*cache is empty*"])
 | |
|     testdir.makeconftest(
 | |
|         """
 | |
|         def pytest_configure(config):
 | |
|             config.cache.set("my/name", [1,2,3])
 | |
|             config.cache.set("my/hello", "world")
 | |
|             config.cache.set("other/some", {1:2})
 | |
|             dp = config.cache.makedir("mydb")
 | |
|             dp.ensure("hello")
 | |
|             dp.ensure("world")
 | |
|     """
 | |
|     )
 | |
|     result = testdir.runpytest()
 | |
|     assert result.ret == 5  # no tests executed
 | |
| 
 | |
|     result = testdir.runpytest("--cache-show")
 | |
|     result.stdout.fnmatch_lines(
 | |
|         [
 | |
|             "*cachedir:*",
 | |
|             "*- cache values for '[*]' -*",
 | |
|             "cache/nodeids contains:",
 | |
|             "my/name contains:",
 | |
|             "  [1, 2, 3]",
 | |
|             "other/some contains:",
 | |
|             "  {*'1': 2}",
 | |
|             "*- cache directories for '[*]' -*",
 | |
|             "*mydb/hello*length 0*",
 | |
|             "*mydb/world*length 0*",
 | |
|         ]
 | |
|     )
 | |
|     assert result.ret == 0
 | |
| 
 | |
|     result = testdir.runpytest("--cache-show", "*/hello")
 | |
|     result.stdout.fnmatch_lines(
 | |
|         [
 | |
|             "*cachedir:*",
 | |
|             "*- cache values for '[*]/hello' -*",
 | |
|             "my/hello contains:",
 | |
|             "  *'world'",
 | |
|             "*- cache directories for '[*]/hello' -*",
 | |
|             "d/mydb/hello*length 0*",
 | |
|         ]
 | |
|     )
 | |
|     stdout = result.stdout.str()
 | |
|     assert "other/some" not in stdout
 | |
|     assert "d/mydb/world" not in stdout
 | |
|     assert result.ret == 0
 | |
| 
 | |
| 
 | |
| class TestLastFailed:
 | |
|     def test_lastfailed_usecase(self, testdir, monkeypatch):
 | |
|         monkeypatch.setattr("sys.dont_write_bytecode", True)
 | |
|         p = testdir.makepyfile(
 | |
|             """
 | |
|             def test_1(): assert 0
 | |
|             def test_2(): assert 0
 | |
|             def test_3(): assert 1
 | |
|             """
 | |
|         )
 | |
|         result = testdir.runpytest(str(p))
 | |
|         result.stdout.fnmatch_lines(["*2 failed*"])
 | |
|         p = testdir.makepyfile(
 | |
|             """
 | |
|             def test_1(): assert 1
 | |
|             def test_2(): assert 1
 | |
|             def test_3(): assert 0
 | |
|             """
 | |
|         )
 | |
|         result = testdir.runpytest(str(p), "--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 3 items / 1 deselected / 2 selected",
 | |
|                 "run-last-failure: rerun previous 2 failures",
 | |
|                 "*= 2 passed, 1 deselected in *",
 | |
|             ]
 | |
|         )
 | |
|         result = testdir.runpytest(str(p), "--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 3 items",
 | |
|                 "run-last-failure: no previously failed tests, not deselecting items.",
 | |
|                 "*1 failed*2 passed*",
 | |
|             ]
 | |
|         )
 | |
|         testdir.tmpdir.join(".pytest_cache").mkdir(".git")
 | |
|         result = testdir.runpytest(str(p), "--lf", "--cache-clear")
 | |
|         result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
 | |
|         assert testdir.tmpdir.join(".pytest_cache", "README.md").isfile()
 | |
|         assert testdir.tmpdir.join(".pytest_cache", ".git").isdir()
 | |
| 
 | |
|         # Run this again to make sure clear-cache is robust
 | |
|         if os.path.isdir(".pytest_cache"):
 | |
|             shutil.rmtree(".pytest_cache")
 | |
|         result = testdir.runpytest("--lf", "--cache-clear")
 | |
|         result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
 | |
| 
 | |
|     def test_failedfirst_order(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             test_a="def test_always_passes(): pass",
 | |
|             test_b="def test_always_fails(): assert 0",
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         # Test order will be collection order; alphabetical
 | |
|         result.stdout.fnmatch_lines(["test_a.py*", "test_b.py*"])
 | |
|         result = testdir.runpytest("--ff")
 | |
|         # Test order will be failing tests first
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: rerun previous 1 failure first",
 | |
|                 "test_b.py*",
 | |
|                 "test_a.py*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_failedfirst_order(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             test_a="def test_always_passes(): assert 1",
 | |
|             test_b="def test_always_fails(): assert 0",
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         # Test order will be collection order; alphabetical
 | |
|         result.stdout.fnmatch_lines(["test_a.py*", "test_b.py*"])
 | |
|         result = testdir.runpytest("--lf", "--ff")
 | |
|         # Test order will be failing tests first
 | |
|         result.stdout.fnmatch_lines(["test_b.py*"])
 | |
|         result.stdout.no_fnmatch_line("*test_a.py*")
 | |
| 
 | |
|     def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
 | |
|         monkeypatch.setattr("sys.dont_write_bytecode", True)
 | |
|         testdir.makepyfile(
 | |
|             test_a="""
 | |
|                 def test_a1(): assert 0
 | |
|                 def test_a2(): assert 1
 | |
|             """,
 | |
|             test_b="def test_b1(): assert 0",
 | |
|         )
 | |
|         p = testdir.tmpdir.join("test_a.py")
 | |
|         p2 = testdir.tmpdir.join("test_b.py")
 | |
| 
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*2 failed*"])
 | |
|         result = testdir.runpytest("--lf", p2)
 | |
|         result.stdout.fnmatch_lines(["*1 failed*"])
 | |
| 
 | |
|         testdir.makepyfile(test_b="def test_b1(): assert 1")
 | |
|         result = testdir.runpytest("--lf", p2)
 | |
|         result.stdout.fnmatch_lines(["*1 passed*"])
 | |
|         result = testdir.runpytest("--lf", p)
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items / 1 deselected / 1 selected",
 | |
|                 "run-last-failure: rerun previous 1 failure",
 | |
|                 "*= 1 failed, 1 deselected in *",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
 | |
|         monkeypatch.setattr("sys.dont_write_bytecode", True)
 | |
|         testdir.makepyfile(
 | |
|             "def test_1(): assert 0", test_something="def test_2(): assert 0"
 | |
|         )
 | |
|         p2 = testdir.tmpdir.join("test_something.py")
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*2 failed*"])
 | |
|         result = testdir.runpytest("--lf", p2)
 | |
|         result.stdout.fnmatch_lines(["*1 failed*"])
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(["*2 failed*"])
 | |
| 
 | |
|     def test_lastfailed_xpass(self, testdir):
 | |
|         testdir.inline_runsource(
 | |
|             """
 | |
|             import pytest
 | |
|             @pytest.mark.xfail
 | |
|             def test_hello():
 | |
|                 assert 1
 | |
|         """
 | |
|         )
 | |
|         config = testdir.parseconfigure()
 | |
|         lastfailed = config.cache.get("cache/lastfailed", -1)
 | |
|         assert lastfailed == -1
 | |
| 
 | |
|     def test_non_serializable_parametrize(self, testdir):
 | |
|         """Test that failed parametrized tests with unmarshable parameters
 | |
|         don't break pytest-cache.
 | |
|         """
 | |
|         testdir.makepyfile(
 | |
|             r"""
 | |
|             import pytest
 | |
| 
 | |
|             @pytest.mark.parametrize('val', [
 | |
|                 b'\xac\x10\x02G',
 | |
|             ])
 | |
|             def test_fail(val):
 | |
|                 assert False
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*1 failed in*"])
 | |
| 
 | |
|     def test_terminal_report_lastfailed(self, testdir):
 | |
|         test_a = testdir.makepyfile(
 | |
|             test_a="""
 | |
|             def test_a1(): pass
 | |
|             def test_a2(): pass
 | |
|         """
 | |
|         )
 | |
|         test_b = testdir.makepyfile(
 | |
|             test_b="""
 | |
|             def test_b1(): assert 0
 | |
|             def test_b2(): assert 0
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 4 items", "*2 failed, 2 passed in*"])
 | |
| 
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: rerun previous 2 failures (skipped 1 file)",
 | |
|                 "*2 failed in*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest(test_a, "--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: 2 known failures not in selected tests",
 | |
|                 "*2 passed in*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest(test_b, "--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: rerun previous 2 failures",
 | |
|                 "*2 failed in*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest("test_b.py::test_b1", "--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 1 item",
 | |
|                 "run-last-failure: rerun previous 1 failure",
 | |
|                 "*1 failed in*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_terminal_report_failedfirst(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             test_a="""
 | |
|             def test_a1(): assert 0
 | |
|             def test_a2(): pass
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 2 items", "*1 failed, 1 passed in*"])
 | |
| 
 | |
|         result = testdir.runpytest("--ff")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: rerun previous 1 failure first",
 | |
|                 "*1 failed, 1 passed in*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_collectfailure(self, testdir, monkeypatch):
 | |
| 
 | |
|         testdir.makepyfile(
 | |
|             test_maybe="""
 | |
|             import os
 | |
|             env = os.environ
 | |
|             if '1' == env['FAILIMPORT']:
 | |
|                 raise ImportError('fail')
 | |
|             def test_hello():
 | |
|                 assert '0' == env['FAILTEST']
 | |
|         """
 | |
|         )
 | |
| 
 | |
|         def rlf(fail_import, fail_run):
 | |
|             monkeypatch.setenv("FAILIMPORT", str(fail_import))
 | |
|             monkeypatch.setenv("FAILTEST", str(fail_run))
 | |
| 
 | |
|             testdir.runpytest("-q")
 | |
|             config = testdir.parseconfigure()
 | |
|             lastfailed = config.cache.get("cache/lastfailed", -1)
 | |
|             return lastfailed
 | |
| 
 | |
|         lastfailed = rlf(fail_import=0, fail_run=0)
 | |
|         assert lastfailed == -1
 | |
| 
 | |
|         lastfailed = rlf(fail_import=1, fail_run=0)
 | |
|         assert list(lastfailed) == ["test_maybe.py"]
 | |
| 
 | |
|         lastfailed = rlf(fail_import=0, fail_run=1)
 | |
|         assert list(lastfailed) == ["test_maybe.py::test_hello"]
 | |
| 
 | |
|     def test_lastfailed_failure_subset(self, testdir, monkeypatch):
 | |
|         testdir.makepyfile(
 | |
|             test_maybe="""
 | |
|             import os
 | |
|             env = os.environ
 | |
|             if '1' == env['FAILIMPORT']:
 | |
|                 raise ImportError('fail')
 | |
|             def test_hello():
 | |
|                 assert '0' == env['FAILTEST']
 | |
|         """
 | |
|         )
 | |
| 
 | |
|         testdir.makepyfile(
 | |
|             test_maybe2="""
 | |
|             import os
 | |
|             env = os.environ
 | |
|             if '1' == env['FAILIMPORT']:
 | |
|                 raise ImportError('fail')
 | |
| 
 | |
|             def test_hello():
 | |
|                 assert '0' == env['FAILTEST']
 | |
| 
 | |
|             def test_pass():
 | |
|                 pass
 | |
|         """
 | |
|         )
 | |
| 
 | |
|         def rlf(fail_import, fail_run, args=()):
 | |
|             monkeypatch.setenv("FAILIMPORT", str(fail_import))
 | |
|             monkeypatch.setenv("FAILTEST", str(fail_run))
 | |
| 
 | |
|             result = testdir.runpytest("-q", "--lf", *args)
 | |
|             config = testdir.parseconfigure()
 | |
|             lastfailed = config.cache.get("cache/lastfailed", -1)
 | |
|             return result, lastfailed
 | |
| 
 | |
|         result, lastfailed = rlf(fail_import=0, fail_run=0)
 | |
|         assert lastfailed == -1
 | |
|         result.stdout.fnmatch_lines(["*3 passed*"])
 | |
| 
 | |
|         result, lastfailed = rlf(fail_import=1, fail_run=0)
 | |
|         assert sorted(list(lastfailed)) == ["test_maybe.py", "test_maybe2.py"]
 | |
| 
 | |
|         result, lastfailed = rlf(fail_import=0, fail_run=0, args=("test_maybe2.py",))
 | |
|         assert list(lastfailed) == ["test_maybe.py"]
 | |
| 
 | |
|         # edge case of test selection - even if we remember failures
 | |
|         # from other tests we still need to run all tests if no test
 | |
|         # matches the failures
 | |
|         result, lastfailed = rlf(fail_import=0, fail_run=0, args=("test_maybe2.py",))
 | |
|         assert list(lastfailed) == ["test_maybe.py"]
 | |
|         result.stdout.fnmatch_lines(["*2 passed*"])
 | |
| 
 | |
|     def test_lastfailed_creates_cache_when_needed(self, testdir):
 | |
|         # Issue #1342
 | |
|         testdir.makepyfile(test_empty="")
 | |
|         testdir.runpytest("-q", "--lf")
 | |
|         assert not os.path.exists(".pytest_cache/v/cache/lastfailed")
 | |
| 
 | |
|         testdir.makepyfile(test_successful="def test_success():\n    assert True")
 | |
|         testdir.runpytest("-q", "--lf")
 | |
|         assert not os.path.exists(".pytest_cache/v/cache/lastfailed")
 | |
| 
 | |
|         testdir.makepyfile(test_errored="def test_error():\n    assert False")
 | |
|         testdir.runpytest("-q", "--lf")
 | |
|         assert os.path.exists(".pytest_cache/v/cache/lastfailed")
 | |
| 
 | |
|     def test_xfail_not_considered_failure(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             import pytest
 | |
|             @pytest.mark.xfail
 | |
|             def test(): assert 0
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*1 xfailed*"])
 | |
|         assert self.get_cached_last_failed(testdir) == []
 | |
| 
 | |
|     def test_xfail_strict_considered_failure(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             import pytest
 | |
|             @pytest.mark.xfail(strict=True)
 | |
|             def test(): pass
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*1 failed*"])
 | |
|         assert self.get_cached_last_failed(testdir) == [
 | |
|             "test_xfail_strict_considered_failure.py::test"
 | |
|         ]
 | |
| 
 | |
|     @pytest.mark.parametrize("mark", ["mark.xfail", "mark.skip"])
 | |
|     def test_failed_changed_to_xfail_or_skip(self, testdir, mark):
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             import pytest
 | |
|             def test(): assert 0
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         assert self.get_cached_last_failed(testdir) == [
 | |
|             "test_failed_changed_to_xfail_or_skip.py::test"
 | |
|         ]
 | |
|         assert result.ret == 1
 | |
| 
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             import pytest
 | |
|             @pytest.{mark}
 | |
|             def test(): assert 0
 | |
|         """.format(
 | |
|                 mark=mark
 | |
|             )
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         assert result.ret == 0
 | |
|         assert self.get_cached_last_failed(testdir) == []
 | |
|         assert result.ret == 0
 | |
| 
 | |
|     @pytest.mark.parametrize("quiet", [True, False])
 | |
|     @pytest.mark.parametrize("opt", ["--ff", "--lf"])
 | |
|     def test_lf_and_ff_prints_no_needless_message(self, quiet, opt, testdir):
 | |
|         # Issue 3853
 | |
|         testdir.makepyfile("def test(): assert 0")
 | |
|         args = [opt]
 | |
|         if quiet:
 | |
|             args.append("-q")
 | |
|         result = testdir.runpytest(*args)
 | |
|         result.stdout.no_fnmatch_line("*run all*")
 | |
| 
 | |
|         result = testdir.runpytest(*args)
 | |
|         if quiet:
 | |
|             result.stdout.no_fnmatch_line("*run all*")
 | |
|         else:
 | |
|             assert "rerun previous" in result.stdout.str()
 | |
| 
 | |
|     def get_cached_last_failed(self, testdir):
 | |
|         config = testdir.parseconfigure()
 | |
|         return sorted(config.cache.get("cache/lastfailed", {}))
 | |
| 
 | |
|     def test_cache_cumulative(self, testdir):
 | |
|         """
 | |
|         Test workflow where user fixes errors gradually file by file using --lf.
 | |
|         """
 | |
|         # 1. initial run
 | |
|         test_bar = testdir.makepyfile(
 | |
|             test_bar="""
 | |
|             def test_bar_1(): pass
 | |
|             def test_bar_2(): assert 0
 | |
|         """
 | |
|         )
 | |
|         test_foo = testdir.makepyfile(
 | |
|             test_foo="""
 | |
|             def test_foo_3(): pass
 | |
|             def test_foo_4(): assert 0
 | |
|         """
 | |
|         )
 | |
|         testdir.runpytest()
 | |
|         assert self.get_cached_last_failed(testdir) == [
 | |
|             "test_bar.py::test_bar_2",
 | |
|             "test_foo.py::test_foo_4",
 | |
|         ]
 | |
| 
 | |
|         # 2. fix test_bar_2, run only test_bar.py
 | |
|         testdir.makepyfile(
 | |
|             test_bar="""
 | |
|             def test_bar_1(): pass
 | |
|             def test_bar_2(): pass
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest(test_bar)
 | |
|         result.stdout.fnmatch_lines(["*2 passed*"])
 | |
|         # ensure cache does not forget that test_foo_4 failed once before
 | |
|         assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"]
 | |
| 
 | |
|         result = testdir.runpytest("--last-failed")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 1 item",
 | |
|                 "run-last-failure: rerun previous 1 failure (skipped 1 file)",
 | |
|                 "*= 1 failed in *",
 | |
|             ]
 | |
|         )
 | |
|         assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"]
 | |
| 
 | |
|         # 3. fix test_foo_4, run only test_foo.py
 | |
|         test_foo = testdir.makepyfile(
 | |
|             test_foo="""
 | |
|             def test_foo_3(): pass
 | |
|             def test_foo_4(): pass
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest(test_foo, "--last-failed")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items / 1 deselected / 1 selected",
 | |
|                 "run-last-failure: rerun previous 1 failure",
 | |
|                 "*= 1 passed, 1 deselected in *",
 | |
|             ]
 | |
|         )
 | |
|         assert self.get_cached_last_failed(testdir) == []
 | |
| 
 | |
|         result = testdir.runpytest("--last-failed")
 | |
|         result.stdout.fnmatch_lines(["*4 passed*"])
 | |
|         assert self.get_cached_last_failed(testdir) == []
 | |
| 
 | |
|     def test_lastfailed_no_failures_behavior_all_passed(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             def test_1(): pass
 | |
|             def test_2(): pass
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*2 passed*"])
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(["*2 passed*"])
 | |
|         result = testdir.runpytest("--lf", "--lfnf", "all")
 | |
|         result.stdout.fnmatch_lines(["*2 passed*"])
 | |
| 
 | |
|         # Ensure the list passed to pytest_deselected is a copy,
 | |
|         # and not a reference which is cleared right after.
 | |
|         testdir.makeconftest(
 | |
|             """
 | |
|             deselected = []
 | |
| 
 | |
|             def pytest_deselected(items):
 | |
|                 global deselected
 | |
|                 deselected = items
 | |
| 
 | |
|             def pytest_sessionfinish():
 | |
|                 print("\\ndeselected={}".format(len(deselected)))
 | |
|         """
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest("--lf", "--lfnf", "none")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items / 2 deselected",
 | |
|                 "run-last-failure: no previously failed tests, deselecting all items.",
 | |
|                 "deselected=2",
 | |
|                 "* 2 deselected in *",
 | |
|             ]
 | |
|         )
 | |
|         assert result.ret == ExitCode.NO_TESTS_COLLECTED
 | |
| 
 | |
|     def test_lastfailed_no_failures_behavior_empty_cache(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             """
 | |
|             def test_1(): pass
 | |
|             def test_2(): assert 0
 | |
|         """
 | |
|         )
 | |
|         result = testdir.runpytest("--lf", "--cache-clear")
 | |
|         result.stdout.fnmatch_lines(["*1 failed*1 passed*"])
 | |
|         result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "all")
 | |
|         result.stdout.fnmatch_lines(["*1 failed*1 passed*"])
 | |
|         result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "none")
 | |
|         result.stdout.fnmatch_lines(["*2 desel*"])
 | |
| 
 | |
|     def test_lastfailed_skip_collection(self, testdir):
 | |
|         """
 | |
|         Test --lf behavior regarding skipping collection of files that are not marked as
 | |
|         failed in the cache (#5172).
 | |
|         """
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_1.py": """
 | |
|                 import pytest
 | |
| 
 | |
|                 @pytest.mark.parametrize('i', range(3))
 | |
|                 def test_1(i): pass
 | |
|             """,
 | |
|                 "pkg2/test_2.py": """
 | |
|                 import pytest
 | |
| 
 | |
|                 @pytest.mark.parametrize('i', range(5))
 | |
|                 def test_1(i):
 | |
|                     assert i not in (1, 3)
 | |
|             """,
 | |
|             }
 | |
|         )
 | |
|         # first run: collects 8 items (test_1: 3, test_2: 5)
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 8 items", "*2 failed*6 passed*"])
 | |
|         # second run: collects only 5 items from test_2, because all tests from test_1 have passed
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: rerun previous 2 failures (skipped 1 file)",
 | |
|                 "*= 2 failed in *",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         # add another file and check if message is correct when skipping more than 1 file
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_3.py": """
 | |
|                 def test_3(): pass
 | |
|             """
 | |
|             }
 | |
|         )
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: rerun previous 2 failures (skipped 2 files)",
 | |
|                 "*= 2 failed in *",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_with_known_failures_not_being_selected(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_1.py": """def test_1(): assert 0""",
 | |
|                 "pkg1/test_2.py": """def test_2(): pass""",
 | |
|             }
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"])
 | |
| 
 | |
|         py.path.local("pkg1/test_1.py").remove()
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 1 item",
 | |
|                 "run-last-failure: 1 known failures not in selected tests",
 | |
|                 "* 1 passed in *",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         # Recreate file with known failure.
 | |
|         testdir.makepyfile(**{"pkg1/test_1.py": """def test_1(): assert 0"""})
 | |
|         result = testdir.runpytest("--lf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 1 item",
 | |
|                 "run-last-failure: rerun previous 1 failure (skipped 1 file)",
 | |
|                 "* 1 failed in *",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         # Remove/rename test: collects the file again.
 | |
|         testdir.makepyfile(**{"pkg1/test_1.py": """def test_renamed(): assert 0"""})
 | |
|         result = testdir.runpytest("--lf", "-rf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items",
 | |
|                 "run-last-failure: 1 known failures not in selected tests",
 | |
|                 "pkg1/test_1.py F *",
 | |
|                 "pkg1/test_2.py . *",
 | |
|                 "FAILED pkg1/test_1.py::test_renamed - assert 0",
 | |
|                 "* 1 failed, 1 passed in *",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest("--lf", "--co")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 1 item",
 | |
|                 "run-last-failure: rerun previous 1 failure (skipped 1 file)",
 | |
|                 "",
 | |
|                 "<Module pkg1/test_1.py>",
 | |
|                 "  <Function test_renamed>",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_args_with_deselected(self, testdir: Testdir) -> None:
 | |
|         """Test regression with --lf running into NoMatch error.
 | |
| 
 | |
|         This was caused by it not collecting (non-failed) nodes given as
 | |
|         arguments.
 | |
|         """
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_1.py": """
 | |
|                     def test_pass(): pass
 | |
|                     def test_fail(): assert 0
 | |
|                 """,
 | |
|             }
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"])
 | |
|         assert result.ret == 1
 | |
| 
 | |
|         result = testdir.runpytest("pkg1/test_1.py::test_pass", "--lf", "--co")
 | |
|         assert result.ret == 0
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "*collected 1 item",
 | |
|                 "run-last-failure: 1 known failures not in selected tests",
 | |
|                 "",
 | |
|                 "<Module pkg1/test_1.py>",
 | |
|                 "  <Function test_pass>",
 | |
|             ],
 | |
|             consecutive=True,
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest(
 | |
|             "pkg1/test_1.py::test_pass", "pkg1/test_1.py::test_fail", "--lf", "--co"
 | |
|         )
 | |
|         assert result.ret == 0
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 2 items / 1 deselected / 1 selected",
 | |
|                 "run-last-failure: rerun previous 1 failure",
 | |
|                 "",
 | |
|                 "<Module pkg1/test_1.py>",
 | |
|                 "  <Function test_fail>",
 | |
|                 "*= 1 deselected in *",
 | |
|             ],
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_with_class_items(self, testdir: Testdir) -> None:
 | |
|         """Test regression with --lf deselecting whole classes."""
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_1.py": """
 | |
|                     class TestFoo:
 | |
|                         def test_pass(self): pass
 | |
|                         def test_fail(self): assert 0
 | |
| 
 | |
|                     def test_other(): assert 0
 | |
|                 """,
 | |
|             }
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 3 items", "* 2 failed, 1 passed in *"])
 | |
|         assert result.ret == 1
 | |
| 
 | |
|         result = testdir.runpytest("--lf", "--co")
 | |
|         assert result.ret == 0
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 3 items / 1 deselected / 2 selected",
 | |
|                 "run-last-failure: rerun previous 2 failures",
 | |
|                 "",
 | |
|                 "<Module pkg1/test_1.py>",
 | |
|                 "  <Class TestFoo>",
 | |
|                 "      <Function test_fail>",
 | |
|                 "  <Function test_other>",
 | |
|                 "",
 | |
|                 "*= 1 deselected in *",
 | |
|             ],
 | |
|             consecutive=True,
 | |
|         )
 | |
| 
 | |
|     def test_lastfailed_with_all_filtered(self, testdir: Testdir) -> None:
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_1.py": """
 | |
|                     def test_fail(): assert 0
 | |
|                     def test_pass(): pass
 | |
|                 """,
 | |
|             }
 | |
|         )
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"])
 | |
|         assert result.ret == 1
 | |
| 
 | |
|         # Remove known failure.
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "pkg1/test_1.py": """
 | |
|                     def test_pass(): pass
 | |
|                 """,
 | |
|             }
 | |
|         )
 | |
|         result = testdir.runpytest("--lf", "--co")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "collected 1 item",
 | |
|                 "run-last-failure: 1 known failures not in selected tests",
 | |
|                 "",
 | |
|                 "<Module pkg1/test_1.py>",
 | |
|                 "  <Function test_pass>",
 | |
|                 "",
 | |
|                 "*= no tests ran in*",
 | |
|             ],
 | |
|             consecutive=True,
 | |
|         )
 | |
|         assert result.ret == 0
 | |
| 
 | |
| 
 | |
| class TestNewFirst:
 | |
|     def test_newfirst_usecase(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "test_1/test_1.py": """
 | |
|                 def test_1(): assert 1
 | |
|             """,
 | |
|                 "test_2/test_2.py": """
 | |
|                 def test_1(): assert 1
 | |
|             """,
 | |
|             }
 | |
|         )
 | |
|         testdir.tmpdir.join("test_1/test_1.py").setmtime(1)
 | |
| 
 | |
|         result = testdir.runpytest("-v")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             ["*test_1/test_1.py::test_1 PASSED*", "*test_2/test_2.py::test_1 PASSED*"]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest("-v", "--nf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             ["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"]
 | |
|         )
 | |
| 
 | |
|         testdir.tmpdir.join("test_1/test_1.py").write(
 | |
|             "def test_1(): assert 1\n" "def test_2(): assert 1\n"
 | |
|         )
 | |
|         testdir.tmpdir.join("test_1/test_1.py").setmtime(1)
 | |
| 
 | |
|         result = testdir.runpytest("--nf", "--collect-only", "-q")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "test_1/test_1.py::test_2",
 | |
|                 "test_2/test_2.py::test_1",
 | |
|                 "test_1/test_1.py::test_1",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         # Newest first with (plugin) pytest_collection_modifyitems hook.
 | |
|         testdir.makepyfile(
 | |
|             myplugin="""
 | |
|             def pytest_collection_modifyitems(items):
 | |
|                 items[:] = sorted(items, key=lambda item: item.nodeid)
 | |
|                 print("new_items:", [x.nodeid for x in items])
 | |
|             """
 | |
|         )
 | |
|         testdir.syspathinsert()
 | |
|         result = testdir.runpytest("--nf", "-p", "myplugin", "--collect-only", "-q")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "new_items: *test_1.py*test_1.py*test_2.py*",
 | |
|                 "test_1/test_1.py::test_2",
 | |
|                 "test_2/test_2.py::test_1",
 | |
|                 "test_1/test_1.py::test_1",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def test_newfirst_parametrize(self, testdir):
 | |
|         testdir.makepyfile(
 | |
|             **{
 | |
|                 "test_1/test_1.py": """
 | |
|                 import pytest
 | |
|                 @pytest.mark.parametrize('num', [1, 2])
 | |
|                 def test_1(num): assert num
 | |
|             """,
 | |
|                 "test_2/test_2.py": """
 | |
|                 import pytest
 | |
|                 @pytest.mark.parametrize('num', [1, 2])
 | |
|                 def test_1(num): assert num
 | |
|             """,
 | |
|             }
 | |
|         )
 | |
| 
 | |
|         testdir.tmpdir.join("test_1/test_1.py").setmtime(1)
 | |
| 
 | |
|         result = testdir.runpytest("-v")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "*test_1/test_1.py::test_1[1*",
 | |
|                 "*test_1/test_1.py::test_1[2*",
 | |
|                 "*test_2/test_2.py::test_1[1*",
 | |
|                 "*test_2/test_2.py::test_1[2*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest("-v", "--nf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "*test_2/test_2.py::test_1[1*",
 | |
|                 "*test_2/test_2.py::test_1[2*",
 | |
|                 "*test_1/test_1.py::test_1[1*",
 | |
|                 "*test_1/test_1.py::test_1[2*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|         testdir.tmpdir.join("test_1/test_1.py").write(
 | |
|             "import pytest\n"
 | |
|             "@pytest.mark.parametrize('num', [1, 2, 3])\n"
 | |
|             "def test_1(num): assert num\n"
 | |
|         )
 | |
|         testdir.tmpdir.join("test_1/test_1.py").setmtime(1)
 | |
| 
 | |
|         # Running only a subset does not forget about existing ones.
 | |
|         result = testdir.runpytest("-v", "--nf", "test_2/test_2.py")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             ["*test_2/test_2.py::test_1[1*", "*test_2/test_2.py::test_1[2*"]
 | |
|         )
 | |
| 
 | |
|         result = testdir.runpytest("-v", "--nf")
 | |
|         result.stdout.fnmatch_lines(
 | |
|             [
 | |
|                 "*test_1/test_1.py::test_1[3*",
 | |
|                 "*test_2/test_2.py::test_1[1*",
 | |
|                 "*test_2/test_2.py::test_1[2*",
 | |
|                 "*test_1/test_1.py::test_1[1*",
 | |
|                 "*test_1/test_1.py::test_1[2*",
 | |
|             ]
 | |
|         )
 | |
| 
 | |
| 
 | |
| class TestReadme:
 | |
|     def check_readme(self, testdir):
 | |
|         config = testdir.parseconfigure()
 | |
|         readme = config.cache._cachedir.joinpath("README.md")
 | |
|         return readme.is_file()
 | |
| 
 | |
|     def test_readme_passed(self, testdir):
 | |
|         testdir.makepyfile("def test_always_passes(): pass")
 | |
|         testdir.runpytest()
 | |
|         assert self.check_readme(testdir) is True
 | |
| 
 | |
|     def test_readme_failed(self, testdir):
 | |
|         testdir.makepyfile("def test_always_fails(): assert 0")
 | |
|         testdir.runpytest()
 | |
|         assert self.check_readme(testdir) is True
 | |
| 
 | |
| 
 | |
| def test_gitignore(testdir):
 | |
|     """Ensure we automatically create .gitignore file in the pytest_cache directory (#3286)."""
 | |
|     from _pytest.cacheprovider import Cache
 | |
| 
 | |
|     config = testdir.parseconfig()
 | |
|     cache = Cache.for_config(config)
 | |
|     cache.set("foo", "bar")
 | |
|     msg = "# Created by pytest automatically.\n*\n"
 | |
|     gitignore_path = cache._cachedir.joinpath(".gitignore")
 | |
|     assert gitignore_path.read_text(encoding="UTF-8") == msg
 | |
| 
 | |
|     # Does not overwrite existing/custom one.
 | |
|     gitignore_path.write_text("custom")
 | |
|     cache.set("something", "else")
 | |
|     assert gitignore_path.read_text(encoding="UTF-8") == "custom"
 | |
| 
 | |
| 
 | |
| def test_does_not_create_boilerplate_in_existing_dirs(testdir):
 | |
|     from _pytest.cacheprovider import Cache
 | |
| 
 | |
|     testdir.makeini(
 | |
|         """
 | |
|         [pytest]
 | |
|         cache_dir = .
 | |
|         """
 | |
|     )
 | |
|     config = testdir.parseconfig()
 | |
|     cache = Cache.for_config(config)
 | |
|     cache.set("foo", "bar")
 | |
| 
 | |
|     assert os.path.isdir("v")  # cache contents
 | |
|     assert not os.path.exists(".gitignore")
 | |
|     assert not os.path.exists("README.md")
 | |
| 
 | |
| 
 | |
| def test_cachedir_tag(testdir):
 | |
|     """Ensure we automatically create CACHEDIR.TAG file in the pytest_cache directory (#4278)."""
 | |
|     from _pytest.cacheprovider import Cache
 | |
|     from _pytest.cacheprovider import CACHEDIR_TAG_CONTENT
 | |
| 
 | |
|     config = testdir.parseconfig()
 | |
|     cache = Cache.for_config(config)
 | |
|     cache.set("foo", "bar")
 | |
|     cachedir_tag_path = cache._cachedir.joinpath("CACHEDIR.TAG")
 | |
|     assert cachedir_tag_path.read_bytes() == CACHEDIR_TAG_CONTENT
 |