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