2014-09-04 02:50:54 +08:00
|
|
|
import os
|
2014-09-02 01:06:32 +08:00
|
|
|
import pkgutil
|
2018-01-11 05:53:45 +08:00
|
|
|
import sys
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
from _pytest import monkeypatch
|
|
|
|
|
2022-07-05 21:33:03 +08:00
|
|
|
from flask import Flask
|
|
|
|
from flask.globals import request_ctx
|
2017-05-24 06:18:39 +08:00
|
|
|
|
|
|
|
|
2019-05-07 03:39:41 +08:00
|
|
|
@pytest.fixture(scope="session", autouse=True)
|
2018-01-11 05:53:45 +08:00
|
|
|
def _standard_os_environ():
|
|
|
|
"""Set up ``os.environ`` at the start of the test session to have
|
|
|
|
standard values. Returns a list of operations that is used by
|
|
|
|
:func:`._reset_os_environ` after each test.
|
|
|
|
"""
|
|
|
|
mp = monkeypatch.MonkeyPatch()
|
|
|
|
out = (
|
2022-06-18 00:26:26 +08:00
|
|
|
(os.environ, "FLASK_ENV_FILE", monkeypatch.notset),
|
2019-05-07 03:39:41 +08:00
|
|
|
(os.environ, "FLASK_APP", monkeypatch.notset),
|
|
|
|
(os.environ, "FLASK_DEBUG", monkeypatch.notset),
|
|
|
|
(os.environ, "FLASK_RUN_FROM_CLI", monkeypatch.notset),
|
|
|
|
(os.environ, "WERKZEUG_RUN_MAIN", monkeypatch.notset),
|
2018-01-11 05:53:45 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
for _, key, value in out:
|
|
|
|
if value is monkeypatch.notset:
|
|
|
|
mp.delenv(key, False)
|
|
|
|
else:
|
|
|
|
mp.setenv(key, value)
|
|
|
|
|
|
|
|
yield out
|
|
|
|
mp.undo()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def _reset_os_environ(monkeypatch, _standard_os_environ):
|
|
|
|
"""Reset ``os.environ`` to the standard environ after each test,
|
|
|
|
in case a test changed something without cleaning up.
|
|
|
|
"""
|
|
|
|
monkeypatch._setitem.extend(_standard_os_environ)
|
|
|
|
|
|
|
|
|
2017-05-24 06:18:39 +08:00
|
|
|
@pytest.fixture
|
|
|
|
def app():
|
2019-05-07 03:39:41 +08:00
|
|
|
app = Flask("flask_test", root_path=os.path.dirname(__file__))
|
2022-07-05 21:33:03 +08:00
|
|
|
app.config.update(
|
|
|
|
TESTING=True,
|
|
|
|
SECRET_KEY="test key",
|
|
|
|
)
|
2017-05-24 06:18:39 +08:00
|
|
|
return app
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def app_ctx(app):
|
|
|
|
with app.app_context() as ctx:
|
|
|
|
yield ctx
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def req_ctx(app):
|
|
|
|
with app.test_request_context() as ctx:
|
|
|
|
yield ctx
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def client(app):
|
|
|
|
return app.test_client()
|
2014-09-02 01:06:32 +08:00
|
|
|
|
|
|
|
|
2014-09-04 20:37:48 +08:00
|
|
|
@pytest.fixture
|
|
|
|
def test_apps(monkeypatch):
|
2020-07-31 09:36:55 +08:00
|
|
|
monkeypatch.syspath_prepend(os.path.join(os.path.dirname(__file__), "test_apps"))
|
|
|
|
original_modules = set(sys.modules.keys())
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
# Remove any imports cached during the test. Otherwise "import app"
|
|
|
|
# will work in the next test even though it's no longer on the path.
|
|
|
|
for key in sys.modules.keys() - original_modules:
|
|
|
|
sys.modules.pop(key)
|
2014-09-04 02:50:54 +08:00
|
|
|
|
2017-05-24 00:21:29 +08:00
|
|
|
|
2014-09-04 02:50:54 +08:00
|
|
|
@pytest.fixture(autouse=True)
|
2017-05-24 00:21:29 +08:00
|
|
|
def leak_detector():
|
|
|
|
yield
|
|
|
|
|
|
|
|
# make sure we're not leaking a request context since we are
|
|
|
|
# testing flask internally in debug mode in a few cases
|
|
|
|
leaks = []
|
2022-07-05 21:33:03 +08:00
|
|
|
while request_ctx:
|
|
|
|
leaks.append(request_ctx._get_current_object())
|
|
|
|
request_ctx.pop()
|
|
|
|
|
2017-05-24 00:21:29 +08:00
|
|
|
assert leaks == []
|
2014-09-04 02:50:54 +08:00
|
|
|
|
|
|
|
|
2014-09-02 01:06:32 +08:00
|
|
|
@pytest.fixture(params=(True, False))
|
|
|
|
def limit_loader(request, monkeypatch):
|
|
|
|
"""Patch pkgutil.get_loader to give loader without get_filename or archive.
|
|
|
|
|
|
|
|
This provides for tests where a system has custom loaders, e.g. Google App
|
|
|
|
Engine's HardenedModulesHook, which have neither the `get_filename` method
|
|
|
|
nor the `archive` attribute.
|
|
|
|
|
|
|
|
This fixture will run the testcase twice, once with and once without the
|
|
|
|
limitation/mock.
|
|
|
|
"""
|
|
|
|
if not request.param:
|
|
|
|
return
|
|
|
|
|
2020-04-05 00:43:06 +08:00
|
|
|
class LimitedLoader:
|
2014-09-02 01:06:32 +08:00
|
|
|
def __init__(self, loader):
|
|
|
|
self.loader = loader
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
2020-04-05 02:39:03 +08:00
|
|
|
if name in {"archive", "get_filename"}:
|
|
|
|
raise AttributeError(f"Mocking a loader which does not have {name!r}.")
|
2014-09-02 01:06:32 +08:00
|
|
|
return getattr(self.loader, name)
|
|
|
|
|
|
|
|
old_get_loader = pkgutil.get_loader
|
|
|
|
|
|
|
|
def get_loader(*args, **kwargs):
|
|
|
|
return LimitedLoader(old_get_loader(*args, **kwargs))
|
2017-05-24 06:18:39 +08:00
|
|
|
|
2019-05-07 03:39:41 +08:00
|
|
|
monkeypatch.setattr(pkgutil, "get_loader", get_loader)
|
2014-09-02 01:06:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2014-09-04 21:22:57 +08:00
|
|
|
def modules_tmpdir(tmpdir, monkeypatch):
|
2017-05-24 06:18:39 +08:00
|
|
|
"""A tmpdir added to sys.path."""
|
2019-05-07 03:39:41 +08:00
|
|
|
rv = tmpdir.mkdir("modules_tmpdir")
|
2014-09-02 01:06:32 +08:00
|
|
|
monkeypatch.syspath_prepend(str(rv))
|
|
|
|
return rv
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2014-09-04 21:22:57 +08:00
|
|
|
def modules_tmpdir_prefix(modules_tmpdir, monkeypatch):
|
2019-05-07 03:39:41 +08:00
|
|
|
monkeypatch.setattr(sys, "prefix", str(modules_tmpdir))
|
2014-09-04 21:22:57 +08:00
|
|
|
return modules_tmpdir
|
2014-09-02 01:06:32 +08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2014-09-04 21:22:57 +08:00
|
|
|
def site_packages(modules_tmpdir, monkeypatch):
|
2017-05-24 06:18:39 +08:00
|
|
|
"""Create a fake site-packages."""
|
2019-05-07 03:39:41 +08:00
|
|
|
rv = (
|
|
|
|
modules_tmpdir.mkdir("lib")
|
2020-04-05 02:39:03 +08:00
|
|
|
.mkdir(f"python{sys.version_info.major}.{sys.version_info.minor}")
|
2019-05-07 03:39:41 +08:00
|
|
|
.mkdir("site-packages")
|
|
|
|
)
|
2014-09-02 01:06:32 +08:00
|
|
|
monkeypatch.syspath_prepend(str(rv))
|
|
|
|
return rv
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def purge_module(request):
|
|
|
|
def inner(name):
|
|
|
|
request.addfinalizer(lambda: sys.modules.pop(name, None))
|
2017-05-24 06:18:39 +08:00
|
|
|
|
2014-09-02 01:06:32 +08:00
|
|
|
return inner
|