diff --git a/pyproject.toml b/pyproject.toml index 20cc1cff..f432275c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ dev = [ "pyinstrument", "pytest", "pytest-cov", + "pytest-lazy-fixture", "pytest-mock", "pytest-timeout", "tox", @@ -118,6 +119,7 @@ module = [ "tifffile.*", "pyinstrument.*", "pytest.*", + "pytest_lazyfixture.*", "scipy.*", "skimage.*", "sklearn.*", diff --git a/src/cellfinder_core/detect/detect.py b/src/cellfinder_core/detect/detect.py index 4fb2e958..a5c63446 100644 --- a/src/cellfinder_core/detect/detect.py +++ b/src/cellfinder_core/detect/detect.py @@ -88,7 +88,7 @@ def main( f"{signal_array.dtype}" ) n_processes = get_num_processes(min_free_cpu_cores=n_free_cpus) - n_ball_procs = n_processes - 1 + n_ball_procs = max(n_processes - 1, 1) start_time = datetime.now() ( diff --git a/tests/tests/conftest.py b/tests/tests/conftest.py index 9e2f2a24..a65a809f 100644 --- a/tests/tests/conftest.py +++ b/tests/tests/conftest.py @@ -1,3 +1,4 @@ +import os from typing import Tuple import numpy as np @@ -9,13 +10,25 @@ @pytest.fixture(scope="session") -def n_free_cpus() -> int: +def no_free_cpus() -> int: """ - Set number of free CPUs while the tests are running. + Set number of free CPUs so all available CPUs are used by the tests. """ return 0 +@pytest.fixture(scope="session") +def run_on_one_cpu_only() -> int: + """ + Set number of free CPUs so tests can use exactly one CPU. + """ + cpus = os.cpu_count() + if cpus is not None: + return cpus - 1 + else: + raise ValueError("No CPUs available.") + + @pytest.fixture(scope="session") def download_default_model(): """ diff --git a/tests/tests/test_integration/test_detection.py b/tests/tests/test_integration/test_detection.py index 52a01c93..f16a437b 100644 --- a/tests/tests/test_integration/test_detection.py +++ b/tests/tests/test_integration/test_detection.py @@ -44,6 +44,13 @@ def background_array(): # FIXME: This isn't a very good example @pytest.mark.slow +@pytest.mark.parametrize( + "n_free_cpus", + [ + pytest.lazy_fixture("no_free_cpus"), + pytest.lazy_fixture("run_on_one_cpu_only"), + ], +) def test_detection_full(signal_array, background_array, n_free_cpus): cells_test = main( signal_array, @@ -72,10 +79,10 @@ def test_detection_full(signal_array, background_array, n_free_cpus): def test_detection_small_planes( - signal_array, background_array, n_free_cpus, mocker + signal_array, background_array, no_free_cpus, mocker ): # Check that processing works when number of planes < number of processes - nproc = get_num_processes(n_free_cpus) + nproc = get_num_processes(no_free_cpus) n_planes = 2 # Don't want to bother classifying in this test, so mock classifcation @@ -92,11 +99,11 @@ def test_detection_small_planes( background_array[0:n_planes], voxel_sizes, ball_z_size=5, - n_free_cpus=n_free_cpus, + n_free_cpus=no_free_cpus, ) -def test_callbacks(signal_array, background_array, n_free_cpus): +def test_callbacks(signal_array, background_array, no_free_cpus): # 20 is minimum number of planes needed to find > 0 cells signal_array = signal_array[0:20] background_array = background_array[0:20] @@ -121,7 +128,7 @@ def detect_finished_callback(points): detect_callback=detect_callback, classify_callback=classify_callback, detect_finished_callback=detect_finished_callback, - n_free_cpus=n_free_cpus, + n_free_cpus=no_free_cpus, ) np.testing.assert_equal(planes_done, np.arange(len(signal_array))) @@ -139,13 +146,13 @@ def test_floating_point_error(signal_array, background_array): main(signal_array, background_array, voxel_sizes) -def test_synthetic_data(synthetic_bright_spots, n_free_cpus): +def test_synthetic_data(synthetic_bright_spots, no_free_cpus): signal_array, background_array = synthetic_bright_spots detected = main( signal_array, background_array, voxel_sizes, - n_free_cpus=n_free_cpus, + n_free_cpus=no_free_cpus, ) assert len(detected) == 8 diff --git a/tox.ini b/tox.ini index 83bfe822..3f00afe3 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,7 @@ commands = python -m pytest -v --color=yes deps = pytest pytest-cov + pytest-lazy-fixture pytest-mock pytest-timeout passenv =