jpayne@69: """ jpayne@69: Pytest configuration and fixtures for the Numpy test suite. jpayne@69: """ jpayne@69: import os jpayne@69: import tempfile jpayne@69: jpayne@69: import hypothesis jpayne@69: import pytest jpayne@69: import numpy jpayne@69: jpayne@69: from numpy.core._multiarray_tests import get_fpu_mode jpayne@69: jpayne@69: jpayne@69: _old_fpu_mode = None jpayne@69: _collect_results = {} jpayne@69: jpayne@69: # Use a known and persistent tmpdir for hypothesis' caches, which jpayne@69: # can be automatically cleared by the OS or user. jpayne@69: hypothesis.configuration.set_hypothesis_home_dir( jpayne@69: os.path.join(tempfile.gettempdir(), ".hypothesis") jpayne@69: ) jpayne@69: jpayne@69: # We register two custom profiles for Numpy - for details see jpayne@69: # https://hypothesis.readthedocs.io/en/latest/settings.html jpayne@69: # The first is designed for our own CI runs; the latter also jpayne@69: # forces determinism and is designed for use via np.test() jpayne@69: hypothesis.settings.register_profile( jpayne@69: name="numpy-profile", deadline=None, print_blob=True, jpayne@69: ) jpayne@69: hypothesis.settings.register_profile( jpayne@69: name="np.test() profile", jpayne@69: deadline=None, print_blob=True, database=None, derandomize=True, jpayne@69: suppress_health_check=list(hypothesis.HealthCheck), jpayne@69: ) jpayne@69: # Note that the default profile is chosen based on the presence jpayne@69: # of pytest.ini, but can be overridden by passing the jpayne@69: # --hypothesis-profile=NAME argument to pytest. jpayne@69: _pytest_ini = os.path.join(os.path.dirname(__file__), "..", "pytest.ini") jpayne@69: hypothesis.settings.load_profile( jpayne@69: "numpy-profile" if os.path.isfile(_pytest_ini) else "np.test() profile" jpayne@69: ) jpayne@69: jpayne@69: jpayne@69: def pytest_configure(config): jpayne@69: config.addinivalue_line("markers", jpayne@69: "valgrind_error: Tests that are known to error under valgrind.") jpayne@69: config.addinivalue_line("markers", jpayne@69: "leaks_references: Tests that are known to leak references.") jpayne@69: config.addinivalue_line("markers", jpayne@69: "slow: Tests that are very slow.") jpayne@69: config.addinivalue_line("markers", jpayne@69: "slow_pypy: Tests that are very slow on pypy.") jpayne@69: jpayne@69: jpayne@69: def pytest_addoption(parser): jpayne@69: parser.addoption("--available-memory", action="store", default=None, jpayne@69: help=("Set amount of memory available for running the " jpayne@69: "test suite. This can result to tests requiring " jpayne@69: "especially large amounts of memory to be skipped. " jpayne@69: "Equivalent to setting environment variable " jpayne@69: "NPY_AVAILABLE_MEM. Default: determined" jpayne@69: "automatically.")) jpayne@69: jpayne@69: jpayne@69: def pytest_sessionstart(session): jpayne@69: available_mem = session.config.getoption('available_memory') jpayne@69: if available_mem is not None: jpayne@69: os.environ['NPY_AVAILABLE_MEM'] = available_mem jpayne@69: jpayne@69: jpayne@69: #FIXME when yield tests are gone. jpayne@69: @pytest.hookimpl() jpayne@69: def pytest_itemcollected(item): jpayne@69: """ jpayne@69: Check FPU precision mode was not changed during test collection. jpayne@69: jpayne@69: The clumsy way we do it here is mainly necessary because numpy jpayne@69: still uses yield tests, which can execute code at test collection jpayne@69: time. jpayne@69: """ jpayne@69: global _old_fpu_mode jpayne@69: jpayne@69: mode = get_fpu_mode() jpayne@69: jpayne@69: if _old_fpu_mode is None: jpayne@69: _old_fpu_mode = mode jpayne@69: elif mode != _old_fpu_mode: jpayne@69: _collect_results[item] = (_old_fpu_mode, mode) jpayne@69: _old_fpu_mode = mode jpayne@69: jpayne@69: jpayne@69: @pytest.fixture(scope="function", autouse=True) jpayne@69: def check_fpu_mode(request): jpayne@69: """ jpayne@69: Check FPU precision mode was not changed during the test. jpayne@69: """ jpayne@69: old_mode = get_fpu_mode() jpayne@69: yield jpayne@69: new_mode = get_fpu_mode() jpayne@69: jpayne@69: if old_mode != new_mode: jpayne@69: raise AssertionError("FPU precision mode changed from {0:#x} to {1:#x}" jpayne@69: " during the test".format(old_mode, new_mode)) jpayne@69: jpayne@69: collect_result = _collect_results.get(request.node) jpayne@69: if collect_result is not None: jpayne@69: old_mode, new_mode = collect_result jpayne@69: raise AssertionError("FPU precision mode changed from {0:#x} to {1:#x}" jpayne@69: " when collecting the test".format(old_mode, jpayne@69: new_mode)) jpayne@69: jpayne@69: jpayne@69: @pytest.fixture(autouse=True) jpayne@69: def add_np(doctest_namespace): jpayne@69: doctest_namespace['np'] = numpy jpayne@69: jpayne@69: @pytest.fixture(autouse=True) jpayne@69: def env_setup(monkeypatch): jpayne@69: monkeypatch.setenv('PYTHONHASHSEED', '0') jpayne@69: jpayne@69: jpayne@69: @pytest.fixture(params=[True, False]) jpayne@69: def weak_promotion(request): jpayne@69: """ jpayne@69: Fixture to ensure "legacy" promotion state or change it to use the new jpayne@69: weak promotion (plus warning). `old_promotion` should be used as a jpayne@69: parameter in the function. jpayne@69: """ jpayne@69: state = numpy._get_promotion_state() jpayne@69: if request.param: jpayne@69: numpy._set_promotion_state("weak_and_warn") jpayne@69: else: jpayne@69: numpy._set_promotion_state("legacy") jpayne@69: jpayne@69: yield request.param jpayne@69: numpy._set_promotion_state(state)