From e80fe8675d37e37e78081bdf242332581f667cb4 Mon Sep 17 00:00:00 2001 From: mbsantiago Date: Sun, 29 Mar 2026 15:55:24 +0100 Subject: [PATCH] Mark some tests as slow for quicker feedback --- justfile | 10 +++++++++- pyproject.toml | 5 +++++ tests/test_api_v2/test_api_v2.py | 5 +++++ tests/test_audio_utils.py | 3 +++ tests/test_cli/test_detect.py | 4 ++++ tests/test_cli/test_predict.py | 1 + tests/test_cli/test_train.py | 2 ++ tests/test_contrib.py | 2 ++ tests/test_detections.py | 3 +++ tests/test_model.py | 3 +++ tests/test_train/test_checkpoints.py | 3 +++ tests/test_train/test_lightning.py | 2 ++ 12 files changed, 42 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index 39fa8b7..f8d298a 100644 --- a/justfile +++ b/justfile @@ -20,7 +20,15 @@ install: # Testing & Coverage # Run tests using pytest. test: - uv run pytest -n auto {{TESTS_DIR}} + uv run pytest {{TESTS_DIR}} + +# Run the fast subset of tests (excludes @pytest.mark.slow). +test-quick: + uv run pytest --durations=10 -m "not slow" {{TESTS_DIR}} + +# Run only long-running tests marked with @pytest.mark.slow. +test-slow: + uv run pytest -m "slow" {{TESTS_DIR}} # Run tests and generate coverage data. coverage: diff --git a/pyproject.toml b/pyproject.toml index 1f91b14..6096d78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,3 +129,8 @@ exclude = [ "src/batdetect2/finetune", "src/batdetect2/utils", ] + +[tool.pytest.ini_options] +markers = [ + "slow: marks long-running tests that are skipped in quick runs", +] diff --git a/tests/test_api_v2/test_api_v2.py b/tests/test_api_v2/test_api_v2.py index cb39c6f..42ad1ec 100644 --- a/tests/test_api_v2/test_api_v2.py +++ b/tests/test_api_v2/test_api_v2.py @@ -46,6 +46,7 @@ def test_process_file_returns_recording_level_predictions( ) +@pytest.mark.slow def test_process_files_is_batch_size_invariant( api_v2: BatDetect2API, example_audio_files: list[Path], @@ -180,6 +181,7 @@ def test_user_can_read_extracted_features_per_detection( assert all(vec.size > 0 for vec in feature_vectors) +@pytest.mark.slow def test_user_can_load_checkpoint_and_finetune( tmp_path: Path, example_annotations, @@ -293,6 +295,7 @@ def test_checkpoint_with_same_targets_config_keeps_heads_unchanged( ) +@pytest.mark.slow def test_user_can_finetune_only_heads( tmp_path: Path, example_annotations, @@ -328,6 +331,7 @@ def test_user_can_finetune_only_heads( assert list(finetune_dir.rglob("*.ckpt")) +@pytest.mark.slow def test_user_can_evaluate_small_dataset_and_get_metrics( api_v2: BatDetect2API, example_annotations, @@ -414,6 +418,7 @@ def test_detection_threshold_override_changes_process_file_results( ) +@pytest.mark.slow def test_detection_threshold_override_is_ephemeral_in_process_file( api_v2: BatDetect2API, example_audio_files: list[Path], diff --git a/tests/test_audio_utils.py b/tests/test_audio_utils.py index 1414401..777d392 100644 --- a/tests/test_audio_utils.py +++ b/tests/test_audio_utils.py @@ -1,4 +1,5 @@ import numpy as np +import pytest import torch import torch.nn.functional as F from hypothesis import given, settings @@ -10,6 +11,7 @@ from batdetect2.utils import audio_utils, detector_utils @given(duration=st.floats(min_value=0.1, max_value=1)) @settings(deadline=None) +@pytest.mark.slow def test_can_compute_correct_spectrogram_width(duration: float): samplerate = parameters.TARGET_SAMPLERATE_HZ params = parameters.DEFAULT_SPECTROGRAM_PARAMETERS @@ -89,6 +91,7 @@ def test_pad_audio_without_fixed_size(duration: float): @given(duration=st.floats(min_value=0.1, max_value=2)) @settings(deadline=None) +@pytest.mark.slow def test_computed_spectrograms_are_actually_divisible_by_the_spec_divide_factor( duration: float, ): diff --git a/tests/test_cli/test_detect.py b/tests/test_cli/test_detect.py index e10cca1..f20c15e 100644 --- a/tests/test_cli/test_detect.py +++ b/tests/test_cli/test_detect.py @@ -3,11 +3,13 @@ from pathlib import Path import pandas as pd +import pytest from click.testing import CliRunner from batdetect2.cli import cli +@pytest.mark.slow def test_cli_detect_command_on_test_audio(tmp_path: Path) -> None: """User story: run legacy detect on example audio directory.""" @@ -29,6 +31,7 @@ def test_cli_detect_command_on_test_audio(tmp_path: Path) -> None: assert len(list(results_dir.glob("*.json"))) == 3 +@pytest.mark.slow def test_cli_detect_command_with_non_trivial_time_expansion( tmp_path: Path, ) -> None: @@ -52,6 +55,7 @@ def test_cli_detect_command_with_non_trivial_time_expansion( assert "Time Expansion Factor: 10" in result.stdout +@pytest.mark.slow def test_cli_detect_command_with_spec_feature_flag(tmp_path: Path) -> None: """User story: request extra spectral features in output CSV.""" diff --git a/tests/test_cli/test_predict.py b/tests/test_cli/test_predict.py index e820e12..23f92dc 100644 --- a/tests/test_cli/test_predict.py +++ b/tests/test_cli/test_predict.py @@ -20,6 +20,7 @@ def test_cli_predict_help() -> None: assert "dataset" in result.output +@pytest.mark.slow def test_cli_predict_directory_runs_on_real_audio( tmp_path: Path, tiny_checkpoint_path: Path, diff --git a/tests/test_cli/test_train.py b/tests/test_cli/test_train.py index 8f2876f..2212a57 100644 --- a/tests/test_cli/test_train.py +++ b/tests/test_cli/test_train.py @@ -2,6 +2,7 @@ from pathlib import Path +import pytest from click.testing import CliRunner from batdetect2.cli import cli @@ -19,6 +20,7 @@ def test_cli_train_help() -> None: assert "--model" in result.output +@pytest.mark.slow def test_cli_train_from_checkpoint_runs_on_small_dataset( tmp_path: Path, tiny_checkpoint_path: Path, diff --git a/tests/test_contrib.py b/tests/test_contrib.py index b97e9c0..ecfa894 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -2,11 +2,13 @@ from pathlib import Path +import pytest from click.testing import CliRunner from batdetect2.cli import cli runner = CliRunner() +pytestmark = pytest.mark.slow def test_can_process_jeff37_files( diff --git a/tests/test_detections.py b/tests/test_detections.py index 7f17012..8263daa 100644 --- a/tests/test_detections.py +++ b/tests/test_detections.py @@ -2,11 +2,14 @@ import os +import pytest + from batdetect2 import api DATA_DIR = os.path.join(os.path.dirname(__file__), "data") +@pytest.mark.slow def test_no_detections_above_nyquist(): """Test that no detections are made above the nyquist frequency.""" # Recording donated by @@kdarras diff --git a/tests/test_model.py b/tests/test_model.py index f13d6b6..43b6eac 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -4,6 +4,7 @@ from pathlib import Path from typing import List import numpy as np +import pytest from hypothesis import given, settings from hypothesis import strategies as st @@ -13,6 +14,7 @@ from batdetect2.detector import parameters @settings(deadline=None, max_examples=5) @given(duration=st.floats(min_value=0.1, max_value=2)) +@pytest.mark.slow def test_can_import_model_without_pickle(duration: float): # NOTE: remove this test once no other issues are found This is a temporary # test to check that change in model loading did not impact model behaviour @@ -42,6 +44,7 @@ def test_can_import_model_without_pickle(duration: float): assert predictions_without_pickle == predictions_with_pickle +@pytest.mark.slow def test_can_import_model_without_pickle_on_test_data( example_audio_files: List[Path], ): diff --git a/tests/test_train/test_checkpoints.py b/tests/test_train/test_checkpoints.py index 7ff97bb..b228378 100644 --- a/tests/test_train/test_checkpoints.py +++ b/tests/test_train/test_checkpoints.py @@ -1,10 +1,13 @@ from pathlib import Path +import pytest from soundevent import data from batdetect2.config import BatDetect2Config from batdetect2.train import run_train +pytestmark = pytest.mark.slow + def _build_fast_train_config() -> BatDetect2Config: config = BatDetect2Config() diff --git a/tests/test_train/test_lightning.py b/tests/test_train/test_lightning.py index 1ea2e3c..284fea4 100644 --- a/tests/test_train/test_lightning.py +++ b/tests/test_train/test_lightning.py @@ -37,6 +37,7 @@ def test_can_initialize_default_module(): assert isinstance(module, L.LightningModule) +@pytest.mark.slow def test_can_save_checkpoint( tmp_path: Path, clip: data.Clip, @@ -182,6 +183,7 @@ def test_api_from_checkpoint_reconstructs_model_config(tmp_path: Path): assert api.audio_config.samplerate == module.model_config.samplerate +@pytest.mark.slow def test_train_smoke_produces_loadable_checkpoint( tmp_path: Path, example_annotations: list[data.ClipAnnotation],